LWJGL Forum

Programming => Lightweight Java Gaming Library => Topic started by: daemon2008 on January 10, 2008, 11:36:35

Title: LWJGL Renderer in split window
Post by: daemon2008 on January 10, 2008, 11:36:35

Hi,

Was wondering if anyone could please provide some info on a custom OBJ Viewer i am writing. Its for my dissertation and i've made a class which is able to render OBJ files using the LWJGL Display class and it works great.  The problem is that i want to be able to view two models in the same 'frame'. So, is there a way to 'split' the Display class into two panels and have a renderer class instance running in each panel. Or to write a new class which creates two new 'Renderer' objects and displays them in a kind of AWT style Frame (BorderLayout) etc.

Hope that makes sense.

Any help would be great.
Thanks!
Dan
Title: Re: LWJGL Renderer in split window
Post by: Matzon on January 10, 2008, 12:50:14
you could use the AWTGLCanvas - but should be possible to render two models in one loop ??? - just align them besides each other, set the camera accordingly - and away you go?
Title: Re: LWJGL Renderer in split window
Post by: daemon2008 on January 10, 2008, 14:08:46
Hi, thanks for the reply.

I was thinking about rendering both in one loop. But wouldnt that mean re-locating one of the objects in 3d space? I am creating a mesh morphing program in lwjgl with obj files and i think if i were to do that then the positions in 3d space would effect the morph.  Ideally i need to render them in two separate Renderer objects.

Does the AWTGLCanvas canvas support the LWJGL Display component and would i be able to split it half?

Thanks
Dan
Title: Re: LWJGL Renderer in split window
Post by: wolf_m on January 10, 2008, 15:26:17
You just need to push the matrix, move to an object's origin, then draw it and pop it afterwards to keep matrices distinct from each other. That way, you can stay in one Display and handle each object's modelview matrix separately.

You basically don't need more than one Display for your task.

If you want two Displays, start two programs. That's the easiest way. But it doesn't make much sense here and sucks performance-wise anyway.
Title: Re: LWJGL Renderer in split window
Post by: daemon2008 on January 10, 2008, 20:19:13

Thanks for the reply wolf. 

I've looked on the NeHe tutorials page and they have something similar as well.
Title: Re: LWJGL Renderer in split window
Post by: darkprophet on January 12, 2008, 01:46:54
You could do what wolf_m said, but that would require you to have two sets of pushing for every object you have per camera and it would just get ugly.

I suggest you use glViewport(...); to split the window up however you want. On each render, use glScisscor with the same position/dimension as the glViewport of that mini display of yours.

So (in pseudocode):

glViewport(...);
glEnable(GL_SCISSOR);
glScissor(...);
glclear(...);
glDisable(GL_SCISSOR);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity(),
transform(camera); // translates and rotates


Then your objects are ready to render for that camera:

glPushMatrix();
transform(object);
draw(object);
glPopMatrix();


This allows you to define a global coordinate system for all your objects, but multiple cameras and multiple viewports in which to see them from.

Edit: Clarified some code.

HTH, DP :)
Title: Re: LWJGL Renderer in split window
Post by: daemon2008 on January 13, 2008, 12:52:06
Ah thats more like what i'm after!

Would all this happen in the render loop?  I'm not too experienced with lwjgl unfortunately, although i do understand the basics..  The below code is my renderer class (with quite a lot taken out) which is based on someone elses basic renderer.  Do this look right (roughly) for each object in the render loop:

      GL11.glScissor(x, y, width, height);
      glEnable(GL_SCISSOR);
      GL11.glViewport(...);
      GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
      GL11.glDisable(GL_SCISSOR);
      GL11.glMatrixMode(GL_MODELVIEW);
      GL11.glLoadIdentity();
      
      GL11.glRotatef(rotateModelY, 0, 1, 0); // around vertical axis
      GL11.glRotatef(rotateModelX, 1, 0, 0); // around horizontal axis
      
      GL11.glPushMatrix();
      renderMesh(object1,0);
      GL11.glPopMatrix();


THANKS AGAIN!


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

RENDERER CODE:

public class GLRenderer{

   private String WINDOW_TITLE = "OBJModel";
   private DisplayMode displayMode;   //Display perspectives and aspect ratios

   public GL_Mesh obj;  //Single OBJ Object
   private String modelLocation;  

   private float z = 0.0f;   // Depth Into The Screen

   private float rotateModelX = 0;
   private float rotateModelY = 0;
   private boolean normalsOn = true;
   private int cursorX, cursorY;

