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);
    }
}

