Camera.java :  » Graphics-3D-2D-OpenGL » dwarf-fw » se » ltu » android » demo » scene » camera » Android Open Source

Android Open Source » Graphics 3D 2D OpenGL » dwarf fw 
dwarf fw » se » ltu » android » demo » scene » camera » Camera.java
/* SVN FILE: $Id: Camera.java 24 2009-08-17 15:32:24Z belse $ */
package se.ltu.android.demo.scene.camera;

import se.ltu.android.demo.scene.intersection.Ray;
import android.opengl.Matrix;

/**
 * A class representing a camera. It's an abstract representation of
 * anything needed to create an OpenGL view.
 * @author ke Svedin <ake.svedin@gmail.com>
 * @version $Revision: 24 $
 * @lastmodified $Date: 2009-08-17 08:32:24 -0700 (Mon, 17 Aug 2009) $
 */
public class Camera {
  /**
   * Constant for multiplying angular degrees to radians. 
   */
  public static final float DEG_TO_RAD = 0.01745329238474369f;
  
  // Projection matrix.. keep static so all instances of camera
  // share the same projection
  private static float[] project = {
    1,0,0,0,
    0,1,0,0,
    0,0,1,0,
    0,0,0,1
  };
  
  // keep some variables that are good for calculating a picking ray
  private static float near_height;
  private static float zNear;
  private static float aspect;
  private static float height;
  private static float half_width;
  private static float half_height;
  
  // Model View matrix and instance variables 
  private float[] model = {
    1,0,0,0,
    0,1,0,0,
    0,0,1,0,
    0,0,0,1  
  };
  private float[] invModel = new float[16];
  private float[] position = new float[3];
  
  public Camera() {
    
  }
  
  /**
   * Set the projection matrix, similar to gluPerspective.
   * @param fovy Field of view angle in y coordinate
   * @param width Width of screen
   * @param height Height of screen
   * @param zNear Distance to near-plane
   * @param zFar Distance to far-plane
   */
  public static void setPerspective(float fovy, float width, float height, float zNear, float zFar) {    
    float tan_fovy_half = (float) Math.tan((fovy * DEG_TO_RAD) / 2);
    Camera.near_height = zNear * tan_fovy_half;
    Camera.zNear = zNear;
    Camera.height = height;
    Camera.half_width = width / 2;
    Camera.half_height = height / 2;
    Camera.aspect = width / height;
    project[5] = 1 / tan_fovy_half;  // = cot(fovy/2)

    // Remember, column major matrix
    project[0] = project[5] / aspect;
    project[1] = 0.0f;
    project[2] = 0.0f;
    project[3] = 0.0f;

    project[4] = 0.0f;
    //project[5] = 1 / near_height;  // already set
    project[6] = 0.0f;
    project[7] = 0.0f;

    project[8] = 0.0f;
    project[9] = 0.0f;
    project[10] = (zFar + zNear) / (zNear - zFar);
    project[11] = -1.0f;

    project[12] = 0.0f;
    project[13] = 0.0f;
    project[14] = (2 * zFar * zNear) / (zNear - zFar);
    project[15] = 0.0f;
  }
  
  /**
   * @return the projection matrix 
   */
  public static float[] getProjectionM() {
    return project;
  }
  
  /**
   * @param m the model-view matrix to set
   */
  public void setModelM(float[] m) {
    synchronized(model) {
      for(int i = 0; i < 16; i++) {
        model[i] = m[i];
      }
    }
  }
  
  /**
   * Sets the cameras rotation matrix. This is similar
   * to setting the model view matrix but it keeps the
   * cameras current position.
   * @param rotM rotation matrix to set
   */
  public void setRotationM(float[] rotM) {
    synchronized(model) {
      for(int i = 0; i < 16; i++) {
        model[i] = rotM[i];
      }
      Matrix.translateM(model, 0, -position[0], -position[1], -position[2]);
    }
  }
  
  /**
   * @return the model view matrix
   */
  public float[] getModelM() {
    synchronized(model) {
      return model;
    }
  }
  
