feat: add canvas rework
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
#ifndef DESTRUM_APPLAYER_H
|
||||
#define DESTRUM_APPLAYER_H
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <destrum/Components/UI/CanvasFrame.h>
|
||||
|
||||
class Camera;
|
||||
class GameRenderer;
|
||||
|
||||
@@ -25,6 +29,7 @@ public:
|
||||
[[nodiscard]] virtual Camera* viewCamera() { return nullptr; }
|
||||
[[nodiscard]] virtual bool hostsViewport() const { return false; }
|
||||
[[nodiscard]] virtual InputFocus gameInputFocus() const { return {}; }
|
||||
[[nodiscard]] virtual std::optional<CanvasFrame> canvasFrame() const { return std::nullopt; }
|
||||
};
|
||||
|
||||
#endif // DESTRUM_APPLAYER_H
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef DESTRUM_CANVAS_H
|
||||
#define DESTRUM_CANVAS_H
|
||||
|
||||
#include <destrum/Components/UI/CanvasFrame.h>
|
||||
#include <destrum/Components/UI/RectTransform.h>
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
|
||||
@@ -22,6 +23,7 @@ public:
|
||||
void Start() override;
|
||||
void Render(const RenderContext& ctx) override;
|
||||
void Draw(const glm::vec2& screenSize);
|
||||
void Draw(const CanvasFrame& frame);
|
||||
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef DESTRUM_CANVASFRAME_H
|
||||
#define DESTRUM_CANVASFRAME_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
// Where canvases lay out, in draw image pixels: a resolution sized screen at min, drawn zoom times larger
|
||||
struct CanvasFrame {
|
||||
glm::vec2 min{0.0f};
|
||||
glm::vec2 resolution{0.0f};
|
||||
float zoom{1.0f};
|
||||
};
|
||||
|
||||
#endif // DESTRUM_CANVASFRAME_H
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
// Where the canvas last drew this object
|
||||
[[nodiscard]] const UIRect& GetScreenRect() const { return m_ScreenRect; }
|
||||
[[nodiscard]] const UIRect& GetParentRect() const { return m_ParentRect; }
|
||||
// Screen pixels per canvas unit at the last draw
|
||||
[[nodiscard]] float GetScreenScale() const { return m_LastScale; }
|
||||
void SetScreenRect(const UIRect& rect, const UIRect& parentRect, float scale)
|
||||
{
|
||||
m_ScreenRect = rect;
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#ifndef RENDERCONTEXT_H
|
||||
#define RENDERCONTEXT_H
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <destrum/Components/UI/CanvasFrame.h>
|
||||
#include <destrum/Graphics/Renderer.h>
|
||||
|
||||
struct RenderContext {
|
||||
GameRenderer& renderer;
|
||||
const Camera& camera;
|
||||
const GameRenderer::SceneData& sceneData;
|
||||
// Unset: canvases fill the draw image
|
||||
std::optional<CanvasFrame> canvasFrame{};
|
||||
};
|
||||
|
||||
#endif //RENDERCONTEXT_H
|
||||
|
||||
@@ -378,6 +378,7 @@ void App::renderFrame()
|
||||
.renderer = renderer,
|
||||
.camera = camera,
|
||||
.sceneData = sceneData,
|
||||
.canvasFrame = layer ? layer->canvasFrame() : std::nullopt,
|
||||
});
|
||||
renderer.endDrawing();
|
||||
renderer.draw(cmd, gfxDevice, camera, sceneData);
|
||||
|
||||
@@ -54,19 +54,29 @@ Canvas* Canvas::FindFor(GameObject& object)
|
||||
|
||||
void Canvas::Render(const RenderContext& ctx)
|
||||
{
|
||||
if (ctx.canvasFrame) {
|
||||
Draw(*ctx.canvasFrame);
|
||||
return;
|
||||
}
|
||||
const VkExtent3D extent = ctx.renderer.getDrawImage().extent;
|
||||
Draw(glm::vec2{static_cast<float>(extent.width), static_cast<float>(extent.height)});
|
||||
}
|
||||
|
||||
void Canvas::Draw(const glm::vec2& screenSize)
|
||||
{
|
||||
Draw(CanvasFrame{.resolution = screenSize});
|
||||
}
|
||||
|
||||
void Canvas::Draw(const CanvasFrame& frame)
|
||||
{
|
||||
ZoneScopedN("Canvas::Draw");
|
||||
|
||||
const UIRect screenRect{.min = glm::vec2{0.0f}, .size = screenSize};
|
||||
const UIRect screenRect{.min = frame.min, .size = frame.resolution * frame.zoom};
|
||||
const float scale = GetScale(frame.resolution) * frame.zoom;
|
||||
if (auto* rectTransform = GetGameObject()->GetComponent<RectTransform>()) {
|
||||
rectTransform->SetScreenRect(screenRect, screenRect, GetScale(screenSize));
|
||||
rectTransform->SetScreenRect(screenRect, screenRect, scale);
|
||||
}
|
||||
DrawObject(*GetGameObject(), screenRect, GetScale(screenSize), true);
|
||||
DrawObject(*GetGameObject(), screenRect, scale, true);
|
||||
}
|
||||
|
||||
float Canvas::GetScale(const glm::vec2& screenSize) const
|
||||
|
||||
@@ -11,6 +11,7 @@ set(EDITOR_SRC
|
||||
src/AssetBrowser.cpp
|
||||
src/TransformGizmo.cpp
|
||||
src/RectGizmo.cpp
|
||||
src/CanvasView.cpp
|
||||
src/ResourcesInspector.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef EDITOR_CANVASVIEW_H
|
||||
#define EDITOR_CANVASVIEW_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <destrum/Components/UI/CanvasFrame.h>
|
||||
|
||||
class GameObject;
|
||||
class RectGizmo;
|
||||
class Scene;
|
||||
struct ImDrawList;
|
||||
|
||||
class CanvasView final {
|
||||
public:
|
||||
[[nodiscard]] bool IsEnabled() const { return m_Enabled; }
|
||||
void SetEnabled(bool enabled);
|
||||
void Toggle() { SetEnabled(!m_Enabled); }
|
||||
|
||||
[[nodiscard]] bool ShowsScene() const { return m_ShowScene; }
|
||||
[[nodiscard]] bool IsPanning() const { return m_Panning; }
|
||||
[[nodiscard]] CanvasFrame GetFrame() const;
|
||||
|
||||
void SetDrawImageSize(const glm::vec2& size);
|
||||
void Fit();
|
||||
|
||||
bool HandleInput(const glm::vec2& mousePixel, const glm::vec2& pixelsPerPoint, bool hovered);
|
||||
void SubmitBackdrop() const;
|
||||
void DrawOverlay(ImDrawList& drawList, const glm::vec2& viewMin, const glm::vec2& viewScale) const;
|
||||
void DrawToolbar(RectGizmo& gizmo);
|
||||
|
||||
[[nodiscard]] static GameObject* Pick(Scene& scene, const glm::vec2& pixel);
|
||||
|
||||
private:
|
||||
bool m_Enabled{false};
|
||||
bool m_ShowScene{false};
|
||||
bool m_Fitted{true};
|
||||
bool m_Panning{false};
|
||||
int m_Preset{0};
|
||||
glm::ivec2 m_Resolution{1920, 1080};
|
||||
glm::vec2 m_DrawImageSize{1.0f};
|
||||
glm::vec2 m_FrameMin{0.0f};
|
||||
float m_Zoom{1.0f};
|
||||
};
|
||||
|
||||
#endif // EDITOR_CANVASVIEW_H
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <destrum/AppLayer.h>
|
||||
|
||||
#include <editor/AssetBrowser.h>
|
||||
#include <editor/CanvasView.h>
|
||||
#include <editor/EditorCamera.h>
|
||||
#include <editor/PlayMode.h>
|
||||
#include <editor/RectGizmo.h>
|
||||
@@ -44,6 +45,7 @@ public:
|
||||
[[nodiscard]] Camera* viewCamera() override;
|
||||
[[nodiscard]] bool hostsViewport() const override { return true; }
|
||||
[[nodiscard]] InputFocus gameInputFocus() const override;
|
||||
[[nodiscard]] std::optional<CanvasFrame> canvasFrame() const override;
|
||||
|
||||
bool OpenScene(std::string_view path);
|
||||
bool SaveScene();
|
||||
@@ -81,7 +83,8 @@ private:
|
||||
|
||||
// Editor.cpp
|
||||
[[nodiscard]] Scene* CurrentScene() const;
|
||||
[[nodiscard]] bool UsesEditorCamera();
|
||||
[[nodiscard]] bool UsesEditorCamera() const;
|
||||
[[nodiscard]] bool CanvasViewActive() const;
|
||||
void HandleShortcuts();
|
||||
void Undo();
|
||||
void Redo();
|
||||
@@ -126,6 +129,7 @@ private:
|
||||
ResourcesInspector m_ResourcesInspector;
|
||||
TransformGizmo m_TransformGizmo;
|
||||
RectGizmo m_RectGizmo;
|
||||
CanvasView m_CanvasView;
|
||||
ShaderHotReload m_ShaderHotReload;
|
||||
|
||||
Panels m_Panels;
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
#include <editor/CanvasView.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <destrum/Components/UI/Canvas.h>
|
||||
#include <destrum/Components/UI/RectTransform.h>
|
||||
#include <destrum/Components/UI/UIGraphic.h>
|
||||
#include <destrum/Graphics/Managers/Renderer2D.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Scene/Scene.h>
|
||||
|
||||
#include <editor/RectGizmo.h>
|
||||
|
||||
namespace {
|
||||
struct ResolutionPreset {
|
||||
const char* label;
|
||||
glm::ivec2 size;
|
||||
};
|
||||
|
||||
constexpr std::array<ResolutionPreset, 6> Presets{{
|
||||
{"1920 x 1080 (16:9)", {1920, 1080}},
|
||||
{"1280 x 720 (16:9)", {1280, 720}},
|
||||
{"2560 x 1440 (16:9)", {2560, 1440}},
|
||||
{"2560 x 1080 (21:9)", {2560, 1080}},
|
||||
{"1024 x 768 (4:3)", {1024, 768}},
|
||||
{"1080 x 1920 (portrait)", {1080, 1920}},
|
||||
}};
|
||||
constexpr int CustomPreset = static_cast<int>(Presets.size());
|
||||
|
||||
constexpr float MinZoom = 0.05f;
|
||||
constexpr float MaxZoom = 16.0f;
|
||||
constexpr float FitMargin = 24.0f;
|
||||
|
||||
constexpr glm::vec4 BackdropColor{0.11f, 0.11f, 0.12f, 1.0f};
|
||||
constexpr glm::vec4 FrameColor{0.21f, 0.21f, 0.23f, 1.0f};
|
||||
constexpr ImU32 FrameOutlineColor = IM_COL32(255, 255, 255, 90);
|
||||
constexpr ImU32 LabelColor = IM_COL32(255, 255, 255, 150);
|
||||
|
||||
[[nodiscard]] bool HasGraphic(const GameObject& object)
|
||||
{
|
||||
for (const auto& component : object.GetComponents()) {
|
||||
const auto* graphic = dynamic_cast<const UIGraphic*>(component.get());
|
||||
if (graphic != nullptr && graphic->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct Hit {
|
||||
GameObject* object{nullptr};
|
||||
bool hasGraphic{false};
|
||||
};
|
||||
|
||||
// Walks in the canvas's draw order, so the last hit is the one on top. Empty layout objects only count when no
|
||||
// graphic is under the mouse, otherwise a stretched container would swallow every click
|
||||
void CollectHits(GameObject& object, const glm::vec2& pixel, bool isCanvasRoot, Hit& hit)
|
||||
{
|
||||
if (object.IsBeingDestroyed() || !object.IsActive()) {
|
||||
return;
|
||||
}
|
||||
if (!isCanvasRoot) {
|
||||
if (object.HasComponent<Canvas>()) {
|
||||
return;
|
||||
}
|
||||
if (const auto* rectTransform = object.GetComponent<RectTransform>();
|
||||
rectTransform != nullptr && rectTransform->GetScreenRect().Contains(pixel)) {
|
||||
const bool graphic = HasGraphic(object);
|
||||
if (graphic || !hit.hasGraphic) {
|
||||
hit = {.object = &object, .hasGraphic = graphic};
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const Transform* child : object.GetTransform().GetChildren()) {
|
||||
if (child != nullptr && child->GetOwner() != nullptr) {
|
||||
CollectHits(*child->GetOwner(), pixel, false, hit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasView::SetEnabled(bool enabled)
|
||||
{
|
||||
if (enabled && !m_Enabled) {
|
||||
Fit();
|
||||
}
|
||||
m_Enabled = enabled;
|
||||
m_Panning = m_Panning && enabled;
|
||||
}
|
||||
|
||||
CanvasFrame CanvasView::GetFrame() const
|
||||
{
|
||||
return CanvasFrame{.min = m_FrameMin, .resolution = glm::vec2{m_Resolution}, .zoom = m_Zoom};
|
||||
}
|
||||
|
||||
void CanvasView::SetDrawImageSize(const glm::vec2& size)
|
||||
{
|
||||
if (size == m_DrawImageSize) {
|
||||
return;
|
||||
}
|
||||
m_DrawImageSize = size;
|
||||
if (m_Fitted) {
|
||||
Fit();
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasView::Fit()
|
||||
{
|
||||
const glm::vec2 resolution{m_Resolution};
|
||||
const glm::vec2 available = glm::max(m_DrawImageSize - glm::vec2{FitMargin * 2.0f}, glm::vec2{1.0f});
|
||||
m_Zoom = std::clamp(std::min(available.x / resolution.x, available.y / resolution.y), MinZoom, MaxZoom);
|
||||
m_FrameMin = glm::round((m_DrawImageSize - resolution * m_Zoom) * 0.5f);
|
||||
m_Fitted = true;
|
||||
}
|
||||
|
||||
bool CanvasView::HandleInput(const glm::vec2& mousePixel, const glm::vec2& pixelsPerPoint, bool hovered)
|
||||
{
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
if (hovered && io.MouseWheel != 0.0f) {
|
||||
const glm::vec2 canvasPoint = (mousePixel - m_FrameMin) / m_Zoom;
|
||||
m_Zoom = std::clamp(m_Zoom * std::pow(1.15f, io.MouseWheel), MinZoom, MaxZoom);
|
||||
m_FrameMin = mousePixel - canvasPoint * m_Zoom;
|
||||
m_Fitted = false;
|
||||
}
|
||||
|
||||
if (hovered && (ImGui::IsMouseClicked(ImGuiMouseButton_Right) || ImGui::IsMouseClicked(ImGuiMouseButton_Middle))) {
|
||||
m_Panning = true;
|
||||
}
|
||||
if (m_Panning && !ImGui::IsMouseDown(ImGuiMouseButton_Right) && !ImGui::IsMouseDown(ImGuiMouseButton_Middle)) {
|
||||
m_Panning = false;
|
||||
}
|
||||
if (m_Panning && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) {
|
||||
m_FrameMin += glm::vec2{io.MouseDelta.x, io.MouseDelta.y} * pixelsPerPoint;
|
||||
m_Fitted = false;
|
||||
}
|
||||
return m_Panning;
|
||||
}
|
||||
|
||||
void CanvasView::SubmitBackdrop() const
|
||||
{
|
||||
if (m_ShowScene) {
|
||||
return;
|
||||
}
|
||||
auto& renderer2D = Renderer2D::GetInstance();
|
||||
renderer2D.DrawRect(glm::vec2{0.0f}, m_DrawImageSize, BackdropColor);
|
||||
renderer2D.DrawRect(m_FrameMin, glm::vec2{m_Resolution} * m_Zoom, FrameColor);
|
||||
}
|
||||
|
||||
void CanvasView::DrawOverlay(ImDrawList& drawList, const glm::vec2& viewMin, const glm::vec2& viewScale) const
|
||||
{
|
||||
const glm::vec2 min = viewMin + m_FrameMin * viewScale;
|
||||
const glm::vec2 max = viewMin + (m_FrameMin + glm::vec2{m_Resolution} * m_Zoom) * viewScale;
|
||||
drawList.AddRect(ImVec2{min.x - 1.0f, min.y - 1.0f}, ImVec2{max.x + 1.0f, max.y + 1.0f}, FrameOutlineColor);
|
||||
|
||||
char label[64];
|
||||
std::snprintf(label, sizeof(label), "%d x %d %.0f%%", m_Resolution.x, m_Resolution.y, m_Zoom * 100.0f);
|
||||
drawList.AddText(ImVec2{min.x, min.y - ImGui::GetTextLineHeight() - 2.0f}, LabelColor, label);
|
||||
}
|
||||
|
||||
void CanvasView::DrawToolbar(RectGizmo& gizmo)
|
||||
{
|
||||
if (!ImGui::Begin("Canvas", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
bool enabled = m_Enabled;
|
||||
if (ImGui::Checkbox("Canvas mode (2)", &enabled)) {
|
||||
SetEnabled(enabled);
|
||||
}
|
||||
|
||||
if (m_Enabled) {
|
||||
ImGui::SetNextItemWidth(180.0f);
|
||||
const char* preview = m_Preset < CustomPreset ? Presets[static_cast<std::size_t>(m_Preset)].label : "Custom";
|
||||
if (ImGui::BeginCombo("Resolution", preview)) {
|
||||
for (int i = 0; i < CustomPreset; ++i) {
|
||||
if (ImGui::Selectable(Presets[static_cast<std::size_t>(i)].label, m_Preset == i)) {
|
||||
m_Preset = i;
|
||||
m_Resolution = Presets[static_cast<std::size_t>(i)].size;
|
||||
Fit();
|
||||
}
|
||||
}
|
||||
if (ImGui::Selectable("Custom", m_Preset == CustomPreset)) {
|
||||
m_Preset = CustomPreset;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (m_Preset == CustomPreset) {
|
||||
ImGui::SetNextItemWidth(180.0f);
|
||||
glm::ivec2 resolution = m_Resolution;
|
||||
if (ImGui::InputInt2("Size", &resolution.x, ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
m_Resolution = glm::clamp(resolution, glm::ivec2{16}, glm::ivec2{16384});
|
||||
Fit();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Button("Fit (F)")) {
|
||||
Fit();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("100%")) {
|
||||
const glm::vec2 center = m_FrameMin + glm::vec2{m_Resolution} * m_Zoom * 0.5f;
|
||||
m_Zoom = 1.0f;
|
||||
m_FrameMin = glm::round(center - glm::vec2{m_Resolution} * 0.5f);
|
||||
m_Fitted = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%.0f%%", m_Zoom * 100.0f);
|
||||
ImGui::Checkbox("Show scene", &m_ShowScene);
|
||||
ImGui::TextDisabled("Wheel zoom, right/middle drag pan");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox("Snap", &gizmo.snap);
|
||||
if (gizmo.snap) {
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(80.0f);
|
||||
ImGui::DragFloat("Step", &gizmo.snapStep, 0.5f, 1.0f, 100.0f, "%.0f px");
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
GameObject* CanvasView::Pick(Scene& scene, const glm::vec2& pixel)
|
||||
{
|
||||
Hit hit;
|
||||
for (const auto& object : scene.GetObjects()) {
|
||||
if (object != nullptr && !object->IsBeingDestroyed() && object->HasComponent<Canvas>() && object->IsActiveInHierarchy()) {
|
||||
CollectHits(*object, pixel, true, hit);
|
||||
}
|
||||
}
|
||||
return hit.object;
|
||||
}
|
||||
+31
-6
@@ -73,11 +73,22 @@ Scene* Editor::CurrentScene() const
|
||||
return sceneManager.GetActiveScenes().empty() ? nullptr : &sceneManager.GetCurrentScene();
|
||||
}
|
||||
|
||||
bool Editor::UsesEditorCamera()
|
||||
bool Editor::UsesEditorCamera() const
|
||||
{
|
||||
return m_PlayMode.IsEditing() || m_PlayMode.IsPaused() || m_App.gameCamera() == nullptr;
|
||||
}
|
||||
|
||||
// While playing the UI fills the screen like it would in the game
|
||||
bool Editor::CanvasViewActive() const
|
||||
{
|
||||
return m_CanvasView.IsEnabled() && UsesEditorCamera();
|
||||
}
|
||||
|
||||
std::optional<CanvasFrame> Editor::canvasFrame() const
|
||||
{
|
||||
return CanvasViewActive() ? std::optional{m_CanvasView.GetFrame()} : std::nullopt;
|
||||
}
|
||||
|
||||
Camera* Editor::viewCamera()
|
||||
{
|
||||
return UsesEditorCamera() ? &m_Camera.GetCamera() : nullptr;
|
||||
@@ -102,7 +113,7 @@ void Editor::onImGuiBegin(float dt)
|
||||
ApplyPick();
|
||||
HandleShortcuts();
|
||||
|
||||
m_CameraHadInput = UsesEditorCamera() && (m_Viewport.hovered || m_Viewport.focused || m_Camera.IsLooking());
|
||||
m_CameraHadInput = UsesEditorCamera() && !CanvasViewActive() && (m_Viewport.hovered || m_Viewport.focused || m_Camera.IsLooking());
|
||||
if (UsesEditorCamera()) {
|
||||
m_Camera.Update(dt, m_CameraHadInput);
|
||||
}
|
||||
@@ -143,9 +154,12 @@ void Editor::onImGui(float)
|
||||
m_ResourcesInspector.drawImageCacheWindow(&m_Panels.imageCache, m_App.getResources(), m_App.getImguiPass());
|
||||
}
|
||||
|
||||
if (scene != nullptr && m_View.gizmos && UsesEditorCamera()) {
|
||||
if (scene != nullptr && UsesEditorCamera()) {
|
||||
GameObject* selected = scene->GetSelectedObject();
|
||||
if (selected != nullptr && !RectGizmo::CanManipulate(*selected)) {
|
||||
const bool rectSelected = selected != nullptr && RectGizmo::CanManipulate(*selected);
|
||||
if (CanvasViewActive() || (m_View.gizmos && rectSelected)) {
|
||||
m_CanvasView.DrawToolbar(m_RectGizmo);
|
||||
} else if (m_View.gizmos && selected != nullptr) {
|
||||
m_TransformGizmo.DrawToolbar();
|
||||
}
|
||||
}
|
||||
@@ -176,7 +190,11 @@ void Editor::onRender(GameRenderer& renderer)
|
||||
{
|
||||
const bool picking = m_PlayMode.IsEditing() || m_PlayMode.IsPaused();
|
||||
renderer.setSelectionPassEnabled(picking);
|
||||
renderer.setHoverPixel(picking ? m_Viewport.hoverPixel : std::nullopt);
|
||||
const bool sceneHidden = CanvasViewActive() && !m_CanvasView.ShowsScene();
|
||||
renderer.setHoverPixel(picking && !sceneHidden ? m_Viewport.hoverPixel : std::nullopt);
|
||||
if (CanvasViewActive()) {
|
||||
m_CanvasView.SubmitBackdrop();
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> highlighted;
|
||||
Scene* scene = CurrentScene();
|
||||
@@ -231,7 +249,10 @@ void Editor::HandleShortcuts()
|
||||
if (!m_Viewport.focused && !m_Viewport.hovered) {
|
||||
return;
|
||||
}
|
||||
if (UsesEditorCamera() && !m_Camera.IsLooking()) {
|
||||
if (UsesEditorCamera() && !m_Camera.IsLooking() && ImGui::IsKeyPressed(ImGuiKey_2, false) && !io.KeyCtrl) {
|
||||
m_CanvasView.Toggle();
|
||||
}
|
||||
if (UsesEditorCamera() && !m_Camera.IsLooking() && !CanvasViewActive()) {
|
||||
m_TransformGizmo.HandleShortcuts();
|
||||
}
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_F, false) && !io.KeyCtrl) {
|
||||
@@ -317,6 +338,10 @@ void Editor::DuplicateSelection()
|
||||
|
||||
void Editor::FocusSelection()
|
||||
{
|
||||
if (CanvasViewActive()) {
|
||||
m_CanvasView.Fit();
|
||||
return;
|
||||
}
|
||||
Scene* scene = CurrentScene();
|
||||
if (GameObject* selected = scene != nullptr ? scene->GetSelectedObject() : nullptr) {
|
||||
m_Camera.Focus(ComputeWorldBounds(*selected, m_App.getResources()));
|
||||
|
||||
@@ -198,6 +198,9 @@ void Editor::DrawViewMenu()
|
||||
|
||||
ImGui::SeparatorText("Scene View");
|
||||
GameRenderer& renderer = m_App.getRenderer();
|
||||
if (bool canvas = m_CanvasView.IsEnabled(); ImGui::MenuItem("Canvas Mode", "2", &canvas)) {
|
||||
m_CanvasView.SetEnabled(canvas);
|
||||
}
|
||||
ImGui::MenuItem("Gizmos", nullptr, &m_View.gizmos);
|
||||
ImGui::MenuItem("Colliders", nullptr, &m_View.colliders);
|
||||
if (bool wireframe = renderer.getRenderWireframe(); ImGui::MenuItem("Wireframe", nullptr, &wireframe)) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <destrum/App.h>
|
||||
#include <destrum/Assets/AssetTypes.h>
|
||||
#include <destrum/Components/AudioSource.h>
|
||||
#include <destrum/Components/UI/RectTransform.h>
|
||||
#include <destrum/Components/MeshRendererComponent.h>
|
||||
#include <destrum/Components/Physics/Collider.h>
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
@@ -92,6 +93,15 @@ void Editor::DrawViewport()
|
||||
m_Viewport.hoverPixel = glm::ivec2{uv * glm::vec2{static_cast<float>(extent.width), static_cast<float>(extent.height)}};
|
||||
}
|
||||
|
||||
const glm::vec2 drawImageSize{static_cast<float>(drawImage.extent.width), static_cast<float>(drawImage.extent.height)};
|
||||
m_CanvasView.SetDrawImageSize(drawImageSize);
|
||||
if (CanvasViewActive()) {
|
||||
const glm::vec2 pixelsPerPoint = drawImageSize / m_Viewport.size;
|
||||
const glm::vec2 mousePixel = (glm::vec2{mouse.x, mouse.y} - m_Viewport.min) * pixelsPerPoint;
|
||||
m_Viewport.mouseClaimed = m_CanvasView.HandleInput(mousePixel, pixelsPerPoint, m_Viewport.hovered);
|
||||
m_CanvasView.DrawOverlay(*ImGui::GetWindowDrawList(), m_Viewport.min, m_Viewport.size / drawImageSize);
|
||||
}
|
||||
|
||||
ImGui::SetCursorScreenPos(origin);
|
||||
if (Scene* scene = CurrentScene(); scene != nullptr && m_View.gizmos && UsesEditorCamera()) {
|
||||
DrawGizmos(*scene);
|
||||
@@ -115,6 +125,9 @@ void Editor::DrawGizmos(Scene& scene)
|
||||
m_Viewport.mouseClaimed = m_Viewport.mouseClaimed || m_RectGizmo.IsInteracting();
|
||||
return;
|
||||
}
|
||||
if (CanvasViewActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_TransformGizmo.Manipulate(*selected, m_Camera.GetCamera(), m_Viewport.min, m_Viewport.size);
|
||||
m_Viewport.mouseClaimed = m_Viewport.mouseClaimed || m_TransformGizmo.IsOver() || m_TransformGizmo.IsUsing();
|
||||
@@ -126,6 +139,20 @@ void Editor::RequestPick()
|
||||
if (!picking || !m_Viewport.hoverPixel || m_Viewport.mouseClaimed || !ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
return;
|
||||
}
|
||||
// GPU picking only sees meshes, UI is hit tested against the rects the canvases laid out last frame
|
||||
if (CanvasViewActive()) {
|
||||
if (Scene* scene = CurrentScene()) {
|
||||
GameObject* object = CanvasView::Pick(*scene, glm::vec2{*m_Viewport.hoverPixel});
|
||||
if (object != nullptr && ImGui::GetIO().KeyAlt) {
|
||||
if (const Transform* parent = object->GetTransform().GetParent();
|
||||
parent != nullptr && parent->GetOwner() != nullptr && parent->GetOwner()->GetComponent<RectTransform>() != nullptr) {
|
||||
object = parent->GetOwner();
|
||||
}
|
||||
}
|
||||
scene->SetSelectedObject(object != nullptr ? object->GetId() : InvalidObjectId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
m_App.getRenderer().requestSelectionPick(*m_Viewport.hoverPixel);
|
||||
m_Viewport.pickSelectsParent = ImGui::GetIO().KeyAlt;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <editor/RectGizmo.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
@@ -60,7 +61,6 @@ bool RectGizmo::Manipulate(GameObject& object, const glm::vec2& drawImageSize, c
|
||||
{
|
||||
m_Hovered = false;
|
||||
auto* rectTransform = object.GetComponent<RectTransform>();
|
||||
const Canvas* canvas = Canvas::FindFor(object);
|
||||
if (!CanManipulate(object) || drawImageSize.x <= 0.0f || drawImageSize.y <= 0.0f) {
|
||||
m_ActiveHandle = -1;
|
||||
return false;
|
||||
@@ -103,7 +103,7 @@ bool RectGizmo::Manipulate(GameObject& object, const glm::vec2& drawImageSize, c
|
||||
if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
|
||||
m_ActiveHandle = -1;
|
||||
} else {
|
||||
const float canvasScale = canvas->GetScale(drawImageSize);
|
||||
const float canvasScale = std::max(rectTransform->GetScreenScale(), 0.0001f);
|
||||
glm::vec2 delta = (mouse - m_DragStartMouse) / (viewScale * canvasScale);
|
||||
if (snap && snapStep > 0.0f) {
|
||||
delta = glm::round(delta / snapStep) * snapStep;
|
||||
|
||||
Reference in New Issue
Block a user