in this new version you can either
- look around (FPS) with mouse and “wasd” keys OR
- let the camera fly around a spot in front of you (denoted by a green sphere)
Toggle with r
Chrisir
// CONSTANT: the floor / field (y value)
final int GENERAL_Y_HEIGHT = 500; 
// rotate around itself (player, FPS) OR around a point 
boolean modeCamRotatesAroundPoint = false; 
// Classes: The camera 
CameraClass cam; 
// The scene (background, field, boxes...) 
Scene scene = new Scene(); 
// minor tools 
Tools tools = new Tools(); 
// angles  
float angleLeftRight; 
float angleUpDown=GENERAL_Y_HEIGHT-66;  // not a real angle!!! 
float modeCamRotatesAroundPointAngle; 
PVector spherePosPV=new PVector(0, GENERAL_Y_HEIGHT-500, 0);
float prevAngle; 
// -----------------------------------------------------
void setup() {
  size (1400, 800, P3D);
  // setup camera  
  cam = new CameraClass (313, 
    GENERAL_Y_HEIGHT - 66, 
    113);
  // avoid clipping : https : // forum.processing.org/two/discussion/4128/quick-q-how-close-is-too-close-why-when-do-3d-objects-disappear
  perspective(PI/3.0, (float) width/height, 1, 1000000);
} // func 
void draw() {
  // set Canvas and Environment
  scene.setCanvasAndEnvironment(); 
  if (modeCamRotatesAroundPoint) {
    // show Cam Rotates Around Point
    showModeCamRotatesAroundPoint();
  } else {
    // first person shooter perspective (FPS)
    showFPS();
  }//else 
  // display the scene with the boxes and HUD text 
  scene.displayScene();
  //
} // func draw
// ---------------------------------------------------
void showModeCamRotatesAroundPoint() {
  // rotate around a point 
  cam.setPosAngle(modeCamRotatesAroundPointAngle); 
  cam.set();    // apply the values of the class cam to the real camera
  tools.mySphere(spherePosPV.x, spherePosPV.y, spherePosPV.z, 
    7);
  if (spherePosPV.y >= GENERAL_Y_HEIGHT) { 
    spherePosPV.y=GENERAL_Y_HEIGHT;  
    modeCamRotatesAroundPointAngle+=.032;
  } else {
    // sphere falls 
    spherePosPV.y+=10;
  }//
}
void showFPS() {
  // rotate around itself (player, FPS)
  angleLeftRight=map(mouseX, 0, width, 0, 3*PI);
  //  angleUpDown=map(mouseY, 0, height, GENERAL_Y_HEIGHT-150, GENERAL_Y_HEIGHT+150);// not a real angle!!! 
  if (pmouseY<mouseY-5)
    angleUpDown += .21; 
  else if (pmouseY>mouseY+5)
    angleUpDown += -.21; 
  // camera 
  cam.setLookAtAngle(angleLeftRight);   // 
  cam.set();    // apply the values of the class cam to the real camera
  // sphere disappears 
  if (spherePosPV.y < GENERAL_Y_HEIGHT-310) { 
    // do nothing
  } else {
    // sphere 
    spherePosPV.y-=10;
    tools.mySphere(spherePosPV.x, spherePosPV.y, spherePosPV.z, 
      7);
  }//else 
  cam.keyPressedThroughout();
}//func 
// ---------------------------------------------------
void keyPressed() {
  //
  if (key=='r') {
    // Toggle: FPS OR cam rotates AROUND a point  
    modeCamRotatesAroundPoint = 
      ! modeCamRotatesAroundPoint;  // toggle 
    // modeCamRotatesAroundPoint has a new value now, 
    // it's a new mode so we can set some start conditions for the new mode.  
    if (modeCamRotatesAroundPoint) {
      // starting mode: modeCamRotatesAroundPoint
      prevAngle=angleLeftRight; 
      modeCamRotatesAroundPointAngle=angleLeftRight-PI; 
      // define camera 
      float x1 = cam.camPos.x + cos(angleLeftRight) *300; 
      float y1 = angleUpDown;   // GENERAL_Y_HEIGHT; 
      float z1 = cam.camPos.z + sin(angleLeftRight) *300;
      cam.setLookAt(x1, y1, z1);
      // define sphere 
      spherePosPV=cam.camLookAt.copy();
      spherePosPV.y=GENERAL_Y_HEIGHT-310;
    } else {
      // starting mode PVS
      angleLeftRight=modeCamRotatesAroundPointAngle;
      float x1 = cam.camLookAt.x - cos(prevAngle) *300; 
      float y1 = angleUpDown;   // GENERAL_Y_HEIGHT; 
      float z1 = cam.camLookAt.z - sin(prevAngle) *300;
      cam.camPos.set(x1, y1, z1); // =cam.camLookAt.copy();
    }
    //
  } else if (key==ESC) {
    modeCamRotatesAroundPoint = false;
    key=0; // kill ESC
  }// else if
}//func 
// =================================================================
// classes 
class Scene {
  // cubes 
  float angleLeftRightCubes=45; // angleLeftRight for the cubes in the scene
  void setCanvasAndEnvironment() {
    // set Canvas and Environment
    // clear canvas 
    background(111);
    // apply lights 
    lights();
  }
  void displayScene() {
    // Drawing of the scene / decoration  
    // Drawing of the red field  ---- 
    fill(255, 2, 2);//RED
    noStroke(); 
    tools.mySphere(500, GENERAL_Y_HEIGHT, 500, 20);
    tools.mySphere(100, GENERAL_Y_HEIGHT, 100, 20);
    tools.mySphere(100, GENERAL_Y_HEIGHT, 500, 20);
    tools.mySphere(500, GENERAL_Y_HEIGHT, 100, 20);
    // rect of lines  / floor / field 
    stroke(255, 2, 2);//RED
    // using 3D lines: 
    line(500, GENERAL_Y_HEIGHT, 500, 500, GENERAL_Y_HEIGHT, 100);  // up |
    line(500, GENERAL_Y_HEIGHT, 100, 100, GENERAL_Y_HEIGHT, 100);  // left <-
    line(100, GENERAL_Y_HEIGHT, 100, 100, GENERAL_Y_HEIGHT, 500 ); // down |
    line(100, GENERAL_Y_HEIGHT, 500, 500, GENERAL_Y_HEIGHT, 500 ); // right ->
    // one small wall of boxes
    stroke(0);
    int z = -60; 
    for (int x = 10; x < 600; x+= 100) {
      fill(map(x, 10, 600, 111, 255), 2, 2);
      for (int y = 410; y < 600; y+= 100) {
        tools.myBox(x, y, z, 24, angleLeftRightCubes);
      }
    }
    // a few additional blue boxes
    fill(0, 0, 255);
    z=-400;
    tools.myBox(220, 10, z, 24, angleLeftRightCubes);
    tools.myBox(600, 10, z, 24, angleLeftRightCubes);
    z=-400;
    tools.myBox(220, 510, z, 24, angleLeftRightCubes);
    tools.myBox(600, 510, z, 24, angleLeftRightCubes);
    z=399;
    tools.myBox(220, 510, z, 24, angleLeftRightCubes);
    tools.myBox(600, 510, z, 24, angleLeftRightCubes);
    z=900;
    tools.myBox(220, 510, z, 24, angleLeftRightCubes);
    tools.myBox(600, 510, z, 24, angleLeftRightCubes);
    angleLeftRightCubes++;
    //
    // text upper left corner (HUD)
    fill(0, 255, 0); 
    cam.HUD_text("Use Mouse. Click r to toggle mode. " );  //
  }
  //
} // class 
// =================================================================
class CameraClass {
  // capsules the normal camera() command and its vectors 
  PVector camPos;     // its vectors 
  PVector camLookAt;
  PVector camUp;
  PVector camPosInitial;     // its vectors - the default (unchanged) 
  PVector camLookAtInitial;
  PVector camUpInitial; 
  //  float angleLeftRightForCamerasUpAndDownMovement=0;   // angleLeftRight for the slight up and down movement of the camera
  // constructor I - without parameters
  CameraClass() {
    // constr
    // set vectors 
    camPos    = new PVector(width/2.0, height/2.0, 990);
    camLookAt = new PVector(width/2.0, height/2.0, -600);
    camUp     = new PVector( 0, 1, 0 );
    // save the initial values
    camPosInitial    = camPos.copy();
    camLookAtInitial = camLookAt.copy();
    camUpInitial     = camUp.copy();
  }  // constr I
  // constructor II - with parameters for POS 
  CameraClass (float x1, float y1, float z1) {
    // constr
    // set vectors 
    camPos    = new PVector(x1, y1, z1);
    camLookAt = new PVector(width/2.0, height/2.0, -600);
    camUp     = new PVector( 0, 1, 0 );
    // save the initial values
    camPosInitial    = camPos.copy();
    camLookAtInitial = camLookAt.copy();
    camUpInitial     = camUp.copy();
    camPos = new PVector(x1, y1, z1);
  }  // constr II
  // ----------------------------------------
  void set() {
    // apply internal class vectors to actual camera
    camera (camPos.x, camPos.y, camPos.z, 
      camLookAt.x, camLookAt.y, camLookAt.z, 
      camUp.x, camUp.y, camUp.z);
  }
  // ----------------------------------------
  void setPos (float x1, float y1, float z1) {
    camPos.set(x1, y1, z1);
  }
  void setLookAt (float x1, float y1, float z1) {
    camLookAt.set(x1, y1, z1);
  }
  // ----------------------------------------
  void setPosAngle (float angle_) {
    // look to point while rotating around it 
    // 
    float x1 = camLookAt.x + cos(angle_) *300; 
    float y1 = angleUpDown;   // GENERAL_Y_HEIGHT; 
    float z1 = camLookAt.z + sin(angle_) *300;
    camPos.set(x1, y1, z1);
  }
  void setLookAtAngle (float angleLeftRight) {
    // FPS 
    // from angleLeftRight
    float x1 = camPos.x + cos(angleLeftRight) *10; 
    float y1 = angleUpDown;   // GENERAL_Y_HEIGHT; 
    float z1 = camPos.z + sin(angleLeftRight) *10;
    camLookAt.set(x1, y1, z1);
  }
  // ----------------------------------------
  void keyPressedThroughout() {
    if (!keyPressed) 
      return;
    switch(key) {
    case 'w':
      // run forward / running towards lookat 
      float x1 = camPos.x + cos(angleLeftRight) *1; 
      float z1 = camPos.z + sin(angleLeftRight) *1;
      camPos.set(x1, camPos.y, z1);
      break;
    case 's':
      // run backward 
      x1 = camPos.x - cos(angleLeftRight) *1; 
      z1 = camPos.z - sin(angleLeftRight) *1;
      camPos.set(x1, camPos.y, z1);
      break;
    case 'a': 
      // left / sideways 
      x1 = camPos.x + cos(angleLeftRight-HALF_PI) * 1;
      z1 = camPos.z + sin(angleLeftRight-HALF_PI) * 1;
      camPos.set(x1, camPos.y, z1);
      break;
    case 'd':
      // right 
      x1 = camPos.x - cos(angleLeftRight-HALF_PI) * 1;
      z1 = camPos.z - sin(angleLeftRight-HALF_PI) * 1;
      camPos.set(x1, camPos.y, z1);
      break;
    }//switch
  }// method
  void HUD_text (String text_) {
    // HUD text upper left corner - this must be called at the very end of draw()
    // this is a 2D HUD 
    camera();
    hint(DISABLE_DEPTH_TEST);
    noLights();
    // ------------------
    textSize(16);
    text (text_, 20, 20);
    // ------------------
    // reset all parameters to defaults
    textAlign(LEFT, BASELINE);
    rectMode(CORNER);
    textSize(32);
    hint(ENABLE_DEPTH_TEST); // no HUD anymore
    lights();
  } // method
  //
} // class
// ======================================
class Tools {
  // tools 
  void myBox(float x, float y, float z, 
    float size1, 
    float angleLeftRightCube) {
    // one nice wrapper for build in box-command
    pushMatrix();
    translate(x, y, z);
    rotateY(radians(angleLeftRightCube));
    rotateX(radians(45));
    box(size1);
    popMatrix();
  }
  void mySphere(float x, float y, float z, 
    float size1) {
    // one nice wrapper for build in sphere-command
    pushMatrix();
    noStroke(); 
    translate(x, y, z);
    sphere(size1);
    popMatrix();
  }
}//class 
//