package classwork; import javax.media.opengl.*; import java.awt.event.KeyEvent; import jocode.*; import jomodel.JOVector; /** * GLART_3_city.java * * Create a simple grid of "buildings" by translating and scaling a cube. Demonstrates * glu.gluPerspective() and glOrtho() to set the PROJECTTION matrix, which controls * the perpective of the scene. * * Use the arrow keys to move the viewpoint. * Hit SPACE to switch between perspective and ortho views. */ public class GLART_3_city extends JOApp { // create two arrays to hold random building sizes and colors. see setup(). int NUM_BLDG_ROWS = 5; int NUM_BLDG_COLS = 5; JOVector[][] buildingSizes = new JOVector[NUM_BLDG_ROWS][NUM_BLDG_COLS]; JOVector[][] buildingColors = new JOVector[NUM_BLDG_ROWS][NUM_BLDG_COLS]; // hold the viewpoint position in a vector object JOVector cameraPos = new JOVector(0f, 15f, 0f); // we'll apply a texture image to the buildings int buildingTexture = 0; // toggle value to control perspective/ortho rendering boolean perspectiveOn = true; /** * Main function just creates and runs the application. */ public static void main(String args[]) { GLART_3_city app = new GLART_3_city(); displayWidth = 800; displayHeight = 600; app.run(); } /** * Initialize OpenGL */ public void setup() { // set the background color gl.glClearColor(.9f, .9f, .9f, 1f); // set the projection matrix to render in perspective (see function at end of file) setPerspective(); // load and activate a texture buildingTexture = makeTexture("images/Blue-Building-Facade-by-Shi-Yali-qpps.jpg"); gl.glBindTexture(GL.GL_TEXTURE_2D, buildingTexture); // make random building sizes and colors for (int r=0; r < NUM_BLDG_ROWS; r++) { for (int c=0; c < NUM_BLDG_COLS; c++) { float grayvalue = .2f + random(.7f); buildingSizes[r][c] = new JOVector( 1+random(2.5f), 3+random(6f), 1+random(2.5f) ); buildingColors[r][c] = new JOVector( grayvalue, grayvalue, grayvalue ); } } } /** * Render the scene. */ public void draw() { // Clear screen and depth buffer gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Select The Modelview Matrix (controls model orientation) gl.glMatrixMode(GL.GL_MODELVIEW); // Reset the Modelview matrix gl.glLoadIdentity(); // Where is the 'eye' glu.gluLookAt( cameraPos.x, cameraPos.y, cameraPos.z, // eye is up the Y axis 0f, 0f, 0f, // look at origin 0f, 0f, -1f); // top of camera is pointing down the Z axis // we're going to draw a 5x5 grid of buildings. Each cell in the grid is 4x4 units, so // the total grid size is 20x20. Translate -8 on X and Z to center the grid at the origin. // the X and Z axes are the ground plane. Y is the vertical axis. gl.glTranslatef(-8,0,-8); for (int r=0; r < NUM_BLDG_ROWS; r++) { for (int c=0; c < NUM_BLDG_COLS; c++) { // set the color gl.glColor3f( buildingColors[r][c].x, buildingColors[r][c].y, buildingColors[r][c].z ); gl.glPushMatrix(); { // shift coordinate system to building position gl.glTranslatef(r*4, buildingSizes[r][c].y/2, c*4); // scale coordinate system to building dimensions gl.glScalef(buildingSizes[r][c].x, buildingSizes[r][c].y, buildingSizes[r][c].z); // now the 1x1x1 cube will be stretched to the building size renderCube(); } gl.glPopMatrix(); } } } /** * The keyDown() function is called by JOApp when a key is pressed. The keycode * parameteris one of the Java KeyEvent ids such as VK_LEFT for the left arrow key. * When the arrow keys are pressed we shift the camera position in the XZ plane. */ public void keyDown(int keycode) { float increment = .2f; if (keycode == KeyEvent.VK_LEFT) { cameraPos.x -= increment; } else if (keycode == KeyEvent.VK_RIGHT) { cameraPos.x += increment; } else if (keycode == KeyEvent.VK_UP) { cameraPos.z -= increment; } else if (keycode == KeyEvent.VK_DOWN) { cameraPos.z += increment; } } /** * The keyUp() function is called by JOApp when a key is released. */ public void keyUp(int keycode) { if (keycode == KeyEvent.VK_SPACE) { perspectiveOn = !perspectiveOn; if (perspectiveOn) { setPerspective(); } else { setOrtho(); } } } /** * change the projection matrix with gluPerspective(). Change the FOV value (first * param in gluPerspective() to see how the scene is affected. * * See also: JOApp.setPerspective(float fov) an easy way to set perspective */ public void setPerspective() { // select projection matrix (controls perspective) and reset it gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadIdentity(); glu.gluPerspective( 55, // how wide is the field of view, in degrees (float)viewportW / (float)viewportH, // what is the aspect ratio of the view (match it to viewport) 1, // how close can geometry be to the eye before it's clipped 500); // how far away can geometry be from eye before it's clipped // return to modelview matrix gl.glMatrixMode(GL.GL_MODELVIEW); } /** * switch the projection matrix to an Orthogonal projection. The grid of buildings * is 20x20 so we could just make our ortho view 20 x 20. But that is not the aspect * ratio of our window and the rendering will be stretched to fit to the 3x4 ratio * of our window. To prevent that we make the ortho projection 26 units wide * and 20 units high (about a 3x4 ratio). * * See also: JOApp.setOrtho(int x, int y, int width, int height) an easy way to set ortho */ public void setOrtho() { // select projection matrix and reset it gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadIdentity(); // With glOrtho the viewing volume is like a box, not a frustum. The sides of // the "viewing volume" are parallel, not pyramid shaped. This makes things // simpler, as we can just imagine putting a box around our scene. The edges // of the box are the edges of the view that will be rendered. Our scene is // 20x20 units centered at the origin, so we'll make the glOrtho bounds -10 to 10. gl.glOrtho( // these are all world coordinates -13.3, 13.3, // left and right edge of scene (26 units wide) -10, 10, // bottom and top edge of scene (20 units high, makes a 3x4 aspect ratio same as window) 1f, 500); // near plane, far plane // return to modelview matrix gl.glMatrixMode(GL.GL_MODELVIEW); } }