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