Skip to content

Commit

Permalink
Added an orbit/arcball-style camera in class orbit_camera
Browse files Browse the repository at this point in the history
* Added orbit_camera

* Tweaked orbit_camera and enabled both cameras in the model_loader example.

* "fixing" camera fuck ups near extrema with good ol' smoothstep

* Tweaked orbit_camera's parameters, documented how it works through source code comment.

* Added an orbit_camera to further example applications.

* Removed accidental comma

* Added want_to_occupy_mouse, begin_wanting_to_occupy_mouse, and end_wanting_to_occupy_mouse to imgui_manager,
tweaked orbit_camera (I have no idea what I'm doing)

* Accounting for FOV in orbit_camera::calculate_lateral_speed
  • Loading branch information
johannesugb committed Jun 3, 2023
1 parent c748a63 commit 424a8b7
Show file tree
Hide file tree
Showing 15 changed files with 387 additions and 91 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ set(avk_toolkit_Sources
auto_vk_toolkit/src/orca_scene.cpp
auto_vk_toolkit/src/quadratic_uniform_b_spline.cpp
auto_vk_toolkit/src/quake_camera.cpp
auto_vk_toolkit/src/orbit_camera.cpp
auto_vk_toolkit/src/swapchain_resized_event.cpp
auto_vk_toolkit/src/transform.cpp
auto_vk_toolkit/src/timer_globals.cpp
Expand Down
4 changes: 2 additions & 2 deletions auto_vk_toolkit/include/camera.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace avk
camera(const camera&) noexcept = default;
camera& operator=(camera&&) noexcept = default;
camera& operator=(const camera&) noexcept = default;
virtual ~camera();
virtual ~camera() = default;

// Returns the type of projection matrix used
enum projection_type projection_type() const { return mProjectionType; }
Expand Down Expand Up @@ -78,7 +78,7 @@ namespace avk
// calculates the z-buffer depth of the specified position in world space
float get_z_buffer_depth(const glm::vec3& aWorldSpacePosition);
// Calculates the z-buffer depth of a given transform's position
float get_z_buffer_depth(transform& aTransform);
float get_z_buffer_depth(const transform& aTransform);

// Calculates and returns the view matrix of this camera.
glm::mat4 view_matrix() const;
Expand Down
25 changes: 25 additions & 0 deletions auto_vk_toolkit/include/imgui_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,29 @@ namespace avk
mUsingSemaphoreInsteadOfFenceForFontUpload = true;
}

/** Indicates whether or not ImGui wants to occupy the mouse.
* This could be because the mouse is over a window, or currently dragging
* some ImGui control.
* \return True if ImGui wants to occupy the mouse in the current frame.
*/
bool want_to_occupy_mouse() const {
return mOccupyMouse;
}

/** Indicates whether or not ImGui wants to START occupying the mouse.
* \return True if ImGui wants to occupy the mouse in the current frame, but didn't in the previous frame.
*/
bool begin_wanting_to_occupy_mouse() const {
return mOccupyMouse && !mOccupyMouseLastFrame;
}

/** Indicates whether or not ImGui ENDS its desire for occupying the mouse.
* \return True if ImGui wanted to occupy the mouse in the previous frame, but doesn't want anymore.
*/
bool end_wanting_to_occupy_mouse() const {
return !mOccupyMouse && mOccupyMouseLastFrame;
}

private:
void upload_fonts();
void construct_render_pass();
Expand All @@ -113,6 +136,8 @@ namespace avk
bool mUserInteractionEnabled;
bool mAlreadyRendered;
bool mUsingSemaphoreInsteadOfFenceForFontUpload;
bool mOccupyMouse = false;
bool mOccupyMouseLastFrame = false;
};

}
59 changes: 59 additions & 0 deletions auto_vk_toolkit/include/orbit_camera.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

#include "camera.hpp"
#include "sequential_invoker.hpp"

