AnimTestbed/3rdparty/ozz-animation/samples/framework/internal/camera.cc

280 lines
10 KiB
C++

//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#define OZZ_INCLUDE_PRIVATE_HEADER // Allows to include private headers.
#include "camera.h"
#include "ozz/base/log.h"
#include "ozz/base/maths/box.h"
#include "ozz/base/maths/math_constant.h"
#include "ozz/base/maths/math_ex.h"
#include "ozz/base/platform.h"
#include "framework/application.h"
#include "framework/imgui.h"
#include "renderer_impl.h"
using ozz::math::Float2;
using ozz::math::Float3;
using ozz::math::Float4x4;
namespace ozz {
namespace sample {
namespace internal {
// Declares camera navigation constants.
const float kDefaultDistance = 8.f;
const Float3 kDefaultCenter = Float3(0.f, .5f, 0.f);
const Float2 kDefaultAngle =
Float2(-ozz::math::kPi * 1.f / 12.f, ozz::math::kPi * 1.f / 5.f);
const float kAngleFactor = .01f;
const float kDistanceFactor = .1f;
const float kScrollFactor = .03f;
const float kPanFactor = .05f;
const float kKeyboardFactor = 100.f;
const float kNear = .01f;
const float kFar = 1000.f;
const float kFovY = ozz::math::kPi / 3.f;
const float kFrameAllZoomOut = 1.3f; // 30% bigger than the scene.
// Setups initial values.
Camera::Camera()
: projection_(Float4x4::identity()),
projection_2d_(Float4x4::identity()),
view_(Float4x4::identity()),
view_proj_(Float4x4::identity()),
angles_(kDefaultAngle),
center_(kDefaultCenter),
distance_(kDefaultDistance),
mouse_last_x_(0),
mouse_last_y_(0),
mouse_last_wheel_(0),
auto_framing_(true) {}
Camera::~Camera() {}
void Camera::Update(const math::Box& _box, float _delta_time,
bool _first_frame) {
// Frame the scene according to the provided box.
if (_box.is_valid()) {
if (auto_framing_ || _first_frame) {
center_ = (_box.max + _box.min) * .5f;
if (_first_frame) {
const float radius = Length(_box.max - _box.min) * .5f;
distance_ = radius * kFrameAllZoomOut / tanf(kFovY * .5f);
}
}
}
// Update manual controls.
const Controls controls = UpdateControls(_delta_time);
// Disable autoframing according to inputs.
auto_framing_ &=
!controls.panning && !controls.zooming && !controls.zooming_wheel;
}
void Camera::Update(const math::Float4x4& _transform, const math::Box& _box,
float _delta_time, bool _first_frame) {
// Extract distance and angles such that theu are coherent when switching out
// of auto_framing_.
if (_box.is_valid()) {
if (auto_framing_ || _first_frame) {
// Extract components from the view martrix.
ozz::math::Float3 camera_dir;
ozz::math::Store3PtrU(-ozz::math::Normalize3(_transform.cols[2]),
&camera_dir.x);
ozz::math::Float3 camera_pos;
ozz::math::Store3PtrU(_transform.cols[3], &camera_pos.x);
// Arbitrary decides that distance (focus point) is from camera to scene
// center.
const ozz::math::Float3 box_center_ = (_box.max + _box.min) * .5f;
distance_ = Length(box_center_ - camera_pos);
center_ = camera_pos + camera_dir * distance_;
angles_.x = asinf(camera_dir.y);
angles_.y = atan2(-camera_dir.x, -camera_dir.z);
}
}
// Update manual controls.
const Controls controls = UpdateControls(_delta_time);
// Disable autoframing according to inputs.
auto_framing_ &= !controls.panning && !controls.rotating &&
!controls.zooming && !controls.zooming_wheel;
if (auto_framing_) {
view_ = Invert(_transform);
}
}
Camera::Controls Camera::UpdateControls(float _delta_time) {
Controls controls;
controls.zooming = false;
controls.zooming_wheel = false;
controls.rotating = false;
controls.panning = false;
// Mouse wheel + SHIFT activates Zoom.
if (glfwGetKey(GLFW_KEY_LSHIFT) == GLFW_PRESS) {
const int w = glfwGetMouseWheel();
const int dw = w - mouse_last_wheel_;
mouse_last_wheel_ = w;
if (dw != 0) {
controls.zooming_wheel = true;
distance_ *= 1.f + -dw * kScrollFactor;
}
} else {
mouse_last_wheel_ = glfwGetMouseWheel();
}
// Fetches current mouse position and compute its movement since last frame.
int x, y;
glfwGetMousePos(&x, &y);
const int mdx = x - mouse_last_x_;
const int mdy = y - mouse_last_y_;
mouse_last_x_ = x;
mouse_last_y_ = y;
// Finds keyboard relative dx and dy commmands.
const int timed_factor =
ozz::math::Max(1, static_cast<int>(kKeyboardFactor * _delta_time));
const int kdx =
timed_factor * (glfwGetKey(GLFW_KEY_LEFT) - glfwGetKey(GLFW_KEY_RIGHT));
const int kdy =
timed_factor * (glfwGetKey(GLFW_KEY_DOWN) - glfwGetKey(GLFW_KEY_UP));
const bool keyboard_interact = kdx || kdy;
// Computes composed keyboard and mouse dx and dy.
const int dx = mdx + kdx;
const int dy = mdy + kdy;
// Mouse right button activates Zoom, Pan and Orbit modes.
if (keyboard_interact ||
glfwGetMouseButton(GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) {
if (glfwGetKey(GLFW_KEY_LSHIFT) == GLFW_PRESS) { // Zoom mode.
controls.zooming = true;
distance_ += dy * kDistanceFactor;
} else if (glfwGetKey(GLFW_KEY_LALT) == GLFW_PRESS) { // Pan mode.
controls.panning = true;
const float dx_pan = -dx * kPanFactor;
const float dy_pan = -dy * kPanFactor;
// Moves along camera axes.
math::Float4x4 transpose = Transpose(view_);
math::Float3 right_transpose, up_transpose;
math::Store3PtrU(transpose.cols[0], &right_transpose.x);
math::Store3PtrU(transpose.cols[1], &up_transpose.x);
center_ = center_ + right_transpose * dx_pan + up_transpose * dy_pan;
} else { // Orbit mode.
controls.rotating = true;
angles_.x = fmodf(angles_.x - dy * kAngleFactor, ozz::math::k2Pi);
angles_.y = fmodf(angles_.y - dx * kAngleFactor, ozz::math::k2Pi);
}
}
// Build the model view matrix components.
const Float4x4 center = Float4x4::Translation(
math::simd_float4::Load(center_.x, center_.y, center_.z, 1.f));
const Float4x4 y_rotation = Float4x4::FromAxisAngle(
math::simd_float4::y_axis(), math::simd_float4::Load1(angles_.y));
const Float4x4 x_rotation = Float4x4::FromAxisAngle(
math::simd_float4::x_axis(), math::simd_float4::Load1(angles_.x));
const Float4x4 distance =
Float4x4::Translation(math::simd_float4::Load(0.f, 0.f, distance_, 1.f));
// Concatenate view matrix components.
view_ = Invert(center * y_rotation * x_rotation * distance);
return controls;
}
void Camera::Reset(const math::Float3& _center, const math::Float2& _angles,
float _distance) {
center_ = _center;
angles_ = _angles;
distance_ = _distance;
}
void Camera::OnGui(ImGui* _im_gui) {
const char* controls_label =
"-RMB: Rotate\n"
"-Shift + Wheel: Zoom\n"
"-Shift + RMB: Zoom\n"
"-Alt + RMB: Pan\n";
_im_gui->DoLabel(controls_label, ImGui::kLeft, false);
_im_gui->DoCheckBox("Automatic", &auto_framing_);
}
void Camera::Bind3D() {
// Updates internal vp matrix.
view_proj_ = projection_ * view_;
}
void Camera::Bind2D() {
// Updates internal vp matrix. View matrix is identity.
view_proj_ = projection_2d_;
}
void Camera::Resize(int _width, int _height) {
// Handle empty windows.
if (_width <= 0 || _height <= 0) {
projection_ = ozz::math::Float4x4::identity();
projection_2d_ = ozz::math::Float4x4::identity();
return;
}
// Compute the 3D projection matrix.
const float ratio = 1.f * _width / _height;
const float h = tan(kFovY * .5f) * kNear;
const float w = h * ratio;
projection_.cols[0] = math::simd_float4::Load(kNear / w, 0.f, 0.f, 0.f);
projection_.cols[1] = math::simd_float4::Load(0.f, kNear / h, 0.f, 0.f);
projection_.cols[2] =
math::simd_float4::Load(0.f, 0.f, -(kFar + kNear) / (kFar - kNear), -1.f);
projection_.cols[3] = math::simd_float4::Load(
0.f, 0.f, -(2.f * kFar * kNear) / (kFar - kNear), 0.f);
// Computes the 2D projection matrix.
projection_2d_.cols[0] = math::simd_float4::Load(2.f / _width, 0.f, 0.f, 0.f);
projection_2d_.cols[1] =
math::simd_float4::Load(0.f, 2.f / _height, 0.f, 0.f);
projection_2d_.cols[2] = math::simd_float4::Load(0.f, 0.f, -2.0f, 0.f);
projection_2d_.cols[3] = math::simd_float4::Load(-1.f, -1.f, 0.f, 1.f);
}
} // namespace internal
} // namespace sample
} // namespace ozz