Jump to content
  • Advertisement
Sign in to follow this  
jamesdk2006

JOGL Rendering many objects with VBO and Display Lists

This topic is 3849 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

This code draws a circular ball. xn and yn are the center of the ball, and POINTS_X and POINTS_Y are the coordinates of the points on the circle at the origin. RESOLUTION is 12, so this code actually draws a 12-sided polygon. I can currently draw about 20,000 bouncing balls at 85+ fps, but I feel that I should be able to draw millions of them considering the power of current video cards and processors. I have a very recently built computer. I have determined that the only part of my code that uses significant time is this draw function, which is called once per ball per frame. I was wondering if there was a more efficient way to draw all of the balls, or if 20,000 is a reasonable limit?
	public void draw(GL gl) {
		if (isWhirling) {
			gl.glColor3f(0.0f, 1.0f, 0.0f);
		} else {
			gl.glColor3f(red, green, blue);
		}
		gl.glBegin(GL.GL_POLYGON);
		for (int a = 0; a < RESOLUTION; a++) {
			gl.glVertex2d(xn+POINTS_X[a], yn+POINTS_Y[a]);
		}
		
		gl.glEnd();
	}


[Edited by - jamesdk2006 on May 29, 2008 8:52:00 PM]

Share this post


Link to post
Share on other sites
Advertisement
Is there a faster way? Absolutely. You need to use a display list. A display list is a collection of primitives (with accompanying attributes like normals, colors, texture coordinates etc.) stored in your graphics card's memory, which means that you can eliminate the slow AGP/PCIe transfer and simply render the entire mesh with just one number/handle.

Here's how you do it:

// Get one available object handle from OpenGL
unsigned int display_list_handle = gl.glGenLists(1);

// Attach a display list to this handle
gl.glNewList(display_list_handle, GL_COMPILE);
gl.glBegin(GL.GL_POLYGON);
for (int a = 0; a < RESOLUTION; a++)
{
gl.glVertex2d(POINTS_X[a], POINTS_Y[a]);
}
gl.glEnd();
gl.glEndList();

// The display list is now ready for use.
// Use glTranslate(x, y, z) to specify the center for each polygon

gl.glLoadIdentity();
gl.glTranslate(xn, yn, 0);
gl.glCallList(display_list_handle);





Here's some additional info from MSDN:

glNewList, glEndList
glCallList

Share this post


Link to post
Share on other sites
The better way would be to use VBO Vertex Buffer Objects, because display lists are not the standard. If you search for it, you will find many tutorials.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ingrater
The better way would be to use VBO Vertex Buffer Objects, because display lists are not the standard. If you search for it, you will find many tutorials.


There's absolutely nothing wrong with using a display list for a simple 12-sided polygon.
If you don't need the extra functionality which comes with a VBO, then use a display list.

Share this post


Link to post
Share on other sites
If you are learning GL, then I suggest that you forget about immediate mode which is what you are doing. Display list is ok and should give plenty of speed. With display list, the driver will convert your data to a suitable format so that it runs fast. For example, glVertex2d might get converted to float. glColor3f might get an alpha. Memory might be padded. It might interleave vertices and color.

A Gf 8 should be able to do something like 30 billion triangles theoretically but keep in mind that most programs are fragment limited, not vertex transform limited.

I prefer VBO over display list since it seems cleaner, makes my engine simpler.

For VBO info
http://www.opengl.org/wiki/index.php/GL_ARB_vertex_buffer_object

Share this post


Link to post
Share on other sites
You need no more than one display list. That is one for every model/mesh you have. The model/mesh in the display list should be centered around <0, 0, 0>. For every model/mesh that you need to render, you modify the modelview matrix first and then call the display list.


// C++

for (int b = 0; b < balls_to_be_rendered; b++)
{
// Reset modelview matrix
glLoadIdentity();

// Modify modelview matrix for this ball's position
glTranslatef(ball.position.x, ball.position.y, ball.position.z);

// Call display list containing the ball mesh
glCallList(ball_mesh_displaylist);
}

Share this post


Link to post
Share on other sites
The display lists appear to be slower. I can only run a 35 FPS with 20000 bouncing balls now.

Did I do something wrong.

I have a list the draws a ball.


gl.glNewList(display_list_handle, GL_COMPILE);
gl.glBegin(GL.GL_POLYGON);
for (int a = 0; a < RESOLUTION; a++)
{
gl.glVertex2d(POINTS_X[a], POINTS_Y[a]);
}
gl.glEnd();
gl.glEndList();



Then I call this to draw each ball each frame.


public void draw(gl) {
gl.glPushMatrix();
gl.glTranslate(xn, yn, 0);
gl.glCallList(display_list_handle);
gl.glPopMatrix();
}

Share this post


Link to post
Share on other sites
Ok here is the class that handles all the display as well as the ball class. I have commented the parts you should look at.


import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;

