LWJGL Forum

Programming => LWJGL Documentation => Topic started by: Evil-Devil on January 05, 2006, 18:16:55

Title: AWTGLCanvas Example (Update: 17.02.2006)
Post by: Evil-Devil on January 05, 2006, 18:16:55
Hi everyone.

As the AWTGLCanvas is often questioned and requested for examples, I thought about providing a small example.

The example is a small tool I wrote for my engine to display OBJ models.
The OBJ loader is far from finish, but the more interesting part is the implementation of the AWTGLCanvas.
As you can see on the screenies there are some controls to change the display like rotating the model or just translate it left.

For the implementation I choosed a similar way like in the lwjgl examples which makes its easy to replace the current renderer with another one.
Chaning the renderer is not included in the example, but from the behavior of Interfaces its supported.

(http://evil-devil.pyrokar.lima-city.de/evil-devil.com/gfx/projects/bmv_temple_small.png) (http://evil-devil.pyrokar.lima-city.de/evil-devil.com/gfx/projects/bmv_temple.png)
(http://evil-devil.pyrokar.lima-city.de/evil-devil.com/gfx/projects/bmv_af_f16_small.png) (http://evil-devil.pyrokar.lima-city.de/evil-devil.com/gfx/projects/bmv_af_f16.png)

To launch the example programm you have to create a new project in your ide and add the included jar file to your build path and of course your lwjgl binaries. I tested the example with version 0.98 and 0.99. But any lwjgl version that comes with the AWTGLCanvas should do it.
The explaining source files are included. any other files like the one in the jar need to recompiled, if you wanna see them ;) or ask for them in this thread.

>> Get The Example << (http://evil-devil.download.lima-city.de/Bubble_Model_Viewer_Distrib.rar)

//edit: the program does not center the models! So if you load any other than the shipped ones, it might happen that they are out of the screen.

Model files have to be placed within the model folder and textures within the textures folder. Sorry, but at first it was never planned to give it out to public.

Benny
Title: hmmmmmmm...
Post by: Fool Running on January 06, 2006, 14:36:18
Nice!  8)
Title: AWTGLCanvas Example (Update: 17.02.2006)
Post by: elias4444 on January 06, 2006, 18:11:20
Hey Evil-Devil, I love the inclusion of AWTGLCanvas... did you want my OBJ loader code? I've got it setup to import OBJs and their material (MTL) files (including textures) as well (you can check out my app at //www.tommytwisters.com/texture/texture.jnlp).

I'd be happy to send you all the code if you're interested.
Title: AWTGLCanvas Example (Update: 17.02.2006)
Post by: Evil-Devil on January 09, 2006, 19:21:14
Thanks for comments :)
How to use your texture app?

I only got a blank window with a commandline...but there I wasn't able to do something... maybe screen resolution to low?

As for the obj load code, yea. That would be cool. I used your basic model loader code as startup and worked my way through the modelformat based on the obj_file documentation I found via google. And sometimes also with the c++ code from the maya obj exporter plugin. The documentation helped me to implement the older obj formats even if i did not include everything. My main purpose was to have a loader that takes a maya 5 obj file and do me display it :)

Does maya smooth the model by itselfs? If so, i only need to implement manual smoothing and of course centering the model.

I updated the tool with a screenshot function and will upload it next time.
Title: AWTGLCanvas Example (Update: 17.02.2006)
Post by: elias4444 on January 09, 2006, 19:47:51
Hey Evil-Devil, go ahead and PM me with an e-mail address and I'll send the whole thing to you (I've already implemented centering as well). No need to reinvent the wheel.  :D
Title: AWTGLCanvas Example (Update: 17.02.2006)
Post by: Evil-Devil on January 12, 2006, 16:41:58
I uploaded a new version of my example. It includes the screenshot function.
Additional i changed some stuff within the model loader classes. Next time there will be also elias4444 provided obj stuff be included to make the loader more complete.

And its still requires J2SE 5!

For those who don't want to download the example, I provide the example itself here in this thread.
Title: AWTGLCanvas Example (Update: 17.02.2006)
Post by: Evil-Devil on January 12, 2006, 16:49:44
As announced, here is the code. Be aware of the imported classes, those were available within the package.
I know the GUI code is a bit messy but I didn't had yet the time to reuse the code. ;)

//edit: sry, i had to remove the GUI class, because the overall message text was too long and my code were cut :(


AWTOpenGLCanvas.java
/* =============================================================================
* AWTOpenGLCanvas.java Bubble_Engine
* com.evildevil.bubble.core
* Copyright (c) 2004 - 2005 Benjamin "Evil-Devil" Behrendt
* All rights reserved
* -------------------------------------------------------------------------- */
package com.evildevil.bubble.core;

import java.awt.GraphicsDevice;

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

/**
* @author Benjamin "Evil-Devil" Behrendt
* @version 1.0, 28.12.2005
*/
public class AWTOpenGLCanvas extends AWTGLCanvas implements Runnable {
   
   private IRenderable renderer = null;
   private Thread renderThread = null;
   private boolean initialize = false;

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

   public AWTOpenGLCanvas(PixelFormat pixel_format) throws LWJGLException {
       super(pixel_format);
   }

   public AWTOpenGLCanvas(GraphicsDevice device, PixelFormat pixel_format)
           throws LWJGLException {
       super(device, pixel_format);        
   }

   public AWTOpenGLCanvas(GraphicsDevice device, PixelFormat pixel_format,
           Drawable drawable) throws LWJGLException {
       super(device, pixel_format, drawable);        
   }
   
   public AWTOpenGLCanvas(IRenderable renderable) throws LWJGLException {
       this();
       setRenderer(renderable);              
   }
   
   public AWTOpenGLCanvas(PixelFormat pixelFormat, IRenderable renderable)
           throws LWJGLException {
       super(pixelFormat);
       setRenderer(renderable);        
   }
   
   public AWTOpenGLCanvas(GraphicsDevice device, PixelFormat pixelFormat,
           IRenderable renderable) throws LWJGLException {
       super(device,pixelFormat);
       setRenderer(renderable);
   }
   
   public AWTOpenGLCanvas(GraphicsDevice device, PixelFormat pixelFormat,
           Drawable drawable, IRenderable renderable) throws LWJGLException {
       super(device,pixelFormat,drawable);
       setRenderer(renderable);        
   }    
   
   public void setRenderer(IRenderable renderable) {
       this.renderer = renderable;
   }
   
   public void paintGL() {        
       // TODO: redo this -.-
       if (!initialize) {
           System.out.println("initializing");
           initRenderThread();            
           initOpenGL();
           // give the renderer a note that we have initialized our main render stuff
           // and init the renderer itself
           renderer.initRenderer();
           renderer.setInitialized(true);            
           initialize = true;
       }
       synchronized(this) {
           try  {                
               makeCurrent();
               renderer.renderScene();                
               swapBuffers();                
           } catch (LWJGLException lwjgle) {
               // should not happen
               lwjgle.printStackTrace();
           }
       }
   }
   
