PrimeWorldEditor/src/Editor/CBasicViewport.cpp

337 lines
9.9 KiB
C++

#include "CBasicViewport.h"
#include <Common/Math/MathUtil.h>
#include <Core/Render/CDrawUtil.h>
#include <Core/Render/CGraphics.h>
#include <Editor/MacOSExtras.h>
#include <QCursor>
CBasicViewport::CBasicViewport(QWidget *pParent)
: QOpenGLWidget(pParent)
, mLastDrawTime(CTimer::GlobalTime())
, mKeysPressed(0)
, mButtonsPressed(0)
, mCursorState(Qt::ArrowCursor)
, mCursorVisible(true)
{
setMouseTracking(true);
mCamera.SetAspectRatio((float) width() / height());
mViewInfo.ShowFlags = EShowFlag::All;
mViewInfo.pCamera = &mCamera;
mViewInfo.GameMode = false;
}
CBasicViewport::~CBasicViewport()
{
}
void CBasicViewport::initializeGL()
{
// Initialize CGraphics
CGraphics::Initialize();
// Setting various GL flags
glEnable(GL_BLEND);
glEnable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_MULTISAMPLE);
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(0xFFFF);
glPolygonOffset(1.f, 5.f);
glDepthFunc(GL_LEQUAL);
// Clear cached material
CMaterial::KillCachedMaterial();
CShader::KillCachedShader();
// Initialize size
OnResize();
}
void CBasicViewport::paintGL()
{
// Prep render
float scale = devicePixelRatioF();
glViewport(0, 0, (int)((float)width() * scale), (int)((float)height() * scale));
glLineWidth(1.f);
glEnable(GL_DEPTH_TEST);
mViewInfo.ViewFrustum = mCamera.FrustumPlanes();
CGraphics::sMVPBlock.ProjectionMatrix = mCamera.ProjectionMatrix();
// Actual rendering is intended to be handled by subclassing CBasicViewport and
// reimplementing Paint().
Paint();
// Finally, draw XYZ axes in the corner
if (!mViewInfo.GameMode)
DrawAxes();
}
void CBasicViewport::resizeGL(int Width, int Height)
{
mCamera.SetAspectRatio((float) Width / Height);
float scale = devicePixelRatioF();
glViewport(0, 0, (int)((float)Width * scale), (int)((float)Height * scale));
OnResize();
}
void CBasicViewport::mousePressEvent(QMouseEvent *pEvent)
{
setFocus();
if (pEvent->button() == Qt::MidButton) mButtonsPressed |= EMouseInput::MiddleButton;
if (pEvent->button() == Qt::RightButton) mButtonsPressed |= EMouseInput::RightButton;
if (IsMouseInputActive())
{
#if __APPLE__
// This will zero out the drag accumulators
gpMouseDragCocoaEventFilter->claimX();
gpMouseDragCocoaEventFilter->claimY();
#endif
SetCursorVisible(false);
mMouseMoved = false;
mMoveTimer.Restart();
}
// Left click only activates if mouse input is inactive to prevent the user from
// clicking on things and creating selection rectangles while the cursor is hidden
else
{
if (pEvent->button() == Qt::LeftButton)
mButtonsPressed |= EMouseInput::LeftButton;
OnMouseClick(pEvent);
}
mLastMousePos = pEvent->globalPos();
}
void CBasicViewport::mouseReleaseEvent(QMouseEvent *pEvent)
{
bool fromMouseInput = IsMouseInputActive();
if (pEvent->button() == Qt::LeftButton) mButtonsPressed &= ~EMouseInput::LeftButton;
if (pEvent->button() == Qt::MidButton) mButtonsPressed &= ~EMouseInput::MiddleButton;
if (pEvent->button() == Qt::RightButton) mButtonsPressed &= ~EMouseInput::RightButton;
// Make cursor visible if needed
if (!IsMouseInputActive())
SetCursorVisible(true);
// Run mouse release if we didn't just exit mouse input (or regardless on left click)
if (!fromMouseInput || (pEvent->button() == Qt::LeftButton))
OnMouseRelease(pEvent);
// Send context menu event to subclass if needed
if ((pEvent->button() == Qt::RightButton) && (mMoveTimer.Time() <= 0.3) && !mMouseMoved)
{
QContextMenuEvent Event(QContextMenuEvent::Mouse, QCursor::pos());
this->ContextMenu(&Event);
}
}
void CBasicViewport::mouseMoveEvent(QMouseEvent* /*pEvent*/)
{
// todo: draggable selection rectangle
}
void CBasicViewport::wheelEvent(QWheelEvent *pEvent)
{
// Maybe track a "wheel delta" member variable and let CCamera decide what to do with it?
mCamera.Zoom(pEvent->angleDelta().y() / 240.f);
}
void CBasicViewport::keyPressEvent(QKeyEvent *pEvent)
{
switch (pEvent->key())
{
case Qt::Key_Q: mKeysPressed |= EKeyInput::Q; break;
case Qt::Key_W: mKeysPressed |= EKeyInput::W; break;
case Qt::Key_E: mKeysPressed |= EKeyInput::E; break;
case Qt::Key_A: mKeysPressed |= EKeyInput::A; break;
case Qt::Key_S: mKeysPressed |= EKeyInput::S; break;
case Qt::Key_D: mKeysPressed |= EKeyInput::D; break;
case Qt::Key_Control: mKeysPressed |= EKeyInput::Ctrl; break;
case Qt::Key_Shift: mKeysPressed |= EKeyInput::Shift; break;
case Qt::Key_Alt: mKeysPressed |= EKeyInput::Alt; break;
}
}
void CBasicViewport::keyReleaseEvent(QKeyEvent *pEvent)
{
switch (pEvent->key())
{
case Qt::Key_Q: mKeysPressed &= ~EKeyInput::Q; break;
case Qt::Key_W: mKeysPressed &= ~EKeyInput::W; break;
case Qt::Key_E: mKeysPressed &= ~EKeyInput::E; break;
case Qt::Key_A: mKeysPressed &= ~EKeyInput::A; break;
case Qt::Key_S: mKeysPressed &= ~EKeyInput::S; break;
case Qt::Key_D: mKeysPressed &= ~EKeyInput::D; break;
case Qt::Key_Control: mKeysPressed &= ~EKeyInput::Ctrl; break;
case Qt::Key_Shift: mKeysPressed &= ~EKeyInput::Shift; break;
case Qt::Key_Alt: mKeysPressed &= ~EKeyInput::Alt; break;
}
}
void CBasicViewport::focusOutEvent(QFocusEvent*)
{
// When the widget loses focus, release all input.
mButtonsPressed = 0;
mKeysPressed = 0;
SetCursorVisible(true);
}
void CBasicViewport::contextMenuEvent(QContextMenuEvent *pEvent)
{
pEvent->ignore();
}
void CBasicViewport::SetShowFlag(EShowFlag Flag, bool Visible)
{
if (Visible)
mViewInfo.ShowFlags |= Flag;
else
mViewInfo.ShowFlags &= ~Flag;
}
void CBasicViewport::SetGameMode(bool Enabled)
{
mViewInfo.GameMode = Enabled;
}
void CBasicViewport::SetCursorState(const QCursor& rkCursor)
{
mCursorState = rkCursor;
if (IsCursorVisible())
setCursor(rkCursor);
}
void CBasicViewport::SetCursorVisible(bool Visible)
{
mCursorVisible = Visible;
if (Visible)
setCursor(mCursorState);
else
setCursor(Qt::BlankCursor);
}
bool CBasicViewport::IsCursorVisible()
{
return mCursorVisible;
}
bool CBasicViewport::IsMouseInputActive()
{
static const FMouseInputs skMoveButtons = EMouseInput::MiddleButton | EMouseInput::RightButton;
return ((mButtonsPressed & skMoveButtons) != 0);
}
bool CBasicViewport::IsKeyboardInputActive()
{
static const FKeyInputs skMoveKeys = EKeyInput::Q | EKeyInput::W | EKeyInput::E |
EKeyInput::A | EKeyInput::S | EKeyInput::D;
return ((mKeysPressed & skMoveKeys) != 0);
}
CCamera& CBasicViewport::Camera()
{
return mCamera;
}
CRay CBasicViewport::CastRay()
{
CVector2f MouseCoords = MouseDeviceCoordinates();
return mCamera.CastRay(MouseCoords);
}
CVector2f CBasicViewport::MouseDeviceCoordinates()
{
QPoint MousePos = mapFromGlobal(QCursor::pos());
CVector2f Device(
(((2.f * MousePos.x()) / width()) - 1.f),
(1.f - ((2.f * MousePos.y()) / height()))
);
return Device;
}
double CBasicViewport::LastRenderDuration()
{
return mFrameTimer.Time();
}
// ************ PUBLIC SLOTS ************
void CBasicViewport::ProcessInput()
{
// Process camera input
double DeltaTime = CTimer::GlobalTime() - mLastDrawTime;
mLastDrawTime = CTimer::GlobalTime();
if (IsMouseInputActive())
{
#ifdef __APPLE__
// QCursor::setPos only works on macOS when the user permits PWE
// to control the computer via Universal Access.
// As an alternative to relying on the delta of a warped mouse,
// use the accumulated delta directly reported by AppKit.
float XMovement = gpMouseDragCocoaEventFilter->claimX() * 0.01f;
float YMovement = gpMouseDragCocoaEventFilter->claimY() * 0.01f;
#else
float XMovement = (QCursor::pos().x() - mLastMousePos.x()) * 0.01f;
float YMovement = (QCursor::pos().y() - mLastMousePos.y()) * 0.01f;
#endif
if ((XMovement != 0) || (YMovement != 0))
{
mCamera.ProcessMouseInput((FKeyInputs) mKeysPressed, (FMouseInputs) mButtonsPressed, XMovement, YMovement);
QCursor::setPos(mLastMousePos);
mMouseMoved = true;
}
}
if (IsKeyboardInputActive())
if ((mKeysPressed & EKeyInput::Ctrl) == 0)
mCamera.ProcessKeyInput((FKeyInputs) mKeysPressed, DeltaTime);
// Update view info
const CMatrix4f& rkView = mCamera.ViewMatrix();
mViewInfo.RotationOnlyViewMatrix = CMatrix4f(rkView[0][0], rkView[0][1], rkView[0][2], 0.f,
rkView[1][0], rkView[1][1], rkView[1][2], 0.f,
rkView[2][0], rkView[2][1], rkView[2][2], 0.f,
rkView[3][0], rkView[3][1], rkView[3][2], 1.f);
mViewInfo.ViewFrustum = mCamera.FrustumPlanes();
// Check user input
CheckUserInput();
}
void CBasicViewport::Render()
{
mFrameTimer.Start();
update();
mFrameTimer.Stop();
}
// ************ PRIVATE ************
void CBasicViewport::DrawAxes()
{
// Draw 64x64 axes in lower-left corner with 8px margins
glBlendFunc(GL_ONE, GL_ZERO);
glViewport(8, 8, 64, 64);
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT);
glDepthRange(0.f, 1.f);
CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(mCamera.Direction() * 5);
CGraphics::sMVPBlock.ViewMatrix = mViewInfo.RotationOnlyViewMatrix;
CGraphics::sMVPBlock.ProjectionMatrix = Math::OrthographicMatrix(-1.f, 1.f, -1.f, 1.f, 0.1f, 100.f);
CGraphics::UpdateMVPBlock();
glLineWidth(1.f);
CDrawUtil::DrawLine(CVector3f(0,0,0), CVector3f(1,0,0), CColor::skRed); // X
CDrawUtil::DrawLine(CVector3f(0,0,0), CVector3f(0,1,0), CColor::skGreen); // Y
CDrawUtil::DrawLine(CVector3f(0,0,0), CVector3f(0,0,1), CColor::skBlue); // Z
}