This commit is contained in:
Shaun Reed 2023-12-27 13:47:24 -05:00
parent d4b7655d50
commit 41db5315e0
15 changed files with 88 additions and 41 deletions

View File

@ -84,12 +84,13 @@ endif ()
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources") set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
set(QTK_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns) set(QTK_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns)
################################################################################
# External Dependencies
################################################################################
# Point CMAKE_PREFIX_PATH to Qt6 install directory # Point CMAKE_PREFIX_PATH to Qt6 install directory
# If Qtk is built within Qt Creator this is not required. # If Qtk is built within Qt Creator this is not required.
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}") list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
if (QTK_PREFIX_QTCREATOR)
# TODO: This might be a bit strange and needs more testing.
set(CMAKE_INSTALL_PREFIX "${QT_INSTALL_DIR}")
endif()
set( set(
QT_CREATOR_DIR QT_CREATOR_DIR
@ -97,8 +98,9 @@ set(
CACHE PATH "Qt Creator path used to install Qtk plugins for Qt Designer." CACHE PATH "Qt Creator path used to install Qtk plugins for Qt Designer."
) )
# Print all QTK options and their values at the end of configuration. # Print all QTK options and their values at the end of configuration.
# We initialize this list here so that we can append to it as needed.
# All variables in this list will be printed at the end of configuration.
get_cmake_property(VAR_NAMES VARIABLES) get_cmake_property(VAR_NAMES VARIABLES)
list(FILTER VAR_NAMES INCLUDE REGEX "^[qQ][tT][kK]_.*$") list(FILTER VAR_NAMES INCLUDE REGEX "^[qQ][tT][kK]_.*$")
list(SORT VAR_NAMES) list(SORT VAR_NAMES)
@ -143,6 +145,7 @@ set(
CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX QTK_PLUGIN_INSTALL_DIR QT6_INSTALL_PREFIX CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX QTK_PLUGIN_INSTALL_DIR QT6_INSTALL_PREFIX
QT_INSTALL_DIR QT_INSTALL_DIR
) )
# Add QT6_INSTALL_PLUGINS to VAR_NAMES so it is printed at end of configuration.
list(APPEND VAR_NAMES QT6_INSTALL_PLUGINS) list(APPEND VAR_NAMES QT6_INSTALL_PLUGINS)
# Find Assimp. # Find Assimp.
@ -184,6 +187,8 @@ if(QTK_EXAMPLE)
add_subdirectory(example-app EXCLUDE_FROM_ALL) add_subdirectory(example-app EXCLUDE_FROM_ALL)
endif() endif()
# Print all QTK options and their values at the end of configuration. This also
# prints any additional variables that we have added to VAR_NAMES and VAR_PATHS.
foreach(VAR_NAME IN LISTS VAR_NAMES VAR_PATHS) foreach(VAR_NAME IN LISTS VAR_NAMES VAR_PATHS)
if(VAR_NAME IN_LIST VAR_PATHS) if(VAR_NAME IN_LIST VAR_PATHS)
# Print absolute if variable is path # Print absolute if variable is path

View File

@ -40,16 +40,16 @@ and [Qt Creator](https://github.com/qt-creator/qt-creator).
Simply open the root `CMakeLists.txt` with either of these editors and Simply open the root `CMakeLists.txt` with either of these editors and
configurations will be loaded. configurations will be loaded.
This project has been ported to **Qt 6.5.0**, which is not yet available in This project has been ported to **Qt 6.6.0**, which is not yet available in
Ubuntu apt repositories. Ubuntu apt repositories.
To run this project, you will *need* to To run this project, you will *need* to
install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for
your system, **version 6.5.0** or later. your system, **version 6.6.0** or later.
Be sure to take note of the Qt6 installation directory, as we will need it to Be sure to take note of the Qt6 installation directory, as we will need it to
correctly set our `CMAKE_PREFIX_PATH` in the next steps. correctly set our `CMAKE_PREFIX_PATH` in the next steps.
If you are building on **Windows / Mac**, consider setting If you are building on **Windows / Mac**, consider setting
the `-DASSIMP_NEW_INTERFACE` build flag. the `-DQTK_ASSIMP_NEW_INTERFACE` cmake build option.
If the build is configured with all options enabled, we can subsequently install If the build is configured with all options enabled, we can subsequently install
individual components as needed with cmake. individual components as needed with cmake.
@ -59,7 +59,7 @@ sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git c
git clone https://github.com/shaunrd0/qtk git clone https://github.com/shaunrd0/qtk
cd qtk cd qtk
# Configure the build with all components enabled # Configure the build with all components enabled
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64 cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.6.0/gcc_64
# Build all targets # Build all targets
cmake --build build-all/ cmake --build build-all/
```` ````
@ -75,7 +75,7 @@ Windows / Mac / Linux) and may be easier
to configure. to configure.
```bash ```bash
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DQTK_SUBMODULES=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64 cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DQTK_SUBMODULES=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.6.0/gcc_64
``` ```
#### Qtk GUI #### Qtk GUI
@ -149,9 +149,9 @@ cmake --build build-all/ --target qtk_plugins -- -j $(nproc)
# The path here should be initialized during build configuration, so no need for --prefix # The path here should be initialized during build configuration, so no need for --prefix
cmake --install build-all/ --component qtk_plugins cmake --install build-all/ --component qtk_plugins
-- Install configuration: "Release" -- Install configuration: "Release"
-- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_library.a -- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_library.a
-- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_plugin_library.a -- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_plugin_library.a
-- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/plugins/designer/libqtk_collection.so -- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/plugins/designer/libqtk_collection.so
``` ```
To uninstall after a previous installation, we can run the following command To uninstall after a previous installation, we can run the following command

