Integrating JME into SWT Shell

Hi all,



today I had a look at jme3 for the first time.

I really appreciate its level of documentation and from the showcases I see that it is powerful!



I’m building a visualization tool, which is in the form of an Eclipse plugin and I’d like to use jme3 for this.

However, being on eclipse means SWT…



Is it possible to include jme’s canvas into swt somehow?

Any suggestions on where to look?

Is there anyone who already tried doing this?



Thank you!

1 Like

EDIT: ignore this, and jump to this post

you mean like this:

[java]// app.start();

app.createCanvas();

app.startCanvas(true);

LwjglCanvas context = (LwjglCanvas) app.getContext();

Canvas c = context.getCanvas();[/java]

or should I give you a full example… working on it (first time doing this lol)

EDIT: ignore this, and jump to this post below



ok something rather simple:

to quit, close window, pressing ESC no worky

[java]package org.jme3.forum2;

/*

  • Copyright © 2009-2010 jMonkeyEngine
  • All rights reserved.

    *
  • Redistribution and use in source and binary forms, with or without
  • modification, are permitted provided that the following conditions are
  • met:

    *
    • Redistributions of source code must retain the above copyright
  • notice, this list of conditions and the following disclaimer.

    *
    • Redistributions in binary form must reproduce the above copyright
  • notice, this list of conditions and the following disclaimer in the
  • documentation and/or other materials provided with the distribution.

    *
    • Neither the name of ‘jMonkeyEngine’ nor the names of its contributors
  • may be used to endorse or promote products derived from this software
  • without specific prior written permission.

    *
  • THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  • "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  • TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  • PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  • CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  • EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  • PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  • PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  • LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  • NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  • SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    /

    import java.awt.Canvas;

    import java.util.prefs.BackingStoreException;

    import javax.swing.JFrame;

    import com.jme3.app.SimpleApplication;

    import com.jme3.bounding.BoundingBox;

    import com.jme3.bounding.BoundingVolume;

    import com.jme3.collision.CollisionResult;

    import com.jme3.collision.CollisionResults;

    import com.jme3.light.DirectionalLight;

    import com.jme3.material.Material;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.FastMath;

    import com.jme3.math.Quaternion;

    import com.jme3.math.Ray;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.Node;

    import com.jme3.scene.Spatial;

    import com.jme3.scene.debug.Arrow;

    import com.jme3.scene.shape.Box;

    import com.jme3.system.AppSettings;

    import com.jme3.system.lwjgl.LwjglCanvas;

    public class TestMousePickAgain2 extends SimpleApplication {

    public static void main(String[] args) throws BackingStoreException {

    TestMousePickAgain2 app = new TestMousePickAgain2();

    AppSettings aps = new AppSettings(true);

    aps.load(aps.getTitle());

    aps.setVSync(true);

    app.setShowSettings(false);

    app.setSettings(aps);

    // app.start();

    app.createCanvas();

    app.startCanvas(true);

    LwjglCanvas context = (LwjglCanvas) app.getContext();

    Canvas c = context.getCanvas();

    System.out.println©;

    JFrame j = new JFrame();

    j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    j.getContentPane().add©;

    j.setVisible(true);

    j.setBounds(0, 0, app.settings.getWidth(), app.settings.getHeight());

    }

    Node shootables;

    Geometry mark;

    private final Vector3f marksScale = new Vector3f(4, 4, 10f);

    @Override

    public void simpleInitApp() {

    flyCam.setDragToRotate(true);

    flyCam.setMoveSpeed(20f);

    initMark();

    /
    * create four colored boxes and a floor to shoot at: /

    shootables = new Node(“Shootables”);

    rootNode.attachChild(shootables);

    // shootables.attachChild(makeCube(“a Dragon”, -2f, 0f, 1f));

    // shootables.attachChild(makeCube(“a tin can”, 1f, -2f, 0f));

    // shootables.attachChild(makeCube(“the Sheriff”, 0f, 1f, -2f));

    // shootables.attachChild(makeCube(“the Deputy”, 1f, 0f, -4f));

    shootables.attachChild(makeFloor());

    shootables.attachChild(makeCharacter());

    rootNode.scale(3.5f, 4.2f, 4.7f);

    rootNode.rotate(FastMath.DEG_TO_RAD * 65f, FastMath.DEG_TO_RAD * 65f,

    FastMath.DEG_TO_RAD * 65f);

    rootNode.setLocalTranslation(0, 0, -29);

    cam.setLocation(new Vector3f(1.4989337f, 4.4191055f, 73.00994f));

    cam.setRotation(new Quaternion(-0.0011200219f, 0.9985451f,

    -0.022919897f, -0.048795838f));

    }

    @Override

    public void simpleUpdate(float tpf) {

    Vector3f origin = cam.getWorldCoordinates(

    inputManager.getCursorPosition(), 0.0f);

    Vector3f direction = cam.getWorldCoordinates(

    inputManager.getCursorPosition(), 0.3f);

    // System.out.println("origin: " + origin + " / dir: " + direction);

    direction.subtractLocal(origin).normalizeLocal();

    Ray ray = new Ray(origin, direction);

    CollisionResults results = new CollisionResults();

    shootables.collideWith(ray, results);

    if (results.size() > 0) {

    CollisionResult closest = results.getClosestCollision();

    Vector3f contact = closest.getContactPoint();

    Vector3f normal = closest.getContactNormal();

    contact = rootNode.worldToLocal(contact, null);

    rootNode.attachChild(mark);

    mark.setLocalTranslation(contact);

    Vector3f upVec = rootNode.worldToLocal(Vector3f.UNIT_Y, null);

    Quaternion q = new Quaternion();

    // makes Z axis be in the same direction as normal

    // (our mark arrow is on the Z axis)

    q.lookAt(normal, upVec.normalize());

    q = mark.getParent().getWorldRotation().inverse().mult(q);

    mark.setLocalRotation(q);

    // if I want to keep mark’s scale to 1, regardless of parents’

    // transforms:

    mark.setLocalScale(marksScale.divide(mark.getParent()

    .getWorldScale()));

    } else {

    rootNode.detachChild(mark);

    }

    }

    /
    * A cube object for target practice /

    protected Geometry makeCube(String name, float x, float y, float z) {

    Box box = new Box(new Vector3f(x, y, z), 1, 1, 1);

    Geometry cube = new Geometry(name, box);

    Material mat1 = new Material(assetManager,

    “Common/MatDefs/Misc/ShowNormals.j3md”);

    // mat1.setColor(“Color”, ColorRGBA.randomColor());

    cube.setMaterial(mat1);

    return cube;

    }

    /
    * A floor to show that the “shot” can go through several objects. /

    protected Geometry makeFloor() {

    Box box = new Box(new Vector3f(0, -4, -5), 15, .2f, 15);

    Geometry floor = new Geometry(“the Floor”, box);

    Material mat1 = new Material(assetManager,

    “Common/MatDefs/Misc/SolidColor.j3md”);

    mat1.setColor(“Color”, ColorRGBA.Gray);

    floor.setMaterial(mat1);

    return floor;

    }

    /
    * A red ball that marks the last spot that was “hit” by the “shot”. */

    protected void initMark() {

    Arrow arrow = new Arrow(Vector3f.UNIT_Z.mult(2f));

    arrow.setLineWidth(3);

    mark = new Geometry(“markGeo”, arrow);

    Material mark_mat = new Material(assetManager,

    “Common/MatDefs/Misc/SolidColor.j3md”);

    mark_mat.setColor(“Color”, ColorRGBA.Red);

    mark.setMaterial(mark_mat);

    }

    protected Spatial makeCharacter() {

    Node charNode = new Node();

    // load a character from jme3test-test-data

    Spatial golem = assetManager.loadModel(“Models/Oto/Oto.mesh.xml”);

    charNode.attachChild(golem);

    golem.setLocalScale(0.5f);

    golem.setLocalTranslation(-1.0f, -1.5f, -0.6f);

    BoundingVolume bv = golem.getWorldBound();

    if (bv.getType() == BoundingVolume.Type.AABB) {

    // WireBox boundingMesh =

    // new WireBox();

    // boundingMesh.fromBoundingBox( bbox );

    BoundingBox boundingBox = (BoundingBox) bv;

    Box boundingMesh = new Box(boundingBox.getCenter(),

    boundingBox.getXExtent(), boundingBox.getYExtent(),

    boundingBox.getZExtent());

    // boundingMesh.getBound()

    Geometry boundingGeo = new Geometry(“golem’s bounding volume”,

    boundingMesh);

    Material boundingMat = new Material(assetManager,

    “Common/MatDefs/Misc/WireColor.j3md”);

    boundingMat.setColor(“Color”, ColorRGBA.Red);

    boundingGeo.setMaterial(boundingMat);

    charNode.attachChild(boundingGeo);

    }

    // We must add a light to make the model visible

    DirectionalLight sun = new DirectionalLight();

    sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f).normalizeLocal());

    golem.addLight(sun);

    // golem.setLocalScale(0.8f);//XXX: when this, bbox remains

    charNode.setLocalScale(3, 4, 5);// when this, they both scale

    return charNode;

    }

    }[/java]

