Browse Source

Add angle interpolation functions, switch from using slerp to nlerp in scene object transform interpolation, and start rewriting the camera and control systems

master
C. J. Howard 3 years ago
parent
commit
7c8b2ddfbc
11 changed files with 309 additions and 94 deletions
  1. +19
    -5
      src/game/bootloader.cpp
  2. +33
    -0
      src/game/components/camera-subject-component.hpp
  3. +2
    -0
      src/game/game-context.hpp
  4. +10
    -16
      src/game/states/play-state.cpp
  5. +124
    -64
      src/game/systems/camera-system.cpp
  6. +38
    -4
      src/game/systems/camera-system.hpp
  7. +3
    -0
      src/game/systems/control-system.hpp
  8. +31
    -0
      src/math/angles.hpp
  9. +20
    -0
      src/math/interpolation.hpp
  10. +28
    -4
      src/math/quaternion-functions.hpp
  11. +1
    -1
      src/scene/scene-object.cpp

+ 19
- 5
src/game/bootloader.cpp View File

@ -777,7 +777,6 @@ void setup_systems(game_context* ctx)
// Setup camera system
ctx->camera_system = new camera_system(*ctx->ecs_registry);
ctx->camera_system->set_orbit_cam(ctx->orbit_cam);
ctx->camera_system->set_viewport(viewport);
// Setup subterrain system
@ -874,8 +873,6 @@ void setup_controls(game_context* ctx)
}
);
// Create screenshot control
ctx->screenshot_control = new control();
ctx->screenshot_control->set_activated_callback
@ -887,6 +884,18 @@ void setup_controls(game_context* ctx)
}
);
// Create rotation controls
ctx->rotate_ccw_control = new control();
ctx->rotate_ccw_control->set_activated_callback
(
std::bind(&camera_system::rotate, ctx->camera_system, math::radians(-45.0f))
);
ctx->rotate_cw_control = new control();
ctx->rotate_cw_control->set_activated_callback
(
std::bind(&camera_system::rotate, ctx->camera_system, math::radians(45.0f))
);
// Create menu back control
ctx->menu_back_control = new control();
ctx->menu_back_control->set_activated_callback
@ -901,6 +910,8 @@ void setup_controls(game_context* ctx)
ctx->application_controls = new control_set();
ctx->application_controls->add_control(ctx->toggle_fullscreen_control);
ctx->application_controls->add_control(ctx->screenshot_control);
ctx->application_controls->add_control(ctx->rotate_ccw_control);
ctx->application_controls->add_control(ctx->rotate_cw_control);
// Create menu control set
ctx->menu_controls = new control_set();
@ -915,6 +926,8 @@ void setup_controls(game_context* ctx)
// Application control mappings
ctx->input_event_router->add_mapping(key_mapping(ctx->toggle_fullscreen_control, nullptr, scancode::f11));
ctx->input_event_router->add_mapping(key_mapping(ctx->screenshot_control, nullptr, scancode::f12));
ctx->input_event_router->add_mapping(key_mapping(ctx->rotate_ccw_control, nullptr, scancode::q));
ctx->input_event_router->add_mapping(key_mapping(ctx->rotate_cw_control, nullptr, scancode::e));
// Add menu control mappings
ctx->input_event_router->add_mapping(key_mapping(ctx->menu_back_control, nullptr, scancode::escape));
@ -985,6 +998,7 @@ void setup_controls(game_context* ctx)
event_dispatcher->subscribe<mouse_moved_event>(ctx->control_system);
event_dispatcher->subscribe<mouse_moved_event>(ctx->camera_system);
event_dispatcher->subscribe<window_resized_event>(ctx->camera_system);
event_dispatcher->subscribe<mouse_moved_event>(ctx->tool_system);
}
@ -1018,6 +1032,7 @@ void setup_callbacks(game_context* ctx)
ctx->timeline->advance(dt);
//ctx->control_system->update(t, dt);
ctx->terrain_system->update(t, dt);
ctx->vegetation_system->update(t, dt);
ctx->placement_system->update(t, dt);
@ -1025,10 +1040,9 @@ void setup_callbacks(game_context* ctx)
ctx->subterrain_system->update(t, dt);
ctx->collision_system->update(t, dt);
ctx->samara_system->update(t, dt);
ctx->camera_system->update(t, dt);
ctx->behavior_system->update(t, dt);
ctx->locomotion_system->update(t, dt);
ctx->control_system->update(t, dt);
ctx->camera_system->update(t, dt);
ctx->tool_system->update(t, dt);
ctx->constraint_system->update(t, dt);

