11 Commits

Author SHA1 Message Date
96a004e029 Cleanup. 2025-03-23 18:01:15 -04:00
8eaebee2c6 Release QObject connections on refresh. 2025-03-23 17:29:56 -04:00
48598de9c8 Convert ShaderView to QWidget. 2025-03-23 15:43:10 -04:00
6218928e15 Refresh object details without reloading widget.
+ Clean up ToolBox widget tree.
2025-03-23 15:31:47 -04:00
034d0a8b4e Store ToolBox ObjectDetails fields as members. 2025-03-23 11:38:20 -04:00
dc94d0a130 Store ToolBox Transform fields as members. 2025-03-23 10:33:59 -04:00
3d4df2eb76 Cleanup. 2025-03-23 09:35:07 -04:00
6528e872e5 Add Model::setLightPosition.
+ Convert some std::strings to QStrings.
2025-03-23 09:29:06 -04:00
5984763d43 Fix setting light poition for models. 2025-03-23 09:08:15 -04:00
95fd1e9977 Disable unimplemented buttons in toolbar. 2025-03-23 09:07:58 -04:00
3cad213a8e Bump qtk version in example. 2025-03-23 09:07:41 -04:00
13 changed files with 36 additions and 220 deletions

View File

@@ -43,16 +43,13 @@ jobs:
with: with:
version: ${{ env.QT_VERSION }} version: ${{ env.QT_VERSION }}
- name: Install pkgconfiglite # Windows
- name: Chocolatey Action
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
uses: crazy-max/ghaction-chocolatey@v2 uses: crazy-max/ghaction-chocolatey@v2
with: with:
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1 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 - name: Install Debian packaging dependencies
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
@@ -194,11 +191,6 @@ jobs:
uses: crazy-max/ghaction-chocolatey@v2 uses: crazy-max/ghaction-chocolatey@v2
with: with:
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1 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 - name: Configure Qtk Library
shell: bash shell: bash

View File

@@ -39,7 +39,7 @@ jobs:
# Check the entire repo for source files to tidy # Check the entire repo for source files to tidy
files-changed-only: false files-changed-only: false
# Ignore qtk build and external assimp directories # Ignore qtk build and external assimp directories
ignore: '.github|build|extern' ignore: '.github|build|extern/assimp/assimp'
# Point to compile_commands.json produced by build # Point to compile_commands.json produced by build
database: 'build' database: 'build'
# Use thread comments as feedback # Use thread comments as feedback
@@ -61,12 +61,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: cpp-linter/cpp-linter-action@v2 - name: clang-format Check
uses: jidicula/clang-format-action@v4.9.0
with: with:
version: '18' clang-format-version: '18'
style: 'file' check-path: ${{ matrix.path }}
tidy-checks: ''
files-changed-only: false
ignore: '.github|build|extern'
extensions: 'cpp,h'
files: ${{ matrix.path }}

View File

