feat: add imguizmo + trigger buxfix
This commit is contained in:
+13
-3
@@ -170,6 +170,14 @@ void App::run()
|
||||
|
||||
if (!isRunning) break;
|
||||
|
||||
if (window.IsMinimized())
|
||||
{
|
||||
// The surface is 0x0 while minimized, there is nothing to render or present to
|
||||
accumulator = 0.0f;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Consume SDL events before updating the base camera so held and
|
||||
// newly pressed inputs are applied in the same frame.
|
||||
camera.Update(dt);
|
||||
@@ -290,11 +298,13 @@ if (!isRunning) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
spdlog::info("Recreating swapchain to size: {}x{}", w, h);
|
||||
|
||||
{
|
||||
ZoneScopedN("Recreate Swapchain");
|
||||
gfxDevice.recreateSwapchain(w, h);
|
||||
if (!gfxDevice.recreateSwapchain(w, h))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
spdlog::info("Recreated swapchain at {}x{}", w, h);
|
||||
imguiPass.onSwapchainRecreated();
|
||||
onWindowResize(w, h);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ void Camera::Update(float deltaTime) {
|
||||
if (input.IsKeyDown(SDL_SCANCODE_LEFT)) m_yaw -= keyLookSpeed * deltaTime;
|
||||
if (input.IsKeyDown(SDL_SCANCODE_RIGHT)) m_yaw += keyLookSpeed * deltaTime;
|
||||
|
||||
if (input.IsMouseDown(SDL_BUTTON_RIGHT)) {
|
||||
m_yaw += static_cast<float>(input.MouseDeltaX()) * m_mouseSensitivity;
|
||||
m_pitch -= static_cast<float>(input.MouseDeltaY()) * m_mouseSensitivity;
|
||||
}
|
||||
|
||||
if (pad0 >= 0) {
|
||||
const float rx = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
const float ry = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
|
||||
@@ -96,7 +96,7 @@ void GfxDevice::cleanup() {
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
void GfxDevice::recreateSwapchain(int width, int height) {
|
||||
bool GfxDevice::recreateSwapchain(int width, int height) {
|
||||
if (!initialized) {
|
||||
throw std::logic_error("GfxDevice::recreateSwapchain called before init");
|
||||
}
|
||||
@@ -105,7 +105,7 @@ void GfxDevice::recreateSwapchain(int width, int height) {
|
||||
}
|
||||
waitIdle();
|
||||
|
||||
swapchain.recreateSwapchain(
|
||||
const bool recreated = swapchain.recreateSwapchain(
|
||||
instanceManager.getDevice(),
|
||||
instanceManager.getVkbDevice(),
|
||||
instanceManager.getSurface(),
|
||||
@@ -115,6 +115,7 @@ void GfxDevice::recreateSwapchain(int width, int height) {
|
||||
m_vSync);
|
||||
|
||||
swapchainFormat = swapchain.getFormat();
|
||||
return recreated;
|
||||
}
|
||||
|
||||
VkCommandBuffer GfxDevice::beginFrame()
|
||||
|
||||
@@ -107,7 +107,7 @@ void Swapchain::createSwapchain(
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
void Swapchain::recreateSwapchain(
|
||||
bool Swapchain::recreateSwapchain(
|
||||
VkDevice,
|
||||
vkb::Device vkbDevice,
|
||||
VkSurfaceKHR surf,
|
||||
@@ -120,7 +120,15 @@ void Swapchain::recreateSwapchain(
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
dirty = true;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// A minimized window can still report its old size while the surface is 0x0
|
||||
VkSurfaceCapabilitiesKHR capabilities{};
|
||||
if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkbDevice.physical_device, surf, &capabilities) == VK_SUCCESS &&
|
||||
(capabilities.currentExtent.width == 0 || capabilities.currentExtent.height == 0)) {
|
||||
dirty = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
surface = surf;
|
||||
@@ -209,6 +217,8 @@ void Swapchain::recreateSwapchain(
|
||||
|
||||
extent = m_swapchain.extent;
|
||||
dirty = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Swapchain::cleanup(VkDevice device) {
|
||||
|
||||
@@ -16,7 +16,9 @@ Transform& Component::GetTransform() const {
|
||||
|
||||
void Component::Destroy() {
|
||||
Object::Destroy();
|
||||
NotifyPhysicsChanged();
|
||||
if (AffectsPhysics()) {
|
||||
NotifyPhysicsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Component::Start() {
|
||||
@@ -24,17 +26,14 @@ void Component::Start() {
|
||||
|
||||
void Component::SetEnabled(bool enabled) {
|
||||
m_IsEnabled = enabled;
|
||||
NotifyPhysicsChanged();
|
||||
if (AffectsPhysics()) {
|
||||
NotifyPhysicsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Component::NotifyPhysicsChanged() {
|
||||
if (m_ParentGameObjectPtr != nullptr) {
|
||||
if (auto* rigidbody = m_ParentGameObjectPtr->GetComponent<Rigidbody>();
|
||||
rigidbody != nullptr && rigidbody->GetPhysicsWorld() != nullptr) {
|
||||
rigidbody->GetPhysicsWorld()->RefreshRigidbody(*rigidbody);
|
||||
} else {
|
||||
m_ParentGameObjectPtr->RefreshPhysics();
|
||||
}
|
||||
m_ParentGameObjectPtr->RefreshPhysics();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -196,8 +196,41 @@ void GameObject::CleanupComponents() {
|
||||
});
|
||||
}
|
||||
|
||||
void GameObject::RefreshPhysics() {
|
||||
if (m_Scene != nullptr) {
|
||||
m_Scene->RefreshPhysics();
|
||||
namespace {
|
||||
void RefreshBody(GameObject& object, Scene* scene) {
|
||||
auto* rigidbody = object.GetComponent<Rigidbody>();
|
||||
if (rigidbody == nullptr || rigidbody->IsBeingDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PhysicsWorld* world = rigidbody->GetPhysicsWorld()) {
|
||||
world->RefreshRigidbody(*rigidbody);
|
||||
} else if (scene != nullptr) {
|
||||
scene->GetPhysics().RefreshGameObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshChildBodies(const Transform& transform, Scene* scene) {
|
||||
for (Transform* child : transform.GetChildren()) {
|
||||
if (GameObject* owner = child->GetOwner()) {
|
||||
RefreshBody(*owner, scene);
|
||||
}
|
||||
RefreshChildBodies(*child, scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::RefreshPhysics() {
|
||||
for (Transform* transform = &m_TransformPtr; transform != nullptr; transform = transform->GetParent()) {
|
||||
GameObject* owner = transform->GetOwner();
|
||||
if (owner != nullptr && owner->GetComponent<Rigidbody>() != nullptr) {
|
||||
RefreshBody(*owner, m_Scene);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::RefreshPhysicsInHierarchy() {
|
||||
RefreshPhysics();
|
||||
RefreshChildBodies(m_TransformPtr, m_Scene);
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ void Transform::SetParent(Transform* parent, bool useWorldPosition) {
|
||||
|
||||
if (m_Owner != nullptr) {
|
||||
m_Owner->SetActiveDirty();
|
||||
m_Owner->RefreshPhysics();
|
||||
m_Owner->RefreshPhysicsInHierarchy();
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -362,10 +362,11 @@ namespace
|
||||
m_Overlaps.clear();
|
||||
}
|
||||
|
||||
std::unordered_map<BodyPair, PairData, PairHash> SwapOverlaps()
|
||||
// Persisted contacts are not reported again, so the set has to outlive each step
|
||||
std::unordered_map<BodyPair, PairData, PairHash> SnapshotOverlaps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
return std::move(m_Overlaps);
|
||||
return m_Overlaps;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -752,7 +753,7 @@ private:
|
||||
|
||||
void ProcessSensorOverlaps()
|
||||
{
|
||||
const auto currentOverlaps = m_SensorListener.SwapOverlaps();
|
||||
auto currentOverlaps = m_SensorListener.SnapshotOverlaps();
|
||||
|
||||
for (const auto& [pair, data] : currentOverlaps)
|
||||
{
|
||||
|
||||
@@ -277,7 +277,7 @@ namespace ImGuiUtils {
|
||||
}
|
||||
|
||||
if (transformChanged) {
|
||||
selectedObject->RefreshPhysics();
|
||||
selectedObject->RefreshPhysicsInHierarchy();
|
||||
}
|
||||
|
||||
const glm::vec3& worldPosition = transform.GetWorldPosition();
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
#include <destrum/Util/TransformGizmo.h>
|
||||
|
||||
#include <destrum/Graphics/Camera.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtx/matrix_decompose.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <ImGuizmo.h>
|
||||
|
||||
namespace {
|
||||
ImGuizmo::OPERATION toImGuizmo(GizmoOperation operation)
|
||||
{
|
||||
switch (operation) {
|
||||
case GizmoOperation::Rotate:
|
||||
return ImGuizmo::ROTATE;
|
||||
case GizmoOperation::Scale:
|
||||
return ImGuizmo::SCALE;
|
||||
case GizmoOperation::Translate:
|
||||
break;
|
||||
}
|
||||
return ImGuizmo::TRANSLATE;
|
||||
}
|
||||
}
|
||||
|
||||
void TransformGizmo::HandleShortcuts()
|
||||
{
|
||||
if (ImGui::GetIO().WantTextInput || ImGuizmo::IsUsing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_T, false)) {
|
||||
operation = GizmoOperation::Translate;
|
||||
}
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_R, false)) {
|
||||
operation = GizmoOperation::Rotate;
|
||||
}
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Y, false)) {
|
||||
operation = GizmoOperation::Scale;
|
||||
}
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_L, false)) {
|
||||
space = space == GizmoSpace::World ? GizmoSpace::Local : GizmoSpace::World;
|
||||
}
|
||||
}
|
||||
|
||||
void TransformGizmo::DrawToolbar()
|
||||
{
|
||||
if (!ImGui::Begin("Gizmo", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto operationButton = [this](const char* label, GizmoOperation buttonOperation) {
|
||||
if (ImGui::RadioButton(label, operation == buttonOperation)) {
|
||||
operation = buttonOperation;
|
||||
}
|
||||
};
|
||||
operationButton("Move (T)", GizmoOperation::Translate);
|
||||
ImGui::SameLine();
|
||||
operationButton("Rotate (R)", GizmoOperation::Rotate);
|
||||
ImGui::SameLine();
|
||||
operationButton("Scale (Y)", GizmoOperation::Scale);
|
||||
|
||||
if (operation != GizmoOperation::Scale) {
|
||||
bool local = space == GizmoSpace::Local;
|
||||
if (ImGui::Checkbox("Local (L)", &local)) {
|
||||
space = local ? GizmoSpace::Local : GizmoSpace::World;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Snap", &snap);
|
||||
if (snap) {
|
||||
ImGui::SetNextItemWidth(80.0f);
|
||||
switch (operation) {
|
||||
case GizmoOperation::Translate:
|
||||
ImGui::DragFloat("Step", &translationSnap, 0.05f, 0.01f, 10.0f, "%.2f m");
|
||||
break;
|
||||
case GizmoOperation::Rotate:
|
||||
ImGui::DragFloat("Step", &rotationSnapDegrees, 1.0f, 1.0f, 90.0f, "%.0f deg");
|
||||
break;
|
||||
case GizmoOperation::Scale:
|
||||
ImGui::DragFloat("Step", &scaleSnap, 0.01f, 0.01f, 1.0f, "%.2f");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
bool TransformGizmo::Manipulate(GameObject& object, const Camera& camera)
|
||||
{
|
||||
ImGuizmo::BeginFrame();
|
||||
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGuizmo::SetOrthographic(false);
|
||||
ImGuizmo::SetRect(viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y);
|
||||
|
||||
const glm::mat4 view = camera.GetViewMatrix();
|
||||
glm::mat4 projection = camera.GetProjectionMatrix();
|
||||
// ImGuizmo expects OpenGL's y up clip space, the engine flips y for Vulkan
|
||||
projection[1][1] *= -1.0f;
|
||||
|
||||
Transform& transform = object.GetTransform();
|
||||
glm::mat4 worldMatrix = transform.GetWorldMatrix();
|
||||
|
||||
const float snapStep = operation == GizmoOperation::Translate ? translationSnap
|
||||
: operation == GizmoOperation::Rotate ? rotationSnapDegrees
|
||||
: scaleSnap;
|
||||
const float snapValues[3] = {snapStep, snapStep, snapStep};
|
||||
const ImGuizmo::MODE mode =
|
||||
operation == GizmoOperation::Scale || space == GizmoSpace::Local ? ImGuizmo::LOCAL : ImGuizmo::WORLD;
|
||||
|
||||
const bool changed = ImGuizmo::Manipulate(
|
||||
glm::value_ptr(view),
|
||||
glm::value_ptr(projection),
|
||||
toImGuizmo(operation),
|
||||
mode,
|
||||
glm::value_ptr(worldMatrix),
|
||||
nullptr,
|
||||
snap ? snapValues : nullptr);
|
||||
|
||||
// Stops the click on a handle from reaching the game, e.g. grabbing the mouse for first person
|
||||
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
|
||||
ImGui::SetNextFrameWantCaptureMouse(true);
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::vec3 position;
|
||||
glm::quat rotation;
|
||||
glm::vec3 scale;
|
||||
glm::vec3 skew;
|
||||
glm::vec4 perspective;
|
||||
if (!glm::decompose(worldMatrix, scale, rotation, position, skew, perspective)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (operation) {
|
||||
case GizmoOperation::Translate:
|
||||
transform.SetWorldPosition(position);
|
||||
break;
|
||||
case GizmoOperation::Rotate:
|
||||
transform.SetWorldRotation(rotation);
|
||||
break;
|
||||
case GizmoOperation::Scale:
|
||||
transform.SetWorldScale(scale);
|
||||
break;
|
||||
}
|
||||
|
||||
object.RefreshPhysicsInHierarchy();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransformGizmo::IsUsing() const
|
||||
{
|
||||
return ImGuizmo::IsUsing();
|
||||
}
|
||||
|
||||
bool TransformGizmo::IsOver() const
|
||||
{
|
||||
return ImGuizmo::IsOver();
|
||||
}
|
||||
Reference in New Issue
Block a user