View File

@ -24,7 +24,7 @@ void ExampleScene::init() {
setSkybox(skybox); setSkybox(skybox);
std::string spartanPath = QTK_EXAMPLE_SOURCE_DIR; std::string spartanPath = QTK_EXAMPLE_SOURCE_DIR;
spartanPath += "/../resources/models/spartan/spartan.obj"; spartanPath += "/resources/models/spartan/spartan.obj";
auto spartan = new Model("spartan", spartanPath.c_str()); auto spartan = new Model("spartan", spartanPath.c_str());
addObject(spartan); addObject(spartan);
spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f); spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f);

View File

@ -1,6 +1,6 @@
#ifndef QTK_RESOURCES_H_IN_H #ifndef QTK_RESOURCES_H_IN_H
#define QTK_RESOURCES_H_IN_H #define QTK_RESOURCES_H_IN_H
#define QTK_EXAMPLE_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@" #define QTK_EXAMPLE_SOURCE_DIR "@CMAKE_SOURCE_DIR@"
#endif // QTK_RESOURCES_H_IN_H #endif // QTK_RESOURCES_H_IN_H

View File

@ -1,5 +1,6 @@
<RCC> <RCC>
<qresource prefix="/textures"> <qresource prefix="/textures">
<file alias="plaster.png">images/plaster.png</file>
<file alias="crate.png">images/crate.png</file> <file alias="crate.png">images/crate.png</file>
<file alias="stone.png">images/stone.png</file> <file alias="stone.png">images/stone.png</file>
<file alias="wood.png">images/wood.png</file> <file alias="wood.png">images/wood.png</file>

View File

@ -41,6 +41,11 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
&Qtk::ToolBox::updateFocus); &Qtk::ToolBox::updateFocus);
} }
// TODO: Fix / use MainWindow in Qt Designer to add these dock widgets.
// For now we will add them manually, but we should be able to do this in the
// designer. At the moment if you edit the UI in designer the dock widget
// areas below will override the designer settings.
// Dock the toolbox widget to the main window. // Dock the toolbox widget to the main window.
addDockWidget(Qt::LeftDockWidgetArea, ui_->qtk__ToolBox); addDockWidget(Qt::LeftDockWidgetArea, ui_->qtk__ToolBox);
// Add an option to toggle active widgets in the GUI's toolbar 'view' menu. // Add an option to toggle active widgets in the GUI's toolbar 'view' menu.
@ -82,7 +87,7 @@ Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name) {
return views_[name]; return views_[name];
} }
void MainWindow::refreshScene(QString sceneName) { void MainWindow::refreshScene(const QString & sceneName) {
// TODO: Select TreeView using sceneName // TODO: Select TreeView using sceneName
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene()); ui_->qtk__TreeView->updateView(getQtkWidget()->getScene());
} }

View File

