FPSCamera.java :  » Game » rugl » com » ryanm » droid » rugl » util » Java Open Source

Java Open Source » Game » rugl 
rugl » com » ryanm » droid » rugl » util » FPSCamera.java

package com.ryanm.droid.rugl.util;

import android.opengl.GLES10;

import com.ryanm.droid.rugl.Game;
import com.ryanm.droid.rugl.gl.GLU;
import com.ryanm.droid.rugl.util.geom.Frustum;
import com.ryanm.droid.rugl.util.geom.Matrix4f;
import com.ryanm.droid.rugl.util.geom.Vector3f;
import com.ryanm.droid.rugl.util.geom.Vector4f;
import com.ryanm.droid.rugl.util.math.Range;
import com.ryanm.preflect.annote.Category;
import com.ryanm.preflect.annote.Order;
import com.ryanm.preflect.annote.Summary;
import com.ryanm.preflect.annote.Variable;

/**
 * A camera suitable for first-person stuff, assumes that the x-z
 * plane is horizontal
 * 
 * @author ryanm
 */
@Variable( "Camera" )
@Summary( "View and steering options" )
public class FPSCamera
{
  private final Vector3f position = new Vector3f();

  /**
   * Invert mode - i.e.: pull down to look up
   */
  @Variable( "Invert Y" )
  @Summary( "Invert mode FTW!" )
  @Order( 0 )
  public boolean invert = true;

  /**
   * The forward vector
   */
  public final Vector3f forward = new Vector3f();

  /**
   * The up vector
   */
  public final Vector3f up = new Vector3f();

  /**
   * The right vector
   */
  public final Vector3f right = new Vector3f();

  private final Matrix4f m = new Matrix4f();

  private Vector4f v4f = new Vector4f();

  /***/
  private float elevation = 0;

  /***/
  private float heading = 0;

  /***/
  @Variable( "Horizontal" )
  @Summary( "in degrees per second" )
  @Category( "Turn speed" )
  public float headingSpeed = 90;

  /***/
  @Variable( "Vertical" )
  @Summary( "in degrees per second" )
  @Category( "Turn speed" )
  public float pitchSpeed = 90;

  /**
   * Aspect ratio of projection. Set to -1 to have it automatically
   * update itself according to screen dimensions
   */
  @Variable( "Aspect ratio" )
  @Summary( "width / height ratio\n<0 to set from screen size" )
  @Category( "Frustum control" )
  public float aspect = -1;

  /**
   * Field of view, in degrees
   */
  @Variable( "Field of vertical view" )
  @Summary( "In degrees" )
  @Category( "Frustum control" )
  public float fov = 70;

  /**
   * Distance to near clipping plane
   */
  @Variable( "Near" )
  @Summary( "Distance to near clip plane" )
  @Category( "Frustum control" )
  public float near = 0.01f;

  /**
   * Distance to far clipping plane
   */
  @Variable( "Far" )
  @Summary( "Distance to far clip plane" )
  @Category( "Frustum control" )
  public float far = 100f;

  /**
   * Handy for culling purposes
   */
  private final Frustum frustum = new Frustum();

  private boolean frustumDirty = true;

  private float[] projectionMatrix = new float[ 16 ];

  private float[] modelViewMatrix = new float[ 16 ];

  /**
   * @param delta
   *           time delta
   * @param x
   *           yaw control, in range -1 to 1
   * @param y
   *           pitch control, in range -1 to 1
   */
  public void advance( float delta, float x, float y )
  {
    heading += -x * Math.toRadians( headingSpeed ) * delta;
    elevation += ( invert ? 1 : -1 ) * y * Math.toRadians( pitchSpeed ) * delta;
    heading = Range.wrap( heading, 0, Trig.TWO_PI );
    elevation = Range.limit( elevation, -Trig.HALF_PI * 0.99f, Trig.HALF_PI * 0.99f );

    if( x != 0 || y != 0 )
    {
      frustumDirty = true;
    }

    updateVectors();
  }

  /**
   * Updates the {@link #forward}, {@link #up} and {@link #right}
   * vectors from the heading and elevation. This is called for you
   * by {@link #advance(float, float, float)}, but not by
   * {@link #setHeading(float)} or {@link #setElevation(float)}
   */
  public void updateVectors()
  {
    m.setIdentity();
    m.rotate( heading, 0, 1, 0 );
    m.rotate( elevation, 1, 0, 0 );
    v4f.set( 0, 0, 1, 0 );
    Matrix4f.transform( m, v4f, v4f );

    forward.set( v4f.x, v4f.y, v4f.z );

    // right vector...
    right.set( forward.z, 0, -forward.x );
    right.normalise();

    // up is target x right
    Vector3f.cross( forward, right, up );
  }

  /**
   * @param heading
   *           in radians
   */
  public void setHeading( float heading )
  {
    this.heading = heading;
    frustumDirty = true;
  }

  /**
   * @param elevation
   *           in radians
   */
  public void setElevation( float elevation )
  {
    this.elevation = elevation;
    frustumDirty = true;
  }

  /**
   * Moves and applies the camera
   * 
   * @param eyeX
   * @param eyeY
   * @param eyeZ
   */
  public void setPosition( float eyeX, float eyeY, float eyeZ )
  {
    if( position.x != eyeX || position.y != eyeY || position.z != eyeZ )
    {
      frustumDirty = true;
    }

    if( aspect == -1 )
    {
      aspect = ( float ) Game.screenWidth / Game.screenHeight;
    }

    GLES10.glMatrixMode( GLES10.GL_PROJECTION );
    GLES10.glLoadIdentity();
    GLU.gluPerspective( fov, aspect, near, far, projectionMatrix );

    GLES10.glMatrixMode( GLES10.GL_MODELVIEW );
    GLES10.glLoadIdentity();

    GLU.gluLookAt( eyeX, eyeY, eyeZ, eyeX + forward.x, eyeY + forward.y, eyeZ
        + forward.z, up.x, up.y, up.z, modelViewMatrix );
  }

  /**
   * Updates the {@link Frustum} as necessary, so call this every
   * frame
   * 
   * @return The camera {@link Frustum}
   */
  public Frustum getFrustum()
  {
    if( frustumDirty )
    {
      frustum.update( projectionMatrix, modelViewMatrix );
      frustumDirty = false;
    }

    return frustum;
  }

  /**
   * @param x
   *           x coordinate of point, in range -1 (for extreme left
   *           of view) to 1 (extreme right)
   * @param y
   *           y coordinate of point, in range -1 (for bottom of
   *           view) to 1 (top of view)
   * @param dest
   *           destination vector to store the result, or null for a
   *           new {@link Vector3f}
   * @return A unit vector pointing in the tapped direction
   */
  public Vector3f unProject( float x, float y, Vector3f dest )
  {
    v4f.set( forward.x, forward.y, forward.z, 0 );

    float yAngle = -y * fov / 2;
    float xAngle = -x * aspect * fov / 2;

    m.setIdentity();
    m.rotate( Trig.toRadians( yAngle ), right.x, right.y, right.z );
    m.rotate( Trig.toRadians( xAngle ), 0, 1, 0 );

    Matrix4f.transform( m, v4f, v4f );

    if( dest == null )
    {
      dest = new Vector3f();
    }

    dest.set( v4f );

    return dest;
  }

  @Override
  public String toString()
  {
    return "e = " + elevation + "\nh = " + heading + "\nf = " + forward + "\nu = " + up
        + "\nr = " + right;
  }
}
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.