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:
version: ${{ env.QT_VERSION }}
- name: Install pkgconfiglite
# Windows
- name: Chocolatey Action
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'
@@ -194,11 +191,6 @@ 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

View File

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

View File

@@ -157,15 +157,8 @@ 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)
# Required to statically link.
add_compile_options(-fPIC)
endif()
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(
@@ -182,14 +175,6 @@ 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()

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.
```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 --build build
./build/bin/qtk_gui

View File

@@ -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<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)
{
connect(scene,

View File

@@ -11,8 +11,8 @@
#include <unordered_map>
#include <QFileDialog>
#include <QMainWindow>
#include <QPlainTextEdit>
#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

View File

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

View File

@@ -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.";
}
// We should still pass the nullptr here if we failed to find the object
// above.
objectFocus_ = object;
if (object != Q_NULLPTR) {
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);
}

View File

@@ -18,8 +18,8 @@
#include <QLabel>
#include <QTextEdit>
#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,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));
value->setText(tr(v));
label->setText(l);
value->setText(v);
}
QLabel * label;
@@ -84,22 +79,16 @@ namespace Qtk
/// We pass the parent widget so that Qt handles releasing memory.
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
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());
name.setItem(tr("Name:"), object->getName());
objectType.setItem(
"Type:",
tr("Type:"),
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.
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

View File

@@ -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) {

View File

@@ -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 <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.
*/

View File

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