From 0c16b7d879d76b623786d7ba40b921ae971e12b9 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 14 Feb 2026 13:41:18 -0500 Subject: [PATCH 1/9] Revert "ToolBar buttons to add and remove objects. (#18)" This reverts commit ed604eb6551fe119dffc063e3a4f742e569a3a26. --- src/app/qtkmainwindow.cpp | 30 -------------------- src/app/qtkmainwindow.h | 19 +------------ src/app/qtkmainwindow.ui | 4 +-- src/designer-plugins/toolbox.cpp | 48 +++----------------------------- src/designer-plugins/toolbox.h | 37 +++--------------------- src/qtk/scene.cpp | 28 ------------------- src/qtk/scene.h | 24 ++++------------ 7 files changed, 17 insertions(+), 173 deletions(-) diff --git a/src/app/qtkmainwindow.cpp b/src/app/qtkmainwindow.cpp index 945ff91..f05b54a 100644 --- a/src/app/qtkmainwindow.cpp +++ b/src/app/qtkmainwindow.cpp @@ -48,16 +48,6 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) &Qtk::ToolBox::updateFocus); } - connect(ui_->actionDelete_Object, - &QAction::triggered, - this, - &MainWindow::deleteObject); - - connect(ui_->actionLoad_Model, - &QAction::triggered, - this, - &MainWindow::loadObject); - // 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 @@ -114,26 +104,6 @@ void MainWindow::refreshScene(const QString & sceneName) ui_->qtk__TreeView->updateView(getQtkWidget()->getScene()); } -void MainWindow::deleteObject() -{ - if (auto object = ui_->qtk__ToolBox->getObjectFocus(); object != Q_NULLPTR) { - auto scene = getQtkWidget()->getScene(); - switch (object->getType()) { - case Qtk::Object::Type::QTK_MESH: - scene->removeObject(dynamic_cast(object)); - ui_->qtk__ToolBox->clearFocus(); - break; - case Qtk::Object::Type::QTK_MODEL: - scene->removeObject(dynamic_cast(object)); - ui_->qtk__ToolBox->clearFocus(); - break; - default: - qDebug() << "Failed to delete model with invalid type"; - break; - } - } -} - void MainWindow::setScene(Qtk::Scene * scene) { connect(scene, diff --git a/src/app/qtkmainwindow.h b/src/app/qtkmainwindow.h index 4d41cc1..40c5df9 100644 --- a/src/app/qtkmainwindow.h +++ b/src/app/qtkmainwindow.h @@ -11,8 +11,8 @@ #include -#include #include +#include #include "designer-plugins/debugconsole.h" @@ -144,23 +144,6 @@ class MainWindow : public QMainWindow */ void refreshScene(const QString & sceneName); - - /** - * Opens a QFileDialog for selecting an object file to load into the scene. - */ - void loadObject() - { - const QUrl file = QFileDialog::getOpenFileName( - this, tr("Load Model"), QDir::homePath(), tr("Object Files (*.obj)")); - getQtkWidget()->getScene()->loadModel(file.fileName().replace(".obj", ""), - file.toString()); - } - - /** - * Deletes the currently selected object from the scene. - */ - void deleteObject(); - private: /*************************************************************************** * Private Members diff --git a/src/app/qtkmainwindow.ui b/src/app/qtkmainwindow.ui index 218d397..3078ef7 100644 --- a/src/app/qtkmainwindow.ui +++ b/src/app/qtkmainwindow.ui @@ -223,7 +223,7 @@ - true + false @@ -238,7 +238,7 @@ - true + false diff --git a/src/designer-plugins/toolbox.cpp b/src/designer-plugins/toolbox.cpp index 268e695..4d57129 100644 --- a/src/designer-plugins/toolbox.cpp +++ b/src/designer-plugins/toolbox.cpp @@ -20,7 +20,7 @@ ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), objectDetails_(this), transformPanel_(this), scalePanel_(this), vertex_(this, "Vertex Shader:"), fragment_(this, "Fragment Shader:"), properiesForm_(new QFormLayout), - shaderForm_(new QFormLayout), ui(new Ui::ToolBox), objectFocus_(Q_NULLPTR) + shaderForm_(new QFormLayout), ui(new Ui::ToolBox) { ui->setupUi(this); setMinimumWidth(350); @@ -44,24 +44,10 @@ void ToolBox::updateFocus(const QString & name) { auto object = QtkWidget::mWidgetManager.get_widget()->getScene()->getObject(name); - // If we can't find the object show a warning. - if (object == Q_NULLPTR) { - qDebug() << "Failed to find selected object: " << name - << "; Clearing object panels."; + if (object != Q_NULLPTR) { + refreshProperties(object); + refreshShaders(object); } - - // We should still pass the nullptr here if we failed to find the object - // above. - objectFocus_ = object; - refreshProperties(object); - refreshShaders(object); -} - -void ToolBox::clearFocus() -{ - objectFocus_ = Q_NULLPTR; - refreshProperties(objectFocus_); - refreshShaders(objectFocus_); } ToolBox::SpinBox3D::SpinBox3D(QWidget * parent, const char * l) : @@ -84,13 +70,6 @@ void ToolBox::SpinBox::disconnect() const void ToolBox::TransformPanel::setObject(const Qtk::Object * object) { - // Zero the panel contents if there is no object selected. - if (object == Q_NULLPTR) { - spinBox3D.clear(); - return; - } - - // Reconnect translation panel controls to the new object. const std::vector binds = {&Object::setTranslationX, &Object::setTranslationY, @@ -111,12 +90,6 @@ void ToolBox::TransformPanel::setObject(const Qtk::Object * object) void ToolBox::ScalePanel::setObject(const Qtk::Object * object) { - // Zero the panel contents if there is no object selected. - if (object == Q_NULLPTR) { - spinBox3D.clear(); - return; - } - // Reconnect scale panel controls to the new object. const std::vector binds = { &Object::setScaleX, &Object::setScaleY, &Object::setScaleZ}; @@ -150,21 +123,8 @@ void ToolBox::refreshProperties(const Object * object) void ToolBox::refreshShaders(const Object * object) { - // Zero the panel contents if there is no object selected. - if (object == Q_NULLPTR) { - vertex_.clear(); - fragment_.clear(); - return; - } - vertex_.path.setValue(object->getVertexShader().c_str()); vertex_.editor->setText(object->getVertexShaderSourceCode().c_str()); fragment_.path.setValue(object->getFragmentShader().c_str()); fragment_.editor->setText(object->getFragmentShaderSourceCode().c_str()); } - -void ToolBox::refresh(const Object * object) -{ - refreshProperties(object); - refreshShaders(object); -} diff --git a/src/designer-plugins/toolbox.h b/src/designer-plugins/toolbox.h index 9f2f621..6eb9769 100644 --- a/src/designer-plugins/toolbox.h +++ b/src/designer-plugins/toolbox.h @@ -18,8 +18,8 @@ #include #include -#include "qtk/object.h" +#include "qtk/scene.h" namespace Ui { @@ -45,13 +45,8 @@ namespace Qtk void refreshShaders(const Object * object); - void refresh(const Object * object); - void updateFocus(const QString & name); - [[nodiscard]] Object * getObjectFocus() const { return objectFocus_; } - - void clearFocus(); private: /************************************************************************* @@ -70,9 +65,9 @@ namespace Qtk { } - void setValue(const char * v) const { value->setText(tr(v)); } + void setValue(const char * v) { value->setText(tr(v)); } - void setItem(const char * l, const char * v) const + void setItem(const char * l, const char * v) { label->setText(tr(l)); value->setText(tr(v)); @@ -89,14 +84,8 @@ namespace Qtk } /// Refresh to display the new object's details - void setObject(const Qtk::Object * object) const + void setObject(const Qtk::Object * object) { - // Zero contents if there is no object selected. - if (object == Q_NULLPTR) { - name.setValue(""); - objectType.setValue("No object selected"); - return; - } name.setItem("Name:", object->getName().toStdString().c_str()); objectType.setItem( "Type:", @@ -130,15 +119,6 @@ namespace Qtk /// Assigning a QWidget to a QLayout also ensures Qt will clean up. explicit SpinBox3D(QWidget * parent, const char * l = "SpinBox3D:"); - /// Zero the SpinBox3D display. - void clear() - { - for (auto & field : fields) { - field->disconnect(); - field->spinBox->setValue(0.0); - } - } - /// The main layout for the SpinBox3D widget. QHBoxLayout * layout; @@ -196,13 +176,6 @@ namespace Qtk layout->addWidget(editor); } - /// Zero the ShaderView display. - void clear() const - { - path.setValue(""); - editor->setText(""); - } - /// The main layout for the ShaderView widget. QVBoxLayout * layout; @@ -217,8 +190,6 @@ namespace Qtk QFormLayout * properiesForm_; QFormLayout * shaderForm_; - Object * objectFocus_ {}; - Ui::ToolBox * ui; }; } // namespace Qtk diff --git a/src/qtk/scene.cpp b/src/qtk/scene.cpp index 45282b1..54fb66f 100644 --- a/src/qtk/scene.cpp +++ b/src/qtk/scene.cpp @@ -55,34 +55,6 @@ template <> Model * Scene::addObject(Model * object) return object; } -template <> void Scene::removeObject(MeshRenderer * object) -{ - auto it = std::find(mMeshes.begin(), mMeshes.end(), object); - if (it == mMeshes.end()) { - qDebug() << "[Scene::removeObject]: Failed to remove object: " - << object->getName() << " (" << object << ")"; - return; - } - - --mObjectCount[object->getName()]; - mMeshes.erase(it); - emit sceneUpdated(mSceneName); -} - -template <> void Scene::removeObject(Model * object) -{ - auto it = std::find(mModels.begin(), mModels.end(), object); - if (it == mModels.end()) { - qDebug() << "[Scene::removeObject]: Failed to remove object: " - << object->getName() << " (" << object << ")"; - return; - } - - --mObjectCount[object->getName()]; - mModels.erase(it); - emit sceneUpdated(mSceneName); -} - void Scene::draw() { if (!mInit) { diff --git a/src/qtk/scene.h b/src/qtk/scene.h index 67f14f7..a4f9180 100644 --- a/src/qtk/scene.h +++ b/src/qtk/scene.h @@ -85,16 +85,16 @@ namespace Qtk void loadModel(const QUrl & url) { - auto fileName = url.fileName().replace(".obj", ""); - auto filePath = url.toLocalFile(); + auto fileName = url.fileName().replace(".obj", "").toStdString(); + auto filePath = url.toLocalFile().toStdString(); loadModel(fileName, filePath); } - void loadModel(const QString & name, const QString & path) + 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.emplace(name.toStdString(), path.toStdString()); + mModelLoadQueue.emplace(name, path); } /************************************************************************* @@ -182,9 +182,8 @@ namespace Qtk /** * Adds objects to the scene. - * This template provides explicit specializations for the valid types: - * MeshRenderer, Model - * Any other object type will cause errors. + * This template provides explicit specializations for valid types. + * Adding any object other than these types will cause errors. * TODO: Refactor to use Object base class container for scene objects. * * If creating a new object type for a scene, it must inherit Qtk::Object @@ -195,17 +194,6 @@ namespace Qtk */ template T * addObject(T * object); - /** - * Removes an object from the scene. - * This template provides explicit specializations for the valid types: - * MeshRenderer, Model - * Any other object type will cause errors. - * TODO: Refactor to use Object base class container for scene objects. - * - * @param object Pointer to the object to remove from the scene. - */ - template void removeObject(T * object); - /** * @param name The name to use for this scene. */ -- 2.49.1 From 0770b0ea65256ed31cfd48a9f628b3118f84bbd4 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 14 Feb 2026 13:54:30 -0500 Subject: [PATCH 2/9] Update clang-format action. --- .github/workflows/linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index f461a2a..83b1c37 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -62,7 +62,7 @@ jobs: - uses: actions/checkout@v4 - name: clang-format Check - uses: jidicula/clang-format-action@v4.9.0 + uses: jidicula/clang-format-action@v4.16.0 with: clang-format-version: '18' check-path: ${{ matrix.path }} -- 2.49.1 From 4f76d37ea08f2dae1c2699edf4c0f90f67558a7c Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 14 Feb 2026 14:08:35 -0500 Subject: [PATCH 3/9] Install NSIS on windows. --- .github/workflows/build.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b924571..6c38987 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,13 +43,16 @@ jobs: with: version: ${{ env.QT_VERSION }} - # Windows - - - name: Chocolatey Action + - name: Install pkgconfiglite if: matrix.os == 'windows-latest' uses: crazy-max/ghaction-chocolatey@v2 with: args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1 + - name: Install nsis + if: matrix.os == 'windows-latest' + uses: crazy-max/ghaction-chocolatey@v2 + with: + args: install nsis - name: Install Debian packaging dependencies if: matrix.os == 'ubuntu-latest' -- 2.49.1 From 6d9689720d7c4f4e036f40a4b8e7a8b1a4527075 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 14 Feb 2026 14:30:56 -0500 Subject: [PATCH 4/9] Remove deprecated GLSL. --- src/qtk/shaders.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/qtk/shaders.h b/src/qtk/shaders.h index 2dc6ff4..57c5a45 100644 --- a/src/qtk/shaders.h +++ b/src/qtk/shaders.h @@ -109,11 +109,12 @@ void main() #version 330 uniform samplerCube uTexture; -varying vec3 vTexCoord; +in vec3 vTexCoord; +out vec4 FragColor; void main() { - gl_FragColor = texture(uTexture, vTexCoord); + FragColor = texture(uTexture, vTexCoord); } )" -- 2.49.1 From 67fcf4619bfeb8dfd52c0115692a62ec02624cdd Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 14 Feb 2026 14:38:34 -0500 Subject: [PATCH 5/9] Update assimp to fix Mac CI. Zlib no longer uses fdopen. Zlib previously had redefined fdopen, causing conflicts in apple things. Zlib has since updated to remove these defines, fixing the issue. --- .github/workflows/build.yml | 2 +- CMakeLists.txt | 17 ++++++++++++++++- README.md | 2 +- extern/assimp/assimp | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c38987..0f188bf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG flags: '' - os: macos-latest - cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG + cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ -Dfdopen=fdopen $CONFIG flags: -j $(nproc) runs-on: ${{ matrix.os }} diff --git a/CMakeLists.txt b/CMakeLists.txt index da5cb99..33b7950 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,8 +157,15 @@ list(APPEND VAR_NAMES QT6_INSTALL_PLUGINS) # Find Assimp. if(QTK_SUBMODULES) + if(APPLE) + # Avoid zlib redefining fdopen, causing build failures in apple clang. + # https://github.com/assimp/assimp/issues/6118 + add_compile_definitions(-Dfdopen=fdopen) + endif() + if(NOT WIN32) + add_compile_options(-fPIC) + endif() # Required to statically link. - add_compile_options(-fPIC) set(BUILD_SHARED_LIBS OFF CACHE STRING "Build static assimp libs" FORCE) set(ASSIMP_BUILD_ZLIB ON CACHE STRING "Build Zlib with assimp." FORCE) set( @@ -175,6 +182,14 @@ if(QTK_SUBMODULES) "${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/" EXCLUDE_FROM_ALL ) + install( + TARGETS assimp zlibstatic + EXPORT qtk_export + COMPONENT qtk + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + ) else() find_package(assimp REQUIRED) endif() diff --git a/README.md b/README.md index 41d700e..c884041 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ The Ubuntu apt repositories contain all the packages we need to build all target To build Qtk desktop application with the scene in the screenshots below run the following commands. ```bash -sudo apt update && sudo apt install cmake build-essential git ccache libxkbcommon-dev libassimp-dev qt6-base-dev qt6-tools-dev +sudo apt update && sudo apt install cmake build-essential git ccache libxkbcommon-dev libassimp-dev qt6-base-dev qt6-tools-dev zlib1g-dev cmake -DQTK_GUI_SCENE=ON -B build cmake --build build ./build/bin/qtk_gui diff --git a/extern/assimp/assimp b/extern/assimp/assimp index 5d5496f..e0b5234 160000 --- a/extern/assimp/assimp +++ b/extern/assimp/assimp @@ -1 +1 @@ -Subproject commit 5d5496f1ad895297cede723b3c96b513263f82ed +Subproject commit e0b52347c6e52de2827ec957a9ebf00ce3c54f79 -- 2.49.1 From a2f849ec72d261d4123e18aa195988d0f26e7956 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 14 Feb 2026 16:33:18 -0500 Subject: [PATCH 6/9] Install NSIS for windows pt2. --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f188bf..538d77c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -194,6 +194,11 @@ jobs: uses: crazy-max/ghaction-chocolatey@v2 with: args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1 + - name: Install nsis + if: matrix.os == 'windows-latest' + uses: crazy-max/ghaction-chocolatey@v2 + with: + args: install nsis - name: Configure Qtk Library shell: bash -- 2.49.1 From 9397e3117b806c87ddcceb532aa6d3c3cefc1642 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 14 Feb 2026 17:29:42 -0500 Subject: [PATCH 7/9] Fix linting CI. --- .github/workflows/linting.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 83b1c37..89a6fbb 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -39,7 +39,7 @@ jobs: # Check the entire repo for source files to tidy files-changed-only: false # Ignore qtk build and external assimp directories - ignore: '.github|build|extern/assimp/assimp' + ignore: '.github|build|extern' # Point to compile_commands.json produced by build database: 'build' # Use thread comments as feedback @@ -61,8 +61,12 @@ jobs: steps: - uses: actions/checkout@v4 - - name: clang-format Check - uses: jidicula/clang-format-action@v4.16.0 + - uses: cpp-linter/cpp-linter-action@v2 with: - clang-format-version: '18' - check-path: ${{ matrix.path }} + version: '18' + style: 'file' + tidy-checks: '' + files-changed-only: false + ignore: '.github|build|extern' + extensions: 'cpp,h' + files: ${{ matrix.path }} -- 2.49.1 From 9a87c1df1094623208e680739f6896aaf79a315d Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 14 Feb 2026 17:31:17 -0500 Subject: [PATCH 8/9] Reapply "ToolBar buttons to add and remove objects. (#18)" This reverts commit 0c16b7d879d76b623786d7ba40b921ae971e12b9. --- src/app/qtkmainwindow.cpp | 30 ++++++++++++++++++++ src/app/qtkmainwindow.h | 19 ++++++++++++- src/app/qtkmainwindow.ui | 4 +-- src/designer-plugins/toolbox.cpp | 48 +++++++++++++++++++++++++++++--- src/designer-plugins/toolbox.h | 37 +++++++++++++++++++++--- src/qtk/scene.cpp | 28 +++++++++++++++++++ src/qtk/scene.h | 24 ++++++++++++---- 7 files changed, 173 insertions(+), 17 deletions(-) diff --git a/src/app/qtkmainwindow.cpp b/src/app/qtkmainwindow.cpp index f05b54a..945ff91 100644 --- a/src/app/qtkmainwindow.cpp +++ b/src/app/qtkmainwindow.cpp @@ -48,6 +48,16 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) &Qtk::ToolBox::updateFocus); } + connect(ui_->actionDelete_Object, + &QAction::triggered, + this, + &MainWindow::deleteObject); + + connect(ui_->actionLoad_Model, + &QAction::triggered, + this, + &MainWindow::loadObject); + // 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 @@ -104,6 +114,26 @@ void MainWindow::refreshScene(const QString & sceneName) ui_->qtk__TreeView->updateView(getQtkWidget()->getScene()); } +void MainWindow::deleteObject() +{ + if (auto object = ui_->qtk__ToolBox->getObjectFocus(); object != Q_NULLPTR) { + auto scene = getQtkWidget()->getScene(); + switch (object->getType()) { + case Qtk::Object::Type::QTK_MESH: + scene->removeObject(dynamic_cast(object)); + ui_->qtk__ToolBox->clearFocus(); + break; + case Qtk::Object::Type::QTK_MODEL: + scene->removeObject(dynamic_cast(object)); + ui_->qtk__ToolBox->clearFocus(); + break; + default: + qDebug() << "Failed to delete model with invalid type"; + break; + } + } +} + void MainWindow::setScene(Qtk::Scene * scene) { connect(scene, diff --git a/src/app/qtkmainwindow.h b/src/app/qtkmainwindow.h index 40c5df9..4d41cc1 100644 --- a/src/app/qtkmainwindow.h +++ b/src/app/qtkmainwindow.h @@ -11,8 +11,8 @@ #include +#include #include -#include #include "designer-plugins/debugconsole.h" @@ -144,6 +144,23 @@ class MainWindow : public QMainWindow */ void refreshScene(const QString & sceneName); + + /** + * Opens a QFileDialog for selecting an object file to load into the scene. + */ + void loadObject() + { + const QUrl file = QFileDialog::getOpenFileName( + this, tr("Load Model"), QDir::homePath(), tr("Object Files (*.obj)")); + getQtkWidget()->getScene()->loadModel(file.fileName().replace(".obj", ""), + file.toString()); + } + + /** + * Deletes the currently selected object from the scene. + */ + void deleteObject(); + private: /*************************************************************************** * Private Members diff --git a/src/app/qtkmainwindow.ui b/src/app/qtkmainwindow.ui index 3078ef7..218d397 100644 --- a/src/app/qtkmainwindow.ui +++ b/src/app/qtkmainwindow.ui @@ -223,7 +223,7 @@ - false + true @@ -238,7 +238,7 @@ - false + true diff --git a/src/designer-plugins/toolbox.cpp b/src/designer-plugins/toolbox.cpp index 4d57129..268e695 100644 --- a/src/designer-plugins/toolbox.cpp +++ b/src/designer-plugins/toolbox.cpp @@ -20,7 +20,7 @@ ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), objectDetails_(this), transformPanel_(this), scalePanel_(this), vertex_(this, "Vertex Shader:"), fragment_(this, "Fragment Shader:"), properiesForm_(new QFormLayout), - shaderForm_(new QFormLayout), ui(new Ui::ToolBox) + shaderForm_(new QFormLayout), ui(new Ui::ToolBox), objectFocus_(Q_NULLPTR) { ui->setupUi(this); setMinimumWidth(350); @@ -44,10 +44,24 @@ void ToolBox::updateFocus(const QString & name) { auto object = QtkWidget::mWidgetManager.get_widget()->getScene()->getObject(name); - if (object != Q_NULLPTR) { - refreshProperties(object); - refreshShaders(object); + // If we can't find the object show a warning. + if (object == Q_NULLPTR) { + qDebug() << "Failed to find selected object: " << name + << "; Clearing object panels."; } + + // We should still pass the nullptr here if we failed to find the object + // above. + objectFocus_ = object; + refreshProperties(object); + refreshShaders(object); +} + +void ToolBox::clearFocus() +{ + objectFocus_ = Q_NULLPTR; + refreshProperties(objectFocus_); + refreshShaders(objectFocus_); } ToolBox::SpinBox3D::SpinBox3D(QWidget * parent, const char * l) : @@ -70,6 +84,13 @@ void ToolBox::SpinBox::disconnect() const void ToolBox::TransformPanel::setObject(const Qtk::Object * object) { + // Zero the panel contents if there is no object selected. + if (object == Q_NULLPTR) { + spinBox3D.clear(); + return; + } + + // Reconnect translation panel controls to the new object. const std::vector binds = {&Object::setTranslationX, &Object::setTranslationY, @@ -90,6 +111,12 @@ void ToolBox::TransformPanel::setObject(const Qtk::Object * object) void ToolBox::ScalePanel::setObject(const Qtk::Object * object) { + // Zero the panel contents if there is no object selected. + if (object == Q_NULLPTR) { + spinBox3D.clear(); + return; + } + // Reconnect scale panel controls to the new object. const std::vector binds = { &Object::setScaleX, &Object::setScaleY, &Object::setScaleZ}; @@ -123,8 +150,21 @@ void ToolBox::refreshProperties(const Object * object) void ToolBox::refreshShaders(const Object * object) { + // Zero the panel contents if there is no object selected. + if (object == Q_NULLPTR) { + vertex_.clear(); + fragment_.clear(); + return; + } + vertex_.path.setValue(object->getVertexShader().c_str()); vertex_.editor->setText(object->getVertexShaderSourceCode().c_str()); fragment_.path.setValue(object->getFragmentShader().c_str()); fragment_.editor->setText(object->getFragmentShaderSourceCode().c_str()); } + +void ToolBox::refresh(const Object * object) +{ + refreshProperties(object); + refreshShaders(object); +} diff --git a/src/designer-plugins/toolbox.h b/src/designer-plugins/toolbox.h index 6eb9769..9f2f621 100644 --- a/src/designer-plugins/toolbox.h +++ b/src/designer-plugins/toolbox.h @@ -18,8 +18,8 @@ #include #include +#include "qtk/object.h" -#include "qtk/scene.h" namespace Ui { @@ -45,8 +45,13 @@ namespace Qtk void refreshShaders(const Object * object); + void refresh(const Object * object); + void updateFocus(const QString & name); + [[nodiscard]] Object * getObjectFocus() const { return objectFocus_; } + + void clearFocus(); private: /************************************************************************* @@ -65,9 +70,9 @@ namespace Qtk { } - void setValue(const char * v) { value->setText(tr(v)); } + void setValue(const char * v) const { value->setText(tr(v)); } - void setItem(const char * l, const char * v) + void setItem(const char * l, const char * v) const { label->setText(tr(l)); value->setText(tr(v)); @@ -84,8 +89,14 @@ namespace Qtk } /// Refresh to display the new object's details - void setObject(const Qtk::Object * object) + void setObject(const Qtk::Object * object) const { + // Zero contents if there is no object selected. + if (object == Q_NULLPTR) { + name.setValue(""); + objectType.setValue("No object selected"); + return; + } name.setItem("Name:", object->getName().toStdString().c_str()); objectType.setItem( "Type:", @@ -119,6 +130,15 @@ namespace Qtk /// Assigning a QWidget to a QLayout also ensures Qt will clean up. explicit SpinBox3D(QWidget * parent, const char * l = "SpinBox3D:"); + /// Zero the SpinBox3D display. + void clear() + { + for (auto & field : fields) { + field->disconnect(); + field->spinBox->setValue(0.0); + } + } + /// The main layout for the SpinBox3D widget. QHBoxLayout * layout; @@ -176,6 +196,13 @@ namespace Qtk layout->addWidget(editor); } + /// Zero the ShaderView display. + void clear() const + { + path.setValue(""); + editor->setText(""); + } + /// The main layout for the ShaderView widget. QVBoxLayout * layout; @@ -190,6 +217,8 @@ namespace Qtk QFormLayout * properiesForm_; QFormLayout * shaderForm_; + Object * objectFocus_ {}; + Ui::ToolBox * ui; }; } // namespace Qtk diff --git a/src/qtk/scene.cpp b/src/qtk/scene.cpp index 54fb66f..45282b1 100644 --- a/src/qtk/scene.cpp +++ b/src/qtk/scene.cpp @@ -55,6 +55,34 @@ template <> Model * Scene::addObject(Model * object) return object; } +template <> void Scene::removeObject(MeshRenderer * object) +{ + auto it = std::find(mMeshes.begin(), mMeshes.end(), object); + if (it == mMeshes.end()) { + qDebug() << "[Scene::removeObject]: Failed to remove object: " + << object->getName() << " (" << object << ")"; + return; + } + + --mObjectCount[object->getName()]; + mMeshes.erase(it); + emit sceneUpdated(mSceneName); +} + +template <> void Scene::removeObject(Model * object) +{ + auto it = std::find(mModels.begin(), mModels.end(), object); + if (it == mModels.end()) { + qDebug() << "[Scene::removeObject]: Failed to remove object: " + << object->getName() << " (" << object << ")"; + return; + } + + --mObjectCount[object->getName()]; + mModels.erase(it); + emit sceneUpdated(mSceneName); +} + void Scene::draw() { if (!mInit) { diff --git a/src/qtk/scene.h b/src/qtk/scene.h index a4f9180..67f14f7 100644 --- a/src/qtk/scene.h +++ b/src/qtk/scene.h @@ -85,16 +85,16 @@ namespace Qtk void loadModel(const QUrl & url) { - auto fileName = url.fileName().replace(".obj", "").toStdString(); - auto filePath = url.toLocalFile().toStdString(); + auto fileName = url.fileName().replace(".obj", ""); + auto filePath = url.toLocalFile(); loadModel(fileName, filePath); } - void loadModel(const std::string & name, const std::string & path) + void loadModel(const QString & name, const QString & path) { // Add the dropped model to the load queue. // This is consumed during rendering of the scene if not empty. - mModelLoadQueue.emplace(name, path); + mModelLoadQueue.emplace(name.toStdString(), path.toStdString()); } /************************************************************************* @@ -182,8 +182,9 @@ namespace Qtk /** * Adds objects to the scene. - * This template provides explicit specializations for valid types. - * Adding any object other than these types will cause errors. + * This template provides explicit specializations for the valid types: + * MeshRenderer, Model + * Any other object type will cause errors. * TODO: Refactor to use Object base class container for scene objects. * * If creating a new object type for a scene, it must inherit Qtk::Object @@ -194,6 +195,17 @@ namespace Qtk */ template T * addObject(T * object); + /** + * Removes an object from the scene. + * This template provides explicit specializations for the valid types: + * MeshRenderer, Model + * Any other object type will cause errors. + * TODO: Refactor to use Object base class container for scene objects. + * + * @param object Pointer to the object to remove from the scene. + */ + template void removeObject(T * object); + /** * @param name The name to use for this scene. */ -- 2.49.1 From fe12015d0d9c81753d4fd9421675f728ddbafbf9 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 14 Feb 2026 20:40:25 -0500 Subject: [PATCH 9/9] Remove stale changes. --- .github/workflows/build.yml | 2 +- CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 538d77c..5121bd5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG flags: '' - os: macos-latest - cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ -Dfdopen=fdopen $CONFIG + cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG flags: -j $(nproc) runs-on: ${{ matrix.os }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 33b7950..378f6b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,9 +163,9 @@ if(QTK_SUBMODULES) add_compile_definitions(-Dfdopen=fdopen) endif() if(NOT WIN32) + # Required to statically link. add_compile_options(-fPIC) endif() - # Required to statically link. set(BUILD_SHARED_LIBS OFF CACHE STRING "Build static assimp libs" FORCE) set(ASSIMP_BUILD_ZLIB ON CACHE STRING "Build Zlib with assimp." FORCE) set( -- 2.49.1