nevermind that, I just found this:

https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:swing_canvas



and here’s your example:

http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/awt/TestCanvas.java

Hi,



sorry for replying only now, I guess we live in different time zones :slight_smile:



Thank you for answering so fast!

I had a look at the example you posted. However that one covers embedding into Swing, while I need to embed into SWT.



Maybe I could use SWT_AWT bridge , which allows to embed awt and swing components into SWT.

I tried now but with no luck. The fact is that I don’t know if I’m doing it correctly since I’m new to both JME and SWT…



Now I’m at work and I don’t have the code with me, when I get back I’ll post it

1 Like

my bad, I thought SWT and Swing is the same thing, I must be newer than you :smiley:

You could wait for someone ie. @normen to reply, or meanwhile you could try asking something in #eclipse on freenode IRC

:slight_smile: thanks a lot for your pointers man, I’ll check on freenode and wait for other replies.

1 Like

No, no support for SWT in jME3. You’ll have to check SWT’s interoperability with heavyweight swing objects.

1 Like

It is possible to integrate SWT with LWJGL, it will require some low-level code however.

http://dev.eclipse.org/viewcvs/viewvc.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet195.java?view=markup



If you’re going to do it, make sure to remove all “GL11” calls. Essentially what you need to do is to create GLCanvas, then bind it to LWJGL via GLContext.useContext(), let jME3 do the rendering, then call canvas.swapBuffers().