   private final void initRenderThread() {
       if (renderThread == null) {
           renderThread = new Thread(this);
           renderThread.start();
       }
   }
   
   public void run() {
       while (renderThread != null && renderThread.isAlive()) {
           synchronized(this) {
               repaint();                
               try {                
                   Thread.sleep(16);
               } catch (InterruptedException ie) {
                   ie.printStackTrace();
               }                            
           }
       }
   }
   
   /**
    * If the context is created and a IRenderable object is provided, then
    * IRenderable.initRenderer() will be called to setup the states etc that were used.
    * @exception throws LWJGLExeption if there is still no context available
    * @exception throws NullPointerException if there was no IRenderable object provided
    */
   protected void initOpenGL() {
       if (getContext() == null)
           new LWJGLException("There is no context available that could be used to render something to it.");
       if (renderer == null)
           new NullPointerException("No IRenderable instance found, can't be null. Need one to render a scene.");
       renderer.initRenderer();
   }
   
   public IRenderable getRenderer() {
       return this.renderer;
   }
}

IRenderable.java
/* =============================================================================
* IRenderable.java Bubble Engine
* Copyright (c) 2004 - 2005 Benjamin "Evil-Devil" Behrendt
* All rights reserved
* -----------------------------------------------------------------------------
* powered by LWJGL - Copyright (c) LWJGL Team (http://www.lwjgl.org)
* powered by JAVA - Copyright (c) Sun Microsystems (http://java.sun.com)
* -------------------------------------------------------------------------- */
package com.evildevil.bubble.core;

/**
* @author Benjamin "Evil-Devil" Behrendt
* @version 1.0, 09.03.2005
*/
public interface IRenderable {
   
   /**
    * Used to initialize/set states of the render api i.e. openGL
    */
   public void initRenderer();
   
   /**
    * Used to load model, textures and everything that is needed
    * before a scene can be prerendered or plain rendered.
    */
   public void initScene();
   
   /**
    * Used for drawing the scene itself
    */
   public void renderScene();

   /**
    * Used to create things like DisplayLists, VBOs etc
    */
   public void preRenderScene();
   
   public void setInitialized(boolean initialized);
   
   public boolean isInitialized();

}

ModelViewerRenderer.java
/* =============================================================================
* DemoRenderer.java Bubble_Engine
* com.evildevil.bubble.demo
* Copyright (c) 2004 - 2005 Benjamin "Evil-Devil" Behrendt
* All rights reserved
* -------------------------------------------------------------------------- */
package com.evildevil.bubble.tools.model;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.HashMap;

import javax.swing.JOptionPane;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.glu.GLU;

import com.evildevil.bubble.core.IRenderable;
import com.evildevil.bubble.model.Face;
import com.evildevil.bubble.model.Mesh;
import com.evildevil.bubble.model.Model;
import com.evildevil.bubble.model.ModelReader;
import com.evildevil.bubble.model.ModelWriter;
import com.evildevil.bubble.model.Normalf;
import com.evildevil.bubble.model.TexCoordf;
import com.evildevil.bubble.model.Vertex3;
import com.evildevil.bubble.texture.TextureManager;
import com.evildevil.bubble.util.BufferUtils;

/**
* @author Benjamin "Evil-Devil" Behrendt
* @version 1.0, 28.12.2005
*/
public class ModelViewerRenderer implements IRenderable {
   
   private boolean canvasInitialized = false;
   private int modelList = 0;
   private Model model;
   private String modelFileName = null;
   private HashMap<String, Integer> textures = null;
   public short width = 640;
   public short height = 480;    
   private float[] whiteDiffuse = { 1f,1f,1f,1f};
   private float[] posSun = {0.0f, 10.0f ,0.0f, 1.0f };    
   private float rotateX = 0f;
   private float rotateY = 0f;
   private float rotateZ = 0f;    
   private float rotateXSpeed = 0f;
   private float rotateYSpeed = 0f;
   private float rotateZSpeed = 0f;
   private boolean autoRotate = false;
   private boolean autoRotateX = false;
   private boolean autoRotateY = false;
   private boolean autoRotateZ = false;
   private float translateX = 0f;
   private float translateY = 0f;
   private float translateZ = 0f;
   private float translateXSpeed = 0f;
   private float translateYSpeed = 0f;
   private float translateZSpeed = 0f;
   private float fov = 100f;

   public ModelViewerRenderer() {
   }

