Browse Source

Improve orbit cam springing

master
C. J. Howard 1 year ago
parent
commit
37f95a087d
20 changed files with 400 additions and 595 deletions
  1. +1
    -0
      CMakeLists.txt
  2. +28
    -13
      src/animation/camera-rig.cpp
  3. +14
    -22
      src/animation/camera-rig.hpp
  4. +199
    -0
      src/animation/orbit-cam.cpp
  5. +48
    -58
      src/animation/orbit-cam.hpp
  6. +9
    -6
      src/animation/spring.hpp
  7. +16
    -17
      src/game/bootloader.cpp
  8. +2
    -1
      src/game/game-context.hpp
  9. +8
    -8
      src/game/states/play-state.cpp
  10. +28
    -125
      src/game/systems/camera-system.cpp
  11. +12
    -38
      src/game/systems/camera-system.hpp
  12. +15
    -144
      src/game/systems/control-system.cpp
  13. +0
    -3
      src/game/systems/control-system.hpp
  14. +7
    -0
      src/game/systems/render-system.cpp
  15. +2
    -0
      src/game/systems/render-system.hpp
  16. +7
    -4
      src/game/systems/tool-system.cpp
  17. +1
    -0
      src/game/systems/tool-system.hpp
  18. +2
    -2
      src/math/angles.hpp
  19. +0
    -154
      src/orbit-cam.cpp
  20. +1
    -0
      src/renderer/material-flags.hpp

+ 1
- 0
CMakeLists.txt View File

@ -14,6 +14,7 @@ find_package(SDL2 REQUIRED COMPONENTS SDL2::SDL2-static SDL2::SDL2main CONFIG)
find_package(OpenAL REQUIRED CONFIG)
find_library(physfs REQUIRED NAMES physfs-static PATHS "${CMAKE_PREFIX_PATH}/lib")
# Determine dependencies
set(STATIC_LIBS
dr_wav

src/camera-rig.cpp → src/animation/camera-rig.cpp View File

@ -17,7 +17,7 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "orbit-cam.hpp"
#include "animation/camera-rig.hpp"
#include "scene/camera.hpp"
#include "math/constants.hpp"
#include "configuration.hpp"
@ -26,8 +26,7 @@
camera_rig::camera_rig():
camera(nullptr),
translation{0.0f, 0.0f, 0.0f},
rotation(math::identity_quaternion<float>),
transform(math::identity_transform<float>),
forward(global_forward),
right(global_right),
up(global_up)
@ -38,7 +37,7 @@ void camera_rig::attach(::camera* camera)
this->camera = camera;
if (camera != nullptr)
{
camera->look_at(translation, translation + forward, up);
camera->set_transform(transform);
}
}
@ -47,17 +46,33 @@ void camera_rig::detach()
camera = nullptr;
}
void camera_rig::set_translation(const float3& translation)
void camera_rig::update_transform(const transform_type& transform)
{
this->translation = translation;
this->transform = transform;
// Calculate orthonormal basis
forward = transform.rotation * global_forward;
up = transform.rotation * global_up;
right = transform.rotation * global_right;
if (camera != nullptr)
{
camera->set_transform(transform);
}
}
void camera_rig::set_rotation(const quaternion_type& rotation)
void camera_rig::update_projection(float fov, float aspect_ratio, float clip_near, float clip_far)
{
this->rotation = rotation;
// Calculate orthonormal basis
forward = rotation * global_forward;
up = rotation * global_up;
right = rotation * global_right;
if (camera != nullptr)
{
camera->set_perspective(fov, aspect_ratio, clip_near, clip_far);
}
}
void camera_rig::update_projection(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far)
{
if (camera != nullptr)
{
camera->set_orthographic(clip_left, clip_right, clip_bottom, clip_top, clip_near, clip_far);
}
}

src/camera-rig.hpp → src/animation/camera-rig.hpp View File

@ -52,38 +52,35 @@ public:
void attach(::camera* camera);
/**
* Detaches a camera from the rig.
* Detaches the camera from the rig.
*/
void detach();
/**
* Sets the translation of the camera rig.
*/
void set_translation(const float3& translation);
/**
* Sets the rotation of the camera rig.
*/
void set_rotation(const quaternion_type& rotation);
/**
* Returns the attached camera.
*/
const ::camera* get_camera() const;
/// @copydoc camera_rig::get_camera() const
::camera* get_camera();
const float3& get_translation() const;
const quaternion_type& get_rotation() const;
const float3& get_forward() const;
const float3& get_right() const;
const float3& get_up() const;
protected:
/**
* Updates the transform of the camera
*/
void update_transform(const transform_type& transform);
void update_projection(float fov, float aspect_ratio, float clip_near, float clip_far);
void update_projection(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far);
private:
camera* camera;
float3 translation;
quaternion_type rotation;
transform_type transform;
float3 forward;
float3 right;
float3 up;
@ -94,19 +91,14 @@ inline const camera* camera_rig::get_camera() const
return camera;
}
inline camera* camera_rig::get_camera()
{
return camera;
}
inline const float3& camera_rig::get_translation() const
{
return translation;
return transform.translation;
}
inline const typename camera_rig::quaternion_type& camera_rig::get_rotation() const
{
return rotation;
return transform.rotation;
}
inline const float3& camera_rig::get_forward() const

+ 199
- 0
src/animation/orbit-cam.cpp View File

