/* * 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 "2DViewport.h" #include "CryEditDoc.h" #include "GameEngine.h" #include "Grid.h" #include "DisplaySettings.h" #include "ViewManager.h" #include "Objects/BrushObject.h" #include #include #define MARKER_SIZE 6.0f #define MARKER_DIR_SIZE 10.0f #define SELECTION_RADIUS 30.0f #define GL_RGBA 0x1908 #define GL_BGRA 0x80E1 #define BACKGROUND_COLOR Vec3(1.0f, 1.0f, 1.0f) #define SELECTION_RECT_COLOR Vec3(0.8f, 0.8f, 0.8f) #define MINOR_GRID_COLOR Vec3(0.55f, 0.55f, 0.55f) #define MAJOR_GRID_COLOR Vec3(0.6f, 0.6f, 0.6f) #define AXIS_GRID_COLOR Vec3(0, 0, 0) #define GRID_TEXT_COLOR Vec3(0, 0, 1.0f) #define MAX_WORLD_SIZE 10000 enum Viewport2dTitleMenuCommands { ID_SHOW_LABELS, ID_SHOW_GRID }; ////////////////////////////////////////////////////////////////////////// static void OnMenuLabels() { GetIEditor()->GetDisplaySettings()->DisplayLabels(!GetIEditor()->GetDisplaySettings()->IsDisplayLabels()); } ////////////////////////////////////////////////////////////////////////// static void OnMenuGrid() { CViewport* pViewport = GetIEditor()->GetViewManager()->GetSelectedViewport(); if (pViewport) { if (Q2DViewport* p2DViewport = viewport_cast(pViewport)) { p2DViewport->SetShowGrid(!p2DViewport->GetShowGrid()); } } } ////////////////////////////////////////////////////////////////////////// inline Vec3 SnapToSize(Vec3 v, double size) { Vec3 snapped; snapped.x = floor((v.x / size) + 0.5) * size; snapped.y = floor((v.y / size) + 0.5) * size; snapped.z = floor((v.z / size) + 0.5) * size; return snapped; } ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// Q2DViewport::Q2DViewport(QWidget* parent) : QtViewport(parent) , m_renderer(nullptr) { // Scroll offset equals origin m_rcSelect.setRect(0, 0, 0, 0); m_viewType = ET_ViewportXY; m_axis = VPA_XY; m_bShowTerrain = true; m_bShowGrid = true; m_bShowObjectsInfo = true; m_bShowMinorGridLines = true; m_bShowMajorGridLines = true; m_bShowNumbers = true; m_bAutoAdjustGrids = true; m_gridAlpha = 1; m_origin2D.Set(0, 0, 0); m_colorGridText = QColor(220, 220, 220); m_colorAxisText = QColor(220, 220, 220); m_colorBackground = QColor(128, 128, 128); m_screenTM.SetIdentity(); m_screenTM_Inverted.SetIdentity(); m_fZoomFactor = 1; m_bContentValid = false; m_bShowViewMarker = true; m_displayContext.pIconManager = GetIEditor()->GetIconManager(); /* In MFC OnCreate was delayed behind the scenes, being called at some time after the constructor but just before the window is shown for the first time. Using QTimer is simple way to accomplish the same thing. QTimer relies on the event loop, so by calling QTimer::singleShot with 0 timeout, the constructor will finish immediately, followed by a call to OnCreate shortly after, once Qt's event loop is up and running. */ QTimer::singleShot(0, [&]() { OnCreate(); }); } ////////////////////////////////////////////////////////////////////////// Q2DViewport::~Q2DViewport() { OnDestroy(); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::SetType(EViewportType type) { m_viewType = type; switch (m_viewType) { case ET_ViewportXY: m_axis = VPA_XY; break; case ET_ViewportXZ: m_axis = VPA_XZ; break; case ET_ViewportYZ: m_axis = VPA_YZ; break; } ; SetAxis(m_axis); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::SetAxis(EViewportAxis axis) { m_axis = axis; switch (m_axis) { case VPA_XY: m_cullAxis = 2; break; case VPA_YX: m_cullAxis = 2; break; case VPA_XZ: m_cullAxis = 1; break; case VPA_YZ: m_cullAxis = 0; break; } } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::CalculateViewTM() { Matrix34 viewTM; Matrix34 tm; tm.SetIdentity(); viewTM.SetIdentity(); float fScale = GetZoomFactor(); Vec3 origin = GetOrigin2D(); float height = m_rcClient.height() / fScale; Vec3 v1; switch (m_axis) { case VPA_XY: tm = Matrix33::CreateScale(Vec3(fScale, -fScale, fScale)) * tm; // No fScale for Z tm.SetTranslation(Vec3(-origin.x, height + origin.y, 0) * fScale); break; case VPA_YX: tm.SetFromVectors(Vec3(0, 1, 0) * fScale, Vec3(1, 0, 0) * fScale, Vec3(0, 0, 1) * fScale, Vec3(0, 0, 0)); tm.SetTranslation(Vec3(-origin.x, height + origin.y, 0) * fScale); break; case VPA_XZ: viewTM.SetFromVectors(Vec3(1, 0, 0), Vec3(0, 0, 1), Vec3(0, 1, 0), Vec3(0, 0, 0)); tm.SetFromVectors(Vec3(1, 0, 0) * fScale, Vec3(0, 0, 1) * fScale, Vec3(0, -1, 0) * fScale, Vec3(0, 0, 0)); tm.SetTranslation(Vec3(-origin.x, height + origin.z, 0) * fScale); break; case VPA_YZ: viewTM.SetFromVectors(Vec3(0, 1, 0), Vec3(0, 0, 1), Vec3(1, 0, 0), Vec3(0, 0, 0)); tm.SetFromVectors(Vec3(0, 0, 1) * fScale, Vec3(1, 0, 0) * fScale, Vec3(0, -1, 0) * fScale, Vec3(0, 0, 0)); // No fScale for Z tm.SetTranslation(Vec3(-origin.y, height + origin.z, 0) * fScale); break; } SetViewTM(viewTM); m_screenTM = tm; m_screenTM_Inverted = m_screenTM.GetInverted(); SetConstructionMatrix(COORDS_VIEW, GetViewTM()); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::ResetContent() { QtViewport::ResetContent(); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::UpdateContent(int flags) { QtViewport::UpdateContent(flags); if (flags & eUpdateObjects) { m_bContentValid = false; } } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point) { if (GetIEditor()->IsInGameMode()) { return; } if (!hasFocus()) { setFocus(); } // Check Edit Tool. MouseCallback(eMouseRDown, point, modifiers); SetCurrentCursor(STD_CURSOR_MOVE, QString()); // Save the mouse down position m_RMouseDownPos = point; m_prevZoomFactor = GetZoomFactor(); //m_prevScrollOffset = m_cScrollOffset; CaptureMouse(); SetViewMode(ScrollZoomMode); m_bContentValid = false; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnRButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point) { ReleaseMouse(); SetViewMode(NothingMode); GetViewManager()->UpdateViews(eUpdateObjects); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point) { //////////////////////////////////////////////////////////////////////// // User pressed the middle mouse button //////////////////////////////////////////////////////////////////////// // Check Edit Tool. if (MouseCallback(eMouseMDown, point, modifiers)) { return; } // Save the mouse down position m_RMouseDownPos = point; SetCurrentCursor(STD_CURSOR_MOVE, QString()); // Save the mouse down position m_RMouseDownPos = point; m_prevZoomFactor = GetZoomFactor(); SetViewMode(ScrollZoomMode); m_bContentValid = false; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point) { // Check Edit Tool. if (MouseCallback(eMouseMUp, point, modifiers)) { return; } SetViewMode(NothingMode); ReleaseMouse(); m_bContentValid = false; GetViewManager()->UpdateViews(eUpdateObjects); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnLButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point) { //////////////////////////////////////////////////////////////////////// // User pressed the left mouse button //////////////////////////////////////////////////////////////////////// if (GetViewMode() != NothingMode) { return; } m_cMouseDownPos = point; m_prevZoomFactor = GetZoomFactor(); //m_prevScrollOffset = m_cScrollOffset; QtViewport::OnLButtonDown(modifiers, point); m_bContentValid = false; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnLButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point) { //////////////////////////////////////////////////////////////////////// // Process the various events depending on the selection and the view // mode //////////////////////////////////////////////////////////////////////// QtViewport::OnLButtonUp(modifiers, point); GetViewManager()->UpdateViews(eUpdateObjects); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnMouseWheel(Qt::KeyboardModifiers, short zDelta, const QPoint& pt) { float z = GetZoomFactor(); float scale = 1.2f * fabs(zDelta / 120.0f); if (zDelta >= 0) { z = z * scale; } else { z = z / scale; } SetZoom(z, m_cMousePos); GetViewManager()->UpdateViews(eUpdateObjects); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnMouseMove(Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint& point) { m_cMousePos = point; if (GetViewMode() == ScrollZoomMode) { // Capture the mouse after the user has moved a bit, otherwise the // right click context menu won't popup if (!m_mouseCaptured && ((point - m_RMouseDownPos).manhattanLength() > QApplication::startDragDistance())) { CaptureMouse(); } QRect rc; // You can only scroll while the middle mouse button is down if (buttons & Qt::RightButton || buttons & Qt::MiddleButton) { if (modifiers & Qt::ShiftModifier) { // Get the dimensions of the window rc = rect(); int w = rc.right(); int h = rc.bottom(); // Zoom to mouse position. float z = m_prevZoomFactor + (point.y() - m_RMouseDownPos.y()) * 0.02f; SetZoom(z, m_RMouseDownPos); } else { // Set the new scrolled coordinates float fScale = GetZoomFactor(); float ofsx, ofsy; GetScrollOffset(ofsx, ofsy); ofsx -= (point.x() - m_RMouseDownPos.x()) / fScale; ofsy += (point.y() - m_RMouseDownPos.y()) / fScale; SetScrollOffset(ofsx, ofsy); m_RMouseDownPos = point; } } return; } QtViewport::OnMouseMove(modifiers, buttons, point); m_bContentValid = false; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::SetScrollOffset(float x, float y, bool bLimits) { Vec3 org = GetOrigin2D(); switch (m_axis) { case VPA_XY: case VPA_YX: org.x = x; org.y = y; break; case VPA_XZ: org.x = x; org.z = y; break; case VPA_YZ: org.y = x; org.z = y; break; } SetOrigin2D(org); CalculateViewTM(); //GetViewManager()->UpdateViews(eUpdateObjects); m_bContentValid = false; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::GetScrollOffset(float& x, float& y) { Vec3 origin = GetOrigin2D(); switch (m_axis) { case VPA_XY: case VPA_YX: x = origin.x; y = origin.y; break; case VPA_XZ: x = origin.x; y = origin.z; break; case VPA_YZ: x = origin.y; y = origin.z; break; } } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::SetZoom(float fZoomFactor, const QPoint& center) { if (fZoomFactor < m_minZoom) { fZoomFactor = m_minZoom; } else if (fZoomFactor > m_maxZoom) { fZoomFactor = m_maxZoom; } float prevz = GetZoomFactor(); // Zoom to mouse position. float ofsx, ofsy; GetScrollOffset(ofsx, ofsy); float s1 = GetZoomFactor(); float s2 = fZoomFactor; SetZoomFactor(fZoomFactor); // Calculate new offset to center zoom on mouse. float x2 = center.x(); float y2 = m_rcClient.height() - center.y(); ofsx = -(x2 / s2 - x2 / s1 - ofsx); ofsy = -(y2 / s2 - y2 / s1 - ofsy); SetScrollOffset(ofsx, ofsy, true); CalculateViewTM(); m_bContentValid = false; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::resizeEvent(QResizeEvent* event) { //////////////////////////////////////////////////////////////////////// // Re-evaluate the zoom / scroll offset values // TODO: Restore the zoom rectangle instead of resetting it //////////////////////////////////////////////////////////////////////// QtViewport::resizeEvent(event); m_rcClient = rect(); CalculateViewTM(); m_bContentValid = false; } void Q2DViewport::showEvent(QShowEvent* event) { QtViewport::showEvent(event); m_bContentValid = false; QMetaObject::invokeMethod(this, "Update", Qt::QueuedConnection); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::paintEvent(QPaintEvent* event) { // nothing to draw here, Render() draws on the overlay widget which has no updates enabled to avoid flicker CGameEngine* ge = GetIEditor()->GetGameEngine(); setRenderOverlayVisible(ge ? ge->IsLevelLoaded() : false); } ////////////////////////////////////////////////////////////////////////// int Q2DViewport::OnCreate() { m_renderer = GetIEditor()->GetRenderer(); assert (m_renderer != NULL); if (m_renderer) { WIN_HWND previousContext = m_renderer->GetCurrentContextHWND(); m_renderer->CreateContext(renderOverlayHWND()); m_renderer->SetCurrentContext(previousContext); } // Calculate the View transformation matrix. CalculateViewTM(); return 0; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::Update() { if (!m_bContentValid) { Render(); } m_bContentValid = true; QtViewport::Update(); } ////////////////////////////////////////////////////////////////////////// QPoint Q2DViewport::WorldToView(const Vec3& wp) const { Vec3 sp = m_screenTM.TransformPoint(wp); QPoint p = QPoint(sp.x, sp.y); return p; } ////////////////////////////////////////////////////////////////////////// QPoint Q2DViewport::WorldToViewParticleEditor(const Vec3& wp, int width, int height) const //Eric@conffx implement for the children class of IDisplayViewport { Vec3 sp = m_screenTM.TransformPoint(wp); QPoint p = QPoint(sp.x, sp.y); return p; } ////////////////////////////////////////////////////////////////////////// Vec3 Q2DViewport::ViewToWorld(const QPoint& vp, bool* collideWithTerrain, bool onlyTerrain, bool bSkipVegetation, bool bTestRenderMesh, bool* collideWithObject) const { Vec3 wp = m_screenTM_Inverted.TransformPoint(Vec3(vp.x(), vp.y(), 0)); switch (m_axis) { case VPA_XY: case VPA_YX: wp.z = 0; break; case VPA_XZ: wp.y = 0; break; case VPA_YZ: wp.x = 0; break; } return wp; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const { raySrc = ViewToWorld(vp); switch (m_axis) { case VPA_XY: case VPA_YX: raySrc.z = MAX_WORLD_SIZE; rayDir(0, 0, -1); break; case VPA_XZ: raySrc.y = MAX_WORLD_SIZE; rayDir(0, -1, 0); break; case VPA_YZ: raySrc.x = MAX_WORLD_SIZE; rayDir(-1, 0, 0); break; } } ////////////////////////////////////////////////////////////////////////// float Q2DViewport::GetScreenScaleFactor(const Vec3& worldPoint) const { return 400.0f / GetZoomFactor(); //return 100.0f / ; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnTitleMenu(QMenu* menu) { bool bShowGrid = true; if (Q2DViewport* p2DViewport = viewport_cast(GetIEditor()->GetViewManager()->GetSelectedViewport())) { bShowGrid = p2DViewport->GetShowGrid(); } QAction* action = menu->addAction(tr("Labels")); action->setCheckable(true); action->setChecked(GetIEditor()->GetDisplaySettings()->IsDisplayLabels()); connect(action, &QAction::triggered, this, OnMenuLabels); action = menu->addAction(tr("Grid")); action->setCheckable(true); action->setChecked(bShowGrid); connect(action, &QAction::triggered, this, OnMenuGrid); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnDestroy() { if (m_renderer) { m_renderer->DeleteContext(renderOverlayHWND()); } } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::Render() { if (GetIEditor()->IsInGameMode()) { return; } if (!m_renderer) { return; } if (!isVisible()) { return; } if (!GetIEditor()->GetDocument()->IsDocumentReady()) { return; } if (m_renderer->IsStereoEnabled()) { return; } FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR); QRect rc = rect(); if (rc.isEmpty()) { return; } CalculateViewTM(); // Render WIN_HWND priorContext = m_renderer->GetCurrentContextHWND(); m_renderer->SetCurrentContext(renderOverlayHWND()); m_renderer->BeginFrame(); m_renderer->ChangeViewport(0, 0, rc.right(), rc.bottom(), true); CScopedWireFrameMode scopedWireFrame(m_renderer, R_SOLID_MODE); auto colorf = Rgb2ColorF(m_colorBackground); m_renderer->ClearTargetsLater(FRT_CLEAR, colorf); ////////////////////////////////////////////////////////////////////////// // 2D Mode. ////////////////////////////////////////////////////////////////////////// if (rc.right() != 0 && rc.bottom() != 0) { TransformationMatrices backupSceneMatrices; m_renderer->Set2DMode(rc.right(), rc.bottom(), backupSceneMatrices); ////////////////////////////////////////////////////////////////////////// // Draw viewport elements here. ////////////////////////////////////////////////////////////////////////// // Calc world bounding box for objects rendering. m_displayBounds = GetWorldBounds(QPoint(0, 0), QPoint(rc.width(), rc.height())); // Draw all objects. DisplayContext& dc = m_displayContext; dc.settings = GetIEditor()->GetDisplaySettings(); dc.view = this; dc.renderer = m_renderer; dc.engine = GetIEditor()->Get3DEngine(); dc.flags = DISPLAY_2D; dc.box = m_displayBounds; dc.camera = &GetIEditor()->GetSystem()->GetViewCamera(); if (!dc.settings->IsDisplayLabels() || !dc.settings->IsDisplayHelpers()) { dc.flags |= DISPLAY_HIDENAMES; } if (dc.settings->IsDisplayLinks() && dc.settings->IsDisplayHelpers()) { dc.flags |= DISPLAY_LINKS; } if (m_bDegradateQuality) { dc.flags |= DISPLAY_DEGRADATED; } SRenderingPassInfo passInfo = SRenderingPassInfo::CreateGeneralPassRenderingInfo(GetIEditor()->GetSystem()->GetViewCamera()); m_renderer->BeginSpawningGeneratingRendItemJobs(passInfo.ThreadID()); m_renderer->BeginSpawningShadowGeneratingRendItemJobs(passInfo.ThreadID()); m_renderer->EF_StartEf(passInfo); dc.SetState(e_Mode3D | e_AlphaBlended | e_FillModeSolid | e_CullModeBack | e_DepthWriteOff | e_DepthTestOn); Draw(dc); m_renderer->EF_EndEf3D(SHDF_STREAM_SYNC, -1, -1, passInfo); m_renderer->EF_RenderTextMessages(); // Return back from 2D mode. m_renderer->Unset2DMode(backupSceneMatrices); m_renderer->RenderDebug(false); ProcessRenderLisneters(m_displayContext); m_renderer->EndFrame(); } GetIEditor()->GetRenderer()->SetCurrentContext(priorContext); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::Draw(DisplayContext& dc) { dc.DepthTestOff(); dc.DepthWriteOff(); if (m_bShowGrid) { DrawGrid(dc); } DrawObjects(dc); DrawSelection(dc); if (m_bShowViewMarker) { DrawViewerMarker(dc); } DrawAxis(dc); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::DrawGrid(DisplayContext& dc, bool bNoXNumbers) { CGrid* pGrid = GetIEditor()->GetViewManager()->GetGrid(); float gridSize = pGrid->size; if (gridSize < 0.00001f) { return; } float fZ = 0; ////////////////////////////////////////////////////////////////////////// float fScale = GetZoomFactor(); int width = m_rcClient.width(); int height = m_rcClient.height(); float origin[2]; GetScrollOffset(origin[0], origin[1]); ////////////////////////////////////////////////////////////////////////// // Draw Minor grid lines. ////////////////////////////////////////////////////////////////////////// float pixelsPerGrid = gridSize * fScale; m_fGridZoom = 1.0f; if (m_bAutoAdjustGrids) { int griditers = 0; pixelsPerGrid = gridSize * fScale; while (pixelsPerGrid <= 5 && griditers++ < 20) { m_fGridZoom *= pGrid->majorLine; gridSize = gridSize * pGrid->majorLine; pixelsPerGrid = gridSize * fScale; } } int firstGridLineX = origin[0] / gridSize - 1; int firstGridLineY = origin[1] / gridSize - 1; int numGridLinesX = (m_rcClient.width() / fScale) / gridSize + 1; int numGridLinesY = (m_rcClient.height() / fScale) / gridSize + 1; Matrix34 viewTM = GetViewTM().GetInverted() * m_screenTM_Inverted; Matrix34 viewTM_Inv = m_screenTM * GetViewTM(); Vec3 viewP0 = viewTM.TransformPoint(Vec3(0, 0, 0)); Vec3 viewP1 = viewTM.TransformPoint(Vec3(m_rcClient.width(), m_rcClient.height(), 0)); Vec3 viewP_Text = viewTM.TransformPoint(Vec3(0, m_rcClient.height(), 0)); if (m_bShowMinorGridLines && (!m_bAutoAdjustGrids || pixelsPerGrid > 5)) { ////////////////////////////////////////////////////////////////////////// // Draw Minor grid lines. ////////////////////////////////////////////////////////////////////////// Vec3 vec0 = SnapToSize(viewP0, gridSize); Vec3 vec1 = SnapToSize(viewP1, gridSize); if (vec0.x > vec1.x) { std::swap(vec0.x, vec1.x); } if (vec0.y > vec1.y) { std::swap(vec0.y, vec1.y); } dc.SetColor(MINOR_GRID_COLOR, m_gridAlpha); for (float dy = vec0.y; dy < vec1.y; dy += gridSize) { Vec3 p0 = viewTM_Inv.TransformPoint(Vec3(viewP0.x, dy, 0)); Vec3 p1 = viewTM_Inv.TransformPoint(Vec3(viewP1.x, dy, 0)); dc.DrawLine(Vec3(p0.x, p0.y, fZ), Vec3(p1.x, p1.y, fZ)); } for (float dx = vec0.x; dx < vec1.x; dx += gridSize) { Vec3 p0 = viewTM_Inv.TransformPoint(Vec3(dx, viewP0.y, 0)); Vec3 p1 = viewTM_Inv.TransformPoint(Vec3(dx, viewP1.y, 0)); dc.DrawLine(Vec3(p0.x, p0.y, fZ), Vec3(p1.x, p1.y, fZ)); } } if (m_bShowMajorGridLines) { ////////////////////////////////////////////////////////////////////////// // Draw Major grid lines. ////////////////////////////////////////////////////////////////////////// gridSize = gridSize * pGrid->majorLine; if (m_bAutoAdjustGrids) { int iters = 0; pixelsPerGrid = gridSize * fScale; while (pixelsPerGrid < 20 && iters < 20) { gridSize = gridSize * 2; pixelsPerGrid = gridSize * fScale; iters++; } } if (!m_bAutoAdjustGrids || pixelsPerGrid > 20) { Vec3 vec0 = SnapToSize(viewP0, gridSize); Vec3 vec1 = SnapToSize(viewP1, gridSize); if (vec0.x > vec1.x) { std::swap(vec0.x, vec1.x); } if (vec0.y > vec1.y) { std::swap(vec0.y, vec1.y); } dc.SetColor(MAJOR_GRID_COLOR, m_gridAlpha); for (float dy = vec0.y; dy < vec1.y; dy += gridSize) { Vec3 p0 = viewTM_Inv.TransformPoint(Vec3(viewP0.x, dy, 0)); Vec3 p1 = viewTM_Inv.TransformPoint(Vec3(viewP1.x, dy, 0)); dc.DrawLine(Vec3(p0.x, p0.y, fZ), Vec3(p1.x, p1.y, fZ)); } for (float dx = vec0.x; dx < vec1.x; dx += gridSize) { Vec3 p0 = viewTM_Inv.TransformPoint(Vec3(dx, viewP0.y, 0)); Vec3 p1 = viewTM_Inv.TransformPoint(Vec3(dx, viewP1.y, 0)); dc.DrawLine(Vec3(p0.x, p0.y, fZ), Vec3(p1.x, p1.y, fZ)); } // Draw numbers. if (m_bShowNumbers) { char text[64]; dc.SetColor(m_colorGridText); if (!bNoXNumbers) { // Draw horizontal grid text. for (float dx = vec0.x; dx <= vec1.x; dx += gridSize) { Vec3 p = viewTM_Inv.TransformPoint(Vec3(dx, viewP_Text.y, 0)); sprintf_s(text, "%i", (int)(dx)); dc.Draw2dTextLabel(p.x - 8, p.y - 13, 1, text); } } for (float dy = vec0.y; dy <= vec1.y; dy += gridSize) { Vec3 p = viewTM_Inv.TransformPoint(Vec3(viewP_Text.x, dy, 0)); sprintf_s(text, "%i", (int)(dy)); dc.Draw2dTextLabel(p.x + 3, p.y - 12, 1, text); } } } } // Draw Main Axis. { Vec3 org = m_screenTM.TransformPoint(Vec3(0, 0, 0)); dc.SetColor(AXIS_GRID_COLOR); dc.DrawLine(Vec3(org.x, 0, fZ), Vec3(org.x, height, fZ)); dc.DrawLine(Vec3(0, org.y, fZ), Vec3(width, org.y, fZ)); } ////////////////////////////////////////////////////////////////////////// } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::DrawAxis(DisplayContext& dc) { int ix = 0; int iy = 0; float cl = 0.85f; char xstr[2], ystr[2], zstr[2]; Vec3 colx, coly, colz; Vec3 colorX(cl, 0, 0); Vec3 colorY(0, cl, 0); Vec3 colorZ(0, 0, cl); switch (m_axis) { case VPA_XY: azstrcpy(xstr, AZ_ARRAY_SIZE(xstr), "x"); azstrcpy(ystr, AZ_ARRAY_SIZE(ystr), "y"); azstrcpy(zstr, AZ_ARRAY_SIZE(zstr), "z"); colx = colorX; coly = colorY; colz = colorZ; break; case VPA_YX: azstrcpy(xstr, AZ_ARRAY_SIZE(xstr), "x"); azstrcpy(ystr, AZ_ARRAY_SIZE(ystr), "y"); azstrcpy(zstr, AZ_ARRAY_SIZE(zstr), "z"); colx = colorY; coly = colorX; colz = colorZ; break; case VPA_XZ: azstrcpy(xstr, AZ_ARRAY_SIZE(xstr), "x"); azstrcpy(ystr, AZ_ARRAY_SIZE(ystr), "z"); azstrcpy(zstr, AZ_ARRAY_SIZE(zstr), "y"); colx = colorX; coly = colorZ; colz = colorY; break; case VPA_YZ: azstrcpy(xstr, AZ_ARRAY_SIZE(xstr), "y"); azstrcpy(ystr, AZ_ARRAY_SIZE(ystr), "z"); azstrcpy(zstr, AZ_ARRAY_SIZE(zstr), "x"); colx = colorY; coly = colorZ; colz = colorX; break; } int width = m_rcClient.width(); int height = m_rcClient.height(); int size = 25; Vec3 pos(30, height - 15, 1); dc.SetColor(colx.x, colx.y, colx.z, 1); dc.DrawLine(pos, pos + Vec3(size, 0, 0)); dc.SetColor(coly.x, coly.y, coly.z, 1); dc.DrawLine(pos, pos - Vec3(0, size, 0)); dc.SetColor(m_colorAxisText); pos.x -= 3; pos.y -= 4; pos.z = 2; dc.Draw2dTextLabel(pos.x + size + 4, pos.y - 2, 1, xstr); dc.Draw2dTextLabel(pos.x + 3, pos.y - size, 1, ystr); dc.Draw2dTextLabel(pos.x - 5, pos.y + 5, 1, zstr); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::DrawSelection(DisplayContext& dc) { AABB box; GetIEditor()->GetSelectedRegion(box); if (!IsEquivalent(box.min, box.max, 0)) { switch (m_axis) { case VPA_XY: case VPA_YX: box.min.z = box.max.z = 0; break; case VPA_XZ: box.min.y = box.max.y = 0; break; case VPA_YZ: box.min.x = box.max.x = 0; break; } dc.PushMatrix(GetScreenTM()); dc.SetColor(SELECTION_RECT_COLOR.x, SELECTION_RECT_COLOR.y, SELECTION_RECT_COLOR.z, 1); dc.DrawWireBox(box.min, box.max); dc.PopMatrix(); } if (!m_selectedRect.isEmpty()) { dc.SetColor(SELECTION_RECT_COLOR.x, SELECTION_RECT_COLOR.y, SELECTION_RECT_COLOR.z, 1); QPoint p1(m_selectedRect.left(), m_selectedRect.top()); QPoint p2(m_selectedRect.right() + 1, m_selectedRect.bottom() +1); dc.DrawLine(Vec3(p1.x(), p1.y(), 0), Vec3(p2.x(), p1.y(), 0)); dc.DrawLine(Vec3(p1.x(), p2.y(), 0), Vec3(p2.x(), p2.y(), 0)); dc.DrawLine(Vec3(p1.x(), p1.y(), 0), Vec3(p1.x(), p2.y(), 0)); dc.DrawLine(Vec3(p2.x(), p1.y(), 0), Vec3(p2.x(), p2.y(), 0)); } } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::DrawViewerMarker(DisplayContext& dc) { float noScale = 1.0f / GetZoomFactor(); CViewport* pVP = GetViewManager()->GetGameViewport(); if (!pVP) { return; } Ang3 viewAngles = Ang3::GetAnglesXYZ(Matrix33(pVP->GetViewTM())); Matrix34 tm; switch (m_axis) { case VPA_XY: case VPA_YX: tm = Matrix34::CreateRotationXYZ(Ang3(0, 0, viewAngles.z)); break; case VPA_XZ: tm = Matrix34::CreateRotationXYZ(Ang3(viewAngles.x, viewAngles.y, viewAngles.z)); break; case VPA_YZ: tm = Matrix34::CreateRotationXYZ(Ang3(viewAngles.x, 0, viewAngles.z)); break; } tm.SetTranslation(pVP->GetViewTM().GetTranslation()); dc.PushMatrix(GetScreenTM() * tm); Vec3 dim(MARKER_SIZE, MARKER_SIZE / 2, MARKER_SIZE); dc.SetColor(QColor(0, 0, 255)); // blue dc.DrawWireBox(-dim * noScale, dim * noScale); float fov = GetIEditor()->GetSystem()->GetViewCamera().GetFov(); Vec3 q[4]; float dist = 30; float ta = (float)tan(0.5f * fov); float h = dist * ta; float w = h * gSettings.viewports.fDefaultAspectRatio; // ASPECT ?? //float h = w / GetAspect(); q[0] = Vec3(w, dist, h) * noScale; q[1] = Vec3(-w, dist, h) * noScale; q[2] = Vec3(-w, dist, -h) * noScale; q[3] = Vec3(w, dist, -h) * noScale; // Draw frustum. dc.DrawLine(Vec3(0, 0, 0), q[0]); dc.DrawLine(Vec3(0, 0, 0), q[1]); dc.DrawLine(Vec3(0, 0, 0), q[2]); dc.DrawLine(Vec3(0, 0, 0), q[3]); // Draw quad. dc.DrawPolyLine(q, 4); dc.PopMatrix(); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::DrawObjects(DisplayContext& dc) { dc.PushMatrix(GetScreenTM()); if (m_bShowObjectsInfo) { GetIEditor()->GetObjectManager()->Display(dc); } // Display editing tool. if (GetEditTool()) { GetEditTool()->Display(dc); } dc.PopMatrix(); } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::MakeConstructionPlane(int axis) { RefCoordSys coordSys = GetIEditor()->GetReferenceCoordSys(); m_constructionMatrix[COORDS_VIEW] = GetViewTM(); Vec3 p(0, 0, 0); switch (m_axis) { case VPA_XY: AssignConstructionPlane(p, p + Vec3(0, 1, 0), p + Vec3(1, 0, 0)); break; case VPA_YX: m_constructionPlane.SetPlane(p, p + Vec3(1, 0, 0), p + Vec3(0, 1, 0)); break; case VPA_XZ: AssignConstructionPlane(p, p + Vec3(0, 0, 1), p + Vec3(1, 0, 0)); break; case VPA_YZ: AssignConstructionPlane(p, p + Vec3(0, 0, 1), p + Vec3(0, 1, 0)); break; } } ////////////////////////////////////////////////////////////////////////// const Matrix34& Q2DViewport::GetConstructionMatrix(RefCoordSys coordSys) { if (coordSys == COORDS_VIEW) { return GetViewTM(); } return m_constructionMatrix[coordSys]; } ////////////////////////////////////////////////////////////////////////// AABB Q2DViewport::GetWorldBounds(const QPoint& pnt1, const QPoint& pnt2) { Vec3 org; AABB box; box.Reset(); box.Add(ViewToWorld(pnt1)); box.Add(ViewToWorld(pnt2)); int maxSize = MAX_WORLD_SIZE; switch (m_axis) { case VPA_XY: case VPA_YX: box.min.z = -maxSize; box.max.z = maxSize; break; case VPA_XZ: box.min.y = -maxSize; box.max.y = maxSize; break; case VPA_YZ: box.min.x = -maxSize; box.max.x = maxSize; break; } return box; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::OnDragSelectRectangle(const QRect &rect, bool bNormalizeRect) { Vec3 org; AABB box; box.Reset(); Vec3 p1 = ViewToWorld(rect.topLeft()); Vec3 p2 = ViewToWorld(rect.bottomRight() + QPoint(1,1)); org = p1; // Calculate selection volume. box.Add(p1); box.Add(p2); int maxSize = MAX_WORLD_SIZE; char szNewStatusText[512]; float w, h; switch (m_axis) { case VPA_XY: box.min.z = -maxSize; box.max.z = maxSize; w = box.max.x - box.min.x; h = box.max.y - box.min.y; sprintf_s(szNewStatusText, "X:%g Y:%g W:%g H:%g", org.x, org.y, w, h); break; case VPA_YX: box.min.z = -maxSize; box.max.z = maxSize; w = box.max.y - box.min.y; h = box.max.x - box.min.x; sprintf_s(szNewStatusText, "X:%g Y:%g W:%g H:%g", org.x, org.y, w, h); break; case VPA_XZ: box.min.y = -maxSize; box.max.y = maxSize; w = box.max.x - box.min.x; h = box.max.z - box.min.z; sprintf_s(szNewStatusText, "X:%g Z:%g W:%g H:%g", org.x, org.z, w, h); break; case VPA_YZ: box.min.x = -maxSize; box.max.x = maxSize; w = box.max.y - box.min.y; h = box.max.z - box.min.z; sprintf_s(szNewStatusText, "Y:%g Z:%g W:%g H:%g", org.y, org.z, w, h); break; } GetIEditor()->SetSelectedRegion(box); // Show marker position in the status bar GetIEditor()->SetStatusText(szNewStatusText); } ////////////////////////////////////////////////////////////////////////// bool Q2DViewport::HitTest(const QPoint& point, HitContext& hitInfo) { hitInfo.bounds = &m_displayBounds; return QtViewport::HitTest(point, hitInfo); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// bool Q2DViewport::IsBoundsVisible(const AABB& box) const { // If at least part of bbox is visible then its visible. if (m_displayBounds.IsIntersectBox(box)) { return true; } return false; } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::CenterOnSelection() { CSelectionGroup* sel = GetIEditor()->GetSelection(); if (sel->IsEmpty()) { return; } AABB bounds = sel->GetBounds(); CenterOnAABB(bounds); } void Q2DViewport::CenterOnAABB(const AABB& aabb) { Vec3 selPos = aabb.GetCenter(); Vec3 v1 = ViewToWorld(m_rcClient.bottomLeft()); Vec3 v2 = ViewToWorld(m_rcClient.topRight()); Vec3 vofs = (v2 - v1) * 0.5f; selPos -= vofs; SetOrigin2D(selPos); m_bContentValid = false; } ////////////////////////////////////////////////////////////////////////// Vec3 Q2DViewport::GetOrigin2D() const { if (gSettings.viewports.bSync2DViews) { return GetViewManager()->GetOrigin2D(); } else { return m_origin2D; } } ////////////////////////////////////////////////////////////////////////// void Q2DViewport::SetOrigin2D(const Vec3& org) { m_origin2D = org; if (gSettings.viewports.bSync2DViews) { GetViewManager()->SetOrigin2D(m_origin2D); } } #include <2DViewport.moc>