Files
Vulkan-Samples/framework/scene_graph/scripts/free_camera.cpp
T
2025-09-04 10:54:47 +08:00

242 lines
6.4 KiB
C++

/* Copyright (c) 2019-2024, Arm Limited and Contributors
* Copyright (c) 2020-2024, Andrew Cox, Huawei Technologies Research & Development (UK) Limited
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "free_camera.h"
#include "common/error.h"
#include "common/glm_common.h"
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/quaternion.hpp>
#include "scene_graph/components/perspective_camera.h"
#include "scene_graph/components/transform.h"
#include "scene_graph/node.h"
namespace vkb
{
namespace sg
{
const float FreeCamera::TOUCH_DOWN_MOVE_FORWARD_WAIT_TIME = 2.0f;
const float FreeCamera::ROTATION_MOVE_WEIGHT = 0.1f;
const float FreeCamera::KEY_ROTATION_MOVE_WEIGHT = 0.5f;
const float FreeCamera::TRANSLATION_MOVE_WEIGHT = 3.0f;
const float FreeCamera::TRANSLATION_MOVE_STEP = 50.0f;
const uint32_t FreeCamera::TRANSLATION_MOVE_SPEED = 4;
FreeCamera::FreeCamera(Node &node) :
NodeScript{node, "FreeCamera"}
{}
void FreeCamera::update(float delta_time)
{
glm::vec3 delta_translation(0.0f, 0.0f, 0.0f);
glm::vec3 delta_rotation(0.0f, 0.0f, 0.0f);
float mul_translation = speed_multiplier;
if (key_pressed[KeyCode::W])
{
delta_translation.z -= TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::S])
{
delta_translation.z += TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::A])
{
delta_translation.x -= TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::D])
{
delta_translation.x += TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::Q])
{
delta_translation.y -= TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::E])
{
delta_translation.y += TRANSLATION_MOVE_STEP;
}
if (key_pressed[KeyCode::LeftControl])
{
mul_translation *= (1.0f * TRANSLATION_MOVE_SPEED);
}
if (key_pressed[KeyCode::LeftShift])
{
mul_translation *= (1.0f / TRANSLATION_MOVE_SPEED);
}
if (key_pressed[KeyCode::I])
{
delta_rotation.x += KEY_ROTATION_MOVE_WEIGHT;
}
if (key_pressed[KeyCode::K])
{
delta_rotation.x -= KEY_ROTATION_MOVE_WEIGHT;
}
if (key_pressed[KeyCode::J])
{
delta_rotation.y += KEY_ROTATION_MOVE_WEIGHT;
}
if (key_pressed[KeyCode::L])
{
delta_rotation.y -= KEY_ROTATION_MOVE_WEIGHT;
}
if (mouse_button_pressed[MouseButton::Left] && mouse_button_pressed[MouseButton::Right])
{
delta_rotation.z += TRANSLATION_MOVE_WEIGHT * mouse_move_delta.x;
}
else if (mouse_button_pressed[MouseButton::Right])
{
delta_rotation.x -= ROTATION_MOVE_WEIGHT * mouse_move_delta.y;
delta_rotation.y -= ROTATION_MOVE_WEIGHT * mouse_move_delta.x;
}
else if (mouse_button_pressed[MouseButton::Left])
{
delta_translation.x += TRANSLATION_MOVE_WEIGHT * mouse_move_delta.x;
delta_translation.y += TRANSLATION_MOVE_WEIGHT * -mouse_move_delta.y;
}
if (touch_pointer_pressed[0])
{
delta_rotation.x -= ROTATION_MOVE_WEIGHT * touch_move_delta.y;
delta_rotation.y -= ROTATION_MOVE_WEIGHT * touch_move_delta.x;
if (touch_pointer_time > TOUCH_DOWN_MOVE_FORWARD_WAIT_TIME)
{
delta_translation.z -= TRANSLATION_MOVE_STEP;
}
else
{
touch_pointer_time += delta_time;
}
}
delta_translation *= mul_translation * delta_time;
delta_rotation *= delta_time;
// Only re-calculate the transform if it's changed
if (delta_rotation != glm::vec3(0.0f, 0.0f, 0.0f) || delta_translation != glm::vec3(0.0f, 0.0f, 0.0f))
{
auto &transform = get_node().get_component<Transform>();
glm::quat qx = glm::angleAxis(delta_rotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
glm::quat qy = glm::angleAxis(delta_rotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
glm::quat orientation = glm::normalize(qy * transform.get_rotation() * qx);
transform.set_translation(transform.get_translation() + delta_translation * glm::conjugate(orientation));
transform.set_rotation(orientation);
}
mouse_move_delta = {};
touch_move_delta = {};
}
void FreeCamera::input_event(const InputEvent &input_event)
{
if (input_event.get_source() == EventSource::Keyboard)
{
const auto &key_event = static_cast<const KeyInputEvent &>(input_event);
if (key_event.get_action() == KeyAction::Down ||
key_event.get_action() == KeyAction::Repeat)
{
key_pressed[key_event.get_code()] = true;
}
else
{
key_pressed[key_event.get_code()] = false;
}
}
else if (input_event.get_source() == EventSource::Mouse)
{
const auto &mouse_button = static_cast<const MouseButtonInputEvent &>(input_event);
glm::vec2 mouse_pos{std::floor(mouse_button.get_pos_x()), std::floor(mouse_button.get_pos_y())};
if (mouse_button.get_action() == MouseAction::Down)
{
mouse_button_pressed[mouse_button.get_button()] = true;
}
if (mouse_button.get_action() == MouseAction::Up)
{
mouse_button_pressed[mouse_button.get_button()] = false;
}
if (mouse_button.get_action() == MouseAction::Move)
{
mouse_move_delta = mouse_pos - mouse_last_pos;
mouse_last_pos = mouse_pos;
}
}
else if (input_event.get_source() == EventSource::Touchscreen)
{
const auto &touch_event = static_cast<const TouchInputEvent &>(input_event);
glm::vec2 touch_pos{std::floor(touch_event.get_pos_x()), std::floor(touch_event.get_pos_y())};
if (touch_event.get_action() == TouchAction::Down)
{
touch_pointer_pressed[touch_event.get_pointer_id()] = true;
touch_last_pos = touch_pos;
}
if (touch_event.get_action() == TouchAction::Up)
{
touch_pointer_pressed[touch_event.get_pointer_id()] = false;
touch_pointer_time = 0.0f;
}
if (touch_event.get_action() == TouchAction::Move && touch_event.get_pointer_id() == 0)
{
touch_move_delta = touch_pos - touch_last_pos;
touch_last_pos = touch_pos;
}
}
}
void FreeCamera::resize(uint32_t width, uint32_t height)
{
auto &camera_node = get_node();
if (camera_node.has_component<Camera>())
{
if (auto camera = dynamic_cast<PerspectiveCamera *>(&camera_node.get_component<Camera>()))
{
camera->set_aspect_ratio(static_cast<float>(width) / height);
}
}
}
} // namespace sg
} // namespace vkb