@ -0,0 +1,199 @@
/*
* Copyright (C) 2020 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "animation/orbit-cam.hpp"
#include "scene/camera.hpp"
#include "math/math.hpp"
#include <algorithm>
#include <cmath>
#include <limits>
#include <iostream>
orbit_cam::orbit_cam():
azimuth_limits({-std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()}),
elevation_limits({-std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()}),
focal_distance_limits({-std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()}),
fov_limits({-std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()}),
clip_near_limits({-std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()}),
clip_far_limits({-std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()})
{
// Init focal point spring
focal_point_spring.v = {0.0f, 0.0f, 0.0f};
focal_point_spring.z = 1.0f;
focal_point_spring.w = 2.0f * math::two_pi<float>;
// Init azimuth spring
azimuth_spring.v = 0.0f;
azimuth_spring.z = 1.0f;
azimuth_spring.w = 2.0f * math::two_pi<float>;
// Init elevation spring
elevation_spring.v = 0.0f;
elevation_spring.z = 1.0f;
elevation_spring.w = 2.0f * math::two_pi<float>;
// Init zoom spring
zoom_spring.v = 0.0f;
zoom_spring.z = 1.0f;
zoom_spring.w = 5.0f * math::two_pi<float>;
}
orbit_cam::~orbit_cam()
{}
void orbit_cam::update(float dt)
{
if (!get_camera())
{
return;
}
// Solve springs
solve_numeric_spring<float3, float>(focal_point_spring, dt);
solve_numeric_spring<float, float>(azimuth_spring, dt);
solve_numeric_spring<float, float>(elevation_spring, dt);
solve_numeric_spring<float, float>(zoom_spring, dt);
// Calculate zoom-dependent variables
float focal_distance = math::log_lerp<float>(focal_distance_limits[1], focal_distance_limits[0], zoom_spring.x0);
float fov = math::log_lerp<float>(fov_limits[1], fov_limits[0], zoom_spring.x0);
float clip_near = math::log_lerp<float>(clip_near_limits[1], clip_near_limits[0], zoom_spring.x0);
float clip_far = math::log_lerp<float>(clip_far_limits[1], clip_far_limits[0], zoom_spring.x0);
// Calculate camera transform
transform_type transform = math::identity_transform<float>;
// Determine rotation
azimuth_rotation = math::angle_axis(azimuth_spring.x0, float3{0.0f, 1.0f, 0.0f});
elevation_rotation = math::angle_axis(elevation_spring.x0, float3{-1.0f, 0.0f, 0.0f});
transform.rotation = math::normalize(azimuth_rotation * elevation_rotation);
// Determine translation
transform.translation = focal_point_spring.x0 + transform.rotation * float3{0.0f, 0.0f, focal_distance};
// Update camera transform
update_transform(transform);
// Update camera projection
float zoom_factor = 0.0f;
update_projection(fov, aspect_ratio, clip_near, clip_far);
}
void orbit_cam::move(const float3& translation)
{
set_target_focal_point(focal_point_spring.x1 + translation);
}
void orbit_cam::pan(float angle)
{
set_target_azimuth(azimuth_spring.x1 + angle);
}
void orbit_cam::tilt(float angle)
{
set_target_elevation(elevation_spring.x1 + angle);
}
void orbit_cam::zoom(float factor)
{
set_target_zoom(zoom_spring.x1 + factor);
}
void orbit_cam::reset_springs()
{
focal_point_spring.x0 = focal_point_spring.x1;
azimuth_spring.x0 = azimuth_spring.x1;
elevation_spring.x0 = elevation_spring.x1;
zoom_spring.x0 = zoom_spring.x1;
}
void orbit_cam::set_aspect_ratio(float ratio)
{
aspect_ratio = ratio;
}
void orbit_cam::set_focal_point(const float3& point)
{
focal_point_spring.x0 = point;
}
void orbit_cam::set_azimuth(float angle)
{
azimuth_spring.x0 = std::max<float>(azimuth_limits[0], std::min<float>(azimuth_limits[1], angle));
}
void orbit_cam::set_elevation(float angle)
{
elevation_spring.x0 = std::max<float>(elevation_limits[0], std::min<float>(elevation_limits[1], angle));
}
void orbit_cam::set_zoom(float factor)
{
zoom_spring.x0 = std::max<float>(0.0f, std::min<float>(1.0f, factor));
}
void orbit_cam::set_target_focal_point(const float3& point)
{
focal_point_spring.x1 = point;
}
void orbit_cam::set_target_azimuth(float angle)
{
azimuth_spring.x1 = std::max<float>(azimuth_limits[0], std::min<float>(azimuth_limits[1], angle));
}
void orbit_cam::set_target_elevation(float angle)
{
elevation_spring.x1 = std::max<float>(elevation_limits[0], std::min<float>(elevation_limits[1], angle));
}
void orbit_cam::set_target_zoom(float factor)
{
zoom_spring.x1 = std::max<float>(0.0f, std::min<float>(1.0f, factor));
}
void orbit_cam::set_azimuth_limits(const std::array<float, 2>& limits)
{
azimuth_limits = limits;
}
void orbit_cam::set_elevation_limits(const std::array<float, 2>& limits)
{
elevation_limits = limits;
}
void orbit_cam::set_focal_distance_limits(const std::array<float, 2>& limits)
{
focal_distance_limits = limits;
}
void orbit_cam::set_fov_limits(const std::array<float, 2>& limits)
{
fov_limits = limits;
}
void orbit_cam::set_clip_near_limits(const std::array<float, 2>& limits)
{
clip_near_limits = limits;
}
void orbit_cam::set_clip_far_limits(const std::array<float, 2>& limits)
{
clip_far_limits = limits;
}

src/orbit-cam.hpp → src/animation/orbit-cam.hpp View File

@ -21,6 +21,8 @@
#define ANTKEEPER_ORBIT_CAM_HPP
#include "camera-rig.hpp"
#include "animation/spring.hpp"
#include <array>
/**
* Rig which orbits around a focal point.
@ -33,98 +35,86 @@ public:
virtual void update(float dt);
/// @param direction Specifies the movement direction and speed scale on the XZ plane
void move(const float2& direction);
void rotate(float angle);
void move(const float3& translation);
void pan(float angle);
void tilt(float angle);
void zoom(float distance);
void zoom(float factor);
void reset_springs();
void set_aspect_ratio(float ratio);
void set_focal_point(const float3& point);
void set_focal_distance(float distance);
void set_elevation(float angle);
void set_azimuth(float angle);
void set_elevation(float angle);
void set_zoom(float factor);
void set_target_focal_point(const float3& point);
void set_target_focal_distance(float distance);
void set_target_elevation(float angle);
void set_target_azimuth(float angle);
void set_target_elevation(float angle);
void set_target_zoom(float factor);
void set_azimuth_limits(const std::array<float, 2>& limits);
void set_elevation_limits(const std::array<float, 2>& limits);
void set_focal_distance_limits(const std::array<float, 2>& limits);
void set_fov_limits(const std::array<float, 2>& limits);
void set_clip_near_limits(const std::array<float, 2>& limits);
void set_clip_far_limits(const std::array<float, 2>& limits);
const float3& get_focal_point() const;
float get_focal_distance() const;
float get_elevation() const;
float get_azimuth() const;
const float3& get_target_focal_point() const;
float get_target_focal_distance() const;
float get_target_elevation() const;
float get_target_azimuth() const;
const float3& get_target_translation() const;
const quaternion_type& get_target_rotation() const;
float get_elevation() const;
float get_zoom() const;
const quaternion_type& get_azimuth_rotation() const;
const quaternion_type& get_elevation_rotation() const;
private:
float3 focal_point;
float focal_distance;
float elevation;
float azimuth;
float aspect_ratio;
numeric_spring<float3, float> focal_point_spring;
numeric_spring<float, float> azimuth_spring;
numeric_spring<float, float> elevation_spring;
numeric_spring<float, float> zoom_spring;
float3 target_focal_point;
float target_focal_distance;
float target_elevation;
float target_azimuth;
std::array<float, 2> azimuth_limits;
std::array<float, 2> elevation_limits;
std::array<float, 2> focal_distance_limits;
std::array<float, 2> fov_limits;
std::array<float, 2> clip_near_limits;
std::array<float, 2> clip_far_limits;
quaternion_type elevation_rotation;
quaternion_type azimuth_rotation;
quaternion_type target_elevation_rotation;
quaternion_type target_azimuth_rotation;
quaternion_type target_rotation;
float3 target_translation;
quaternion_type elevation_rotation;
};
inline const float3& orbit_cam::get_focal_point() const
{
return focal_point;
}
inline float orbit_cam::get_focal_distance() const
{
return focal_distance;
}
inline float orbit_cam::get_elevation() const
{
return elevation;
return focal_point_spring.x0;
}
inline float orbit_cam::get_azimuth() const
{
return azimuth;
return azimuth_spring.x0;
}
inline const float3& orbit_cam::get_target_focal_point() const
{
return target_focal_point;
}
inline float orbit_cam::get_target_focal_distance() const
inline float orbit_cam::get_elevation() const
{
return target_focal_distance;
return elevation_spring.x0;
}
inline float orbit_cam::get_target_elevation() const
inline float orbit_cam::get_zoom() const
{
return target_elevation;
return zoom_spring.x0;
}
inline float orbit_cam::get_target_azimuth() const
{
return target_azimuth;
}
inline const float3& orbit_cam::get_target_translation() const
inline const typename camera_rig::quaternion_type& orbit_cam::get_azimuth_rotation() const
{
return target_translation;
return azimuth_rotation;
}
inline const typename camera_rig::quaternion_type& orbit_cam::get_target_rotation() const
inline const typename camera_rig::quaternion_type& orbit_cam::get_elevation_rotation() const
{
return target_rotation;
return elevation_rotation;
}
#endif // ANTKEEPER_ORBIT_CAM_HPP

+ 9
- 6
src/animation/spring.hpp View File

@ -27,9 +27,10 @@
* @tparam S Scalar type.
*
* @see spring()
* @see solve_numeric_spring()
*/
template <typename T, typename S>
struct spring_constraint
struct numeric_spring
{
T x0; ///< Start value
T x1; ///< End value
@ -55,13 +56,15 @@ template
void spring(T& x0, T& v, const T& x1, S z, S w, S dt);
/**
* Solves a spring constraint using the spring() function.
* Solves a numeric spring using the spring() function.
*
* @param[in,out] constraint Spring constraint to be sovled.
* @param[in,out] ns Numeric spring to be sovled.
* @param dt Delta time, in seconds.
*
* @see spring()
*/
template <typename T, typename S>
void solve_spring_constraint(spring_constraint<T, S>& constraint, S dt);
void solve_numeric_spring(numeric_spring<T, S>& ns, S dt);
template <typename T, typename S>
void spring(T& x0, T& v, const T& x1, S z, S w, S dt)
@ -78,9 +81,9 @@ void spring(T& x0, T& v, const T& x1, S z, S w, S dt)
}
template <typename T, typename S>
void solve_spring_constraint(spring_constraint<T, S>& constraint, S dt)
void solve_numeric_spring(numeric_spring<T, S>& ns, S dt)
{
spring(constraint.x0, constraint.v, constraint.x1, constraint.z, constraint.w, dt);
spring(ns.x0, ns.v, ns.x1, ns.z, ns.w, dt);
}
#endif // ANTKEEPER_SPRING_HPP