1 Like
Momoko_Fan said:
Essentially what you need to do is to create GLCanvas, then bind it to LWJGL via GLContext.useContext(), let jME3 do the rendering, then call canvas.swapBuffers().


I don't think I understand what I need to do.
I know how to create a GLCanvas and render in it with LWJGL.
But I don't get the part about rendering with jME3 and swapping the buffers in the GLCanvas.

Can you please provide me an example or a more "for dummies" explanation?

Right now I'm busy implementing a new feature for my project. I will look more into the framework and the integration this weekend.

I posted this question early in case that the integration was easy to do. :D
In the weekend I'll improve my understanding of jME3's structure and come back to bug you guys if I don't succeed.

I am afraid there’s no simple explanation. This is a fairly low-level task, it is supposed to be difficult, in a way.

I would start by implementing LwjglContext, then copying code from LwjglAbstractDisplay and LwjglCanvas to have certain functionality.

1 Like

Ok, I’ll dig into this during the weekend and the week to come.

If I succeed in this, I’ll post my code for others.



Thanks!

In the end I started working on the integration only today.



I created this interface

[java]

public interface JmeCanvasContextSwt extends JmeContext {

public GLCanvas getCanvas ();

}

[/java]

and the class LwjglSwtCanvas extends LwjglAbstractDisplay implements JmeCanvasContextSwt



Basically all the code of this class comes from LwjglCanvas, but I replaced the awt canvas with a GLCanvas and I ported some awt specific calls to their swt counterparts.

However I’m stuck on the implementation for createContext(AppSettings)



[java]