public class PongDisplay implements GLEventListener, KeyListener {

private static int NUM_BALLS = 20000; //number of balls
private static int PADDLE1_SIZE = 90;
private static int PADDLE2_SIZE = 90;
private static int PADDLE1_SIDE = 15;
private static int PADDLE2_SIDE = 585;
private static int PADDLE1_TOP = 320;
private static int PADDLE2_TOP = 320;

private static GL gl;
private static Court court = new Court();
private static TextOverlay text = new TextOverlay();
private static Ball ball[] = new Ball[NUM_BALLS];
private static Paddle paddleP1 = new Paddle(1, PADDLE1_TOP, PADDLE1_SIDE, PADDLE1_SIZE, 10);
private static Paddle paddleP2 = new Paddle(2, PADDLE2_TOP, PADDLE2_SIDE, PADDLE2_SIZE, 7);
private static CompAI AI = new CompAI();
private static CollisionDetector collisionDetector = new CollisionDetector();
private static Random gen = new Random();

static {
for (int i = 0; i < NUM_BALLS; i++) {
ball = new Ball(300, 300, gen.nextDouble() * 3 + 1.5); //construct balls
}
}

enum KEY_STATE {
NOT_PRESSED, KEY_UP, KEY_DOWN
}

KEY_STATE keyState = KEY_STATE.NOT_PRESSED;

public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
keyState = KEY_STATE.KEY_UP;
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
keyState = KEY_STATE.KEY_DOWN;
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
paddleP1.slap();
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
paddleP1.power();
} else {
keyState = KEY_STATE.NOT_PRESSED;
}
}

public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP && keyState != KEY_STATE.KEY_DOWN) {
keyState = KEY_STATE.NOT_PRESSED;
}
if (e.getKeyCode() == KeyEvent.VK_DOWN && keyState != KEY_STATE.KEY_UP) {
keyState = KEY_STATE.NOT_PRESSED;
}
}

public void keyTyped(KeyEvent e) {
}

public void init(GLAutoDrawable gld) {
gl = gld.getGL();
GLU glu = new GLU();
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.glPointSize(5.0f);
gl.glViewport(0, 0, 600, 600);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluOrtho2D(0, 600, 0, 600);
for (int i = 0; i < NUM_BALLS; i++) {
ball.generateDisplayList(gl); //generate display list for each ball
}
}

public void display(GLAutoDrawable gld) {

if (keyState == KEY_STATE.KEY_UP && paddleP1.getTop() < 550) {
paddleP1.moveUp();
}
if (keyState == KEY_STATE.KEY_DOWN
&& paddleP1.getTop() - paddleP1.getSize() > 3) {
paddleP1.moveDown();
}

gl.glClear(GL.GL_COLOR_BUFFER_BIT);

text.draw(gld, "Pong", 257, 532);
text.draw(gld, Integer.toString(paddleP1.getScore()), 7, 532);
text.draw(gld, Integer.toString(paddleP2.getScore()), 548, 532);

court.draw(gl);

paddleP1.draw(gl);
paddleP2.draw(gl);

for (int i = 0; i < NUM_BALLS; i++) {

if (AI.slap(ball.getXPos(), ball.getSpeed())) {
paddleP2.slap();
}

if (AI.power(ball.getXPos(), ball.getSpeed())) {
paddleP2.power();
}

if (AI.track(paddleP2.getCenter(), paddleP2.getSize(), ball
.getYPos()) == 1) {
if (!paddleP2.isSlapping()) {
paddleP2.moveUp();
paddleP2.draw(gl);
}
} else if (AI.track(paddleP2.getCenter(), paddleP2.getSize(),
ball.getYPos()) == 2) {
if (!paddleP2.isSlapping()) {
paddleP2.moveDown();
paddleP2.draw(gl);
}
}

if (collisionDetector.detectPad1(ball.getXPos(), ball
.getYPos(), ball.getXDirection(), paddleP1.getSide(),
paddleP1.getTop(), paddleP1.getSize())) {
ball.changeXDirection();
ball.whirl(false);
if (paddleP1.isSlapping()) {
ball.setSpeed(ball.getSpeed() * 2);
} else if (paddleP1.isPowered()) {
ball.setSlope((ball.getSlope()*100+ball.getYPos() - paddleP1.getCenter())/100);
ball.setSpeed(ball.getSpeed() * 1.2);
ball.whirl(true);
} else {
ball.setSlope((ball.getSlope()*100+ball.getYPos() - paddleP1.getCenter())/100);
ball.setSpeed(ball.getSpeed() * 1.05);
}
}

if (collisionDetector.detectPad2(ball.getXPos(), ball
.getYPos(), ball.getXDirection(), paddleP2.getSide(),
paddleP2.getTop(), paddleP2.getSize())) {
ball.changeXDirection();
ball.whirl(false);
if (paddleP2.isSlapping()) {
ball.setSpeed(ball.getSpeed() * 2);
} else if (paddleP2.isPowered()) {
ball.setSlope((ball.getSlope()*100+ball.getYPos() - paddleP2.getCenter())/100);
ball.setSpeed(ball.getSpeed() * 1.2);
ball.whirl(true);
} else {
ball.setSlope((ball.getSlope()*100+ball.getYPos() - paddleP2.getCenter())/100);
ball.setSpeed(ball.getSpeed() * 1.05);
}
}

if (collisionDetector.detectTopWall(ball.getYDirection(),
ball.getYPos())) {
ball.changeYDirection();
}

if (collisionDetector.detectBottomWall(ball.getYDirection(),
ball.getYPos())) {
ball.changeYDirection();
}

if (collisionDetector.detectP1Score(ball.getXDirection(),
ball.getXPos())) {
paddleP1.score();
ball.changeXDirection();
ball.setSlope((gen.nextDouble() - .5) * 2 + .4);
ball.setPos(300, gen.nextDouble() * 400.0 + 20);
ball.setSpeed(2.5);
ball.whirl(false);
}

if (collisionDetector.detectP2Score(ball.getXDirection(),
ball.getXPos())) {
paddleP2.score();
ball.changeXDirection();
ball.setSlope((gen.nextDouble() - .5) * 2 + .4);
ball.setPos(300, gen.nextDouble() * 400.0 + 20);
ball.setSpeed(2.5);
ball.whirl(false);
}
ball.moveBall();
ball.draw(gl); //draw ball
}
}

