LWJGL 2.0 Applets

I've heard of a feature in Lwjgl 2.0 that applets will be faster if implemented without AWTGLCanvas and just use Display.setParent().  Has anybody figured out an implementation to do that in JME?



I wrote this a while ago:

Looking at the code a bit more, I notice a couple things, there are only two types of JMECanvas, which utilize LWJGL, a AWT and a SWT version.  The AWT version (LWJGLCanvas.java) is nice, but in order to do the new Display.setParent in LWJGL 2.0 we'll either need to rewrite this because it uses AWTGLCanvas.  Or the another option is to somehow use the SWT version of LWJGL JMECanvas (LWJGLSWTCanvas.java) since it use GLCanvas?  Yet another option is to write a new JMECanvas entirely for this purpose and have it extend java.awt.Canvas like the new GearsApplets does.

Along the same lines, I think the functionality of Implementors for JMECanvas needs to be changed, since it's the Applet's method start in the new GearsApplet that causes the update.  I think this is right?


Scrapping implementers of JMECanvas seems to me like a good idea , but maybe there is some better alternative?  I've tried for several hours to think of a good way, but I can't think of any better options, has anybody else done some work to implement Display.setParent().

Anybody there?



Does anybody have any thoughts on LWJGL 2.0 applets?

I think this is something you will have to test yourself (I am curious about the performance difference though :))…

I say try write one using this new method, even if it doesn't exactly comply to the JMECanvas design, hack it up. Then we will have a better idea on what needs to change to accommodate it, and it gives us a chance to give you better input.

Cool.  Somebody else working with applets!



I'm going to try transitioning from JME1 to 2 over this weekend.  Since I'll probably run into issues while porting it will probably make sense to see if there's a better way at the same time.



I'll let you know how I get on.

I'm having troubles, but at least I got somewhere.  I'll give you guys the code and see if you can find my mistake.  For me this shows an applet with a black screen and the mouse disappears into it.  The code from the test should show the same stuff as FlugRush Lesson 2, but for now it's showing just black screen without errors.



A BetterLWJGLCanvas.java

package com.jmex.awt.lwjgl;

import java.awt.Canvas;
import java.util.logging.Logger;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.PixelFormat;

import com.jme.system.DisplaySystem;
import com.jme.system.canvas.BetterJMECanvas;
import com.jme.system.lwjgl.LWJGLDisplaySystem;

public class BetterLWJGLCanvas extends Canvas implements BetterJMECanvas {
   
private static final Logger logger = Logger.getLogger(BetterLWJGLCanvas.class
        .getName());

private static final long serialVersionUID = 1L;

private boolean updateInput = false;

private double syncNS = 0;
private int syncRate = 0;

private boolean drawWhenDirty = false;
private boolean dirty = true;

public BetterLWJGLCanvas() throws LWJGLException {
    super();
}

private static PixelFormat generatePixelFormat() {
    return ((LWJGLDisplaySystem) DisplaySystem.getDisplaySystem())
            .getFormat();
}
/*
 * (non-Javadoc)
 * @see com.jmex.awt.JMECanvas#doUpdateInput()
 */
public boolean isUpdateInput() {
    return updateInput;
}

/*
 * (non-Javadoc)
 * @see com.jmex.awt.JMECanvas#setUpdateInput(boolean)
 */
public void setUpdateInput(boolean doUpdate) {
    updateInput = doUpdate;
}

public void setTargetRate(int fps) {
    this.syncRate = fps;
    this.syncNS = 1000000000.0 / syncRate;
}

public int getTargetSyncRate() {
    return syncRate;
}

public void setDrawWhenDirty(boolean whenDirty) {
    this.drawWhenDirty = whenDirty;
}

public boolean isDrawWhenDirty() {
    return drawWhenDirty;
}

public void makeDirty() {
    dirty = true;
}

}



A new BaseJMEApplet or BetterBaseJMEApplet

package com.jmex.awt.applet;

import java.applet.Applet;
import java.awt.BorderLayout;
import java.util.logging.Level;
import java.util.logging.Logger;

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

import com.jme.app.AbstractGame;
import com.jme.input.InputSystem;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.system.lwjgl.LWJGLDisplaySystem;
import com.jme.util.ThrowableHandler;
import com.jmex.awt.lwjgl.BetterLWJGLCanvas;

public abstract class BetterBaseJMEApplet extends Applet {
   private static final long serialVersionUID = 6894421316159346138L;
    private static final Logger logger = Logger.getLogger(BetterBaseJMEApplet.class
            .getName());   
   protected ThrowableHandler throwableHandler;
   
   protected BetterLWJGLCanvas display_parent;
   protected Thread gameThread;
   
    /** Flag for running the system. */
    protected boolean finished = false;
   protected DisplaySystem display;
   
    /** Renderer used to display the game */

   public void init() {
        logger.info("Applet initialized.");
      setLayout(new BorderLayout());
      try {
         display_parent = new BetterLWJGLCanvas();
         display_parent.setSize(getWidth(),getHeight());
         add(display_parent);
         display_parent.setFocusable(true);
         display_parent.requestFocus();
         display_parent.setIgnoreRepaint(true);
         //setResizable(true);
         setVisible(true);
      } catch (Exception e) {
         System.err.println(e);
         throw new RuntimeException("Unable to create display");
      }
   }   
   
    /**
     * The simplest main game loop possible: render and update as fast as
     * possible.
     */
    public final void start() {
        logger.info("Applet started.");
      gameThread = new Thread() {
         public void run() {
            try {
               logger.info("display_parent.isDisplayable() = " + display_parent.isDisplayable());
               Display.setParent(display_parent);
               //Display.setVSyncEnabled(true);
               Display.create();
               //initGL();
            } catch (LWJGLException e) {
               e.printStackTrace();
            }
            gameLoop();
         }
      };
      gameThread.start();
    }
   
   public void destroy() {
        if (display != null)
            display.close();
      remove(display_parent);
      super.destroy();
      logger.info("Clear up");
   }
   
