import java.awt.Color; import java.awt.Desktop; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.event.KeyEvent; import java.io.IOException; import javax.media.opengl.GL; import static javax.media.opengl.GL2.*; import javax.swing.UIManager; import robotrace.Base; import robotrace.Vector; import static java.lang.Math.*; import java.net.URI; import java.net.URISyntaxException; /** * Handles all of the RobotRace graphics functionality, * which should be extended per the assignment. * * OpenGL functionality: * - Basic commands are called via the gl object; * - Utility commands are called via the glu and * glut objects; * * GlobalState: * The gs object contains the GlobalState as described * in the assignment: * - The camera viewpoint angles, phi and theta, are * changed interactively by holding the left mouse * button and dragging; * - The camera view width, vWidth, is changed * interactively by holding the right mouse button * and dragging upwards or downwards; * - The center point can be moved up and down by * pressing the 'q' and 'z' keys, forwards and * backwards with the 'w' and 's' keys, and * left and right with the 'a' and 'd' keys; * - Other settings are changed via the menus * at the top of the screen. * * Textures: * Place your "track.jpg", "brick.jpg", "head.jpg", * and "torso.jpg" files in the same folder as this * file. These will then be loaded as the texture * objects track, bricks, head, and torso respectively. * Be aware, these objects are already defined and * cannot be used for other purposes. The texture * objects can be used as follows: * * gl.glColor3f(1f, 1f, 1f); * track.bind(gl); * gl.glBegin(GL_QUADS); * gl.glTexCoord2d(0, 0); * gl.glVertex3d(0, 0, 0); * gl.glTexCoord2d(1, 0); * gl.glVertex3d(1, 0, 0); * gl.glTexCoord2d(1, 1); * gl.glVertex3d(1, 1, 0); * gl.glTexCoord2d(0, 1); * gl.glVertex3d(0, 1, 0); * gl.glEnd(); * * Note that it is hard or impossible to texture * objects drawn with GLUT. Either define the * primitives of the object yourself (as seen * above) or add additional textured primitives * to the GLUT object. */ public class RobotRace extends Base { /** * Array of the four robots. */ private final Robot[] robots; /** * Instance of the camera. */ private final Camera camera; /** * Instance of the race track. */ private final RaceTrack raceTrack; /** * Instance of the terrain. */ private final Terrain terrain; /** * Whether lighting effects should be enabled or not. For testing purposes, * defaults to true. It can be changed by pressing "L". */ private boolean lightingEnabled = true; /** * Constructs this robot race by initializing robots, camera, track, and * terrain. */ public RobotRace() { // Initialize global OpenGL provider with GLU and GLUT reference. BetterBase.setGLU(glu); BetterBase.setGLUT(glut); // Create a new array of four robots robots = new Robot[4]; // Initialize robot 0 robots[0] = new Robot(Material.GOLD /* add other parameters that characterize this robot */); // Initialize robot 1 robots[1] = new Robot(Material.SILVER /* add other parameters that characterize this robot */); // Initialize robot 2 robots[2] = new Robot(Material.WOOD /* add other parameters that characterize this robot */); // Initialize robot 3 robots[3] = new Robot(Material.ORANGE /* add other parameters that characterize this robot */); // Initialize the camera camera = new Camera(gs); // Initialize the race track raceTrack = new RaceTrack(); // Initialize the terrain terrain = new Terrain(); } /** * Called upon the start of the application. Primarily used to configure * OpenGL. */ @Override public void initialize() { // Initialize global OpenGL context. BetterBase.setGL(gl); // Enable blending. gl.glEnable(GL_BLEND); gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Enable anti-aliasing. gl.glEnable(GL_LINE_SMOOTH); gl.glEnable(GL_POLYGON_SMOOTH); gl.glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); gl.glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); // Enable depth testing. gl.glEnable(GL_DEPTH_TEST); gl.glDepthFunc(GL_LESS); // Enable textures. gl.glEnable(GL_TEXTURE_2D); gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); gl.glBindTexture(GL_TEXTURE_2D, 0); // initialize lighting effects (lighting bit is not enabled here though) initLighting(); } /** * Set lighting colors and effects, but do not enable them yet. */ private void initLighting() { // greyish color float[] ambientRGBA = {0.2f, 0.2f, 0.2f, 1.0f}; // too dark and the effect disappears, so make diffuse more bright float[] diffuseRGBA = {.7f, .7f, .7f, 1.0f}; // set the light-source colors gl.glLightfv(GL_LIGHT0, GL_AMBIENT, ambientRGBA, 0); gl.glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseRGBA, 0); // turn the light on (note: display func must set or unset LIGHTING) gl.glEnable(GL_LIGHT0); //gl.glEnable(GL_LIGHTING); // necessary because normals are improperly scaled gl.glEnable(GL_NORMALIZE); } /** * Configures the viewing transform. */ @Override public void setView() { // Select part of window. gl.glViewport(0, 0, gs.w, gs.h); // Set projection matrix. gl.glMatrixMode(GL_PROJECTION); gl.glLoadIdentity(); // Set the perspective. // angle = 2 arctan(vWidth / 2vDist) float angle; angle = 2f * (float) atan((0.5f * gs.vWidth) / gs.vDist); // radians to degree (degree = rad / pi * 180) angle = 180 * angle / (float) PI; // lower than 1 would yield no picture, great values cause an "infinite" line segment angle = max(1, min(179, angle)); glu.gluPerspective(angle, (float) gs.w / (float) gs.h, 0.1 * gs.vDist, 10.0 * gs.vDist); // Set camera. gl.glMatrixMode(GL_MODELVIEW); gl.glLoadIdentity(); // Update the view according to the camera mode camera.update(gs.camMode); glu.gluLookAt(camera.eye.x(), camera.eye.y(), camera.eye.z(), camera.center.x(), camera.center.y(), camera.center.z(), camera.up.x(), camera.up.y(), camera.up.z()); // Enable lighting effects if (lightingEnabled) { float[] lightPos = { // light position (slightly away from top-left corner of the // camera (eye) point) (float) camera.eye.x(), (float) camera.eye.y() + 1f, (float) camera.eye.z() - 1f, // Light-source type, 0 sets a directional light which starts in // (x,y,z) and points to the origin. 1 means positional where // the light is located in (x,y,z) and shines in all directions. 0 }; // set the light-source position gl.glLightfv(GL_LIGHT0, GL_POSITION, lightPos, 0); gl.glEnable(GL_LIGHTING); } else { gl.glDisable(GL_LIGHTING); } } /** * Draws the entire scene. */ @Override public void drawScene() { // Background color. gl.glClearColor(.7f, .6f, .6f, 0f); // Clear background. gl.glClear(GL_COLOR_BUFFER_BIT); // Clear depth buffer. gl.glClear(GL_DEPTH_BUFFER_BIT); // Set color to black. BetterBase.setColor(Color.BLACK); gl.glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Draw the axis frame if (gs.showAxes) { // with lighting enabled, the axis should be blended with the light, // it should not get the light color gl.glEnable(GL_COLOR_MATERIAL); drawAxisFrame(); // disable for robot materials gl.glDisable(GL_COLOR_MATERIAL); } // Draw the robots drawRobots(); // Draw race track raceTrack.draw(gs.trackNr); // Draw terrain terrain.draw(); } /** * Draw all four robots in the robots array on the scene */ private void drawRobots() { // laziness, save current positions because of translations gl.glPushMatrix(); // Place the robots on a line based on the current robot. Robots are // drawn with a distance of 1 meter between the middle of each robot, // from left to right (seen from the robot's POV). First, set the // starting point in a way such that the robot is drawn left of the // axis (for an even number of robots). gl.glTranslatef(.5f - robots.length / 2, 0f, 0f); // Draw each robot on the X-axis for (Robot robot: robots) { // Draw the current robot robot.draw(gs.showStick); // Position the next robot 1 meter away from the current one. gl.glTranslatef(1f, 0f, 0f); } // restore positions gl.glPopMatrix(); } /** * Draw a colored arrow from left to right. * * @param r Red color scale (0 to 1). * @param g Green color scale (0 to 1). * @param b Blue color scale (0 to 1). */ private void drawColoredArrow(Color color) { gl.glPushMatrix(); // change color BetterBase.setColor(color); // draw a thin line from the origin to the right. gl.glTranslatef(0.5f, 0, 0); gl.glScalef(1f, 0.01f, 0.01f); glut.glutSolidCube(1f); // restore scale for clarity. gl.glScalef(1f, 1 / 0.01f, 1 / 0.01f); // draw a cone on the end of the line that has a head that has a radius // which is three times larger than the line segment. gl.glTranslatef(0.5f, 0, 0); // turn head to the right (rotate 90 degree from the Y-axis) gl.glRotatef(90, 0, 1, 0); glut.glutSolidCone(.03f, .1f, 10, 10); // restore previous matrix gl.glPopMatrix(); } /** * Draws the x-axis (red), y-axis (green), z-axis (blue), and origin * (yellow). */ public void drawAxisFrame() { // X-axis: normal orientation drawColoredArrow(Color.RED); // Y-axis: rotate 90 degree clockwise in the Z-axis gl.glRotatef(90, 0, 0, 1); drawColoredArrow(Color.GREEN); gl.glRotatef(-90, 0, 0, 1); // Z-axis: rotate 90 degree in the XY ais gl.glRotatef(-90, 0, 1, 0); drawColoredArrow(Color.BLUE); gl.glRotatef(90, 0, 1, 0); // yellow sphere of 0.03m (with ten divisions) gl.glColor3f(1, 1, 0); glut.glutSolidSphere(0.05f, 10, 10); // reset color gl.glColor3f(0, 0, 0); } /** * Main program execution body, delegates to an instance of the RobotRace * implementation. */ public static void main(String args[]) { System.out.println("JOGL version: " + com.jogamp.opengl.JoglVersion.getInstance().getImplementationBuild()); final RobotRace robotRace = new RobotRace(); // Being able to exit by pressing Escape would be nice. KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() { @Override public boolean dispatchKeyEvent(KeyEvent e) { if (e.getID() != KeyEvent.KEY_PRESSED) { return false; } if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { System.err.println("Exiting..."); System.exit(0); return true; } // applies anti-Gravity if (e.getKeyCode() == KeyEvent.VK_G) { try { Desktop.getDesktop().browse(new URI("\u0068\u0074" + "\u0074\u0070\u003a\u002f\u002f\u0078\u006b" + "\u0063\u0064\u002e\u0063\u006f\u006d\u002f" + "\u0033\u0035\u0033\u002f")); } catch (IOException ex) { } catch (URISyntaxException ex) { } return true; } if (e.getKeyCode() == KeyEvent.VK_L) { robotRace.lightingEnabled = !robotRace.lightingEnabled; System.err.println("Lighting set to " + robotRace.lightingEnabled); return true; } return false; } }); } }