+ 33
- 0
src/game/components/camera-subject-component.hpp View File

@ -0,0 +1,33 @@
/*
* 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/>.
*/
#ifndef ANTKEEPER_ECS_CAMERA_SUBJECT_COMPONENT_HPP
#define ANTKEEPER_ECS_CAMERA_SUBJECT_COMPONENT_HPP
namespace ecs {
struct camera_subject_component
{
};
} // namespace ecs
#endif // ANTKEEPER_ECS_CAMERA_SUBJECT_COMPONENT_HPP

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

@ -194,6 +194,8 @@ struct game_context
control* menu_back_control;
control* menu_select_control;
control* screenshot_control;
control* rotate_ccw_control;
control* rotate_cw_control;
control* toggle_fullscreen_control;
// Entities

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

@ -36,7 +36,6 @@
#include "game/states/game-states.hpp"
#include "math/math.hpp"
#include "nest.hpp"
#include "orbit-cam.hpp"
#include "renderer/material.hpp"
#include "renderer/model.hpp"
#include "renderer/passes/sky-pass.hpp"
@ -44,6 +43,7 @@
#include "scene/model-instance.hpp"
#include "scene/scene.hpp"
#include "game/systems/control-system.hpp"
#include "game/systems/camera-system.hpp"
#include "utility/fundamental-types.hpp"
void play_state_enter(game_context* ctx)
@ -152,20 +152,15 @@ void play_state_enter(game_context* ctx)
ecs_registry.get<ecs::transform_component>(grass_entity_2).transform.rotation = math::angle_axis(math::radians(120.0f), float3{0, 1, 0});
*/
// Setup overworld camera
camera* camera = ctx->overworld_camera;
orbit_cam* orbit_cam = ctx->orbit_cam;
orbit_cam->attach(camera);
orbit_cam->set_target_focal_point({0, 0, 0});
orbit_cam->set_target_focal_distance(15.0f);
orbit_cam->set_target_elevation(math::radians(25.0f));
orbit_cam->set_target_azimuth(0.0f);
orbit_cam->set_focal_point(orbit_cam->get_target_focal_point());
orbit_cam->set_focal_distance(orbit_cam->get_target_focal_distance());
orbit_cam->set_elevation(orbit_cam->get_target_elevation());
orbit_cam->set_azimuth(orbit_cam->get_target_azimuth());
// Setup camera
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
@ -243,7 +238,6 @@ void play_state_enter(game_context* ctx)
control_system* control_system = ctx->control_system;
control_system->update(0.0, 0.0);
control_system->set_nest(nest);
orbit_cam->update(0.0f);
// Start fade in
ctx->fade_transition->transition(1.0f, true, ease<float>::in_quad);

+ 124
- 64
src/game/systems/camera-system.cpp View File