   public void gameLoop() {
        try {
            if (!finished) {
               
                display = DisplaySystem.getDisplaySystem();
               
                ((LWJGLDisplaySystem)display).initForApplet(getWidth(), getHeight());
               
                initSystem();               

               
                assertDisplayCreated();
               
                initGame();

                // main loop
                while (!finished && !display.isClosing()) {
                    // handle input events prior to updating the scene
                    // - some applications may want to put this into update of
                    // the game state
                    InputSystem.update();

                    // update game state, do not use interpolation parameter
                    update(-1.0f);

                    // render, do not use interpolation parameter
                    render(-1.0f);

                    // swap buffers
                    display.getRenderer().displayBackBuffer();

                    Thread.yield();
                }
            }
        } catch (Throwable t) {
            logger.logp(Level.SEVERE, this.getClass().toString(), "start()", "Exception in game loop", t);
            if (throwableHandler != null) {
            throwableHandler.handle(t);
         }
        }
   }

    /**
     * Get the exception handler if one hs been set.
     *
     * @return the exception handler, or {@code null} if not set.
     */
   protected ThrowableHandler getThrowableHandler() {
      return throwableHandler;
   }

   /**
    *
    * @param throwableHandler
    */
   protected void setThrowableHandler(ThrowableHandler throwableHandler) {
      this.throwableHandler = throwableHandler;
   }
   
    /**
     * <code>assertDisplayCreated</code> determines if the display system was
     * successfully created before use.
     *
     * @throws JmeException
     *             if the display system was not successfully created
     */
    protected void assertDisplayCreated() throws JmeException {
        if (display == null) {
            logger.severe("Display system is null.");

            throw new JmeException("Window must be created during"
                    + " initialization.");
        }
    }

    /**
     * @param interpolation
     *            unused in this implementation
     * @see AbstractGame#update(float interpolation)
     */
    protected abstract void update(float interpolation);

    /**
     * @param interpolation
     *            unused in this implementation
     * @see AbstractGame#render(float interpolation)
     */
    protected abstract void render(float interpolation);

    /**
     * @see AbstractGame#initSystem()
     */
    protected abstract void initSystem();

    /**
     * @see AbstractGame#initGame()
     */
    protected abstract void initGame();

    /**
     * @see AbstractGame#reinit()
     */
    protected abstract void reinit();

    /**
     * @see AbstractGame#cleanup()
     */
    protected abstract void cleanup();


}



A test for the BaseApplet

import jmetest.flagrushtut.Lesson2;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jmex.awt.applet.BetterBaseJMEApplet;

public class TestBetterBaseApplet extends BetterBaseJMEApplet {

   private static final long serialVersionUID = -3897767886566458005L;

   protected Timer timer;
   //Our camera object for viewing the scene
   private Camera cam;
   //the root node of the scene graph
   private Node scene;
   //TextureState to show the monkey on the sphere.
   private TextureState ts;
   
   @Override
   protected void cleanup() {
      // TODO Auto-generated method stub
      
   }

   @Override
   protected void initGame() {
      // TODO Auto-generated method stub
      scene = new Node("Scene graph node");
      cam.update();

      //Create our Sphere
      Sphere s = new Sphere("Sphere", 30, 30, 25);
      s.setLocalTranslation(new Vector3f(0, 0, -40));
      s.setModelBound(new BoundingBox());
      s.updateModelBound();

      ts = display.getRenderer().createTextureState();
      ts.setEnabled(true);
      ts.setTexture(TextureManager.loadTexture(Lesson2.class.getClassLoader()
            .getResource("jmetest/data/images/Monkey.jpg"),
            Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));

      s.setRenderState(ts);

      scene.attachChild(s);

      //update the scene graph for rendering
      scene.updateGeometricState(0.0f, true);
      scene.updateRenderState();
   }

   @Override
   protected void initSystem() {
      cam = display.getRenderer().createCamera(super.getWidth(), super.getHeight());
      
      // TODO Auto-generated method stub
      //set the background to black
      display.getRenderer().setBackgroundColor(ColorRGBA.black.clone());
      
      //initialize the camera
      cam.setFrustumPerspective(45.0f, (float)super.getWidth()/ (float)super.getHeight(), 1, 1000);
      Vector3f loc = new Vector3f(0.0f, 0.0f, 25.0f);
      Vector3f left = new Vector3f(-1.0f, 0.0f, 0.0f);
      Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
      Vector3f dir = new Vector3f(0.0f, 0f, -1.0f);
      // Move our camera to a correct place and orientation.
      cam.setFrame(loc, left, up, dir);
      /** Signal that we've changed our camera's location/frustum. */
      cam.update();
      
       /** Get a high resolution timer for FPS updates. */
       timer = Timer.getTimer();

      display.getRenderer().setCamera(cam);

      KeyBindingManager.getKeyBindingManager().set("exit",
            KeyInput.KEY_ESCAPE);
   }

   @Override
   protected void reinit() {
      // TODO Auto-generated method stub
      
   }

   @Override
   protected void render(float interpolation) {
      // TODO Auto-generated method stub
      display.getRenderer().clearBuffers();

      display.getRenderer().draw(scene);

   }

   @Override
   protected void update(float interpolation) {
      // TODO Auto-generated method stub
      timer.update();
      interpolation = timer.getTimePerFrame();
      //if escape was pressed, we exit
      if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
         finished = true;
      }
   }

}




Add this method initForApplet in com.jme.system.lwjgl.LWJGLDisplaySystem.java

    public void initForApplet(int w, int h) {
        renderer = new LWJGLRenderer(w, h);
       switchContext(this);
        renderer.setHeadless(true);
        currentContext.setupRecords(renderer);
        DisplaySystem.updateStates(renderer);
        created = true;
    }



I think that's all you need to reproduce what I have.


Edit: thanks to Wizem for the created = true; update

What about the BetterJMECanvas?

I get the same results as you when replacing it with an empty interface. Looking through the code to see if I can find anything.

Thanks for you help, I believe if we figure this out, it will really help with applet development.  I saw that you asked why there's no BetterJMECanvas, I'll answer it for posterity.  Really there's no need for JMECanvas or even a BetterLWJGLCanvas for the new LWJGL applets that utilize Display.setParent().  Really I believe that in the end if we figure this out will just use a plain old Java Canvas, just like the new Gears Applet.  The only reason I kept the BetterLWJGLCanvas was because it contains the some code that has properties that might be useful.  Thanks for any help Wizem, or anybody else for that matter.

I haven't finished looking at the code (checking GearsApplet and yours), but to get stuff on screen, you definitely need Display.update() in the game loop:



                while (!finished && !display.isClosing()) {
                    // handle input events prior to updating the scene
                    // - some applications may want to put this into update of
                    // the game state
                    InputSystem.update();
                   
                   
                    // update game state, do not use interpolation parameter
                    update(-1f);

                    // render, do not use interpolation parameter
                    render(-1f);
                    // swap buffers
//                    display.getRenderer().displayBackBuffer();                   
                    Display.update();                               

                    Thread.yield();
                }



