/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
Code/Editor/EditorFramework/DocumentWindow/Implementation/EngineViewWidget.cpp
853 строки
27 KB
Sanakan8472
Render Graph (#1948)
03 июн 2026, 10:19
Не верифицирован
03 июн 2026, 10:19
60bb12c
Код
Авторство
О чём код?
#include <EditorFramework/EditorFrameworkPCH.h> #include <EditorFramework/Assets/AssetDocument.h> #include <EditorFramework/DocumentWindow/EngineDocumentWindow.moc.h> #include <EditorFramework/DocumentWindow/EngineViewWidget.moc.h> #include <EditorFramework/InputContexts/EditorInputContext.h> #include <EditorFramework/Preferences/EditorPreferences.h> #include <Foundation/Utilities/GraphicsUtils.h> #include <GuiFoundation/ActionViews/ToolBarActionMapView.moc.h> #include <QLabel> #include <QTimer> ezUInt32 ezQtEngineViewWidget::s_uiNextViewID = 0; ezQtEngineViewWidget::InteractionContext ezQtEngineViewWidget::s_InteractionContext; void ezObjectPickingResult::Reset() { m_PickedComponent = ezUuid(); m_PickedObject = ezUuid(); m_PickedOther = ezUuid(); m_uiPartIndex = 0; m_vPickedPosition.SetZero(); m_vPickedNormal.SetZero(); m_vPickingRayStart.SetZero(); } /// \brief Small helper class which exposes the native surface that the renderer can render into. class ezQtNativeSurfaceWidget : public QWidget { public: ezQtNativeSurfaceWidget(QWidget* pParent = nullptr) : QWidget(pParent) { // setAttribute(Qt::WA_OpaquePaintEvent); setAutoFillBackground(false); setMouseTracking(true); setMinimumSize(64, 64); // prevent the window from becoming zero sized, otherwise the rendering code may crash setAttribute(Qt::WA_PaintOnScreen, true); setAttribute(Qt::WA_NativeWindow, true); setAttribute(Qt::WA_NoSystemBackground); } virtual void paintEvent(QPaintEvent* pEvent) override {} virtual QPaintEngine* paintEngine() const override { return nullptr; } }; //////////////////////////////////////////////////////////////////////// // ezQtEngineViewWidget public functions //////////////////////////////////////////////////////////////////////// ezSizeU32 ezQtEngineViewWidget::s_FixedResolution(0, 0); ezQtEngineViewWidget::ezQtEngineViewWidget(QWidget* pParent, ezQtEngineDocumentWindow* pDocumentWindow, ezEngineViewConfig* pViewConfig) : QWidget(pParent) , m_pDocumentWindow(pDocumentWindow) , m_pViewConfig(pViewConfig) { setAutoFillBackground(false); setMouseTracking(true); setMinimumSize(64, 64); setFocusPolicy(Qt::FocusPolicy::StrongFocus); m_pMainLayout = new QHBoxLayout(this); m_pMainLayout->setContentsMargins(0, 0, 0, 0); setLayout(m_pMainLayout); // We use a timer instead of resizing the engine viewport when the parent window resizes, because Nvidia driver deadlocks on Linux if you do so in rapid succession. m_pResizeTimer = new QTimer(this); m_pResizeTimer->setSingleShot(true); m_pResizeTimer->setInterval(100); connect(m_pResizeTimer, &QTimer::timeout, this, [this]() { if (m_pViewportWidget && !s_FixedResolution.HasNonZeroArea()) { m_pViewportWidget->setGeometry(0, 0, width(), height()); m_pDocumentWindow->TriggerRedraw(); } }); RecreateEngineViewport(); m_bUpdatePickingData = false; m_bInDragAndDropOperation = false; m_uiViewID = s_uiNextViewID; ++s_uiNextViewID; m_fCameraLerp = 1.0f; m_fCameraTargetFovOrDim = 70.0f; ezEditorEngineProcessConnection::s_Events.AddEventHandler(ezMakeDelegate(&ezQtEngineViewWidget::EngineViewProcessEventHandler, this)); if (ezEditorEngineProcessConnection::GetSingleton()->IsProcessCrashed()) ShowRestartButton(true); } ezQtEngineViewWidget::~ezQtEngineViewWidget() { ezEditorEngineProcessConnection::s_Events.RemoveEventHandler(ezMakeDelegate(&ezQtEngineViewWidget::EngineViewProcessEventHandler, this)); { // Ensure the engine process swap chain is destroyed before the window. ezViewDestroyedMsgToEngine msg; msg.m_uiViewID = GetViewID(); // If we fail to send the message the engine process is down and we don't need to clean up. if (m_pDocumentWindow->GetDocument()->SendMessageToEngine(&msg)) { // Wait for engine process response auto callback = [&](ezProcessMessage* pMsg) -> bool { auto pResponse = static_cast<ezViewDestroyedResponseMsgToEditor*>(pMsg); return pResponse->m_DocumentGuid == m_pDocumentWindow->GetDocument()->GetGuid() && pResponse->m_uiViewID == msg.m_uiViewID; }; ezProcessCommunicationChannel::WaitForMessageCallback cb = callback; if (ezEditorEngineProcessConnection::GetSingleton()->WaitForMessage(ezGetStaticRTTI<ezViewDestroyedResponseMsgToEditor>(), ezTime::MakeFromSeconds(5), &cb).Failed()) { ezLog::Error("Timeout while waiting for engine process to destroy view."); } } } m_pDocumentWindow->RemoveViewWidget(this); } void ezQtEngineViewWidget::SyncToEngine() { ezViewRedrawMsgToEngine cam; cam.m_uiRenderMode = m_pViewConfig->m_RenderMode; float fov = m_pViewConfig->m_Camera.GetFovOrDim(); if (m_pViewConfig->m_Camera.IsPerspective()) { ezEditorPreferencesUser* pPref = ezPreferences::QueryPreferences<ezEditorPreferencesUser>(); fov = pPref->m_fPerspectiveFieldOfView; } cam.m_uiViewID = GetViewID(); cam.m_fNearPlane = m_pViewConfig->m_Camera.GetNearPlane(); cam.m_fFarPlane = m_pViewConfig->m_Camera.GetFarPlane(); cam.m_iCameraMode = (ezInt8)m_pViewConfig->m_Camera.GetCameraMode(); cam.m_bUseCameraTransformOnDevice = m_pViewConfig->m_bUseCameraTransformOnDevice; cam.m_fFovOrDim = fov; cam.m_vDirForwards = m_pViewConfig->m_Camera.GetCenterDirForwards(); cam.m_vDirUp = m_pViewConfig->m_Camera.GetCenterDirUp(); cam.m_vDirRight = m_pViewConfig->m_Camera.GetCenterDirRight(); cam.m_vPosition = m_pViewConfig->m_Camera.GetCenterPosition(); cam.m_ViewMatrix = m_pViewConfig->m_Camera.GetViewMatrix(); m_pViewConfig->m_Camera.GetProjectionMatrix((float)m_pViewportWidget->width() / (float)m_pViewportWidget->height(), cam.m_ProjMatrix); cam.m_uiHWND = (ezUInt64)(m_pViewportWidget->winId()); cam.m_uiWindowWidth = m_pViewportWidget->width() * this->devicePixelRatio(); cam.m_uiWindowHeight = m_pViewportWidget->height() * this->devicePixelRatio(); cam.m_bUpdatePickingData = m_bUpdatePickingData; cam.m_bEnablePickingSelected = IsPickingAgainstSelectionAllowed() && (!ezEditorInputContext::IsAnyInputContextActive() || ezEditorInputContext::GetActiveInputContext()->IsPickingSelectedAllowed()); cam.m_bEnablePickTransparent = m_bPickTransparent; if (s_FixedResolution.HasNonZeroArea()) { cam.m_uiWindowWidth = s_FixedResolution.width; cam.m_uiWindowHeight = s_FixedResolution.height; } m_pDocumentWindow->GetEditorEngineConnection()->SendMessage(&cam); } void ezQtEngineViewWidget::GetCameraMatrices(ezMat4& out_mViewMatrix, ezMat4& out_mProjectionMatrix) const { out_mViewMatrix = m_pViewConfig->m_Camera.GetViewMatrix(); m_pViewConfig->m_Camera.GetProjectionMatrix((float)m_pViewportWidget->width() / (float)m_pViewportWidget->height(), out_mProjectionMatrix); } void ezQtEngineViewWidget::UpdateCameraInterpolation() { if (m_fCameraLerp >= 1.0f) return; const ezTime tNow = ezTime::Now(); const ezTime tDiff = tNow - m_LastCameraUpdate; m_LastCameraUpdate = tNow; m_fCameraLerp += tDiff.GetSeconds() * 3.0f; if (m_fCameraLerp >= 1.0f) m_fCameraLerp = 1.0f; ezCamera& cam = m_pViewConfig->m_Camera; const float fLerpValue = ezMath::Sin(ezAngle::MakeFromDegree(90.0f * m_fCameraLerp)); ezQuat qRot, qRotFinal; qRot = ezQuat::MakeShortestRotation(m_vCameraStartDirection, m_vCameraTargetDirection); qRotFinal = ezQuat::MakeSlerp(ezQuat::MakeIdentity(), qRot, fLerpValue); const ezVec3 vNewDirection = qRotFinal * m_vCameraStartDirection; const ezVec3 vNewPosition = ezMath::Lerp(m_vCameraStartPosition, m_vCameraTargetPosition, fLerpValue); const float fNewFovOrDim = ezMath::Lerp(m_fCameraStartFovOrDim, m_fCameraTargetFovOrDim, fLerpValue); /// \todo Hard coded up vector cam.LookAt(vNewPosition, vNewPosition + vNewDirection, m_vCameraUp); cam.SetCameraMode(cam.GetCameraMode(), fNewFovOrDim, cam.GetNearPlane(), cam.GetFarPlane()); } void ezQtEngineViewWidget::InterpolateCameraTo(const ezVec3& vPosition, const ezVec3& vDirection, float fFovOrDim, const ezVec3* pNewUpDirection /*= nullptr*/, bool bImmediate /*= false*/) { m_vCameraStartPosition = m_pViewConfig->m_Camera.GetPosition(); m_vCameraTargetPosition = vPosition; m_vCameraStartDirection = m_pViewConfig->m_Camera.GetCenterDirForwards(); m_vCameraTargetDirection = vDirection; if (pNewUpDirection) m_vCameraUp = *pNewUpDirection; else m_vCameraUp = m_pViewConfig->m_Camera.GetCenterDirUp(); m_vCameraStartDirection.Normalize(); m_vCameraTargetDirection.Normalize(); m_vCameraUp.Normalize(); m_fCameraStartFovOrDim = m_pViewConfig->m_Camera.GetFovOrDim(); if (fFovOrDim > 0.0f) m_fCameraTargetFovOrDim = fFovOrDim; EZ_ASSERT_DEV(m_fCameraTargetFovOrDim > 0, "Invalid FOV or ortho dimension"); if (m_vCameraStartPosition == m_vCameraTargetPosition && m_vCameraStartDirection == m_vCameraTargetDirection && m_fCameraStartFovOrDim == m_fCameraTargetFovOrDim) return; m_LastCameraUpdate = ezTime::Now(); m_fCameraLerp = 0.0f; if (bImmediate) { // make sure the next camera update interpolates all the way m_LastCameraUpdate -= ezTime::MakeFromSeconds(10); m_fCameraLerp = 0.9f; } } void ezQtEngineViewWidget::SetEnablePicking(bool bEnable) { m_bUpdatePickingData = bEnable; } void ezQtEngineViewWidget::SetPickTransparent(bool bEnable) { if (m_bPickTransparent == bEnable) return; m_bPickTransparent = bEnable; m_LastPickingResult.Reset(); } void ezQtEngineViewWidget::OpenContextMenu(QPoint globalPos) { s_InteractionContext.m_pLastHoveredViewWidget = this; s_InteractionContext.m_pLastPickingResult = &m_LastPickingResult; OnOpenContextMenu(globalPos); } const ezObjectPickingResult& ezQtEngineViewWidget::PickObject(ezUInt16 uiScreenPosX, ezUInt16 uiScreenPosY) const { if (!ezEditorEngineProcessConnection::GetSingleton()->IsEngineSetup()) { m_LastPickingResult.Reset(); } else { ezViewPickingMsgToEngine msg; msg.m_uiViewID = GetViewID(); msg.m_uiPickPosX = uiScreenPosX * devicePixelRatio(); msg.m_uiPickPosY = uiScreenPosY * devicePixelRatio(); GetDocumentWindow()->GetDocument()->SendMessageToEngine(&msg); } return m_LastPickingResult; } void ezQtEngineViewWidget::ClearLastPickedObject() { m_LastPickingResult.Reset(); } ezResult ezQtEngineViewWidget::PickPlane(ezUInt16 uiScreenPosX, ezUInt16 uiScreenPosY, const ezPlane& plane, ezVec3& out_vPosition) const { const auto& cam = m_pViewConfig->m_Camera; ezMat4 mView = cam.GetViewMatrix(); ezMat4 mProj; cam.GetProjectionMatrix((float)m_pViewportWidget->width() / (float)m_pViewportWidget->height(), mProj); ezMat4 mViewProj = mProj * mView; ezMat4 mInvViewProj = mViewProj.GetInverse(); ezVec3 vScreenPos(uiScreenPosX, uiScreenPosY, 0); ezVec3 vResPos, vResRay; if (ezGraphicsUtils::ConvertScreenPosToWorldPos(mInvViewProj, 0, 0, m_pViewportWidget->width(), m_pViewportWidget->height(), vScreenPos, vResPos, &vResRay).Failed()) return EZ_FAILURE; if (plane.GetRayIntersection(vResPos, vResRay, nullptr, &out_vPosition)) return EZ_SUCCESS; return EZ_FAILURE; } void ezQtEngineViewWidget::HandleViewMessage(const ezEditorEngineViewMsg* pMsg) { if (const ezViewPickingResultMsgToEditor* pFullMsg = ezDynamicCast<const ezViewPickingResultMsgToEditor*>(pMsg)) { m_LastPickingResult.m_PickedObject = pFullMsg->m_ObjectGuid; m_LastPickingResult.m_PickedComponent = pFullMsg->m_ComponentGuid; m_LastPickingResult.m_PickedOther = pFullMsg->m_OtherGuid; m_LastPickingResult.m_uiPartIndex = pFullMsg->m_uiPartIndex; m_LastPickingResult.m_vPickedPosition = pFullMsg->m_vPickedPosition; m_LastPickingResult.m_vPickedNormal = pFullMsg->m_vPickedNormal; m_LastPickingResult.m_vPickingRayStart = pFullMsg->m_vPickingRayStartPosition; return; } else if (const ezViewMarqueePickingResultMsgToEditor* pFullMsg = ezDynamicCast<const ezViewMarqueePickingResultMsgToEditor*>(pMsg)) { HandleMarqueePickingResult(pFullMsg); return; } } ezPlane ezQtEngineViewWidget::GetFallbackPickingPlane(ezVec3 vPointOnPlane) const { if (m_pViewConfig->m_Camera.IsPerspective()) { return ezPlane::MakeFromNormalAndPoint(ezVec3(0, 0, 1), vPointOnPlane); } else { return ezPlane::MakeFromNormalAndPoint(-m_pViewConfig->m_Camera.GetCenterDirForwards(), vPointOnPlane); } } void ezQtEngineViewWidget::TakeScreenshot(const char* szOutputPath) const { ezViewScreenshotMsgToEngine msg; msg.m_uiViewID = GetViewID(); msg.m_sOutputFile = szOutputPath; m_pDocumentWindow->GetDocument()->SendMessageToEngine(&msg); } //////////////////////////////////////////////////////////////////////// // ezQtEngineViewWidget qt overrides //////////////////////////////////////////////////////////////////////// bool ezQtEngineViewWidget::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::Type::ShortcutOverride) { if (ezEditorInputContext::IsAnyInputContextActive()) { // if the active input context does not like other shortcuts, // accept this event and thus block further shortcut processing // instead Qt will then send a keypress event if (ezEditorInputContext::GetActiveInputContext()->GetShortcutsDisabled()) event->accept(); } } return false; } void ezQtEngineViewWidget::paintEvent(QPaintEvent* event) { // event->accept(); } void ezQtEngineViewWidget::resizeEvent(QResizeEvent* event) { if (s_FixedResolution.HasNonZeroArea()) { m_pDocumentWindow->TriggerRedraw(); return; } // Defer the viewport resize to avoid recreating the swapchain on every intermediate size // while the user is still dragging the window border. m_pResizeTimer->start(); } void ezQtEngineViewWidget::keyPressEvent(QKeyEvent* e) { if (e->isAutoRepeat()) return; // if a context is active, it gets exclusive access to the input data if (ezEditorInputContext::IsAnyInputContextActive()) { if (ezEditorInputContext::GetActiveInputContext()->KeyPressEvent(e) == ezEditorInput::WasExclusivelyHandled) return; } if (ezEditorInputContext::IsAnyInputContextActive()) return; // Override context { ezEditorInputContext* pOverride = GetDocumentWindow()->GetDocument()->GetEditorInputContextOverride(); if (pOverride != nullptr) { if (pOverride->KeyPressEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) return; } } // if no context is active, pass the input through in a certain order, until someone handles it for (auto pContext : m_InputContexts) { if (pContext->KeyPressEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) return; } QWidget::keyPressEvent(e); } void ezQtEngineViewWidget::keyReleaseEvent(QKeyEvent* e) { if (e->isAutoRepeat()) return; // if a context is active, it gets exclusive access to the input data if (ezEditorInputContext::IsAnyInputContextActive()) { if (ezEditorInputContext::GetActiveInputContext()->KeyReleaseEvent(e) == ezEditorInput::WasExclusivelyHandled) return; } if (ezEditorInputContext::IsAnyInputContextActive()) return; // Override context { ezEditorInputContext* pOverride = GetDocumentWindow()->GetDocument()->GetEditorInputContextOverride(); if (pOverride != nullptr) { if (pOverride->KeyReleaseEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) return; } } // if no context is active, pass the input through in a certain order, until someone handles it for (auto pContext : m_InputContexts) { if (pContext->KeyReleaseEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) return; } QWidget::keyReleaseEvent(e); } void ezQtEngineViewWidget::mousePressEvent(QMouseEvent* e) { // if a context is active, it gets exclusive access to the input data if (ezEditorInputContext::IsAnyInputContextActive()) { if (ezEditorInputContext::GetActiveInputContext()->MousePressEvent(e) == ezEditorInput::WasExclusivelyHandled) { e->accept(); return; } } if (ezEditorInputContext::IsAnyInputContextActive()) { e->accept(); return; } // Override context { ezEditorInputContext* pOverride = GetDocumentWindow()->GetDocument()->GetEditorInputContextOverride(); if (pOverride != nullptr) { if (pOverride->MousePressEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) return; } } // if no context is active, pass the input through in a certain order, until someone handles it for (auto pContext : m_InputContexts) { if (pContext->MousePressEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) { e->accept(); return; } } QWidget::mousePressEvent(e); } void ezQtEngineViewWidget::mouseReleaseEvent(QMouseEvent* e) { // if a context is active, it gets exclusive access to the input data if (ezEditorInputContext::IsAnyInputContextActive()) { if (ezEditorInputContext::GetActiveInputContext()->MouseReleaseEvent(e) == ezEditorInput::WasExclusivelyHandled) { e->accept(); return; } } if (ezEditorInputContext::IsAnyInputContextActive()) { e->accept(); return; } // Override context { ezEditorInputContext* pOverride = GetDocumentWindow()->GetDocument()->GetEditorInputContextOverride(); if (pOverride != nullptr) { if (pOverride->MouseReleaseEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) return; } } // if no context is active, pass the input through in a certain order, until someone handles it for (auto pContext : m_InputContexts) { if (pContext->MouseReleaseEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) { e->accept(); return; } } QWidget::mouseReleaseEvent(e); } void ezQtEngineViewWidget::mouseMoveEvent(QMouseEvent* e) { s_InteractionContext.m_pLastHoveredViewWidget = this; s_InteractionContext.m_pLastPickingResult = &m_LastPickingResult; // kick off the picking PickObject(e->pos().x(), e->pos().y()); // if a context is active, it gets exclusive access to the input data if (ezEditorInputContext::IsAnyInputContextActive()) { if (ezEditorInputContext::GetActiveInputContext()->MouseMoveEvent(e) == ezEditorInput::WasExclusivelyHandled) { e->accept(); return; } } if (ezEditorInputContext::IsAnyInputContextActive()) { e->accept(); return; } // Override context { ezEditorInputContext* pOverride = GetDocumentWindow()->GetDocument()->GetEditorInputContextOverride(); if (pOverride != nullptr) { if (pOverride->MouseMoveEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) return; } } // if no context is active, pass the input through in a certain order, until someone handles it for (auto pContext : m_InputContexts) { if (pContext->MouseMoveEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) { e->accept(); return; } } QWidget::mouseMoveEvent(e); } void ezQtEngineViewWidget::wheelEvent(QWheelEvent* e) { // if a context is active, it gets exclusive access to the input data if (ezEditorInputContext::IsAnyInputContextActive()) { if (ezEditorInputContext::GetActiveInputContext()->WheelEvent(e) == ezEditorInput::WasExclusivelyHandled) return; } if (ezEditorInputContext::IsAnyInputContextActive()) return; // Override context { ezEditorInputContext* pOverride = GetDocumentWindow()->GetDocument()->GetEditorInputContextOverride(); if (pOverride != nullptr) { if (pOverride->WheelEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) return; } } // if no context is active, pass the input through in a certain order, until someone handles it for (auto pContext : m_InputContexts) { if (pContext->WheelEvent(e) == ezEditorInput::WasExclusivelyHandled || ezEditorInputContext::IsAnyInputContextActive()) return; } QWidget::wheelEvent(e); } void ezQtEngineViewWidget::focusOutEvent(QFocusEvent* e) { if (ezEditorInputContext::IsAnyInputContextActive()) { ezEditorInputContext::GetActiveInputContext()->FocusLost(false); ezEditorInputContext::SetActiveInputContext(nullptr); } QWidget::focusOutEvent(e); } void ezQtEngineViewWidget::dragEnterEvent(QDragEnterEvent* e) { m_bInDragAndDropOperation = true; } void ezQtEngineViewWidget::dragLeaveEvent(QDragLeaveEvent* e) { m_bInDragAndDropOperation = false; } void ezQtEngineViewWidget::dropEvent(QDropEvent* e) { m_bInDragAndDropOperation = false; } //////////////////////////////////////////////////////////////////////// // ezQtEngineViewWidget protected functions //////////////////////////////////////////////////////////////////////// void ezQtEngineViewWidget::EngineViewProcessEventHandler(const ezEditorEngineProcessConnection::Event& e) { switch (e.m_Type) { case ezEditorEngineProcessConnection::Event::Type::ProcessCrashed: { ShowProcessStuckIndicator(false); ShowRestartButton(true); } break; case ezEditorEngineProcessConnection::Event::Type::ProcessStarted: { RecreateEngineViewport(); ShowRestartButton(false); } break; case ezEditorEngineProcessConnection::Event::Type::ProcessMessage: break; case ezEditorEngineProcessConnection::Event::Type::Invalid: EZ_ASSERT_DEV(false, "Invalid message should never happen"); break; case ezEditorEngineProcessConnection::Event::Type::ProcessShutdown: case ezEditorEngineProcessConnection::Event::Type::ProcessRestarted: case ezEditorEngineProcessConnection::Event::Type::ProcessUnstuck: ShowProcessStuckIndicator(false); break; case ezEditorEngineProcessConnection::Event::Type::ProcessStuck: ShowProcessStuckIndicator(true); break; } } void ezQtEngineViewWidget::ShowRestartButton(bool bShow) { ezQtScopedUpdatesDisabled _(this); if (m_pRestartButton == nullptr && bShow == true) { m_pRestartButton = new QPushButton(this); m_pRestartButton->setText("Restart Engine View Process"); m_pRestartButton->setVisible(ezEditorEngineProcessConnection::GetSingleton()->IsProcessCrashed()); m_pRestartButton->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); m_pRestartButton->connect(m_pRestartButton, &QPushButton::clicked, this, &ezQtEngineViewWidget::SlotRestartEngineProcess); m_pMainLayout->addWidget(m_pRestartButton); } if (m_pRestartButton) { m_pRestartButton->setVisible(bShow); if (bShow) m_pRestartButton->update(); } m_pViewportWidget->setVisible(!bShow); } void ezQtEngineViewWidget::ShowProcessStuckIndicator(bool bShow) { if (m_pStuckIndicator == nullptr && bShow) { m_pStuckIndicator = new QWidget(m_pViewportWidget); m_pStuckIndicator->setAttribute(Qt::WA_TransparentForMouseEvents); QHBoxLayout* pLayout = new QHBoxLayout(m_pStuckIndicator); pLayout->setContentsMargins(8, 8, 8, 8); QLabel* pIcon = new QLabel(m_pStuckIndicator); pIcon->setPixmap(QIcon(":/GuiFoundation/Icons/Warning.svg").pixmap(32, 32)); pIcon->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); pLayout->addWidget(pIcon); ezOsProcessID engineProcess = ezEditorEngineProcessConnection::GetSingleton()->GetEngineProcessID(); ezStringBuilder sText; sText.SetFormat("Viewport Stalled, ProcessId: {}\nThe engine process might be busy or ran into a problem.", engineProcess); QLabel* pText = new QLabel(ezMakeQString(sText), m_pStuckIndicator); pText->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); pText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); pLayout->addWidget(pText); m_pStuckIndicator->adjustSize(); m_pStuckIndicator->setFixedWidth(5000); } if (m_pStuckIndicator) { m_pStuckIndicator->setVisible(bShow); if (bShow) { m_pStuckIndicator->move(0, 0); m_pStuckIndicator->raise(); } } } void ezQtEngineViewWidget::RecreateEngineViewport() { if (m_pViewportWidget) { m_pStuckIndicator = nullptr; m_pViewportWidget->removeEventFilter(this); m_pViewportWidget->hide(); m_pViewportWidget->setParent(nullptr); m_pViewportWidget->deleteLater(); } m_pViewportWidget = new ezQtNativeSurfaceWidget(this); m_pViewportWidget->installEventFilter(this); m_pViewportWidget->setFocusProxy(this); if (s_FixedResolution.HasNonZeroArea()) { qreal pixelRatio = devicePixelRatio(); // When using DPI scaling, this could actually not be possible to achieve so we use the ceiling of the logical size. This is fine, as the editor tests crop the resulting image if not of the proper size. m_pViewportWidget->setFixedSize( static_cast<ezInt32>(ezMath::Ceil(s_FixedResolution.width / pixelRatio)), static_cast<ezInt32>(ezMath::Ceil(s_FixedResolution.height / pixelRatio))); } else { // Don't add the viewport widget to the layout. Instead, it is resized manually via // a timer in resizeEvent to debounce resize events during interactive window resizing. m_pViewportWidget->setGeometry(0, 0, width(), height()); } } //////////////////////////////////////////////////////////////////////// // ezQtEngineViewWidget private slots //////////////////////////////////////////////////////////////////////// void ezQtEngineViewWidget::SlotRestartEngineProcess() { ezEditorEngineProcessConnection::GetSingleton()->RestartProcess().IgnoreResult(); } //////////////////////////////////////////////////////////////////////// // ezQtViewWidgetContainer //////////////////////////////////////////////////////////////////////// ezQtViewWidgetContainer::ezQtViewWidgetContainer(ads::CDockManager* pDockManager, QWidget* pParent, ezQtEngineViewWidget* pViewWidget, const char* szToolBarMapping) : ads::CDockWidget(pDockManager, "3D View", pParent) { setObjectName("ezQtViewWidgetContainer"); setFeature(ads::CDockWidget::DockWidgetFeature::DockWidgetClosable, false); setFeature(ads::CDockWidget::DockWidgetFeature::DockWidgetFloatable, false); setFeature(ads::CDockWidget::DockWidgetFeature::DockWidgetMovable, false); setFeature(ads::CDockWidget::DockWidgetFeature::DockWidgetFocusable, true); // need contrast with the rest of the widgets around it setBackgroundRole(QPalette::Base); setAutoFillBackground(true); QWidget* pDummy = new QWidget(); pDummy->setObjectName("Dummy"); m_pLayout = new QVBoxLayout(pDummy); m_pLayout->setObjectName("QVBoxLayout1"); m_pLayout->setContentsMargins(0, 0, 0, 0); m_pLayout->setSpacing(0); pDummy->setLayout(m_pLayout); m_pViewWidget = pViewWidget; m_pViewWidget->setParent(pDummy); if (!ezStringUtils::IsNullOrEmpty(szToolBarMapping)) { // Add Tool Bar ezQtToolBarActionMapView* pToolBar = new ezQtToolBarActionMapView("Toolbar", this); ezActionContext context; context.m_sMapping = szToolBarMapping; context.m_pDocument = pViewWidget->GetDocumentWindow()->GetDocument(); context.m_pWindow = m_pViewWidget; pToolBar->SetActionContext(context); m_pLayout->addWidget(pToolBar, 0); } m_pLayout->addWidget(m_pViewWidget, 1); setWidget(pDummy); } ezQtViewWidgetContainer::~ezQtViewWidgetContainer() = default;