public void reshape(GLAutoDrawable drawable, int x, int y, int width,
int height) {
}

public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
boolean deviceChanged) {
}
}




import java.util.Random;

import javax.media.opengl.GL;

public class Ball {
Random gen = new Random();

private int display_list; //display list

private double speed;
private double xi;
private double yi;
private double xn;
private double yn;
private double slope;
private boolean movingRight;
private boolean movingUp;
private boolean isWhirling = false;

private final static double radius = 5;
private final float red = 1.0f;
private final float green = 1.0f;
private final float blue = 1.0f;
private final static double THREE_SIXTY = 2 * Math.PI;

private final static int RESOLUTION = 12;

private static double[] POINTS_X = new double[RESOLUTION];
private static double[] POINTS_Y = new double[RESOLUTION];
static {
for (int a = 0; a < RESOLUTION; a++) {
POINTS_X[a] = radius
* (Math.cos((double) a / (double) RESOLUTION * THREE_SIXTY));
POINTS_Y[a] = radius
* (Math.sin((double) a / (double) RESOLUTION * THREE_SIXTY));
}
}

public Ball(double x, double y, double s) {
xi = x;
yi = y;
xn = xi;
yn = yi;
slope = (gen.nextDouble() - .5) / gen.nextDouble();
speed = s;
movingRight = true;

if (slope > 0 && movingRight) {
movingUp = true;
} else if (slope > 0 && !movingRight) {
movingUp = false;
} else if (slope < 0 && movingRight) {
movingUp = false;
} else if (slope < 0 && !movingRight) {
movingUp = true;
}
}

public void generateDisplayList(GL gl) { //generate display list
display_list = gl.glGenLists(1);
gl.glNewList(display_list, GL.GL_COMPILE);
gl.glBegin(GL.GL_POLYGON);
for (int a = 0; a < RESOLUTION; a++)
{
gl.glVertex2d(POINTS_X[a], POINTS_Y[a]);
}
gl.glEnd();
gl.glEndList();
}

public void moveBall() {
if (speed > 12) {
speed = 12;
}

xi = xn;
yi = yn;
if (movingRight) {
xn += speed;
} else {
xn -= speed;
}
if (isWhirling) {
yn = (slope * (xn - xi) + yi);
yn = yn += (gen.nextDouble() - .5) * 35;
} else {
yn = (slope * (xn - xi) + yi);
}

if (yn > 545) {
yn = 546;
}

if (yn < 6) {
yn = 5;
}
}

public void changeXDirection() {
if (movingRight) {
movingRight = false;
} else if (!movingRight) {
movingRight = true;
}

slope *= -1;
xi = xn;
yi = yn;
}

public void changeYDirection() {
if (movingUp) {
movingUp = false;
}

else if (!movingUp) {
movingUp = true;
}

slope *= -1;
xi = xn;
yi = yn;
}

public void setXDirection(boolean d) {
movingRight = d;
}

public void setYDirection(boolean d) {
movingUp = d;
}

public boolean getXDirection() {
return movingRight;
}

public boolean getYDirection() {
return movingUp;
}

public void setPos(double x, double y) {
xn = x;
yn = y;
}

public double getXPos() {
return xn;
}

public double getYPos() {
return yn;
}

public void setSpeed(double s) {
speed = s;
}

public double getSpeed() {
return speed;
}

public double getSlope() {
return slope;
}

public void whirl(boolean w) {
isWhirling = w;
}

public void setSlope(double sl) {
slope = sl;
if (sl > 0 && movingRight) {
movingUp = true;
} else if (sl > 0 && !movingRight) {
movingUp = false;
} else if (sl < 0 && movingRight) {
movingUp = false;
} else if (sl < 0 && !movingRight) {
movingUp = true;
}
}

public void draw(GL gl) { //draw ball
if (isWhirling) {
gl.glColor3f(0.0f, 1.0f, 0.0f);
} else {
gl.glColor3f(red, green, blue);
}
gl.glPushMatrix();
gl.glTranslated(xn, yn, 0);
gl.glCallList(display_list);
gl.glPopMatrix();
}
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!