   // camera position and rotation (see handleNavigationKeys())
   float[] cameraPos = new float[] {0,0,100};
   float cameraRotation = 0f;
   final float piover180 = 0.0174532925f;// A constant used in navigation

   // for mouse drag
   Vector3D mousePrevPos = null;
   boolean mouseIsDown = false;
   Vector3D mousePoint = new Vector3D(.1f,0,100);

   // Arrays to hold matrices for projectVerts() operation.
   // For memory efficiency, instantiate these once and reuse.
   // see getModelviewMatrixA(), getMatrixAsArray() and mesh.projectVerts().
   public static float[][] modelViewMatrix = new float[4][4];
   public static float[][] projectionMatrix = new float[4][4];
   public static int[]     viewport = new int[4];

   // color of overall scene lighting
   float ambient[]       = { 0f, 0f, 0f, 1f };

   // color of light source
   float lightDiffuse[]  = { .9f, .9f, .6f, 1f }; // direct light
   float lightSpecular[] = { .9f, .9f, .6f, 1f }; // highlight
   float lightAmbient[]  = { .5f, .0f, .0f, 1f }; // scattered light

   // light position: if last value is 0, then this describes light direction.  
   // If 1, then light position.
   float lightPosition[] = { -100f, 100f, 100, 0f };

   // color of material
   float mtlDiffuse[]      = { .6f, 0f, 0f, 1f };    // red
   float mtlAmbient[]      = { .2f, 0f, 0f, 1f };   // dark red
   float mtlSpecular[]     = { .8f, .8f, .8f, 1f }; // almost white: very reflective
   float mtlShininess      = 100f;   // 0=no shine,  127=max shine

   private boolean done = false;
   private boolean norm = false;
   private boolean tetra = false;
   private boolean renderAnchors = false;
   private boolean objLoaded = false;

   private Tetrahedron tet;
   public int[] anchors;

   private GL_Vertex modelCenter;
   private boolean wireframe;


   public GLRenderer(GL_Mesh mesh){
      this.obj = mesh;
      objLoaded = true;   
   }

   public GLRenderer(String filename){
      this.modelLocation = filename;
      run();
   }


   public void run() {
      try {
         init();
         while (!done) {
            if(!norm){
               MainLoop();
               render();
               Display.update();}
         }
         cleanup();
      }
      catch (Exception e) {
         e.printStackTrace();
         System.exit(0);
      }
   }


   public void init() throws Exception {
      createWindow();
      initGL();

      if(!objLoaded){
         Importer importOBJ = new Importer();
         obj = importOBJ.importOBJ(modelLocation);}


      // Get the current projection and viewport matrices
      // for use in projectVerts().  Modelview matrix will change
      // every frame, so we'll get that in render().
      projectionMatrix = getProjectionMatrixA();
      viewport = getViewportA();
   }

   public void initGL() {
      Display.setVSyncEnabled(false);    //Seriously slows down FPS


      // Select the Projection Matrix (controls perspective)
      GL11.glMatrixMode(GL11.GL_PROJECTION);
      GL11.glLoadIdentity();    // Reset The Projection Matrix

      // Define perspective
      GLU.gluPerspective(
            30.0f,        // Field Of View
            (float)displayMode.getWidth() / (float)displayMode.getHeight(), // aspect ratio
            0.01f,         // near Z clipping plane
            1000.0f);      // far Z clipping plane

      // Select The Modelview Matrix (controls model orientation)
      GL11.glMatrixMode(GL11.GL_MODELVIEW);
      GL11.glLoadIdentity();    // Reset The Modelview Matrix

      // make sure OpenGL correctly layers objects
      GL11.glEnable(GL11.GL_DEPTH_TEST);

      // OpenGL won't draw backward facing triangles ("back faces")
      GL11.glEnable(GL11.GL_CULL_FACE);

      // turn lighting on (does not create a light)
      GL11.glEnable(GL11.GL_LIGHTING);


      // Create a light
      // diffuse is the color of direct light from this light source
      // specular is the hightlight color
      // ambient is the color of scattered light from this source
      // position is where the light is, or it's direction
      setLight( GL11.GL_LIGHT1, lightDiffuse, lightAmbient, lightSpecular, lightPosition );

      // no overall scene lighting
      setAmbientLight(ambient);
      GL11.glShadeModel(GL11.GL_SMOOTH);

      // set the background color
      GL11.glClearColor(.08f, .08f, .1f, 1);
      GL11.glLineWidth(1);

   }

