pixelcity/Camera.cpp

404 lines
11 KiB
C++

/*-----------------------------------------------------------------------------
Camera.cpp
2009 Shamus Young
-------------------------------------------------------------------------------
This tracks the position and oritentation of the camera. In screensaver
mode, it moves the camera around the world in order to create dramatic
views of the hot zone.
-----------------------------------------------------------------------------*/
#define EYE_HEIGHT 2.0f
#define MAX_PITCH 85
#define FLYCAM_CIRCUT 60000
#define FLYCAM_CIRCUT_HALF (FLYCAM_CIRCUT / 2)
#define FLYCAM_LEG (FLYCAM_CIRCUT / 4)
#define ONE_SECOND 1000
#define CAMERA_CHANGE_INTERVAL 15
#define CAMERA_CYCLE_LENGTH (CAMERA_MODES*CAMERA_CHANGE_INTERVAL)
#include <windows.h>
#include <math.h>
#include <time.h>
#include "glTypes.h"
#include "ini.h"
#include "macro.h"
#include "math.h"
#include "world.h"
#include "win.h"
enum
{
CAMERA_FLYCAM1,
CAMERA_ORBIT_INWARD,
CAMERA_ORBIT_OUTWARD,
CAMERA_ORBIT_ELLIPTICAL,
CAMERA_FLYCAM2,
CAMERA_SPEED,
CAMERA_SPIN,
CAMERA_FLYCAM3,
CAMERA_MODES
};
static GLvector angle;
static GLvector position;
static GLvector auto_angle;
static GLvector auto_position;
static float distance;
static GLvector movement;
static bool cam_auto;
static float tracker;
static unsigned last_update;
static int camera_behavior;
static unsigned last_move;
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
static GLvector flycam_position (unsigned t)
{
unsigned leg;
float delta;
GLvector start, end;
GLbbox hot_zone;
hot_zone = WorldHotZone ();
t %= FLYCAM_CIRCUT;
leg = t / FLYCAM_LEG;
delta = (float)(t % FLYCAM_LEG) / FLYCAM_LEG;
switch (leg) {
case 0:
start = glVector (hot_zone.min.x, 25.0f, hot_zone.min.z);
end = glVector (hot_zone.min.x, 60.0f, hot_zone.max.z);
break;
case 1:
start = glVector (hot_zone.min.x, 60.0f, hot_zone.max.z);
end = glVector (hot_zone.max.x, 25.0f, hot_zone.max.z);
break;
case 2:
start = glVector (hot_zone.max.x, 25.0f, hot_zone.max.z);
end = glVector (hot_zone.max.x, 60.0f, hot_zone.min.z);
break;
case 3:
start = glVector (hot_zone.max.x, 60.0f, hot_zone.min.z);
end = glVector (hot_zone.min.x, 25.0f, hot_zone.min.z);
break;
}
delta = MathScalarCurve (delta);
return glVectorInterpolate (start, end, delta);
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
static void do_auto_cam ()
{
float dist;
unsigned t;
unsigned elapsed;
unsigned now;
int behavior;
GLvector target;
now = GetTickCount ();
elapsed = now - last_update;
elapsed = MIN (elapsed, 50); //limit to 1/20th second worth of time
if (elapsed == 0)
return;
last_update = now;
t = time (NULL) % CAMERA_CYCLE_LENGTH;
#if SCREENSAVER
behavior = t / CAMERA_CHANGE_INTERVAL;
#else
behavior = camera_behavior;
#endif
tracker += (float)elapsed / 300.0f;
//behavior = CAMERA_FLYCAM1;
switch (behavior) {
case CAMERA_ORBIT_INWARD:
auto_position.x = WORLD_HALF + sinf (tracker * DEGREES_TO_RADIANS) * 150.0f;
auto_position.y = 60.0f;
auto_position.z = WORLD_HALF + cosf (tracker * DEGREES_TO_RADIANS) * 150.0f;
target = glVector (WORLD_HALF, 40.0f, WORLD_HALF);
break;
case CAMERA_ORBIT_OUTWARD:
auto_position.x = WORLD_HALF + sinf (tracker * DEGREES_TO_RADIANS) * 250.0f;
auto_position.y = 60.0f;
auto_position.z = WORLD_HALF + cosf (tracker * DEGREES_TO_RADIANS) * 250.0f;
target = glVector (WORLD_HALF, 30.0f, WORLD_HALF);
break;
case CAMERA_ORBIT_ELLIPTICAL:
dist = 150.0f + sinf (tracker * DEGREES_TO_RADIANS / 1.1f) * 50;
auto_position.x = WORLD_HALF + sinf (tracker * DEGREES_TO_RADIANS) * dist;
auto_position.y = 60.0f;
auto_position.z = WORLD_HALF + cosf (tracker * DEGREES_TO_RADIANS) * dist;
target = glVector (WORLD_HALF, 50.0f, WORLD_HALF);
break;
case CAMERA_FLYCAM1:
case CAMERA_FLYCAM2:
case CAMERA_FLYCAM3:
auto_position = (flycam_position (now) + flycam_position (now + 4000)) / 2.0f;
target = flycam_position (now + FLYCAM_CIRCUT_HALF - ONE_SECOND * 3);
break;
case CAMERA_SPEED:
auto_position = (flycam_position (now) + flycam_position (now + 500)) / 2.0f;
target = flycam_position (now + ONE_SECOND * 5);
auto_position.y /= 2;
target.y /= 2;
break;
case CAMERA_SPIN:
default:
target.x = WORLD_HALF + sinf (tracker * DEGREES_TO_RADIANS) * 300.0f;
target.y = 30.0f;
target.z = WORLD_HALF + cosf (tracker * DEGREES_TO_RADIANS) * 300.0f;
auto_position.x = WORLD_HALF + sinf (tracker * DEGREES_TO_RADIANS) * 50.0f;
auto_position.y = 60.0f;
auto_position.z = WORLD_HALF + cosf (tracker * DEGREES_TO_RADIANS) * 50.0f;
}
dist = MathDistance (auto_position.x, auto_position.z, target.x, target.z);
auto_angle.y = MathAngle (-MathAngle (auto_position.x, auto_position.z, target.x, target.z));
auto_angle.x = 90.0f + MathAngle (0, auto_position.y, dist, target.y);
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraAutoToggle ()
{
cam_auto = !cam_auto;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraNextBehavior ()
{
camera_behavior++;
camera_behavior %= CAMERA_MODES;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraYaw (float delta)
{
angle.y -= delta;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraPitch (float delta)
{
angle.x -= delta;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraPan (float delta)
{
float move_x, move_y;
move_x = (float)sin (-angle.y * DEGREES_TO_RADIANS) / 10.0f;
move_y = (float)cos (-angle.y * DEGREES_TO_RADIANS) / 10.0f;
position.x -= move_y * delta;
position.z -= -move_x * delta;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraForward (float delta)
{
float move_x, move_y;
move_y = (float)sin (-angle.y * DEGREES_TO_RADIANS) / 10.0f;
move_x = (float)cos (-angle.y * DEGREES_TO_RADIANS) / 10.0f;
position.x -= move_y * delta;
position.z -= move_x * delta;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraVertical (float val)
{
movement.y += val;
last_move = GetTickCount ();
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraLateral (float val)
{
movement.x += val;
last_move = GetTickCount ();
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraMedial (float val)
{
movement.z += val;
last_move = GetTickCount ();
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
GLvector CameraPosition (void)
{
if (cam_auto)
return auto_position;
return position;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraReset ()
{
position.y = 50.0f;
position.x = WORLD_HALF;
position.z = WORLD_HALF;
angle.x = 0.0f;
angle.y = 0.0f;
angle.z = 0.0f;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraPositionSet (GLvector new_pos)
{
position = new_pos;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
GLvector CameraAngle (void)
{
if (cam_auto)
return auto_angle;
return angle;
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraAngleSet (GLvector new_angle)
{
angle = new_angle;
angle.x = CLAMP (angle.x, -80.0f, 80.0f);
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraInit (void)
{
angle = IniVector ("CameraAngle");
position = IniVector ("CameraPosition");
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraUpdate (void)
{
CameraPan (movement.x);
CameraForward (movement.z);
position.y += movement.y / 10.0f;
if (GetTickCount () - last_move > 1000)
movement *= 0.9f;
else
movement *= 0.99f;
if (SCREENSAVER)
cam_auto = true;
if (cam_auto)
do_auto_cam ();
if (angle.y < 0.0f)
angle.y = 360.0f - (float)fmod (fabs (angle.y), 360.0f);
angle.y = (float)fmod (angle.y, 360.0f);
angle.x = CLAMP (angle.x, -MAX_PITCH, MAX_PITCH);
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/
void CameraTerm (void)
{
//just store our most recent position in the ini
IniVectorSet ("CameraAngle", angle);
IniVectorSet ("CameraPosition", position);
}