   public void initRenderer() {
       GL11.glShadeModel(GL11.GL_SMOOTH);
       GL11.glClearColor(0.0f,0.0f,0.0f,0.0f);         // black background
       GL11.glClearDepth(1.0f);                        // depth of 0 to 1
       GL11.glEnable(GL11.GL_DEPTH_TEST);              // enable depth testing        
       GL11.glDepthFunc(GL11.GL_LEQUAL);        
       GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT,GL11.GL_NICEST);
       GL11.glEnable(GL11.GL_TEXTURE_2D);
       GL11.glLight(GL11.GL_LIGHT0,GL11.GL_DIFFUSE,BufferUtils.createFloatBuffer(whiteDiffuse));
       GL11.glLight(GL11.GL_LIGHT0,GL11.GL_POSITION,BufferUtils.createFloatBuffer(posSun));
       GL11.glEnable(GL11.GL_LIGHT0);
       GL11.glEnable(GL11.GL_LIGHTING);
       GL11.glViewport(0,0,width,height);        
       GL11.glMatrixMode(GL11.GL_PROJECTION);
       GL11.glLoadIdentity();
       // a nice 45Ã,° perspective
       GLU.gluPerspective(45.0f,(float)width/(float)height,0.1f,fov);
       GL11.glMatrixMode(GL11.GL_MODELVIEW);
       GL11.glLoadIdentity();
   }

   public void initScene() {
       modelList =  GL11.glGenLists(1);
       ModelReader mr = null;
       // we have no model? so don't try to load one
       if (modelFileName == null)
           return;
       StringBuilder msg = new StringBuilder();
       try {
           // read            
           mr = new ModelReader(new FileReader(new File("models/"+modelFileName)));            
           mr.readModel();
           model = mr.getModel();            
           // write if exists
           File outModel = new File(modelFileName.substring(0,modelFileName.length()-4)+".bmf");
           if (!outModel.exists()) {
               ModelWriter mw = new ModelWriter(new FileOutputStream(outModel),model);
               mw.writeModel();
           }
       } catch (FileNotFoundException fnfe) {            
           fnfe.printStackTrace();
       } catch (NumberFormatException nfe) {
           //nfe.printStackTrace()
           msg.append("Line: ("+mr.getLineNumber()+ ") "+mr.getLastReadLine()+"\n\n");
           msg.append(nfe.getMessage()+"\n\n");
           for (StackTraceElement ste : nfe.getStackTrace()) {
               msg.append(ste.toString()+"\n");
           }
           JOptionPane.showMessageDialog(null,msg.toString(),"Error on model loading",JOptionPane.ERROR_MESSAGE);
           return;     // on number parse errors, return!        
       } catch (IOException ioe) {
           ioe.printStackTrace();
           //return;     // on read errors, just return to read the next model
       
       }
       textures = new HashMap<String,Integer>();
       // preload textures
       for (String material : model.getMaterials().keySet()) {            
           textures.put(material,TextureManager.getManager().getTexture("textures/"+model.getMaterials().get(material).trim()));        
       }
   }

   public void renderScene() {
           
       GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
       GL11.glLoadIdentity();
       GL11.glTranslatef(0.0f+translateX,-5.0f+translateY,-40.0f+translateZ);        
       GL11.glRotatef(rotateX,1.0f,0.0f,0.0f);
       GL11.glRotatef(rotateY,0.0f,1.0f,0.0f);
       GL11.glRotatef(rotateZ,0.0f,0.0f,1.0f);
       GL11.glListBase(modelList);
       GL11.glCallList(modelList);        
       
       if (autoRotate) {
           if (autoRotateX)
               rotateX();
           if (autoRotateY)
               rotateY();
           if (autoRotateZ)
               rotateZ();
       }        
   }

   public void preRenderScene() {
       if (model == null)
           return; // no model, no rendering
       
       GL11.glNewList(modelList,GL11.GL_COMPILE);          
       for (Mesh mesh : model.getMeshes()) {
           // bind the texture this mesh is using              
           if (model.getMaterials().get(mesh.getMaterial()) != null) {            
               GL11.glBindTexture(GL11.GL_TEXTURE_2D,textures.get(mesh.getMaterial()));                
           }              
   
           for (Face face : mesh.getFaces()) {                
               final int[] vertices = face.getVertices();
               final int[] uvs = face.getUVs();
               final int[] normals = face.getNormals();
               GL11.glBegin(face.getGLPolygonMode());                  
                   for (int i=0; i<vertices.length; i++) {                        
                       Vertex3<Float> v = model.getVertices().get(vertices[i]);
                       Normalf n = model.getNormals().get(normals[i]);                        
                       n.glNormal();
                       
                       if (uvs.length > 0) {                      
                           TexCoordf uv = model.getUVs().get(uvs[i]);
                           uv.glTexCoord();
                       }
                       GL11.glVertex3f(v.getX(),v.getY(),v.getZ());
                   }                      
               GL11.glEnd();
           }      
       }
       GL11.glEndList();
   }
   
   public void rotateX() {
       rotateX += rotateXSpeed;        
   }
   
   public void rotateY() {
       rotateY += rotateYSpeed;        
   }
   
   public void rotateZ() {
       rotateZ += rotateZSpeed;        
   }
   
   public void translateX() {
       translateX += translateXSpeed;        
   }
   
   public void translateY() {
       translateY += translateYSpeed;        
   }
   
   public void translateZ() {
       translateZ += translateZSpeed;        
   }
   
   public void setAutoRotateX(boolean auto) {
       this.autoRotateX = auto;
   }
   
   public void setAutoRotateY(boolean auto) {
       this.autoRotateY = auto;
   }
   
   public void setAutoRotateZ(boolean auto) {
       this.autoRotateZ = auto;
   }
   
   public void setAutoRotate(boolean auto) {
       this.autoRotate = auto;
   }
   
   public void setRotateXSpeed(float speed) {
       this.rotateXSpeed = speed;
   }
   
   public void setRotateYSpeed(float speed) {
       this.rotateYSpeed = speed;
   }
   
   public void setRotateZSpeed(float speed) {
       this.rotateZSpeed = speed;
   }
   
   public void setTranslateXSpeed(float speed) {
       this.translateXSpeed = speed;
   }
   
   public void setTranslateYSpeed(float speed) {
       this.translateYSpeed = speed;
   }
   
   public void setTranslateZSpeed(float speed) {
       this.translateZSpeed = speed;
   }
   
   public void setTexturing(boolean enable) {
       if (enable)
           GL11.glEnable(GL11.GL_TEXTURE_2D);
       else
           GL11.glDisable(GL11.GL_TEXTURE_2D);
   }
   
   public void setLighting(boolean enable) {
       if (enable)
           GL11.glEnable(GL11.GL_LIGHTING);
       else
           GL11.glDisable(GL11.GL_LIGHTING);
   }
   
   public void setWireframe(boolean enable) {
       if (enable)
           GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK,GL11.GL_LINE);
       else
           GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK,GL11.GL_FILL);
   }
   
   public void reset() {
       // reset rotations
       rotateX = 0f;
       rotateY = 0f;
       rotateZ = 0f;
       
       // reset translations
       translateX = 0f;
       translateY = 0f;
       translateZ = 0f;
   }

   public void setInitialized(boolean initialized) {
       this.canvasInitialized = initialized;
   }

   public boolean isInitialized() {    
       return this.canvasInitialized;
   }
   
   public void setModelFileName(String name) {
       if (name == null)
           throw new NullPointerException("Modelname can't be null");
       this.modelFileName = name;
   }
   
   public void resetRenderer() {
       // we are the first time here? ok, return
       if (textures == null || modelList == 0)
           return;
           
       int[] tex = new int[textures.size()];
       
       for (int i=0; i<tex.length; i++) {
           if (textures.get(i) == null)
               tex[i] = 0;
           else
               tex[i] = textures.get(i);
       }        
       // start with deleting the textures
       GL11.glDeleteTextures(BufferUtils.createIntBuffer(tex));
               
       // delete also the displaylist
       GL11.glDeleteLists(modelList,1);
       
       // reset also the placeholder
       textures = null;
       modelList = 0;
   }
   
   public final void setFOV(float fov) {
       this.fov = fov;
   }
   
   public final float getFOV() {
       return this.fov;
   }
   
   public final void updateFOV() {
       GL11.glMatrixMode(GL11.GL_PROJECTION);
       GL11.glLoadIdentity();
       // a nice 45Ã,° perspective
       GLU.gluPerspective(45.0f,(float)width/(float)height,0.1f,fov);
       GL11.glMatrixMode(GL11.GL_MODELVIEW);
       GL11.glLoadIdentity();
   }
   
   public final void takeScreenShot() {
       ByteBuffer screenData = BufferUtils.createByteBuffer(width*height*3);
       
       GL11.glReadPixels(0,0,(int)width,(int)height,GL11.GL_RGB,GL11.GL_UNSIGNED_BYTE,screenData);
       // swap the r and g values
       ByteBuffer swapedScreenData = BufferUtils.createByteBuffer(width*height*3);
       for (int i=0; i<swapedScreenData.capacity(); i+=3) {
           final byte red = screenData.get();
           final byte blue = screenData.get();
           final byte green = screenData.get();
           swapedScreenData.put(green);     // G
           swapedScreenData.put(blue);     // B
           swapedScreenData.put(red);     // R
       }
       swapedScreenData.flip();
       writeScreenShot(swapedScreenData);
   }
   
   private void writeScreenShot(ByteBuffer data) {
       byte[] tgaHeader = new byte[] { 0,0,2,0,0,0,0,0,0,0,0,0 };        
       final Date date = new Date();
       File screenFile = new File("Screenshot_"+date.getDate()+date.getMonth()+date.getYear()+"_"+date.getHours()+date.getMinutes()+".tga");
       try {
           FileOutputStream fos = new FileOutputStream(screenFile);
           DataOutputStream dos = new DataOutputStream(fos);
           // write the header
           fos.write(tgaHeader);
           dos.write(128);
           dos.write(2);
           dos.write(224);
           dos.write(1);
           dos.write(24);
           dos.write(0);
           // write the image data
           fos.getChannel().write(data);          
           fos.close();
       } catch (FileNotFoundException fnfe) {
           fnfe.printStackTrace();
       } catch (IOException ioe) {
           ioe.printStackTrace();
       }
       
   }
}
Title: AWTGLCanvas Example (Update: 17.02.2006)
Post by: Anonymous on January 24, 2006, 07:41:17
How do you handle canvas resizing?

