qtk/src/app/qtkwidget.cpp

378 lines
10 KiB
C++
Raw Normal View History

2021-09-03 12:56:57 -04:00
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: QtkWidget for Qt desktop application ##
2021-09-03 12:56:57 -04:00
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <QKeyEvent>
2023-12-27 19:36:47 +00:00
#include <QMimeData>
#include <QVBoxLayout>
#include <qtk/input.h>
#include <qtk/scene.h>
#include <qtk/shape.h>
#include <QVBoxLayout>
2021-09-03 12:56:57 -04:00
#include <qtk/input.h>
#include <qtk/scene.h>
#include <qtk/shape.h>
#include "debugconsole.h"
#include "qtkmainwindow.h"
#include "qtkwidget.h"
2021-09-03 12:56:57 -04:00
using namespace Qtk;
2021-09-03 12:56:57 -04:00
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
QtkWidget::QtkWidget(QWidget * parent) : QtkWidget(parent, "QtkWidget") {}
QtkWidget::QtkWidget(QWidget * parent, const QString & name) :
2025-03-08 11:47:19 -05:00
QtkWidget(parent, name, Q_NULLPTR)
{
}
2021-09-03 12:56:57 -04:00
QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR),
2025-03-08 11:47:19 -05:00
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR)
{
2023-12-27 19:36:47 +00:00
setAcceptDrops(true);
setScene(scene);
setObjectName(name);
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(4, 6);
// Set the number of samples used for glEnable(GL_MULTISAMPLING)
format.setSamples(4);
// Set the size of the depth bufer for glEnable(GL_DEPTH_TEST)
format.setDepthBufferSize(16);
// If QTK_DEBUG is set, enable debug context
format.setOption(QSurfaceFormat::DebugContext);
2021-09-03 12:56:57 -04:00
setFormat(format);
setFocusPolicy(Qt::ClickFocus);
2021-09-03 12:56:57 -04:00
}
2025-03-08 11:47:19 -05:00
QtkWidget::~QtkWidget()
{
2021-09-03 12:56:57 -04:00
makeCurrent();
teardownGL();
}
/*******************************************************************************
* Public Methods
2021-09-03 12:56:57 -04:00
******************************************************************************/
2025-03-08 11:47:19 -05:00
QAction * QtkWidget::getActionToggleConsole()
{
auto action = new QAction(mScene->getSceneName() + " debug console");
action->setCheckable(true);
action->setChecked(mConsoleActive);
action->setStatusTip("Add a debug console for this QtkWidget.");
connect(action, &QAction::triggered, this, &QtkWidget::toggleConsole);
return action;
}
2025-03-08 11:47:19 -05:00
void QtkWidget::initializeGL()
{
2021-09-03 12:56:57 -04:00
initializeOpenGLFunctions();
// Connect the frameSwapped signal to call the update() function
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
2025-03-09 11:43:38 -04:00
// Add the debug console widget to the window and set its hidden state.
MainWindow::getMainWindow()->addDockWidget(
Qt::DockWidgetArea::BottomDockWidgetArea, mConsole);
mConsole->setHidden(!mConsoleActive);
2021-09-03 12:56:57 -04:00
// Initialize OpenGL debug context
mDebugLogger = new QOpenGLDebugLogger(this);
2025-03-08 11:47:19 -05:00
if (mDebugLogger->initialize()) {
2021-09-03 12:56:57 -04:00
qDebug() << "GL_DEBUG Debug Logger" << mDebugLogger << "\n";
2025-03-08 11:52:39 -05:00
connect(mDebugLogger,
SIGNAL(messageLogged(QOpenGLDebugMessage)),
this,
2025-03-08 11:47:19 -05:00
SLOT(messageLogged(QOpenGLDebugMessage)));
2021-09-03 12:56:57 -04:00
mDebugLogger->startLogging();
}
printContextInformation();
2022-11-24 22:26:53 +00:00
// Initialize opengl settings
2021-09-03 12:56:57 -04:00
glEnable(GL_MULTISAMPLE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL);
glDepthRange(0.1f, 1.0f);
glClearDepth(1.0f);
glClearColor(0.0f, 0.25f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
2025-03-08 11:47:19 -05:00
void QtkWidget::resizeGL(int width, int height)
{
2022-11-26 18:24:38 +00:00
Scene::getProjectionMatrix().setToIdentity();
2025-03-08 11:52:39 -05:00
Scene::getProjectionMatrix().perspective(
45.0f, float(width) / float(height), 0.1f, 1000.0f);
2021-09-03 12:56:57 -04:00
}
2025-03-08 11:47:19 -05:00
void QtkWidget::paintGL()
{
2022-11-26 18:24:38 +00:00
// Clear buffers and draw the scene if it is valid.
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
2025-03-08 11:47:19 -05:00
if (mScene != Q_NULLPTR) {
2022-11-26 18:24:38 +00:00
mScene->draw();
}
}
2025-03-08 11:47:19 -05:00
void QtkWidget::setScene(Scene * scene)
{
if (mScene != Q_NULLPTR) {
delete mScene;
2025-03-08 11:52:39 -05:00
connect(scene,
&Scene::sceneUpdated,
MainWindow::getMainWindow(),
2025-03-08 11:47:19 -05:00
&MainWindow::refreshScene);
}
mScene = scene;
2025-03-08 11:47:19 -05:00
if (mScene != Q_NULLPTR) {
mConsole->setTitle(mScene->getSceneName());
} else {
mConsole->setTitle("Null Scene");
}
}
2025-03-08 11:47:19 -05:00
void QtkWidget::toggleConsole()
{
2025-03-09 11:43:38 -04:00
mConsole->setHidden(mConsoleActive);
mConsoleActive = !mConsoleActive;
}
2021-09-03 12:56:57 -04:00
/*******************************************************************************
* Protected Methods
2021-09-03 12:56:57 -04:00
******************************************************************************/
2025-03-08 11:47:19 -05:00
void QtkWidget::dragEnterEvent(QDragEnterEvent * event)
{
if (event->mimeData()->hasFormat("text/plain")) {
2023-12-27 19:36:47 +00:00
event->acceptProposedAction();
}
}
2025-03-08 11:47:19 -05:00
void QtkWidget::dropEvent(QDropEvent * event)
{
2023-12-27 19:36:47 +00:00
mConsole->sendLog(event->mimeData()->text());
auto urls = event->mimeData()->urls();
2025-03-08 11:47:19 -05:00
if (!urls.isEmpty()) {
if (urls.size() > 1) {
2023-12-27 19:36:47 +00:00
qDebug() << "Cannot accept drop of multiple files.";
event->ignore();
return;
}
// TODO: Support other object types.
auto url = urls.front();
2025-03-08 11:47:19 -05:00
if (url.fileName().endsWith(".obj")) {
2023-12-27 19:36:47 +00:00
mScene->loadModel(url);
event->acceptProposedAction();
} else {
qDebug() << "Unsupported file type: " + url.fileName() + "\n";
event->ignore();
}
}
}
2025-03-08 11:47:19 -05:00
void QtkWidget::keyPressEvent(QKeyEvent * event)
{
if (event->isAutoRepeat()) {
// Do not repeat input while a key is held down
event->ignore();
} else {
Input::registerKeyPress(event->key());
}
}
2025-03-08 11:47:19 -05:00
void QtkWidget::keyReleaseEvent(QKeyEvent * event)
{
if (event->isAutoRepeat()) {
event->ignore();
} else {
Input::registerKeyRelease(event->key());
}
}
2025-03-08 11:47:19 -05:00
void QtkWidget::mousePressEvent(QMouseEvent * event)
{
Input::registerMousePress(event->button());
}
2025-03-08 11:47:19 -05:00
void QtkWidget::mouseReleaseEvent(QMouseEvent * event)
{
Input::registerMouseRelease(event->button());
}
2025-03-08 11:47:19 -05:00
void QtkWidget::update()
{
2021-09-03 12:56:57 -04:00
updateCameraInput();
2025-03-08 11:47:19 -05:00
if (mScene != Q_NULLPTR) {
2022-11-24 22:26:53 +00:00
mScene->update();
}
2021-09-03 12:56:57 -04:00
QWidget::update();
}
2025-03-08 11:47:19 -05:00
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg)
{
2021-09-03 12:56:57 -04:00
QString error;
DebugContext context;
2021-09-03 12:56:57 -04:00
// Format based on severity
2025-03-08 11:47:19 -05:00
switch (msg.severity()) {
2021-09-03 12:56:57 -04:00
case QOpenGLDebugMessage::NotificationSeverity:
error += "--";
context = Status;
2021-09-03 12:56:57 -04:00
break;
case QOpenGLDebugMessage::HighSeverity:
error += "!!";
context = Fatal;
2021-09-03 12:56:57 -04:00
break;
case QOpenGLDebugMessage::MediumSeverity:
error += "!~";
context = Error;
2021-09-03 12:56:57 -04:00
break;
case QOpenGLDebugMessage::LowSeverity:
error += "~~";
context = Warn;
2021-09-03 12:56:57 -04:00
break;
}
error += " (";
// Format based on source
2022-11-24 22:26:53 +00:00
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error += #c; \
break
2025-03-08 11:47:19 -05:00
switch (msg.source()) {
2021-09-03 12:56:57 -04:00
CASE(APISource);
CASE(WindowSystemSource);
CASE(ShaderCompilerSource);
CASE(ThirdPartySource);
CASE(ApplicationSource);
CASE(OtherSource);
CASE(InvalidSource);
}
#undef CASE
error += " : ";
// Format based on type
2022-11-24 22:26:53 +00:00
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error += #c; \
break
2025-03-08 11:47:19 -05:00
switch (msg.type()) {
2021-09-03 12:56:57 -04:00
CASE(InvalidType);
CASE(ErrorType);
CASE(DeprecatedBehaviorType);
CASE(UndefinedBehaviorType);
CASE(PortabilityType);
CASE(PerformanceType);
CASE(OtherType);
CASE(MarkerType);
CASE(GroupPushType);
CASE(GroupPopType);
}
#undef CASE
error += ")\n" + msg.message() + "\n";
qDebug() << qPrintable(error);
sendLog("(OpenGL) " + error.replace("\n", "\n(OpenGL) "), context);
2021-09-03 12:56:57 -04:00
}
/*******************************************************************************
* Private Methods
2021-09-03 12:56:57 -04:00
******************************************************************************/
2025-03-08 11:47:19 -05:00
void QtkWidget::teardownGL()
{ /* Nothing to teardown yet... */
2021-09-03 12:56:57 -04:00
}
2025-03-08 11:47:19 -05:00
void QtkWidget::updateCameraInput()
{
Input::update();
// Camera Transformation
2025-03-08 11:47:19 -05:00
if (Input::buttonPressed(Qt::LeftButton)
|| Input::buttonPressed(Qt::RightButton)) {
static const float transSpeed = 0.1f;
static const float rotSpeed = 0.5f;
2021-09-03 12:56:57 -04:00
// Handle rotations
Scene::getCamera().getTransform().rotate(
-rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp);
Scene::getCamera().getTransform().rotate(
-rotSpeed * Input::mouseDelta().y(), Scene::getCamera().getRight());
2021-09-03 12:56:57 -04:00
// Handle translations
QVector3D translation;
2025-03-08 11:47:19 -05:00
if (Input::keyPressed(Qt::Key_W)) {
translation += Scene::getCamera().getForward();
}
2025-03-08 11:47:19 -05:00
if (Input::keyPressed(Qt::Key_S)) {
translation -= Scene::getCamera().getForward();
}
2025-03-08 11:47:19 -05:00
if (Input::keyPressed(Qt::Key_A)) {
translation -= Scene::getCamera().getRight();
}
2025-03-08 11:47:19 -05:00
if (Input::keyPressed(Qt::Key_D)) {
translation += Scene::getCamera().getRight();
}
2025-03-08 11:47:19 -05:00
if (Input::keyPressed(Qt::Key_Q)) {
translation -= Scene::getCamera().getUp() / 2.0f;
}
2025-03-08 11:47:19 -05:00
if (Input::keyPressed(Qt::Key_E)) {
translation += Scene::getCamera().getUp() / 2.0f;
}
Scene::getCamera().getTransform().translate(transSpeed * translation);
}
}
2025-03-08 11:47:19 -05:00
void QtkWidget::printContextInformation()
{
2021-09-03 12:56:57 -04:00
QString glType;
QString glVersion;
QString glProfile;
QString glVendor;
QString glRenderer;
// Get Version Information
glType = (context()->isOpenGLES()) ? "OpenGL ES" : "OpenGL";
glVersion = reinterpret_cast<const char *>(glGetString(GL_VERSION));
2022-11-24 22:26:53 +00:00
glVendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
glRenderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
2021-09-03 12:56:57 -04:00
// Get Profile Information
2022-11-24 22:26:53 +00:00
#define CASE(c) \
case QSurfaceFormat::c: \
glProfile = #c; \
break
2025-03-08 11:47:19 -05:00
switch (format().profile()) {
2021-09-03 12:56:57 -04:00
CASE(NoProfile);
CASE(CoreProfile);
CASE(CompatibilityProfile);
}
#undef CASE
auto message = QString(glType) + glVersion + "(" + glProfile + ")"
+ "\nOpenGL Vendor: " + glVendor
+ "\nRendering Device: " + glRenderer;
qDebug() << qPrintable(message);
sendLog("(OpenGL) " + message.replace("\n", "\n(OpenGL) "), Status);
2021-09-03 12:56:57 -04:00
}