+ 16
- 17
src/game/bootloader.cpp View File

@ -81,7 +81,6 @@
#include "input/game-controller.hpp"
#include "input/mouse.hpp"
#include "input/keyboard.hpp"
#include "orbit-cam.hpp"
#include "pheromone-matrix.hpp"
#include "configuration.hpp"
#include "input/scancode.hpp"
@ -457,10 +456,10 @@ void setup_rendering(game_context* ctx)
ctx->shadow_map_framebuffer = new framebuffer(shadow_map_resolution, shadow_map_resolution);
ctx->shadow_map_framebuffer->attach(framebuffer_attachment_type::depth, ctx->shadow_map_depth_texture);
// Create bloom pingpong framebuffers (32F color, no depth)
// Create bloom pingpong framebuffers (16F color, no depth)
int bloom_width = viewport_dimensions[0] >> 1;
int bloom_height = viewport_dimensions[1] >> 1;
ctx->bloom_texture = new texture_2d(bloom_width, bloom_height, pixel_type::float_32, pixel_format::rgb);
ctx->bloom_texture = new texture_2d(bloom_width, bloom_height, pixel_type::float_16, pixel_format::rgb);
ctx->bloom_texture->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp);
ctx->bloom_texture->set_filters(texture_min_filter::linear, texture_mag_filter::linear);
ctx->bloom_texture->set_max_anisotropy(0.0f);
@ -486,8 +485,8 @@ void setup_rendering(game_context* ctx)
ctx->overworld_bloom_pass = new bloom_pass(ctx->rasterizer, ctx->framebuffer_bloom, ctx->resource_manager);
ctx->overworld_bloom_pass->set_source_texture(ctx->framebuffer_hdr_color);
ctx->overworld_bloom_pass->set_brightness_threshold(1.0f);
ctx->overworld_bloom_pass->set_blur_iterations(4);
ctx->overworld_bloom_pass->set_enabled(false);
ctx->overworld_bloom_pass->set_blur_iterations(5);
ctx->overworld_bloom_pass->set_enabled(true);
ctx->overworld_final_pass = new ::final_pass(ctx->rasterizer, &ctx->rasterizer->get_default_framebuffer(), ctx->resource_manager);
ctx->overworld_final_pass->set_color_texture(ctx->framebuffer_hdr_color);
ctx->overworld_final_pass->set_bloom_texture(ctx->bloom_texture);
@ -563,6 +562,10 @@ void setup_scenes(game_context* ctx)
const auto& viewport_dimensions = ctx->rasterizer->get_default_framebuffer().get_dimensions();
float viewport_aspect_ratio = static_cast<float>(viewport_dimensions[0]) / static_cast<float>(viewport_dimensions[1]);
// Create infinite culling mask
float inf = std::numeric_limits<float>::infinity();
ctx->no_cull = {{-inf, -inf, -inf}, {inf, inf, inf}};
// Setup overworld camera
ctx->overworld_camera = new camera();
ctx->overworld_camera->set_perspective(math::radians<float>(45.0f), viewport_aspect_ratio, 0.1f, 1000.0f);
@ -755,9 +758,6 @@ void setup_systems(game_context* ctx)
const auto& viewport_dimensions = ctx->app->get_viewport_dimensions();
float4 viewport = {0.0f, 0.0f, static_cast<float>(viewport_dimensions[0]), static_cast<float>(viewport_dimensions[1])};
ctx->orbit_cam = new orbit_cam();
ctx->orbit_cam->attach(ctx->overworld_camera);
// Setup terrain system
ctx->terrain_system = new ::terrain_system(*ctx->ecs_registry, ctx->resource_manager);
ctx->terrain_system->set_patch_size(TERRAIN_PATCH_SIZE);
@ -770,16 +770,16 @@ void setup_systems(game_context* ctx)
ctx->vegetation_system->set_vegetation_model(ctx->resource_manager->load<model>("grass-tuft.obj"));
ctx->vegetation_system->set_scene(ctx->overworld_scene);
// Setup camera system
ctx->camera_system = new camera_system(*ctx->ecs_registry);
ctx->camera_system->set_viewport(viewport);
// Setup tool system
ctx->tool_system = new tool_system(*ctx->ecs_registry);
ctx->tool_system->set_camera(ctx->overworld_camera);
ctx->tool_system->set_orbit_cam(ctx->orbit_cam);
ctx->tool_system->set_orbit_cam(ctx->camera_system->get_orbit_cam());
ctx->tool_system->set_viewport(viewport);
// Setup camera system
ctx->camera_system = new camera_system(*ctx->ecs_registry);
ctx->camera_system->set_viewport(viewport);
// Setup subterrain system
ctx->subterrain_system = new ::subterrain_system(*ctx->ecs_registry, ctx->resource_manager);
ctx->subterrain_system->set_scene(ctx->underworld_scene);
@ -822,7 +822,6 @@ void setup_systems(game_context* ctx)
// Setup control system
ctx->control_system = new ::control_system(*ctx->ecs_registry);
ctx->control_system->set_orbit_cam(ctx->orbit_cam);
ctx->control_system->set_viewport(viewport);
ctx->control_system->set_underworld_camera(ctx->underworld_camera);
ctx->control_system->set_tool(nullptr);
@ -891,12 +890,12 @@ void setup_controls(game_context* ctx)
ctx->rotate_ccw_control = new control();
ctx->rotate_ccw_control->set_activated_callback
(
std::bind(&camera_system::rotate, ctx->camera_system, math::radians(-90.0f))
std::bind(&camera_system::pan, ctx->camera_system, math::radians(-90.0f))
);
ctx->rotate_cw_control = new control();
ctx->rotate_cw_control->set_activated_callback
(
std::bind(&camera_system::rotate, ctx->camera_system, math::radians(90.0f))
std::bind(&camera_system::pan, ctx->camera_system, math::radians(90.0f))
);
// Create menu back control
@ -1050,7 +1049,7 @@ void setup_callbacks(game_context* ctx)
ctx->tool_system->update(t, dt);
ctx->constraint_system->update(t, dt);
(*ctx->focal_point_tween)[1] = ctx->orbit_cam->get_focal_point();
//(*ctx->focal_point_tween)[1] = ctx->orbit_cam->get_focal_point();
ctx->ui_system->update(dt);
ctx->render_system->update(t, dt);