   public void createWindow() throws Exception {

      Display.setFullscreen(false);
      // get all possible display resolutions
      DisplayMode d[] = Display.getAvailableDisplayModes();
      // find a resolution we like
      for (int i = 0; i < d.length; i++) {
         if (d.getWidth() == 800
               && d.getHeight() == 600
               && d.getBitsPerPixel() == 32) {
            displayMode = d;
            break;
         }
      }
      // set the display to the resolution we picked
      Display.setDisplayMode(displayMode);
      Display.setTitle(WINDOW_TITLE);
      // create the window
      // specify Pixel Format:
      //        0    -- default bits per pixel for alpha values
      //        24   -- 24 bits per pixel for depth buffer
      //        0    -- default bits per pixel for stencil buffer
      // NOTE:
      //     we specify 24 bits per pixel instead of the default
      //     8 bits because this scene is deep
      //     (zdepth is .01 - 1000 in the gluPerspective() command)
      //     and 8 bit values only hold 0-255 (rounding
      //     errors cause close objects to overlap instead of
      //     correctly layering.
      Display.create(new PixelFormat(0,24,0));

   }


   public void MainLoop() {
      if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {  
         done = true;
      }
      // Has Window been closed
      if(Display.isCloseRequested()) {                
         done = true;
      }
      // Handle key press events (down/up)
      handleKeyPressEvents();
      // Handle arrow keys (press and hold)
      handleNavigationKeys();

      handleMouseEvents();
      //cleanup();
   }

   public void render() {  
      // Clear screen and depth buffer
      GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

      // reset the coordinate system to center of screen
      GL11.glLoadIdentity();

      GL11.glPushMatrix();
      {
         // Place the 'camera'
         GLU.gluLookAt(   // camera position
               cameraPos[0], cameraPos[1], cameraPos[2],
               // look at a point directly in front of camera
               cameraPos[0]- (float) Math.sin(cameraRotation* piover180), cameraPos[1],
               cameraPos[2]- (float) Math.cos(cameraRotation* piover180),
               // which way is up
               0f, 1f, 0f);

         // alternative
         // shift and rotate entire scene (opposite to "camera" position)
         //GL11.glRotatef((360.0f-cameraRotation), 0, 1f, 0); // first
         // rotate around y axis
         //GL11.glTranslatef( -cameraPos[0], -cameraPos[1], -cameraPos[2]);
         // // then move forward on x,z axis (staying on ground, so no Y
         // motion)

         // set the light position (have to do this each render() since
         // we're moving the scene around with navigation, have to update
         // light position. Light pos is transformed just like a vertex).
         //setLightPos(GL11.GL_LIGHT1, lightPosition[0], lightPosition[1],   lightPosition[2]);

         // draw white sphere at light position
         GL11.glDisable(GL11.GL_LIGHTING);
         GL11.glColor4f(1, 1, .9f, 1);
         GL11.glPushMatrix();
         {
            //GL11.glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
            //renderSphere();
         }
         GL11.glPopMatrix();
         GL11.glEnable(GL11.GL_LIGHTING);

         GL11.glPushMatrix();
         {
            // rotate model (based on mouse drag)
            GL11.glRotatef(rotateModelY, 0, 1, 0); // around vertical axis
            GL11.glRotatef(rotateModelX, 1, 0, 0); // around horizontal axis

            renderMesh(obj,0);
            if(tetra){
               drawTetrahedron();
            }
            if(renderAnchors){
               highlightFixPoints();
            }
            //pps+=polycount;  

         }
         GL11.glPopMatrix();

      }
      GL11.glPopMatrix();
   }

   public void renderMesh(GL_Mesh o, int textureHandle)

   {
      GL_Triangle t;
      GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
      GL11.glBegin(GL11.GL_TRIANGLES);
      for (int j = 0; j < o.triangles.length; j++) { // draw all triangles in object
         t = o.triangles[j];

         GL11.glTexCoord2f(t.uvw1.x, t.uvw1.y);
         GL11.glNormal3f(t.norm1.x, t.norm1.y, t.norm1.z);
         GL11.glVertex3f( (float)t.p1.pos.x, (float)t.p1.pos.y, (float)t.p1.pos.z);

         GL11.glTexCoord2f(t.uvw2.x, t.uvw2.y);
         GL11.glNormal3f(t.norm2.x, t.norm2.y, t.norm2.z);
         GL11.glVertex3f( (float)t.p2.pos.x, (float)t.p2.pos.y, (float)t.p2.pos.z);

         GL11.glTexCoord2f(t.uvw3.x, t.uvw3.y);
         GL11.glNormal3f(t.norm3.x, t.norm3.y, t.norm3.z);
         GL11.glVertex3f( (float)t.p3.pos.x, (float)t.p3.pos.y, (float)t.p3.pos.z);
      }
      GL11.glEnd();

   }


