feat: add click to select
This commit is contained in:
@@ -11,7 +11,7 @@ struct InstanceData {
|
|||||||
mat4 transform;
|
mat4 transform;
|
||||||
VertexBuffer vertexBuffer;
|
VertexBuffer vertexBuffer;
|
||||||
uint materialID;
|
uint materialID;
|
||||||
uint padding;
|
uint objectID;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout (buffer_reference, scalar) readonly buffer InstanceBuffer {
|
layout (buffer_reference, scalar) readonly buffer InstanceBuffer {
|
||||||
|
|||||||
@@ -13,9 +13,23 @@ layout (location = 1) in vec2 inUV;
|
|||||||
layout (location = 2) in vec3 inNormal;
|
layout (location = 2) in vec3 inNormal;
|
||||||
layout (location = 3) in vec4 inTangent;
|
layout (location = 3) in vec4 inTangent;
|
||||||
layout (location = 4) flat in uint inMaterialID;
|
layout (location = 4) flat in uint inMaterialID;
|
||||||
|
layout (location = 5) flat in uint inObjectID;
|
||||||
|
|
||||||
layout (location = 0) out vec4 outFragColor;
|
layout (location = 0) out vec4 outFragColor;
|
||||||
|
|
||||||
|
// Stable per object, the same object keeps its color between frames and runs
|
||||||
|
vec3 selectionColor(uint objectID)
|
||||||
|
{
|
||||||
|
if (objectID == 0u) {
|
||||||
|
return vec3(0.0);
|
||||||
|
}
|
||||||
|
uint hash = objectID * 2654435761u;
|
||||||
|
hash ^= hash >> 15;
|
||||||
|
hash *= 2246822519u;
|
||||||
|
hash ^= hash >> 13;
|
||||||
|
return vec3(hash & 255u, (hash >> 8) & 255u, (hash >> 16) & 255u) / 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
vec3 calculateNormal(MaterialData material, uint samplerID)
|
vec3 calculateNormal(MaterialData material, uint samplerID)
|
||||||
{
|
{
|
||||||
vec3 n = normalize(inNormal);
|
vec3 n = normalize(inNormal);
|
||||||
@@ -127,6 +141,10 @@ void main()
|
|||||||
fragColor = mix(fragColor, lightHeatmapColor(tileLightCount), 0.5);
|
fragColor = mix(fragColor, lightHeatmapColor(tileLightCount), 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pcs.sceneData.debugSelectionView != 0) {
|
||||||
|
fragColor = selectionColor(inObjectID);
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// TANGENT DEBUG
|
// TANGENT DEBUG
|
||||||
if (inTangent == vec4(0.0)) {
|
if (inTangent == vec4(0.0)) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ layout (location = 1) out vec2 outUV;
|
|||||||
layout (location = 2) out vec3 outNormal;
|
layout (location = 2) out vec3 outNormal;
|
||||||
layout (location = 3) out vec4 outTangent;
|
layout (location = 3) out vec4 outTangent;
|
||||||
layout (location = 4) flat out uint outMaterialID;
|
layout (location = 4) flat out uint outMaterialID;
|
||||||
|
layout (location = 5) flat out uint outObjectID;
|
||||||
|
|
||||||
// The depth pre-pass reuses this shader, positions have to match bit for bit
|
// The depth pre-pass reuses this shader, positions have to match bit for bit
|
||||||
// so the main pass can depth test against it with LESS_OR_EQUAL
|
// so the main pass can depth test against it with LESS_OR_EQUAL
|
||||||
@@ -34,4 +35,5 @@ void main()
|
|||||||
// w is the bitangent sign, the fragment shader builds the TBN from the interpolated vectors
|
// w is the bitangent sign, the fragment shader builds the TBN from the interpolated vectors
|
||||||
outTangent = vec4(mat3(instance.transform) * v.tangent.xyz, v.tangent.w);
|
outTangent = vec4(mat3(instance.transform) * v.tangent.xyz, v.tangent.w);
|
||||||
outMaterialID = instance.materialID;
|
outMaterialID = instance.materialID;
|
||||||
|
outObjectID = instance.objectID;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
|
|||||||
// directional shadows
|
// directional shadows
|
||||||
uint shadowMapId;
|
uint shadowMapId;
|
||||||
float shadowNormalOffset;
|
float shadowNormalOffset;
|
||||||
uint padding;
|
uint debugSelectionView;
|
||||||
mat4 lightViewProj;
|
mat4 lightViewProj;
|
||||||
|
|
||||||
// spot shadows
|
// spot shadows
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
// Same interface as mesh.vert, only the object ID is used
|
||||||
|
layout (location = 0) in vec3 inPos;
|
||||||
|
layout (location = 1) in vec2 inUV;
|
||||||
|
layout (location = 2) in vec3 inNormal;
|
||||||
|
layout (location = 3) in vec4 inTangent;
|
||||||
|
layout (location = 4) flat in uint inMaterialID;
|
||||||
|
layout (location = 5) flat in uint inObjectID;
|
||||||
|
|
||||||
|
layout (location = 0) out uint outObjectID;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
outObjectID = inObjectID;
|
||||||
|
}
|
||||||
@@ -123,12 +123,16 @@ private:
|
|||||||
void drawDockSpace();
|
void drawDockSpace();
|
||||||
void drawViewportWindow();
|
void drawViewportWindow();
|
||||||
void applyViewportResize();
|
void applyViewportResize();
|
||||||
|
void requestSelectionPick();
|
||||||
|
void applySelectionPick();
|
||||||
|
[[nodiscard]] bool canPickInViewport() const { return usesEditorViewport() && (!playing || paused); }
|
||||||
|
|
||||||
ViewportRect viewportRect{};
|
ViewportRect viewportRect{};
|
||||||
glm::ivec2 viewportDesiredSize{0};
|
glm::ivec2 viewportDesiredSize{0};
|
||||||
bool viewportHovered{false};
|
bool viewportHovered{false};
|
||||||
bool viewportFocused{false};
|
bool viewportFocused{false};
|
||||||
bool viewportMouseClaimed{false};
|
bool viewportMouseClaimed{false};
|
||||||
|
bool pickSelectsParent{false};
|
||||||
|
|
||||||
void drawPlayControls();
|
void drawPlayControls();
|
||||||
void applyPlayModeToggle();
|
void applyPlayModeToggle();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ struct GPUInstanceData {
|
|||||||
glm::mat4 transform;
|
glm::mat4 transform;
|
||||||
VkDeviceAddress vertexBuffer;
|
VkDeviceAddress vertexBuffer;
|
||||||
std::uint32_t materialId;
|
std::uint32_t materialId;
|
||||||
std::uint32_t padding;
|
std::uint32_t objectId;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(GPUInstanceData) == 80);
|
static_assert(sizeof(GPUInstanceData) == 80);
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ public:
|
|||||||
VkDeviceSize offset = 0,
|
VkDeviceSize offset = 0,
|
||||||
VkDeviceSize size = VK_WHOLE_SIZE) const;
|
VkDeviceSize size = VK_WHOLE_SIZE) const;
|
||||||
|
|
||||||
|
void invalidateAllocation(
|
||||||
|
const GPUBuffer& buffer,
|
||||||
|
VkDeviceSize offset = 0,
|
||||||
|
VkDeviceSize size = VK_WHOLE_SIZE) const;
|
||||||
|
|
||||||
[[nodiscard]] VkDeviceAddress getBufferAddress(const GPUBuffer& buffer) const;
|
[[nodiscard]] VkDeviceAddress getBufferAddress(const GPUBuffer& buffer) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ struct MeshDrawCommand {
|
|||||||
// skinned meshes only
|
// skinned meshes only
|
||||||
const SkinnedMesh* skinnedMesh{nullptr};
|
const SkinnedMesh* skinnedMesh{nullptr};
|
||||||
std::uint32_t jointMatricesStartIndex{0};
|
std::uint32_t jointMatricesStartIndex{0};
|
||||||
|
|
||||||
|
std::uint32_t objectId{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //MESHDRAWCOMMAND_H
|
#endif //MESHDRAWCOMMAND_H
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ public:
|
|||||||
GfxDevice& gfxDevice,
|
GfxDevice& gfxDevice,
|
||||||
RenderResources& resources,
|
RenderResources& resources,
|
||||||
VkFormat drawImageFormat,
|
VkFormat drawImageFormat,
|
||||||
VkFormat depthImageFormat);
|
VkFormat depthImageFormat,
|
||||||
|
VkFormat selectionImageFormat);
|
||||||
|
|
||||||
void drawDepthPrepass(
|
void drawDepthPrepass(
|
||||||
VkCommandBuffer cmd,
|
VkCommandBuffer cmd,
|
||||||
@@ -32,7 +33,8 @@ public:
|
|||||||
const RenderResources& resources,
|
const RenderResources& resources,
|
||||||
const GPUBuffer& sceneDataBuffer,
|
const GPUBuffer& sceneDataBuffer,
|
||||||
const IndirectDrawList& drawList,
|
const IndirectDrawList& drawList,
|
||||||
const IndirectDrawList::View& view);
|
const IndirectDrawList::View& view,
|
||||||
|
bool writeObjectIds);
|
||||||
|
|
||||||
void draw(
|
void draw(
|
||||||
VkCommandBuffer cmd,
|
VkCommandBuffer cmd,
|
||||||
@@ -71,6 +73,7 @@ private:
|
|||||||
VkPipelineLayout m_pipelineLayout{VK_NULL_HANDLE};
|
VkPipelineLayout m_pipelineLayout{VK_NULL_HANDLE};
|
||||||
std::unique_ptr<Pipeline> m_pipeline;
|
std::unique_ptr<Pipeline> m_pipeline;
|
||||||
std::unique_ptr<Pipeline> m_depthPrepassPipeline;
|
std::unique_ptr<Pipeline> m_depthPrepassPipeline;
|
||||||
|
std::unique_ptr<Pipeline> m_selectionPrepassPipeline;
|
||||||
|
|
||||||
bool m_renderWireframe{false};
|
bool m_renderWireframe{false};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define RENDERER_H
|
#define RENDERER_H
|
||||||
|
|
||||||
#include <glm/mat4x4.hpp>
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/vec2.hpp>
|
||||||
#include <glm/vec3.hpp>
|
#include <glm/vec3.hpp>
|
||||||
|
|
||||||
#include <destrum/Graphics/Camera.h>
|
#include <destrum/Graphics/Camera.h>
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <destrum/Graphics/Pipelines/SkinningPipeline.h>
|
#include <destrum/Graphics/Pipelines/SkinningPipeline.h>
|
||||||
@@ -58,9 +60,9 @@ public:
|
|||||||
void draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData);
|
void draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData);
|
||||||
void cleanup(GfxDevice& gfxDevice);
|
void cleanup(GfxDevice& gfxDevice);
|
||||||
|
|
||||||
void drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId);
|
void drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId, std::uint32_t objectId = 0);
|
||||||
void drawSkinnedMesh(MeshID id, const glm::mat4& transform, MaterialID materialId, SkinnedMesh* skinnedMesh,
|
void drawSkinnedMesh(MeshID id, const glm::mat4& transform, MaterialID materialId, SkinnedMesh* skinnedMesh,
|
||||||
std::size_t jointMatricesStartIndex);
|
std::size_t jointMatricesStartIndex, std::uint32_t objectId = 0);
|
||||||
void submitLight(const Light& light);
|
void submitLight(const Light& light);
|
||||||
const GPUImage& getDrawImage() const;
|
const GPUImage& getDrawImage() const;
|
||||||
|
|
||||||
@@ -101,6 +103,31 @@ public:
|
|||||||
return renderLightHeatmap;
|
return renderLightHeatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setRenderSelectionView(bool selectionView)
|
||||||
|
{
|
||||||
|
renderSelectionView = selectionView;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getRenderSelectionView() const
|
||||||
|
{
|
||||||
|
return renderSelectionView;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSelectionPassEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
selectionPassEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void requestSelectionPick(const glm::ivec2& pixel)
|
||||||
|
{
|
||||||
|
requestedPick = pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<std::uint32_t> takeSelectionPick()
|
||||||
|
{
|
||||||
|
return std::exchange(pickResult, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
void setShadowDistance(float distance)
|
void setShadowDistance(float distance)
|
||||||
{
|
{
|
||||||
shadowMapPipeline->setShadowDistance(distance);
|
shadowMapPipeline->setShadowDistance(distance);
|
||||||
@@ -126,6 +153,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate);
|
void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate);
|
||||||
void uploadLights(const Camera& camera);
|
void uploadLights(const Camera& camera);
|
||||||
|
void recordSelectionPick(VkCommandBuffer cmd, std::uint32_t frameIndex);
|
||||||
[[nodiscard]] std::uint32_t assignShadowIndex(const Light& light, const Frustum& cameraFrustum);
|
[[nodiscard]] std::uint32_t assignShadowIndex(const Light& light, const Frustum& cameraFrustum);
|
||||||
|
|
||||||
RenderResources* resources = nullptr;
|
RenderResources* resources = nullptr;
|
||||||
@@ -142,9 +170,18 @@ private:
|
|||||||
|
|
||||||
VkFormat drawImageFormat{VK_FORMAT_R16G16B16A16_SFLOAT};
|
VkFormat drawImageFormat{VK_FORMAT_R16G16B16A16_SFLOAT};
|
||||||
VkFormat depthImageFormat{VK_FORMAT_D32_SFLOAT};
|
VkFormat depthImageFormat{VK_FORMAT_D32_SFLOAT};
|
||||||
|
VkFormat selectionImageFormat{VK_FORMAT_R32_UINT};
|
||||||
|
|
||||||
ImageID drawImageId{NULL_IMAGE_ID};
|
ImageID drawImageId{NULL_IMAGE_ID};
|
||||||
ImageID depthImageId{NULL_IMAGE_ID};
|
ImageID depthImageId{NULL_IMAGE_ID};
|
||||||
|
ImageID selectionImageId{NULL_IMAGE_ID};
|
||||||
|
|
||||||
|
std::array<GPUBuffer, FRAMES_IN_FLIGHT> pickBuffers{};
|
||||||
|
std::array<bool, FRAMES_IN_FLIGHT> pickPending{};
|
||||||
|
std::optional<glm::ivec2> requestedPick;
|
||||||
|
std::optional<std::uint32_t> pickResult;
|
||||||
|
bool selectionPassEnabled{false};
|
||||||
|
bool renderSelectionView{false};
|
||||||
|
|
||||||
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
||||||
|
|
||||||
@@ -173,7 +210,7 @@ private:
|
|||||||
|
|
||||||
std::uint32_t shadowMapId;
|
std::uint32_t shadowMapId;
|
||||||
float shadowNormalOffset;
|
float shadowNormalOffset;
|
||||||
std::uint32_t padding;
|
std::uint32_t debugSelectionView;
|
||||||
glm::mat4 lightViewProj;
|
glm::mat4 lightViewProj;
|
||||||
|
|
||||||
std::uint32_t spotShadowAtlasId;
|
std::uint32_t spotShadowAtlasId;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <destrum/FS/AssetFS.h>
|
#include <destrum/FS/AssetFS.h>
|
||||||
#include <destrum/Graphics/Resources/FontCache.h>
|
#include <destrum/Graphics/Resources/FontCache.h>
|
||||||
#include <destrum/ObjectModel/GameObject.h>
|
#include <destrum/ObjectModel/GameObject.h>
|
||||||
|
#include <destrum/ObjectModel/Transform.h>
|
||||||
#include <destrum/Scene/Scene.h>
|
#include <destrum/Scene/Scene.h>
|
||||||
#include <destrum/Scene/SceneManager.h>
|
#include <destrum/Scene/SceneManager.h>
|
||||||
#include <destrum/Serialization/SceneSerializer.h>
|
#include <destrum/Serialization/SceneSerializer.h>
|
||||||
@@ -234,6 +235,7 @@ if (!isRunning) break;
|
|||||||
{
|
{
|
||||||
ZoneScopedN("Debug ImGui");
|
ZoneScopedN("Debug ImGui");
|
||||||
drawWindowsMenu();
|
drawWindowsMenu();
|
||||||
|
applySelectionPick();
|
||||||
drawViewportWindow();
|
drawViewportWindow();
|
||||||
|
|
||||||
if (m_DebugWindows.inspector)
|
if (m_DebugWindows.inspector)
|
||||||
@@ -380,6 +382,7 @@ if (!isRunning) break;
|
|||||||
|
|
||||||
{
|
{
|
||||||
ZoneScopedN("customDraw");
|
ZoneScopedN("customDraw");
|
||||||
|
renderer.setSelectionPassEnabled(canPickInViewport());
|
||||||
customDraw();
|
customDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,10 +552,52 @@ void App::drawViewportWindow()
|
|||||||
|
|
||||||
ImGui::SetCursorScreenPos(origin);
|
ImGui::SetCursorScreenPos(origin);
|
||||||
drawViewportOverlay();
|
drawViewportOverlay();
|
||||||
|
requestSelectionPick();
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::requestSelectionPick()
|
||||||
|
{
|
||||||
|
if (!canPickInViewport() || !viewportHovered || viewportMouseClaimed ||
|
||||||
|
!ImGui::IsMouseClicked(ImGuiMouseButton_Left))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImVec2 mouse = ImGui::GetMousePos();
|
||||||
|
const glm::vec2 uv = (glm::vec2{mouse.x, mouse.y} - viewportRect.min) / viewportRect.size;
|
||||||
|
if (uv.x < 0.0f || uv.y < 0.0f || uv.x >= 1.0f || uv.y >= 1.0f)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkExtent3D extent = renderer.getDrawImage().extent;
|
||||||
|
renderer.requestSelectionPick(glm::ivec2{uv * glm::vec2{static_cast<float>(extent.width), static_cast<float>(extent.height)}});
|
||||||
|
pickSelectsParent = ImGui::GetIO().KeyAlt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::applySelectionPick()
|
||||||
|
{
|
||||||
|
const auto picked = renderer.takeSelectionPick();
|
||||||
|
auto& sceneManager = SceneManager::GetInstance();
|
||||||
|
if (!picked || sceneManager.GetActiveScenes().empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene& scene = sceneManager.GetCurrentScene();
|
||||||
|
GameObject* object = scene.FindGameObject(*picked);
|
||||||
|
if (object != nullptr && pickSelectsParent)
|
||||||
|
{
|
||||||
|
if (const Transform* parent = object->GetTransform().GetParent(); parent != nullptr && parent->GetOwner() != nullptr)
|
||||||
|
{
|
||||||
|
object = parent->GetOwner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scene.SetSelectedObject(object != nullptr ? object->GetId() : InvalidObjectId);
|
||||||
|
}
|
||||||
|
|
||||||
void App::applyViewportResize()
|
void App::applyViewportResize()
|
||||||
{
|
{
|
||||||
if (viewportDesiredSize.x <= 0 || viewportDesiredSize.y <= 0)
|
if (viewportDesiredSize.x <= 0 || viewportDesiredSize.y <= 0)
|
||||||
|
|||||||
@@ -216,9 +216,11 @@ void MeshRendererComponent::Render(const RenderContext& ctx) {
|
|||||||
GetTransform().GetWorldMatrix(),
|
GetTransform().GetWorldMatrix(),
|
||||||
materialID,
|
materialID,
|
||||||
m_skinnedMesh.get(),
|
m_skinnedMesh.get(),
|
||||||
jointMatricesStartIndex
|
jointMatricesStartIndex,
|
||||||
|
static_cast<std::uint32_t>(GetGameObject()->GetId())
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
ctx.renderer.drawMesh(meshID, GetTransform().GetWorldMatrix(), materialID);
|
ctx.renderer.drawMesh(meshID, GetTransform().GetWorldMatrix(), materialID,
|
||||||
|
static_cast<std::uint32_t>(GetGameObject()->GetId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ IndirectDrawList::View IndirectDrawList::addView(
|
|||||||
? dc.skinnedMesh->skinnedVertexBuffer.address
|
? dc.skinnedMesh->skinnedVertexBuffer.address
|
||||||
: mesh.vertexBuffer.address,
|
: mesh.vertexBuffer.address,
|
||||||
.materialId = dc.materialId,
|
.materialId = dc.materialId,
|
||||||
.padding = 0,
|
.objectId = dc.objectId,
|
||||||
});
|
});
|
||||||
++commands.back().instanceCount;
|
++commands.back().instanceCount;
|
||||||
++view.instanceCount;
|
++view.instanceCount;
|
||||||
|
|||||||
@@ -113,3 +113,15 @@ void MemoryManager::flushAllocation(
|
|||||||
|
|
||||||
VK_CHECK(vmaFlushAllocation(allocator, buffer.allocation, offset, size));
|
VK_CHECK(vmaFlushAllocation(allocator, buffer.allocation, offset, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemoryManager::invalidateAllocation(
|
||||||
|
const GPUBuffer& buffer,
|
||||||
|
VkDeviceSize offset,
|
||||||
|
VkDeviceSize size) const
|
||||||
|
{
|
||||||
|
if (!buffer.hostVisible || buffer.allocation == VK_NULL_HANDLE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VK_CHECK(vmaInvalidateAllocation(allocator, buffer.allocation, offset, size));
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ void MeshPipeline::init(
|
|||||||
GfxDevice& gfxDevice,
|
GfxDevice& gfxDevice,
|
||||||
RenderResources& resources,
|
RenderResources& resources,
|
||||||
VkFormat drawImageFormat,
|
VkFormat drawImageFormat,
|
||||||
VkFormat depthImageFormat)
|
VkFormat depthImageFormat,
|
||||||
|
VkFormat selectionImageFormat)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -31,6 +32,9 @@ void MeshPipeline::init(
|
|||||||
const auto fragShader =
|
const auto fragShader =
|
||||||
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/mesh.frag");
|
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/mesh.frag");
|
||||||
|
|
||||||
|
const auto selectionShader =
|
||||||
|
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/selection.frag");
|
||||||
|
|
||||||
const auto bufferRange = VkPushConstantRange{
|
const auto bufferRange = VkPushConstantRange{
|
||||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
@@ -82,6 +86,27 @@ void MeshPipeline::init(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PipelineConfigInfo pipelineConfig{};
|
||||||
|
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
||||||
|
|
||||||
|
pipelineConfig.name = "Mesh Selection Prepass Pipeline";
|
||||||
|
pipelineConfig.pipelineLayout = m_pipelineLayout;
|
||||||
|
|
||||||
|
pipelineConfig.vertexAttributeDescriptions = {};
|
||||||
|
pipelineConfig.vertexBindingDescriptions = {};
|
||||||
|
|
||||||
|
pipelineConfig.colorAttachments = {selectionImageFormat};
|
||||||
|
pipelineConfig.depthAttachment = depthImageFormat;
|
||||||
|
|
||||||
|
m_selectionPrepassPipeline = std::make_unique<Pipeline>(
|
||||||
|
gfxDevice,
|
||||||
|
vertexShader.string(),
|
||||||
|
selectionShader.string(),
|
||||||
|
pipelineConfig
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
PipelineConfigInfo pipelineConfig{};
|
PipelineConfigInfo pipelineConfig{};
|
||||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
||||||
@@ -120,16 +145,18 @@ void MeshPipeline::drawDepthPrepass(
|
|||||||
const RenderResources& resources,
|
const RenderResources& resources,
|
||||||
const GPUBuffer& sceneDataBuffer,
|
const GPUBuffer& sceneDataBuffer,
|
||||||
const IndirectDrawList& drawList,
|
const IndirectDrawList& drawList,
|
||||||
const IndirectDrawList::View& view)
|
const IndirectDrawList::View& view,
|
||||||
|
bool writeObjectIds)
|
||||||
{
|
{
|
||||||
if (!m_depthPrepassPipeline)
|
const auto& pipeline = writeObjectIds ? m_selectionPrepassPipeline : m_depthPrepassPipeline;
|
||||||
|
if (!pipeline)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawMeshes(
|
drawMeshes(
|
||||||
cmd,
|
cmd,
|
||||||
*m_depthPrepassPipeline,
|
*pipeline,
|
||||||
renderExtent,
|
renderExtent,
|
||||||
resources,
|
resources,
|
||||||
sceneDataBuffer,
|
sceneDataBuffer,
|
||||||
@@ -228,6 +255,7 @@ void MeshPipeline::cleanup(VkDevice device)
|
|||||||
{
|
{
|
||||||
m_pipeline.reset();
|
m_pipeline.reset();
|
||||||
m_depthPrepassPipeline.reset();
|
m_depthPrepassPipeline.reset();
|
||||||
|
m_selectionPrepassPipeline.reset();
|
||||||
|
|
||||||
if (m_pipelineLayout != VK_NULL_HANDLE && device != VK_NULL_HANDLE)
|
if (m_pipelineLayout != VK_NULL_HANDLE && device != VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,7 +33,15 @@ void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm::
|
|||||||
createDrawImage(gfxDevice, drawImageSize, true);
|
createDrawImage(gfxDevice, drawImageSize, true);
|
||||||
|
|
||||||
meshPipeline = std::make_unique<MeshPipeline>();
|
meshPipeline = std::make_unique<MeshPipeline>();
|
||||||
meshPipeline->init(gfxDevice, _resources, drawImageFormat, depthImageFormat);
|
meshPipeline->init(gfxDevice, _resources, drawImageFormat, depthImageFormat, selectionImageFormat);
|
||||||
|
|
||||||
|
for (GPUBuffer& pickBuffer : pickBuffers)
|
||||||
|
{
|
||||||
|
pickBuffer = gfxDevice.createBuffer(
|
||||||
|
sizeof(std::uint32_t),
|
||||||
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||||
|
VMA_MEMORY_USAGE_GPU_TO_CPU);
|
||||||
|
}
|
||||||
|
|
||||||
skyboxPipeline = std::make_unique<SkyboxPipeline>();
|
skyboxPipeline = std::make_unique<SkyboxPipeline>();
|
||||||
skyboxPipeline->init(gfxDevice, _resources, drawImageFormat, depthImageFormat);
|
skyboxPipeline->init(gfxDevice, _resources, drawImageFormat, depthImageFormat);
|
||||||
@@ -65,6 +73,15 @@ void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm::
|
|||||||
|
|
||||||
void GameRenderer::beginDrawing(GfxDevice& gfxDevice)
|
void GameRenderer::beginDrawing(GfxDevice& gfxDevice)
|
||||||
{
|
{
|
||||||
|
// This frame's fence was waited on, so a pick copied by its last use has landed
|
||||||
|
const std::uint32_t frameIndex = gfxDevice.getCurrentFrameIndex();
|
||||||
|
if (pickPending[frameIndex])
|
||||||
|
{
|
||||||
|
pickPending[frameIndex] = false;
|
||||||
|
gfxDevice.getMemoryManager().invalidateAllocation(pickBuffers[frameIndex]);
|
||||||
|
pickResult = *static_cast<const std::uint32_t*>(pickBuffers[frameIndex].info.pMappedData);
|
||||||
|
}
|
||||||
|
|
||||||
meshDrawCommands.clear();
|
meshDrawCommands.clear();
|
||||||
skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
||||||
lightCullingPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
lightCullingPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
||||||
@@ -207,7 +224,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
|||||||
.debugLightHeatmap = renderLightHeatmap ? 1u : 0u,
|
.debugLightHeatmap = renderLightHeatmap ? 1u : 0u,
|
||||||
.shadowMapId = static_cast<std::uint32_t>(shadowMapPipeline->getShadowMapId()),
|
.shadowMapId = static_cast<std::uint32_t>(shadowMapPipeline->getShadowMapId()),
|
||||||
.shadowNormalOffset = shadowMapPipeline->getNormalOffset(),
|
.shadowNormalOffset = shadowMapPipeline->getNormalOffset(),
|
||||||
.padding = 0,
|
.debugSelectionView = renderSelectionView ? 1u : 0u,
|
||||||
.lightViewProj = lightViewProj,
|
.lightViewProj = lightViewProj,
|
||||||
.spotShadowAtlasId = static_cast<std::uint32_t>(shadowMapPipeline->getSpotShadowAtlasId()),
|
.spotShadowAtlasId = static_cast<std::uint32_t>(shadowMapPipeline->getSpotShadowAtlasId()),
|
||||||
.spotShadowTilesPerRow = ShadowMapPipeline::SPOT_SHADOW_TILES_PER_ROW,
|
.spotShadowTilesPerRow = ShadowMapPipeline::SPOT_SHADOW_TILES_PER_ROW,
|
||||||
@@ -288,12 +305,29 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
|||||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Depth Prepass");
|
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Depth Prepass");
|
||||||
vkutil::ScopedCmdLabel label(cmd, "Depth Prepass", vkutil::labelColors::depth);
|
vkutil::ScopedCmdLabel label(cmd, "Depth Prepass", vkutil::labelColors::depth);
|
||||||
|
|
||||||
|
const auto& selectionImage = resources->getImage(selectionImageId);
|
||||||
|
if (selectionPassEnabled)
|
||||||
|
{
|
||||||
|
vkutil::transitionImage(
|
||||||
|
cmd,
|
||||||
|
selectionImage.image,
|
||||||
|
selectionImage.layout,
|
||||||
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
selectionImage.setLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
}
|
||||||
|
|
||||||
auto depthPrepassInfo = vkutil::createRenderingInfo({
|
auto depthPrepassInfo = vkutil::createRenderingInfo({
|
||||||
.renderExtent = drawImage.getExtent2D(),
|
.renderExtent = drawImage.getExtent2D(),
|
||||||
|
.colorImageView = selectionPassEnabled ? selectionImage.imageView : VK_NULL_HANDLE,
|
||||||
|
.colorImageClearValue = glm::vec4{0.f},
|
||||||
.depthImageView = depthImage.imageView,
|
.depthImageView = depthImage.imageView,
|
||||||
.depthImageClearValue = 1.f,
|
.depthImageClearValue = 1.f,
|
||||||
});
|
});
|
||||||
depthPrepassInfo.renderingInfo.pDepthAttachment = &depthPrepassInfo.depthAttachment;
|
depthPrepassInfo.renderingInfo.pDepthAttachment = &depthPrepassInfo.depthAttachment;
|
||||||
|
if (selectionPassEnabled)
|
||||||
|
{
|
||||||
|
depthPrepassInfo.renderingInfo.pColorAttachments = &depthPrepassInfo.colorAttachment;
|
||||||
|
}
|
||||||
|
|
||||||
vkCmdBeginRendering(cmd, &depthPrepassInfo.renderingInfo);
|
vkCmdBeginRendering(cmd, &depthPrepassInfo.renderingInfo);
|
||||||
|
|
||||||
@@ -303,9 +337,16 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
|||||||
*resources,
|
*resources,
|
||||||
sceneDataBuffer.getBuffer(),
|
sceneDataBuffer.getBuffer(),
|
||||||
drawList,
|
drawList,
|
||||||
mainView);
|
mainView,
|
||||||
|
selectionPassEnabled);
|
||||||
|
|
||||||
vkCmdEndRendering(cmd);
|
vkCmdEndRendering(cmd);
|
||||||
|
|
||||||
|
if (selectionPassEnabled && requestedPick)
|
||||||
|
{
|
||||||
|
recordSelectionPick(cmd, gfxDevice.getCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
requestedPick.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -404,6 +445,51 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameRenderer::recordSelectionPick(VkCommandBuffer cmd, std::uint32_t frameIndex)
|
||||||
|
{
|
||||||
|
vkutil::ScopedCmdLabel label(cmd, "Selection Pick", vkutil::labelColors::transfer);
|
||||||
|
|
||||||
|
const auto& image = resources->getImage(selectionImageId);
|
||||||
|
const VkExtent2D extent = image.getExtent2D();
|
||||||
|
const glm::ivec2 pixel = glm::clamp(
|
||||||
|
*requestedPick,
|
||||||
|
glm::ivec2{0},
|
||||||
|
glm::ivec2{static_cast<int>(extent.width) - 1, static_cast<int>(extent.height) - 1});
|
||||||
|
|
||||||
|
vkutil::transitionImage(cmd, image.image, image.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||||
|
image.setLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||||
|
|
||||||
|
const VkBufferImageCopy region{
|
||||||
|
.bufferOffset = 0,
|
||||||
|
.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
|
||||||
|
.imageOffset = {pixel.x, pixel.y, 0},
|
||||||
|
.imageExtent = {1, 1, 1},
|
||||||
|
};
|
||||||
|
vkCmdCopyImageToBuffer(
|
||||||
|
cmd,
|
||||||
|
image.image,
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
pickBuffers[frameIndex].buffer,
|
||||||
|
1,
|
||||||
|
®ion);
|
||||||
|
|
||||||
|
const VkMemoryBarrier2 toHost{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
|
||||||
|
.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
|
||||||
|
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
||||||
|
.dstStageMask = VK_PIPELINE_STAGE_2_HOST_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_2_HOST_READ_BIT,
|
||||||
|
};
|
||||||
|
const VkDependencyInfo dependency{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||||
|
.memoryBarrierCount = 1,
|
||||||
|
.pMemoryBarriers = &toHost,
|
||||||
|
};
|
||||||
|
vkCmdPipelineBarrier2(cmd, &dependency);
|
||||||
|
|
||||||
|
pickPending[frameIndex] = true;
|
||||||
|
}
|
||||||
|
|
||||||
void GameRenderer::draw2D(
|
void GameRenderer::draw2D(
|
||||||
VkCommandBuffer cmd,
|
VkCommandBuffer cmd,
|
||||||
GfxDevice& gfxDevice,
|
GfxDevice& gfxDevice,
|
||||||
@@ -440,6 +526,13 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
|||||||
|
|
||||||
sceneDataBuffer.cleanup(gfxDevice);
|
sceneDataBuffer.cleanup(gfxDevice);
|
||||||
drawList.cleanup(gfxDevice);
|
drawList.cleanup(gfxDevice);
|
||||||
|
for (GPUBuffer& pickBuffer : pickBuffers)
|
||||||
|
{
|
||||||
|
gfxDevice.destroyBuffer(pickBuffer);
|
||||||
|
}
|
||||||
|
pickPending = {};
|
||||||
|
pickResult.reset();
|
||||||
|
requestedPick.reset();
|
||||||
|
|
||||||
// if (drawImageId != NULL_IMAGE_ID)
|
// if (drawImageId != NULL_IMAGE_ID)
|
||||||
// gfxDevice.destroyImage();
|
// gfxDevice.destroyImage();
|
||||||
@@ -449,6 +542,7 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
|||||||
|
|
||||||
drawImageId = NULL_IMAGE_ID;
|
drawImageId = NULL_IMAGE_ID;
|
||||||
depthImageId = NULL_IMAGE_ID;
|
depthImageId = NULL_IMAGE_ID;
|
||||||
|
selectionImageId = NULL_IMAGE_ID;
|
||||||
resources = nullptr;
|
resources = nullptr;
|
||||||
meshPipeline.reset();
|
meshPipeline.reset();
|
||||||
skyboxPipeline.reset();
|
skyboxPipeline.reset();
|
||||||
@@ -467,7 +561,7 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
|||||||
GameState::GetInstance().SetRenderer(nullptr);
|
GameState::GetInstance().SetRenderer(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId)
|
void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId, std::uint32_t objectId)
|
||||||
{
|
{
|
||||||
const auto& mesh = resources->meshes().getMesh(id);
|
const auto& mesh = resources->meshes().getMesh(id);
|
||||||
const auto worldBoundingSphere = edge::calculateBoundingSphereWorld(transform, mesh.boundingSphere, false);
|
const auto worldBoundingSphere = edge::calculateBoundingSphereWorld(transform, mesh.boundingSphere, false);
|
||||||
@@ -478,6 +572,7 @@ void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialID ma
|
|||||||
.transformMatrix = transform,
|
.transformMatrix = transform,
|
||||||
.worldBoundingSphere = worldBoundingSphere,
|
.worldBoundingSphere = worldBoundingSphere,
|
||||||
.materialId = materialId,
|
.materialId = materialId,
|
||||||
|
.objectId = objectId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,7 +580,8 @@ void GameRenderer::drawSkinnedMesh(MeshID id,
|
|||||||
const glm::mat4& transform,
|
const glm::mat4& transform,
|
||||||
MaterialID materialId,
|
MaterialID materialId,
|
||||||
SkinnedMesh* skinnedMesh,
|
SkinnedMesh* skinnedMesh,
|
||||||
std::size_t jointMatricesStartIndex)
|
std::size_t jointMatricesStartIndex,
|
||||||
|
std::uint32_t objectId)
|
||||||
{
|
{
|
||||||
const auto& mesh = resources->meshes().getMesh(id);
|
const auto& mesh = resources->meshes().getMesh(id);
|
||||||
const auto worldBoundingSphere = edge::calculateBoundingSphereWorld(transform, mesh.boundingSphere, true);
|
const auto worldBoundingSphere = edge::calculateBoundingSphereWorld(transform, mesh.boundingSphere, true);
|
||||||
@@ -499,6 +595,7 @@ void GameRenderer::drawSkinnedMesh(MeshID id,
|
|||||||
.materialId = materialId,
|
.materialId = materialId,
|
||||||
.skinnedMesh = skinnedMesh,
|
.skinnedMesh = skinnedMesh,
|
||||||
.jointMatricesStartIndex = static_cast<std::uint32_t>(jointMatricesStartIndex),
|
.jointMatricesStartIndex = static_cast<std::uint32_t>(jointMatricesStartIndex),
|
||||||
|
.objectId = objectId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,4 +747,16 @@ void GameRenderer::createDrawImage(GfxDevice& gfxDevice,
|
|||||||
depthImageId = resources->createImage(gfxDevice, createInfo, "depth image");
|
depthImageId = resources->createImage(gfxDevice, createInfo, "depth image");
|
||||||
spdlog::info("Created depth image with id {}", depthImageId);
|
spdlog::info("Created depth image with id {}", depthImageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto createInfo = vkutil::CreateImageInfo{
|
||||||
|
.format = selectionImageFormat,
|
||||||
|
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||||
|
.extent = drawImageExtent,
|
||||||
|
.samples = noMsaa,
|
||||||
|
};
|
||||||
|
|
||||||
|
resources->destroyImage(selectionImageId);
|
||||||
|
selectionImageId = resources->createImage(gfxDevice, createInfo, "selection image");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
void setupSkybox();
|
void setupSkybox();
|
||||||
void updateViewMode();
|
void updateViewMode();
|
||||||
|
void focusSelection();
|
||||||
|
void updateFocus(float dt);
|
||||||
[[nodiscard]] CameraComponent* getSceneCamera() const;
|
[[nodiscard]] CameraComponent* getSceneCamera() const;
|
||||||
[[nodiscard]] Camera& getViewCamera();
|
[[nodiscard]] Camera& getViewCamera();
|
||||||
[[nodiscard]] bool isPlayerViewActive() const;
|
[[nodiscard]] bool isPlayerViewActive() const;
|
||||||
@@ -57,6 +59,7 @@ private:
|
|||||||
|
|
||||||
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
|
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
|
||||||
std::optional<Camera> editorCamera;
|
std::optional<Camera> editorCamera;
|
||||||
|
std::optional<glm::vec3> focusTarget;
|
||||||
|
|
||||||
std::unique_ptr<CubeMap> skyboxCubemap;
|
std::unique_ptr<CubeMap> skyboxCubemap;
|
||||||
MainSceneObjects mainScene{};
|
MainSceneObjects mainScene{};
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ void LightKeeper::customUpdate(float dt)
|
|||||||
// after SDL events have been consumed.
|
// after SDL events have been consumed.
|
||||||
if (!isPlayerViewActive() && getSceneCamera() == nullptr)
|
if (!isPlayerViewActive() && getSceneCamera() == nullptr)
|
||||||
{
|
{
|
||||||
|
if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_F))
|
||||||
|
{
|
||||||
|
focusSelection();
|
||||||
|
}
|
||||||
|
updateFocus(dt);
|
||||||
camera.Update(dt);
|
camera.Update(dt);
|
||||||
}
|
}
|
||||||
if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_F11))
|
if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_F11))
|
||||||
|
|||||||
@@ -19,8 +19,12 @@
|
|||||||
|
|
||||||
#include <components/FirstPersonController.h>
|
#include <components/FirstPersonController.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@@ -162,6 +166,11 @@ void LightKeeper::drawDebugMenuBar()
|
|||||||
if (ImGui::MenuItem("Forward+ Light Heatmap", nullptr, &lightHeatmap)) {
|
if (ImGui::MenuItem("Forward+ Light Heatmap", nullptr, &lightHeatmap)) {
|
||||||
renderer.setRenderLightHeatmap(lightHeatmap);
|
renderer.setRenderLightHeatmap(lightHeatmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool selectionView = renderer.getRenderSelectionView();
|
||||||
|
if (ImGui::MenuItem("Selection Buffer", nullptr, &selectionView)) {
|
||||||
|
renderer.setRenderSelectionView(selectionView);
|
||||||
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +226,71 @@ void LightKeeper::onViewportAssetDropped(std::string_view path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LightKeeper::focusSelection()
|
||||||
|
{
|
||||||
|
GameObject* selected = SceneManager::GetInstance().GetCurrentScene().GetSelectedObject();
|
||||||
|
if (selected == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 boundsMin{std::numeric_limits<float>::max()};
|
||||||
|
glm::vec3 boundsMax{std::numeric_limits<float>::lowest()};
|
||||||
|
std::vector<GameObject*> pending{selected};
|
||||||
|
while (!pending.empty()) {
|
||||||
|
GameObject* object = pending.back();
|
||||||
|
pending.pop_back();
|
||||||
|
for (const Transform* child : object->GetTransform().GetChildren()) {
|
||||||
|
if (child != nullptr && child->GetOwner() != nullptr) {
|
||||||
|
pending.push_back(child->GetOwner());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* meshRenderer = object->GetComponent<MeshRendererComponent>();
|
||||||
|
if (meshRenderer == nullptr || meshRenderer->GetMeshID() == NULL_MESH_ID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const GPUMesh& mesh = resources.meshes().getMesh(meshRenderer->GetMeshID());
|
||||||
|
const glm::mat4 world = object->GetTransform().GetWorldMatrix();
|
||||||
|
for (int corner = 0; corner < 8; ++corner) {
|
||||||
|
const glm::vec3 local{
|
||||||
|
corner & 1 ? mesh.maxPos.x : mesh.minPos.x,
|
||||||
|
corner & 2 ? mesh.maxPos.y : mesh.minPos.y,
|
||||||
|
corner & 4 ? mesh.maxPos.z : mesh.minPos.z,
|
||||||
|
};
|
||||||
|
const glm::vec3 point{world * glm::vec4{local, 1.0f}};
|
||||||
|
boundsMin = glm::min(boundsMin, point);
|
||||||
|
boundsMax = glm::max(boundsMax, point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool hasBounds = boundsMin.x <= boundsMax.x;
|
||||||
|
const glm::vec3 center = hasBounds ? (boundsMin + boundsMax) * 0.5f : selected->GetTransform().GetWorldPosition();
|
||||||
|
const float radius = hasBounds ? std::max(glm::length(boundsMax - boundsMin) * 0.5f, 0.1f) : 1.0f;
|
||||||
|
|
||||||
|
// Fit the bounding sphere in the narrower of the two field of view angles
|
||||||
|
const float halfFovY = glm::radians(camera.GetFOVY()) * 0.5f;
|
||||||
|
const float halfFovX = std::atan(std::tan(halfFovY) * camera.GetAspectRatio());
|
||||||
|
const float distance = radius / std::sin(std::min(halfFovX, halfFovY)) * 1.1f;
|
||||||
|
focusTarget = center - camera.GetForward() * distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightKeeper::updateFocus(float dt)
|
||||||
|
{
|
||||||
|
if (!focusTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (InputManager::GetInstance().IsMouseDown(SDL_BUTTON_RIGHT)) {
|
||||||
|
focusTarget.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
camera.m_position = glm::mix(camera.m_position, *focusTarget, 1.0f - std::exp(-12.0f * dt));
|
||||||
|
if (glm::distance(camera.m_position, *focusTarget) < 0.01f) {
|
||||||
|
camera.m_position = *focusTarget;
|
||||||
|
focusTarget.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LightKeeper::drawViewportOverlay()
|
void LightKeeper::drawViewportOverlay()
|
||||||
{
|
{
|
||||||
if (!showTransformGizmo || isPlayerViewActive()) {
|
if (!showTransformGizmo || isPlayerViewActive()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user