@ -18,96 +18,111 @@
*/
#include "camera-system.hpp"
#include "game/components/collision-component.hpp"
#include "game/components/tool-component.hpp"
#include "game/components/camera-subject-component.hpp"
#include "game/components/transform-component.hpp"
#include "scene/camera.hpp"
#include "orbit-cam.hpp"
#include "geometry/mesh.hpp"
#include "geometry/intersection.hpp"
#include "math/math.hpp"
#include <cmath>
#include <iostream>
using namespace ecs;
camera_system::camera_system(entt::registry& registry):
entity_system(registry),
orbit_cam(nullptr),
camera(nullptr),
viewport{0, 0, 0, 0},
mouse_position{0, 0}
{}
void camera_system::update(double t, double dt)
{
if (!orbit_cam)
return;
const camera* camera = orbit_cam->get_camera();
if (!camera)
return;
/*
registry.view<transform_component, tool_component>().each(
[&](auto entity, auto& transform, auto& tool)
// Determine focal point
int subject_count = 0;
float3 focal_point = {0, 0, 0};
registry.view<camera_subject_component, transform_component>().each(
[&](auto entity, auto& subject, auto& transform)
{
if (!tool.active)
return;
focal_point += transform.transform.translation;
++subject_count;
});
if (subject_count > 1)
focal_point /= static_cast<float>(subject_count);
// Determine focal distance
float focal_distance = math::log_lerp<float>(focal_distance_far, focal_distance_near, zoom_factor);
// Determine view point
quaternion_type rotation = math::normalize(azimuth_rotation * elevation_rotation);
float3 view_point = focal_point + rotation * float3{0.0f, 0.0f, focal_distance};
// Update camera transform
transform_type source_transform = camera->get_transform();
transform_type target_transform = math::identity_transform<float>;
target_transform.translation = view_point;
target_transform.rotation = rotation;
float2 xz_direction = math::normalize(math::swizzle<0, 2>(focal_point) - math::swizzle<0, 2>(source_transform.translation));
float source_azimuth = math::wrap_radians(std::atan2(-xz_direction.y, xz_direction.x) - math::half_pi<float>);
float source_elevation = elevation;
std::cout << "azimuth: " << math::degrees(azimuth) << "\n";
std::cout << "source azimuth: " << math::degrees(source_azimuth) << "\n";
float smooth_factor = 0.1f;
float smooth_azimuth = math::lerp_angle(source_azimuth, azimuth, smooth_factor);
float smooth_elevation = math::lerp_angle(source_elevation, elevation, smooth_factor);
quaternion_type smooth_azimuth_rotation = math::angle_axis(smooth_azimuth, float3{0.0f, 1.0f, 0.0f});
quaternion_type smooth_elevation_rotation = math::angle_axis(smooth_elevation, float3{-1.0f, 0.0f, 0.0f});
quaternion_type smooth_rotation = math::normalize(smooth_azimuth_rotation * smooth_elevation_rotation);
float3 smooth_view_point = focal_point + smooth_rotation * float3{0.0f, 0.0f, focal_distance};
float3 screen_position = camera->project(transform.transform.translation, viewport);
std::cout << screen_position << std::endl;
transform_type smooth_transform;
smooth_transform.translation = smooth_view_point;
//smooth_transform.translation = math::lerp(source_transform.translation, target_transform.translation, smooth_factor);
//smooth_transform.rotation = math::slerp(source_transform.rotation, target_transform.rotation, smooth_factor);
smooth_transform.rotation = smooth_rotation;
smooth_transform.scale = math::lerp(source_transform.scale, target_transform.scale, smooth_factor);
camera->set_transform(smooth_transform);
// Project tool position onto viewport
});
*/
// Cast a ray straight down from infinity at the focal point's lateral coordinates.
// std::numeric_limits<float>::infinity() actually doesn't work here
float3 focal_point = orbit_cam->get_target_focal_point();
float3 pick_origin = float3{focal_point.x, focal_point.y + 500.0f, focal_point.z};
float3 pick_direction = float3{0, -1, 0};
ray<float> picking_ray = {pick_origin, pick_direction};
float a = std::numeric_limits<float>::infinity();
bool intersection = false;
float3 pick;
registry.view<transform_component, collision_component>().each(
[&](auto entity, auto& transform, auto& collision)
{
math::transform<float> inverse_transform = math::inverse(transform.transform);
float3 origin = inverse_transform * pick_origin;
float3 direction = math::normalize(math::conjugate(transform.transform.rotation) * pick_direction);
ray<float> transformed_ray = {origin, direction};
// Broad phase AABB test
auto aabb_result = ray_aabb_intersection(transformed_ray, collision.bounds);
if (!std::get<0>(aabb_result))
{
return;
}
// Narrow phase mesh test
auto mesh_result = ray_mesh_intersection(transformed_ray, *collision.mesh);
if (std::get<0>(mesh_result))
{
intersection = true;
if (std::get<1>(mesh_result) < a)
{
a = std::get<1>(mesh_result);
pick = picking_ray.extrapolate(a);
}
}
});
// Determine FOV
float fov = math::log_lerp<float>(fov_far, fov_near, zoom_factor);
// Determine aspect ratio
float aspect_ratio = viewport[2] / viewport[3];
// 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, aspect_ratio, clip_near, clip_far);
}
void camera_system::rotate(float angle)
{
set_azimuth(azimuth + angle);
}
void camera_system::tilt(float angle)
{
set_elevation(elevation + angle);
}
if (intersection)
{
//orbit_cam->set_target_focal_point(pick);
}
void camera_system::zoom(float factor)
{
set_zoom(std::max<float>(0.0f, std::min<float>(1.0f, zoom_factor + factor)));
}
void camera_system::set_orbit_cam(::orbit_cam* orbit_cam)
void camera_system::set_camera(::camera* camera)
{
this->orbit_cam = orbit_cam;
this->camera = camera;
}
void camera_system::set_viewport(const float4& viewport)
@ -115,9 +130,54 @@ 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});
}
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});
}
void camera_system::set_zoom(float factor)
{
this->zoom_factor = factor;
}
void camera_system::set_focal_distance(float distance_near, float distance_far)
{
focal_distance_near = distance_near;
focal_distance_far = distance_far;
}
void camera_system::set_fov(float angle_near, float angle_far)
{
fov_near = angle_near;
fov_far = angle_far;
}
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;
}
void camera_system::handle_event(const mouse_moved_event& event)
{
mouse_position[0] = event.x;
mouse_position[1] = event.y;
}
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)});
}

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