@@ -157,15 +157,8 @@ list(APPEND VAR_NAMES QT6_INSTALL_PLUGINS)
# Find Assimp. # Find Assimp.
if(QTK_SUBMODULES) 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)
# Required to statically link. # Required to statically link.
add_compile_options(-fPIC) add_compile_options(-fPIC)
endif()
set(BUILD_SHARED_LIBS OFF CACHE STRING "Build static assimp libs" FORCE) 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(ASSIMP_BUILD_ZLIB ON CACHE STRING "Build Zlib with assimp." FORCE)
set( set(
@@ -182,14 +175,6 @@ if(QTK_SUBMODULES)
"${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/" "${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/"
EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
) )
install(
TARGETS assimp zlibstatic
EXPORT qtk_export
COMPONENT qtk
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
else() else()
find_package(assimp REQUIRED) find_package(assimp REQUIRED)
endif() endif()

View File

@@ -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. To build Qtk desktop application with the scene in the screenshots below run the following commands.
```bash ```bash
sudo apt update && sudo apt install cmake build-essential git ccache libxkbcommon-dev libassimp-dev qt6-base-dev qt6-tools-dev zlib1g-dev sudo apt update && sudo apt install cmake build-essential git ccache libxkbcommon-dev libassimp-dev qt6-base-dev qt6-tools-dev
cmake -DQTK_GUI_SCENE=ON -B build cmake -DQTK_GUI_SCENE=ON -B build
cmake --build build cmake --build build
./build/bin/qtk_gui ./build/bin/qtk_gui

View File

@@ -48,16 +48,6 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent)
&Qtk::ToolBox::updateFocus); &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. // 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 // 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 // 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()); 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<Qtk::MeshRenderer *>(object));
ui_->qtk__ToolBox->clearFocus();
break;
case Qtk::Object::Type::QTK_MODEL:
scene->removeObject(dynamic_cast<Qtk::Model *>(object));
ui_->qtk__ToolBox->clearFocus();
break;
default:
qDebug() << "Failed to delete model with invalid type";
break;
}
}
}
void MainWindow::setScene(Qtk::Scene * scene) void MainWindow::setScene(Qtk::Scene * scene)
{ {
connect(scene, connect(scene,

View File

@@ -11,8 +11,8 @@
#include <unordered_map> #include <unordered_map>
#include <QFileDialog>
#include <QMainWindow> #include <QMainWindow>
#include <QPlainTextEdit>
#include "designer-plugins/debugconsole.h" #include "designer-plugins/debugconsole.h"
@@ -144,23 +144,6 @@ class MainWindow : public QMainWindow
*/ */
void refreshScene(const QString & sceneName); 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:
/*************************************************************************** /***************************************************************************
* Private Members * Private Members

View File

@@ -223,7 +223,7 @@
</action> </action>
<action name="actionLoad_Model"> <action name="actionLoad_Model">
<property name="enabled"> <property name="enabled">
<bool>true</bool> <bool>false</bool>
</property> </property>
<property name="icon"> <property name="icon">
<iconset> <iconset>
@@ -238,7 +238,7 @@
</action> </action>
<action name="actionDelete_Object"> <action name="actionDelete_Object">
<property name="enabled"> <property name="enabled">
<bool>true</bool> <bool>false</bool>
</property> </property>
<property name="icon"> <property name="icon">
<iconset> <iconset>

View File

@@ -20,7 +20,7 @@ ToolBox::ToolBox(QWidget * parent) :
QDockWidget(parent), objectDetails_(this), transformPanel_(this), QDockWidget(parent), objectDetails_(this), transformPanel_(this),
scalePanel_(this), vertex_(this, "Vertex Shader:"), scalePanel_(this), vertex_(this, "Vertex Shader:"),
fragment_(this, "Fragment Shader:"), properiesForm_(new QFormLayout), 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); ui->setupUi(this);
setMinimumWidth(350); setMinimumWidth(350);
@@ -44,24 +44,10 @@ void ToolBox::updateFocus(const QString & name)
{ {
auto object = auto object =
QtkWidget::mWidgetManager.get_widget()->getScene()->getObject(name); QtkWidget::mWidgetManager.get_widget()->getScene()->getObject(name);
// If we can't find the object show a warning. if (object != Q_NULLPTR) {
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); refreshProperties(object);
refreshShaders(object); refreshShaders(object);
} }
void ToolBox::clearFocus()
{
objectFocus_ = Q_NULLPTR;
refreshProperties(objectFocus_);
refreshShaders(objectFocus_);
} }
ToolBox::SpinBox3D::SpinBox3D(QWidget * parent, const char * l) : 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) 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. // Reconnect translation panel controls to the new object.
const std::vector binds = {&Object::setTranslationX, const std::vector binds = {&Object::setTranslationX,
&Object::setTranslationY, &Object::setTranslationY,
@@ -111,12 +90,6 @@ void ToolBox::TransformPanel::setObject(const Qtk::Object * object)
void ToolBox::ScalePanel::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. // Reconnect scale panel controls to the new object.
const std::vector binds = { const std::vector binds = {
&Object::setScaleX, &Object::setScaleY, &Object::setScaleZ}; &Object::setScaleX, &Object::setScaleY, &Object::setScaleZ};
@@ -150,21 +123,8 @@ void ToolBox::refreshProperties(const Object * object)
void ToolBox::refreshShaders(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_.path.setValue(object->getVertexShader().c_str());
vertex_.editor->setText(object->getVertexShaderSourceCode().c_str()); vertex_.editor->setText(object->getVertexShaderSourceCode().c_str());
fragment_.path.setValue(object->getFragmentShader().c_str()); fragment_.path.setValue(object->getFragmentShader().c_str());
fragment_.editor->setText(object->getFragmentShaderSourceCode().c_str()); fragment_.editor->setText(object->getFragmentShaderSourceCode().c_str());
} }
void ToolBox::refresh(const Object * object)
{
refreshProperties(object);
refreshShaders(object);
}

View File

@@ -18,8 +18,8 @@
#include <QLabel> #include <QLabel>
#include <QTextEdit> #include <QTextEdit>
#include "qtk/object.h"
#include "qtk/scene.h"
namespace Ui namespace Ui
{ {
@@ -45,13 +45,8 @@ namespace Qtk
void refreshShaders(const Object * object); void refreshShaders(const Object * object);
void refresh(const Object * object);
void updateFocus(const QString & name); void updateFocus(const QString & name);
[[nodiscard]] Object * getObjectFocus() const { return objectFocus_; }
void clearFocus();
private: private:
/************************************************************************* /*************************************************************************
@@ -70,12 +65,12 @@ namespace Qtk
{ {
} }
void setValue(const char * v) const { value->setText(tr(v)); } void setValue(const QString & v) { value->setText(v); }
void setItem(const char * l, const char * v) const void setItem(const QString & l, const QString & v)
{ {
label->setText(tr(l)); label->setText(l);
value->setText(tr(v)); value->setText(v);
} }
QLabel * label; QLabel * label;
@@ -84,22 +79,16 @@ namespace Qtk
/// We pass the parent widget so that Qt handles releasing memory. /// We pass the parent widget so that Qt handles releasing memory.
explicit ObjectDetails(QWidget * parent) : explicit ObjectDetails(QWidget * parent) :
name(parent, "Name:"), objectType(parent, "Object Type:") name(parent, "Name:"), objectType(parent, "ObjectType:")
{ {
} }
/// Refresh to display the new object's details /// 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. name.setItem(tr("Name:"), object->getName());
if (object == Q_NULLPTR) {
name.setValue("");
objectType.setValue("No object selected");
return;
}
name.setItem("Name:", object->getName().toStdString().c_str());
objectType.setItem( objectType.setItem(
"Type:", tr("Type:"),
object->getType() == Object::QTK_MESH ? "Mesh" : "Model"); object->getType() == Object::QTK_MESH ? "Mesh" : "Model");
} }
@@ -130,15 +119,6 @@ namespace Qtk
/// Assigning a QWidget to a QLayout also ensures Qt will clean up. /// Assigning a QWidget to a QLayout also ensures Qt will clean up.
explicit SpinBox3D(QWidget * parent, const char * l = "SpinBox3D:"); 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. /// The main layout for the SpinBox3D widget.
QHBoxLayout * layout; QHBoxLayout * layout;
@@ -196,13 +176,6 @@ namespace Qtk
layout->addWidget(editor); layout->addWidget(editor);
} }
/// Zero the ShaderView display.
void clear() const
{
path.setValue("");
editor->setText("");
}
/// The main layout for the ShaderView widget. /// The main layout for the ShaderView widget.
QVBoxLayout * layout; QVBoxLayout * layout;
@@ -217,8 +190,6 @@ namespace Qtk
QFormLayout * properiesForm_; QFormLayout * properiesForm_;
QFormLayout * shaderForm_; QFormLayout * shaderForm_;
Object * objectFocus_ {};
Ui::ToolBox * ui; Ui::ToolBox * ui;
}; };
} // namespace Qtk } // namespace Qtk

View File

@@ -55,34 +55,6 @@ template <> Model * Scene::addObject(Model * object)
return 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() void Scene::draw()
{ {
if (!mInit) { if (!mInit) {

View File

@@ -85,16 +85,16 @@ namespace Qtk
void loadModel(const QUrl & url) void loadModel(const QUrl & url)
{ {
auto fileName = url.fileName().replace(".obj", ""); auto fileName = url.fileName().replace(".obj", "").toStdString();
auto filePath = url.toLocalFile(); auto filePath = url.toLocalFile().toStdString();
loadModel(fileName, filePath); 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. // 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.emplace(name.toStdString(), path.toStdString()); mModelLoadQueue.emplace(name, path);
} }
/************************************************************************* /*************************************************************************
@@ -182,9 +182,8 @@ namespace Qtk
/** /**
* Adds objects to the scene. * Adds objects to the scene.
* This template provides explicit specializations for the valid types: * This template provides explicit specializations for valid types.
* MeshRenderer, Model * Adding any object other than these types will cause errors.
* Any other object type will cause errors.
* TODO: Refactor to use Object base class container for scene objects. * 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 * If creating a new object type for a scene, it must inherit Qtk::Object
@@ -195,17 +194,6 @@ namespace Qtk
*/ */
template <typename T> T * addObject(T * object); template <typename T> 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 <typename T> void removeObject(T * object);
/** /**
* @param name The name to use for this scene. * @param name The name to use for this scene.
*/ */

View File

@@ -109,12 +109,11 @@ void main()
#version 330 #version 330
uniform samplerCube uTexture; uniform samplerCube uTexture;
in vec3 vTexCoord; varying vec3 vTexCoord;
out vec4 FragColor;
void main() void main()
{ {
FragColor = texture(uTexture, vTexCoord); gl_FragColor = texture(uTexture, vTexCoord);
} }
)" )"