The Display.update() call will swap buffers and receive system messages. However, the applet can't be resized (that results in major video glitches), and closing it throws an exception (when calling display.close()).

I might have to go for a while (like a day), so maybe you'll have more luck meanwhile.

Wow, that little addition made it work!!!  I believe that if we fix this up a little, we'll have enough for a contribution.  I don't know what video glitches your getting, but I can resize it fine.  Are you able to resize the gears applet?  Maybe it some deal with your drivers.  I do get the error when it closes, but that shouldn't be too hard to fix.  I think if I simplify some of this code and add on to other spots, I do believe this will help others a lot.

Ok, I fixed it to make it not have the error anymore.  Move

        if (display != null)

            display.close();

to below the while loop.  Also you can remove BetterLWJGLCanvas, so it's just a java Canvas.

I got tied up more than I expected to, so sorry for the delay  :// (actually, I got a major leg wound two weeks ago, and that's kind of what's tying me up, hence the unpredictability  :D)



Anyways, you're right, it's actually working… I must have changed something else and forgot about it when I was picking the code; when I copied your code and inserted the Display.update() in place, it's all peachy  :).



Well this is great… I was really looking forward to getting the new applets. Want me to help out with the fixing up? I'm not really sure what to do… I guess we need camera controls, canvas resizing (like in the old-style applets, so the scene scales when the applet is resized), a mouse pointer, and some debug functionality, + javadocs. Anything else?

That sounds about right, getting input to work, along with resizing should be something dealt with first, but shouldn't take very long.  I think we should make the BaseApplet like BaseGame so that we can add on to it with a better SimpleApplet andor a StandardApplet.  I guess paste any changes as you come up with them here.

Yeah, my thoughts exactly on the BaseApplet/SimpleApplet :). Let's get to it then  }:-@

I guess this should be O.K. as a BaseApplet:



import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.util.logging.Level;
import java.util.logging.Logger;

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

import com.jme.app.AbstractGame;
import com.jme.input.InputSystem;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.system.lwjgl.LWJGLDisplaySystem;
import com.jme.util.ThrowableHandler;

public abstract class BaseApplet extends Applet {
    private static final long serialVersionUID = 6894421316159346138L;
    private static final Logger logger = Logger.getLogger(BaseApplet.class
       .getName());
    protected ThrowableHandler throwableHandler;

    /** The awt canvas to draw to */
    protected Canvas displayParent;
    /** The thread with the game logic: initialization, updating, rendering */
    protected Thread gameThread;
    /** Flag for running the system. */
    protected boolean finished = false;
    protected DisplaySystem display;

    /**
     * Initializes the awt canvas to later render the jme scene to via
     * Display.setParent()
     */
    public void init() {
   logger.info("Applet initialized.");
   setLayout(new BorderLayout());
   try {
       displayParent = new Canvas();
       displayParent.setSize(getWidth(), getHeight());
       add(displayParent);
       displayParent.setFocusable(true);
       displayParent.requestFocus();
       displayParent.setIgnoreRepaint(true);
       setVisible(true);
   } catch (Exception e) {
       System.err.println(e);
       throw new RuntimeException("Unable to create display");
   }
    }

    /**
     * Creates the game thread, which first initializes the display, then runs
     * the game updates and renders.
     */
    public final void start() {
   logger.info("Applet started.");
   gameThread = new Thread() {
       public void run() {
      try {
          logger.info("display_parent.isDisplayable() = "
             + displayParent.isDisplayable());
          Display.setParent(displayParent);
          // Display.setVSyncEnabled(true);
          Display.create();
          // initGL();
      } catch (LWJGLException e) {
          e.printStackTrace();
      }
      gameLoop();
       }
   };
   gameThread.start();
    }

    public void destroy() {
   if (display != null) {
       display.reset();
       display.close();
   }
   remove(displayParent);
   super.destroy();
   logger.info("Clear up");
    }

    public void gameLoop() {
   try {
       if (!finished) {
      display = DisplaySystem.getDisplaySystem();
      ((LWJGLDisplaySystem) display).initForApplet(getWidth(),
         getHeight());
      initSystem();
      assertDisplayCreated();
      initGame();
      // main loop
      while (!finished && !display.isClosing()) {
          // handle input events prior to updating the scene
          // - some applications may want to put this into update of
          // the game state
          InputSystem.update();
          // update game state, do not use interpolation parameter
          update(-1.0f);
          // render, do not use interpolation parameter
          render(-1.0f);
          // Swap buffers, process messages, handle input
          Display.update();
          Thread.yield();
      }
       }
   } catch (Throwable t) {
       logger.logp(Level.SEVERE, this.getClass().toString(), "start()",
          "Exception in game loop", t);
       if (throwableHandler != null) {
      throwableHandler.handle(t);
       }
   }

   cleanup();
   logger.info("Application ending.");
   destroy();
    }

    /**
     * Get the exception handler if one hs been set.
     *
     * @return the exception handler, or {@code null} if not set.
     */
    protected ThrowableHandler getThrowableHandler() {
   return throwableHandler;
    }

    /**
     *
     * @param throwableHandler
     */
    protected void setThrowableHandler(ThrowableHandler throwableHandler) {
   this.throwableHandler = throwableHandler;
    }

    /**
     * <code>assertDisplayCreated</code> determines if the display system was
     * successfully created before use.
     *
     * @throws JmeException
     *             if the display system was not successfully created
     */
    protected void assertDisplayCreated() throws JmeException {
   if (display == null) {
       logger.severe("Display system is null.");
       throw new JmeException("Window must be created during"
          + " initialization.");
   }
    }

    /**
     * @param interpolation
     *            unused in this implementation
     * @see AbstractGame#update(float interpolation)
     */
    protected abstract void update(float interpolation);

    /**
     * @param interpolation
     *            unused in this implementation
     * @see AbstractGame#render(float interpolation)
     */
    protected abstract void render(float interpolation);

    /**
     * @see AbstractGame#initSystem()
     */
    protected abstract void initSystem();

    /**
     * @see AbstractGame#initGame()
     */
    protected abstract void initGame();

    /**
     * @see AbstractGame#reinit()
     */
    protected abstract void reinit();

    /**
     * @see AbstractGame#cleanup()
     */
    protected abstract void cleanup();
}