I've been having problems getting gluPerspective to work once the GLContext is running.
Title: hmmmmmm...
Post by: Fool Running on January 24, 2006, 14:29:16
Just set up the perspective every paint call.
Title: AWTGLCanvas Example (Update: 17.02.2006)
Post by: Evil-Devil on February 17, 2006, 16:54:51
update:
As i removed the GUI code in the first time. Now there is a reworked version of it. I reused most of the actions code so it look cleaner and might be a little bit harder to read. But as you have to read anyway through it, that should no matter. Here we go, the GUI code.
BubbleModelViewer.java

/* =============================================================================
* BubbleModelViewer.java Bubble_Engine
* com.evildevil.bubble.tools
* Copyright (c) 2004 - 2005 Benjamin "Evil-Devil" Behrendt
* All rights reserved
* -------------------------------------------------------------------------- */
package com.evildevil.bubble.tools.model;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Hashtable;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSlider;
import javax.swing.JTextField;

import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.html.HTMLEditorKit;

import org.lwjgl.LWJGLException;

import com.evildevil.bubble.core.AWTOpenGLCanvas;

import com.evildevil.tools.designtools.Screen;

/**
* @author Benjamin "Evil-Devil" Behrendt
* @version 1.0, 11.11.2005
*/
public class BubbleModelViewer {
   
   private final String APP_TITLE = "Bubble Model Viewer";
   private final String APP_ABOUT_IMAGE_NAME = "resources/Bubble_Engine_Logo_Small.png";
   private final float APP_VERSION = 1.1f;
   private final String APP_ABOUT_TEXT = "<html><h2>"+APP_TITLE+"</h2><hr>"+
                               "<strong>Version:</strong> "+APP_VERSION+"<br><br>"+
                               "<strong><em>(c) Copyright 2005 Benjamin &quot;Evil-Devil&quot; Behrendt<br>"+
                               "All rights reserved</em></strong><hr>"+
                               "Visit us at: <a href=\"http://www.evil-devil.com\" target=\"_blank\" style=\"hover:font-weight: bold;\">"+
                               "http://www.evil-devil.com</a></html>";
   
   private JFrame mvFrame = null;
   private AWTOpenGLCanvas awtOGLCanvas = null;
   private JMenuBar mvMenuBar = null;
   
   private JCheckBox autoRot;
   private JCheckBox autoRotX;
   private JCheckBox autoRotY;
   private JCheckBox autoRotZ;
   private final JSlider rotXSpeed = createControlSlider(-100,100,0,10,5);
   private final JSlider rotYSpeed = createControlSlider(-100,100,0,10,5);
   private final JSlider rotZSpeed = createControlSlider(-100,100,0,10,5);
   private final JSlider moveXSpeed = createControlSlider(-100,100,0,10,5);
   private final JSlider moveYSpeed = createControlSlider(-100,100,0,10,5);
   private final JSlider moveZSpeed = createControlSlider(-100,100,0,10,5);
   private final JTextField rotX = new JTextField("0");
   private final JTextField rotY = new JTextField("0");
   private final JTextField rotZ = new JTextField("0");
   private final JTextField moveX = new JTextField("0");
   private final JTextField moveY = new JTextField("0");
   private final JTextField moveZ = new JTextField("0");
   private JCheckBox texturing;
   private JCheckBox wireframe;
   private JCheckBox lighting;
   