@ -23,28 +23,62 @@
#include "entity-system.hpp"
#include "event/event-handler.hpp"
#include "event/input-events.hpp"
#include "event/window-events.hpp"
#include "utility/fundamental-types.hpp"
#include "math/quaternion-type.hpp"
#include "math/transform-type.hpp"
class camera;
class orbit_cam;
class camera_system:
public entity_system,
public event_handler<mouse_moved_event>
public event_handler<mouse_moved_event>,
public event_handler<window_resized_event>
{
public:
typedef math::quaternion<float> quaternion_type;
typedef math::transform<float> transform_type;
camera_system(entt::registry& registry);
virtual void update(double t, double dt);
void set_orbit_cam(orbit_cam* orbit_cam);
void rotate(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);
private:
virtual void handle_event(const mouse_moved_event& event);
virtual void handle_event(const window_resized_event& event);
orbit_cam* orbit_cam;
camera* camera;
float4 viewport;
float2 mouse_position;
float azimuth;
float elevation;
float zoom_factor;
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;
};
#endif // ANTKEEPER_CAMERA_SYSTEM_HPP

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

@ -40,6 +40,9 @@ public:
control_system(entt::registry& registry);
virtual void update(double t, double dt);
void set_invert_mouse_x(bool invert);
void set_invert_mouse_y(bool invert);
void set_orbit_cam(orbit_cam* orbit_cam);
void set_nest(::nest* nest);

+ 31
- 0
src/math/angles.hpp View File

