import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.event.KeyEvent; 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.*; /** * 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; /** * Constructs this robot race by initializing robots, * camera, track, and terrain. */ public RobotRace() { // 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(); // 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() { // 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); } /** * 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()); } /** * Draws the entire scene. */ @Override public void drawScene() { // Background color. gl.glClearColor(1f, 1f, 1f, 0f); // Clear background. gl.glClear(GL_COLOR_BUFFER_BIT); // Clear depth buffer. gl.glClear(GL_DEPTH_BUFFER_BIT); // Set color to black. gl.glColor3f(0f, 0f, 0f); gl.glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Draw the axis frame if (gs.showAxes) { drawAxisFrame(); } // Draw the first robot robots[0].draw(false); // Draw race track raceTrack.draw(gs.trackNr); // Draw terrain terrain.draw(); // Unit box around origin. //glut.glutWireCube(1f); // Move in x-direction. gl.glTranslatef(2f, 0f, 0f); // Rotate 30 degrees, around z-axis. gl.glRotatef(30f, 0f, 0f, 1f); // Scale in z-direction. gl.glScalef(1f, 1f, 2f); // Translated, rotated, scaled box. //glut.glutWireCube(1f); } /** * 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(float r, float g, float b) { gl.glPushMatrix(); // change color gl.glColor3f(r, g, b); // 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(1, 0, 0); // Y-axis: rotate 90 degree clockwise in the Z-axis gl.glRotatef(90, 0, 0, 1); drawColoredArrow(0, 1, 0); gl.glRotatef(-90, 0, 0, 1); // Z-axis: rotate 90 degree in the XY ais gl.glRotatef(-90, 0, 1, 0); drawColoredArrow(0, 0, 1); 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); } /** * Materials that can be used for the robots. */ public enum Material { /** * Gold material properties. * Modify the default values to make it look like gold. */ GOLD ( new float[] {0.8f, 0.8f, 0.8f, 1.0f}, new float[] {0.0f, 0.0f, 0.0f, 1.0f}), /** * Silver material properties. * Modify the default values to make it look like silver. */ SILVER ( new float[] {0.8f, 0.8f, 0.8f, 1.0f}, new float[] {0.0f, 0.0f, 0.0f, 1.0f}), /** * Wood material properties. * Modify the default values to make it look like wood. */ WOOD ( new float[] {0.8f, 0.8f, 0.8f, 1.0f}, new float[] {0.0f, 0.0f, 0.0f, 1.0f}), /** * Orange material properties. * Modify the default values to make it look like orange. */ ORANGE ( new float[] {0.8f, 0.8f, 0.8f, 1.0f}, new float[] {0.0f, 0.0f, 0.0f, 1.0f}); /** The diffuse RGBA reflectance of the material. */ float[] diffuse; /** The specular RGBA reflectance of the material. */ float[] specular; /** * Constructs a new material with diffuse and specular properties. */ private Material(float[] diffuse, float[] specular) { this.diffuse = diffuse; this.specular = specular; } } /** * Represents a Robot, to be implemented according to the Assignments. */ private class Robot { /** The material from which this robot is built. */ private final Material material; /** Relative lengths, widths and heights of robot model. */ private final float torsoHeight, torsoWidth, shoulderRadius, armLength, legLength, armWidth, legWidth, neckHeight, headRadius, depth; /** * Constructs the robot with initial parameters. */ public Robot(Material material) { /* Set all parameters of the robot */ this.material = material; this.torsoHeight = 0.6f; this.torsoWidth = 0.48f; this.shoulderRadius = 0.09f; this.armLength = 0.6f; this.armWidth = 0.06f; this.legLength = 0.78f; this.legWidth = 0.06f; this.neckHeight = 0.15f; this.headRadius = 0.12f; this.depth = 0.24f; } /** * Draws this robot (as a {@code stickfigure} if specified). */ public void draw(boolean stickFigure) { if (stickFigure) { // Draw the robot drawTorso(); // Torso drawShoulders(); // Shoulders drawLegs(); // Legs and feet drawArms(); // Arms and hands drawHead(); // Neck and head } else { // Draw the stick figure drawStickFigure(); } } /** * Draws the stickfigure of the robot. */ private void drawStickFigure() { // Create all vectors based on the real position and the // translations of the real robot. Vector torsoLeftTop = new Vector(torsoWidth /2, 0f, torsoHeight /2); Vector torsoRightTop = new Vector(-torsoWidth/2, 0f, torsoHeight/2); Vector torsoLeftBottom = new Vector(torsoWidth / 2, 0f, -torsoHeight /2); Vector torsoRightBottom = new Vector(-torsoWidth /2 , 0f, -torsoHeight /2); Vector leftShoulder = new Vector(torsoWidth / 2 + armWidth * 1.5f, 0f, torsoHeight / 2 - 0.05f); Vector rightShoulder = new Vector(-torsoWidth / 2 - armWidth * 1.5f, 0f, torsoHeight / 2 - 0.05f); Vector leftHand = new Vector(torsoWidth / 2 + armWidth * 1.5f, 0f, torsoHeight / 2 - 0.05f - armLength); Vector rightHand = new Vector(-torsoWidth / 2 - armWidth * 1.5f, 0f, torsoHeight / 2 - 0.05f - armLength); Vector neckStart = new Vector(0f, 0f, torsoHeight/2); Vector neckEnd = new Vector(0f, 0f, torsoHeight/2 + neckHeight); Vector leftLegTop = new Vector(torsoWidth / 2 - legWidth * 2, 0f, -torsoHeight / 2); Vector leftLegBottom = new Vector(torsoWidth / 2 - legWidth * 2, 0f, -torsoHeight / 2 - legLength); Vector rightLegTop = new Vector(-torsoWidth / 2 + legWidth * 2, 0f, -torsoHeight / 2); Vector rightLegBottom = new Vector(-torsoWidth / 2 + legWidth * 2, 0f, -torsoHeight / 2 - legLength); /* * For future, translate all vectors so they are positioned * correctly and that they are rotated correctly. * * Arms and feet should be translated afterwards to represent * the angle to simulate movement. */ // Set the color to black setColorRGB(0, 0, 0); // Connect all torso points with a line stip gl.glBegin(GL_LINE_STRIP); stickPoint(torsoLeftTop); stickPoint(torsoRightTop); stickPoint(torsoRightBottom); stickPoint(torsoLeftBottom); stickPoint(torsoLeftTop); gl.glEnd(); // Draw head lines based on neck start and end gl.glBegin(GL_LINES); stickPoint(neckStart); stickPoint(neckEnd); gl.glEnd(); // Draw arm and leg lines, and connect arms with torso (shoulders) gl.glBegin(GL_LINES); stickPoint(leftLegTop); stickPoint(leftLegBottom); stickPoint(rightLegTop); stickPoint(rightLegBottom); stickPoint(leftShoulder); stickPoint(leftHand); stickPoint(rightShoulder); stickPoint(rightHand); stickPoint(rightShoulder); stickPoint(torsoRightTop); stickPoint(leftShoulder); stickPoint(torsoLeftTop); gl.glEnd(); // Set point size of the stickfigure gl.glPointSize(7.5f); // Begin drawing all points gl.glBegin(GL_POINTS); // First the torso points stickPoint(torsoLeftTop); stickPoint(torsoRightTop); stickPoint(torsoRightBottom); stickPoint(torsoLeftBottom); // Shoulders and the hands stickPoint(leftShoulder); stickPoint(rightShoulder); stickPoint(leftHand); stickPoint(rightHand); // The neck stickPoint(neckStart); stickPoint(neckEnd); // And finally the legs stickPoint(leftLegTop); stickPoint(leftLegBottom); stickPoint(rightLegTop); stickPoint(rightLegBottom); // Stop drawing points gl.glEnd(); } /** * Draws a point with a given vector p * @param p Point in space */ private void stickPoint(Vector p) { gl.glVertex3f((float) p.x(), (float) p.y(), (float) p.z()); } /** * Draws the torso of the robot. */ private void drawTorso() { // Push matrix for returning to origin later gl.glPushMatrix(); // Scale the torso to specified values gl.glScalef(torsoWidth, depth, torsoHeight); // Draw the torso itself with given color setColorRGB(230, 230, 230); glut.glutSolidCube(1f); // Pop matrix so we return to the origin gl.glPopMatrix(); } /** * Draws both shoulders of the robot. */ private void drawShoulders() { // Push the translation matrix so we can return to the origin gl.glPushMatrix(); // Translate to the left of the robot for left shoulder gl.glTranslatef(torsoWidth / 2 + shoulderRadius / 1.5f, 0f, torsoHeight / 2); // Set the drawing color and draw left shoulder setColorRGB(100, 130, 255); glut.glutSolidSphere(shoulderRadius, 10, 10); // Pop the matrix and then push it, so we are at the origin gl.glPopMatrix(); gl.glPushMatrix(); // Translate to the right of the robot for right shoulder gl.glTranslatef(- torsoWidth / 2 - shoulderRadius / 1.5f, 0f, torsoHeight / 2); // Set the drawing color and draw right shoulder setColorRGB(100, 130, 255); glut.glutSolidSphere(shoulderRadius, 10, 10); // Pop the translation matrix so we are at the origin gl.glPopMatrix(); } /** * Draws the legs and feet of the robot. */ private void drawLegs() { // Push the matrix for returning to origin gl.glPushMatrix(); // Translate and scale for the left leg gl.glTranslatef(torsoWidth / 2 - legWidth * 2, 0f, - legLength / 2 - torsoHeight / 2); gl.glScalef(legWidth, legWidth, legLength); // Set the color and draw the left leg setColorRGB(150, 150, 150); glut.glutSolidCube(1f); // First scale and translate back, // then new transformation for left foot gl.glScalef(1/legWidth, 1/legWidth, 1/legLength); gl.glTranslatef(0f, 0f, - legLength / 2); gl.glTranslatef(0f, legWidth / 2, -legWidth / 2); gl.glScalef(legWidth, legWidth * 2, legWidth); // Set the color and draw left foot setColorRGB(100, 100, 100); glut.glutSolidCube(1f); // Pop the matrix and then push, so we are at the origin gl.glPopMatrix(); gl.glPushMatrix(); // Translate and scale for the right leg gl.glTranslatef(-torsoWidth / 2 + legWidth * 2, 0f, - legLength / 2 - torsoHeight / 2); gl.glScalef(legWidth, legWidth, legLength); // Set the color and draw right leg setColorRGB(150, 150, 150); glut.glutSolidCube(1f); // Scale and translate back, // then translate and scale for right foot gl.glScalef(1/legWidth, 1/legWidth, 1/legLength); gl.glTranslatef(0f, 0f, - legLength / 2); gl.glTranslatef(0f, legWidth / 2, -legWidth / 2); gl.glScalef(legWidth, legWidth * 2, legWidth); // Set color and draw right foot setColorRGB(100, 100, 100); glut.glutSolidCube(1f); // Pop so we are at the origin gl.glPopMatrix(); } /** * Draw both arms and both hands of the robot. */ private void drawArms() { // Push the translation matrix so we can return to the origin gl.glPushMatrix(); // Translate and scale for the left arm gl.glTranslatef(torsoWidth / 2 + armWidth * 1.5f, 0f, torsoHeight / 2 - armLength / 2 - 0.05f); gl.glScalef(armWidth, armWidth, armLength); // Set the color and draw the arm itself setColorRGB(200, 200, 200); glut.glutSolidCube(1f); // Translate and scale for the left hand gl.glScalef(1/armWidth, 1/armWidth, 1/armLength); gl.glTranslatef(0f, 0f, - armLength / 2); // Set the color and draw the left hand setColorRGB(130, 158, 174); glut.glutSolidSphere(armWidth * 1.25f, 10, 10); // Pop the translation matrix and return to the origin gl.glPopMatrix(); gl.glPushMatrix(); // Translate and scale for the right arm gl.glTranslatef(-(torsoWidth / 2 + armWidth * 1.5f), 0f, torsoHeight / 2 - armLength / 2 - 0.05f); gl.glScalef(armWidth, armWidth, armLength); // Set the color and draw the right arm setColorRGB(200, 200, 200); glut.glutSolidCube(1f); // Translate and scale for right hand gl.glScalef(1/armWidth, 1/armWidth, 1/armLength); gl.glTranslatef(0f, 0f, - armLength / 2); // Set color and draw the right hand setColorRGB(130, 158, 174); glut.glutSolidSphere(armWidth * 1.25f, 10, 10); // Pop the translation matrix so we are at the origin again gl.glPopMatrix(); } /** * Set the color for drawing specified with RGB values. * * @param r Red parameter (0 - 255) * @param g Green parameter (0 - 255) * @param b Blue parameter (0 - 255) */ private void setColorRGB(int r, int g, int b) { gl.glColor3b((byte) r, (byte) g, (byte) b); } /** * Draw the head and the neck of the robot. */ private void drawHead() { // Push matrix so we can go to the origin afterwards gl.glPushMatrix(); // Translate and scale for the neck gl.glTranslatef(0f, 0f, torsoHeight / 2 + neckHeight / 2); gl.glScalef(headRadius / 2.5f, headRadius / 2.5f, neckHeight); // Set color and draw neck setColorRGB(230, 230, 230); glut.glutSolidCube(1f); // Pop the matrix and push so we are at the origin again gl.glPopMatrix(); gl.glPushMatrix(); // Translate so we are above the neck for the head gl.glTranslatef(0f, 0f, torsoHeight / 2 + neckHeight + headRadius / 2); // Set color and draw head setColorRGB(190, 210, 220); glut.glutSolidSphere(headRadius, 10, 10); // Pop so we are at the origin again gl.glPopMatrix(); } } /** * Implementation of a camera with a position and orientation. */ private class Camera { /** The position of the camera. */ public Vector eye = new Vector(3f, 6f, 5f); /** The point to which the camera is looking. */ public Vector center = Vector.O; /** The up vector. */ public Vector up = Vector.Z; /** * Updates the camera viewpoint and direction based on the * selected camera mode. */ public void update(int mode) { robots[0].toString(); // Helicopter mode if (1 == mode) { setHelicopterMode(); // Motor cycle mode } else if (2 == mode) { setMotorCycleMode(); // First person mode } else if (3 == mode) { setFirstPersonMode(); // Auto mode } else if (4 == mode) { // code goes here... // Default mode } else { setDefaultMode(); } } /** * Computes {@code eye}, {@code center}, and {@code up}, based * on the camera's default mode. */ private void setDefaultMode() { /* z | * | vDist % * | % * Ez * |%________*________ y * Ex / % * * / s % * * x / - - - - - - - * * Ey * phi is angle between vDist and XY plane (Z direction) * theta is angle between X-axis and s (XY plane) * E = (Ex, Ey, Ez) * sin phi = Ez / vDist => Ez = vDist * sin phi * cos phi = s / vDist => s = vDist * cos phi * Ex = s * sin theta * Ey = s * cos theta */ float Ex, Ey, Ez, s; Ez = gs.vDist * (float) sin(gs.phi); s = gs.vDist * (float) cos(gs.phi); Ex = s * (float) sin(gs.theta); Ey = s * (float) cos(gs.theta); // change center point with WASD Ex += gs.cnt.x(); Ey += gs.cnt.y(); Ez += gs.cnt.z(); eye = new Vector(Ex, Ey, Ez); } /** * Computes {@code eye}, {@code center}, and {@code up}, based * on the helicopter mode. */ private void setHelicopterMode() { // code goes here ... } /** * Computes {@code eye}, {@code center}, and {@code up}, based * on the motorcycle mode. */ private void setMotorCycleMode() { // code goes here ... } /** * Computes {@code eye}, {@code center}, and {@code up}, based * on the first person mode. */ private void setFirstPersonMode() { // code goes here ... } } /** * Implementation of a race track that is made from Bezier segments. */ private class RaceTrack { /** Array with control points for the O-track. */ private Vector[] controlPointsOTrack; /** Array with control points for the L-track. */ private Vector[] controlPointsLTrack; /** Array with control points for the C-track. */ private Vector[] controlPointsCTrack; /** Array with control points for the custom track. */ private Vector[] controlPointsCustomTrack; /** * Constructs the race track, sets up display lists. */ public RaceTrack() { // code goes here ... } /** * Draws this track, based on the selected track number. */ public void draw(int trackNr) { // The test track is selected if (0 == trackNr) { // code goes here ... // The O-track is selected } else if (1 == trackNr) { // code goes here ... // The L-track is selected } else if (2 == trackNr) { // code goes here ... // The C-track is selected } else if (3 == trackNr) { // code goes here ... // The custom track is selected } else if (4 == trackNr) { // code goes here ... } } /** * Returns the position of the curve at 0 <= {@code t} <= 1. */ public Vector getPoint(double t) { return Vector.O; // <- code goes here } /** * Returns the tangent of the curve at 0 <= {@code t} <= 1. */ public Vector getTangent(double t) { return Vector.O; // <- code goes here } } /** * Implementation of the terrain. */ private class Terrain { /** * Can be used to set up a display list. */ public Terrain() { // code goes here ... } /** * Draws the terrain. */ public void draw() { // code goes here ... } /** * Computes the elevation of the terrain at ({@code x}, {@code y}). */ public float heightAt(float x, float y) { return 0; // <- code goes here } } /** * Main program execution body, delegates to an instance of * the RobotRace implementation. */ public static void main(String args[]) { // Being able to exit by pressing Escape would be nice. KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() { @Override public boolean dispatchKeyEvent(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { System.err.println("Exiting..."); System.exit(0); return true; } return false; } }); System.out.println("JOGL version: " + com.jogamp.opengl.JoglVersion.getInstance().getImplementationBuild()); RobotRace robotRace = new RobotRace(); } }