This works, with out resizing and no cursor. I basically copied the game hierarchy for applications, and edited some stuff to make it work in an applet. Aside from needing the cursor and resizing, this is definitely still something to refactor and test, but it works, the camera moves, and the basic hierarchy is there:



(Sorry if I’m posting too often, trying to go sleep, and failed attempts lead to code  }:-@ )



BaseApplet (I changed it a bit from the above):



import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.util.logging.Level;
import java.util.logging.Logger;

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

import com.jme.app.AbstractGame;
import com.jme.input.InputSystem;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.system.lwjgl.LWJGLDisplaySystem;
import com.jme.util.ThrowableHandler;

public abstract class BaseApplet extends Applet {
    private static final long serialVersionUID = 6894421316159346138L;
    private static final Logger logger = Logger.getLogger(BaseApplet.class
       .getName());
    protected ThrowableHandler throwableHandler;

    /**
     * Copied from AbstractGame; Perhaps it's better to make this public from
     * there, and reference it?
     */
    private final static String JME_VERSION_TAG = "jME version 2.0 Stable (r4093)";

    /** The awt canvas to draw to */
    protected Canvas displayParent;
    /** The thread with the game logic: initialization, updating, rendering */
    protected Thread gameThread;
    /** Flag for running the system. */
    protected boolean finished = false;
    protected DisplaySystem display;

    /**
     *@see AbstractGame#getVersion()
     */
    public String getVersion() {
   return JME_VERSION_TAG;
    }

    /** Halts execution (cleanup methods are called afterwards) */
    public void finish() {
   finished = true;
    }

    /**
     * Initializes the awt canvas to later render the jme scene to via
     * Display.setParent()
     */
    public void init() {
   logger.info("Applet initialized.");
   setLayout(new BorderLayout());
   try {
       displayParent = new Canvas();
       displayParent.setSize(getWidth(), getHeight());
       add(displayParent);
       displayParent.setFocusable(true);
       displayParent.requestFocus();
       displayParent.setIgnoreRepaint(true);
       setVisible(true);
   } catch (Exception e) {
       System.err.println(e);
       throw new RuntimeException("Unable to create display");
   }
    }

    /**
     * Creates the game thread, which first initializes the display, then runs
     * the game updates and renders.
     */
    public final void start() {
   logger.info("Applet started.");
   gameThread = new Thread() {
       public void run() {
      try {
          logger.info("display_parent.isDisplayable() = "
             + displayParent.isDisplayable());
          Display.setParent(displayParent);
          // Display.setVSyncEnabled(true);
          Display.create();
          // initGL();
      } catch (LWJGLException e) {
          e.printStackTrace();
      }

      gameLoop();

      cleanup();
      logger.info("Application ending.");
      if (display != null) {
          display.reset();
          display.close();
      }
      remove(displayParent);
       }
   };
   gameThread.start();
    }

    public void destroy() {

   super.destroy();
   logger.info("Clear up");
    }

    public void gameLoop() {
   try {
       if (!finished) {
      display = DisplaySystem.getDisplaySystem();
      ((LWJGLDisplaySystem) display).initForApplet(getWidth(),
         getHeight());
      initSystem();
      assertDisplayCreated();
      initGame();
      // main loop
      while (!finished && !display.isClosing()) {
          // handle input events prior to updating the scene
          // - some applications may want to put this into update of
          // the game state
          InputSystem.update();
          // update game state, do not use interpolation parameter
          update(-1.0f);
          // render, do not use interpolation parameter
          render(-1.0f);
          // Swap buffers, process messages, handle input
          Display.update();
          Thread.yield();
      }
       }
   } catch (Throwable t) {
       logger.logp(Level.SEVERE, this.getClass().toString(), "start()",
          "Exception in game loop", t);
       if (throwableHandler != null) {
      throwableHandler.handle(t);
       }
   }

    }

    /**
     * Get the exception handler if one hs been set.
     *
     * @return the exception handler, or {@code null} if not set.
     */
    protected ThrowableHandler getThrowableHandler() {
   return throwableHandler;
    }

    /**
     *
     * @param throwableHandler
     */
    protected void setThrowableHandler(ThrowableHandler throwableHandler) {
   this.throwableHandler = throwableHandler;
    }

    /**
     * <code>assertDisplayCreated</code> determines if the display system was
     * successfully created before use.
     *
     * @throws JmeException
     *             if the display system was not successfully created
     */
    protected void assertDisplayCreated() throws JmeException {
   if (display == null) {
       logger.severe("Display system is null.");
       throw new JmeException("Window must be created during"
          + " initialization.");
   }
    }

    /**
     * @param interpolation
     *            unused in this implementation
     * @see AbstractGame#update(float interpolation)
     */
    protected abstract void update(float interpolation);

    /**
     * @param interpolation
     *            unused in this implementation
     * @see AbstractGame#render(float interpolation)
     */
    protected abstract void render(float interpolation);

    /**
     * @see AbstractGame#initSystem()
     */
    protected abstract void initSystem();

    /**
     * @see AbstractGame#initGame()
     */
    protected abstract void initGame();

    /**
     * @see AbstractGame#reinit()
     */
    protected abstract void reinit();

    /**
     * @see AbstractGame#cleanup()
     */
    protected abstract void cleanup();
}
}




SimpleApplet:



import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.concurrent.Callable;

import com.jme.app.AbstractGame;
import com.jme.app.BaseSimpleGame;
import com.jme.input.FirstPersonHandler;
import com.jme.input.MouseInput;
import com.jme.renderer.Renderer;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;

public abstract class SimpleApplet extends BaseSimpleApplet {
    /**
     * Called every frame to update scene information.
     *
     * @param interpolation
     *            unused in this implementation
     * @see BaseSimpleGame#update(float interpolation)
     */
    protected final void update(float interpolation) {
   super.update(interpolation);

   if (!pause) {
       /** Call simpleUpdate in any derived classes of SimpleGame. */
       simpleUpdate();

       /** Update controllers/render states/transforms/bounds for rootNode. */
       rootNode.updateGeometricState(tpf, true);
       statNode.updateGeometricState(tpf, true);
   }
    }

    /**
     * This is called every frame in BaseGame.start(), after update()
     *
     * @param interpolation
     *            unused in this implementation
     * @see AbstractGame#render(float interpolation)
     */
    protected final void render(float interpolation) {
   super.render(interpolation);

   Renderer r = display.getRenderer();

   /** Draw the rootNode and all its children. */
   r.draw(rootNode);

   /** Call simpleRender() in any derived classes. */
   simpleRender();

   /** Draw the stats node to show our stat charts. */
   r.draw(statNode);

   doDebug(r);
    }