+ 2
- 1
src/game/game-context.hpp View File

@ -22,6 +22,7 @@
#include "utility/fundamental-types.hpp"
#include "resources/string-table.hpp"
#include "geometry/aabb.hpp"
#include <optional>
#include <entt/entt.hpp>
#include <fstream>
@ -173,6 +174,7 @@ struct game_context
ambient_light* underworld_ambient_light;
spotlight* spotlight;
billboard* splash_billboard;
aabb<float> no_cull;
// Animation
timeline* timeline;
@ -227,7 +229,6 @@ struct game_context
cli* cli;
// Misc
orbit_cam* orbit_cam;
pheromone_matrix* pheromones;
};

+ 8
- 8
src/game/states/play-state.cpp View File

@ -46,6 +46,7 @@
#include "scene/camera.hpp"
#include "game/systems/control-system.hpp"
#include "game/systems/camera-system.hpp"
#include "game/systems/render-system.hpp"
#include "utility/fundamental-types.hpp"
void play_state_enter(game_context* ctx)
@ -64,7 +65,7 @@ void play_state_enter(game_context* ctx)
ecs::archetype* maple_tree_archetype = resource_manager->load<ecs::archetype>("maple-tree.ent");
ecs::archetype* nest_archetype = resource_manager->load<ecs::archetype>("harvester-nest.ent");
ecs::archetype* samara_archetype = resource_manager->load<ecs::archetype>("samara.ent");
ecs::archetype* forceps_archetype = resource_manager->load<ecs::archetype>("forceps.ent");
ecs::archetype* forceps_archetype = resource_manager->load<ecs::archetype>("lens.ent");
ecs::archetype* larva_archetype = resource_manager->load<ecs::archetype>("larva.ent");
ecs::archetype* pebble_archetype = resource_manager->load<ecs::archetype>("pebble.ent");
@ -175,13 +176,7 @@ void play_state_enter(game_context* ctx)
// Setup camera
ctx->overworld_camera->look_at({0, 0, 1}, {0, 0, 0}, {0, 1, 0});
ctx->camera_system->set_camera(ctx->overworld_camera);
ctx->camera_system->set_azimuth(0.0f);
ctx->camera_system->set_elevation(math::radians(45.0f));
ctx->camera_system->set_zoom(0.0f);
ctx->camera_system->set_focal_distance(2.0f, 200.0f);
ctx->camera_system->set_fov(math::radians(80.0f), math::radians(35.0f));
ctx->camera_system->set_clip_near(1.0f, 5.0f);
ctx->camera_system->set_clip_far(100.0f, 2000.0f);
// Create forceps tool
@ -189,6 +184,11 @@ void play_state_enter(game_context* ctx)
ecs::tool_component forceps_tool_component;
forceps_tool_component.active = true;
ecs_registry.assign<ecs::tool_component>(forceps_entity, forceps_tool_component);
model_instance* forceps_model_instance = ctx->render_system->get_model_instance(forceps_entity);
if (forceps_model_instance)
{
forceps_model_instance->set_culling_mask(&ctx->no_cull);
}
ctx->overworld_scene->update_tweens();