   public void cleanup() {
      Display.destroy();
   }

   public void screenDepth(){

      GL11.glTranslatef(0.0f,0.0f,z);                     // Translate Into/Out Of The Screen By z
   }



   /**
    * Detect changes in key state, ie. a key is pressed or released, and
    * call keydown() and keyup() functions.  These are non-repeating events
    * (keydown will be called only when the key is first pressed).
    */
   public void handleKeyPressEvents()
   {
      while ( Keyboard.next() )  {
         if (Keyboard.getEventKeyState()) {
            keyDown(Keyboard.getEventKey());
         }
         else {
            keyUp(Keyboard.getEventKey());
         }
      }       
   }

   /**
    * Key event functions (keyDown() and keyUp() are are called by mainloop()).
    * @param keycode
    */
   public void keyDown(int keycode) {
      // Normals off/on
      if (keycode == Keyboard.KEY_N) {
         normalsOn = !normalsOn;
      }
   }

   public void keyUp(int keycode) {
   }

   public void handleNavigationKeys()
   {
      // Turn left
      if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)) {
         cameraRotation += 1.0f;
      }
      // Turn right
      if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) {
         cameraRotation -= 1.0f;
      }
      // move forward in current direction
      if (Keyboard.isKeyDown(Keyboard.KEY_UP)) {
         cameraPos[0] -= (float) Math.sin(cameraRotation * piover180) * 1f;
         cameraPos[2] -= (float) Math.cos(cameraRotation * piover180) * 1f;
      }
      // move backward in current direction
      if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
         cameraPos[0] += (float) Math.sin(cameraRotation * piover180) * 1f;
         cameraPos[2] += (float) Math.cos(cameraRotation * piover180) * 1f;
      }
      // move camera down
      if (Keyboard.isKeyDown(Keyboard.KEY_PRIOR)) {
         cameraPos[1] +=  .3f;
      }
      // move camera up
      if (Keyboard.isKeyDown(Keyboard.KEY_NEXT)) {
         cameraPos[1] -=  .3f;
      }
      if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
         setWireframeOn();
      }
      if (Keyboard.isKeyDown(Keyboard.KEY_E)) {
         setWireframeOff();
      }
      if (Keyboard.isKeyDown(Keyboard.KEY_T)) {
         testCenter();
      }
      if (Keyboard.isKeyDown(Keyboard.KEY_N)) {
         normalise();
      }

   }

   //----------------------------------------------------------------
   // Wireframe
   //----------------------------------------------------------------


   public void setWireframeOff() {
      GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
      GL11.glEnable(GL11.GL_CULL_FACE);
      wireframe = false;
   }   

   /** enter wireframe mode */
   public void setWireframeOn() {
      GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
      GL11.glDisable(GL11.GL_CULL_FACE);
      wireframe = true;
   }
}
Title: Re: LWJGL Renderer in split window
Post by: darkprophet on January 13, 2008, 22:46:45
I haven't looked through your Renderer code, I just haven't got the time. But I did look at the code at the start. Its sort of right:


GL11.glScissor(x, y, width, height);
      glEnable(GL_SCISSOR);
      GL11.glViewport(...);


I would put the glViewport outside of the SCISSOR block, simply because I dont know the behaviour of that call inside the scissor block.


      GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
      GL11.glDisable(GL_SCISSOR);
      GL11.glMatrixMode(GL_MODELVIEW);
      GL11.glLoadIdentity();
     
      GL11.glRotatef(rotateModelY, 0, 1, 0); // around vertical axis
      GL11.glRotatef(rotateModelX, 1, 0, 0); // around horizontal axis


That bit is wrong. The transformations are of the camera, not of the actual object. Think of it as a camera man and an actor, you want to render the actor, but rotate the camera. The two are abit similar, but lets keep the camera concept. Also, go read about gimbal lock, you shouldn't do two rotations as the second might cancel out the first.


      GL11.glPushMatrix();
      renderMesh(object1,0);
      GL11.glPopMatrix();

Thats right, but you need the transform() here. Above renderMesh.

Do exactly what my previous post says...

DP :)