1f9b284de2
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> # Conflicts: # Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h # Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h # Code/Editor/Plugins/EditorCommon/stdafx.cpp # Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h # Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h # Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h # Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h # Code/Tools/AssetProcessor/native/precompiled.h # Code/Tools/Standalone/StandaloneTools_precompiled.h # Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h # Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h # Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h # Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h # Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h # Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h # Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake # Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h # Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake # Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h # Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h # Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h # Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h # Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h # Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp # Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake # Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h # Gems/Blast/Code/Source/StdAfx.cpp # Gems/Camera/Code/Source/Camera_precompiled.h # Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h # Gems/FastNoise/Code/Source/FastNoise_precompiled.h # Gems/Gestures/Code/Source/Gestures_precompiled.h # Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h # Gems/GraphCanvas/Code/precompiled.h # Gems/ImGui/Code/Source/ImGui_precompiled.h # Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h # Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h # Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp # Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h # Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h # Gems/LyShine/Code/Source/LyShine_precompiled.h # Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h # Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h # Gems/Maestro/Code/Source/Maestro_precompiled.h # Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h # Gems/Metastream/Code/Source/Metastream_precompiled.h # Gems/Microphone/Code/Source/Microphone_precompiled.h # Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h # Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h # Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h # Gems/PhysX/Code/Source/PhysX_precompiled.h # Gems/PhysX/Code/physx_unsupported_files.cmake # Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h # Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h # Gems/ScriptCanvas/Code/Editor/precompiled.h # Gems/ScriptCanvas/Code/Source/precompiled.h # Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h # Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h # Gems/ScriptEvents/Code/Source/precompiled.h # Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp # Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h # Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h # Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h # Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h # Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h # Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h # Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h # Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h # Gems/Twitch/Code/Source/Twitch_precompiled.h # Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h # Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h # Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
356 lines
12 KiB
C++
356 lines
12 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
#include "EditorCommon.h"
|
|
#include "ViewportPivot.h"
|
|
|
|
#include <LyShine/Bus/UiLayoutFitterBus.h>
|
|
|
|
namespace ViewportHelpers
|
|
{
|
|
bool IsControlledByLayout(const AZ::Entity* element)
|
|
{
|
|
AZ::Entity* parentElement = EntityHelpers::GetParentElement(element);
|
|
bool isControlledByParent = false;
|
|
if (parentElement)
|
|
{
|
|
EBUS_EVENT_ID_RESULT(isControlledByParent, parentElement->GetId(), UiLayoutBus, IsControllingChild, element->GetId());
|
|
}
|
|
|
|
return isControlledByParent;
|
|
}
|
|
|
|
float GetDpiScaledSize(float size)
|
|
{
|
|
return size * ViewportIcon::GetDpiScaleFactor();
|
|
}
|
|
|
|
bool IsHorizontallyFit(const AZ::Entity* element)
|
|
{
|
|
bool isHorizontallyFit = false;
|
|
|
|
EBUS_EVENT_ID_RESULT(isHorizontallyFit, element->GetId(), UiLayoutFitterBus, GetHorizontalFit);
|
|
|
|
return isHorizontallyFit;
|
|
}
|
|
|
|
bool IsVerticallyFit(const AZ::Entity* element)
|
|
{
|
|
bool isVerticallyFit = false;
|
|
|
|
EBUS_EVENT_ID_RESULT(isVerticallyFit, element->GetId(), UiLayoutFitterBus, GetVerticalFit);
|
|
|
|
return isVerticallyFit;
|
|
}
|
|
|
|
float GetPerpendicularAngle(float angle)
|
|
{
|
|
return fmodf(angle + 90.0f, 180.0f);
|
|
}
|
|
|
|
Qt::CursorShape GetSizingCursor(float angle)
|
|
{
|
|
Qt::CursorShape sizingCursors[] {
|
|
Qt::SizeVerCursor, Qt::SizeBDiagCursor, Qt::SizeHorCursor, Qt::SizeFDiagCursor
|
|
};
|
|
|
|
// The expected angle range is [-180, +180]. Each cursor covers two 45 degree
|
|
// sections that are opposite each other on that circle.
|
|
float section = 45.0f;
|
|
|
|
// Starting at -180, the transitions are Vert, BDiag (/), Horiz, FDiag (\) and
|
|
// continues in that same pattern for 0 to 180 (V,B,H,F), so the full pattern is
|
|
// V B H F V B H F which can be done with mod 4. However, the modulus operator
|
|
// doesn't handle negative values the way that we want, so we need to get the angle
|
|
// in the range [0, 360]. For our purposes, it's okay if the angle flips to its opposite.
|
|
angle += 180.0f;
|
|
|
|
// We shift the cursor sections by 45/2 degrees, so that the center of each cursor
|
|
// section is directly on a multiple of 45 degrees (0, 45, 90, etc.).
|
|
angle += 0.5f * section;
|
|
|
|
// Compute which section this angle is in.
|
|
int index = ((int)(angle / section)) % 4;
|
|
|
|
// Return the appropriate sizing cursor.
|
|
return sizingCursors[index];
|
|
}
|
|
|
|
void TransformIconScale(AZ::Vector2& iconSize, const AZ::Matrix4x4& transform)
|
|
{
|
|
// Make two unit vectors in untransformed space
|
|
AZ::Vector3 widthVec(1, 0, 0);
|
|
AZ::Vector3 heightVec(0, 1, 0);
|
|
|
|
// Convert these two unit vectors into the transformed space
|
|
widthVec = transform.Multiply3x3(widthVec);
|
|
heightVec = transform.Multiply3x3(heightVec);
|
|
|
|
// Divide the iconSize (for untransformed space) by the scale that each unit vector received
|
|
iconSize.SetX(iconSize.GetX() / AZ::Vector2(widthVec.GetX(), widthVec.GetY()).GetLength());
|
|
iconSize.SetY(iconSize.GetY() / AZ::Vector2(heightVec.GetX(), heightVec.GetY()).GetLength());
|
|
}
|
|
|
|
AZ::Vector2 ComputeAnchorPoint(AZ::Vector2 rectTopLeft, AZ::Vector2 rectSize, float anchorX, float anchorY)
|
|
{
|
|
rectSize.SetX(rectSize.GetX() * anchorX);
|
|
rectSize.SetY(rectSize.GetY() * anchorY);
|
|
return rectTopLeft + rectSize;
|
|
}
|
|
|
|
bool IsPointInIconRect(AZ::Vector2 point, AZ::Vector2 iconCenter, AZ::Vector2 iconSize, float leftPart, float rightPart, float topPart, float bottomPart)
|
|
{
|
|
float left = iconCenter.GetX() + leftPart * iconSize.GetX();
|
|
float right = iconCenter.GetX() + rightPart * iconSize.GetX();
|
|
float top = iconCenter.GetY() + topPart * iconSize.GetY();
|
|
float bottom = iconCenter.GetY() + bottomPart * iconSize.GetY();
|
|
|
|
return (left < point.GetX() && point.GetX() < right &&
|
|
top < point.GetY() && point.GetY() < bottom);
|
|
}
|
|
|
|
void GetHorizTargetPoints(const UiTransformInterface::RectPoints& elemRect, float y, AZ::Vector2& leftTarget, AZ::Vector2& rightTarget)
|
|
{
|
|
leftTarget = (elemRect.TopLeft() + elemRect.BottomLeft()) * 0.5f;
|
|
rightTarget = (elemRect.TopRight() + elemRect.BottomRight()) * 0.5f;
|
|
|
|
if (y >= elemRect.TopLeft().GetY())
|
|
{
|
|
if (y <= elemRect.BottomLeft().GetY())
|
|
{
|
|
leftTarget.SetY(y);
|
|
rightTarget.SetY(leftTarget.GetY());
|
|
}
|
|
else
|
|
{
|
|
leftTarget.SetY(elemRect.BottomLeft().GetY());
|
|
rightTarget.SetY(leftTarget.GetY());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
leftTarget.SetY(elemRect.TopLeft().GetY());
|
|
rightTarget.SetY(leftTarget.GetY());
|
|
}
|
|
}
|
|
|
|
void GetVerticalTargetPoints(const UiTransformInterface::RectPoints& elemRect, float x, AZ::Vector2& topTarget, AZ::Vector2& bottomTarget)
|
|
{
|
|
topTarget = (elemRect.TopLeft() + elemRect.TopRight()) * 0.5f;
|
|
bottomTarget = (elemRect.BottomLeft() + elemRect.BottomRight()) * 0.5f;
|
|
|
|
if (x >= elemRect.TopLeft().GetX())
|
|
{
|
|
if (x <= elemRect.TopRight().GetX())
|
|
{
|
|
topTarget.SetX(x);
|
|
bottomTarget.SetX(topTarget.GetX());
|
|
}
|
|
else
|
|
{
|
|
topTarget.SetX(elemRect.TopRight().GetX());
|
|
bottomTarget.SetX(topTarget.GetX());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
topTarget.SetX(elemRect.TopLeft().GetX());
|
|
bottomTarget.SetX(topTarget.GetX());
|
|
}
|
|
}
|
|
|
|
UiTransform2dInterface::Offsets MoveGrabbedEdges(const UiTransform2dInterface::Offsets& offset,
|
|
const ViewportHelpers::ElementEdges& grabbedEdges,
|
|
const AZ::Vector2& v)
|
|
{
|
|
UiTransform2dInterface::Offsets outOffset(offset);
|
|
|
|
outOffset.m_left += (grabbedEdges.m_left ? v.GetX() : 0.0f);
|
|
outOffset.m_right += (grabbedEdges.m_right ? v.GetX() : 0.0f);
|
|
outOffset.m_top += (grabbedEdges.m_top ? v.GetY() : 0.0f);
|
|
outOffset.m_bottom += (grabbedEdges.m_bottom ? v.GetY() : 0.0f);
|
|
|
|
return outOffset;
|
|
}
|
|
|
|
UiTransform2dInterface::Anchors MoveGrabbedAnchor(const UiTransform2dInterface::Anchors& anchor,
|
|
const ViewportHelpers::SelectedAnchors& grabbedAnchors, bool keepTogetherHorizontally,
|
|
bool keepTogetherVertically, const AZ::Vector2& v)
|
|
{
|
|
UiTransform2dInterface::Anchors outAnchor(anchor);
|
|
|
|
outAnchor.m_left += (grabbedAnchors.m_left) ? v.GetX() : 0.0f;
|
|
outAnchor.m_right += (grabbedAnchors.m_right) ? v.GetX() : 0.0f;
|
|
outAnchor.m_top += (grabbedAnchors.m_top) ? v.GetY() : 0.0f;
|
|
outAnchor.m_bottom += (grabbedAnchors.m_bottom) ? v.GetY() : 0.0f;
|
|
|
|
if (keepTogetherHorizontally)
|
|
{
|
|
if (grabbedAnchors.m_left && !grabbedAnchors.m_right)
|
|
{
|
|
outAnchor.m_right = outAnchor.m_left;
|
|
}
|
|
else if (grabbedAnchors.m_right && !grabbedAnchors.m_left)
|
|
{
|
|
outAnchor.m_left = outAnchor.m_right;
|
|
}
|
|
}
|
|
|
|
if (keepTogetherVertically)
|
|
{
|
|
if (grabbedAnchors.m_top && !grabbedAnchors.m_bottom)
|
|
{
|
|
outAnchor.m_bottom = outAnchor.m_top;
|
|
}
|
|
else if (grabbedAnchors.m_bottom && !grabbedAnchors.m_top)
|
|
{
|
|
outAnchor.m_top = outAnchor.m_bottom;
|
|
}
|
|
}
|
|
|
|
// Clamp the anchors
|
|
outAnchor.UnitClamp();
|
|
|
|
return outAnchor;
|
|
}
|
|
|
|
void MoveGrabbedEdges(UiTransformInterface::RectPoints& points,
|
|
const ViewportHelpers::ElementEdges& grabbedEdges,
|
|
const AZ::Vector2& topEdge,
|
|
const AZ::Vector2& leftEdge)
|
|
{
|
|
if (grabbedEdges.m_left)
|
|
{
|
|
points.TopLeft() += topEdge;
|
|
points.BottomLeft() += topEdge;
|
|
}
|
|
if (grabbedEdges.m_right)
|
|
{
|
|
points.TopRight() += topEdge;
|
|
points.BottomRight() += topEdge;
|
|
}
|
|
if (grabbedEdges.m_top)
|
|
{
|
|
points.TopLeft() += leftEdge;
|
|
points.TopRight() += leftEdge;
|
|
}
|
|
if (grabbedEdges.m_bottom)
|
|
{
|
|
points.BottomLeft() += leftEdge;
|
|
points.BottomRight() += leftEdge;
|
|
}
|
|
}
|
|
|
|
const char* InteractionModeToString(int mode)
|
|
{
|
|
switch (static_cast<ViewportInteraction::InteractionMode>(mode))
|
|
{
|
|
case ViewportInteraction::InteractionMode::SELECTION:
|
|
return "Selection";
|
|
break;
|
|
case ViewportInteraction::InteractionMode::MOVE:
|
|
return "Move";
|
|
break;
|
|
case ViewportInteraction::InteractionMode::ANCHOR:
|
|
return "Anchor";
|
|
break;
|
|
case ViewportInteraction::InteractionMode::ROTATE:
|
|
return "Rotate";
|
|
break;
|
|
case ViewportInteraction::InteractionMode::RESIZE:
|
|
return "Resize";
|
|
break;
|
|
default:
|
|
AZ_Assert(false, "Invalid mode");
|
|
return "UNKNOWN";
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char* CoordinateSystemToString(int s)
|
|
{
|
|
switch (static_cast<ViewportInteraction::CoordinateSystem>(s))
|
|
{
|
|
case ViewportInteraction::CoordinateSystem::LOCAL:
|
|
return "Local";
|
|
break;
|
|
case ViewportInteraction::CoordinateSystem::VIEW:
|
|
return "View";
|
|
break;
|
|
default:
|
|
AZ_Assert(false, "Invalid coordinate system enum value");
|
|
return "UNKNOWN";
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char* InteractionTypeToString(int type)
|
|
{
|
|
switch (static_cast<ViewportInteraction::InteractionType>(type))
|
|
{
|
|
case ViewportInteraction::InteractionType::DIRECT:
|
|
return "DIRECT";
|
|
break;
|
|
case ViewportInteraction::InteractionType::TRANSFORM_GIZMO:
|
|
return "TRANSFORM_GIZMO";
|
|
break;
|
|
case ViewportInteraction::InteractionType::ANCHORS:
|
|
return "ANCHORS";
|
|
break;
|
|
case ViewportInteraction::InteractionType::PIVOT:
|
|
return "PIVOT";
|
|
break;
|
|
case ViewportInteraction::InteractionType::NONE:
|
|
return "NONE";
|
|
break;
|
|
default:
|
|
AZ_Assert(false, "Invalid interaction type");
|
|
return "UNKNOWN";
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DrawRotationValue(const AZ::Entity* element,
|
|
ViewportInteraction* viewportInteraction,
|
|
const ViewportPivot* viewportPivot,
|
|
Draw2dHelper& draw2d)
|
|
{
|
|
// Draw the rotation in degrees when the left mouse button is down on the rotation gizmo
|
|
if ((viewportInteraction->GetInteractionType() == ViewportInteraction::InteractionType::TRANSFORM_GIZMO)
|
|
&& viewportInteraction->GetLeftButtonIsActive())
|
|
{
|
|
float rotation = 0.0f;
|
|
EBUS_EVENT_ID_RESULT(rotation, element->GetId(), UiTransformBus, GetZRotation);
|
|
QString rotationString = QString("%1").number(rotation, 'f', 2);
|
|
QChar degChar(0xB0);
|
|
rotationString.append(degChar);
|
|
|
|
AZ::Vector2 pivotPos;
|
|
EBUS_EVENT_ID_RESULT(pivotPos, element->GetId(), UiTransformBus, GetViewportSpacePivot);
|
|
float offset = (viewportPivot->GetSize().GetY() * 0.5f) + (GetDpiScaledSize(4.0f));
|
|
AZ::Vector2 rotationStringPos(pivotPos.GetX(), pivotPos.GetY() - offset);
|
|
|
|
draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom);
|
|
draw2d.SetTextRotation(0.0f);
|
|
draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, GetDpiScaledSize(16.0f), 1.0f);
|
|
}
|
|
}
|
|
|
|
void DrawCursorText(const AZStd::string& textLabel,
|
|
Draw2dHelper& draw2d,
|
|
const ViewportWidget* viewport)
|
|
{
|
|
const AZ::Vector2 textLabelOffset(10.0f, -10.0f);
|
|
QPoint viewportCursorPos = viewport->mapFromGlobal(QCursor::pos());
|
|
AZ::Vector2 textPos = AZ::Vector2(aznumeric_cast<float>(viewportCursorPos.x()), aznumeric_cast<float>(viewportCursorPos.y())) + textLabelOffset;
|
|
|
|
draw2d.SetTextAlignment(IDraw2d::HAlign::Left, IDraw2d::VAlign::Bottom);
|
|
draw2d.SetTextRotation(0.0f);
|
|
draw2d.DrawText(textLabel.c_str(), textPos, GetDpiScaledSize(16.0f), 1.0f);
|
|
}
|
|
} // namespace ViewportHelpers
|