@ -21,6 +21,7 @@
#define ANTKEEPER_MATH_ANGLES_HPP
#include "math/constants.hpp"
#include <cmath>
namespace math {
@ -45,6 +46,24 @@ T degrees(T radians);
template <class T>
T radians(T degrees);
/**
* Wraps an angle on [-180, 180].
*
* @param degrees Angle in degrees.
* @return Wrapped angle.
*/
template <class T>
T wrap_degrees(T degrees);
/**
* Wraps an angle on [-pi, pi].
*
* @param radians Angle in radians.
* @return Wrapped angle.
*/
template <class T>
T wrap_radians(T radians);
template <class T>
inline T degrees(T radians)
{
@ -57,6 +76,18 @@ inline T radians(T degrees)
return degrees * pi<T> / T(180);
}
template <class T>
inline T wrap_degrees(T degrees)
{
return std::remainder(degrees, T(360));
}
template <class T>
inline T wrap_radians(T radians)
{
return std::remainder(radians, two_pi<T>);
}
/// @}
} // namespace math

+ 20
- 0
src/math/interpolation.hpp View File

@ -20,6 +20,7 @@
#ifndef ANTKEEPER_MATH_INTERPOLATION_HPP
#define ANTKEEPER_MATH_INTERPOLATION_HPP
#include "math/constants.hpp"
#include "math/matrix-type.hpp"
#include "math/quaternion-type.hpp"
#include "math/transform-type.hpp"
@ -46,6 +47,18 @@ namespace math {
template <typename T, typename S = float>
T lerp(const T& x, const T& y, S a);
/**
* Linearly interpolates between two angles, @p x and @p y.
*
* @tparam T Value type.
* @param x Start angle, in radians.
* @param y End angle, in radians.
* @param a Interpolation ratio.
* @return Interpolated angle, in radians.
*/
template <typename T>
T lerp_angle(T x, T y, T a);
/**
* Logarithmically interpolates between @p x and @p y.
*
@ -63,6 +76,13 @@ inline T lerp(const T& x, const T& y, S a)
return (y - x) * a + x;
}
template <typename T>
T lerp_angle(T x, T y, T a)
{
T shortest_angle = std::remainder((y - x), two_pi<T>);
return std::remainder(x + shortest_angle * a, two_pi<T>);
}
template <typename T, typename S>
inline T log_lerp(const T& x, const T& y, S a)
{

+ 28
- 4
src/math/quaternion-functions.hpp View File

@ -154,6 +154,17 @@ vector mul(const quaternion& q, const vector& v);
template <class T>
quaternion<T> negate(const quaternion<T>& x);
/**
* Performs normalized linear interpolation between two quaternions.
*
* @param x First quaternion.
* @param y Second quaternion.
* @param a Interpolation factor.
* @return Interpolated quaternion.
*/
template <class T>
quaternion<T> nlerp(const quaternion<T>& x, const quaternion<T>& y, T a);
/**
* Normalizes a quaternion.
*/
@ -251,10 +262,10 @@ inline quaternion lerp(const quaternion& x, const quaternion& y, T a)
{
return
{
x.w * (T(1) - a) + y.w * a,
x.x * (T(1) - a) + y.x * a,
x.y * (T(1) - a) + y.y * a,
x.z * (T(1) - a) + y.z * a
(y.w - x.w) * a + x.w,
(y.x - x.x) * a + x.x,
(y.y - x.y) * a + x.y,
(y.z - x.z) * a + x.z
};
}
@ -328,6 +339,19 @@ inline quaternion negate(const quaternion& x)
return {-x.w, -x.x, -x.y, -x.z};
}
template <class T>
quaternion<T> nlerp(const quaternion<T>& x, const quaternion<T>& y, T a)
{
if (dot(x, y) < T(0))
{
return normalize(add(mul(x, T(1) - a), mul(y, -a)));
}
else
{
return normalize(add(mul(x, T(1) - a), mul(y, a)));
}
}
template <class T>
inline quaternion<T> normalize(const quaternion<T>& x)
{

+ 1
- 1
src/scene/scene-object.cpp View File

@ -25,7 +25,7 @@ typename scene_object_base::transform_type scene_object_base::interpolate_transf
return
{
math::lerp(x.translation, y.translation, a),
math::slerp(x.rotation, y.rotation, a),
math::nlerp(x.rotation, y.rotation, a),
math::lerp(x.scale, y.scale, a),
};
}

Loading…
Cancel
Save