+ 28
- 125
src/game/systems/camera-system.cpp View File

@ -20,9 +20,9 @@
#include "camera-system.hpp"
#include "game/components/camera-subject-component.hpp"
#include "game/components/transform-component.hpp"
#include "animation/spring.hpp"
#include "scene/camera.hpp"
#include "math/math.hpp"
#include <algorithm>
#include <cmath>
#include <iostream>
@ -34,25 +34,19 @@ camera_system::camera_system(entt::registry& registry):
viewport{0, 0, 0, 0},
mouse_position{0, 0}
{
// Init azimuth spring constraint
azimuth_spring.v = 0.0f;
azimuth_spring.z = 1.0f;
azimuth_spring.w = 2.0f * math::two_pi<float>;
orbit_cam.set_elevation_limits({math::radians(5.0f), math::radians(89.0f)});
orbit_cam.set_focal_distance_limits({2.0f, 200.0f});
orbit_cam.set_fov_limits({math::radians(80.0f), math::radians(35.0f)});
orbit_cam.set_clip_near_limits({0.1f, 5.0f});
orbit_cam.set_clip_far_limits({100.0f, 2000.0f});
// Init elevation spring constraint
elevation_spring.v = 0.0f;
elevation_spring.z = 1.0f;
elevation_spring.w = 2.0f * math::two_pi<float>;
orbit_cam.set_target_focal_point({0.0f, 0.0f, 0.0f});
orbit_cam.set_target_azimuth(0.0f);
orbit_cam.set_target_elevation(math::radians(45.0f));
orbit_cam.set_target_zoom(0.0f);
// Init focal distance spring constraint
focal_distance_spring.v = 0.0f;
focal_distance_spring.z = 1.0f;
focal_distance_spring.w = 5.0f * math::two_pi<float>;
// Init fov spring constraint
fov_spring.v = 0.0f;
fov_spring.z = 1.0f;
fov_spring.w = 5.0f * math::two_pi<float>;
orbit_cam.reset_springs();
}
void camera_system::update(double t, double dt)
@ -72,124 +66,45 @@ void camera_system::update(double t, double dt)
if (subject_count > 1)
target_focal_point /= static_cast<float>(subject_count);
// Get source transform
transform_type source_transform = camera->get_transform();
// Solve azimuth spring
float2 xz_direction = math::normalize(math::swizzle<0, 2>(target_focal_point) - math::swizzle<0, 2>(source_transform.translation));
azimuth_spring.x0 = math::wrap_radians(std::atan2(-xz_direction.y, xz_direction.x) - math::half_pi<float>);
azimuth_spring.x1 = azimuth_spring.x0 + math::wrap_radians(azimuth_spring.x1 - azimuth_spring.x0);
solve_spring_constraint<float, float>(azimuth_spring, dt);
// Sovle elevation spring
elevation_spring.x0 = elevation;
elevation_spring.x1 = elevation_spring.x0 + math::wrap_radians(elevation_spring.x1 - elevation_spring.x0);
solve_spring_constraint<float, float>(elevation_spring, dt);
// Solve focal distance spring
focal_distance_spring.x0 = math::length(source_transform.translation - target_focal_point);
solve_spring_constraint<float, float>(focal_distance_spring, dt);
// Solve FOV spring
fov_spring.x0 = camera->get_fov();
solve_spring_constraint<float, float>(fov_spring, dt);
// Determine camera rotation
quaternion_type smooth_azimuth_rotation = math::angle_axis(azimuth_spring.x0, float3{0.0f, 1.0f, 0.0f});
quaternion_type smooth_elevation_rotation = math::angle_axis(elevation_spring.x0, float3{-1.0f, 0.0f, 0.0f});
quaternion_type smooth_rotation = math::normalize(smooth_azimuth_rotation * smooth_elevation_rotation);
// Determine camera view point
float3 smooth_view_point = target_focal_point + smooth_rotation * float3{0.0f, 0.0f, focal_distance_spring.x0};
// Update camera transform
transform_type smooth_transform;
smooth_transform.translation = smooth_view_point;
smooth_transform.rotation = smooth_rotation;
smooth_transform.scale = source_transform.scale;
camera->set_transform(smooth_transform);
// Determine aspect ratio
float aspect_ratio = viewport[2] / viewport[3];
target_focal_point.y += 0.2f;
// Determine clipping planes
float clip_near = math::log_lerp<float>(near_clip_far, near_clip_near, zoom_factor);
float clip_far = math::log_lerp<float>(far_clip_far, far_clip_near, zoom_factor);
// Update camera projection
camera->set_perspective(fov_spring.x0, aspect_ratio, clip_near, clip_far);
orbit_cam.set_target_focal_point(target_focal_point);
orbit_cam.update(static_cast<float>(dt));
}
void camera_system::rotate(float angle)
void camera_system::pan(float angle)
{
set_azimuth(azimuth + angle);
orbit_cam.pan(angle);
}
void camera_system::tilt(float angle)
{
set_elevation(elevation + angle);
orbit_cam.tilt(angle);
}
void camera_system::zoom(float factor)
{
set_zoom(zoom_factor + factor);
orbit_cam.zoom(factor);
}
void camera_system::set_camera(::camera* camera)
{
this->camera = camera;
if (camera)
{
orbit_cam.attach(camera);
}
else
{
orbit_cam.detach();
}
}
void camera_system::set_viewport(const float4& viewport)
{
this->viewport = viewport;
}
void camera_system::set_azimuth(float angle)
{
azimuth = math::wrap_radians(angle);
azimuth_rotation = math::angle_axis(azimuth, float3{0.0f, 1.0f, 0.0f});
azimuth_spring.x1 = azimuth;
}
void camera_system::set_elevation(float angle)
{
elevation = math::wrap_radians(angle);
elevation_rotation = math::angle_axis(elevation, float3{-1.0f, 0.0f, 0.0f});
elevation_spring.x1 = elevation;
}
void camera_system::set_zoom(float factor)
{
this->zoom_factor = std::max<float>(0.0f, std::min<float>(1.0f, factor));
update_focal_distance();
update_fov();
}
void camera_system::set_focal_distance(float distance_near, float distance_far)
{
focal_distance_near = distance_near;
focal_distance_far = distance_far;
update_focal_distance();
}
void camera_system::set_fov(float angle_near, float angle_far)
{
fov_near = angle_near;
fov_far = angle_far;
update_fov();
}
void camera_system::set_clip_near(float distance_near, float distance_far)
{
near_clip_near = distance_near;
near_clip_far = distance_far;
}
void camera_system::set_clip_far(float distance_near, float distance_far)
{
far_clip_near = distance_near;
far_clip_far = distance_far;
orbit_cam.set_aspect_ratio(viewport[2] / viewport[3]);
}
void camera_system::handle_event(const mouse_moved_event& event)
@ -202,15 +117,3 @@ void camera_system::handle_event(const window_resized_event& event)
{
set_viewport({0.0f, 0.0f, static_cast<float>(event.w), static_cast<float>(event.h)});
}
void camera_system::update_focal_distance()
{
focal_distance = math::log_lerp<float>(focal_distance_far, focal_distance_near, zoom_factor);
focal_distance_spring.x1 = focal_distance;
}
void camera_system::update_fov()
{
fov = math::log_lerp<float>(fov_far, fov_near, zoom_factor);
fov_spring.x1 = fov;
}

