/* 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 #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 "sys/sys.h" using math::degrees360f; using math::degrees180f; 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; 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() { 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_eye.clear(); camera_target.clear(); } void Camera::shutdown() { } 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(); 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_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; default: break; } } void Camera::view_next() { if (!core::localcontrol()) { set_mode(Overview); return; } switch (camera_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: break; } } void Camera::view_previous() { if (!core::localcontrol()) { set_mode(Overview); return; } switch (camera_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: break; } } void Camera::set_zoom(float zoom) { camera_zoom += zoom; math::clamp(camera_zoom, 1.0f, 10.0f); } void Camera::frame(float seconds) { 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); } } if (mode() == Overview) { camera_eye.clear(); if (core::localplayer()->view()) { // player view entity camera_axis.assign(core::localplayer()->view()->axis()); if (core::localplayer()->view() == core::localcontrol()) { camera_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_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)); } /* } else if (core::localplayer()->zone()->default_view()) { // default zone view entity camera_target.assign(core::localplayer()->zone()->default_view()->location()); camera_axis.assign(core::localplayer()->zone()->default_view()->axis()); camera_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_axis.clear(); pitch_current = pitch_overview; camera_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 n.assign(math::crossproduct(camera_axis.forward(), target_axis.forward())); if (!(n.length() < MIN_DELTA)) { n.normalize(); cosangle = math::dotproduct(camera_axis.forward(), target_axis.forward()); angle = acos(cosangle) * seconds; // * 180.0f / M_PI; if (angle > MIN_DELTA) camera_axis.rotate(n, -angle); } n.assign(math::crossproduct(camera_axis.left(), target_axis.left())); if (!(n.length() < MIN_DELTA)) { n.normalize(); cosangle = math::dotproduct(camera_axis.left(), target_axis.left()); angle = acos(cosangle) * seconds; // * 180.0f / M_PI; if (angle > MIN_DELTA) camera_axis.rotate(n, -angle); } n.assign(math::crossproduct(camera_axis.up(), target_axis.up())); if (!(n.length() < MIN_DELTA)) { n.normalize(); cosangle = math::dotproduct(camera_axis.up(), target_axis.up()); angle = acos(cosangle) * seconds; // * 180.0f / M_PI; if (angle > MIN_DELTA) camera_axis.rotate(n, -angle); } if (core::localcontrol()->model()) { camera_target -= camera_axis.forward() * math::max(FRUSTUMFRONT / WORLDSCALE, core::localcontrol()->model()->maxbbox().x()); camera_target += camera_axis.up() * math::max(FRUSTUMFRONT / WORLDSCALE, core::localcontrol()->model()->maxbbox().z() * 2.0f); } else { camera_target -= camera_axis.forward() * math::max(FRUSTUMFRONT / WORLDSCALE, FRUSTUMFRONT / WORLDSCALE + core::localcontrol()->radius()); camera_target += camera_axis.up() * math::max(FRUSTUMFRONT / WORLDSCALE, FRUSTUMFRONT / WORLDSCALE + core::localcontrol()->radius()); } distance = math::max(FRUSTUMFRONT / WORLDSCALE, FRUSTUMFRONT / WORLDSCALE + camera_zoom * core::localcontrol()->radius()) + 0.001f; } 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); distance = math::max(FRUSTUMFRONT / WORLDSCALE, FRUSTUMFRONT / WORLDSCALE + camera_zoom * core::localcontrol()->radius()) + 0.001f; } else if (mode() == Cockpit) { camera_axis.assign(target_axis); if (core::localcontrol()->model()) { camera_target += (core::localcontrol()->model()->maxbbox().x()) * core::localcontrol()->axis().forward(); } else { camera_target += (core::localcontrol()->radius()) * core::localcontrol()->axis().forward(); } distance = (FRUSTUMFRONT / WORLDSCALE) - 0.001f; } } // calculate eye position camera_eye = camera_target - (distance * camera_axis.forward()); } void Camera::frustum() { // 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::matrixmode(GL_MODELVIEW); gl::loadidentity(); // 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; matrix.assign(camera_axis); gl::multmatrix(matrix.transpose()); gl::scale(4.0f, 4.0f, 4.0f); gl::translate(-1.0f * camera_eye); } void Camera::frustum_default(float distance, float cx, float cy) { // 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) // 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::frustum(-FRUSTUMSIZE, FRUSTUMSIZE, -FRUSTUMSIZE / State::aspect(), FRUSTUMSIZE / State::aspect(), FRUSTUMFRONT, 1023.0f); gl::matrixmode(GL_MODELVIEW); gl::loadidentity(); // 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); gl::translate(distance + 1.0f, 0.0f, 0.0f); // extra model rotation gl::rotate(-core::application()->time() / 8.0f *360.0f , 0.0f, 0.0f, 1.0f); } void Camera::ortho() { // switch to orthographic projection gl::matrixmode(GL_PROJECTION); gl::loadidentity(); glOrtho(0, State::width(), State::height(), 0, -16.0f, 16.0f); gl::matrixmode(GL_MODELVIEW); gl::loadidentity(); } 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); } }