  /**
   * Define a viewing transformation in terms of an eye point, a center of view, and an up vector.
   * @param eyex eye x coordinate
   * @param eyey eye y coordinate
   * @param eyez eye z coordinate
   * @param centerx view center x coordinate
   * @param centery view center y coordinate
   * @param centerz view center z coordinate
   * @param upx up vector x coordinate
   * @param upy up vector y coordinate
   * @param upz up vector z coordinate
   */
  public void lookAt(
      float eyex, float eyey, float eyez,
      float centerx, float centery, float centerz,
      float upx, float upy, float upz) {
    
      float[] x = new float[3]; 
      float[] y = new float[3];
      float[] z = new float[3];
      float mag;

      // Make rotation matrix
        
      // Z vector
      z[0] = eyex - centerx;
      z[1] = eyey - centery;
      z[2] = eyez - centerz;
      
      mag = Matrix.length(z[0], z[1], z[2]);
      if (mag > 0) {      // mpichler, 19950515
        mag = 1/mag;
        z[0] *= mag;
        z[1] *= mag;
        z[2] *= mag;
      }
      
      // Y vector
      y[0] = upx;
      y[1] = upy;
      y[2] = upz;

      // X vector = Y cross Z      
      x[0] = y[1] * z[2] - y[2] * z[1];
      x[1] = -y[0] * z[2] + y[2] * z[0];
      x[2] = y[0] * z[1] - y[1] * z[0];
      
      // Recompute Y = Z cross X      
      y[0] = z[1] * x[2] - z[2] * x[1];
      y[1] = -z[0] * x[2] + z[2] * x[0];
      y[2] = z[0] * x[1] - z[1] * x[0];
      
      // mpichler, 19950515
      
      // cross product gives area of parallelogram, which is < 1.0 for
      // non-perpendicular unit-length vectors; so normalize x, y here

      mag = Matrix.length(x[0], x[1], x[2]);
      if (mag > 0) {
        mag = 1/mag;
        x[0] *= mag;
        x[1] *= mag;
        x[2] *= mag;
      }

      mag = Matrix.length(y[0], y[1], y[2]);
      if (mag > 0) {
        mag = 1/mag;
        y[0] *= mag;
        y[1] *= mag;
        y[2] *= mag;
      }

      synchronized(model) {
        model[0] = x[0];
        model[4] = x[1];
        model[8] = x[2];
        model[12] = 0.0f;
        model[1] = y[0];
        model[5] = y[1];
        model[9] = y[2];
        model[13] = 0.0f;
        model[2] = z[0];
        model[6] = z[1];
        model[10] = z[2];
        model[14] = 0.0f;
        model[3] = 0.0f;
        model[7] = 0.0f;
        model[11] = 0.0f;
        model[15] = 1.0f;
        
        //Matrix.multiplyMM(model, 0, m, 0, model, 0);
        // Translate Eye to Origin 
        position[0] = eyex;
        position[1] = eyey;
        position[2] = eyez;
        Matrix.translateM(model, 0, -position[0], -position[1], -position[2]);
      }
    }

  /**
   * Translate the cameras position with the given coordinates
   * @param x translation x coordinate
   * @param y translation y coordinate
   * @param z translation z coordinate
   */
  public void translate(float x, float y, float z) {
    synchronized(model) {
      position[0] -= x;
      position[1] -= y;
      position[2] -= z;
      Matrix.translateM(model, 0, -x, -y, -z);
    }
  }
  
  /**
   * Translate the cameras position with the given coordinates
   * @param vector3f an array of size three, containing x,y and z coordinates
   */
  public void translate(float[] vector3f) {
    if(vector3f == null || vector3f.length != 3) {
      return;
    }
    synchronized(model) {
      position[0] -= vector3f[0];
      position[1] -= vector3f[1];
      position[2] -= vector3f[2];
      Matrix.translateM(model, 0, -vector3f[0], -vector3f[1], -vector3f[2]);
    }
  }
  
  /**
   * @return the current position
   */
  public float[] getPosition() {
    return position;
  }
  
  /**
   * Set the cameras model view matrix to the identity matrix
   */
  public void setIdentity() {
    synchronized(model) {
      Matrix.setIdentityM(model, 0);
      position[0] = 0;
      position[1] = 0;
      position[2] = 0;
    }
  }
  
  /**
   * Set the absolute position of this camera
   * @param x x coordinate
   * @param y y coordinate
   * @param z z coordinate
   */
  public void setPosition(float x, float y, float z) {
    synchronized(model) {
      // revert last position
      Matrix.translateM(model, 0, position[0], position[1], position[2]);
      // set new position
      position[0] = x;
        position[1] = y;
        position[2] = z;
        Matrix.translateM(model, 0, -position[0], -position[1], -position[2]);
    }
  }
  
  /**
   * Calculates a pick ray based on the given screen coordinates and
   * the current projection matrix and model view matrix.
   * 
   * The screen coordinates are expected to have (0,0) at the upper left 
   * corner of the screen and the y-axis is reversed compared to the OpenGL y-axis. 
   * @param pickX screen x coordinate
   * @param pickY screen y coordinate
   */
    public Ray calculatePickRay(float pickX, float pickY) {
      // coordinates centered on the screen
      // -1 <= x <= 1 and -1 <= y <= 1
      float unit_x = (pickX - half_width)/half_width;
      float unit_y = ((height - pickY) - half_height)/half_height;
    
    float[] rayRawPos = {0.0f, 0.0f, 0.0f, 1.0f};
    float[] rayRawDir = {unit_x * near_height * aspect, unit_y * near_height, -zNear, 0.0f};
    float[] rayPos = new float[4];
    float[] rayDir = new float[4];
    
    // multiply the position and vector with the inverse model matrix
    // to get world coordinates
    synchronized(model) {
      Matrix.invertM(invModel, 0, model, 0);
    }
    Matrix.multiplyMV(rayPos, 0, invModel, 0, rayRawPos, 0);
    Matrix.multiplyMV(rayDir, 0, invModel, 0, rayRawDir, 0);

    return new Ray(rayPos[0], rayPos[1], rayPos[2], rayDir[0], rayDir[1], rayDir[2]);
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.