/* * 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 "EditorDefs.h" #include "Viewport.h" // Qt #include // AzQtComponents #include #include #include // AzToolsFramework #include #include #include // Editor #include "Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h" #include "ViewManager.h" #include "Include/HitContext.h" #include "Util/3DConnexionDriver.h" #include "PluginManager.h" #include "GameEngine.h" #include "Settings.h" #include #ifdef LoadCursor #undef LoadCursor #endif ////////////////////////////////////////////////////////////////////// // Viewport drag and drop support ////////////////////////////////////////////////////////////////////// void QtViewport::BuildDragDropContext( AzQtComponents::ViewportDragContext& context, const AzFramework::ViewportId viewportId, const QPoint& point) { context.m_hitLocation = AzToolsFramework::FindClosestPickIntersection( viewportId, AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(point * devicePixelRatioF()), AzToolsFramework::EditorPickRayLength, AzToolsFramework::GetDefaultEntityPlacementDistance()); } void QtViewport::dragEnterEvent(QDragEnterEvent* event) { if (!GetIEditor()->GetGameEngine()->IsLevelLoaded()) { return; } // first use the legacy pathway, which assumes its always okay as long as any callback is installed if (m_dropCallback) { event->setDropAction(Qt::CopyAction); event->setAccepted(true); } else { // new bus-based way of doing it (install a listener!) using namespace AzQtComponents; ViewportDragContext context; BuildDragDropContext(context, GetViewportId(), event->pos()); DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::DragEnter, event, context); } } void QtViewport::dragMoveEvent(QDragMoveEvent* event) { if (!GetIEditor()->GetGameEngine()->IsLevelLoaded()) { return; } // first use the legacy pathway, which assumes its always okay as long as any callback is installed if (m_dropCallback) { event->setDropAction(Qt::CopyAction); event->setAccepted(true); } else { // new bus-based way of doing it (install a listener!) using namespace AzQtComponents; ViewportDragContext context; BuildDragDropContext(context, GetViewportId(), event->pos()); DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::DragMove, event, context); } } void QtViewport::dropEvent(QDropEvent* event) { using namespace AzQtComponents; if (!GetIEditor()->GetGameEngine()->IsLevelLoaded()) { return; } // first use the legacy pathway, which assumes its always okay as long as any callback is installed if (m_dropCallback) { m_dropCallback(this, event->pos().x(), event->pos().y(), m_dropCallbackCustom); event->setAccepted(true); } else { // new bus-based way of doing it (install a listener!) ViewportDragContext context; BuildDragDropContext(context, GetViewportId(), event->pos()); DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::Drop, event, context); if (event->isAccepted()) { // send focus to whatever window accepted it. Its not necessarily this window, as it might be a child embedded in it. QWidget* widget = qApp->widgetAt(event->pos()); if (widget) { widget->setFocus(Qt::MouseFocusReason); } } } } void QtViewport::dragLeaveEvent(QDragLeaveEvent* event) { using namespace AzQtComponents; DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::DragLeave, event); } ////////////////////////////////////////////////////////////////////////// float CViewport::GetFOV() const { return SandboxEditor::CameraDefaultFovRadians(); } ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// bool QtViewport::m_bDegradateQuality = false; QtViewport::QtViewport(QWidget* parent) : QWidget(parent) , m_renderOverlay(this) { m_cViewMenu.addMenu(tr("&View Options"))->addAction(tr("&Fullscreen")); //connect(action, &QAction::triggered, ); m_selectionTolerance = 0; m_fGridZoom = 1.0f; m_nLastUpdateFrame = 0; m_nLastMouseMoveFrame = 0; ////////////////////////////////////////////////////////////////////////// // Init standard cursors. ////////////////////////////////////////////////////////////////////////// m_hCurrCursor = QCursor(); m_hCursor[STD_CURSOR_DEFAULT] = QCursor(Qt::ArrowCursor); m_hCursor[STD_CURSOR_HIT] = CMFCUtils::LoadCursor(IDC_POINTER_OBJHIT); m_hCursor[STD_CURSOR_MOVE] = CMFCUtils::LoadCursor(IDC_POINTER_OBJECT_MOVE); m_hCursor[STD_CURSOR_ROTATE] = CMFCUtils::LoadCursor(IDC_POINTER_OBJECT_ROTATE); m_hCursor[STD_CURSOR_SCALE] = CMFCUtils::LoadCursor(IDC_POINTER_OBJECT_SCALE); m_hCursor[STD_CURSOR_SEL_PLUS] = CMFCUtils::LoadCursor(IDC_POINTER_PLUS); m_hCursor[STD_CURSOR_SEL_MINUS] = CMFCUtils::LoadCursor(IDC_POINTER_MINUS); m_hCursor[STD_CURSOR_SUBOBJ_SEL] = CMFCUtils::LoadCursor(IDC_POINTER_SO_SELECT); m_hCursor[STD_CURSOR_SUBOBJ_SEL_PLUS] = CMFCUtils::LoadCursor(IDC_POINTER_SO_SELECT_PLUS); m_hCursor[STD_CURSOR_SUBOBJ_SEL_MINUS] = CMFCUtils::LoadCursor(IDC_POINTER_SO_SELECT_MINUS); m_activeAxis = AXIS_TERRAIN; m_screenTM.SetIdentity(); m_bAdvancedSelectMode = false; GetIEditor()->GetViewManager()->RegisterViewport(this); m_nCurViewportID = MAX_NUM_VIEWPORTS - 1; m_dropCallback = nullptr; // Leroy@Conffx setMouseTracking(true); setFocusPolicy(Qt::StrongFocus); // Create drop target to handle Qt drop events. setAcceptDrops(true); m_renderOverlay.setVisible(true); m_renderOverlay.setUpdatesEnabled(false); m_renderOverlay.setMouseTracking(true); m_renderOverlay.setObjectName("renderOverlay"); m_renderOverlay.winId(); // Force the render overlay to create a backing native window m_viewportUi.InitializeViewportUi(this, &m_renderOverlay); setAcceptDrops(true); } ////////////////////////////////////////////////////////////////////////// QtViewport::~QtViewport() { GetIEditor()->GetViewManager()->UnregisterViewport(this); } ////////////////////////////////////////////////////////////////////////// void QtViewport::ScreenToClient(QPoint& pPoint) const { pPoint = mapFromGlobal(pPoint); } ////////////////////////////////////////////////////////////////////////// void QtViewport::GetDimensions(int* pWidth, int* pHeight) const { if (pWidth) { *pWidth = width(); } if (pHeight) { *pHeight = height(); } } ////////////////////////////////////////////////////////////////////////// void QtViewport::AddPostRenderer(IPostRenderer* pPostRenderer) { stl::push_back_unique(m_postRenderers, pPostRenderer); } ////////////////////////////////////////////////////////////////////////// bool QtViewport::RemovePostRenderer(IPostRenderer* pPostRenderer) { PostRenderers::iterator itr = m_postRenderers.begin(); PostRenderers::iterator end = m_postRenderers.end(); for (; itr != end; ++itr) { if (*itr == pPostRenderer) { break; } } if (itr != end) { m_postRenderers.erase(itr); return true; } return false; } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnMouseWheel([[maybe_unused]] Qt::KeyboardModifiers modifiers, short zDelta, [[maybe_unused]] const QPoint& pt) { if (zDelta != 0) { float z = GetZoomFactor() + (zDelta / 120.0f) * 0.5f; SetZoomFactor(z); GetIEditor()->GetViewManager()->SetZoomFactor(z); } } ////////////////////////////////////////////////////////////////////////// QString QtViewport::GetName() const { return windowTitle(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetName(const QString& name) { setWindowTitle(name); } ////////////////////////////////////////////////////////////////////////// void QtViewport::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); m_renderOverlay.setGeometry(rect()); Update(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::paintEvent([[maybe_unused]] QPaintEvent* event) { QPainter painter(this); // Fill the entire client area painter.fillRect(rect(), QColor(0xf0, 0xf0, 0xf0)); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnActivate() { //////////////////////////////////////////////////////////////////////// // Make this edit window the current one //////////////////////////////////////////////////////////////////////// } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnDeactivate() { } ////////////////////////////////////////////////////////////////////////// void QtViewport::ResetContent() { } ////////////////////////////////////////////////////////////////////////// void QtViewport::UpdateContent(int flags) { if (flags & eRedrawViewports) { update(); } } ////////////////////////////////////////////////////////////////////////// void QtViewport::Update() { m_viewportUi.Update(); m_bAdvancedSelectMode = false; if (CheckVirtualKey(Qt::Key_Space) && !CheckVirtualKey(Qt::Key_Shift) && hasFocus()) { m_bAdvancedSelectMode = true; } m_nLastUpdateFrame++; } ////////////////////////////////////////////////////////////////////////// QPoint QtViewport::WorldToView(const Vec3& wp) const { return QPoint(static_cast(wp.x), static_cast(wp.y)); } ////////////////////////////////////////////////////////////////////////// Vec3 QtViewport::WorldToView3D(const Vec3& wp, [[maybe_unused]] int nFlags) const { QPoint p = WorldToView(wp); Vec3 out; out.x = static_cast(p.x()); out.y = static_cast(p.y()); out.z = wp.z; return out; } ////////////////////////////////////////////////////////////////////////// Vec3 QtViewport::ViewToWorld(const QPoint& vp, bool* pCollideWithTerrain, [[maybe_unused]] bool onlyTerrain, [[maybe_unused]] bool bSkipVegetation, [[maybe_unused]] bool bTestRenderMesh, [[maybe_unused]] bool* collideWithObject) const { Vec3 wp; wp.x = static_cast(vp.x()); wp.y = static_cast(vp.y()); wp.z = 0; if (pCollideWithTerrain) { *pCollideWithTerrain = true; } return wp; } ////////////////////////////////////////////////////////////////////////// Vec3 QtViewport::ViewToWorldNormal([[maybe_unused]] const QPoint& vp, [[maybe_unused]] bool onlyTerrain, [[maybe_unused]] bool bTestRenderMesh) { return Vec3(0, 0, 0); } ////////////////////////////////////////////////////////////////////////// void QtViewport::ViewToWorldRay([[maybe_unused]] const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const { raySrc(0, 0, 0); rayDir(0, 0, -1); } void QtViewport::mousePressEvent(QMouseEvent* event) { switch (event->button()) { case Qt::LeftButton: OnLButtonDown(event->modifiers(), event->pos()); break; case Qt::MiddleButton: OnMButtonDown(event->modifiers(), event->pos()); break; case Qt::RightButton: OnRButtonDown(event->modifiers(), event->pos()); break; } } void QtViewport::mouseReleaseEvent(QMouseEvent* event) { switch (event->button()) { case Qt::LeftButton: OnLButtonUp(event->modifiers(), event->pos()); break; case Qt::MiddleButton: OnMButtonUp(event->modifiers(), event->pos()); break; case Qt::RightButton: OnRButtonUp(event->modifiers(), event->pos()); break; } // For MFC compatibility, send a spurious move event after a button release. // CryDesigner depends on this behaviour mouseMoveEvent(event); } void QtViewport::mouseDoubleClickEvent(QMouseEvent* event) { switch (event->button()) { case Qt::LeftButton: OnLButtonDblClk(event->modifiers(), event->pos()); break; case Qt::MiddleButton: OnMButtonDblClk(event->modifiers(), event->pos()); break; case Qt::RightButton: OnRButtonDblClk(event->modifiers(), event->pos()); break; } } void QtViewport::mouseMoveEvent(QMouseEvent* event) { OnMouseMove(event->modifiers(), event->buttons(), event->pos()); OnSetCursor(); } void QtViewport::wheelEvent(QWheelEvent* event) { OnMouseWheel(event->modifiers(), static_cast(event->angleDelta().y()), event->position().toPoint()); event->accept(); } void QtViewport::keyPressEvent(QKeyEvent* event) { int nativeKey = event->nativeVirtualKey(); #if AZ_TRAIT_OS_PLATFORM_APPLE // nativeVirtualKey is always zero on macOS, therefore we // need to manually set the nativeKey based on the Qt key switch (event->key()) { case Qt::Key_Control: nativeKey = VK_CONTROL; break; case Qt::Key_Alt: nativeKey = VK_MENU; break; case Qt::Key_QuoteLeft: nativeKey = VK_OEM_3; break; case Qt::Key_BracketLeft: nativeKey = VK_OEM_4; break; case Qt::Key_BracketRight: nativeKey = VK_OEM_6; break; case Qt::Key_Comma: nativeKey = VK_OEM_COMMA; break; case Qt::Key_Period: nativeKey = VK_OEM_PERIOD; break; case Qt::Key_Escape: nativeKey = VK_ESCAPE; break; } #endif OnKeyDown(nativeKey, 1, event->nativeModifiers()); } void QtViewport::keyReleaseEvent(QKeyEvent* event) { int nativeKey = event->nativeVirtualKey(); #if AZ_TRAIT_OS_PLATFORM_APPLE // nativeVirtualKey is always zero on macOS, therefore we // need to manually set the nativeKey based on the Qt key switch (event->key()) { case Qt::Key_Control: nativeKey = VK_CONTROL; break; case Qt::Key_Alt: nativeKey = VK_MENU; break; case Qt::Key_QuoteLeft: nativeKey = VK_OEM_3; break; case Qt::Key_BracketLeft: nativeKey = VK_OEM_4; break; case Qt::Key_BracketRight: nativeKey = VK_OEM_6; break; case Qt::Key_Comma: nativeKey = VK_OEM_COMMA; break; case Qt::Key_Period: nativeKey = VK_OEM_PERIOD; break; case Qt::Key_Escape: nativeKey = VK_ESCAPE; break; } #endif OnKeyUp(nativeKey, 1, event->nativeModifiers()); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnSetCursor() { } ////////////////////////////////////////////////////////////////////////// void QtViewport::ResetSelectionRegion() { m_selectedRect = QRect(); } void QtViewport::SetSelectionRectangle(const QRect& rect) { m_selectedRect = rect.normalized(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnDragSelectRectangle(const QRect& rect, bool bNormalizeRect) { Vec3 org; AZ::Aabb box = AZ::Aabb::CreateNull(); //adjust QRect bottom and right corner once before extracting bottom/right coordinates const QRect correctedRect = rect.adjusted(0, 0, 1, 1); Vec3 p1 = ViewToWorld(correctedRect.topLeft()); Vec3 p2 = ViewToWorld(correctedRect.bottomRight()); org = p1; // Calculate selection volume. if (!bNormalizeRect) { box.AddPoint(LYVec3ToAZVec3(p1)); box.AddPoint(LYVec3ToAZVec3(p2)); } else { const QRect rc = correctedRect.normalized(); box.AddPoint(LYVec3ToAZVec3(ViewToWorld(rc.topLeft()))); box.AddPoint(LYVec3ToAZVec3(ViewToWorld(rc.topRight()))); box.AddPoint(LYVec3ToAZVec3(ViewToWorld(rc.bottomLeft()))); box.AddPoint(LYVec3ToAZVec3(ViewToWorld(rc.bottomRight()))); } AZ::Vector3 boxMin = box.GetMin(); AZ::Vector3 boxMax = box.GetMax(); boxMin.SetZ(-10000); boxMax.SetZ(10000); box.Set(boxMin, boxMax); // Show marker position in the status bar float w = box.GetXExtent(); float h = box.GetYExtent(); char szNewStatusText[512]; sprintf_s(szNewStatusText, "X:%g Y:%g Z:%g W:%g H:%g", org.x, org.y, org.z, w, h); GetIEditor()->SetStatusText(szNewStatusText); } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetCurrentCursor(const QCursor& hCursor, const QString& cursorString) { setCursor(hCursor); m_cursorStr = cursorString; } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetCurrentCursor(EStdCursor stdCursor, const QString& cursorString) { QCursor hCursor = m_hCursor[stdCursor]; setCursor(hCursor); m_cursorStr = cursorString; } void QtViewport::SetSupplementaryCursorStr(const QString& str) { m_cursorSupplementaryStr = str; } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetCurrentCursor(EStdCursor stdCursor) { QCursor hCursor = m_hCursor[stdCursor]; setCursor(hCursor); } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetCursorString(const QString& cursorString) { m_cursorStr = cursorString; } ////////////////////////////////////////////////////////////////////////// void QtViewport::ResetCursor() { SetCurrentCursor(STD_CURSOR_DEFAULT, QString()); } ////////////////////////////////////////////////////////////////////////// HWND QtViewport::renderOverlayHWND() const { return reinterpret_cast(m_renderOverlay.winId()); } ////////////////////////////////////////////////////////////////////////// void QtViewport::setRenderOverlayVisible(bool visible) { m_renderOverlay.setVisible(visible); } bool QtViewport::isRenderOverlayVisible() const { return m_renderOverlay.isVisible(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetAxisConstrain(int axis) { m_activeAxis = axis; }; AzToolsFramework::ViewportInteraction::MouseInteraction QtViewport::BuildMouseInteraction( [[maybe_unused]] Qt::MouseButtons buttons, [[maybe_unused]] Qt::KeyboardModifiers modifiers, [[maybe_unused]] const QPoint& point) { // Implemented by sub-class return AzToolsFramework::ViewportInteraction::MouseInteraction(); } ////////////////////////////////////////////////////////////////////////// bool QtViewport::HitTest(const QPoint& point, HitContext& hitInfo) { Vec3 raySrc(0, 0, 0), rayDir(1, 0, 0); ViewToWorldRay(point, hitInfo.raySrc, hitInfo.rayDir); hitInfo.view = this; hitInfo.point2d = point; if (m_bAdvancedSelectMode) { hitInfo.bUseSelectionHelpers = true; } const int viewportId = GetViewportId(); AzToolsFramework::EntityIdList visibleEntityIds; AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Event( viewportId, &AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Events::FindVisibleEntities, visibleEntityIds); // Look through all visible entities to find the closest one to the specified mouse point using namespace AzToolsFramework::ViewportInteraction; AZ::EntityId entityIdUnderCursor; float closestDistance = std::numeric_limits::max(); MouseInteraction mouseInteraction = BuildMouseInteraction(QGuiApplication::mouseButtons(), QGuiApplication::queryKeyboardModifiers(), point); for (auto entityId : visibleEntityIds) { using AzFramework::ViewportInfo; // Check if components provide an aabb if (const AZ::Aabb aabb = AzToolsFramework::CalculateEditorEntitySelectionBounds(entityId, ViewportInfo{ viewportId }); aabb.IsValid()) { // Coarse grain check if (AzToolsFramework::AabbIntersectMouseRay(mouseInteraction, aabb)) { // If success, pick against specific component if (AzToolsFramework::PickEntity( entityId, mouseInteraction, closestDistance, viewportId)) { entityIdUnderCursor = entityId; } } } } // If we hit a valid Entity, then store the distance in the HitContext // so that the caller can use this for calculations if (entityIdUnderCursor.IsValid()) { hitInfo.dist = closestDistance; return true; } return false; } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetZoomFactor(float fZoomFactor) { m_fZoomFactor = fZoomFactor; if (gSettings.viewports.bSync2DViews && GetType() != ET_ViewportCamera && GetType() != ET_ViewportModel) { GetViewManager()->SetZoom2D(fZoomFactor); } }; ////////////////////////////////////////////////////////////////////////// float QtViewport::GetZoomFactor() const { if (gSettings.viewports.bSync2DViews && GetType() != ET_ViewportCamera && GetType() != ET_ViewportModel) { m_fZoomFactor = GetViewManager()->GetZoom2D(); } return m_fZoomFactor; }; ////////////////////////////////////////////////////////////////////////// Vec3 QtViewport::SnapToGrid(const Vec3& vec) { return vec; } ////////////////////////////////////////////////////////////////////////// void QtViewport::BeginUndo() { DegradateQuality(true); GetIEditor()->BeginUndo(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::AcceptUndo(const QString& undoDescription) { DegradateQuality(false); GetIEditor()->AcceptUndo(undoDescription); GetIEditor()->UpdateViews(eUpdateObjects); } ////////////////////////////////////////////////////////////////////////// void QtViewport::CancelUndo() { DegradateQuality(false); GetIEditor()->CancelUndo(); GetIEditor()->UpdateViews(eUpdateObjects); } ////////////////////////////////////////////////////////////////////////// void QtViewport::RestoreUndo() { GetIEditor()->RestoreUndo(); } ////////////////////////////////////////////////////////////////////////// bool QtViewport::IsUndoRecording() const { return GetIEditor()->IsUndoRecording(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::DegradateQuality(bool bEnable) { m_bDegradateQuality = bEnable; } ////////////////////////////////////////////////////////////////////////// QSize QtViewport::GetIdealSize() const { return QSize(0, 0); } ////////////////////////////////////////////////////////////////////////// bool QtViewport::IsBoundsVisible([[maybe_unused]] const AZ::Aabb& box) const { // Always visible in standard implementation. return true; } ////////////////////////////////////////////////////////////////////////// float QtViewport::GetDistanceToLine(const Vec3& lineP1, const Vec3& lineP2, const QPoint& point) const { QPoint p1 = WorldToView(lineP1); QPoint p2 = WorldToView(lineP2); return AZ::Intersect::PointSegmentDistanceSq( AZ::Vector3(static_cast(point.x()), static_cast(point.y()), 0.0f), AZ::Vector3(static_cast(p1.x()), static_cast(p1.y()), 0.0f), AZ::Vector3(static_cast(p2.x()), static_cast(p2.y()), 0.0f) ); } ////////////////////////////////////////////////////////////////////////// void QtViewport::GetPerpendicularAxis(EAxis* pAxis, bool* pIs2D) const { EViewportType vpType = GetType(); switch (vpType) { case ET_ViewportXY: if (pIs2D) { *pIs2D = true; } if (pAxis) { *pAxis = AXIS_Z; } break; case ET_ViewportXZ: if (pIs2D) { *pIs2D = true; } if (pAxis) { *pAxis = AXIS_Y; } break; case ET_ViewportYZ: if (pIs2D) { *pIs2D = true; } if (pAxis) { *pAxis = AXIS_X; } break; case ET_ViewportMap: case ET_ViewportZ: if (pIs2D) { *pIs2D = true; } break; } } ////////////////////////////////////////////////////////////////////////// bool QtViewport::GetAdvancedSelectModeFlag() { return m_bAdvancedSelectMode; } ////////////////////////////////////////////////////////////////////////// #if defined(AZ_PLATFORM_WINDOWS) // Note: Both CreateAnglesYPR and CreateOrientationYPR were copied verbatim from Cry_Camera.h which has been removed. // // Description //
//   x-YAW
//   y-PITCH (negative=looking down / positive=looking up)
//   z-ROLL
//   
// Note: If we are looking along the z-axis, its not possible to specify the x and z-angle inline Ang3 CreateAnglesYPR(const Matrix33& m) { assert(m.IsOrthonormal()); float l = Vec3(m.m01, m.m11, 0.0f).GetLength(); if (l > 0.0001) { return Ang3(atan2f(-m.m01 / l, m.m11 / l), atan2f(m.m21, l), atan2f(-m.m20 / l, m.m22 / l)); } else { return Ang3(0, atan2f(m.m21, l), 0); } } // Description // This function builds a 3x3 orientation matrix using YPR-angles // Rotation order for the orientation-matrix is Z-X-Y. (Zaxis=YAW / Xaxis=PITCH / Yaxis=ROLL) // //
//  COORDINATE-SYSTEM
//
//  z-axis
//    ^
//    |
//    |  y-axis
//    |  /
//    | /
//    |/
//    +--------------->   x-axis
// 
// // Example: // Matrix33 orientation=CreateOrientationYPR( Ang3(1,2,3) ); inline Matrix33 CreateOrientationYPR(const Ang3& ypr) { f32 sz, cz; sincos_tpl(ypr.x, &sz, &cz); //Zaxis = YAW f32 sx, cx; sincos_tpl(ypr.y, &sx, &cx); //Xaxis = PITCH f32 sy, cy; sincos_tpl(ypr.z, &sy, &cy); //Yaxis = ROLL Matrix33 c; c.m00 = cy * cz - sy * sz * sx; c.m01 = -sz * cx; c.m02 = sy * cz + cy * sz * sx; c.m10 = cy * sz + sy * sx * cz; c.m11 = cz * cx; c.m12 = sy * sz - cy * sx * cz; c.m20 = -sy * cx; c.m21 = sx; c.m22 = cy * cx; return c; } void QtViewport::OnRawInput([[maybe_unused]] UINT wParam, HRAWINPUT lParam) { static C3DConnexionDriver* p3DConnexionDriver = 0; if (GetType() == ET_ViewportCamera) { if (!p3DConnexionDriver) { p3DConnexionDriver = (C3DConnexionDriver*)GetIEditor()->GetPluginManager()->GetPluginByGUID("{AD109901-9128-4ffd-8E67-137CB2B1C41B}"); } if (p3DConnexionDriver) { S3DConnexionMessage msg; if (p3DConnexionDriver->GetInputMessageData((LPARAM)lParam, msg)) { if (msg.bGotTranslation || msg.bGotRotation) { static int all6DOFs[6] = { 0 }; if (msg.bGotTranslation) { all6DOFs[0] = msg.raw_translation[0]; all6DOFs[1] = msg.raw_translation[1]; all6DOFs[2] = msg.raw_translation[2]; } if (msg.bGotRotation) { all6DOFs[3] = msg.raw_rotation[0]; all6DOFs[4] = msg.raw_rotation[1]; all6DOFs[5] = msg.raw_rotation[2]; } Matrix34 viewTM = GetViewTM(); // Scale axis according to CVars ICVar* sys_scale3DMouseTranslation = gEnv->pConsole->GetCVar("sys_scale3DMouseTranslation"); ICVar* sys_Scale3DMouseYPR = gEnv->pConsole->GetCVar("sys_Scale3DMouseYPR"); float fScaleYPR = sys_Scale3DMouseYPR->GetFVal(); float s = 0.01f * gSettings.cameraMoveSpeed; Vec3 t = Vec3(s * all6DOFs[0], -s * all6DOFs[1], -s * all6DOFs[2] * 0.5f); t *= sys_scale3DMouseTranslation->GetFVal(); float as = 0.001f * gSettings.cameraMoveSpeed; Ang3 ypr = CreateAnglesYPR(Matrix33(viewTM)); ypr.x += -all6DOFs[5] * as * fScaleYPR; ypr.y = AZStd::clamp(ypr.y + all6DOFs[3] * as * fScaleYPR, -1.5f, 1.5f); // to keep rotation in reasonable range ypr.z = 0; // to have camera always upward viewTM = Matrix34(CreateOrientationYPR(ypr), viewTM.GetTranslation()); viewTM = viewTM * Matrix34::CreateTranslationMat(t); SetViewTM(viewTM); } } } } } #endif ////////////////////////////////////////////////////////////////////////// float QtViewport::GetFOV() const { return SandboxEditor::CameraDefaultFovRadians(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::setRay(QPoint& vp, Vec3& raySrc, Vec3& rayDir) { m_vp = vp; m_raySrc = raySrc; m_rayDir = rayDir; } #include