+ 12
- 38
src/game/systems/camera-system.hpp View File

@ -27,7 +27,7 @@
#include "utility/fundamental-types.hpp"
#include "math/quaternion-type.hpp"
#include "math/transform-type.hpp"
#include "animation/spring.hpp"
#include "animation/orbit-cam.hpp"
class camera;
class orbit_cam;
@ -44,61 +44,35 @@ public:
camera_system(entt::registry& registry);
virtual void update(double t, double dt);
void rotate(float angle);
void pan(float angle);
void tilt(float angle);
void zoom(float factor);
void set_camera(::camera* camera);
void set_viewport(const float4& viewport);
void set_azimuth(float angle);
void set_elevation(float angle);
void set_zoom(float factor);
void set_focal_distance(float distance_near, float distance_far);
void set_fov(float angle_near, float angle_far);
void set_clip_near(float distance_near, float distance_far);
void set_clip_far(float distance_near, float distance_far);
const spring_constraint<float, float>& get_azimuth_spring() const;
const orbit_cam* get_orbit_cam() const;
orbit_cam* get_orbit_cam();
private:
virtual void handle_event(const mouse_moved_event& event);
virtual void handle_event(const window_resized_event& event);
void update_focal_distance();
void update_fov();
camera* camera;
float4 viewport;
float2 mouse_position;
spring_constraint<float, float> azimuth_spring;
spring_constraint<float, float> elevation_spring;
spring_constraint<float, float> focal_distance_spring;
spring_constraint<float, float> fov_spring;
float azimuth;
float elevation;
float focal_distance;
float zoom_factor;
float fov;
float focal_distance_near;
float focal_distance_far;
float fov_near;
float fov_far;
float near_clip_near;
float near_clip_far;
float far_clip_near;
float far_clip_far;
quaternion_type elevation_rotation;
quaternion_type azimuth_rotation;
orbit_cam orbit_cam;
};
inline const spring_constraint<float, float>& camera_system::get_azimuth_spring() const
inline const orbit_cam* camera_system::get_orbit_cam() const
{
return &orbit_cam;
}
inline orbit_cam* camera_system::get_orbit_cam()
{
return azimuth_spring;
return &orbit_cam;
}
#endif // ANTKEEPER_CAMERA_SYSTEM_HPP

+ 15
- 144
src/game/systems/control-system.cpp View File

