diff --git a/libopenage/renderer/camera/camera.cpp b/libopenage/renderer/camera/camera.cpp index f70efbee64..ec5a926594 100644 --- a/libopenage/renderer/camera/camera.cpp +++ b/libopenage/renderer/camera/camera.cpp @@ -61,38 +61,7 @@ Camera::Camera(const std::shared_ptr &renderer, } void Camera::look_at_scene(Eigen::Vector3f scene_pos) { - if (scene_pos[1] > this->scene_pos[1]) { - // TODO: camera can't look at a position that's - // higher than it's own position - } - - // TODO: Although the below method should be faster, calculating and adding the direction - // vector from scene_pos to new_pos may be easier to understand - // i.e. new_pos = scene_pos + b/sin(30) * direction_vec - - // due to the fixed angle, the centered scene position - // and the new camera position form a right triangle. - // - // c - + new camera pos - // - |b - // center +------+ - // a - // - // we can calculate the new camera position via the offset a - // using the angle and length of side b. - auto y_delta = this->scene_pos[1] - scene_pos[1]; // b (vertical distance) - auto xz_distance = y_delta * std::numbers::sqrt3; // a (horizontal distance); a = b * (cos(30°) / sin(30°)) - - // get x and z offsets - // the camera is pointed diagonally to the negative x and z axis - // a is the length of the diagonal from camera.xz to scene_pos.xz - // so the x and z offest are sides of a square with the same diagonal - auto side_length = xz_distance / std::numbers::sqrt2; - auto new_pos = Eigen::Vector3f( - scene_pos[0] + side_length, - this->scene_pos[1], // height unchanged - scene_pos[2] + side_length); - + auto new_pos = calc_look_at(scene_pos); this->move_to(new_pos); } @@ -103,8 +72,6 @@ void Camera::look_at_coord(coord::scene3 coord_pos) { } void Camera::move_to(Eigen::Vector3f scene_pos) { - // TODO: Check and set bounds for where the camera can go and check them here - this->scene_pos = scene_pos; this->moved = true; } @@ -113,6 +80,15 @@ void Camera::move_rel(Eigen::Vector3f direction, float delta) { this->move_to(this->scene_pos + (direction * delta)); } +void Camera::move_rel(Eigen::Vector3f direction, float delta, CameraBoundaries camera_boundaries) { + auto new_pos = calc_look_at(this->scene_pos + (direction * delta)); + + new_pos[0] = std::clamp(new_pos[0], camera_boundaries.x_min, camera_boundaries.x_max); + new_pos[2] = std::clamp(new_pos[2], camera_boundaries.z_min, camera_boundaries.z_max); + + this->move_to(new_pos); +} + void Camera::set_zoom(float zoom) { if (zoom < Camera::MAX_ZOOM_IN) { zoom = Camera::MAX_ZOOM_IN; @@ -292,8 +268,43 @@ void Camera::init_uniform_buffer(const std::shared_ptr &renderer) { this->uniform_buffer = renderer->add_uniform_buffer(ubo_info); } +Eigen::Vector3f Camera::calc_look_at(Eigen::Vector3f target) { + if (target[1] > this->scene_pos[1]) { + // TODO: camera can't look at a position that's + // higher than it's own position + } + + // TODO: Although the below method should be faster, calculating and adding the direction + // vector from scene_pos to new_pos may be easier to understand + // i.e. new_pos = scene_pos + b/sin(30) * direction_vec + + // due to the fixed angle, the centered scene position + // and the new camera position form a right triangle. + // + // c - + new camera pos + // - |b + // center +------+ + // a + // + // we can calculate the new camera position via the offset a + // using the angle and length of side b. + auto y_delta = this->scene_pos[1] - target[1]; // b (vertical distance) + auto xz_distance = y_delta * std::numbers::sqrt3; // a (horizontal distance); a = b * (cos(30°) / sin(30°)) + + // get x and z offsets + // the camera is pointed diagonally to the negative x and z axis + // a is the length of the diagonal from camera.xz to scene_pos.xz + // so the x and z offest are sides of a square with the same diagonal + auto side_length = xz_distance / std::numbers::sqrt2; + return Eigen::Vector3f( + target[0] + side_length, + this->scene_pos[1], // height unchanged + target[2] + side_length); +} + inline float Camera::get_real_zoom_factor() const { return 0.5f * this->default_zoom_ratio * this->zoom; } + } // namespace openage::renderer::camera diff --git a/libopenage/renderer/camera/camera.h b/libopenage/renderer/camera/camera.h index eccb5b40e6..132ee4ed81 100644 --- a/libopenage/renderer/camera/camera.h +++ b/libopenage/renderer/camera/camera.h @@ -4,7 +4,9 @@ #include #include +#include #include +#include #include @@ -23,6 +25,13 @@ class UniformBuffer; namespace camera { +/** + * Defines constant boundaries for the camera's view in the X and Z axes. + */ +struct CameraBoundaries { + float x_min, x_max, z_min, z_max; +}; + /** * Camera for selecting what part of the ingame world is displayed. * @@ -86,6 +95,7 @@ class Camera { */ void move_to(Eigen::Vector3f scene_pos); + /** * Move the camera position in the direction of a given vector. * @@ -96,6 +106,19 @@ class Camera { */ void move_rel(Eigen::Vector3f direction, float delta = 1.0f); + + /** + * Move the camera position in the direction of a given vector taking the + * camera boundaries into account. + * + * @param direction Direction vector. Added to the current position. + * @param delta Delta for controlling the amount by which the camera is moved. The + * value is multiplied with the directional vector before its applied to + * the positional vector. + * @param camera_boundaries X and Z boundaries for the camera in the scene. + */ + void move_rel(Eigen::Vector3f direction, float delta, CameraBoundaries camera_boundaries); + /** * Set the zoom level of the camera. Values smaller than 1.0f let the * camera zoom in, values greater than 1.0f let the camera zoom out. @@ -200,6 +223,7 @@ class Camera { */ const Frustum3d get_frustum_3d() const; + private: /** * Create the uniform buffer for the camera. @@ -208,6 +232,13 @@ class Camera { */ void init_uniform_buffer(const std::shared_ptr &renderer); + /** + * Calculates the camera's position needed to center its view on the given target. + * + * @param target The target position in the 3D scene the camera should focus on. + */ + Eigen::Vector3f calc_look_at(Eigen::Vector3f target); + /** * Get the zoom factor applied to the camera projection. * diff --git a/libopenage/renderer/camera/definitions.h b/libopenage/renderer/camera/definitions.h index 9586239a2f..1a35c7fe5e 100644 --- a/libopenage/renderer/camera/definitions.h +++ b/libopenage/renderer/camera/definitions.h @@ -58,4 +58,10 @@ static constexpr float DEFAULT_MAX_ZOOM_OUT = 64.0f; */ static constexpr float DEFAULT_ZOOM_RATIO = 1.0f / 49; +/** + * Constant values for the camera bounds. + * TODO: Make boundaries dynamic based on map size. + */ +static const float X_MIN = 12.25f, X_MAX = 32.25f, Z_MIN = -8.25f, Z_MAX = 12.25f; + } // namespace openage::renderer::camera diff --git a/libopenage/renderer/demo/demo_6.h b/libopenage/renderer/demo/demo_6.h index f86728819b..279276122a 100644 --- a/libopenage/renderer/demo/demo_6.h +++ b/libopenage/renderer/demo/demo_6.h @@ -19,6 +19,7 @@ class Texture2d; namespace camera { class Camera; +class CameraManager; } namespace gui { @@ -72,6 +73,9 @@ class RenderManagerDemo6 { /// Camera std::shared_ptr camera; + /// Camera manager + std::shared_ptr camera_manager; + /// Render passes std::shared_ptr obj_2d_pass; std::shared_ptr obj_3d_pass; diff --git a/libopenage/renderer/stages/camera/manager.cpp b/libopenage/renderer/stages/camera/manager.cpp index 39eddd2b52..0574dcc47e 100644 --- a/libopenage/renderer/stages/camera/manager.cpp +++ b/libopenage/renderer/stages/camera/manager.cpp @@ -3,8 +3,8 @@ #include "manager.h" #include +#include -#include "renderer/camera/camera.h" #include "renderer/uniform_buffer.h" #include "renderer/uniform_input.h" @@ -15,7 +15,8 @@ CameraManager::CameraManager(const std::shared_ptr &ca move_motion_directions{static_cast(MoveDirection::NONE)}, zoom_motion_direction{static_cast(ZoomDirection::NONE)}, move_motion_speed{0.2f}, - zoom_motion_speed{0.05f} { + zoom_motion_speed{0.05f}, + camera_boundaries({X_MIN, X_MAX, Z_MIN, Z_MAX}) { this->uniforms = this->camera->get_uniform_buffer()->new_uniform_input( "view", camera->get_view_matrix(), @@ -33,18 +34,18 @@ void CameraManager::move_frame(MoveDirection direction, float speed) { case MoveDirection::LEFT: // half the speed because the relationship between forward/back and // left/right is 1:2 in our ortho projection. - this->camera->move_rel(Eigen::Vector3f(-1.0f, 0.0f, 1.0f), speed / 2); + this->camera->move_rel(Eigen::Vector3f(-1.0f, 0.0f, 1.0f), speed / 2, this->camera_boundaries); break; case MoveDirection::RIGHT: // half the speed because the relationship between forward/back and // left/right is 1:2 in our ortho projection. - this->camera->move_rel(Eigen::Vector3f(1.0f, 0.0f, -1.0f), speed / 2); + this->camera->move_rel(Eigen::Vector3f(1.0f, 0.0f, -1.0f), speed / 2, this->camera_boundaries); break; case MoveDirection::FORWARD: - this->camera->move_rel(Eigen::Vector3f(-1.0f, 0.0f, -1.0f), speed); + this->camera->move_rel(Eigen::Vector3f(-1.0f, 0.0f, -1.0f), speed, this->camera_boundaries); break; case MoveDirection::BACKWARD: - this->camera->move_rel(Eigen::Vector3f(1.0f, 0.0f, 1.0f), speed); + this->camera->move_rel(Eigen::Vector3f(1.0f, 0.0f, 1.0f), speed, this->camera_boundaries); break; default: @@ -83,7 +84,7 @@ void CameraManager::update_motion() { move_dir += Eigen::Vector3f(1.0f, 0.0f, 1.0f); } - this->camera->move_rel(move_dir, this->move_motion_speed); + this->camera->move_rel(move_dir, this->move_motion_speed, this->camera_boundaries); } if (this->zoom_motion_direction != static_cast(ZoomDirection::NONE)) { diff --git a/libopenage/renderer/stages/camera/manager.h b/libopenage/renderer/stages/camera/manager.h index 13d876b25f..d5dcf07b46 100644 --- a/libopenage/renderer/stages/camera/manager.h +++ b/libopenage/renderer/stages/camera/manager.h @@ -3,7 +3,9 @@ #pragma once #include +#include +#include "renderer/camera/camera.h" namespace openage::renderer { class UniformBufferInput; @@ -11,6 +13,7 @@ class UniformBufferInput; namespace camera { class Camera; +struct CameraBoundaries; enum class MoveDirection { NONE = 0x0000, @@ -143,6 +146,12 @@ class CameraManager { * Uniform buffer input for the camera. */ std::shared_ptr uniforms; + + /** + * Camera boundaries for X and Z movement. Contains minimum and maximum values for each axes. + */ + CameraBoundaries camera_boundaries; + }; } // namespace camera