protected void createContext ( AppSettings settings ) {

// In case canvas is not visible, we still take framerate

// from settings to prevent “100% CPU usage”

frameRate = settings.getFrameRate();



try {

// First create the pbuffer, if it is needed.

makePbufferAvailable();



if ( renderable.get() ) {

// if the pbuffer is currently active,

// make sure to deactivate it

if ( pbuffer.isCurrent() ) {

pbuffer.releaseContext();

}



Display.setVSyncEnabled( settings.isVSync() );

Display.setParent( canvas );

Display.create( acquirePixelFormat(), pbuffer );



// because the display is a different opengl context

// must reset the context state.

renderer.invalidateState();

}

else {

pbuffer.makeCurrent();

}

// At this point, the OpenGL context is active.



if ( runningFirstTime ) {

// THIS is the part that creates the renderer.

// It must always be called, now that we have the pbuffer workaround.

initContextFirstTime();

runningFirstTime = false;

}

}

catch (LWJGLException ex) {

listener.handleError( “Failed to initialize OpenGL context”, ex );

// TODO: Fix deadlock that happens after the error (throw runtime exception?)

}

[/java]



This code comes from LwjglCanvas.



My guess is that here I should do what you mentioned about having jme3 handle the rendering, but I have no idea on how to proceed.

I really need some help for this…

Right now the canvas is using a pbuffer as a “backup” in case the canvas component becomes invisible. So just keep all the pbuffer stuff.

What you probably need to get rid of, is usage of the “Display” class. All of the appropriate configuration needs to be set on the SWT canvas, and then you “bind” the LWJGL to the SWT canvas by using GLContext.setContext().



See the contents of the main() method here:

http://dev.eclipse.org/viewcvs/viewvc.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet195.java?view=markup

I replaced Display part with the following

[java]

@Override

protected void createContext ( AppSettings settings ) {

// In case canvas is not visible, we still take framerate

// from settings to prevent “100% CPU usage”

frameRate = settings.getFrameRate();



try {

// First create the pbuffer, if it is needed.

makePbufferAvailable();



if ( renderable.get() ) {

// if the pbuffer is currently active,

// make sure to deactivate it

if ( pbuffer.isCurrent() ) {

pbuffer.releaseContext();

}



GLData data = new GLData();

data.doubleBuffer = true;

canvas = new SwtCanvas( data );



canvas.setCurrent();

try {

GLContext.useContext( canvas );

}

catch (LWJGLException e) {

e.printStackTrace();

}





// because the display is a different opengl context

// must reset the context state.

renderer.invalidateState();

}

else {

pbuffer.makeCurrent();

}

// At this point, the OpenGL context is active.



if ( runningFirstTime ) {

// THIS is the part that creates the renderer.

// It must always be called, now that we have the pbuffer workaround.

initContextFirstTime();

runningFirstTime = false;

}

}

catch (LWJGLException ex) {

listener.handleError( “Failed to initialize OpenGL context”, ex );

// TODO: Fix deadlock that happens after the error (throw runtime exception?)

}

}

[/java]



From my very limited understanding, I’d say that now I have a JmeContext using swt and lwjgl.

However I have no idea where the rendering is happening, so where I should put:



[java]

display.asyncExec(new Runnable() {

public void run() {

canvas.swapBuffers();

}

});

[/java]



Moreover, I need to extend JmeSystem and override newContextLwjgl(settings, contextType), in order to create my context, correct?



Your help is really appreciated, thanks!

You need to replace this part in LwjglAbstractDisplay:

[java]if (autoFlush){

Display.update(false);

}else{

Display.processMessages();

Thread.sleep(50);

// add a small wait

// to reduce CPU usage

}[/java]

Essentially Display.update() ==> canvas.swapBuffers()

Also, make sure to get rid of all calls to the Display class as they are going to crash. It seems like it might actually be better to implement LwjglContext instead of LwjglAbstractDisplay in your case.

About JmeSystem, you don’t have to override newContextLwjgl, set AppSettings.setCustomRenderer() to your context class

Hi momoko,

this morning I’ve done all the modifications you suggested and tried to run a jme3 app (I think HelloJME3, or another simple example) into an eclipse swt view.

The result is that when jme tries to start, I get the following error:

[java]

2011-05-14 11:54:41.037 java[29279:903] [Java CocoaComponent compatibility mode]: Enabled

2011-05-14 11:54:41.040 java[29279:903] [Java CocoaComponent compatibility mode]: Setting timeout for SWT to 0.100000

2011-05-14 11:54:42.174 java[29279:14503] *** -[NSConditionLock unlock]: lock (<NSConditionLock: 0x1006e8dc0> ‘(null)’) unlocked when not locked

2011-05-14 11:54:42.175 java[29279:14503] *** Break on _NSLockError() to debug.

2011-05-14 11:54:42.284 java[29279:14503] *** -[NSConditionLock unlock]: lock (<NSConditionLock: 0x10063dd90> ‘(null)’) unlocked when not locked

2011-05-14 11:54:42.284 java[29279:14503] *** Break on _NSLockError() to debug.

2011-05-14 11:54:42.385 java[29279:14503] *** -[NSConditionLock unlock]: lock (<NSConditionLock: 0x10063c410> ‘(null)’) unlocked when not locked

2011-05-14 11:54:42.386 java[29279:14503] *** Break on _NSLockError() to debug.

2011-05-14 11:54:42.510 java[29279:14503] *** -[NSConditionLock unlock]: lock (<NSConditionLock: 0x120a06e60> ‘(null)’) unlocked when not locked

2011-05-14 11:54:42.510 java[29279:14503] *** Break on _NSLockError() to debug.

2011-05-14 11:54:42.613 java[29279:14503] *** -[NSConditionLock unlock]: lock (<NSConditionLock: 0x10014ab80> ‘(null)’) unlocked when not locked

2011-05-14 11:54:42.613 java[29279:14503] *** Break on _NSLockError() to debug.

2011-05-14 11:54:42.718 java[29279:14503] *** -[NSConditionLock unlock]: lock (<NSConditionLock: 0x1006e4620> ‘(null)’) unlocked when not locked

2011-05-14 11:54:42.718 java[29279:14503] *** Break on _NSLockError() to debug.

2011-05-14 11:54:42.822 java[29279:14503] *** -[NSConditionLock unlock]: lock (<NSConditionLock: 0x120a19dc0> ‘(null)’) unlocked when not locked

2011-05-14 11:54:42.822 java[29279:14503] *** Break on _NSLockError() to debug.

2011-05-14 11:54:42.923 java[29279:14503] *** -[NSConditionLock unlock]: lock (<NSConditionLock: 0x120a2df90> ‘(null)’) unlocked when not locked

2011-05-14 11:54:42.923 java[29279:14503] *** Break on _NSLockError() to debug.

2011-05-14 11:54:43.026 java[29279:14503] *** -[NSConditionLock unlock]: lock (<NSConditionLock: 0x1006f75d0> ‘(null)’) unlocked when not locked

2011-05-14 11:54:43.026 java[29279:14503] *** Break on _NSLockError() to debug.

[/java]

I created the HelloJME3 project as described at https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:setting_up_jme3_in_eclipse

I noticed that if I simply add swt’s jar on the buildpah, without changing anything in the code of the working example, I get the same error.

I’m running OSX 10.6.7, Eclipse 3.6.2 on a MacBookPro4.1.

This is the output of java --version

java version “1.6.0_24”

Java™ SE Runtime Environment (build 1.6.0_24-b07-334-10M3326)

Java HotSpot™ 64-Bit Server VM (build 19.1-b02-334, mixed mode)



Please don’t tell me that there is no way to fix this…

By the way, the framework I’ve been using until now, which is called Ardor3d, works with swt both on coccoa and gtk.

This means that in some way it is possible to get this to work…



EDIT

I noticed that by setting app.setShowSettings(false) the problem is solved. It looks like the Display context type used when creating the window to show the settings isn’t appreciated by coccoa when in conjunction with SWT :slight_smile:

I still have some other issues, but I’m practically sure that they are related to my code.

Great. :slight_smile:

Yeah make sure there’s no uses of the Display class

Hi momoko,



After fixing the various errors, I got to the point where I got no exceptions at all, but the canvas is not shown.

I can’t discern if this is a problem on swt’s side or if I’m doing something wrong in my class.

In case that the error is in my code, I’m sure that someone who knows the framework, would spot conceptual mistakes pretty easily. Even though I know it sucks to ask someone to view your code, I need this integration too much for not trying to ask…



[java]

package jme3.swt;



import org.eclipse.swt.SWT;

import org.eclipse.swt.opengl.GLCanvas;

import org.eclipse.swt.opengl.GLData;

import org.eclipse.swt.widgets.Shell;



public class SwtCanvas extends GLCanvas {



public SwtCanvas(GLData data) {

super( new Shell(), SWT.NONE, data );

setCurrent();

}



public int getWidth () {

return getSize().x;

}



public int getHeight() {

return getSize().y;

}

}

[/java]



[java]

package jme3.swt;



import org.eclipse.swt.opengl.GLCanvas;

import com.jme3.system.JmeContext;



public interface JmeCanvasContextSwt extends JmeContext {



public GLCanvas getCanvas ();

}

[/java]



These two look ok to me…



[java]

package jme3.swt;



import java.util.concurrent.BrokenBarrierException;

import java.util.concurrent.CyclicBarrier;

import java.util.concurrent.atomic.AtomicBoolean;

import java.util.logging.Level;

import java.util.logging.Logger;

import org.eclipse.swt.opengl.GLCanvas;

import org.eclipse.swt.opengl.GLData;

import org.eclipse.swt.widgets.Display;

import org.lwjgl.LWJGLException;

import org.lwjgl.Sys;

import org.lwjgl.input.Mouse;

import org.lwjgl.opengl.GLContext;

import org.lwjgl.opengl.OpenGLException;

import org.lwjgl.opengl.Pbuffer;

import org.lwjgl.opengl.PixelFormat;

import org.lwjgl.opengl.Util;

import com.jme3.system.AppSettings;

import com.jme3.system.JmeSystem;

import com.jme3.system.lwjgl.LwjglAbstractDisplay;

import com.jme3.system.lwjgl.LwjglDisplay;



public class LwjglSwtCanvas extends LwjglAbstractDisplay implements JmeCanvasContextSwt {



private static final Logger logger = Logger.getLogger( LwjglDisplay.class.getName() );

private SwtCanvas canvas;

private int width;

private int height;



private final AtomicBoolean needRestoreCanvas = new AtomicBoolean( false );

private final AtomicBoolean needDestroyCanvas = new AtomicBoolean( false );

private final CyclicBarrier actionRequiredBarrier = new CyclicBarrier( 2 );



private Thread renderThread;

private boolean runningFirstTime = true;

private boolean mouseWasGrabbed = false;



private Pbuffer pbuffer;

private PixelFormat pixelFormat;



public LwjglSwtCanvas() {

super();

setupCanvas();

}



@Override

public Type getType () {

return Type.Canvas;

}



@Override

public void create ( boolean waitFor ) {

if ( renderThread == null ) {

logger.log( Level.INFO, “MAIN: Creating OGL thread.” );



renderThread = new Thread( LwjglSwtCanvas.this, “LWJGL Renderer Thread” );

renderThread.start();

}

// do not do anything.

// superclass’s create() will be called at initInThread()

if ( waitFor )

waitFor( true );

}



@Override

public void setTitle ( String arg0 ) {

// this is not for a windowed application, therefore there’s no title to set

}



@Override

public void restart () {

frameRate = settings.getFrameRate();

// TODO: Handle other cases, like change of pixel format, etc.

}



@Override

public SwtCanvas getCanvas () {

return canvas;

}



@Override

public void run () {

if ( listener == null )

throw new IllegalStateException( “SystemListener is not set on context!” + “Must set with JmeContext.setSystemListner().” );



logger.log( Level.INFO, “Using LWJGL {0}”, Sys.getVersion() );

initInThread();

while ( true ) {

if ( renderable.get() ) {

// if ( Display.isCloseRequested() )

if ( canvas.isDisposed() )

listener.requestClose( false );



// if ( wasActive != Display.isActive() ) {

if ( wasActive != canvas.isFocusControl() ) {

if ( !wasActive ) {

listener.gainFocus();

timer.reset();

wasActive = true;

}

else {

listener.loseFocus();

wasActive = false;

}

}

}



runLoop();



if ( needClose.get() )

break;

}

deinitInThread();

}



@Override

protected void runLoop () {

if ( needDestroyCanvas.getAndSet( false ) ) {

// Destroy canvas

logger.log( Level.INFO, “OGL: Received destroy request! Complying…” );

try {

listener.loseFocus();

pauseCanvas();

}

finally {

try {

// Required to avoid deadlock if an exception occurs

actionRequiredBarrier.await();

}

catch (InterruptedException ex) {

logger.log( Level.SEVERE, "OGL: Interrupted! ", ex );

}

catch (BrokenBarrierException ex) {

logger.log( Level.SEVERE, "OGL: Broken barrier! ", ex );

}

}

}

else if ( needRestoreCanvas.getAndSet( false ) ) {

// Put canvas back online

logger.log( Level.INFO, “OGL: Canvas is now visible! Re-initializing…” );

restoreCanvas();

listener.gainFocus();

}



if ( Display.getDefault().isDisposed() || canvas.isDisposed() )

return;

Display.getDefault().syncExec( new Runnable() {

@Override

public void run () {

if ( width != canvas.getWidth() || height != canvas.getHeight() ) {

width = canvas.getWidth();

height = canvas.getHeight();

if ( listener != null )

listener.reshape( width, height );

}

}

} );



if ( !created.get() )

throw new IllegalStateException();



listener.update();



// All this does is call swap buffers

// If the canvas is not active, there’s no need to waste time

// doing that …

if ( renderable.get() ) {

assert checkGLError();



// calls swap buffers, etc.

try {

if ( autoFlush ) {

canvas.swapBuffers();

}

else {

// Display.processMessages();

Thread.sleep( 50 );

// add a small wait

// to reduce CPU usage

}

}

catch (Throwable ex) {

listener.handleError( “Error while swapping buffers”, ex );

}

}



if ( frameRate > 0 ) {

// Display.sync(frameRate);

}



if ( renderable.get() ) {

if ( autoFlush ) {

// check input after we synchronize with framerate.

// this reduces input lag.

// Display.processMessages();

}

}



// Subclasses just call GLObjectManager clean up objects here

// it is safe … for now.

renderer.onFrame();

}



private void pauseCanvas () {

if ( Mouse.isCreated() && Mouse.isGrabbed() ) {

Mouse.setGrabbed( false );

mouseWasGrabbed = true;

}



logger.log( Level.INFO, “OGL: Canvas will become invisible! Destroying …” );



renderable.set( false );

destroyContext();

}



/**

  • Called to restore the canvas.

    */

    private void restoreCanvas () {

    logger.log( Level.INFO, “OGL: Waiting for canvas to become displayable…” );

    while ( !canvas.isVisible() ) { // isVisible() could be the replacement for isDisplyable()

    try {

    Thread.sleep( 10 );

    }

    catch (InterruptedException ex) {

    logger.log( Level.SEVERE, "OGL: Interrupted! ", ex );

    }

    }



    logger.log( Level.INFO, “OGL: Creating display…” );



    // Set renderable to true, since canvas is now displayable.

    renderable.set( true );

    createContext( settings );



    logger.log( Level.INFO, “OGL: Display is active!” );



    if ( Mouse.isCreated() && mouseWasGrabbed ) {

    Mouse.setGrabbed( true );

    mouseWasGrabbed = false;

    }

    Display.getDefault().asyncExec( new Runnable() {

    public void run () {

    canvas.setFocus(); // setFocus is confirmed to be the perfect replacement for WT’s requestFocus

    }

    } );

    }



    @Override

    protected void initInThread () {

    try {

    if ( !JmeSystem.isLowPermissions() ) {

    // Enable uncaught exception handler only for current thread

    Thread.currentThread().setUncaughtExceptionHandler( new Thread.UncaughtExceptionHandler() {



    public void uncaughtException ( Thread thread, Throwable thrown ) {

    listener.handleError( "Uncaught exception thrown in " + thread.toString(), thrown );

    }

    } );

    }



    // For canvas, this will create a pbuffer,

    // allowing us to query information.

    // When the canvas context becomes available, it will

    // be replaced seamlessly.

    settings.setRenderer( AppSettings.LWJGL_OPENGL2 );

    createContext( settings );

    printContextInitInfo();



    created.set( true );

    }

    catch (Exception ex) {

    try {

    if ( canvas != null )

    canvas.dispose();

    }

    catch (Exception ex2) {

    logger.log( Level.WARNING, null, ex2 );

    }



    listener.handleError( “Failed to create display”, ex );

    return; // if we failed to create display, do not continue

    }



    super.internalCreate();

    listener.initialize();

    }



    @Override

    protected void deinitInThread () {

    destroyContext();



    listener.destroy();

    logger.info( “Display destroyed.” );

    super.internalDestroy();

    }



    /**
  • It seems it is best to use one pixel format for all shared contexts.
  • See http://developer.apple.com/library/mac/#qa/qa1248/_index.html.

    */

    protected PixelFormat acquirePixelFormat () {

    if ( pixelFormat == null ) {

    pixelFormat = new PixelFormat( settings.getBitsPerPixel(), 0, settings.getDepthBits(), settings.getStencilBits(), settings.getSamples() );

    }

    return pixelFormat;

    }



    /**
  • Makes sure the pbuffer is available and ready for use

    */

    protected void makePbufferAvailable () throws LWJGLException {

    if ( pbuffer == null || pbuffer.isBufferLost() ) {

    if ( pbuffer != null && pbuffer.isBufferLost() ) {

    logger.log( Level.WARNING, "PBuffer was lost!" );

    pbuffer.destroy();

    }

    // Let the implementation choose an appropriate pixel format.

    pbuffer = new Pbuffer( 1, 1, new PixelFormat( 0, 0, 0, 0, 0 ), null );

    //pbuffer = new Pbuffer(1, 1, acquirePixelFormat(), null);

    logger.log( Level.INFO, "OGL: Pbuffer has been created" );

    }

    }



    /**
  • This is called:
    1. When the context thread starts
    1. Any time the canvas becomes displayable again.

      */

      @Override

      protected void createContext ( AppSettings settings ) {

      // In case canvas is not visible, we still take framerate

      // from settings to prevent "100% CPU usage"

      frameRate = settings.getFrameRate();



      try {

      // First create the pbuffer, if it is needed.

      makePbufferAvailable();



      if ( renderable.get() ) {

      // if the pbuffer is currently active,

      // make sure to deactivate it

      if ( pbuffer.isCurrent() ) {

      pbuffer.releaseContext();

      }



      if ( canvas != null && !canvas.isDisposed() )

      setupCanvas();



      // because the display is a different opengl context

      // must reset the context state.

      renderer.invalidateState();

      }

      else {

      pbuffer.makeCurrent();

      }

      // At this point, the OpenGL context is active.



      if ( runningFirstTime ) {

      // THIS is the part that creates the renderer.

      // It must always be called, now that we have the pbuffer workaround.

      initContextFirstTime();

      runningFirstTime = false;

      }

      }

      catch (LWJGLException ex) {

      listener.handleError( "Failed to initialize OpenGL context", ex );

      // TODO: Fix deadlock that happens after the error (throw runtime exception?)

      }

      }



      /**
  • This is called:
    1. When the context thread ends
    1. Any time the canvas becomes non-displayable

      */

      protected void destroyContext () {

      if ( !canvas.isDisposed() )//&& canvas.isVisible() )

      canvas.dispose();



      try {

      // The canvas is no longer visible,

      // but the context thread is still running.

      if ( !needClose.get() ) {

      // MUST make sure there’s still a context current here …

      // Display is dead, make pbuffer available to the system

      makePbufferAvailable();



      // pbuffer is now available, make it current

      pbuffer.makeCurrent();



      // invalidate the state so renderer can resume operation

      renderer.invalidateState();

      }

      else {

      // The context thread is no longer running.

      // Destroy pbuffer.

      if ( pbuffer != null ) {

      pbuffer.destroy();

      }

      }

      }

      catch (LWJGLException ex) {

      listener.handleError( “Failed make pbuffer available”, ex );

      }

      }





      private void setupCanvas () {

      GLData data = new GLData();

      data.doubleBuffer = true;

      this.canvas = new SwtCanvas( data );



      try {

      GLContext.useContext( canvas );

      }

      catch (LWJGLException e) {

      e.printStackTrace();

      }

      }



      }



      [/java]



      I produced this implementation of JmeCanvasContextSwt by copying from LwjglCanvas and by overriding those methods in LwjglAbstractDisplay, which were using lwjgl’s Display.



      This is how I create the application:

      [java]

      MyGame app = new MyGame();

      AppSettings settings = new AppSettings( true );

      settings.setCustomRenderer( LwjglSwtCanvas.class );

      settings.setBitsPerPixel( 24 );

      settings.setSamples( 8 );

      app.setSettings( settings );

      app.setShowSettings( false );



      app.createCanvas();



      canvas = ((LwjglSwtCanvas) app.getContext()).getCanvas();

      // attach canvas to eclipse’s swt view

      canvas.setParent( wrapper );



      app.startCanvas();

      [/java]



      The application starts and I can see from the logged output that the scene is created:



      [java]

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Gui Node)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (BitmapFont) attached to this node (null)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (null) attached to this node (Statistics View)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (Statistics View) attached to this node (Gui Node)

      May 15, 2011 5:17:24 PM com.jme3.renderer.Camera <init>

      INFO: Camera created (W: 1, H: 1)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (Box) attached to this node (Root Node)

      May 15, 2011 5:17:24 PM com.jme3.scene.Node attachChild

      INFO: Child (Box 2) attached to this node (Root Node)

      May 15, 2011 5:17:25 PM com.jme3.material.MaterialDef <init>

      INFO: Loaded material definition: Bloom

      May 15, 2011 5:17:25 PM com.jme3.material.MaterialDef <init>

      INFO: Loaded material definition: Bloom

      May 15, 2011 5:17:25 PM com.jme3.material.MaterialDef <init>

      INFO: Loaded material definition: Bloom

      May 15, 2011 5:17:25 PM com.jme3.material.MaterialDef <init>

      INFO: Loaded material definition: Bloom Final

      [/java]



      I tried running the same app with the usual canvas context, with no swt and it works perfectly.

      The code I use to attach the canvas to eclipse’s view is the same I’m using in the current version of my plugin.



      I really feel like I missed some fundamental call in my LwjglSwtCanvas, but not knowing the framework design, I’m unable to progress any further.

Yes … Right now its using the pbuffer workaround to start. But after that you need to switch to the canvas once it becomes visible.

In LwjglCanvas you will notice the addNotify() and removeNotify() callbacks are overloaded to detect when the canvas becomes visible and set the “needRestoreCanvas” boolean.