    protected void initGame() {
   super.initGame();
   MouseInput.get().setCursorVisible(true);
   ((FirstPersonHandler) input).getMouseLookHandler().setEnabled(false);
    }

    protected void initSystem() {
   super.initSystem();
   this.addComponentListener(new ComponentAdapter() {
       public void componentResized(ComponentEvent ce) {
      Callable<?> exe = new Callable<Object>() {
          public Object call() {
         display.getRenderer().reinit(
            SimpleApplet.this.getWidth(),
            SimpleApplet.this.getHeight());
         cam.setFrustumPerspective(45.0f, (float) displayParent
            .getWidth()
            / (float) displayParent.getHeight(), 1, 1000);
         return null;
          }
      };
      GameTaskQueueManager.getManager()
         .getQueue(GameTaskQueue.RENDER).enqueue(exe);
       }
   });
    }

}



tiny test:


import com.jme.math.Vector3f;
import com.jme.scene.shape.Box;

public class TestSimpleApplet extends SimpleApplet {

    @Override
    protected void simpleInitGame() {
   rootNode.attachChild(new Box("A test box", Vector3f.ZERO, 5,5,5));
   
    }



BaseSimpleApplet separately, because it says the message is too long.

Edit: added resizing, added cursor (it believe it makes sense for the system cursor to be visible for applets, but I guess some applications want a textured cursor, so I moved that code to simpleApplet)

BaseSimpleApplet:



import java.util.logging.Level;
import java.util.logging.Logger;

import com.jme.app.AbstractGame;
import com.jme.app.BaseSimpleGame;
import com.jme.input.FirstPersonHandler;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.input.joystick.JoystickInput;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.Text;
import com.jme.scene.Spatial.CullHint;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.LightState;
import com.jme.scene.state.WireframeState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.Debug;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jme.util.geom.Debugger;
import com.jme.util.stat.StatCollector;
import com.jme.util.stat.StatType;
import com.jme.util.stat.graph.DefColorFadeController;
import com.jme.util.stat.graph.GraphFactory;
import com.jme.util.stat.graph.LineGrapher;
import com.jme.util.stat.graph.TabledLabelGrapher;

public abstract class BaseSimpleApplet extends BaseApplet {
    private static final Logger logger = Logger
        .getLogger(BaseSimpleApplet.class.getName());

    /**
     * The camera that we see through.
     */
    protected Camera cam;

    /**
     * The root of our normal scene graph.
     */
    protected Node rootNode;

    /**
     * Handles our mouse/keyboard input.
     */
    protected InputHandler input;

    /**
     * High resolution timer for jME.
     */
    protected Timer timer;

    /**
     * The root node for our stats and text.
     */
    protected Node statNode;

    /**
     * The root node for our stats graphs.
     */
    protected Node graphNode;

    /**
     * Alpha bits to use for the renderer. Any changes must be made prior to
     * call of start().
     */
    protected int alphaBits = 0;

    /**
     * Depth bits to use for the renderer. Any changes must be made prior to
     * call of start().
     */
    protected int depthBits = 8;

    /**
     * Stencil bits to use for the renderer. Any changes must be made prior to
     * call of start().
     */
    protected int stencilBits = 0;

    /**
     * Number of samples to use for the multisample buffer. Any changes must be
     * made prior to call of start().
     */
    protected int samples = 0;

    /**
     * Simply an easy way to get at timer.getTimePerFrame(). Also saves math
     * cycles since you don't call getTimePerFrame more than once per frame.
     */
    protected float tpf;

    /**
     * True if the renderer should display the depth buffer.
     */
    protected boolean showDepth = false;

    /**
     * True if the renderer should display bounds.
     */
    protected boolean showBounds = false;

    /**
     * True if the renderer should display normals.
     */
    protected boolean showNormals = false;

    /**
     * True if the we should show the stats graphs.
     */
    protected boolean showGraphs = false;

    /**
     * A wirestate to turn on and off for the rootNode
     */
    protected WireframeState wireState;

    /**
     * A lightstate to turn on and off for the rootNode
     */
    protected LightState lightState;

    /**
     * boolean for toggling the simpleUpdate and geometric update parts of the
     * game loop on and off.
     */
    protected boolean pause;

    private TabledLabelGrapher tgrapher;

    // private TimedAreaGrapher lgrapher;
    private LineGrapher lgrapher;

    private Quad lineGraph, labGraph;

    /**
     * Updates the timer, sets tpf, updates the input and updates the fps
     * string. Also checks keys for toggling pause, bounds, normals, lights,
     * wire etc.
     *
     * @param interpolation
     *            unused in this implementation
     * @see AbstractGame#update(float interpolation)
     */
    protected void update(float interpolation) {
    /** Recalculate the framerate. */
    timer.update();
    /** Update tpf to time per frame according to the Timer. */
    tpf = timer.getTimePerFrame();

    /** Check for key/mouse updates. */
    updateInput();

    /** update stats, if enabled. */
    if (Debug.stats) {
        StatCollector.update();
    }

    // Execute updateQueue item
     GameTaskQueueManager.getManager().getQueue(GameTaskQueue.UPDATE).execute();

    /** If toggle_pause is a valid command (via key p), change pause. */
    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "toggle_pause", false)) {
        pause = !pause;
    }

    /**
     * If step is a valid command (via key ADD), update scenegraph one unit.
     */
    if (KeyBindingManager.getKeyBindingManager().isValidCommand("step",
        true)) {
        simpleUpdate();
        rootNode.updateGeometricState(tpf, true);
    }

