summaryrefslogtreecommitdiff
path: root/src/robotrace/Base.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/robotrace/Base.java')
-rw-r--r--src/robotrace/Base.java388
1 files changed, 388 insertions, 0 deletions
diff --git a/src/robotrace/Base.java b/src/robotrace/Base.java
new file mode 100644
index 0000000..0f67ce6
--- /dev/null
+++ b/src/robotrace/Base.java
@@ -0,0 +1,388 @@
+package robotrace;
+
+import com.jogamp.opengl.util.FPSAnimator;
+import com.jogamp.opengl.util.gl2.GLUT;
+import com.jogamp.opengl.util.texture.Texture;
+import com.jogamp.opengl.util.texture.TextureIO;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.media.opengl.GL;
+import javax.media.opengl.GL2;
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.awt.GLJPanel;
+import javax.media.opengl.glu.GLU;
+import javax.swing.UIManager;
+
+/**
+ * Handles all of the RobotRace graphics functionality,
+ * which should be extended per the Assignments.
+ */
+abstract public class Base {
+
+ // Library version number.
+ static public int LIBRARY_VERSION = 3;
+
+ // Minimum distance of camera to center point.
+ static public float MIN_CAMERA_DISTANCE = 1f;
+
+ // Distance multiplier per mouse wheel tick.
+ static public float MOUSE_WHEEL_FACTOR = 1.2f;
+
+ // Minimum value of phi.
+ static public float PHI_MIN = -(float) Math.PI / 2f + 0.01f;
+
+ // Maximum value of phi.
+ static public float PHI_MAX = (float) Math.PI / 2f - 0.01f;
+
+ // Ratio of distance in pixels dragged and radial change of camera.
+ static public float DRAG_PIXEL_TO_RADIAN = 0.025f;
+
+ // Minimum value of vWidth.
+ static public float VWIDTH_MIN = 1f;
+
+ // Maximum value of vWidth.
+ static public float VWIDTH_MAX = 1000f;
+
+ // Ratio of vertical distance dragged and change of vWidth;
+ static public float DRAG_PIXEL_TO_VWIDTH = 0.1f;
+
+ // Extent of center point change based on key input.
+ static public float CENTER_POINT_CHANGE = 1f;
+
+ // Desired frames per second.
+ static public int FPS = 30;
+
+
+ // Global state, created at startup.
+ protected GlobalState gs;
+
+ // OpenGL reference, continuously updated for correct thread.
+ protected GL2 gl;
+
+ // OpenGL utility functions.
+ protected GLU glu;
+ protected GLUT glut;
+
+ // Start time of animation.
+ private long startTime;
+
+ // Textures.
+ protected Texture track, brick, head, torso;
+
+ /**
+ * Constructs base class.
+ */
+ public Base() {
+ // Print library version number.
+ System.out.println("Using RobotRace library version " + LIBRARY_VERSION);
+
+ // Global state.
+ this.gs = new GlobalState();
+
+ // Enable fancy GUI theme.
+ try {
+ UIManager.setLookAndFeel(
+ "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
+ } catch(Exception ex) {
+ Logger.getLogger(Base.class.getName()).log(Level.SEVERE, null, ex);
+ }
+
+ // GUI frame.
+ MainFrame frame = new MainFrame(gs);
+
+ // OpenGL utility functions.
+ this.glu = new GLU();
+ this.glut = new GLUT();
+
+ // Redirect OpenGL listener to the abstract render functions.
+ GLJPanel glPanel = (GLJPanel) frame.glPanel;
+ glPanel.addGLEventListener(new GLEventDelegate());
+
+ // Attach mouse and keyboard listeners.
+ GLListener listener = new GLListener();
+ glPanel.addMouseListener(listener);
+ glPanel.addMouseMotionListener(listener);
+ glPanel.addMouseWheelListener(listener);
+ glPanel.addKeyListener(listener);
+ glPanel.setFocusable(true);
+ glPanel.requestFocusInWindow();
+
+ // Attach animator to OpenGL panel and begin refresh
+ // at the specified number of frames per second.
+ final FPSAnimator animator =
+ new FPSAnimator((GLJPanel) frame.glPanel, FPS, true);
+ animator.setIgnoreExceptions(false);
+ animator.setPrintExceptions(true);
+
+ animator.start();
+
+ // Stop animator when window is closed.
+ frame.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+ // Show frame.
+ frame.setVisible(true);
+ }
+
+ /**
+ * Called upon the start of the application.
+ * Primarily used to configure OpenGL.
+ */
+ abstract public void initialize();
+
+ /**
+ * Configures the viewing transform.
+ */
+ abstract public void setView();
+
+ /**
+ * Draws the entire scene.
+ */
+ abstract public void drawScene();
+
+ /**
+ * Pass a vector as a vertex to OpenGL.
+ */
+ public void glVertex(Vector vector) {
+ gl.glVertex3d(vector.x(),
+ vector.y(),
+ vector.z());
+ }
+
+ /**
+ * Delegates OpenGL events to abstract methods.
+ */
+ private final class GLEventDelegate implements GLEventListener {
+
+ /**
+ * Initialization of OpenGL state.
+ */
+ @Override
+ public void init(GLAutoDrawable drawable) {
+ gl = drawable.getGL().getGL2();
+
+ // Try to load textures.
+ track = loadTexture("track.jpg");
+ brick = loadTexture("brick.jpg");
+ head = loadTexture("head.jpg");
+ torso = loadTexture("torso.jpg");
+
+ // Print library version number.
+ //System.out.println("Using RobotRace library version " + LIBRARY_VERSION);
+
+ initialize();
+ }
+
+ /**
+ * Try to load a texture from the given file. The file
+ * should be located in the same folder as RobotRace.java.
+ */
+ private Texture loadTexture(String file) {
+ Texture result = null;
+
+ try {
+ // Try to load from local folder.
+ result = TextureIO.newTexture(new File(file), false);
+ } catch(Exception e1) {
+ // Try to load from /src folder instead.
+ try {
+ result = TextureIO.newTexture(new File("src/" + file), false);
+ } catch(Exception e2) {
+
+ }
+ }
+
+ if(result != null) {
+ System.out.println("Loaded " + file);
+ result.enable(gl);
+ }
+
+ return result;
+ }
+
+ /**
+ * Render scene.
+ */
+ @Override
+ public void display(GLAutoDrawable drawable) {
+ gl = drawable.getGL().getGL2();
+
+ // Update wall time, and reset if required.
+ if(gs.tAnim < 0) {
+ startTime = System.currentTimeMillis();
+ }
+ gs.tAnim = (float) (System.currentTimeMillis() - startTime) / 1000f;
+
+ // Also update view, because global state may have changed.
+ setView();
+ drawScene();
+
+ // Report OpenGL errors.
+ int errorCode = gl.glGetError();
+ while(errorCode != GL.GL_NO_ERROR) {
+ System.err.println(errorCode + " " +
+ glu.gluErrorString(errorCode));
+ errorCode = gl.glGetError();
+ }
+ }
+
+ /**
+ * Canvas reshape.
+ */
+ @Override
+ public void reshape(GLAutoDrawable drawable,
+ int x, int y,
+ int width, int height) {
+ gl = drawable.getGL().getGL2();
+
+ // Update state.
+ gs.w = width;
+ gs.h = height;
+
+ setView();
+ }
+
+ @Override
+ public void dispose(GLAutoDrawable drawable) {
+
+ }
+
+ }
+
+ /**
+ * Handles mouse events of the GLJPanel to support the interactive
+ * change of camera angles and distance in the global state.
+ */
+ private final class GLListener implements MouseMotionListener,
+ MouseListener,
+ MouseWheelListener,
+ KeyListener {
+ // Position of mouse drag source.
+ private int dragSourceX, dragSourceY;
+
+ // Last mouse button pressed.
+ private int mouseButton;
+
+ @Override
+ public void mouseDragged(MouseEvent e) {
+ float dX = e.getX() - dragSourceX;
+ float dY = e.getY() - dragSourceY;
+
+ // Change camera angle when left button is pressed.
+ if(mouseButton == MouseEvent.BUTTON1) {
+ gs.theta += dX * DRAG_PIXEL_TO_RADIAN;
+ gs.phi = Math.max(PHI_MIN,
+ Math.min(PHI_MAX,
+ gs.phi + dY * DRAG_PIXEL_TO_RADIAN));
+ }
+ // Change vWidth when right button is pressed.
+ else if(mouseButton == MouseEvent.BUTTON3) {
+ gs.vWidth = Math.max(VWIDTH_MIN,
+ Math.min(VWIDTH_MAX,
+ gs.vWidth + dY * DRAG_PIXEL_TO_VWIDTH));
+ }
+
+ dragSourceX = e.getX();
+ dragSourceY = e.getY();
+ }
+
+ @Override
+ public void mouseMoved(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ gs.vDist = (float) Math.max(MIN_CAMERA_DISTANCE,
+ gs.vDist *
+ Math.pow(MOUSE_WHEEL_FACTOR,
+ e.getWheelRotation()));
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ dragSourceX = e.getX();
+ dragSourceY = e.getY();
+ mouseButton = e.getButton();
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ // Move center point.
+ double phiQ = gs.theta + Math.PI / 2.0;
+
+ switch(e.getKeyChar()) {
+ // Right.
+ case 'a': gs.cnt = gs.cnt.subtract(
+ new Vector(Math.cos(phiQ), Math.sin(phiQ), 0)
+ .scale(CENTER_POINT_CHANGE));
+ break;
+ // Left.
+ case 'd': gs.cnt = gs.cnt.add(
+ new Vector(Math.cos(phiQ), Math.sin(phiQ), 0)
+ .scale(CENTER_POINT_CHANGE));
+ break;
+ // Forwards.
+ case 'w': gs.cnt = gs.cnt.subtract(
+ new Vector(Math.cos(gs.theta), Math.sin(gs.theta), 0)
+ .scale(CENTER_POINT_CHANGE));
+ break;
+ // Backwards.
+ case 's': gs.cnt = gs.cnt.add(
+ new Vector(Math.cos(gs.theta), Math.sin(gs.theta), 0)
+ .scale(CENTER_POINT_CHANGE));
+ break;
+ // Up.
+ case 'q': gs.cnt = new Vector(gs.cnt.x,
+ gs.cnt.y,
+ gs.cnt.z + CENTER_POINT_CHANGE);
+ break;
+ // Down.
+ case 'z': gs.cnt = new Vector(gs.cnt.x,
+ gs.cnt.y,
+ gs.cnt.z - CENTER_POINT_CHANGE);
+ break;
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+
+ }
+
+}