337 lines
9.9 KiB
C++
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
|
|
}
|