Egon, have you ever run into the problem with AWTGLCanvas in which the entire rendering is out of place on the canvas that I did about a decade ago and ever since on every computer I've ever run my stuff on? I'd love to have that fixed (most likely by switching to lwjgl 3 and convincing them to add AWTGLCanvas back). What do you think?
I've moved the question into a new topic, just in case you are wondering.
I'm not aware of a problem with the location of AWTGLCanvas but then again, I've never really used it in situations where it doesn't use the whole frame. So I never tried to use it as part of an actual UI.
Have to tried to disable Java2D's accelerated rendering pipeline? Maybe it's a problem with that (albeit I doubt it, but it might be worth a try).
I've tried everything. Test it out as an actual UI and let me know, please.
Although I should reiterate that I usually use it on the entire frame. I'm making a sometimes-2d sometimes 3d game and it would be available there.
Egon?
I'm not of much help here, I'm afraid. I've actually no clue about UI stuff in Java. All I'm usually doing is to open a JFrame/Frame and then draw stuff all by myself.
I dimly remember that others tried to mix an actual UI with the AWTGLCanvas and IIRC, it didn't work our too well (but was possible to a degree). But I couldn't find this thread.
Have you actually tried this approach: https://www.jpct.net/wiki/index226d.html?title=Using_jPCT_in_Swing (https://www.jpct.net/wiki/index226d.html?title=Using_jPCT_in_Swing)
I usually use AWT, but I'll try Swing to see if it's still there. Have you used the Canvas on a AWT frame to see the problem?
This seems to be software. Am I missing something?
Quote from: AGP on September 26, 2025, 01:47:01 AMThis seems to be software. Am I missing something?
I'm not sure what you mean!?
I tried quoting this line, but it didn't show up for some reason:
Have you actually tried this approach: https://www.jpct.net/wiki/index226d.html?title=Using_jPCT_in_Swing
This example seems to be using software rendering.
CG proposed the following changes to AWTJPCTCanvas. If you could possibly compile it for me (I've never compiled jpct, only my C# port) so that I could test it I would be very grateful:
package com.threed.jpct;
import org.lwjgl.opengl.*;
import java.awt.GraphicsConfiguration;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.AffineTransform;
class AWTJPCTCanvas extends AWTGLCanvas implements IThreadBuffer {
private static final long serialVersionUID = 1L;
private AWTGLRenderer renderer = null;
private AWTDisplayList[] list = null;
private AWTDisplayList onceList = null;
private int curList = 0;
private Object lock = new Object();
private boolean paintObserver = false;
private Object[] result = null;
AWTJPCTCanvas() throws Exception {
}
AWTJPCTCanvas(AWTGLRenderer myRenderer) throws Exception {
super();
init(myRenderer);
}
AWTJPCTCanvas(AWTGLRenderer myRenderer, PixelFormat pf) throws Exception {
super(pf);
init(myRenderer);
}
public void setSamples(int samples) {
// Bereits im PF erledigt...hier egal...
}
protected void init(AWTGLRenderer myRenderer) {
renderer = myRenderer;
list = new AWTDisplayList[2];
list
[o]= new AWTDisplayList();
list[1] = new AWTDisplayList();
onceList = new AWTDisplayList();
// Reassert viewport after resizes so device-pixel size stays in sync.
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
// Trigger a repaint so paintGL() can recompute the viewport.
repaint();
}
});
}
public void exceptionOccurred(org.lwjgl.LWJGLException e) {
if (e.getMessage().indexOf("No support for") != -1) {
// A hack to take care of missing aa-support in intel. Injects a new
// pixel format into the canvas vie reflection and makes the
// canvas think that it's new...
try {
java.lang.reflect.Field field = AWTGLCanvas.class.getDeclaredField("pixel_format");
field.setAccessible(true);
PixelFormat pf = (PixelFormat) field.get(this);
if (pf == null || pf.getSamples() != 0) {
Logger.log("No support for multi sampling...trying to recover!", Logger.WARNING);
field.set(this, new PixelFormat());
java.lang.reflect.Field count = AWTGLCanvas.class.getDeclaredField("reentry_count");
java.lang.reflect.Field peer = AWTGLCanvas.class.getDeclaredField("peer_info");
count.setAccessible(true);
peer.setAccessible(true);
peer.set(this, (Object[]) null);
} else {
Logger.log(e, Logger.ERROR);
}
} catch (Throwable e1) {
Logger.log(e1, Logger.WARNING);
e.printStackTrace();
Logger.log(e, Logger.ERROR);
}
} else {
Logger.log(e, Logger.ERROR);
}
}
public void dispose() {
setEnabled(false);
}
public final void add(int command, Object params) {
list[curList].add(command, params);
}
public void add(Camera proj, int[] ambient) {
Object[] params = new Object[2];
Camera cam2 = new Camera();
cam2.setBack(proj.getBack().cloneMatrix());
cam2.setPosition(proj.getPosition());
cam2.setFOVLimits(proj.getMinFOV(), proj.getMaxFOV());
cam2.setFOV(proj.getFOV());
params
[o]= cam2;
int[] am = new int[ambient.length];
for (int i = 0; i < am.length; i++) {
am[i] = ambient[i];
}
params[1] = am;
add(GLBase.MODE_SET_CAMERA, params);
}
public final void addOnce(int command, Object params) {
onceList.addOnce(command, params);
}
public Object getLock() {
return lock;
}
public final void add(VisList v, int s, int e, int m) {
list[curList].add(v, s, e, m);
}
public void enableRenderTarget() {
list[curList].enableRenderTarget();
}
public void disableRenderTarget() {
list[curList].disableRenderTarget();
}
public boolean hasRenderTarget() {
return list[curList].hasRenderTarget();
}
public final void setColor(int col) {
list[curList].setColor(col);
}
public final AWTDisplayList getDisplayList() {
return list[curList];
}
/*
* public final void fillInstances() { synchronized (lock) {
* list[curList].fillInstances(); } }
*/
public final void switchList() {
if (Config.synchronizedRendering) {
while (!list[curList ^ 1].rendered) {
Thread.yield();
}
}
synchronized (lock) {
list[curList].switchBuffers();
curList ^= 1;
list[curList].reset();
}
}
public final boolean hasBeenPainted() {
return paintObserver;
}
public final void observePainting() {
paintObserver = false;
}
public final Object[] getPaintResults() {
if (hasBeenPainted()) {
Object[] res = result;
result = null;
return res;
}
return null;
}
// --- HiDPI fix: ensure the GL viewport matches the device-pixel backbuffer ---
/** Ensure GL viewport matches device-pixel backbuffer (fixes HiDPI offset). */
private void ensureHiDPIVP() {
int fbW, fbH;
GraphicsConfiguration gc = getGraphicsConfiguration();
if (gc != null) {
AffineTransform tx = gc.getDefaultTransform();
double sx = tx.getScaleX(), sy = tx.getScaleY();
int lw = Math.max(1, getWidth());
int lh = Math.max(1, getHeight());
fbW = (int)Math.round(lw * sx);
fbH = (int)Math.round(lh * sy);
} else {
fbW = Math.max(1, getWidth());
fbH = Math.max(1, getHeight());
}
GL11.glViewport(0, 0, fbW, fbH);
}
// ------------------------------------------------------------------------------
public void paintGL() {
try {
makeCurrent();
// HiDPI: align viewport to device-pixel framebuffer before any rendering.
ensureHiDPIVP();
if (!renderer.isDisposed()) {
if (!renderer.isInitialized()) {
renderer.init();
}
renderer.execute(GLBase.MODE_START_PAINTING, null);
synchronized (lock) {
// Normales Zeichnen...
int ind = curList ^ 1;
AWTDisplayList al = list[ind];
int end = al.count;
for (int i = 0; i < end; i++) {
int mode = al.mode[i];
switch (mode) {
case (AWTDisplayList.EXECUTE): {
renderer.executeGL(al, i);
break;
}
case (AWTDisplayList.ARRAY): {
renderer.drawVertexArray(al, i);
break;
}
case (AWTDisplayList.STRIP): {
renderer.drawStrip(al, i);
break;
}
case (AWTDisplayList.POLYGON): {
renderer.drawPolygon(al, i);
break;
}
case (AWTDisplayList.WIREFRAME): {
renderer.drawWireframe(al, i);
break;
}
}
}
// "Once"-Befehle ausführen (pixelgrabben und Context
// befragen z.B.)!
boolean po = paintObserver;
if (onceList.count != 0) {
boolean pop = false;
for (int i = 0; i < onceList.count; i++) {
int mode = onceList.mode[i];
if (mode == AWTDisplayList.EXECUTE_ONCE && !po) {
result = renderer.executeGL(onceList, i);
onceList.delete(i);
pop = true;
}
}
paintObserver = pop;
}
al.rendered = true;
}
}
renderer.execute(GLBase.MODE_END_PAINTING, null);
swapBuffers();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean isVisible() {
return super.isVisible();
}
}
Quote from: AGP on September 27, 2025, 09:01:49 AMI tried quoting this line, but it didn't show up for some reason:
Have you actually tried this approach: https://www.jpct.net/wiki/index226d.html?title=Using_jPCT_in_Swing
This example seems to be using software rendering.
I don't think so. At the least the text says it's for the AWTGL-Renderer. As mentioned, I've never tried anything like that myself.
It's software. I managed to recompile just the one class. And the fix worked!
I've compiled a new version that includes that change (actually, the code above isn't 100% correct, because it does some changes to the existing code that make no sense, but I've omitted those).
Can you try this one and see if it also fixes your issue: https://www.jpct.de/download/net/jpctapi_133.zip (https://www.jpct.de/download/net/jpctapi_133.zip)
Thanks very much. I'll let you know before I sleep.
Exception in thread "main" java.lang.NoClassDefFoundError: com/threed/jpct/AWTJPCTCanvas$1
at com.threed.jpct.AWTJPCTCanvas.init(AWTJPCTCanvas.java:53)
at com.threed.jpct.AWTJPCTCanvas.<init>(AWTJPCTCanvas.java:38)
at com.threed.jpct.AWTGLRenderer.init(AWTGLRenderer.java:73)
at com.threed.jpct.FrameBuffer.enableRenderer(FrameBuffer.java:1154)
at com.threed.jpct.FrameBuffer.enableGLCanvasRenderer(FrameBuffer.java:739)
Opps, I missed that. I've updated the download, please try again.
It works, thanks a lot. I'm glad jpct is back at form. Actually, my standalone GLB importer is nearly perfect now. I have a script for splitting animations inside Blender and exporting multiple clips straight into jpct, so it's better than at form.
There's a little problem with the Canvas fix: it's not centering. I've only noticed once I updated jpct on a very centered (fighting) game. Check out the two images. One, naturally, is before the fix, the other one is after. Everything renders in the same place, it's just that the new version doesn't clip on the canvas space.
Also, we're missing a version of blit() because the following line isn't compiling: buffer.blit(splash1, 0, 0, 0, 0, splash1.getWidth(), splash1.getHeight(), buffer.getOutputWidth(), buffer.getOutputHeight(), 200, true);
https://www.dropbox.com/scl/fi/hlz31f7yuhgpusijmhjbw/CanvasFix_before.png?rlkey=rx8al2fz1ani7dz3khprxkgc5&st=cxyi3f3w&dl=0
https://www.dropbox.com/scl/fi/dinizqm379uh1rzaho1qf/CanvasFix_after.png?rlkey=axi07hpkewics8qbfmsgpp65m&st=9ww1q8pb&dl=0
I'm not sure what you mean by the missing blit call!? Missing compared to what? Actually, nothing has changed for ages in that regard.
That line compiles with old jpct versions and not with the latest one.
What about the canvas?
The last change regarding blit was 8 years ago when I moved from int-parameters to floats. No signature change other than that happened. I tried your line with the latest version, it compiles just fine. I'm not sure why you are having issues with it. What part of it is causing which problem exactly?
About the canvas: I don't know, if I even got the issue? Is it that it's somehow shifted to the left when it's supposed to be centered?
It's bizarre that you compiled. I'll see if I did something weird to the jar.
Yeah, it's like our repair just extended the drawing area. I'll show you the code tomorrow, but it should draw at the ends of the frame, but instead it's not centralized.