ADD: aspect ratio preserving picking
This commit is contained in:
@@ -178,6 +178,41 @@ VkRect2D vkutil::compute_letterbox_rect(VkExtent2D srcSize, VkExtent2D dstSize)
|
|||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool vkutil::map_window_to_letterbox_src(const glm::vec2 &windowPosPixels,
|
||||||
|
VkExtent2D srcSize,
|
||||||
|
VkExtent2D dstSize,
|
||||||
|
glm::vec2 &outSrcPosPixels)
|
||||||
|
{
|
||||||
|
outSrcPosPixels = glm::vec2{0.0f, 0.0f};
|
||||||
|
if (srcSize.width == 0 || srcSize.height == 0 || dstSize.width == 0 || dstSize.height == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkRect2D rect = compute_letterbox_rect(srcSize, dstSize);
|
||||||
|
if (rect.extent.width == 0 || rect.extent.height == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float localX = windowPosPixels.x - static_cast<float>(rect.offset.x);
|
||||||
|
const float localY = windowPosPixels.y - static_cast<float>(rect.offset.y);
|
||||||
|
|
||||||
|
if (localX < 0.0f || localY < 0.0f ||
|
||||||
|
localX >= static_cast<float>(rect.extent.width) ||
|
||||||
|
localY >= static_cast<float>(rect.extent.height))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float u = localX / static_cast<float>(rect.extent.width);
|
||||||
|
const float v = localY / static_cast<float>(rect.extent.height);
|
||||||
|
|
||||||
|
outSrcPosPixels.x = u * static_cast<float>(srcSize.width);
|
||||||
|
outSrcPosPixels.y = v * static_cast<float>(srcSize.height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void vkutil::copy_image_to_image_letterboxed(VkCommandBuffer cmd,
|
void vkutil::copy_image_to_image_letterboxed(VkCommandBuffer cmd,
|
||||||
VkImage source,
|
VkImage source,
|
||||||
VkImage destination,
|
VkImage destination,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <core/types.h>
|
#include <core/types.h>
|
||||||
|
#include <glm/vec2.hpp>
|
||||||
|
|
||||||
namespace vkutil {
|
namespace vkutil {
|
||||||
|
|
||||||
@@ -8,6 +9,13 @@ namespace vkutil {
|
|||||||
// Compute a letterboxed destination rect inside dstSize that preserves srcSize aspect ratio.
|
// Compute a letterboxed destination rect inside dstSize that preserves srcSize aspect ratio.
|
||||||
VkRect2D compute_letterbox_rect(VkExtent2D srcSize, VkExtent2D dstSize);
|
VkRect2D compute_letterbox_rect(VkExtent2D srcSize, VkExtent2D dstSize);
|
||||||
|
|
||||||
|
// Map a window-space pixel position (in dstSize, top-left origin) into pixel coordinates
|
||||||
|
// inside the letterboxed srcSize view. Returns false if the position lies in black bars.
|
||||||
|
bool map_window_to_letterbox_src(const glm::vec2 &windowPosPixels,
|
||||||
|
VkExtent2D srcSize,
|
||||||
|
VkExtent2D dstSize,
|
||||||
|
glm::vec2 &outSrcPosPixels);
|
||||||
|
|
||||||
void copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize);
|
void copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize);
|
||||||
// Blit source into a letterboxed rect in destination (preserves aspect ratio).
|
// Blit source into a letterboxed rect in destination (preserves aspect ratio).
|
||||||
void copy_image_to_image_letterboxed(VkCommandBuffer cmd,
|
void copy_image_to_image_letterboxed(VkCommandBuffer cmd,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
#include "render/passes/tonemap.h"
|
#include "render/passes/tonemap.h"
|
||||||
#include "render/passes/shadow.h"
|
#include "render/passes/shadow.h"
|
||||||
#include "device/resource.h"
|
#include "device/resource.h"
|
||||||
|
#include "device/images.h"
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "core/pipeline/manager.h"
|
#include "core/pipeline/manager.h"
|
||||||
#include "core/assets/texture_cache.h"
|
#include "core/assets/texture_cache.h"
|
||||||
@@ -893,11 +894,16 @@ void VulkanEngine::draw()
|
|||||||
VkExtent2D swapExt = _swapchainManager->swapchainExtent();
|
VkExtent2D swapExt = _swapchainManager->swapchainExtent();
|
||||||
VkExtent2D drawExt = _drawExtent;
|
VkExtent2D drawExt = _drawExtent;
|
||||||
|
|
||||||
float sx = _pendingPick.windowPos.x / float(std::max(1u, swapExt.width));
|
glm::vec2 logicalPos{};
|
||||||
float sy = _pendingPick.windowPos.y / float(std::max(1u, swapExt.height));
|
if (!vkutil::map_window_to_letterbox_src(_pendingPick.windowPos, drawExt, swapExt, logicalPos))
|
||||||
|
{
|
||||||
uint32_t idX = uint32_t(glm::clamp(sx * float(drawExt.width), 0.0f, float(drawExt.width - 1)));
|
// Click landed outside the active letterboxed region.
|
||||||
uint32_t idY = uint32_t(glm::clamp(sy * float(drawExt.height), 0.0f, float(drawExt.height - 1)));
|
_pendingPick.active = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint32_t idX = uint32_t(glm::clamp(logicalPos.x, 0.0f, float(drawExt.width - 1)));
|
||||||
|
uint32_t idY = uint32_t(glm::clamp(logicalPos.y, 0.0f, float(drawExt.height - 1)));
|
||||||
_pendingPick.idCoords = {idX, idY};
|
_pendingPick.idCoords = {idX, idY};
|
||||||
|
|
||||||
RGImportedBufferDesc bd{};
|
RGImportedBufferDesc bd{};
|
||||||
@@ -945,6 +951,7 @@ void VulkanEngine::draw()
|
|||||||
_pendingPick.active = false;
|
_pendingPick.active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (auto *lighting = _renderPassManager->getPass<LightingPass>())
|
if (auto *lighting = _renderPassManager->getPass<LightingPass>())
|
||||||
{
|
{
|
||||||
lighting->register_graph(_renderGraph.get(), hDraw, hGBufferPosition, hGBufferNormal, hGBufferAlbedo, hGBufferExtra,
|
lighting->register_graph(_renderGraph.get(), hDraw, hGBufferPosition, hGBufferNormal, hGBufferAlbedo, hGBufferExtra,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "core/pipeline/manager.h"
|
#include "core/pipeline/manager.h"
|
||||||
#include "core/assets/texture_cache.h"
|
#include "core/assets/texture_cache.h"
|
||||||
#include "core/assets/ibl_manager.h"
|
#include "core/assets/ibl_manager.h"
|
||||||
|
#include "device/images.h"
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include <core/types.h>
|
#include <core/types.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -1186,18 +1187,36 @@ namespace
|
|||||||
|
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
ImGuizmo::SetOrthographic(false);
|
ImGuizmo::SetOrthographic(false);
|
||||||
|
|
||||||
|
VkExtent2D swapExtent = eng->_swapchainManager
|
||||||
|
? eng->_swapchainManager->swapchainExtent()
|
||||||
|
: VkExtent2D{1, 1};
|
||||||
|
VkExtent2D drawExtent{
|
||||||
|
static_cast<uint32_t>(static_cast<float>(eng->_logicalRenderExtent.width) * eng->renderScale),
|
||||||
|
static_cast<uint32_t>(static_cast<float>(eng->_logicalRenderExtent.height) * eng->renderScale)
|
||||||
|
};
|
||||||
|
if (drawExtent.width == 0 || drawExtent.height == 0)
|
||||||
|
{
|
||||||
|
drawExtent = VkExtent2D{1, 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
VkRect2D activeRect = vkutil::compute_letterbox_rect(drawExtent, swapExtent);
|
||||||
|
const float fbScaleX = (io.DisplayFramebufferScale.x > 0.0f) ? io.DisplayFramebufferScale.x : 1.0f;
|
||||||
|
const float fbScaleY = (io.DisplayFramebufferScale.y > 0.0f) ? io.DisplayFramebufferScale.y : 1.0f;
|
||||||
|
const float rectX = static_cast<float>(activeRect.offset.x) / fbScaleX;
|
||||||
|
const float rectY = static_cast<float>(activeRect.offset.y) / fbScaleY;
|
||||||
|
const float rectW = static_cast<float>(activeRect.extent.width) / fbScaleX;
|
||||||
|
const float rectH = static_cast<float>(activeRect.extent.height) / fbScaleY;
|
||||||
|
|
||||||
ImGuizmo::SetDrawlist();
|
ImGuizmo::SetDrawlist();
|
||||||
ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y);
|
ImGuizmo::SetRect(rectX, rectY, rectW, rectH);
|
||||||
|
|
||||||
// Build a distance-based perspective projection for ImGuizmo instead of
|
// Build a distance-based perspective projection for ImGuizmo instead of
|
||||||
// using the engine's reversed-Z Vulkan projection.
|
// using the engine's reversed-Z Vulkan projection.
|
||||||
Camera &cam = sceneMgr->getMainCamera();
|
Camera &cam = sceneMgr->getMainCamera();
|
||||||
float fovRad = glm::radians(cam.fovDegrees);
|
float fovRad = glm::radians(cam.fovDegrees);
|
||||||
VkExtent2D extent = eng->_swapchainManager
|
float aspect = drawExtent.height > 0
|
||||||
? eng->_swapchainManager->swapchainExtent()
|
? static_cast<float>(drawExtent.width) / static_cast<float>(drawExtent.height)
|
||||||
: VkExtent2D{1, 1};
|
|
||||||
float aspect = extent.height > 0
|
|
||||||
? static_cast<float>(extent.width) / static_cast<float>(extent.height)
|
|
||||||
: 1.0f;
|
: 1.0f;
|
||||||
|
|
||||||
// Distance from camera to object; clamp to avoid degenerate planes.
|
// Distance from camera to object; clamp to avoid degenerate planes.
|
||||||
@@ -1221,7 +1240,7 @@ namespace
|
|||||||
ImDrawList* dl = ImGui::GetForegroundDrawList();
|
ImDrawList* dl = ImGui::GetForegroundDrawList();
|
||||||
ImGuizmo::SetDrawlist(dl);
|
ImGuizmo::SetDrawlist(dl);
|
||||||
|
|
||||||
ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y);
|
ImGuizmo::SetRect(rectX, rectY, rectW, rectH);
|
||||||
ImGuizmo::Manipulate(&view[0][0], &proj[0][0],
|
ImGuizmo::Manipulate(&view[0][0], &proj[0][0],
|
||||||
op, mode,
|
op, mode,
|
||||||
&targetTransform[0][0]);
|
&targetTransform[0][0]);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "vk_scene.h"
|
#include "vk_scene.h"
|
||||||
|
|
||||||
#include "core/device/swapchain.h"
|
#include "core/device/swapchain.h"
|
||||||
|
#include "core/device/images.h"
|
||||||
#include "core/context.h"
|
#include "core/context.h"
|
||||||
#include "mesh_bvh.h"
|
#include "mesh_bvh.h"
|
||||||
|
|
||||||
@@ -433,18 +434,34 @@ bool SceneManager::pick(const glm::vec2 &mousePosPixels, RenderObject &outObject
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkExtent2D extent = swapchain->windowExtent();
|
VkExtent2D dstExtent = swapchain->windowExtent();
|
||||||
if (extent.width == 0 || extent.height == 0)
|
if (dstExtent.width == 0 || dstExtent.height == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float width = static_cast<float>(extent.width);
|
VkExtent2D logicalExtent{ kRenderWidth, kRenderHeight };
|
||||||
float height = static_cast<float>(extent.height);
|
if (_context)
|
||||||
|
{
|
||||||
|
VkExtent2D ctxLogical = _context->getLogicalRenderExtent();
|
||||||
|
if (ctxLogical.width > 0 && ctxLogical.height > 0)
|
||||||
|
{
|
||||||
|
logicalExtent = ctxLogical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Convert from window coordinates (top-left origin) to NDC in [-1, 1].
|
glm::vec2 logicalPos{};
|
||||||
float ndcX = (2.0f * mousePosPixels.x / width) - 1.0f;
|
if (!vkutil::map_window_to_letterbox_src(mousePosPixels, logicalExtent, dstExtent, logicalPos))
|
||||||
float ndcY = 1.0f - (2.0f * mousePosPixels.y / height);
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float width = static_cast<float>(logicalExtent.width);
|
||||||
|
float height = static_cast<float>(logicalExtent.height);
|
||||||
|
|
||||||
|
// Convert from logical view coordinates (top-left origin) to NDC in [-1, 1].
|
||||||
|
float ndcX = (2.0f * logicalPos.x / width) - 1.0f;
|
||||||
|
float ndcY = 1.0f - (2.0f * logicalPos.y / height);
|
||||||
|
|
||||||
float fovRad = glm::radians(mainCamera.fovDegrees);
|
float fovRad = glm::radians(mainCamera.fovDegrees);
|
||||||
float tanHalfFov = std::tan(fovRad * 0.5f);
|
float tanHalfFov = std::tan(fovRad * 0.5f);
|
||||||
@@ -566,16 +583,58 @@ void SceneManager::selectRect(const glm::vec2 &p0, const glm::vec2 &p1, std::vec
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkExtent2D extent = _context->getSwapchain()->windowExtent();
|
SwapchainManager *swapchain = _context->getSwapchain();
|
||||||
if (extent.width == 0 || extent.height == 0)
|
VkExtent2D dstExtent = swapchain->windowExtent();
|
||||||
|
if (dstExtent.width == 0 || dstExtent.height == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float width = static_cast<float>(extent.width);
|
VkExtent2D logicalExtent{ kRenderWidth, kRenderHeight };
|
||||||
float height = static_cast<float>(extent.height);
|
VkExtent2D ctxLogical = _context->getLogicalRenderExtent();
|
||||||
|
if (ctxLogical.width > 0 && ctxLogical.height > 0)
|
||||||
|
{
|
||||||
|
logicalExtent = ctxLogical;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert from window coordinates (top-left origin) to NDC in [-1, 1].
|
VkRect2D activeRect = vkutil::compute_letterbox_rect(logicalExtent, dstExtent);
|
||||||
|
if (activeRect.extent.width == 0 || activeRect.extent.height == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 selMin = glm::min(p0, p1);
|
||||||
|
glm::vec2 selMax = glm::max(p0, p1);
|
||||||
|
|
||||||
|
glm::vec2 activeMin{static_cast<float>(activeRect.offset.x),
|
||||||
|
static_cast<float>(activeRect.offset.y)};
|
||||||
|
glm::vec2 activeMax{activeMin.x + static_cast<float>(activeRect.extent.width),
|
||||||
|
activeMin.y + static_cast<float>(activeRect.extent.height)};
|
||||||
|
|
||||||
|
glm::vec2 clipMin = glm::max(selMin, activeMin);
|
||||||
|
glm::vec2 clipMax = glm::min(selMax, activeMax);
|
||||||
|
if (clipMax.x <= clipMin.x || clipMax.y <= clipMin.y)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto toLogical = [&](const glm::vec2 &p) -> glm::vec2
|
||||||
|
{
|
||||||
|
float localX = p.x - activeMin.x;
|
||||||
|
float localY = p.y - activeMin.y;
|
||||||
|
float u = localX / static_cast<float>(activeRect.extent.width);
|
||||||
|
float v = localY / static_cast<float>(activeRect.extent.height);
|
||||||
|
return glm::vec2{u * static_cast<float>(logicalExtent.width),
|
||||||
|
v * static_cast<float>(logicalExtent.height)};
|
||||||
|
};
|
||||||
|
|
||||||
|
glm::vec2 logical0 = toLogical(clipMin);
|
||||||
|
glm::vec2 logical1 = toLogical(clipMax);
|
||||||
|
|
||||||
|
float width = static_cast<float>(logicalExtent.width);
|
||||||
|
float height = static_cast<float>(logicalExtent.height);
|
||||||
|
|
||||||
|
// Convert from logical view coordinates (top-left origin) to NDC in [-1, 1].
|
||||||
auto toNdc = [&](const glm::vec2 &p) -> glm::vec2
|
auto toNdc = [&](const glm::vec2 &p) -> glm::vec2
|
||||||
{
|
{
|
||||||
float ndcX = (2.0f * p.x / width) - 1.0f;
|
float ndcX = (2.0f * p.x / width) - 1.0f;
|
||||||
@@ -583,8 +642,8 @@ void SceneManager::selectRect(const glm::vec2 &p0, const glm::vec2 &p1, std::vec
|
|||||||
return glm::vec2{ndcX, ndcY};
|
return glm::vec2{ndcX, ndcY};
|
||||||
};
|
};
|
||||||
|
|
||||||
glm::vec2 ndc0 = toNdc(p0);
|
glm::vec2 ndc0 = toNdc(logical0);
|
||||||
glm::vec2 ndc1 = toNdc(p1);
|
glm::vec2 ndc1 = toNdc(logical1);
|
||||||
glm::vec2 ndcMin = glm::min(ndc0, ndc1);
|
glm::vec2 ndcMin = glm::min(ndc0, ndc1);
|
||||||
glm::vec2 ndcMax = glm::max(ndc0, ndc1);
|
glm::vec2 ndcMax = glm::max(ndc0, ndc1);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user