    /** If toggle_wire is a valid command (via key T), change wirestates. */
    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "toggle_wire", false)) {
        wireState.setEnabled(!wireState.isEnabled());
        rootNode.updateRenderState();
    }
    /** If toggle_lights is a valid command (via key L), change lightstate. */
    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "toggle_lights", false)) {
        lightState.setEnabled(!lightState.isEnabled());
        rootNode.updateRenderState();
    }
    /** If toggle_bounds is a valid command (via key B), change bounds. */
    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "toggle_bounds", false)) {
        showBounds = !showBounds;
    }

    /** If toggle_depth is a valid command (via key F3), change depth. */
    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "toggle_depth", false)) {
        showDepth = !showDepth;
    }

    if (Debug.stats) {
        /** handle toggle_stats command (key F4) */
        if (KeyBindingManager.getKeyBindingManager().isValidCommand(
            "toggle_stats", false)) {
        showGraphs = !showGraphs;
        Debug.updateGraphs = showGraphs;
        labGraph.clearControllers();
        lineGraph.clearControllers();
        labGraph.addController(new DefColorFadeController(labGraph,
            showGraphs ? .6f : 0f, showGraphs ? .5f : -.5f));
        lineGraph.addController(new DefColorFadeController(lineGraph,
            showGraphs ? .6f : 0f, showGraphs ? .5f : -.5f));
        }
    }

    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "toggle_normals", false)) {
        showNormals = !showNormals;
    }
    /** If camera_out is a valid command (via key C), show camera location. */
    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "camera_out", false)) {
        logger.info("Camera at: "
            + display.getRenderer().getCamera().getLocation());
    }

    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "screen_shot", false)) {
        display.getRenderer().takeScreenShot("SimpleGameScreenShot");
    }

    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "parallel_projection", false)) {
        if (cam.isParallelProjection()) {
        cameraPerspective();
        } else {
        cameraParallel();
        }
    }

    if (KeyBindingManager.getKeyBindingManager().isValidCommand(
        "mem_report", false)) {
        long totMem = Runtime.getRuntime().totalMemory();
        long freeMem = Runtime.getRuntime().freeMemory();
        long maxMem = Runtime.getRuntime().maxMemory();

        logger.info("|*|*|  Memory Stats  |*|*|");
        logger.info("Total memory: " + (totMem >> 10) + " kb");
        logger.info("Free memory: " + (freeMem >> 10) + " kb");
        logger.info("Max memory: " + (maxMem >> 10) + " kb");
    }

    if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit",
        false)) {
        finish();

    }
    }

    /**
     * Check for key/mouse updates. Allow overriding this method to skip update
     * in subclasses.
     */
    protected void updateInput() {
    input.update(tpf);
    }

}



Still too long, continued further

