/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "ViewManager.h" #include "Viewport.h" #include "./Terrain/Heightmap.h" #include "ViewPane.h" #include "Include/ITransformManipulator.h" #include #include "Util/3DConnexionDriver.h" #include "Util/Ruler.h" #include "PluginManager.h" #include "./Include/IRenderListener.h" #if ENABLE_CRY_PHYSICS #include #endif #include #include "GameEngine.h" #include "UserMessageDefines.h" #include #include #include "QtUtilWin.h" #include #include #ifdef LoadCursor #undef LoadCursor #endif ////////////////////////////////////////////////////////////////////// // Viewport drag and drop support ////////////////////////////////////////////////////////////////////// void QtViewport::BuildDragDropContext(AzQtComponents::ViewportDragContext& context, const QPoint& pt) { context.m_hitLocation = AZ::Vector3::CreateZero(); PreWidgetRendering(); // required so that the current render cam is set. Vec3 pos = Vec3(ZERO); HitContext hit; if (HitTest(pt, hit)) { pos = hit.raySrc + hit.rayDir * hit.dist; pos = SnapToGrid(pos); } else { bool hitTerrain; pos = ViewToWorld(pt, &hitTerrain); if (hitTerrain) { pos.z = GetIEditor()->GetTerrainElevation(pos.x, pos.y); } pos = SnapToGrid(pos); } context.m_hitLocation = AZ::Vector3(pos.x, pos.y, pos.z); PostWidgetRendering(); } 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, 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, 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, event->pos()); DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::Drop, event, context); } if (event->isAccepted()) { // Navigation triggered - Drag+Drop from Component Palette/Asset Browser to View port to create a new entity with the component AzToolsFramework::EditorMetricsEventsBusAction editorMetricsEventsBusActionWrapper(AzToolsFramework::EditorMetricsEventsBusTraits::NavigationTrigger::DragAndDrop); } } void QtViewport::dragLeaveEvent(QDragLeaveEvent* event) { using namespace AzQtComponents; DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::DragLeave, event); } ////////////////////////////////////////////////////////////////////////// float CViewport::GetFOV() const { return gSettings.viewports.fDefaultFov; } ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// bool QtViewport::m_bDegradateQuality = false; QtViewport::QtViewport(QWidget* parent) : QWidget(parent) , m_renderOverlay(this) { QAction* action = 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; for (int i = 0; i < LAST_COORD_SYSTEM; i++) { m_constructionMatrix[i].SetIdentity(); } m_viewTM.SetIdentity(); m_screenTM.SetIdentity(); m_pMouseOverObject = 0; m_bAdvancedSelectMode = false; m_pVisibleObjectsCache = new CBaseObjectsCache; m_constructionPlane.SetPlane(Vec3_OneZ, Vec3_Zero); m_constructionPlaneAxisX = Vec3_Zero; m_constructionPlaneAxisY = Vec3_Zero; GetIEditor()->GetViewManager()->RegisterViewport(this); m_pLocalEditTool = 0; 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(false); m_renderOverlay.setUpdatesEnabled(false); m_renderOverlay.setMouseTracking(true); m_renderOverlay.setObjectName("renderOverlay"); setAcceptDrops(true); } ////////////////////////////////////////////////////////////////////////// QtViewport::~QtViewport() { if (m_pLocalEditTool) m_pLocalEditTool->deleteLater(); delete m_pVisibleObjectsCache; 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(); } } ////////////////////////////////////////////////////////////////////////// CEditTool* QtViewport::GetEditTool() { if (m_pLocalEditTool) { return m_pLocalEditTool; } return GetIEditor()->GetEditTool(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetEditTool(CEditTool* pEditTool, bool bLocalToViewport /*=false */) { if (m_pLocalEditTool == pEditTool) { return; } if (m_pLocalEditTool) { m_pLocalEditTool->EndEditParams(); } m_pLocalEditTool = 0; if (bLocalToViewport) { m_pLocalEditTool = pEditTool; m_pLocalEditTool->BeginEditParams(GetIEditor(), 0); } else { m_pLocalEditTool = 0; GetIEditor()->SetEditTool(pEditTool); } } ////////////////////////////////////////////////////////////////////////// void QtViewport::RegisterRenderListener(IRenderListener* piListener) { #ifdef _DEBUG size_t nCount(0); size_t nTotal(0); nTotal = m_cRenderListeners.size(); for (nCount = 0; nCount < nTotal; ++nCount) { if (m_cRenderListeners[nCount] == piListener) { assert(!"Registered the same RenderListener multiple times."); break; } } #endif //_DEBUG m_cRenderListeners.push_back(piListener); } ////////////////////////////////////////////////////////////////////////// bool QtViewport::UnregisterRenderListener(IRenderListener* piListener) { size_t nCount(0); size_t nTotal(0); nTotal = m_cRenderListeners.size(); for (nCount = 0; nCount < nTotal; ++nCount) { if (m_cRenderListeners[nCount] == piListener) { m_cRenderListeners.erase(m_cRenderListeners.begin() + nCount); return true; } } return false; } ////////////////////////////////////////////////////////////////////////// bool QtViewport::IsRenderListenerRegistered(IRenderListener* piListener) { size_t nCount(0); size_t nTotal(0); nTotal = m_cRenderListeners.size(); for (nCount = 0; nCount < nTotal; ++nCount) { if (m_cRenderListeners[nCount] == piListener) { return true; } } return false; } ////////////////////////////////////////////////////////////////////////// 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(Qt::KeyboardModifiers modifiers, short zDelta, 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::leaveEvent(QEvent* event) { QWidget::leaveEvent(event); MouseCallback(eMouseLeave, QPoint(), Qt::KeyboardModifiers(), Qt::MouseButtons()); } ////////////////////////////////////////////////////////////////////////// void QtViewport::paintEvent(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() { m_pMouseOverObject = 0; // Need to clear visual object cache. // Right after loading new level, some code(e.g. OnMouseMove) access invalid // previous level object before cache updated. GetVisibleObjectsCache()->ClearObjects(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::UpdateContent(int flags) { if (flags & eRedrawViewports) { update(); } } ////////////////////////////////////////////////////////////////////////// void QtViewport::Update() { FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR); m_bAdvancedSelectMode = false; bool bSpaceClick = false; CEditTool* pEditTool = GetIEditor()->GetEditTool(); if (pEditTool && pEditTool->IsNeedSpecificBehaviorForSpaceAcce()) { bSpaceClick = CheckVirtualKey(Qt::Key_Space); } else { bSpaceClick = CheckVirtualKey(Qt::Key_Space) & !CheckVirtualKey(Qt::Key_Shift) /*& !CheckVirtualKey(Qt::Key_Control)*/; } if (bSpaceClick && hasFocus()) { m_bAdvancedSelectMode = true; } m_nLastUpdateFrame++; } ////////////////////////////////////////////////////////////////////////// QPoint QtViewport::WorldToView(const Vec3& wp) const { return QPoint(wp.x, wp.y); } ////////////////////////////////////////////////////////////////////////// Vec3 QtViewport::WorldToView3D(const Vec3& wp, int nFlags) const { QPoint p = WorldToView(wp); Vec3 out; out.x = p.x(); out.y = p.y(); out.z = wp.z; return out; } ////////////////////////////////////////////////////////////////////////// Vec3 QtViewport::ViewToWorld(const QPoint& vp, bool* pCollideWithTerrain, bool onlyTerrain, bool bSkipVegetation, bool bTestRenderMesh, bool* collideWithObject) const { Vec3 wp; wp.x = vp.x(); wp.y = vp.y(); wp.z = 0; if (pCollideWithTerrain) { *pCollideWithTerrain = true; } return wp; } ////////////////////////////////////////////////////////////////////////// Vec3 QtViewport::ViewToWorldNormal(const QPoint& vp, bool onlyTerrain, bool bTestRenderMesh) { return Vec3(0, 0, 0); } ////////////////////////////////////////////////////////////////////////// void QtViewport::ViewToWorldRay(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(), 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::OnLButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point) { // Save the mouse down position m_cMouseDownPos = point; if (MouseCallback(eMouseLDown, point, modifiers)) { return; } } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnLButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point) { // Check Edit Tool. MouseCallback(eMouseLUp, point, modifiers); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point) { MouseCallback(eMouseRDown, point, modifiers); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnRButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point) { MouseCallback(eMouseRUp, point, modifiers); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point) { // Check Edit Tool. MouseCallback(eMouseMDown, point, modifiers); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point) { // Move the viewer to the mouse location. // Check Edit Tool. MouseCallback(eMouseMUp, point, modifiers); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnMButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point) { MouseCallback(eMouseMDblClick, point, modifiers); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnMouseMove(Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint& point) { MouseCallback(eMouseMove, point, modifiers, buttons); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnSetCursor() { if (GetEditTool()) { GetEditTool()->OnSetCursor(this); } } ////////////////////////////////////////////////////////////////////////// void QtViewport::ResetSelectionRegion() { AABB box(Vec3(0, 0, 0), Vec3(0, 0, 0)); GetIEditor()->SetSelectedRegion(box); m_selectedRect = QRect(); } void QtViewport::SetSelectionRectangle(const QRect& rect) { m_selectedRect = rect.normalized(); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnDragSelectRectangle(const QRect& rect, bool bNormalizeRect) { Vec3 org; AABB box; box.Reset(); //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.Add(p1); box.Add(p2); } else { const QRect rc = correctedRect.normalized(); box.Add(ViewToWorld(rc.topLeft())); box.Add(ViewToWorld(rc.topRight())); box.Add(ViewToWorld(rc.bottomLeft())); box.Add(ViewToWorld(rc.bottomRight())); } box.min.z = -10000; box.max.z = 10000; GetIEditor()->SetSelectedRegion(box); // Show marker position in the status bar float w = box.max.x - box.min.x; float h = box.max.y - box.min.y; 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::OnLButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point) { if (GetIEditor()->IsInGameMode()) { // Ignore double clicks while in game. return; } MouseCallback(eMouseLDblClick, point, modifiers); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnRButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point) { MouseCallback(eMouseRDblClick, point, modifiers); } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if (GetIEditor()->IsInGameMode()) { // Ignore key downs while in game. return; } if (GetEditTool()) { if (GetEditTool()->OnKeyDown(this, nChar, nRepCnt, nFlags)) { return; } } } ////////////////////////////////////////////////////////////////////////// void QtViewport::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { if (GetIEditor()->IsInGameMode()) { // Ignore key downs while in game. return; } if (GetEditTool()) { if (GetEditTool()->OnKeyUp(this, nChar, nRepCnt, nFlags)) { return; } } } ////////////////////////////////////////////////////////////////////////// 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()); } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetConstructionOrigin(const Vec3& worldPos) { Matrix34 tm; tm.SetIdentity(); tm.SetTranslation(worldPos); SetConstructionMatrix(COORDS_LOCAL, tm); SetConstructionMatrix(COORDS_PARENT, tm); SetConstructionMatrix(COORDS_USERDEFINED, tm); } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetConstructionMatrix(RefCoordSys coordSys, const Matrix34& xform) { m_constructionMatrix[coordSys] = xform; m_constructionMatrix[coordSys].OrthonormalizeFast(); // Remove scale component from matrix. if (coordSys == COORDS_LOCAL) { m_constructionMatrix[COORDS_VIEW].SetTranslation(xform.GetTranslation()); m_constructionMatrix[COORDS_WORLD].SetTranslation(xform.GetTranslation()); m_constructionMatrix[COORDS_USERDEFINED].SetIdentity(); m_constructionMatrix[COORDS_USERDEFINED].SetTranslation(xform.GetTranslation()); m_constructionMatrix[COORDS_PARENT] = xform; } MakeConstructionPlane(GetAxisConstrain()); } ////////////////////////////////////////////////////////////////////////// const Matrix34& QtViewport::GetConstructionMatrix(RefCoordSys coordSys) { if (coordSys == COORDS_VIEW) { return m_constructionMatrix[COORDS_WORLD]; } return m_constructionMatrix[coordSys]; } ////////////////////////////////////////////////////////////////////////// void QtViewport::AssignConstructionPlane(const Vec3& p1, const Vec3& p2, const Vec3& p3) { m_constructionPlane.SetPlane(p1, p2, p3); m_constructionPlaneAxisX = p3 - p1; m_constructionPlaneAxisY = p2 - p1; } ////////////////////////////////////////////////////////////////////////// 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::MakeConstructionPlane(int axis) { QPoint cursorPos; if (m_mouseCaptured) { cursorPos = m_cMouseDownPos; } else { cursorPos = QCursor::pos(); ScreenToClient(cursorPos); } Vec3 raySrc(Vec3_Zero), rayDir(Vec3_OneX); ViewToWorldRay(cursorPos, raySrc, rayDir); int coordSys = GetIEditor()->GetReferenceCoordSys(); Vec3 xAxis(Vec3_OneX); Vec3 yAxis(Vec3_OneY); Vec3 zAxis(Vec3_OneZ); xAxis = m_constructionMatrix[coordSys].TransformVector(xAxis); yAxis = m_constructionMatrix[coordSys].TransformVector(yAxis); zAxis = m_constructionMatrix[coordSys].TransformVector(zAxis); Vec3 pos = m_constructionMatrix[coordSys].GetTranslation(); if (axis == AXIS_X) { // X direction. Vec3 n; float d1 = fabs(rayDir.Dot(yAxis)); float d2 = fabs(rayDir.Dot(zAxis)); if (d1 > d2) { n = yAxis; } else { n = zAxis; } if (rayDir.Dot(n) < 0) { n = -n; // face construction plane to the ray. } //Vec3 n = Vec3(0,0,1); Vec3 v1 = n.Cross(xAxis); Vec3 v2 = n.Cross(v1); AssignConstructionPlane(pos, pos + v2, pos + v1); } else if (axis == AXIS_Y) { // Y direction. Vec3 n; float d1 = fabs(rayDir.Dot(xAxis)); float d2 = fabs(rayDir.Dot(zAxis)); if (d1 > d2) { n = xAxis; } else { n = zAxis; } if (rayDir.Dot(n) < 0) { n = -n; // face construction plane to the ray. } Vec3 v1 = n.Cross(yAxis); Vec3 v2 = n.Cross(v1); AssignConstructionPlane(pos, pos + v2, pos + v1); } else if (axis == AXIS_Z) { // Z direction. Vec3 n; float d1 = fabs(rayDir.Dot(xAxis)); float d2 = fabs(rayDir.Dot(yAxis)); if (d1 > d2) { n = xAxis; } else { n = yAxis; } if (rayDir.Dot(n) < 0) { n = -n; // face construction plane to the ray. } Vec3 v1 = n.Cross(zAxis); Vec3 v2 = n.Cross(v1); AssignConstructionPlane(pos, pos + v2, pos + v1); } else if (axis == AXIS_XY) { AssignConstructionPlane(pos, pos + yAxis, pos + xAxis); } else if (axis == AXIS_XZ) { AssignConstructionPlane(pos, pos + zAxis, pos + xAxis); } else if (axis == AXIS_YZ) { AssignConstructionPlane(pos, pos + zAxis, pos + yAxis); } else { AssignConstructionPlane(pos, pos + yAxis, pos + xAxis); } } ////////////////////////////////////////////////////////////////////////// Vec3 QtViewport::MapViewToCP(const QPoint& point, int axis) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); if (axis == AXIS_TERRAIN) { return SnapToGrid(ViewToWorld(point)); } MakeConstructionPlane(axis); Vec3 raySrc(Vec3_Zero), rayDir(Vec3_OneX); ViewToWorldRay(point, raySrc, rayDir); Vec3 v; Ray ray(raySrc, rayDir); if (!Intersect::Ray_Plane(ray, m_constructionPlane, v)) { Plane inversePlane = m_constructionPlane; inversePlane.n = -inversePlane.n; inversePlane.d = -inversePlane.d; if (!Intersect::Ray_Plane(ray, inversePlane, v)) { v = Vec3_Zero; } } // Snap value to grid. v = SnapToGrid(v); return v; } ////////////////////////////////////////////////////////////////////////// Vec3 QtViewport::GetCPVector(const Vec3& p1, const Vec3& p2, int axis) { Vec3 v = p2 - p1; int coordSys = GetIEditor()->GetReferenceCoordSys(); Vec3 xAxis(Vec3_OneX); Vec3 yAxis(Vec3_OneY); Vec3 zAxis(Vec3_OneZ); // In local coordinate system transform axises by construction matrix. xAxis = m_constructionMatrix[coordSys].TransformVector(xAxis); yAxis = m_constructionMatrix[coordSys].TransformVector(yAxis); zAxis = m_constructionMatrix[coordSys].TransformVector(zAxis); if (axis == AXIS_X || axis == AXIS_Y || axis == AXIS_Z) { // Project vector v on transformed axis x,y or z. Vec3 axisVector; if (axis == AXIS_X) { axisVector = xAxis; } if (axis == AXIS_Y) { axisVector = yAxis; } if (axis == AXIS_Z) { axisVector = zAxis; } // Project vector on construction plane into the one of axises. v = v.Dot(axisVector) * axisVector; } else if (axis == AXIS_XY) { // Project vector v on transformed plane x/y. Vec3 planeNormal = xAxis.Cross(yAxis); Vec3 projV = v.Dot(planeNormal) * planeNormal; v = v - projV; } else if (axis == AXIS_XZ) { // Project vector v on transformed plane x/y. Vec3 planeNormal = xAxis.Cross(zAxis); Vec3 projV = v.Dot(planeNormal) * planeNormal; v = v - projV; } else if (axis == AXIS_YZ) { // Project vector v on transformed plane x/y. Vec3 planeNormal = yAxis.Cross(zAxis); Vec3 projV = v.Dot(planeNormal) * planeNormal; v = v - projV; } else if (axis == AXIS_TERRAIN) { v.z = 0; } return v; } ////////////////////////////////////////////////////////////////////////// void QtViewport::SetAxisConstrain(int axis) { m_activeAxis = axis; }; ////////////////////////////////////////////////////////////////////////// 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; } return GetIEditor()->GetObjectManager()->HitTest(hitInfo); } ////////////////////////////////////////////////////////////////////////// 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 m_viewManager->GetGrid()->Snap(vec, m_fGridZoom); } float QtViewport::GetGridStep() const { return m_viewManager->GetGrid()->scale * m_viewManager->GetGrid()->size; } ////////////////////////////////////////////////////////////////////////// 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(const AABB& box) const { // Always visible in standard implementation. return true; } ////////////////////////////////////////////////////////////////////////// bool QtViewport::HitTestLine(const Vec3& lineP1, const Vec3& lineP2, const QPoint& hitpoint, int pixelRadius, float* pToCameraDistance) const { float dist = GetDistanceToLine(lineP1, lineP2, hitpoint); if (dist <= pixelRadius) { if (pToCameraDistance) { Vec3 raySrc, rayDir; ViewToWorldRay(hitpoint, raySrc, rayDir); Vec3 rayTrg = raySrc + rayDir * 10000.0f; Vec3 pa, pb; float mua, mub; LineLineIntersect(lineP1, lineP2, raySrc, rayTrg, pa, pb, mua, mub); *pToCameraDistance = mub; } return true; } return false; } ////////////////////////////////////////////////////////////////////////// float QtViewport::GetDistanceToLine(const Vec3& lineP1, const Vec3& lineP2, const QPoint& point) const { QPoint p1 = WorldToView(lineP1); QPoint p2 = WorldToView(lineP2); return PointToLineDistance2D( Vec3(p1.x(), p1.y(), 0), Vec3(p2.x(), p2.y(), 0), Vec3(point.x(), point.y(), 0)); } ////////////////////////////////////////////////////////////////////////// void QtViewport::GetPerpendicularAxis(EAxis* pAxis, bool* pIs2D) const { EViewportType vpType = GetType(); bool b2D = false; 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; } ////////////////////////////////////////////////////////////////////////// bool QtViewport::MouseCallback(EMouseEvent event, const QPoint& point, Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); // Ignore any mouse events in game mode. if (GetIEditor()->IsInGameMode()) { return true; } // We must ignore mouse events when we are in the middle of an assert. // Reason: If we have an assert called from an engine module under the editor, if we call this function, // it may call the engine again and cause a deadlock. // Concrete example: CryPhysics called from Trackview causing an assert, and moving the cursor over the viewport // would cause the editor to freeze as it calls CryPhysics again for a raycast while it didn't release the lock. if (gEnv->pSystem->IsAssertDialogVisible()) { return true; } // RAII wrapper for Pre / PostWidgetRendering calls. // It also tracks the times a mouse callback potentially created a new viewport context. struct ScopedProcessingMouseCallback { explicit ScopedProcessingMouseCallback(QtViewport* viewport) : m_viewport(viewport) { m_viewport->m_processingMouseCallbacksCounter++; m_viewport->PreWidgetRendering(); } ~ScopedProcessingMouseCallback() { m_viewport->PostWidgetRendering(); m_viewport->m_processingMouseCallbacksCounter--; } QtViewport* m_viewport; }; ScopedProcessingMouseCallback scopedProcessingMouseCallback(this); ////////////////////////////////////////////////////////////////////////// // Hit test gizmo objects. ////////////////////////////////////////////////////////////////////////// bool bAltClick = (modifiers & Qt::AltModifier); bool bCtrlClick = (modifiers & Qt::ControlModifier); bool bShiftClick = (modifiers & Qt::ShiftModifier); int flags = (bCtrlClick ? MK_CONTROL : 0) | (bShiftClick ? MK_SHIFT : 0) | ((buttons& Qt::LeftButton) ? MK_LBUTTON : 0) | ((buttons& Qt::MiddleButton) ? MK_MBUTTON : 0) | ((buttons& Qt::RightButton) ? MK_RBUTTON : 0); switch (event) { case eMouseMove: if (m_nLastUpdateFrame == m_nLastMouseMoveFrame) { // If mouse move event generated in the same frame, ignore it. return false; } m_nLastMouseMoveFrame = m_nLastUpdateFrame; // Skip the marker position update if anything is selected, since it is only used // by the info bar which doesn't show the marker when there is an active selection. // This helps a performance issue when calling ViewToWorld (which calls RayWorldIntersection) // on every mouse movement becomes very expensive in scenes with large amounts of entities. CSelectionGroup* selection = GetIEditor()->GetSelection(); if (!(buttons & Qt::RightButton) /* && m_nLastUpdateFrame != m_nLastMouseMoveFrame*/ && (selection && selection->IsEmpty())) { //m_nLastMouseMoveFrame = m_nLastUpdateFrame; Vec3 pos = ViewToWorld(point); GetIEditor()->SetMarkerPosition(pos); } break; } ////////////////////////////////////////////////////////////////////////// // Asks Ruler to handle mouse callback. CRuler* pRuler = GetIEditor()->GetRuler(); QPoint tempPoint(point.x(), point.y()); if (pRuler) { if (pRuler->MouseCallback(this, event, tempPoint, flags)) { return true; } } ////////////////////////////////////////////////////////////////////////// // Handle viewport manipulators. ////////////////////////////////////////////////////////////////////////// if (!bAltClick) { ITransformManipulator* pManipulator = GetIEditor()->GetTransformManipulator(); if (pManipulator) { if (pManipulator->MouseCallback(this, event, tempPoint, flags)) { return true; } } } ////////////////////////////////////////////////////////////////////////// // Asks current edit tool to handle mouse callback. CEditTool* pEditTool = GetEditTool(); if (pEditTool) { if (pEditTool->MouseCallback(this, event, tempPoint, flags)) { return true; } // Ask all chain of parent tools if they are handling mouse event. CEditTool* pParentTool = pEditTool->GetParentTool(); while (pParentTool) { if (pParentTool->MouseCallback(this, event, tempPoint, flags)) { return true; } pParentTool = pParentTool->GetParentTool(); } } return false; } ////////////////////////////////////////////////////////////////////////// void QtViewport::ProcessRenderLisneters(DisplayContext& rstDisplayContext) { FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR); size_t nCount(0); size_t nTotal(0); nTotal = m_cRenderListeners.size(); for (nCount = 0; nCount < nTotal; ++nCount) { m_cRenderListeners[nCount]->Render(rstDisplayContext); } } ////////////////////////////////////////////////////////////////////////// #if defined(AZ_PLATFORM_WINDOWS) void QtViewport::OnRawInput(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 = CCamera::CreateAnglesYPR(Matrix33(viewTM)); ypr.x += -all6DOFs[5] * as * fScaleYPR; ypr.y = 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(CCamera::CreateOrientationYPR(ypr), viewTM.GetTranslation()); viewTM = viewTM * Matrix34::CreateTranslationMat(t); SetViewTM(viewTM); } } } } } #endif ////////////////////////////////////////////////////////////////////////// float QtViewport::GetFOV() const { return gSettings.viewports.fDefaultFov; } ////////////////////////////////////////////////////////////////////////// void QtViewport::setRay(QPoint& vp, Vec3& raySrc, Vec3& rayDir) { m_vp = vp; m_raySrc = raySrc; m_rayDir = rayDir; } //////////////////////////////////////////////////////////////////////// void QtViewport::setHitcontext(QPoint& vp, Vec3& raySrc, Vec3& rayDir) { vp = m_vp; raySrc = m_raySrc; rayDir = m_rayDir; } #include