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_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns)
################################################################################
# External Dependencies
################################################################################
# Point CMAKE_PREFIX_PATH to Qt6 install directory
# If Qtk is built within Qt Creator this is not required.
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(
QT_CREATOR_DIR
@ -97,8 +98,9 @@ set(
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.
# 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)
list(FILTER VAR_NAMES INCLUDE REGEX "^[qQ][tT][kK]_.*$")
list(SORT VAR_NAMES)
@ -143,6 +145,7 @@ set(
CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX QTK_PLUGIN_INSTALL_DIR QT6_INSTALL_PREFIX
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)
# Find Assimp.
@ -184,6 +187,8 @@ if(QTK_EXAMPLE)
add_subdirectory(example-app EXCLUDE_FROM_ALL)
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)
if(VAR_NAME IN_LIST VAR_PATHS)
# 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
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.
To run this project, you will *need* to
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
correctly set our `CMAKE_PREFIX_PATH` in the next steps.
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
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
cd qtk
# 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
cmake --build build-all/
````
@ -75,7 +75,7 @@ Windows / Mac / Linux) and may be easier
to configure.
```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
@ -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
cmake --install build-all/ --component qtk_plugins
-- 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.5.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/lib/libqtk_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.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

View File

@ -24,7 +24,7 @@ void ExampleScene::init() {
setSkybox(skybox);
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());
addObject(spartan);
spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f);

View File

@ -1,6 +1,6 @@
#ifndef 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

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/textures">
<file alias="plaster.png">images/plaster.png</file>
<file alias="crate.png">images/crate.png</file>
<file alias="stone.png">images/stone.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);
}
// 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.
addDockWidget(Qt::LeftDockWidgetArea, ui_->qtk__ToolBox);
// 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];
}
void MainWindow::refreshScene(QString sceneName) {
void MainWindow::refreshScene(const QString & sceneName) {
// TODO: Select TreeView using sceneName
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene());
}

View File

@ -53,6 +53,13 @@ class MainWindow : public QMainWindow {
*/
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);
/**
@ -69,7 +76,7 @@ class MainWindow : public QMainWindow {
* Trigger a refresh for widgets related to a scene that has been updated.
* @param sceneName The name of the scene that has been modified.
*/
void refreshScene(QString sceneName);
void refreshScene(const QString & sceneName);
private:
/***************************************************************************

View File

@ -37,10 +37,10 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>A custom widget tool tip.</string>
<string>Object details and configuration panel.</string>
</property>
<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>
</widget>
</item>
@ -66,10 +66,10 @@
<item>
<widget class="Qtk::QtkWidget" name="qtk::QtkWidget">
<property name="toolTip">
<string>A custom widget tool tip.</string>
<string/>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
<string>Qtk scene view rendered using OpenGL.</string>
</property>
</widget>
</item>
@ -91,10 +91,10 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>A custom widget tool tip.</string>
<string>TreeView of objects within the current scene.</string>
</property>
<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>
</widget>
</item>

View File

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

View File

@ -43,7 +43,7 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
texture.mTexture->destroy();
texture.mTexture->create();
texture.mTexture->setData(
*OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY));
OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY));
modified = true;
}
}
@ -94,6 +94,9 @@ void Model::loadModel(const std::string & path) {
// Optimizes drawing so that overlapping objects are not overwritten
// + Since the topmost object will be drawn first
sortModelMeshes();
if(mTexturesLoaded.empty()) {
mTexturesLoaded.emplace_back("basic", ":/textures/plaster2.png");
}
// Object finished loading, insert it into ModelManager
mManager.insert(getName().c_str(), this);
@ -238,7 +241,7 @@ ModelMesh::Textures Model::loadMaterialTextures(
// Add the texture to the textures container
textures.push_back(texture);
// 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);
}
// Always reset active texture to GL_TEXTURE0 before we draw.
// This is important for models with no textures.
glActiveTexture(GL_TEXTURE0);
// Draw the mesh
glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
@ -62,7 +66,6 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
}
shader.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 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. */
GLuint mID {};
QOpenGLTexture * mTexture {};

View File

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

View File

@ -74,9 +74,9 @@ namespace Qtk {
* Can be absolute or Qt resource path.
* @param flipX If true the image will be flipped on X 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);
/**
@ -132,6 +132,14 @@ namespace Qtk {
const char * right, const char * top, const char * front,
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 ctor to prevent creating instances of this class
OpenGLTextureFactory() = default;