Aaaand, finally




    /**
     * Clears stats, the buffers and renders bounds and normals if on.
     *
     * @param interpolation
     *            unused in this implementation
     * @see AbstractGame#render(float interpolation)
     */
    protected void render(float interpolation) {
    Renderer r = display.getRenderer();
    /** Clears the previously rendered information. */
    r.clearBuffers();
    r.draw(rootNode);

     //Execute renderQueue item
     GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER).execute();
    }

    protected void doDebug(Renderer r) {
    /**
     * If showing bounds, draw rootNode's bounds, and the bounds of all its
     * children.
     */
    if (showBounds) {
        Debugger.drawBounds(rootNode, r, true);
    }

    if (showNormals) {
        Debugger.drawNormals(rootNode, r);
        Debugger.drawTangents(rootNode, r);
    }
    }

    /**
     * Creates display, sets up camera, and binds keys. Called in
     * BaseGame.start() directly after the dialog box.
     *
     * @see AbstractGame#initSystem()
     */
    protected void initSystem() throws JmeException {
    logger.info(getVersion());
    cam = display.getRenderer().createCamera(super.getWidth(),
        super.getHeight());
    // initialize the camera
    cam.setFrustumPerspective(45.0f, (float) super.getWidth()
        / (float) super.getHeight(), 1, 1000);
    Vector3f loc = new Vector3f(0.0f, 0.0f, 25.0f);
    Vector3f left = new Vector3f(-1.0f, 0.0f, 0.0f);
    Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
    Vector3f dir = new Vector3f(0.0f, 0f, -1.0f);
    // Move our camera to a correct place and orientation.
    cam.setFrame(loc, left, up, dir);
    /** Signal that we've changed our camera's location/frustum. */
    cam.update();
    display.getRenderer().setCamera(cam);

    /** Create a basic input controller. */
    FirstPersonHandler firstPersonHandler = new FirstPersonHandler(cam, 50,
        1);
    input = firstPersonHandler;

    /** Get a high resolution timer for FPS updates. */
    timer = Timer.getTimer();

    /** Sets the title of our display. */
    String className = getClass().getName();
    if (className.lastIndexOf('.') > 0)
        className = className.substring(className.lastIndexOf('.') + 1);
    display.setTitle(className);

    /** Assign key P to action "toggle_pause". */
    KeyBindingManager.getKeyBindingManager().set("toggle_pause",
        KeyInput.KEY_P);
    /** Assign key ADD to action "step". */
    KeyBindingManager.getKeyBindingManager().set("step", KeyInput.KEY_ADD);
    /** Assign key T to action "toggle_wire". */
    KeyBindingManager.getKeyBindingManager().set("toggle_wire",
        KeyInput.KEY_T);
    /** Assign key L to action "toggle_lights". */
    KeyBindingManager.getKeyBindingManager().set("toggle_lights",
        KeyInput.KEY_L);
    /** Assign key B to action "toggle_bounds". */
    KeyBindingManager.getKeyBindingManager().set("toggle_bounds",
        KeyInput.KEY_B);
    /** Assign key N to action "toggle_normals". */
    KeyBindingManager.getKeyBindingManager().set("toggle_normals",
        KeyInput.KEY_N);
    /** Assign key C to action "camera_out". */
    KeyBindingManager.getKeyBindingManager().set("camera_out",
        KeyInput.KEY_C);
    /** Assign key R to action "mem_report". */
    KeyBindingManager.getKeyBindingManager().set("mem_report",
        KeyInput.KEY_R);

    KeyBindingManager.getKeyBindingManager().set("exit",
        KeyInput.KEY_ESCAPE);

    KeyBindingManager.getKeyBindingManager().set("screen_shot",
        KeyInput.KEY_F1);
    KeyBindingManager.getKeyBindingManager().set("parallel_projection",
        KeyInput.KEY_F2);
    KeyBindingManager.getKeyBindingManager().set("toggle_depth",
        KeyInput.KEY_F3);
    KeyBindingManager.getKeyBindingManager().set("toggle_stats",
        KeyInput.KEY_F4);
DisplaySystem.getDisplaySystem().getRenderer().setBackgroundColor(new ColorRGBA(1,1,1,1));
    }

    protected void cameraPerspective() {
    cam.setFrustumPerspective(45.0f, (float) display.getWidth()
        / (float) display.getHeight(), 1, 1000);
    cam.setParallelProjection(false);
    cam.update();
    }

    protected void cameraParallel() {
    cam.setParallelProjection(true);
    float aspect = (float) display.getWidth() / display.getHeight();
    cam.setFrustum(-100, 1000, -50 * aspect, 50 * aspect, -50, 50);
    cam.update();
    }

    /**
     * Creates rootNode, lighting, statistic text, and other basic render
     * states. Called in BaseGame.start() after initSystem().
     *
     * @see AbstractGame#initGame()
     */
    protected void initGame() {
    /** Create rootNode */
    rootNode = new Node("rootNode");

    /**
     * Create a wirestate to toggle on and off. Starts disabled with default
     * width of 1 pixel.
     */
    wireState = display.getRenderer().createWireframeState();
    wireState.setEnabled(false);
    rootNode.setRenderState(wireState);

    /**
     * Create a ZBuffer to display pixels closest to the camera above
     * farther ones.
     */
    ZBufferState buf = display.getRenderer().createZBufferState();
    buf.setEnabled(true);
    buf.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
    rootNode.setRenderState(buf);

    // -- STATS, text node
    // Finally, a stand alone node (not attached to root on purpose)
    statNode = new Node("Stats node");
    statNode.setCullHint(Spatial.CullHint.Never);
    statNode.setRenderQueueMode(Renderer.QUEUE_ORTHO);

    if (Debug.stats) {
        graphNode = new Node("Graph node");
        graphNode.setCullHint(Spatial.CullHint.Never);
        statNode.attachChild(graphNode);

        setupStatGraphs();
        setupStats();
    }

    // ---- LIGHTS
    /** Set up a basic, default light. */
    PointLight light = new PointLight();
    light.setDiffuse(new ColorRGBA(0.75f, 0.75f, 0.75f, 0.75f));
    light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
    light.setLocation(new Vector3f(100, 100, 100));
    light.setEnabled(true);

    /** Attach the light to a lightState and the lightState to rootNode. */
    lightState = display.getRenderer().createLightState();
    lightState.setEnabled(true);
    lightState.attach(light);
    rootNode.setRenderState(lightState);

    /** Let derived classes initialize. */
    simpleInitGame();

    timer.reset();

    /**
     * Update geometric and rendering information for both the rootNode and
     * fpsNode.
     */
    rootNode.updateGeometricState(0.0f, true);
    rootNode.updateRenderState();
    statNode.updateGeometricState(0.0f, true);
    statNode.updateRenderState();

    timer.reset();
    }

    /**
     * Called near end of initGame(). Must be defined by derived classes.
     */
    protected abstract void simpleInitGame();

    /**
     * Can be defined in derived classes for custom updating. Called every frame
     * in update.
     */
    protected void simpleUpdate() {
    // do nothing
    }

    /**
     * Can be defined in derived classes for custom rendering. Called every
     * frame in render.
     */
    protected void simpleRender() {
    // do nothing
    }

    /**
     * unused
     *
     * @see AbstractGame#reinit()
     */
    protected void reinit() {
    // do nothing
    }

    /**
     * Cleans up the keyboard.
     *
     * @see AbstractGame#cleanup()
     */
    protected void cleanup() {
    logger.info("Cleaning up resources.");

    TextureManager.doTextureCleanup();
    if (display != null && display.getRenderer() != null)
        display.getRenderer().cleanup();
    KeyInput.destroyIfInitalized();
    MouseInput.destroyIfInitalized();
    JoystickInput.destroyIfInitalized();
    }

    /**
     * Destroys the display, removes the rendering canvas from the applet
     */
    protected void quit() {
    super.destroy();
    // System.exit( 0 );
    }

    /**
     * Set up which stats to graph
     */
    protected void setupStats() {
    lgrapher.addConfig(StatType.STAT_FRAMES, LineGrapher.ConfigKeys.Color
        .name(), ColorRGBA.green);
    lgrapher.addConfig(StatType.STAT_FRAMES, LineGrapher.ConfigKeys.Stipple
        .name(), 0XFF0F);
    lgrapher.addConfig(StatType.STAT_TRIANGLE_COUNT,
        LineGrapher.ConfigKeys.Color.name(), ColorRGBA.cyan);
    lgrapher.addConfig(StatType.STAT_TRIANGLE_COUNT,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    lgrapher.addConfig(StatType.STAT_QUAD_COUNT,
        LineGrapher.ConfigKeys.Color.name(), ColorRGBA.lightGray);
    lgrapher.addConfig(StatType.STAT_QUAD_COUNT,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    lgrapher.addConfig(StatType.STAT_LINE_COUNT,
        LineGrapher.ConfigKeys.Color.name(), ColorRGBA.red);
    lgrapher.addConfig(StatType.STAT_LINE_COUNT,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    lgrapher.addConfig(StatType.STAT_GEOM_COUNT,
        LineGrapher.ConfigKeys.Color.name(), ColorRGBA.gray);
    lgrapher.addConfig(StatType.STAT_GEOM_COUNT,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    lgrapher.addConfig(StatType.STAT_TEXTURE_BINDS,
        LineGrapher.ConfigKeys.Color.name(), ColorRGBA.orange);
    lgrapher.addConfig(StatType.STAT_TEXTURE_BINDS,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);

    tgrapher.addConfig(StatType.STAT_FRAMES,
        TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
    tgrapher.addConfig(StatType.STAT_FRAMES,
        TabledLabelGrapher.ConfigKeys.Name.name(), "Frames/s:");
    tgrapher.addConfig(StatType.STAT_TRIANGLE_COUNT,
        TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
    tgrapher.addConfig(StatType.STAT_TRIANGLE_COUNT,
        TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Tris:");
    tgrapher.addConfig(StatType.STAT_TRIANGLE_COUNT,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    tgrapher.addConfig(StatType.STAT_QUAD_COUNT,
        TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
    tgrapher.addConfig(StatType.STAT_QUAD_COUNT,
        TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Quads:");
    tgrapher.addConfig(StatType.STAT_QUAD_COUNT,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    tgrapher.addConfig(StatType.STAT_LINE_COUNT,
        TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
    tgrapher.addConfig(StatType.STAT_LINE_COUNT,
        TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Lines:");
    tgrapher.addConfig(StatType.STAT_LINE_COUNT,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    tgrapher.addConfig(StatType.STAT_GEOM_COUNT,
        TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
    tgrapher.addConfig(StatType.STAT_GEOM_COUNT,
        TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Objs:");
    tgrapher.addConfig(StatType.STAT_GEOM_COUNT,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    tgrapher.addConfig(StatType.STAT_TEXTURE_BINDS,
        TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
    tgrapher.addConfig(StatType.STAT_TEXTURE_BINDS,
        TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Tex binds:");
    tgrapher.addConfig(StatType.STAT_TEXTURE_BINDS,
        TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);

    // If you want to try out
    // lgrapher.addConfig(StatType.STAT_RENDER_TIMER,
    // TimedAreaGrapher.ConfigKeys.Color.name(), ColorRGBA.blue);
    // lgrapher.addConfig(StatType.STAT_UNSPECIFIED_TIMER,
    // TimedAreaGrapher.ConfigKeys.Color.name(), ColorRGBA.white);
    // lgrapher.addConfig(StatType.STAT_STATES_TIMER,
    // TimedAreaGrapher.ConfigKeys.Color.name(), ColorRGBA.yellow);
    // lgrapher.addConfig(StatType.STAT_DISPLAYSWAP_TIMER,
    // TimedAreaGrapher.ConfigKeys.Color.name(), ColorRGBA.red);
    //
    // tgrapher.addConfig(StatType.STAT_RENDER_TIMER,
    // TabledLabelGrapher.ConfigKeys.Decimals.name(), 2);
    // tgrapher.addConfig(StatType.STAT_RENDER_TIMER,
    // TabledLabelGrapher.ConfigKeys.Name.name(), "Render:");
    // tgrapher.addConfig(StatType.STAT_RENDER_TIMER,
    // TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    // tgrapher.addConfig(StatType.STAT_UNSPECIFIED_TIMER,
    // TabledLabelGrapher.ConfigKeys.Decimals.name(), 2);
    // tgrapher.addConfig(StatType.STAT_UNSPECIFIED_TIMER,
    // TabledLabelGrapher.ConfigKeys.Name.name(), "Other:");
    // tgrapher.addConfig(StatType.STAT_UNSPECIFIED_TIMER,
    // TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    // tgrapher.addConfig(StatType.STAT_STATES_TIMER,
    // TabledLabelGrapher.ConfigKeys.Decimals.name(), 2);
    // tgrapher.addConfig(StatType.STAT_STATES_TIMER,
    // TabledLabelGrapher.ConfigKeys.Name.name(), "States:");
    // tgrapher.addConfig(StatType.STAT_STATES_TIMER,
    // TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    // tgrapher.addConfig(StatType.STAT_DISPLAYSWAP_TIMER,
    // TabledLabelGrapher.ConfigKeys.Decimals.name(), 2);
    // tgrapher.addConfig(StatType.STAT_DISPLAYSWAP_TIMER,
    // TabledLabelGrapher.ConfigKeys.Name.name(), "DisplaySwap:");
    // tgrapher.addConfig(StatType.STAT_DISPLAYSWAP_TIMER,
    // TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
    //
    // StatCollector.addTimedStat(StatType.STAT_RENDER_TIMER);
    // StatCollector.addTimedStat(StatType.STAT_STATES_TIMER);
    // StatCollector.addTimedStat(StatType.STAT_UNSPECIFIED_TIMER);
    // StatCollector.addTimedStat(StatType.STAT_DISPLAYSWAP_TIMER);
    }

    /**
     * Set up the graphers we will use and the quads we'll show the stats on.
     *
     */
    protected void setupStatGraphs() {
    StatCollector.setSampleRate(1000L);
    StatCollector.setMaxSamples(40);

    lineGraph = new Quad("lineGraph", display.getWidth(), display
        .getHeight() * .75f) {
        private static final long serialVersionUID = 1L;

        @Override
        public void draw(Renderer r) {
        StatCollector.pause();
        super.draw(r);
        StatCollector.resume();
        }
    };
    lgrapher = GraphFactory.makeLineGraph(
        (int) (lineGraph.getWidth() + .5f), (int) (lineGraph
            .getHeight() + .5f), lineGraph);
    // lgrapher =
    // GraphFactory.makeTimedGraph((int)(lineGraph.getWidth()+.5f),
    // (int)(lineGraph.getHeight()+.5f), lineGraph);
    lineGraph.setLocalTranslation((display.getWidth() * .5f), (display
        .getHeight() * .625f), 0);
    lineGraph.setCullHint(CullHint.Always);
    lineGraph.getDefaultColor().a = 0;
    graphNode.attachChild(lineGraph);

    Text f4Hint = new Text("f4", "F4 - toggle stats") {
        private static final long serialVersionUID = 1L;

        @Override
        public void draw(Renderer r) {
        StatCollector.pause();
        super.draw(r);
        StatCollector.resume();
        }
    };
    f4Hint.setCullHint(Spatial.CullHint.Never);
    f4Hint.setRenderState(Text.getDefaultFontTextureState());
    f4Hint.setRenderState(Text.getFontBlend());
    f4Hint.setLocalScale(.8f);
    f4Hint.setTextColor(ColorRGBA.gray);
    f4Hint.setLocalTranslation(display.getRenderer().getWidth()
        - f4Hint.getWidth() - 15, display.getRenderer().getHeight()
        - f4Hint.getHeight() - 10, 0);
    graphNode.attachChild(f4Hint);

    labGraph = new Quad("labelGraph", display.getWidth(), display
        .getHeight() * .25f) {
        private static final long serialVersionUID = 1L;

        @Override
        public void draw(Renderer r) {
        StatCollector.pause();
        super.draw(r);
        StatCollector.resume();
        }
    };
    tgrapher = GraphFactory.makeTabledLabelGraph(
        (int) (labGraph.getWidth() + .5f),
        (int) (labGraph.getHeight() + .5f), labGraph);
    tgrapher.setColumns(2);
    tgrapher.setMinimalBackground(false);
    tgrapher.linkTo(lgrapher);
    labGraph.setLocalTranslation((display.getWidth() * .5f), (display
        .getHeight() * .125f), 0);
    labGraph.setCullHint(CullHint.Always);
    labGraph.getDefaultColor().a = 0;
    graphNode.attachChild(labGraph);

    }

Ok, final post, promise.



I made some enhancements to the code I initially posted (resizing, removed camera movements by mouse, and added the cursor). It should be pretty work-with-able now.



If I messed up somewhere while copying, forgive me: it's 10am here, and I still can't sleep  :D. Everything seems to work on my system, so if there are problems found, I'll probably be able to fix them relatively quick, just let me know…

Wow awesome work! :slight_smile:



JME APPLETS (2.0) ftw.