   public BubbleModelViewer() {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               createAndShowGUI();
           }
       });
   }
   
   private void createAndShowGUI() {
       mvFrame = new JFrame(this.APP_TITLE);
       mvFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       
       // add menu
       mvFrame.setJMenuBar(createModelViewerMenu());
       
       JPanel mvPanel = new JPanel();          // becomes our ContentPane
       JPanel displayPanel = new JPanel();     // will hold the GLCanvas
       JPanel toolPanel = new JPanel();        // takes our tools        
       JPanel consolePanel = new JPanel();     // takes our output console
       JPanel renderAutoPanel = new JPanel();
       renderAutoPanel.setLayout(new BoxLayout(renderAutoPanel,BoxLayout.LINE_AXIS));
       renderAutoPanel.add(createRenderingPanel());
       renderAutoPanel.add(Box.createRigidArea(new Dimension(8,0)));
       renderAutoPanel.add(createAutoRotationPanel());
       
       // mvPanel setup
       mvPanel.setLayout(new BoxLayout(mvPanel,BoxLayout.LINE_AXIS));
       
       // displayPanel setup
       displayPanel.setLayout(new BorderLayout());
       displayPanel.setMaximumSize(new Dimension(640,480));
       try {
           awtOGLCanvas = new AWTOpenGLCanvas(new ModelViewerRenderer());
           awtOGLCanvas.setSize(640,480);
           // init the scene data
       } catch (LWJGLException lwjgle) {
           lwjgle.printStackTrace();
       }
       displayPanel.add(awtOGLCanvas,BorderLayout.CENTER);
       
       // toolPanel setup
       toolPanel.setLayout(new BoxLayout(toolPanel,BoxLayout.PAGE_AXIS));
       toolPanel.setBorder(new EmptyBorder(2,4,2,2));
       toolPanel.add(renderAutoPanel);
       toolPanel.add(Box.createRigidArea(new Dimension(0,9)));
       toolPanel.add(createTranslationPanel());
       toolPanel.add(Box.createRigidArea(new Dimension(0,9)));
       toolPanel.add(createRotationPanel());
       
       // add everything
       mvPanel.add(displayPanel);
       mvPanel.add(toolPanel);
       mvFrame.getContentPane().add(mvPanel);
       
       // pack it and set the position
       mvFrame.pack();
       mvFrame.setLocation(Screen.getCenteredTopLeftPosition(mvFrame.getSize()));
       
       // Display the stuff
       mvFrame.setResizable(false);
       awtOGLCanvas.setVisible(true);
       mvFrame.setVisible(true);
   }
   
   private JPanel createRotationPanel() {
       final JPanel rotPanel = new JPanel();    // the final panel
       final TitledBorder title = new TitledBorder("Rotation");
       JPanel buttonPanel;
       JPanel sliderPanel;
       JPanel fieldPanel;
       
       rotPanel.setAlignmentY(Component.LEFT_ALIGNMENT);
       rotPanel.setBorder(title);
       rotPanel.setLayout(new BoxLayout(rotPanel,BoxLayout.LINE_AXIS));
       
       // create and add the buttons
       buttonPanel = new JPanel();
       buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.PAGE_AXIS));
       buttonPanel.add(createControlButton("Rotate X",new RotateAction("X")));
       buttonPanel.add(Box.createRigidArea(new Dimension(0,35)));
       buttonPanel.add(createControlButton("Rotate Y",new RotateAction("Y")));
       buttonPanel.add(Box.createRigidArea(new Dimension(0,38)));
       buttonPanel.add(createControlButton("Rotate Z",new RotateAction("Z")));
       
       rotPanel.add(buttonPanel);
       rotPanel.add(Box.createRigidArea(new Dimension(16,0)));
       
       // create and add the sliders
       sliderPanel = new JPanel();
       sliderPanel.setLayout(new BoxLayout(sliderPanel,BoxLayout.PAGE_AXIS));
       JLabel sliderLabel = createControlLabel("Speed");
       sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
       rotXSpeed.addChangeListener(new SliderAction("RX",rotX));
       sliderPanel.add(sliderLabel);
       sliderPanel.add(rotXSpeed);
       
       sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
       rotYSpeed.addChangeListener(new SliderAction("RY",rotY));
       sliderPanel.add(Box.createRigidArea(new Dimension(0,10)));
       sliderPanel.add(rotYSpeed);
       
       sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
       rotZSpeed.addChangeListener(new SliderAction("RZ",rotZ));
       sliderPanel.add(Box.createRigidArea(new Dimension(0,10)));
       sliderPanel.add(rotZSpeed);
       rotPanel.add(sliderPanel);
       rotPanel.add(Box.createRigidArea(new Dimension(16,0)));
       
       // create and add the textfields
       fieldPanel = new JPanel();
       fieldPanel.setLayout(new BoxLayout(fieldPanel,BoxLayout.PAGE_AXIS));
       fieldPanel.add(Box.createRigidArea(new Dimension(0,12)));
       rotX.setColumns(4);
       rotX.addActionListener(new SliderFieldAction(rotXSpeed,"RX"));
       fieldPanel.add(rotX);
       fieldPanel.add(Box.createRigidArea(new Dimension(0,30)));
       rotY.setColumns(4);
       rotY.addActionListener(new SliderFieldAction(rotYSpeed,"RY"));
       fieldPanel.add(rotY);
       fieldPanel.add(Box.createRigidArea(new Dimension(0,38)));
       rotZ.setColumns(4);
       rotZ.addActionListener(new SliderFieldAction(rotZSpeed,"RZ"));
       fieldPanel.add(rotZ);
       fieldPanel.add(Box.createRigidArea(new Dimension(0,12)));
       rotPanel.add(fieldPanel);
       
       return rotPanel;
   }
   
   private JPanel createAutoRotationPanel() {
       final JPanel rotPanel = new JPanel();
       final TitledBorder title = new TitledBorder("Auto-Rotation");
       
       rotPanel.setAlignmentY(Component.LEFT_ALIGNMENT);
       rotPanel.setLayout(new BoxLayout(rotPanel,BoxLayout.PAGE_AXIS));
       rotPanel.setBorder(title);
       
       final JPanel autoPanel = new JPanel();
       autoPanel.setLayout(new BoxLayout(autoPanel,BoxLayout.LINE_AXIS));
       autoPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
       autoPanel.add(autoRot = createControlCheckBox("Enable",new RenderingAndAutoRotateAction("AUTO"),true,false));
       
       final JPanel xyzPanel = new JPanel();
       xyzPanel.setLayout(new BoxLayout(xyzPanel,BoxLayout.LINE_AXIS));
       xyzPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
       xyzPanel.add(autoRotX = createControlCheckBox("X",new RenderingAndAutoRotateAction("X"),false,false));
       xyzPanel.add(Box.createRigidArea(new Dimension(16,0)));
       xyzPanel.add(autoRotY = createControlCheckBox("Y",new RenderingAndAutoRotateAction("Y"),false,false));
       
       xyzPanel.add(Box.createRigidArea(new Dimension(16,0)));
       xyzPanel.add(autoRotZ = createControlCheckBox("Z",new RenderingAndAutoRotateAction("Z"),false,false));
       
       rotPanel.add(autoPanel);
       rotPanel.add(Box.createVerticalStrut(8));
       rotPanel.add(xyzPanel);
       
       return rotPanel;
   }
   
   private JPanel createTranslationPanel() {
       final JPanel rotPanel = new JPanel();    // the final panel
       final TitledBorder title = new TitledBorder("Translation");
       JPanel buttonPanel;
       JPanel sliderPanel;
       JPanel fieldPanel;
       
       rotPanel.setAlignmentY(Component.LEFT_ALIGNMENT);
       rotPanel.setBorder(title);
       rotPanel.setLayout(new BoxLayout(rotPanel,BoxLayout.LINE_AXIS));
       
       // create and add the buttons with the apropriate listeners
       buttonPanel = new JPanel();
       buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.PAGE_AXIS));
       buttonPanel.add(createControlButton("Transl. X",new TranslateAction("X")));
       buttonPanel.add(Box.createRigidArea(new Dimension(0,35)));
       buttonPanel.add(createControlButton("Transl. Y",new TranslateAction("Y")));
       buttonPanel.add(Box.createRigidArea(new Dimension(0,38)));
       buttonPanel.add(createControlButton("Transl. Z",new TranslateAction("Z")));
       
       rotPanel.add(buttonPanel);
       rotPanel.add(Box.createRigidArea(new Dimension(16,0)));
       
       // create and add the sliders
       sliderPanel = new JPanel();
       sliderPanel.setLayout(new BoxLayout(sliderPanel,BoxLayout.PAGE_AXIS));
       JLabel sliderLabel = createControlLabel("Speed");
       sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
       moveXSpeed.addChangeListener(new SliderAction("TX",moveX));
       sliderPanel.add(sliderLabel);
       sliderPanel.add(moveXSpeed);
       
       sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
       moveYSpeed.addChangeListener(new SliderAction("TY",moveY));
       sliderPanel.add(Box.createRigidArea(new Dimension(0,10)));
       sliderPanel.add(moveYSpeed);
       
       sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
       moveZSpeed.addChangeListener(new SliderAction("TZ",moveZ));
       sliderPanel.add(Box.createRigidArea(new Dimension(0,10)));
       sliderPanel.add(moveZSpeed);
       rotPanel.add(sliderPanel);
       rotPanel.add(Box.createRigidArea(new Dimension(16,0)));
       
       // create and add the textfields
       fieldPanel = new JPanel();
       fieldPanel.setLayout(new BoxLayout(fieldPanel,BoxLayout.PAGE_AXIS));
       fieldPanel.add(Box.createRigidArea(new Dimension(0,12)));
       moveX.setColumns(4);
       moveX.addActionListener(new SliderFieldAction(moveXSpeed,"TX"));
       fieldPanel.add(moveX);
       fieldPanel.add(Box.createRigidArea(new Dimension(0,30)));
       moveY.setColumns(4);
       moveY.addActionListener(new SliderFieldAction(moveYSpeed,"TY"));
       fieldPanel.add(moveY);
       fieldPanel.add(Box.createRigidArea(new Dimension(0,38)));
       moveZ.setColumns(4);
       moveZ.addActionListener(new SliderFieldAction(moveZSpeed,"TZ"));
       fieldPanel.add(moveZ);
       fieldPanel.add(Box.createRigidArea(new Dimension(0,12)));
       rotPanel.add(fieldPanel);
       
       return rotPanel;
   }
   
   private JPanel createRenderingPanel() {
       final JPanel renderPanel = new JPanel();
       final TitledBorder title = new TitledBorder("Rendering");
       
       renderPanel.setAlignmentY(Component.LEFT_ALIGNMENT);
       renderPanel.setBorder(title);
       renderPanel.setLayout(new BoxLayout(renderPanel,BoxLayout.PAGE_AXIS));
       
       JPanel panel = new JPanel();
       panel.setLayout(new BoxLayout(panel,BoxLayout.LINE_AXIS));
       panel.setAlignmentX(Component.LEFT_ALIGNMENT);
       panel.add(texturing = createControlCheckBox("Texturing",new RenderingAndAutoRotateAction("TEXTURE"),true));
       panel.add(Box.createRigidArea(new Dimension(13,0)));
       panel.add(lighting = createControlCheckBox("Lighting",new RenderingAndAutoRotateAction("LIGHT"),true));
       renderPanel.add(panel);
       renderPanel.add(Box.createRigidArea(new Dimension(0,8)));
       
       panel = new JPanel();
       panel.setLayout(new BoxLayout(panel,BoxLayout.LINE_AXIS));
       panel.setAlignmentX(Component.LEFT_ALIGNMENT);
       panel.add(wireframe = createControlCheckBox("Wireframe",new RenderingAndAutoRotateAction("WIREFRAME"),false));
       panel.add(Box.createRigidArea(new Dimension(4,0)));
       panel.add(createControlCheckBox("Reset all",new RenderingAndAutoRotateAction("RESET"),false));
       renderPanel.add(panel);
       
       return renderPanel;
   }
   
   private void setAutoRotate(boolean auto) {
       this.autoRotX.setEnabled(auto);
       this.autoRotY.setEnabled(auto);
       this.autoRotZ.setEnabled(auto);
   }
   
   
   
   private JLabel createControlLabel(String title) {
       final JLabel label = new JLabel(title);
       return label;
   }

   private JButton createControlButton(String title) {
       final JButton button = new JButton(title);
       // we want small buttons ^_^
       button.setMargin(new Insets(1,8,0,8));
       return button;
   }
   
   private JButton createControlButton(String title, ActionListener listener) {
       final JButton button = createControlButton(title);
       button.addActionListener(listener);
       return button;
   }
   
   private JCheckBox createControlCheckBox(String title, ItemListener listener, boolean enabled, boolean selected) {
       final JCheckBox box = createControlCheckBox(title,listener,selected);
       box.setEnabled(enabled);
       return box;
   }
   
   private JCheckBox createControlCheckBox(String title, ItemListener listener, boolean selected) {
       final JCheckBox box = new JCheckBox(title);
       box.setSelected(selected);
       box.addItemListener(listener);
       return box;
   }
   
   private JSlider createControlSlider(int min, int max, int start,int major, int minor) {
       final JSlider slider = new JSlider(min,max,start);
       final Hashtable<Integer,JLabel> sliderTexts = new Hashtable<Integer,JLabel>();
       sliderTexts.put(min,new JLabel(Float.toString(min/major)));
       sliderTexts.put(start,new JLabel(Float.toString(start)));
       sliderTexts.put(max,new JLabel(Float.toString(max/major)));
       
       slider.setMajorTickSpacing(major);
       slider.setMinorTickSpacing(minor);
       slider.setLabelTable(sliderTexts);
       slider.setPaintTicks(true);
       slider.setPaintTrack(true);
       slider.setPaintLabels(true);
       return slider;
   }
   
   private JMenuBar createModelViewerMenu() {
       final JMenuBar bar = new JMenuBar();
       JPopupMenu.setDefaultLightWeightPopupEnabled(false);    // disable leightweight
       JMenu menu = new JMenu("File");
       menu.add(createMenuItem("Open",new OpenAction()));
       //menu.addSeparator();
       //menu.add(createMenuItem("Show Temple Model",new ShowAction("temple_tex.obj")));
       //menu.add(createMenuItem("Show F16 Model",new ShowAction("AF_F16.obj")));
       //menu.addSeparator();
       menu.add(createMenuItem("Preferences",new PreferencesAction()));
       menu.add(createMenuItem("Take Screenshot",new ScreenShotAction()));
       menu.add(createMenuItem("Exit",new ExitAction()));
       bar.add(menu);
       menu = new JMenu("?");
       menu.add(createMenuItem("About",new AboutAction()));
       bar.add(menu);
       JPopupMenu.setDefaultLightWeightPopupEnabled(true);     // enable leightweight
       return bar;
   }
   
   private JMenuItem createMenuItem(String name, ActionListener listener) {
       final JMenuItem item = new JMenuItem(name);
       item.addActionListener(listener);
       return item;
   }
   
   private void reset() {
       // reset all checkboxes and sliders we have
       texturing.setSelected(true);
       lighting.setSelected(true);
       wireframe.setSelected(false);
       
       // reset the autorot stuff
       autoRot.setSelected(false);
       autoRotX.setSelected(false);
       autoRotY.setSelected(false);
       autoRotZ.setSelected(false);
       
       // reset the rotspeed sliders
       rotXSpeed.setValue(0);
       rotYSpeed.setValue(0);
       rotZSpeed.setValue(0);
       
       // reset the transspeed sliders
       moveXSpeed.setValue(0);
       moveYSpeed.setValue(0);
       moveZSpeed.setValue(0);
       
       // reset also the rotation and translation of the model
       getRenderer().reset();
   }
   
   public ModelViewerRenderer getRenderer() {
       return (ModelViewerRenderer)awtOGLCanvas.getRenderer();
   }
   
   /* =========================================================================
    * MAIN Entrance
    * ---------------------------------------------------------------------- */
   public static void main(String[] args) {
       BubbleModelViewer bmv = new BubbleModelViewer();
   }
   
   /* =========================================================================
    * ACTIONS
    * ---------------------------------------------------------------------- */
   class ExitAction extends AbstractAction {
       
       public void actionPerformed(ActionEvent e) {
           mvFrame.dispose();
           System.exit(0);     // just to make sure the JVM will exit
       }
   }
   
   class AboutAction extends AbstractAction {
       
       public void actionPerformed(ActionEvent e) {
           SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                   createAboutDialog();
               }
           });
       }
       
       private void createAboutDialog() {
           final JDialog dialog = new JDialog(mvFrame,"About",true);
           dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
           JPanel panel = new JPanel();
           panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
           panel.setBorder(new EmptyBorder(5,5,5,5));
           
           JEditorPane textPane = new JEditorPane();
           textPane.setEditable(false);
           textPane.setBackground(dialog.getBackground());
           textPane.setEditorKit(new HTMLEditorKit());
           textPane.setText(APP_ABOUT_TEXT);
           panel.add(textPane);
           
           JButton okButton = new JButton("OK");
           okButton.setAlignmentX(Component.RIGHT_ALIGNMENT);
           okButton.addActionListener(new ActionListener() {
               public void actionPerformed(ActionEvent e) {
                   dialog.dispose();
               }
           });
           panel.add(Box.createRigidArea(new Dimension(0,8)));
           panel.add(okButton);
           dialog.setContentPane(panel);
           
           // packing :)
           dialog.pack();
           // center it
           dialog.setLocation(Screen.getCenteredTopLeftPosition(dialog.getSize()));
           dialog.setVisible(true);
       }
   }
   
   class OpenAction extends AbstractAction {
       
       public void actionPerformed(ActionEvent e) {
           SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                   showOpenDialog();
               }
           });
       }
       
       private void showOpenDialog() {
           JFileChooser fc = new JFileChooser("models/");
           int result = fc.showOpenDialog(mvFrame);
           
           if (result == JFileChooser.APPROVE_OPTION) {
               // we assign the rendercanvas to our own one :)
               final ModelViewerRenderer renderer = getRenderer();
               // only load the model if the canvas that is used by the renderer
               // acknowlegded the renderer that its ready
               if (renderer.isInitialized()) {
                   // reset any modified controls
                   reset();
                   // reset the renderer just to be sure there is no old data rest
                   renderer.resetRenderer();
                   // set the modelFileName
                   renderer.setModelFileName(fc.getSelectedFile().getName());
                   // init the data
                   renderer.initScene();
                   // we got the data, now pre-render it
                   renderer.preRenderScene();
                   // everything is done, the model should now be displayed
               }
           }
       }
   }
   
   class PreferencesAction extends AbstractAction {
       private float fov = 0f;
       private boolean needUpdate = false;
       
       public void actionPerformed(ActionEvent e) {
           SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                   showPreferences();
               }
           });
       }
       
       private void showPreferences() {
           final JDialog dialog = new JDialog(mvFrame,"Preferences",true);
           final JButton okButton = new JButton("OK");
           final JButton cancelButton = new JButton("Cancel");
           final JButton applyButton = new JButton("Apply");
           dialog.setSize(223,98);
           final float x = mvFrame.getX() + mvFrame.getWidth()/2 - dialog.getWidth()/2;
           final float y = mvFrame.getY() + mvFrame.getHeight()/2 - dialog.getHeight()/2;
           dialog.setLocation((int)x,(int)y);
           JPanel dialogPanel = new JPanel();
           dialogPanel.setLayout(new BoxLayout(dialogPanel,BoxLayout.PAGE_AXIS));
           dialogPanel.setBorder(new EmptyBorder(5,5,5,5));
           
           // build the dialog GUI
           JPanel panel = new JPanel();
           final JTextField fovField = new JTextField(Float.toString(getRenderer().getFOV()));
           fovField.setColumns(5);
           fovField.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent e) {
                  final JTextField source = (JTextField)e.getSource();
                  if (source.getText() == null || source.getText().length() == 0)
                      return;
                  try {
                      fov = Float.parseFloat(source.getText());
                  } catch (NumberFormatException nfe) {
                      source.setText(Float.toString(getRenderer().getFOV()));
                  }
                  // as the value was changed or even not, disable the ok button and enable apply button
                  okButton.setEnabled(false);
                  applyButton.setEnabled(true);
              }
           });
           panel.setLayout(new BoxLayout(panel,BoxLayout.LINE_AXIS));
           panel.add(createControlLabel("Field Of View (FOV):"));
           panel.add(Box.createRigidArea(new Dimension(8,0)));
           panel.add(fovField);
           dialogPanel.add(panel);
           dialogPanel.add(Box.createRigidArea(new Dimension(0,8)));
           
           panel = new JPanel();
           okButton.addActionListener(new ActionListener() {
               public void actionPerformed(ActionEvent e) {
                   // call the renderer updateFOV()
                   // only update if the new fov differs from the current
                   if (needUpdate) {
                       // make sure that we have a valid renderer,
                       // for the case that someone change something within
                       // the preferences before everything else
                       getRenderer().updateFOV();
                       needUpdate = false;
                   }
                   // exit the dialog
                   dialog.dispose();
               }
           });
           
           cancelButton.addActionListener(new ActionListener() {
               public void actionPerformed(ActionEvent e) {
                   // just exist without doing something;
                   dialog.dispose();
               }
           });
           
           applyButton.setEnabled(false);
           applyButton.addActionListener(new ActionListener() {
               public void actionPerformed(ActionEvent e) {
                   // update the fov value
                   if (fov > 0f && fov != getRenderer().getFOV())
                       getRenderer().setFOV(fov);
                   // enable the ok button
                   okButton.setEnabled(true);
                   // disable you
                   applyButton.setEnabled(false);
                   needUpdate = true;
               }
           });
           panel.setLayout(new BoxLayout(panel,BoxLayout.LINE_AXIS));
           panel.add(okButton);
           panel.add(Box.createRigidArea(new Dimension(8,0)));
           panel.add(cancelButton);
           panel.add(Box.createRigidArea(new Dimension(8,0)));
           panel.add(applyButton);
           dialogPanel.add(panel);
           dialog.setContentPane(dialogPanel);
           dialog.pack();
           dialog.setVisible(true);
       }
   }
   
   class ScreenShotAction extends AbstractAction {
               
       public void actionPerformed(ActionEvent e) {
           if (getRenderer().isInitialized())
               getRenderer().takeScreenShot();
       }
   }
   
   class RotateAction extends AbstractAction {
       private String rotationTarget = null;
       
       public RotateAction(String rotation) {
           this.rotationTarget = rotation;
       }
       
       public void actionPerformed(ActionEvent e) {
           if (rotationTarget.equalsIgnoreCase("X"))
               ((ModelViewerRenderer)awtOGLCanvas.getRenderer()).rotateX();
           if (rotationTarget.equalsIgnoreCase("Y"))
               ((ModelViewerRenderer)awtOGLCanvas.getRenderer()).rotateY();
           if (rotationTarget.equalsIgnoreCase("Z"))
               ((ModelViewerRenderer)awtOGLCanvas.getRenderer()).rotateZ();
       }        
   }
   
   class TranslateAction extends AbstractAction {
       private String translateTarget = null;
       
       public TranslateAction(String translation) {
           this.translateTarget = translation;
       }
       
       public void actionPerformed(ActionEvent e) {
           if (translateTarget.equalsIgnoreCase("X"))
               getRenderer().translateX();
           if (translateTarget.equalsIgnoreCase("Y"))
               getRenderer().translateY();
           if (translateTarget.equalsIgnoreCase("Z"))
               getRenderer().translateZ();
       }    
   }
   
   class RenderingAndAutoRotateAction implements ItemListener {
       private String renderingTarget = null;
       
       public RenderingAndAutoRotateAction(String rendering) {
           this.renderingTarget = rendering;
       }
       
       public void itemStateChanged(ItemEvent e) {
           final boolean active = e.getStateChange() == ItemEvent.SELECTED ? true : false;
           
           if (renderingTarget.equalsIgnoreCase("TEXTURE"))
               getRenderer().setTexturing(active);
           if (renderingTarget.equalsIgnoreCase("LIGHT"))
               getRenderer().setLighting(active);
           if (renderingTarget.equalsIgnoreCase("WIREFRAME"))
               getRenderer().setWireframe(active);
           if (renderingTarget.equalsIgnoreCase("RESET") && active) {
               reset();    // reset our GUI elements and the renderer                                
               ((JCheckBox)e.getSource()).setSelected(false);
           }
           if (renderingTarget.equalsIgnoreCase("AUTO")) {
               setAutoRotate(active);
               getRenderer().setAutoRotate(active);
           }
           if (autoRot.isEnabled()) {
               if (renderingTarget.equalsIgnoreCase("X"))
                   getRenderer().setAutoRotateX(active);
               if (renderingTarget.equalsIgnoreCase("Y"))
                   getRenderer().setAutoRotateY(active);
               if (renderingTarget.equalsIgnoreCase("Z"))
                   getRenderer().setAutoRotateZ(active);
           }
       }
   }
   
   class SliderAction implements ChangeListener {
       private String sliderTarget = "";
       private JTextField inputTarget = null;
       
       public SliderAction(String slider, JTextField input) {
           this.sliderTarget = slider;
           this.inputTarget = input;
       }
       
       public void stateChanged(ChangeEvent e) {
           final JSlider slider = (JSlider)e.getSource();
           
           if(!slider.getValueIsAdjusting()) {
               final float speed = (float)slider.getValue()/10;                
               // rotation speed
               if (sliderTarget.equalsIgnoreCase("RX"))
                   getRenderer().setRotateXSpeed(speed);
               if (sliderTarget.equalsIgnoreCase("RY"))
                   getRenderer().setRotateYSpeed(speed);
               if (sliderTarget.equalsIgnoreCase("RZ"))
                   getRenderer().setRotateZSpeed(speed);
               
               // translation speed
               if (sliderTarget.equalsIgnoreCase("TX"))
                   getRenderer().setTranslateXSpeed(speed);
               if (sliderTarget.equalsIgnoreCase("TY"))
                   getRenderer().setTranslateYSpeed(speed);
               if (sliderTarget.equalsIgnoreCase("TZ"))
                   getRenderer().setTranslateZSpeed(speed);
               if (inputTarget != null)
                   inputTarget.setText(Float.toString((float)slider.getValue()/10));
           }            
       }
   }
   
   class SliderFieldAction extends AbstractAction {
       private JSlider sliderTarget = null;        
       private String renderTarget = "";

       public SliderFieldAction(JSlider slider, String render) {
           this.sliderTarget = slider;            
           this.renderTarget = render;
       }
       
       public void actionPerformed(ActionEvent e) {
           final JTextField field = (JTextField)e.getSource();
           
           if (field.getText() != null) {
               try {
                   final int value = Integer.parseInt(field.getText().substring(0,field.getText().length() > 3 ? 3 : field.getText().length()));
                   applyToSlider(this.sliderTarget,value,10);
                   field.setText(Float.toString(value));
                   setRenderAction(this.renderTarget,value);
               } catch (NumberFormatException nfe) {
                   field.setText("0");
                   setRenderAction(this.renderTarget,0);
               }
           }
       }
       
       private void setRenderAction(String render, float value) {
           if (render.equalsIgnoreCase("TX"))
               getRenderer().setTranslateXSpeed(value);
           if (render.equalsIgnoreCase("TY"))
               getRenderer().setTranslateYSpeed(value);
           if (render.equalsIgnoreCase("TZ"))
               getRenderer().setTranslateZSpeed(value);
           if (render.equalsIgnoreCase("RX"))
               getRenderer().setRotateXSpeed(value);
           if (render.equalsIgnoreCase("RY"))
               getRenderer().setRotateYSpeed(value);
           if (render.equalsIgnoreCase("RZ"))
               getRenderer().setRotateZSpeed(value);
       }
       
       private void applyToSlider(JSlider slider, int value, int multiple) {
           if (value >= slider.getMaximum())
               slider.setValue(slider.getMaximum());
           else if (value <= slider.getMinimum())
               slider.setValue(slider.getMinimum());
           else slider.setValue(value*multiple);
       }        
   }
   
   class ShowAction extends AbstractAction {
       private String modelName = null;
       
       public ShowAction(String model) {
           this.modelName = model;
       }        

       public void actionPerformed(ActionEvent e) {          
           final ModelViewerRenderer renderer = getRenderer();
           // only load the model if the canvas that is used by the renderer
           // acknowlegded the renderer that its ready
           if (this.modelName != null && renderer.isInitialized()) {
               // reset any modified controls
               reset();
               // reset the renderer just to be sure there is no old data rest
               renderer.resetRenderer();
               // set the modelFileName
               renderer.setModelFileName(this.modelName);
               // init the data
               renderer.initScene();
               // we got the data, now pre-render it
               renderer.preRenderScene();
               // everything is done, the model should now be displayed
           }
       }
       
   }
}