Project::OSiRiON - Git repositories
Project::OSiRiON
News . About . Screenshots . Downloads . Forum . Wiki . Tracker . Git
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/render/camera.cc')
-rw-r--r--src/render/camera.cc349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/render/camera.cc b/src/render/camera.cc
new file mode 100644
index 0000000..8da6bc9
--- /dev/null
+++ b/src/render/camera.cc
@@ -0,0 +1,349 @@
+/*
+ render/camera.cc
+ This file is part of the Osirion project and is distributed under
+ the terms and conditions of the GNU General Public License version 2
+*/
+
+#include "core/core.h"
+#include "math/mathlib.h"
+#include "math/matrix4f.h"
+#include "render/camera.h"
+#include "render/gl.h"
+#include "sys/sys.h"
+
+using math::degrees360f;
+using math::degrees180f;
+
+namespace render
+{
+
+const float MIN_DELTA = 10e-10;
+
+const float pitch_track = -15.0f;
+const float pitch_overview = -75.0f;
+
+float Camera::camera_aspect = 1.0f;
+math::Vector3f Camera::camera_eye;
+math::Vector3f Camera::camera_target;
+math::Axis Camera::camera_axis;
+Camera::Mode Camera::camera_mode;
+
+// current and target yaw angle in XZ plane, positive is looking left
+float Camera::direction_current;
+float Camera::direction_target;
+float Camera::target_direction;
+
+// current and target pitch angle in XY, positive is looking up
+float Camera::pitch_current;
+float Camera::pitch_target;
+float Camera::target_pitch;
+
+float Camera::distance;
+
+void Camera::init()
+{
+ camera_aspect = 1.0f;
+
+ direction_current = 0;
+ direction_target = 0;
+
+ pitch_current = pitch_track * 2;
+ pitch_target = pitch_track;
+
+ target_pitch = 0.0f;
+ target_direction = 0.0f;
+
+ distance = 0.4f;
+
+ set_mode(Track);
+
+ camera_axis.clear();
+ camera_eye.clear();
+ camera_target.clear();
+
+}
+
+void Camera::shutdown()
+{
+}
+
+void Camera::set_aspect(float aspect)
+{
+ camera_aspect = aspect;
+}
+
+void Camera::set_mode(Mode newmode) {
+
+ direction_target = 0;
+ direction_current = direction_target;
+ pitch_target = pitch_track;
+ pitch_current = pitch_target;
+
+ target_direction = 0.0f;
+ target_pitch = 0.0f;
+ distance = 0.4f;
+
+ camera_axis.clear();
+
+ switch(newmode) {
+ case Track:
+ // switch camera to Track mode
+ camera_mode = Track;
+ if (core::localcontrol()) {
+ if (core::localcontrol()->state())
+ camera_axis.assign(core::localcontrol()->state()->axis());
+ else
+ camera_axis.assign(core::localcontrol()->axis());
+ }
+ break;
+
+ case Free:
+ // switch camera to Free mode
+ camera_mode = Free;
+ pitch_target = 2.0 * pitch_track;
+ pitch_current = pitch_target;
+ break;
+
+ case Cockpit:
+ camera_mode = Cockpit;
+ break;
+
+ case Overview:
+ // switch camera to Overview mode
+ camera_mode = Overview;
+
+ default:
+ break;
+ }
+
+}
+
+void Camera::next_mode()
+{
+
+ if (!core::localcontrol()) {
+ set_mode(Overview);
+ return;
+ }
+
+ switch(camera_mode) {
+ case Free:
+ // switch camera to Track mode
+ set_mode(Track);
+ con_print << "camera mode: track" << std::endl;
+ break;
+
+ case Track:
+ // switch camera to Cockpit mode
+ set_mode(Cockpit);
+ con_print << "camera mode: cockpit" << std::endl;
+ break;
+
+ case Cockpit:
+ // switch camera to Free mode
+ set_mode(Free);
+ con_print << "camera mode: free" << std::endl;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void Camera::draw(float seconds)
+{
+ math::Matrix4f matrix;
+ math::Axis target_axis;
+ float d = 0;
+
+ if (!core::localcontrol()) {
+
+ if (camera_mode != Overview) {
+ set_mode(Overview);
+ }
+
+ camera_eye.clear();
+ camera_target.clear();
+ camera_axis.clear();
+ pitch_current = pitch_overview;
+ camera_axis.change_pitch(pitch_current);
+
+ distance = 20.0f;
+
+ } else {
+ if (mode() == Overview)
+ set_mode(Track);
+
+ if (core::localcontrol()->state()) {
+ camera_target.assign(core::localcontrol()->state()->location());
+ target_axis.assign(core::localcontrol()->state()->axis());
+ } else {
+ camera_target.assign(core::localcontrol()->location());
+ target_axis.assign(core::localcontrol()->axis());
+ }
+
+ if (core::localcontrol()->model()) {
+ distance = core::localcontrol()->model()->radius();
+ } else {
+ distance = 1.0f;
+ }
+
+ if (mode() == Track) {
+ float cosangle;
+ float angle;
+ float side;
+ float u;
+ const float cam_speed = seconds;
+
+ math::Vector3f n;
+ math::Vector3f p;
+
+ // camera axis: pitch
+
+ // project target_axis.up() into the plane with axis->left() normal
+ n = camera_axis.left();
+ p = target_axis.up();
+ u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
+ p = target_axis.up() + u * n;
+
+ side = camera_axis.forward().x * p.x +
+ camera_axis.forward().y * p.y +
+ camera_axis.forward().z * p.z;
+
+ if ((fabs(side) - MIN_DELTA > 0)) {
+
+ cosangle = math::dotproduct(p, camera_axis.up());
+ if (fabs(cosangle) + MIN_DELTA < 1 ) {
+ angle = acos(cosangle) * 180.0f / M_PI;
+ angle = math::sgnf(side) * angle * cam_speed;
+ camera_axis.change_pitch(-angle);
+ }
+ }
+
+ // camera axis: direction
+
+ // project target_axis.forward() into the plane with axis.up() normal
+ n = camera_axis.up();
+ p = target_axis.forward();
+ u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
+ p = target_axis.forward() + u * n;
+
+ side = camera_axis.left().x * p.x +
+ camera_axis.left().y * p.y +
+ camera_axis.left().z * p.z;
+
+ if ((fabs(side) - MIN_DELTA > 0)) {
+
+ cosangle = math::dotproduct(p, camera_axis.forward());
+ if (fabs(cosangle) + MIN_DELTA < 1 ) {
+ angle = acos(cosangle) * 180.0f / M_PI;
+ angle = math::sgnf(side) * angle * cam_speed;
+ camera_axis.change_direction(angle);
+ }
+ }
+
+ // camera axis: roll
+
+ // project target_axis.up() into the plane with axis.forward() normal
+ n = camera_axis.forward();
+ p = target_axis.up();
+ u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
+ p = target_axis.up() + u * n;
+
+ side = camera_axis.left().x * p.x +
+ camera_axis.left().y * p.y +
+ camera_axis.left().z * p.z;
+
+ if ((fabs(side) - MIN_DELTA > 0)) {
+
+ cosangle = math::dotproduct(p, camera_axis.up());
+ if (fabs(cosangle) + MIN_DELTA < 1 ) {
+ angle = acos(cosangle) * 180.0f / M_PI;
+ angle = math::sgnf(side) * angle * cam_speed;
+ camera_axis.change_roll(angle);
+ }
+ }
+
+ if (core::localcontrol()->model()) {
+ camera_target -= (core::localcontrol()->model()->maxbbox().x + 0.1f) * camera_axis.forward();
+ camera_target += (core::localcontrol()->model()->maxbbox().z + 0.1f ) * camera_axis.up();
+ }
+
+ } else if (mode() == Free) {
+ camera_axis.assign(target_axis);
+
+ direction_target = direction_current - 90 * target_direction;
+ pitch_target = pitch_current - 90 * target_pitch;
+
+ // adjust direction
+ d = degrees180f(direction_current - direction_target);
+ direction_current = degrees360f( direction_current - d * seconds);
+ camera_axis.change_direction(direction_current);
+
+ // adjust pitch
+ d = degrees180f(pitch_current - pitch_target);
+ pitch_current = degrees360f(pitch_current - d * seconds);
+ camera_axis.change_pitch(pitch_current);
+
+ } else if (mode() == Cockpit) {
+ camera_axis.assign(target_axis);
+
+ if (core::localcontrol()->state() && core::localcontrol()->model())
+ camera_target += (core::localcontrol()->model()->maxbbox().x+0.05) *
+ core::localcontrol()->state()->axis().forward();
+
+ distance = 0.0f;
+ }
+ }
+
+ // Change to the projection matrix and set our viewing volume.
+ gl::matrixmode(GL_PROJECTION);
+ gl::loadidentity();
+
+ const float frustum_size = 0.5f;
+ const float frustum_front = 1.0f;
+ distance += frustum_front;
+ gl::frustum(-frustum_size*aspect(), frustum_size*aspect(), -frustum_size, frustum_size, frustum_front, 1024.0f);
+
+ // model view
+ gl::matrixmode(GL_MODELVIEW);
+ gl::loadidentity();
+
+ // map world coordinates to opengl coordinates
+ gl::rotate(90.0f, 0, 1.0, 0);
+ gl::rotate(-90.0f, 1.0f , 0, 0);
+
+ // assign transformation matrix
+ matrix.assign(camera_axis);
+
+ // apply the transpose of the axis transformation (the axis is orhtonormal)
+ gl::multmatrix(matrix.transpose());
+
+ // match the camera with the current target
+ gl::translate(-1.0f * camera_target);
+
+ // apply camera offset
+ gl::translate(distance * camera_axis.forward());
+
+ // calculate eye position
+ camera_eye = camera_target - (distance * camera_axis.forward());
+}
+
+void Camera::set_direction(float direction)
+{
+ target_direction = direction;
+ math::clamp(target_direction, -1.0f, 1.0f);
+}
+
+void Camera::set_pitch(float pitch)
+{
+ target_pitch = pitch;
+ math::clamp(target_pitch, -1.0f, 1.0f);
+}
+
+void Camera::reset()
+{
+ set_mode(camera_mode);
+}
+
+}