SimpleCanvasImpl and GameTaskQueueManager problem

Hi,

in my project I took the famous RenParticleEditor and transformed it so that now it extends JPanel (originally it extends JFrame).

I add this panel to a JTabbedPane in a class that extends JFrame. This tab is not initially selected.

My program opens several frames with such a panel.

So to sum up - I have several JFrames, each has a JTabbedPane inside and one of the tabs contains JPanel with canvas.



The problem begins when I click on this tab in any JFrame…

In constructor of the class that contains canvas I execute (same as in the RenParticleEditor):


   Callable<Void> exe = new Callable<Void>() {
      public Void call()
      {
         if (prefs.getBoolean("yUp", true))
            yUpCamera();
         else
            zUpCamera();
         return null;
      }
   };
   GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER).enqueue(exe);



The problem is that it seems that all instances of my class share the same GameTaskQueueManager, because when I set only one tab with canvas to visible, than yUpCamera() is executed in all instances at once. Then for all frames where JPanel with canvas in not visible I get java.lang.NullPointerException, because:


   impl.getRenderer()


returns null.

What can I do to solve this problem, so that GameTaskQueueManager executes yUpCamera() only in the appropriate instance, not inn all instances at once.

If you want me to paste more code, go ahead and ask, but I believe it is not necessary since you can take a look at RenParticleEditor - it is peaty much the same in my implementation.

jME is really not setup for multiple rendering context like what you are trying to do…



What I would suggest is either looking at momoko_fans context system (search forum for it) or attack the problem w/ jME usage as a singleton type thing (use one canvas and maintain camera data for each tab)…

Well, I tried momoko_fans context system and in my case I couldn't overcome the exception:


Exception in thread "AWT-EventQueue-1" java.lang.NullPointerException
   at com.jme.renderer.lwjgl.LWJGLCamera.doFrustumChange(LWJGLCamera.java:193)



your workaround for this issue doesn't work for me...

I have to thank you anyway, because I didn't give my original solution and I found a workaround  XD
I will explain in case this will help someone in the future.

As I already wrote first problem was with yUpCamera. In the RenParticleEditor this method is called in the constructor, because it assumes that canvas will be immediately displayed. In my case the tab that contains the canvas is not initially active. And because canvas are not visible, impl.getRenderer() returns null in the constructor.

A workaround for this is to wrap yUpCamera in a thread that waits for the impl.getRenderer()  to return the renderer.


private class CameraScheduller extends Thread {
   public CameraScheduller() {
      super();
   }

   public void run() {
      yUpCamera();
   }

   private void yUpCamera() {
      Camera cam = null;
      while (cam == null)
         try {
            cam = impl.getCamera();
            Thread.sleep(100);
         }
         catch (InterruptedException e)
         {}

      camhand.worldUpVector.set(Vector3f.UNIT_Y);
      cam.getLocation().set(0, 850, -850);
      camhand.recenterCamera();
      grid.getLocalRotation().fromAngleAxis(0, Vector3f.UNIT_X);
      grid.updateRenderState();
      prefs.putBoolean("yUp", true);
   }
}

public void yUpCamera() {
   new CameraScheduller().start();
}



The last thing is to call this thread in the JTabbedPane listener when the tab with canvas is activated.


tabbedPane.addChangeListener(new ChangeListener() {
   public void stateChanged(ChangeEvent changeEvent) {
      // check if we selected the canvas pane
      if (statePanel.equals(tabbedPane.getComponentAt(tabbedPane.getSelectedIndex())))
         SwingUtilities.invokeLater(new Runnable() {
            public void run() {
               try {
                  clusteringStatePanel.setupCamera();
               }
               catch (Exception e) {
                  e.printStackTrace();
               }
            }
         });
   }
});




Second problem occurs with doResize - it throws java.lang.NullPointerException in GL11.glViewport.
This one was really tough! The only solution I found is to hide all the canvas except the one that will be resized, than resize the canvas and finally show the canvas that were hidden.

I will post only the essential parts of the code. First of all you will have to store references to all canvas in a way that they can be accessed for a canvas itself. I solved it creating a singleton factory that stores all the canvas that it has created.
Than you created two methods:


public static void setSingleViewerVisible(UUID viewerUUID) {
   for (ClusteringViewer clusteringViewer : getClusteringViewers())
      if (clusteringViewer.viewerUUID.equals(viewerUUID))
         clusteringViewer.setCanvasVisible(true);
      else
         clusteringViewer.setCanvasVisible(false);
}

public static void setAllViewersVisible(boolean visible) {
   for (ClusteringViewer clusteringViewer : getClusteringViewers())
      clusteringViewer.setCanvasVisible(visible);
}



The lest step is to modify the doResize:


protected void doResize() {
   if (impl != null)
      if (impl.getCamera() != null) {
         // hide canvas in all instances but the one that is being resized
         ClusteringViewerFactory.setSingleViewerVisible(parentUUID);
         
         Callable<Void> resize = new Callable<Void>() {
            public Void call() {
               // resize the canvas
               impl.resizeCanvas(glCanvas.getWidth(), glCanvas.getHeight());
               impl.getCamera().setFrustumPerspective(45.0f, (float) glCanvas.getWidth() / (float) glCanvas.getHeight(), 1, 10000);

               // show all canvas
               Callable<Void> resizeFinished = new Callable<Void>() {
                  public Void call()
                  {
                     ClusteringViewerFactory.setAllViewersVisible(true);
                     return null;
                  }
               };
               GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER).enqueue(resizeFinished);
               return null;
            }
         };
         GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER).enqueue(resize);
      }
}



Hope this helps someone, because I wasted whole day working it out....