@ -53,6 +53,13 @@ class MainWindow : public QMainWindow {
*/ */
static MainWindow * getMainWindow(); static MainWindow * getMainWindow();
static std::pair<QString, Qtk::DebugContext> parseError(
const QOpenGLDebugMessage & msg);
static void log(const QString & message);
static void log(const QOpenGLDebugMessage & msg);
Qtk::QtkWidget * getQtkWidget(int64_t index = 0); Qtk::QtkWidget * getQtkWidget(int64_t index = 0);
/** /**
@ -69,7 +76,7 @@ class MainWindow : public QMainWindow {
* Trigger a refresh for widgets related to a scene that has been updated. * Trigger a refresh for widgets related to a scene that has been updated.
* @param sceneName The name of the scene that has been modified. * @param sceneName The name of the scene that has been modified.
*/ */
void refreshScene(QString sceneName); void refreshScene(const QString & sceneName);
private: private:
/*************************************************************************** /***************************************************************************

View File

@ -37,10 +37,10 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>A custom widget tool tip.</string> <string>Object details and configuration panel.</string>
</property> </property>
<property name="whatsThis"> <property name="whatsThis">
<string>Custom widget what's this?</string> <string>When an object is double-clicked in the TreeView for a scene, this panel will display relevant details and options.</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -66,10 +66,10 @@
<item> <item>
<widget class="Qtk::QtkWidget" name="qtk::QtkWidget"> <widget class="Qtk::QtkWidget" name="qtk::QtkWidget">
<property name="toolTip"> <property name="toolTip">
<string>A custom widget tool tip.</string> <string/>
</property> </property>
<property name="whatsThis"> <property name="whatsThis">
<string>Custom widget what's this?</string> <string>Qtk scene view rendered using OpenGL.</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -91,10 +91,10 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>A custom widget tool tip.</string> <string>TreeView of objects within the current scene.</string>
</property> </property>
<property name="whatsThis"> <property name="whatsThis">
<string>Custom widget what's this?</string> <string>TreeView of objects within the current scene. Double-click to select an object and snap to it's position.</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -77,6 +77,7 @@ void QtkWidget::initializeGL() {
// Connect the frameSwapped signal to call the update() function // Connect the frameSwapped signal to call the update() function
connect(this, SIGNAL(frameSwapped()), this, SLOT(update())); connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
toggleConsole();
// Initialize OpenGL debug context // Initialize OpenGL debug context
mDebugLogger = new QOpenGLDebugLogger(this); mDebugLogger = new QOpenGLDebugLogger(this);
if(mDebugLogger->initialize()) { if(mDebugLogger->initialize()) {
@ -154,8 +155,8 @@ void QtkWidget::dragEnterEvent(QDragEnterEvent * event) {
void QtkWidget::dropEvent(QDropEvent * event) { void QtkWidget::dropEvent(QDropEvent * event) {
mConsole->sendLog(event->mimeData()->text()); mConsole->sendLog(event->mimeData()->text());
if(event->mimeData()->hasUrls()) { auto urls = event->mimeData()->urls();
auto urls = event->mimeData()->urls(); if(!urls.isEmpty()) {
if(urls.size() > 1) { if(urls.size() > 1) {
qDebug() << "Cannot accept drop of multiple files."; qDebug() << "Cannot accept drop of multiple files.";
event->ignore(); event->ignore();
@ -168,7 +169,7 @@ void QtkWidget::dropEvent(QDropEvent * event) {
mScene->loadModel(url); mScene->loadModel(url);
event->acceptProposedAction(); event->acceptProposedAction();
} else { } else {
qDebug() << "Unsupported file type."; qDebug() << "Unsupported file type: " + url.fileName() + "\n";
event->ignore(); event->ignore();
} }
} }

View File

@ -43,7 +43,7 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
texture.mTexture->destroy(); texture.mTexture->destroy();
texture.mTexture->create(); texture.mTexture->create();
texture.mTexture->setData( texture.mTexture->setData(
*OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY)); OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY));
modified = true; modified = true;
} }
} }
@ -94,6 +94,9 @@ void Model::loadModel(const std::string & path) {
// Optimizes drawing so that overlapping objects are not overwritten // Optimizes drawing so that overlapping objects are not overwritten
// + Since the topmost object will be drawn first // + Since the topmost object will be drawn first
sortModelMeshes(); sortModelMeshes();
if(mTexturesLoaded.empty()) {
mTexturesLoaded.emplace_back("basic", ":/textures/plaster2.png");
}
// Object finished loading, insert it into ModelManager // Object finished loading, insert it into ModelManager
mManager.insert(getName().c_str(), this); mManager.insert(getName().c_str(), this);
@ -238,7 +241,7 @@ ModelMesh::Textures Model::loadMaterialTextures(
// Add the texture to the textures container // Add the texture to the textures container
textures.push_back(texture); textures.push_back(texture);
// Add the texture to the loaded textures to avoid loading it twice // Add the texture to the loaded textures to avoid loading it twice
mTexturesLoaded.push_back(texture); mTexturesLoaded.push_back(textures.back());
} }
} }

View File

@ -52,6 +52,10 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
shader.setUniformValue((name + number).c_str(), i); shader.setUniformValue((name + number).c_str(), i);
} }
// Always reset active texture to GL_TEXTURE0 before we draw.
// This is important for models with no textures.
glActiveTexture(GL_TEXTURE0);
// Draw the mesh // Draw the mesh
glDrawElements( glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data()); GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
@ -62,7 +66,6 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
} }
shader.release(); shader.release();
mVAO->release(); mVAO->release();
glActiveTexture(GL_TEXTURE0);
} }
/******************************************************************************* /*******************************************************************************

View File

@ -30,6 +30,21 @@ namespace Qtk {
* Struct to store model textures. 3D Models may have multiple. * Struct to store model textures. 3D Models may have multiple.
*/ */
struct QTKAPI ModelTexture { struct QTKAPI ModelTexture {
ModelTexture() = default;
/**
* Construct a ModelTexture.
*
* @param id Texture ID for this texture.
* @param type Type of texture in string format.
* @param path Path to the texture on disk.
*/
ModelTexture(const std::string & type, const std::string & path) :
mType(type), mPath(path) {
mTexture = OpenGLTextureFactory::initTexture(path.c_str());
mID = mTexture->textureId();
}
/** Texture ID for for this texture. */ /** Texture ID for for this texture. */
GLuint mID {}; GLuint mID {};
QOpenGLTexture * mTexture {}; QOpenGLTexture * mTexture {};

View File

@ -87,7 +87,7 @@ namespace Qtk {
void loadModel(const std::string & name, const std::string & path) { void loadModel(const std::string & name, const std::string & path) {
// Add the dropped model to the load queue. // Add the dropped model to the load queue.
// This is consumed during rendering of the scene if not empty. // This is consumed during rendering of the scene if not empty.
mModelLoadQueue.push({name, path}); mModelLoadQueue.emplace(name, path);
} }
/************************************************************************* /*************************************************************************

View File

@ -9,19 +9,18 @@
#include <QDebug> #include <QDebug>
#include <QImageReader> #include <QImageReader>
#include "app/qtkmainwindow.h"
#include "texture.h" #include "texture.h"
using namespace Qtk; using namespace Qtk;
QImage * OpenGLTextureFactory::initImage( QImage OpenGLTextureFactory::initImage(
const char * image, bool flipX, bool flipY) { const char * image, bool flipX, bool flipY) {
// Qt6 limits loaded images to 256MB by default // Qt6 limits loaded images to 256MB by default
QImageReader::setAllocationLimit(512); QImageReader::setAllocationLimit(1024);
auto loadedImage = new QImage(QImage(image).mirrored(flipX, flipY)); auto loadedImage = QImage(image).mirrored(flipX, flipY);
if(loadedImage->isNull()) { if(loadedImage.isNull()) {
qDebug() << "[Qtk::OpenGLTextureFactory] Error loading image: " << image return defaultTexture();
<< "\nSupported types: " << QImageReader::supportedImageFormats();
return Q_NULLPTR;
} }
return loadedImage; return loadedImage;
@ -29,13 +28,12 @@ QImage * OpenGLTextureFactory::initImage(
QOpenGLTexture * OpenGLTextureFactory::initTexture( QOpenGLTexture * OpenGLTextureFactory::initTexture(
const char * texture, bool flipX, bool flipY) { const char * texture, bool flipX, bool flipY) {
QImage * image = initImage(texture, flipX, flipY); QImage image = initImage(texture, flipX, flipY);
auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
newTexture->setData(*image); newTexture->setData(image);
newTexture->setWrapMode(QOpenGLTexture::Repeat); newTexture->setWrapMode(QOpenGLTexture::Repeat);
newTexture->setMinMagFilters( newTexture->setMinMagFilters(
QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear); QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
delete image;
return newTexture; return newTexture;
} }
@ -71,6 +69,7 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
QImage faceImage(faceTextures[i]); QImage faceImage(faceTextures[i]);
if(faceImage.isNull()) { if(faceImage.isNull()) {
qDebug() << "Error loading cube map image\n"; qDebug() << "Error loading cube map image\n";
faceImage = defaultTexture();
} }
faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888); faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888);

View File

@ -74,9 +74,9 @@ namespace Qtk {
* Can be absolute or Qt resource path. * Can be absolute or Qt resource path.
* @param flipX If true the image will be flipped on X axis. * @param flipX If true the image will be flipped on X axis.
* @param flipY If true the image will be flipped on Y axis. * @param flipY If true the image will be flipped on Y axis.
* @return Pointer to an initialized QImage object. * @return QImage object.
*/ */
static QImage * initImage( static QImage initImage(
const char * image, bool flipX = false, bool flipY = false); const char * image, bool flipX = false, bool flipY = false);
/** /**
@ -132,6 +132,14 @@ namespace Qtk {
const char * right, const char * top, const char * front, const char * right, const char * top, const char * front,
const char * left, const char * bottom, const char * back); const char * left, const char * bottom, const char * back);
/// The texture used in place of a missing texture.
static QImage defaultTexture() {
// Use plaster for default texture if image fails to load.
// This prevents segfaults when loading a texture that doesn't exist.
// TODO: Replace with a '?' texture to indicate missing texture.
return QImage(":/textures/plaster.png");
}
private: private:
// Private ctor to prevent creating instances of this class // Private ctor to prevent creating instances of this class
OpenGLTextureFactory() = default; OpenGLTextureFactory() = default;