@ -19,7 +19,6 @@
#include "control-system.hpp"
#include "input/control.hpp"
#include "orbit-cam.hpp"
#include "scene/camera.hpp"
#include "geometry/intersection.hpp"
#include "animation/ease.hpp"
@ -27,6 +26,7 @@
#include "math/math.hpp"
#include "game/entity-commands.hpp"
#include "game/systems/camera-system.hpp"
#include "animation/orbit-cam.hpp"
control_system::control_system(entt::registry& registry):
entity_system(registry),
@ -74,7 +74,6 @@ control_system::control_system(entt::registry& registry):
far_clip_far = 2000.0f;
nest = nullptr;
orbit_cam = nullptr;
mouse_angle = 0.0f;
old_mouse_angle = mouse_angle;
@ -85,6 +84,8 @@ control_system::control_system(entt::registry& registry):
void control_system::update(double t, double dt)
{
timestep = dt;
// Zoom camera
if (zoom_in_control.is_active())
camera_system->zoom(zoom_speed * dt);
@ -104,12 +105,15 @@ void control_system::update(double t, double dt)
if (math::length_squared(movement) != 0.0f)
{
float max_speed = 100.0f * dt;
std::array<float, 2> speed_limits = {15.0f, 100.0f};
float zoom = camera_system->get_orbit_cam()->get_zoom();
float max_speed = math::log_lerp(speed_limits[1], speed_limits[0], zoom) * dt;
float speed = std::min<float>(max_speed, math::length(movement * max_speed));
movement = math::normalize(movement) * speed;
float azimuth = camera_system->get_azimuth_spring().x0;
math::quaternion<float> azimuth_rotation = math::angle_axis(azimuth, float3{0.0f, 1.0f, 0.0f});
const math::quaternion<float>& azimuth_rotation = camera_system->get_orbit_cam()->get_azimuth_rotation();
movement = azimuth_rotation * movement;
ec::translate(registry, camera_subject_eid, movement);
@ -150,139 +154,6 @@ void control_system::update(double t, double dt)
}
}
/*
void control_system::update(double t, double dt)
{
this->timestep = dt;
if (adjust_camera_control.is_active() && !adjust_camera_control.was_active())
{
}
// Determine zoom
if (zoom_in_control.is_active())
zoom += zoom_speed * dt * zoom_in_control.get_current_value();
if (zoom_out_control.is_active())
zoom -= zoom_speed * dt * zoom_out_control.get_current_value();
zoom = std::max<float>(0.0f, std::min<float>(1.0f, zoom));
float focal_distance = math::log_lerp<float>(near_focal_distance, far_focal_distance, 1.0f - zoom);
float fov = math::log_lerp<float>(near_fov, far_fov, 1.0f - zoom);
//float elevation_factor = (orbit_cam->get_target_elevation() - min_elevation) / max_elevation;
//fov = math::log_lerp<float>(near_fov, far_fov, elevation_factor);
float clip_near = math::log_lerp<float>(near_clip_near, far_clip_near, 1.0f - zoom);
float clip_far = math::log_lerp<float>(near_clip_far, far_clip_far, 1.0f - zoom);
float movement_speed = math::log_lerp<float>(near_movement_speed * dt, far_movement_speed * dt, 1.0f - zoom);
orbit_cam->set_target_focal_distance(focal_distance);
orbit_cam->get_camera()->set_perspective(fov, orbit_cam->get_camera()->get_aspect_ratio(), clip_near, clip_far);
const float rotation_speed = 2.0f * dt;
float rotation = 0.0f;
if (rotate_ccw_control.is_active())
rotation += rotate_ccw_control.get_current_value() * rotation_speed;
if (rotate_cw_control.is_active())
rotation -= rotate_cw_control.get_current_value() * rotation_speed;
if (rotation)
{
orbit_cam->rotate(rotation);
}
const float tilt_speed = 2.0f * dt;
float tilt = 0.0f;
if (tilt_up_control.is_active())
tilt -= tilt_up_control.get_current_value() * tilt_speed;
if (tilt_down_control.is_active())
tilt += tilt_down_control.get_current_value() * tilt_speed;
if (tilt)
{
orbit_cam->tilt(tilt);
float elevation = orbit_cam->get_target_elevation();
elevation = std::min<float>(max_elevation, std::max<float>(min_elevation, elevation));
orbit_cam->set_target_elevation(elevation);
}
float2 movement{0.0f, 0.0f};
if (move_right_control.is_active())
movement[0] += move_right_control.get_current_value();
if (move_left_control.is_active())
movement[0] -= move_left_control.get_current_value();
if (move_forward_control.is_active())
movement[1] -= move_forward_control.get_current_value();
if (move_back_control.is_active())
movement[1] += move_back_control.get_current_value();
const float deadzone = 0.01f;
float magnitude_squared = math::length_squared(movement);
if (magnitude_squared > deadzone)
{
if (magnitude_squared > 1.0f)
{
movement = math::normalize(movement);
}
orbit_cam->move(movement * movement_speed);
}
float ascention = 0.0f;
if (ascend_control.is_active())
ascention += ascend_control.get_current_value();
if (descend_control.is_active())
ascention -= descend_control.get_current_value();
if (ascention)
{
float old_depth = -orbit_cam->get_target_focal_point()[1];
orbit_cam->set_target_focal_point(orbit_cam->get_target_focal_point() + float3{0.0f, ascention * movement_speed, 0.0f});
if (nest)
{
float3 focal_point = orbit_cam->get_target_focal_point();
float depth = -focal_point[1];
float delta_shaft_angle = nest->get_shaft_angle(*nest->get_central_shaft(), depth) - nest->get_shaft_angle(*nest->get_central_shaft(), old_depth);
//orbit_cam->set_target_azimuth(orbit_cam->get_target_azimuth() - delta_shaft_angle);
orbit_cam->set_target_focal_point(nest->get_shaft_position(*nest->get_central_shaft(), depth));
}
}
orbit_cam->update(dt);
camera* camera = orbit_cam->get_camera();
float3 pick_near = camera->unproject({mouse_position[0], viewport[3] - mouse_position[1], 0.0f}, viewport);
float3 pick_far = camera->unproject({mouse_position[0], viewport[3] - mouse_position[1], 1.0f}, viewport);
float3 pick_direction = math::normalize(pick_far - pick_near);
ray<float> picking_ray = {pick_near, pick_direction};
plane<float> ground_plane = {float3{0, 1, 0}, 0.0f};
auto intersection_result = ray_plane_intersection(picking_ray, ground_plane);
if (std::get<0>(intersection_result))
{
float3 pick = picking_ray.extrapolate(std::get<1>(intersection_result));
if (tool)
tool->set_translation(pick);
}
if (toggle_view_control.is_active() && !toggle_view_control.was_active())
{
}
}
*/
void control_system::set_orbit_cam(::orbit_cam* orbit_cam)
{
this->orbit_cam = orbit_cam;
}
void control_system::set_camera_system(::camera_system* camera_system)
{
this->camera_system = camera_system;
@ -337,12 +208,12 @@ void control_system::handle_event(const mouse_moved_event& event)
elevation_factor *= -1.0f;
}
float rotation = math::radians(22.5f) * rotation_factor * timestep;
float elevation = orbit_cam->get_target_elevation() + elevation_factor * 0.25f * timestep;
elevation = std::min<float>(max_elevation, std::max<float>(min_elevation, elevation));
orbit_cam->rotate(rotation);
orbit_cam->set_target_elevation(elevation);
float rotation = math::radians(15.0f) * rotation_factor * timestep;
float tilt = math::radians(15.0f) * elevation_factor * timestep;
camera_system->pan(rotation);
camera_system->tilt(tilt);
}
else if (!adjust_camera_control.was_active())
{

+ 0
- 3
src/game/systems/control-system.hpp View File

@ -29,7 +29,6 @@
#include "scene/model-instance.hpp"
#include "utility/fundamental-types.hpp"
class orbit_cam;
class nest;
class camera;
class camera_system;
@ -47,7 +46,6 @@ public:
void set_invert_mouse_x(bool invert);
void set_invert_mouse_y(bool invert);
void set_orbit_cam(orbit_cam* orbit_cam);
void set_camera_system(camera_system* camera_system);
void set_nest(::nest* nest);
void set_tool(model_instance* tool);
@ -111,7 +109,6 @@ private:
float timestep;
float zoom;
orbit_cam* orbit_cam;
camera_system* camera_system;
::nest* nest;
model_instance* tool;

+ 7
- 0
src/game/systems/render-system.cpp View File

@ -78,6 +78,13 @@ void render_system::set_renderer(::renderer* renderer)
this->renderer = renderer;
}
model_instance* render_system::get_model_instance(entt::entity entity)
{
if (auto it = model_instances.find(entity); it != model_instances.end())
return it->second;
return nullptr;
}
void render_system::update_model_and_materials(entt::entity entity, model_component& model)
{
if (auto model_it = model_instances.find(entity); model_it != model_instances.end())

+ 2
- 0
src/game/systems/render-system.hpp View File

@ -42,6 +42,8 @@ public:
void remove_layers();
void set_renderer(::renderer* renderer);
model_instance* get_model_instance(entt::entity entity);
private:
void update_model_and_materials(entt::entity entity, ecs::model_component& model);

+ 7
- 4
src/game/systems/tool-system.cpp View File

@ -22,7 +22,7 @@
#include "game/components/tool-component.hpp"
#include "game/components/transform-component.hpp"
#include "scene/camera.hpp"
#include "orbit-cam.hpp"
#include "animation/orbit-cam.hpp"
#include "geometry/mesh.hpp"
#include "geometry/intersection.hpp"
#include "math/math.hpp"
@ -35,7 +35,8 @@ tool_system::tool_system(entt::registry& registry):
orbit_cam(orbit_cam),
viewport{0, 0, 0, 0},
mouse_position{0, 0},
pick_enabled(true)
pick_enabled(true),
was_pick_enabled(pick_enabled)
{}
void tool_system::update(double t, double dt)
@ -106,12 +107,14 @@ void tool_system::update(double t, double dt)
if (intersection)
{
transform.transform.translation = pick;
transform.transform.translation = pick + float3{0, 15, 0};
}
math::quaternion<float> rotation = math::angle_axis(orbit_cam->get_azimuth() + pick_angle, float3{0, 1, 0});
transform.transform.rotation = rotation;
});
was_pick_enabled = pick_enabled;
}
void tool_system::set_camera(const ::camera* camera)
@ -136,7 +139,7 @@ void tool_system::set_pick(bool enabled)
void tool_system::handle_event(const mouse_moved_event& event)
{
if (pick_enabled)
if (pick_enabled && was_pick_enabled)
{
mouse_position[0] = event.x;
mouse_position[1] = event.y;

+ 1
- 0
src/game/systems/tool-system.hpp View File

@ -48,6 +48,7 @@ private:
const orbit_cam* orbit_cam;
float4 viewport;
float2 mouse_position;
bool was_pick_enabled;
bool pick_enabled;
};

+ 2
- 2
src/math/angles.hpp View File

@ -47,7 +47,7 @@ template
T radians(T degrees);
/**
* Wraps an angle on [-180, 180].
* Wraps an angle to [-180, 180].
*
* @param degrees Angle in degrees.
* @return Wrapped angle.
@ -56,7 +56,7 @@ template
T wrap_degrees(T degrees);
/**
* Wraps an angle on [-pi, pi].
* Wraps an angle to [-pi, pi].
*
* @param radians Angle in radians.
* @return Wrapped angle.

+ 0
- 154
src/orbit-cam.cpp View File

@ -1,154 +0,0 @@
/*
* Copyright (C) 2020 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "orbit-cam.hpp"
#include "scene/camera.hpp"
#include "math/math.hpp"
#include <algorithm>
#include <cmath>
orbit_cam::orbit_cam():
elevation_rotation(math::identity_quaternion<float>),
azimuth_rotation(math::identity_quaternion<float>),
target_elevation_rotation(math::identity_quaternion<float>),
target_azimuth_rotation(math::identity_quaternion<float>),
target_rotation(math::identity_quaternion<float>)
{}
orbit_cam::~orbit_cam()
{}
void orbit_cam::update(float dt)
{
float interpolation_factor = 1.0f;
// Calculate rotation and target rotation quaternions
//rotation = azimuth_rotation * elevation_rotation;
target_rotation = math::normalize(target_azimuth_rotation * target_elevation_rotation);
// Calculate target translation
target_translation = target_focal_point + target_rotation * float3{0.0f, 0.0f, target_focal_distance};
// Interpolate rotation
//rotation = glm::mix(rotation, target_rotation, interpolation_factor);
// Interpolate angles
set_elevation(math::lerp(elevation, target_elevation, interpolation_factor));
set_azimuth(math::lerp(azimuth, target_azimuth, interpolation_factor));
// Calculate rotation
set_rotation(math::normalize(azimuth_rotation * elevation_rotation));
// Interpolate focal point and focal distance
focal_point = math::lerp(focal_point, target_focal_point, interpolation_factor);
focal_distance = math::lerp(focal_distance, target_focal_distance, interpolation_factor);
// Caluclate translation
set_translation(focal_point + get_rotation() * float3{0.0f, 0.0f, focal_distance});
/*
// Recalculate azimuth
azimuth_rotation = rotation;
azimuth_rotation.x = 0.0f;
azimuth_rotation.z = 0.0f;