namespace avk
{
// An orbit camera or arcball camera, mostly by clicking and dragging the mouse.
// Controls
// - Left click down ............... Camera rotates around rotation center if the mouse is moved
// - Right click down .............. Camera and rotation center is moved with the mouse movements.
// - Scroll ........................ Move towards or away from rotation center.
// - Alt || right click + scroll ... Move camera and rotation center in forward/back direction.
// - Ctrl .......................... Slower movements
// - Shift ......................... Faster movements
class orbit_camera : public camera, public invokee
{
public:
orbit_camera(std::string aName = "orbit_camera", bool aIsEnabled = true);
orbit_camera(orbit_camera&&) noexcept = default;
orbit_camera(const orbit_camera&) noexcept = default;
orbit_camera& operator=(orbit_camera&&) noexcept = default;
orbit_camera& operator=(const orbit_camera&) noexcept = default;
~orbit_camera() override = default;

// Invoked when this camera becomes active
void on_enable() override;
// Invoked when this camera becomes inactive
void on_disable() override;
// Invoked every frame to handle input and update the camera's position
void update() override;

/** Sets the pivot distance, which is the distance along the front vector
* of the camera which this camera orbits around.
* Note: The passed value might get clamped to min/max bounds.
* @param aDistanceFromCamera The desired distance along the front vector.
*/
void set_pivot_distance(float aDistanceFromCamera);

/** Gets the current pivot distance
* @return The currently used pivot distance.
*/
float pivot_distance() const;

private:
void calculate_lateral_speed();

protected:
float mRotationSpeed;
float mPivotDistance;
float mPivotDistanceSpeed;
float mMinPivotDistance;
float mMaxPivotDistance;
float mPivotDistanceSlowDownRange;
glm::vec2 mLateralSpeed;
float mFastMultiplier;
float mSlowMultiplier;
};
}
2 changes: 1 addition & 1 deletion auto_vk_toolkit/include/quake_camera.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace avk
quake_camera(const quake_camera&) noexcept = default;
quake_camera& operator=(quake_camera&&) noexcept = default;
quake_camera& operator=(const quake_camera&) noexcept = default;
~quake_camera();
~quake_camera() override = default;

// Invoked when this camera becomes active
void on_enable() override;
Expand Down
13 changes: 4 additions & 9 deletions auto_vk_toolkit/src/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,8 @@ namespace avk
, mTop{ 0 }
, mBottom{ 0 }
{}

camera::~camera()
{}


camera& camera::set_projection_matrix(const glm::mat4& aMatrix)

camera& camera::set_projection_matrix(const glm::mat4& aMatrix)
{
mProjectionType = projection_type::unknown;
mProjectionMatrix = aMatrix;
Expand Down Expand Up @@ -133,10 +129,9 @@ namespace avk
return depth;
}

float camera::get_z_buffer_depth(transform& aTransform)
float camera::get_z_buffer_depth(const transform& aTransform)
{
// TODO: pass transform's world space position:
return get_z_buffer_depth(glm::vec3{ 0.f, 0.f, 0.f });
return get_z_buffer_depth(aTransform.translation());
}


Expand Down
7 changes: 7 additions & 0 deletions auto_vk_toolkit/src/imgui_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ namespace avk
io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); // TODO: If the framebuffer has a different resolution as the window
io.DeltaTime = avk::time().delta_time();

mOccupyMouseLastFrame = mOccupyMouse;
if (mUserInteractionEnabled) {
mOccupyMouse = io.WantCaptureMouse || ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow);

// Cursor position:
static const auto input = []() -> input_buffer& { return composition_interface::current()->input(); };

Expand Down Expand Up @@ -255,6 +258,10 @@ namespace avk
io.AddInputCharacter(c);
}
}
else {
mOccupyMouse = false;
}

// start of new frame and callback invocations have to be in the update() call of the invokee,
// ... to give the updater an opportunity to clean up (callbacks themselves may cause update events)
mAlreadyRendered = false;
Expand Down
124 changes: 124 additions & 0 deletions auto_vk_toolkit/src/orbit_camera.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "orbit_camera.hpp"
#include "composition_interface.hpp"
#include "timer_interface.hpp"
#include <glm/gtx/quaternion.hpp>

