diff options
Diffstat (limited to 'src/render/camera.cc')
-rw-r--r-- | src/render/camera.cc | 512 |
1 files changed, 211 insertions, 301 deletions
diff --git a/src/render/camera.cc b/src/render/camera.cc index d6cf562..70684d7 100644 --- a/src/render/camera.cc +++ b/src/render/camera.cc @@ -6,17 +6,14 @@ #include <cmath> -#include "core/application.h" -#include "core/gameinterface.h" -#include "math/mathlib.h" -#include "math/matrix4f.h" #include "render/camera.h" #include "render/gl.h" #include "render/state.h" +#include "core/entity.h" +#include "core/range.h" +#include "math/functions.h" #include "sys/sys.h" -using math::degrees360f; -using math::degrees180f; namespace render { @@ -24,134 +21,72 @@ namespace render const float MIN_DELTA = 10e-10; const float COS_PI_4 = sqrt(2.0f) * 0.5f; -const float pitch_free = -30.0f; -const float pitch_track = -5.0f; -const float pitch_overview = -5.0f; -math::Vector3f Camera::camera_eye; -math::Vector3f Camera::camera_target; -math::Axis Camera::camera_axis; -math::Axis Camera::camera_scene_axis; -Camera::Mode Camera::camera_mode; -Camera::Mode Camera::camera_previous_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; -float Camera::camera_zoom; - -void Camera::init() +Camera::Camera(const Mode mode) { - 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; - camera_zoom = 2.0f; - - camera_mode = Overview; - camera_previous_mode = Track; - set_mode(Track); - - camera_axis.clear(); - camera_scene_axis.clear(); - camera_eye.clear(); - camera_target.clear(); - + _mode = mode; + _distance = 1.0f; + _multiplier = 1.0f; + _target_entity = 0; + + _freelook_direction = 0.0f; + _freelook_pitch = 0.0f; + + _movement_direction = 0.0f; + _movement_pitch = 0.0f; } -void Camera::shutdown() +Camera::~Camera() { } -void Camera::set_mode(Mode newmode) +void Camera::set_mode(const Mode mode) { + _mode = mode; + reset(); +} - 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_scene_axis.clear(); - - if (camera_mode != Overview) - camera_previous_mode = camera_mode; - - switch (newmode) { - case Track: - // switch camera to Track mode - camera_mode = Track; - if (core::localcontrol()) { - camera_scene_axis.assign(core::localcontrol()->axis()); - } - break; - - case Free: - // switch camera to Free mode - camera_mode = Free; - pitch_target = pitch_free; - pitch_current = pitch_target; - break; - - case Cockpit: - camera_mode = Cockpit; - break; - - case Overview: - // switch camera to Overview mode - camera_mode = Overview; +void Camera::set_multiplier(const float multiplier) +{ + _multiplier = multiplier; +} - default: - break; - } +void Camera::set_freelook_direction(const float angle) +{ + _freelook_direction = angle; +} +void Camera::set_freelook_pitch(const float angle) +{ + _freelook_pitch = angle; } -void Camera::view_next() +void Camera::set_movement_direction(const float speed) +{ + _movement_direction = speed; + math::clamp(_movement_direction, -1.0f, 1.0f); +} + +void Camera::set_movement_pitch(const float speed) { + _movement_pitch = speed; + math::clamp(_movement_pitch, -1.0f, 1.0f); +} - if (!core::localcontrol()) { - set_mode(Overview); - return; - } +void Camera::cycle_mode_next() +{ - switch (camera_mode) { + switch (mode()) { case Free: - // switch camera to Track mode set_mode(Track); - //con_print << "view: track" << std::endl; - //core::application()->notify_message(core::Message::Info, std::string("view: track")); break; case Track: - // switch camera to Cockpit mode set_mode(Cockpit); - //con_print << "view: cockpit" << std::endl; - //core::application()->notify_message(core::Message::Info, std::string("view: cockpit")); break; case Cockpit: - // switch camera to Free mode set_mode(Free); - //con_print << "view: free" << std::endl; - //core::application()->notify_message(core::Message::Info, std::string("view: free")); break; default: @@ -159,34 +94,19 @@ void Camera::view_next() } } -void Camera::view_previous() +void Camera::cycle_mode_previous() { - - if (!core::localcontrol()) { - set_mode(Overview); - return; - } - - switch (camera_mode) { + switch (mode()) { case Cockpit: - // switch camera to Track mode set_mode(Track); - //con_print << "view: track" << std::endl; - //core::application()->notify_message(core::Message::Info, std::string("view: track")); break; case Free: - // switch camera to Cockpit mode set_mode(Cockpit); - //con_print << "view: cockpit" << std::endl; - //core::application()->notify_message(core::Message::Info, std::string("view: cockpit")); break; case Track: - // switch camera to Free mode set_mode(Free); - //con_print << "view: free" << std::endl; - //core::application()->notify_message(core::Message::Info, std::string("view: free")); break; default: @@ -194,180 +114,182 @@ void Camera::view_previous() } } -void Camera::set_zoom(float zoom) +void Camera::reset() { - camera_zoom += zoom; - math::clamp(camera_zoom, 1.0f, 10.0f); -} + if (target()) + { + _target_location.assign(target()->location()); + _target_axis.assign(target()->axis()); + _distance = 0.0f; + } + else + { + _location.clear(); + _target_axis.clear(); + _distance = 0.0f; + } + _axis.assign(_target_axis); + if (mode() == Free) + { + _target_axis.clear(); + } + + _freelook_direction = 0.0f; + _freelook_pitch = 0.0f; -void Camera::frame(float seconds) + _movement_direction = 0.0f; + _movement_pitch = 0.0f; +} +void Camera::set_target(const core::Entity *entity) { - math::Axis target_axis; - float d = 0; - - if (core::localplayer()->view()) { - if (camera_mode != Overview) { - set_mode(Overview); - } - } else if (core::localcontrol()) { - if (camera_mode == Overview) { - set_mode(camera_previous_mode); - } - } else { - if (camera_mode != Overview) { - set_mode(Overview); - } - } + _target_entity = entity; +} - if (mode() == Overview) { - camera_eye.clear(); - - if (core::localplayer()->view()) { - // player view entity - - camera_scene_axis.assign(core::localplayer()->view()->axis()); - if (core::localplayer()->view() == core::localcontrol()) { - camera_scene_axis.change_pitch(pitch_free); - camera_target.assign(core::localplayer()->view()->location()); - distance = math::max(core::localplayer()->view()->radius(), 1.0f) * 2.0f; - } else { - distance = math::max(core::localplayer()->view()->radius(), 1.0f) * 3.0f; - camera_scene_axis.change_direction(180.0f); - camera_target.assign(core::localplayer()->view()->location() - core::localplayer()->view()->axis().left()*(math::max(core::localplayer()->view()->radius(), 1.0f)*0.5f)); +void Camera::frame(const float elapsed) +{ + const float ROTATESPEED = 25.0f * elapsed; + switch(mode()) + { + case Track: + { + math::Axis desired_axis; + + // 3rd person view + if (target()) + { + _target_location.assign(target()->location()); + + if (target()->model()) + { + const float modelscale = target()->radius() / target()->model()->radius(); + _target_location += target()->axis().up() * target()->model()->box().max().z() * modelscale; + } + else + { + _target_location += target()->axis().up() * target()->radius(); + } + desired_axis.assign(target()->axis()); + _distance = target()->radius() * _multiplier * 2.0f; } - - /* - } else if (core::localplayer()->zone()->default_view()) { - // default zone view entity - camera_target.assign(core::localplayer()->zone()->default_view()->location()); - camera_scene_axis.assign(core::localplayer()->zone()->default_view()->axis()); - camera_scene_axis.change_direction(180.0f); - distance = math::max(core::localplayer()->zone()->default_view()->radius(), 1.0f) * 2.0f; - */ - } else { - // default location (0,0,0) - camera_target.clear(); - camera_scene_axis.clear(); - pitch_current = pitch_overview; - camera_scene_axis.change_pitch(pitch_current); - distance = 8.0f; - } - } else { - - camera_target.assign(core::localcontrol()->location()); - target_axis.assign(core::localcontrol()->axis()); - distance = core::localcontrol()->radius(); - - if (mode() == Track) { - - float cosangle; // cosine of an angle - float angle; // angle in radians - math::Vector3f n; // normal of a plane + else + { + _target_location.assign(0.0f, 0.0f, 1.0f); + _distance = _multiplier * 2.0f; + } + // FIXME Bad solution below - // freelook target - target_axis.change_direction(90 * target_direction); - target_axis.change_pitch(90 * target_pitch); - - // rotate scene axis towards target axis - n.assign(math::crossproduct(camera_scene_axis.forward(), target_axis.forward())); - if (!(n.length() < MIN_DELTA)) { + math::Vector3f n (math::crossproduct(_target_axis.forward(), desired_axis.forward())); + float l = n.length(); + float d = math::dotproduct(_target_axis.forward(), desired_axis.forward()); + float a = (d > 0.0f ? 1.0f - d : 1.0f); + if ((a > MIN_DELTA) && (l > MIN_DELTA)) + { n.normalize(); - cosangle = math::dotproduct(camera_scene_axis.forward(), target_axis.forward()); - angle = acos(cosangle) * seconds; // * 180.0f / M_PI; - if (angle > MIN_DELTA) - camera_scene_axis.rotate(n, -angle); + _target_axis.rotate(n, -ROTATESPEED * a); + } - - n.assign(math::crossproduct(camera_scene_axis.left(), target_axis.left())); - if (!(n.length() < MIN_DELTA)) { + + n.assign (math::crossproduct(_target_axis.up(), desired_axis.up())); + l = n.length(); + d = math::dotproduct(_target_axis.up(), desired_axis.up()); + a = (d > 0.0f ? 1.0f - d : 1.0f); + if ((a > MIN_DELTA) && (l > MIN_DELTA)) + { n.normalize(); - cosangle = math::dotproduct(camera_scene_axis.left(), target_axis.left()); - angle = acos(cosangle) * seconds; // * 180.0f / M_PI; - if (angle > MIN_DELTA) - camera_scene_axis.rotate(n, -angle); + _target_axis.rotate(n, -ROTATESPEED * a); + } - n.assign(math::crossproduct(camera_scene_axis.up(), target_axis.up())); - if (!(n.length() < MIN_DELTA)) { - n.normalize(); - cosangle = math::dotproduct(camera_scene_axis.up(), target_axis.up()); - angle = acos(cosangle) * seconds; // * 180.0f / M_PI; - if (angle > MIN_DELTA) - camera_scene_axis.rotate(n, -angle); + _axis.assign(_target_axis); + _axis.change_direction(_freelook_direction); + _axis.change_pitch(_freelook_pitch); + break; + } + + case Cockpit: + { + // 1st person view + if (target()) + { + _target_location.assign(target()->location()); + _target_axis.assign(target()->axis()); + _distance = 0.0f; } - - if (core::localcontrol()->model() && core::localcontrol()->model()->radius()) { - const float modelscale = core::localcontrol()->radius() / core::localcontrol()->model()->radius(); + else + { + _target_location.clear(); + _target_axis.clear(); + _distance = 0.0f; + } + + _axis.assign(_target_axis); + _axis.change_direction(_freelook_direction); + _axis.change_pitch(_freelook_pitch); + break; + } + case Free: + { + // look at self + if (target()) + { + _target_location.assign(target()->location()); + _axis.assign(target()->axis()); - camera_target -= camera_scene_axis.forward() * math::max(FRUSTUMFRONT / WORLDSCALE, core::localcontrol()->model()->box().max().x() * modelscale); - camera_target += camera_scene_axis.up() * math::max(FRUSTUMFRONT / WORLDSCALE, core::localcontrol()->model()->box().max().z() * modelscale); - } else { - camera_target -= camera_scene_axis.forward() * math::max(FRUSTUMFRONT / WORLDSCALE, FRUSTUMFRONT / WORLDSCALE + core::localcontrol()->radius()); - camera_target += camera_scene_axis.up() * math::max(FRUSTUMFRONT / WORLDSCALE, FRUSTUMFRONT / WORLDSCALE + core::localcontrol()->radius()); + _distance = target()->radius() * _multiplier * 2.0f; + } + else + { + _target_location.clear(); + _axis.clear(); + + _distance = _multiplier * 2.0f; } - distance = math::max(FRUSTUMFRONT / WORLDSCALE, FRUSTUMFRONT / WORLDSCALE + camera_zoom * core::localcontrol()->radius()) + 0.001f; - - } else if (mode() == Free) { - - camera_scene_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_scene_axis.change_direction(direction_current); - - // adjust pitch - d = degrees180f(pitch_current - pitch_target); - pitch_current = degrees360f(pitch_current - d * seconds); - camera_scene_axis.change_pitch(pitch_current); - - distance = math::max(FRUSTUMFRONT / WORLDSCALE, FRUSTUMFRONT / WORLDSCALE + camera_zoom * core::localcontrol()->radius()) + 0.001f; - - } else if (mode() == Cockpit) { + _target_axis.rotate(math::Vector3f(0.0f, 0.0f, 1.0f), -M_PI * _movement_direction * elapsed); + _target_axis.change_pitch(180.0f * _movement_pitch * elapsed); - camera_scene_axis.assign(target_axis); - - direction_target = + 90 * target_direction; - pitch_target = + 90 * target_pitch; - - // adjust direction - d = degrees180f(direction_current - direction_target); - direction_current = degrees360f(direction_current - d * seconds); - camera_scene_axis.change_direction(direction_current); - - // adjust pitch - d = degrees180f(pitch_current - pitch_target); - pitch_current = degrees360f(pitch_current - d * seconds); - camera_scene_axis.change_pitch(pitch_current); - - if (core::localcontrol()->model()) { - const float modelscale = core::localcontrol()->radius() / core::localcontrol()->model()->radius(); - camera_target += (core::localcontrol()->model()->box().max().x() * modelscale) * - core::localcontrol()->axis().forward(); - } else { - camera_target += (core::localcontrol()->radius()) * - core::localcontrol()->axis().forward(); + _axis.assign(_axis * _target_axis); + _axis.change_direction(_freelook_direction); + _axis.change_pitch(_freelook_pitch); + break; + } + case Overview: + { + if (target()) + { + _target_location.assign(target()->location()); + _target_axis.assign(target()->axis()); + _distance = 2.0f * target()->radius() * _multiplier; + + _target_axis.change_direction(180.0f); + + // default pitch angle + _target_axis.change_pitch(-5.0f); + } + else + { + _target_location.clear(); + _target_axis.clear(); + + _distance = 2.0f * _multiplier; } - distance = (FRUSTUMFRONT / WORLDSCALE) - 0.001f; + + _axis.assign(_target_axis); + break; } } - - // calculate eye position - camera_eye = camera_target - (distance * camera_scene_axis.forward()); - camera_axis.assign(camera_scene_axis); + + _distance += FRUSTUMFRONT / WORLDSCALE; + _location.assign(_target_location - _axis.forward() * _distance); } -void Camera::frustum() +void Camera::draw() { // Change to the projection matrix and set our viewing volume large enough for the skysphere gl::matrixmode(GL_PROJECTION); gl::loadidentity(); - gl::frustum(-FRUSTUMSIZE, FRUSTUMSIZE, -FRUSTUMSIZE / State::aspect(), FRUSTUMSIZE / State::aspect(), FRUSTUMFRONT, core::range::maxdistance * WORLDSCALE); + gl::frustum(-FRUSTUMSIZE, FRUSTUMSIZE, -FRUSTUMSIZE / State::aspect(), FRUSTUMSIZE / State::aspect(), FRUSTUMFRONT, FARPLANE); gl::matrixmode(GL_MODELVIEW); gl::loadidentity(); @@ -376,25 +298,25 @@ void Camera::frustum() gl::rotate(-90.0f, 1.0f , 0.0f, 0.0f); // apply the transpose of the axis transformation (the axis is orhtonormal) - math::Matrix4f matrix; - matrix.assign(camera_scene_axis); + math::Matrix4f matrix(_axis); gl::multmatrix(matrix.transpose()); - gl::scale(4.0f, 4.0f, 4.0f); - - gl::translate(-1.0f * camera_eye); + // apply world scale + gl::scale(WORLDSCALE, WORLDSCALE, WORLDSCALE); + // apply camera eye translation + gl::translate(-1.0f * _location); } -void Camera::frustum_default(float distance, float cx, float cy) +void Camera::draw(const float center_x, const float center_y) { // Change to the projection matrix and set our viewing volume large enough for the skysphere gl::matrixmode(GL_PROJECTION); gl::loadidentity(); - // move eye to (cx, cy) + // move projection center to (cx, cy) // note: the factor 2.0f probably has to be 1.0f/frustum_size - gl::translate(2.0f*(-State::width() * 0.5f + cx) / State::width() , 2.0f*(State::height() * 0.5f - cy) / State::height(), 0.0f); + gl::translate(2.0f*(-State::width() * 0.5f + center_x) / State::width() , 2.0f * (State::height() * 0.5f - center_y) / State::height(), 0.0f); gl::frustum(-FRUSTUMSIZE, FRUSTUMSIZE, -FRUSTUMSIZE / State::aspect(), FRUSTUMSIZE / State::aspect(), FRUSTUMFRONT, 1023.0f); @@ -404,10 +326,13 @@ void Camera::frustum_default(float distance, float cx, float cy) // map world coordinates to opengl coordinates gl::rotate(90.0f, 0.0f, 1.0f, 0.0f); gl::rotate(-90.0f, 1.0f , 0.0f, 0.0f); + + // apply the transpose of the axis transformation (the axis is orhtonormal) + math::Matrix4f matrix(_axis); + gl::multmatrix(matrix.transpose()); - gl::translate(distance + 1.0f, 0.0f, 0.0f); - camera_eye.assign(-distance - 1.0f, 0.0f, 0.0f); - camera_axis.clear(); + // apply camera eye translation + gl::translate(-1.0f * _location); } void Camera::ortho() @@ -420,22 +345,7 @@ void Camera::ortho() gl::matrixmode(GL_MODELVIEW); gl::loadidentity(); } + -void Camera::set_direction(float direction) -{ - target_direction = direction; - math::clamp(target_direction, -1.0f, 1.0f); -} +} // namespace render -void Camera::set_pitch(float pitch) -{ - target_pitch = pitch; - math::clamp(target_pitch, -1.0f, 1.0f); -} - -void Camera::reset() -{ - set_mode(camera_mode); -} - -} |