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
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?
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
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.
Thanks for the reply wolf.
I've looked on the NeHe tutorials page and they have something similar as well.
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 :)
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;
}
}
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 :)