namespace avk
{
orbit_camera::orbit_camera(std::string aName, bool aIsEnabled)
: invokee(std::move(aName), aIsEnabled)
, mRotationSpeed(0.002f)
, mPivotDistance{ 10.f }
, mPivotDistanceSpeed{ .5f }
, mMinPivotDistance{ 1.f }
, mMaxPivotDistance{ 30.f }
, mPivotDistanceSlowDownRange{ 10.f }
, mLateralSpeed{ 1.f }
, mFastMultiplier(6.0f)
, mSlowMultiplier(0.2f)
{
}

void orbit_camera::on_enable()
{
calculate_lateral_speed();
}

void orbit_camera::on_disable()
{
}

void orbit_camera::update()
{
static const auto input = []() -> input_buffer& { return composition_interface::current()->input(); };

auto deltaCursor = input().delta_cursor_position();
auto deltaTime = time().delta_time();

// query the position of the mouse cursor
auto mousePos = input().cursor_position();

// calculate how much the cursor has moved from the center of the screen
auto mouseMoved = deltaCursor;

constexpr uint8_t LMB = 0;
constexpr uint8_t RMB = 1;
if (input().mouse_button_down(LMB)) {
auto rotSpeed = mRotationSpeed
* ((input().key_down(key_code::left_shift) || input().key_down(key_code::right_shift)) ? mFastMultiplier : 1.f)
* ((input().key_down(key_code::left_control) || input().key_down(key_code::right_control)) ? mSlowMultiplier : 1.f);

glm::quat rotHoriz = glm::angleAxis(rotSpeed * static_cast<float>(mouseMoved.x), up());

rotSpeed = rotSpeed
* glm::smoothstep(.999f, 0.707f, glm::dot(up(), back(*this)))
* glm::smoothstep(.999f, 0.707f, glm::dot(up(), front(*this)));
glm::quat rotVert = glm::angleAxis(rotSpeed * static_cast<float>(mouseMoved.y), right(*this));

const auto oldPos = back(*this) * mPivotDistance;
const auto newPos = (rotHoriz * rotVert) * oldPos;
translate(*this, newPos - oldPos);
look_along(-newPos);
}
if (input().mouse_button_down(RMB)) {
const auto latSpeed = mLateralSpeed
* ((input().key_down(key_code::left_shift) || input().key_down(key_code::right_shift)) ? mFastMultiplier : 1.f)
* ((input().key_down(key_code::left_control) || input().key_down(key_code::right_control)) ? mSlowMultiplier : 1.f);

const auto t
= down(*this) * static_cast<float>(deltaCursor.y) * latSpeed.y
+ right(*this) * static_cast<float>(deltaCursor.x) * latSpeed.x;
translate(*this, t);
}

const auto scrollDist = static_cast<float>(input().scroll_delta().y);
const auto pivDistSpeed = mPivotDistanceSpeed
* ((input().key_down(key_code::left_shift) || input().key_down(key_code::right_shift)) ? mFastMultiplier : 1.f)
* ((input().key_down(key_code::left_control) || input().key_down(key_code::right_control)) ? mSlowMultiplier : 1.f);

if (input().mouse_button_down(RMB) || input().key_down(key_code::left_alt) || input().key_down(key_code::right_alt)) {
// Move pivot along with the camera
translate(*this, front(*this) * scrollDist * pivDistSpeed);
// ...and leave mPivotDistance unchanged.
}
else {
// Move camera towards/away from camera
const auto moveCloser = scrollDist > 0.f;
const auto moveAway = scrollDist < 0.f;

if (moveCloser) {
auto len = glm::smoothstep(mMinPivotDistance, mMinPivotDistance + mPivotDistanceSlowDownRange, mPivotDistance);
auto move = front(*this) * len * pivDistSpeed;
translate(*this, move);
mPivotDistance -= len * pivDistSpeed;
calculate_lateral_speed();
}
if (moveAway) {
auto len = glm::smoothstep(mMaxPivotDistance, mMaxPivotDistance - mPivotDistanceSlowDownRange, mPivotDistance);
auto move = back(*this) * len * pivDistSpeed;
translate(*this, move);
mPivotDistance += len * pivDistSpeed;
calculate_lateral_speed();
}
}
}

void orbit_camera::set_pivot_distance(float aDistanceFromCamera) {
mPivotDistance = glm::clamp(aDistanceFromCamera, mMinPivotDistance, mMaxPivotDistance);
calculate_lateral_speed();
}

float orbit_camera::pivot_distance() const {
return mPivotDistance;
}

void orbit_camera::calculate_lateral_speed()
{
const auto* wnd = context().main_window();
auto resi = nullptr != wnd ? wnd->resolution() : glm::uvec2{1920, 1080};
mLateralSpeed = glm::vec2{ // This v accounts for different FOVs
mPivotDistance / static_cast<float>(resi.x) / (near_plane_distance() * -projection_matrix()[1][1]),
mPivotDistance / static_cast<float>(resi.x) / (near_plane_distance() * -projection_matrix()[1][1])
}; // No idea why there ^ must .x for both axes. *shrug*
}
}
8 changes: 2 additions & 6 deletions auto_vk_toolkit/src/quake_camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ namespace avk
, mSlowMultiplier(0.2f) // 0.9 m/s
{
}

quake_camera::~quake_camera()
{
}


void quake_camera::on_enable()
{
composition_interface::current()->input().set_cursor_mode(cursor::cursor_disabled_raw_input);
Expand All @@ -30,7 +26,7 @@ namespace avk

void quake_camera::update()
{
static const auto input = []() { return composition_interface::current()->input(); };
static const auto input = []()->input_buffer& { return composition_interface::current()->input(); };
// display info about myself
if (input().key_pressed(key_code::i)
&& (input().key_down(key_code::left_control) || input().key_down(key_code::right_control))) {
Expand Down
Loading

0 comments on commit 424a8b7

Please sign in to comment.