feat: add basic canvas / improve inspector
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
#include <destrum/Util/RectGizmo.h>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
#include <destrum/Components/UI/Canvas.h>
|
||||
#include <destrum/Components/UI/RectTransform.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace {
|
||||
constexpr int MoveHandle = 0;
|
||||
constexpr float HandleRadius = 5.0f;
|
||||
constexpr std::array<glm::vec2, 8> HandleDirections{{
|
||||
{-1.0f, -1.0f}, {0.0f, -1.0f}, {1.0f, -1.0f}, {1.0f, 0.0f},
|
||||
{1.0f, 1.0f}, {0.0f, 1.0f}, {-1.0f, 1.0f}, {-1.0f, 0.0f},
|
||||
}};
|
||||
|
||||
constexpr ImU32 OutlineColor = IM_COL32(60, 200, 255, 255);
|
||||
constexpr ImU32 ActiveColor = IM_COL32(255, 200, 60, 255);
|
||||
constexpr ImU32 HiddenColor = IM_COL32(150, 150, 150, 200);
|
||||
constexpr ImU32 AnchorColor = IM_COL32(255, 255, 255, 200);
|
||||
|
||||
[[nodiscard]] ImVec2 ToImVec(const glm::vec2& value) { return ImVec2{value.x, value.y}; }
|
||||
[[nodiscard]] glm::vec2 ToVec(const ImVec2& value) { return glm::vec2{value.x, value.y}; }
|
||||
|
||||
[[nodiscard]] ImGuiMouseCursor CursorFor(int handle)
|
||||
{
|
||||
if (handle == MoveHandle) {
|
||||
return ImGuiMouseCursor_ResizeAll;
|
||||
}
|
||||
const glm::vec2 direction = HandleDirections[static_cast<std::size_t>(handle - 1)];
|
||||
if (direction.x == 0.0f) {
|
||||
return ImGuiMouseCursor_ResizeNS;
|
||||
}
|
||||
if (direction.y == 0.0f) {
|
||||
return ImGuiMouseCursor_ResizeEW;
|
||||
}
|
||||
return direction.x == direction.y ? ImGuiMouseCursor_ResizeNWSE : ImGuiMouseCursor_ResizeNESW;
|
||||
}
|
||||
|
||||
void DrawAnchor(ImDrawList& drawList, const glm::vec2& point, const glm::vec2& outwards)
|
||||
{
|
||||
constexpr float size = 8.0f;
|
||||
const glm::vec2 tip = point;
|
||||
const glm::vec2 a = point + glm::vec2{outwards.x * size, outwards.y * size * 0.4f};
|
||||
const glm::vec2 b = point + glm::vec2{outwards.x * size * 0.4f, outwards.y * size};
|
||||
drawList.AddTriangleFilled(ToImVec(tip), ToImVec(a), ToImVec(b), AnchorColor);
|
||||
}
|
||||
}
|
||||
|
||||
bool RectGizmo::CanManipulate(GameObject& object)
|
||||
{
|
||||
return object.GetComponent<RectTransform>() != nullptr && object.GetComponent<Canvas>() == nullptr &&
|
||||
Canvas::FindFor(object) != nullptr;
|
||||
}
|
||||
|
||||
bool RectGizmo::Manipulate(GameObject& object, const glm::vec2& drawImageSize)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
const glm::vec2 viewOrigin = ToVec(viewport->Pos);
|
||||
const glm::vec2 viewScale = ToVec(viewport->Size) / drawImageSize;
|
||||
const auto toView = [&](const glm::vec2& point) { return viewOrigin + point * viewScale; };
|
||||
|
||||
const UIRect& rect = rectTransform->GetScreenRect();
|
||||
const glm::vec2 viewMin = toView(rect.min);
|
||||
const glm::vec2 viewMax = toView(rect.Max());
|
||||
const glm::vec2 viewCenter = (viewMin + viewMax) * 0.5f;
|
||||
const glm::vec2 viewHalfSize = (viewMax - viewMin) * 0.5f;
|
||||
|
||||
const glm::vec2 mouse = ToVec(ImGui::GetIO().MousePos);
|
||||
int hovered = -1;
|
||||
const bool overImGui = ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow) || ImGui::IsAnyItemActive();
|
||||
if (m_ActiveHandle < 0 && !overImGui) {
|
||||
for (std::size_t i = 0; i < HandleDirections.size(); ++i) {
|
||||
const glm::vec2 handle = viewCenter + HandleDirections[i] * viewHalfSize;
|
||||
if (glm::length(mouse - handle) <= HandleRadius + 2.0f) {
|
||||
hovered = static_cast<int>(i) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hovered < 0 && mouse.x >= viewMin.x && mouse.y >= viewMin.y && mouse.x <= viewMax.x && mouse.y <= viewMax.y) {
|
||||
hovered = MoveHandle;
|
||||
}
|
||||
if (hovered >= 0 && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
m_ActiveHandle = hovered;
|
||||
m_DragStartMouse = mouse;
|
||||
m_DragStartPosition = rectTransform->GetPosition();
|
||||
m_DragStartSize = rectTransform->GetSize();
|
||||
}
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
if (m_ActiveHandle >= 0) {
|
||||
if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
|
||||
m_ActiveHandle = -1;
|
||||
} else {
|
||||
const float canvasScale = canvas->GetScale(drawImageSize);
|
||||
glm::vec2 delta = (mouse - m_DragStartMouse) / (viewScale * canvasScale);
|
||||
if (snap && snapStep > 0.0f) {
|
||||
delta = glm::round(delta / snapStep) * snapStep;
|
||||
}
|
||||
|
||||
glm::vec2 position = m_DragStartPosition;
|
||||
glm::vec2 size = m_DragStartSize;
|
||||
if (m_ActiveHandle == MoveHandle) {
|
||||
position += delta;
|
||||
} else {
|
||||
// The edge opposite the handle stays put, so the pivot moves by the pivot's share of the resize
|
||||
const glm::vec2 direction = HandleDirections[static_cast<std::size_t>(m_ActiveHandle - 1)];
|
||||
const glm::vec2& pivot = rectTransform->GetPivot();
|
||||
for (int axis = 0; axis < 2; ++axis) {
|
||||
if (direction[axis] == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
size[axis] += direction[axis] * delta[axis];
|
||||
position[axis] += delta[axis] * (direction[axis] > 0.0f ? pivot[axis] : 1.0f - pivot[axis]);
|
||||
}
|
||||
}
|
||||
|
||||
if (position != rectTransform->GetPosition() || size != rectTransform->GetSize()) {
|
||||
rectTransform->SetPosition(position);
|
||||
rectTransform->SetSize(size);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int highlighted = m_ActiveHandle >= 0 ? m_ActiveHandle : hovered;
|
||||
if (highlighted >= 0) {
|
||||
ImGui::SetMouseCursor(CursorFor(highlighted));
|
||||
// Keeps the click from reaching the game, same as the 3D gizmo
|
||||
ImGui::SetNextFrameWantCaptureMouse(true);
|
||||
}
|
||||
|
||||
ImDrawList& drawList = *ImGui::GetBackgroundDrawList();
|
||||
const ImU32 outlineColor = object.IsActiveInHierarchy() ? OutlineColor : HiddenColor;
|
||||
|
||||
const UIRect& parentRect = rectTransform->GetParentRect();
|
||||
const glm::vec2 anchorMin = toView(parentRect.min + parentRect.size * rectTransform->GetAnchorMin());
|
||||
const glm::vec2 anchorMax = toView(parentRect.min + parentRect.size * rectTransform->GetAnchorMax());
|
||||
DrawAnchor(drawList, {anchorMin.x, anchorMin.y}, {-1.0f, -1.0f});
|
||||
DrawAnchor(drawList, {anchorMax.x, anchorMin.y}, {1.0f, -1.0f});
|
||||
DrawAnchor(drawList, {anchorMax.x, anchorMax.y}, {1.0f, 1.0f});
|
||||
DrawAnchor(drawList, {anchorMin.x, anchorMax.y}, {-1.0f, 1.0f});
|
||||
|
||||
drawList.AddRect(ToImVec(viewMin), ToImVec(viewMax), highlighted == MoveHandle ? ActiveColor : outlineColor, 0.0f, 0, 1.5f);
|
||||
|
||||
const glm::vec2 pivot = viewMin + (viewMax - viewMin) * rectTransform->GetPivot();
|
||||
drawList.AddCircle(ToImVec(pivot), 4.0f, outlineColor, 12, 1.5f);
|
||||
|
||||
for (std::size_t i = 0; i < HandleDirections.size(); ++i) {
|
||||
const glm::vec2 handle = viewCenter + HandleDirections[i] * viewHalfSize;
|
||||
const ImU32 color = highlighted == static_cast<int>(i) + 1 ? ActiveColor : outlineColor;
|
||||
drawList.AddRectFilled(ToImVec(handle - glm::vec2{HandleRadius}), ToImVec(handle + glm::vec2{HandleRadius}), color);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
Reference in New Issue
Block a user