Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Renderer: Added camera boundaries to CameraManager and clamping #1682. #1691

Merged
merged 11 commits into from
Nov 7, 2024
10 changes: 10 additions & 0 deletions libopenage/presenter/presenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,17 @@ void Presenter::init_graphics(bool debug) {
this->camera->resize(w, h);
});

// Camera manager
this->camera_manager = std::make_shared<renderer::camera::CameraManager>(this->camera);
// TODO: Make boundaries dynamic based on map size.
this->camera_manager->set_camera_boundaries(
renderer::camera::CameraBoundaries{
renderer::camera::X_BOUND_MIN,
renderer::camera::X_BOUND_MAX,
renderer::camera::Y_BOUND_MIN,
renderer::camera::Y_BOUND_MAX,
renderer::camera::Z_BOUND_MIN,
renderer::camera::Z_BOUND_MAX});

// Skybox
this->skybox_renderer = std::make_shared<renderer::skybox::SkyboxRenderStage>(
Expand Down
1 change: 1 addition & 0 deletions libopenage/renderer/camera/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_sources(libopenage
boundaries.cpp
camera.cpp
definitions.cpp
frustum_2d.cpp
Expand Down
9 changes: 9 additions & 0 deletions libopenage/renderer/camera/boundaries.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2024-2024 the openage authors. See copying.md for legal info.

#include "boundaries.h"


namespace openage::renderer::camera {


} // namespace openage::renderer::camera
25 changes: 25 additions & 0 deletions libopenage/renderer/camera/boundaries.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2024-2024 the openage authors. See copying.md for legal info.

#pragma once

namespace openage::renderer::camera {

/**
* Defines boundaries for the camera's view.
*/
struct CameraBoundaries {
/// The minimum boundary for the camera's X-coordinate.
float x_min;
/// The maximum boundary for the camera's X-coordinate.
float x_max;
/// The minimum boundary for the camera's Y-coordinate.
float y_min;
/// The maximum boundary for the camera's Y-coordinate.
float y_max;
/// The minimum boundary for the camera's Z-coordinate.
float z_min;
/// The maximum boundary for the camera's Z-coordinate.
float z_max;
};

} // namespace openage::renderer::camera
78 changes: 42 additions & 36 deletions libopenage/renderer/camera/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,38 +61,7 @@ Camera::Camera(const std::shared_ptr<Renderer> &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);
}

Expand All @@ -102,15 +71,17 @@ void Camera::look_at_coord(coord::scene3 coord_pos) {
this->look_at_scene(scene_pos);
}

void Camera::move_to(Eigen::Vector3f scene_pos) {
// TODO: Check and set bounds for where the camera can go and check them here
void Camera::move_to(Eigen::Vector3f scene_pos, const CameraBoundaries &camera_boundaries) {
scene_pos[0] = std::clamp(scene_pos[0], camera_boundaries.x_min, camera_boundaries.x_max);
scene_pos[1] = std::clamp(scene_pos[1], camera_boundaries.y_min, camera_boundaries.y_max);
scene_pos[2] = std::clamp(scene_pos[2], camera_boundaries.z_min, camera_boundaries.z_max);

this->scene_pos = scene_pos;
this->moved = true;
}

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, const CameraBoundaries &camera_boundaries) {
this->move_to(this->scene_pos + (direction * delta), camera_boundaries);
}

void Camera::set_zoom(float zoom) {
Expand Down Expand Up @@ -292,8 +263,43 @@ void Camera::init_uniform_buffer(const std::shared_ptr<Renderer> &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;
}


EdvinLndh marked this conversation as resolved.
Show resolved Hide resolved
} // namespace openage::renderer::camera
20 changes: 17 additions & 3 deletions libopenage/renderer/camera/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@

#include <cmath>
#include <cstddef>
#include <limits>
#include <memory>
#include <optional>

#include <eigen3/Eigen/Dense>

#include "coord/pixel.h"
#include "coord/scene.h"
#include "util/vector.h"

#include "renderer/camera/boundaries.h"
#include "renderer/camera/definitions.h"
#include "renderer/camera/frustum_2d.h"
#include "renderer/camera/frustum_3d.h"
Expand Down Expand Up @@ -83,18 +86,21 @@ class Camera {
* Move the camera position in the direction of a given vector.
*
* @param scene_pos New 3D position of the camera in the scene.
* @param camera_boundaries 3D boundaries for the camera.
*/
void move_to(Eigen::Vector3f scene_pos);
void move_to(Eigen::Vector3f scene_pos, const CameraBoundaries &camera_boundaries = DEFAULT_CAM_BOUNDARIES);

/**
* Move the camera position in the direction of a given vector.
* 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 3D boundaries for the camera.
*/
void move_rel(Eigen::Vector3f direction, float delta = 1.0f);
void move_rel(Eigen::Vector3f direction, float delta = 1.0f, const CameraBoundaries &camera_boundaries = DEFAULT_CAM_BOUNDARIES);

/**
* Set the zoom level of the camera. Values smaller than 1.0f let the
Expand Down Expand Up @@ -200,6 +206,7 @@ class Camera {
*/
const Frustum3d get_frustum_3d() const;


EdvinLndh marked this conversation as resolved.
Show resolved Hide resolved
private:
/**
* Create the uniform buffer for the camera.
Expand All @@ -208,6 +215,13 @@ class Camera {
*/
void init_uniform_buffer(const std::shared_ptr<Renderer> &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.
*
Expand Down
21 changes: 21 additions & 0 deletions libopenage/renderer/camera/definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
#pragma once

#include <eigen3/Eigen/Dense>
#include <limits>

#include "renderer/camera/boundaries.h"

namespace openage::renderer::camera {

Expand Down Expand Up @@ -58,4 +60,23 @@ static constexpr float DEFAULT_MAX_ZOOM_OUT = 64.0f;
*/
static constexpr float DEFAULT_ZOOM_RATIO = 1.0f / 49;

static constexpr CameraBoundaries DEFAULT_CAM_BOUNDARIES{
std::numeric_limits<float>::lowest(),
std::numeric_limits<float>::max(),
std::numeric_limits<float>::lowest(),
std::numeric_limits<float>::max(),
std::numeric_limits<float>::lowest(),
std::numeric_limits<float>::max()};

/**
* Constant values for the camera bounds (based on current fix terrain grid of 20x20).
* TODO: Make boundaries dynamic based on map size.
*/
static constexpr float X_BOUND_MIN = 12.25f;
static constexpr float X_BOUND_MAX = 32.25f;
static constexpr float Y_BOUND_MIN = 0.0f;
static constexpr float Y_BOUND_MAX = 20.0f;
static constexpr float Z_BOUND_MIN = -8.25f;
static constexpr float Z_BOUND_MAX = 12.25f;

} // namespace openage::renderer::camera
12 changes: 12 additions & 0 deletions libopenage/renderer/demo/demo_3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ void renderer_demo_3(const util::Path &path) {
// it is updated each frame before the render stages
auto cam_manager = std::make_shared<renderer::camera::CameraManager>(camera);

// Set boundaries for camera movement in the scene
// this restricts camera movement to the area defined by the boundaries
// i.e. the map terrain in this case
cam_manager->set_camera_boundaries(
camera::CameraBoundaries{
12.25f,
22.25f,
0.0f,
20.0f,
2.25f,
12.25f});

// Render stages
// every stage use a different subrenderer that manages renderables,
// shaders, textures & more.
Expand Down
22 changes: 14 additions & 8 deletions libopenage/renderer/stages/camera/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
#include "manager.h"

#include <eigen3/Eigen/Dense>
#include <numbers>

#include "renderer/camera/camera.h"
#include "renderer/uniform_buffer.h"
#include "renderer/uniform_input.h"

namespace openage::renderer::camera {

CameraManager::CameraManager(const std::shared_ptr<renderer::camera::Camera> &camera) :
CameraManager::CameraManager(const std::shared_ptr<renderer::camera::Camera> &camera,
const CameraBoundaries &camera_boundaries) :
camera{camera},
move_motion_directions{static_cast<int>(MoveDirection::NONE)},
zoom_motion_direction{static_cast<int>(ZoomDirection::NONE)},
move_motion_speed{0.2f},
zoom_motion_speed{0.05f} {
zoom_motion_speed{0.05f},
camera_boundaries{camera_boundaries} {
this->uniforms = this->camera->get_uniform_buffer()->new_uniform_input(
"view",
camera->get_view_matrix(),
Expand All @@ -33,18 +35,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:
Expand All @@ -66,6 +68,10 @@ void CameraManager::zoom_frame(ZoomDirection direction, float speed) {
}
}

void CameraManager::set_camera_boundaries(const CameraBoundaries &camera_boundaries) {
this->camera_boundaries = camera_boundaries;
}

void CameraManager::update_motion() {
if (this->move_motion_directions != static_cast<int>(MoveDirection::NONE)) {
Eigen::Vector3f move_dir{0.0f, 0.0f, 0.0f};
Expand All @@ -83,7 +89,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<int>(ZoomDirection::NONE)) {
Expand Down
18 changes: 17 additions & 1 deletion libopenage/renderer/stages/camera/manager.h
heinezen marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
#pragma once

#include <memory>
#include <utility>

#include "renderer/camera/camera.h"

namespace openage::renderer {
class UniformBufferInput;
Expand Down Expand Up @@ -47,8 +49,10 @@ class CameraManager {
* Create a new camera manager.
*
* @param camera Camera to manage.
* @param camera_boundaries Boundaries for the camera movement in the scene.
*/
CameraManager(const std::shared_ptr<renderer::camera::Camera> &camera);
CameraManager(const std::shared_ptr<renderer::camera::Camera> &camera,
const CameraBoundaries &camera_boundaries = DEFAULT_CAM_BOUNDARIES);
~CameraManager() = default;

/**
Expand Down Expand Up @@ -103,6 +107,13 @@ class CameraManager {
*/
void set_zoom_motion_speed(float speed);

/**
* Set boundaries for camera movement in the scene.
*
* @param camera_boundaries XYZ boundaries for the camera movement.
*/
void set_camera_boundaries(const CameraBoundaries &camera_boundaries);

private:
/**
* Update the camera parameters.
Expand Down Expand Up @@ -143,6 +154,11 @@ class CameraManager {
* Uniform buffer input for the camera.
*/
std::shared_ptr<renderer::UniformBufferInput> uniforms;

/**
* Camera boundaries for X and Z movement. Contains minimum and maximum values for each axes.
*/
CameraBoundaries camera_boundaries;
};

} // namespace camera
Expand Down
Loading