diff --git a/.github/workflows/all-builds.yml b/.github/workflows/all-builds.yml index 65e24a4..410caa4 100644 --- a/.github/workflows/all-builds.yml +++ b/.github/workflows/all-builds.yml @@ -220,7 +220,7 @@ jobs: if: matrix.os == 'windows-latest' uses: actions/upload-artifact@v4 with: - name: qtk-${{ matrix.os }} + name: libqtk-${{ matrix.os }} path: | build/packages/*.exe @@ -235,7 +235,7 @@ jobs: if: matrix.os == 'macos-latest' uses: actions/upload-artifact@v4 with: - name: qtk-${{ matrix.os }} + name: libqtk-${{ matrix.os }} path: | build/packages/*.tar.gz diff --git a/README.md b/README.md index b198392..c7c092b 100644 --- a/README.md +++ b/README.md @@ -9,38 +9,53 @@ that allow rendering geometry in 2D and 3D using custom GLSL shader programs. The Qtk desktop application provides a model loader using [Assimp](https://assimp.org/) within a Qt widget application. You can fly around the scene using WASD while holding down the right mouse button. +Object names can be double-clicked in the tree view panel for quick camera navigation. +Properties of the object, like shader code and translation / scale, can be viewed and modified in the side panel. + +![](resources/screenshots/screen.png) + +All side panels and toolbars are dockable widgets that can be popped out +and reorganized as needed. Panels can also be stacked to create a docked widget with +tabs. The central widget that provides the camera view into the scene cannot be +detached from the main window in this way. + +![](resources/screenshots/screen-1.png) + +The small triangles floating near 3D models represent the light source being used for the shader. +These appear on models using phong, specular, and diffuse lighting techniques. + +The default scene contains basic examples like texture mapping to make a crate from basic cube geometry + +![](resources/screenshots/screen-texture.png) + +Examples of Ambient, Diffuse, and Specular GLSL shaders. + +| Ambient | Diffuse | Specular | +|-------------------------------------------------------|-------------------------------------------------------|--------------------------------------------------------| +| | | | + +And more advanced techniques like Phong lighting (ambient + diffuse + specular) and normal mapping. + +![](resources/screenshots/screen-phong.png) + +| Normal Mapping Disabled | Normal Mapping Enabled | +|------------------------------------------------------|--------------------------------------------------------| +| | | + +See the `View` toolbar menu to enable debug console widgets for open scenes or reopen previously closed panels. + Key features that are planned: - [x] Runtime loading of `.obj` or similar 3D models. - [x] Drag-and-drop interaction for adding objects to the scene. +- [x] Shader / object properties panel to modify related settings. - [ ] Runtime reloading of modified GLSL shaders attached to objects within scenes. - [ ] Multiple views of a scene at one time. - [ ] Camera control modes such as panning, orbiting, or following objects. - [ ] Save / load for scene data. The current inheritance model is temporary. - [ ] Basic text editor for quickly modifying shaders attached to objects. -- [ ] Shader / object properties panel to modify related settings. - [ ] Reduce size of application resources and git references. -![](resources/screenshot.png) - -Spartan with no normals - - -![](resources/spartan-specular.png) - -Spartan with normals - - -![](resources/spartan-normals.png) - -Object names can be double-clicked in the tree view panel for quick camera -navigation. All side panels and toolbars are dockable widgets that can be popped out -and reorganized as needed. Panels can also be stacked to create a docked widget with -tabs. The central widget that provides the camera view into the scene cannot be -detached from the main window in this way. See the `View` menu to enable debug -console widgets for open scenes or reopen previously closed panels. - -The small triangles floating near 3D models represent the light source being used for the shader. -These appear on models using phong, specular, and diffuse lighting techniques. - For examples of using the Qtk API, see the `example-app` project in the root of this repository. diff --git a/resources/resources.qrc b/resources/resources.qrc index 4aaf9bc..807349d 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -46,8 +46,8 @@ shaders/vertex/solid-phong.vert shaders/fragment/model-basic.frag shaders/vertex/model-basic.vert - shaders/fragment/model-specular.frag - shaders/vertex/model-specular.vert + shaders/fragment/model-phong.frag + shaders/vertex/model-phong.vert shaders/fragment/model-normals.frag shaders/vertex/model-normals.vert skybox/skybox.frag diff --git a/resources/screenshot.png b/resources/screenshot.png deleted file mode 100644 index 2802538..0000000 Binary files a/resources/screenshot.png and /dev/null differ diff --git a/resources/qtk-reference.png b/resources/screenshots/qtk-reference.png similarity index 100% rename from resources/qtk-reference.png rename to resources/screenshots/qtk-reference.png diff --git a/resources/qtk-views-setup.png b/resources/screenshots/qtk-views-setup.png similarity index 100% rename from resources/qtk-views-setup.png rename to resources/screenshots/qtk-views-setup.png diff --git a/resources/qtk-views.png b/resources/screenshots/qtk-views.png similarity index 100% rename from resources/qtk-views.png rename to resources/screenshots/qtk-views.png diff --git a/resources/screenshots/screen-1.png b/resources/screenshots/screen-1.png new file mode 100644 index 0000000..f2f3423 Binary files /dev/null and b/resources/screenshots/screen-1.png differ diff --git a/resources/screenshots/screen-ambient.png b/resources/screenshots/screen-ambient.png new file mode 100644 index 0000000..630bc50 Binary files /dev/null and b/resources/screenshots/screen-ambient.png differ diff --git a/resources/screenshots/screen-diffuse.png b/resources/screenshots/screen-diffuse.png new file mode 100644 index 0000000..f591899 Binary files /dev/null and b/resources/screenshots/screen-diffuse.png differ diff --git a/resources/screenshots/screen-phong.png b/resources/screenshots/screen-phong.png new file mode 100644 index 0000000..a3370f7 Binary files /dev/null and b/resources/screenshots/screen-phong.png differ diff --git a/resources/screenshots/screen-specular.png b/resources/screenshots/screen-specular.png new file mode 100644 index 0000000..3fab967 Binary files /dev/null and b/resources/screenshots/screen-specular.png differ diff --git a/resources/screenshots/screen-texture.png b/resources/screenshots/screen-texture.png new file mode 100644 index 0000000..c4f9830 Binary files /dev/null and b/resources/screenshots/screen-texture.png differ diff --git a/resources/screenshots/screen.png b/resources/screenshots/screen.png new file mode 100644 index 0000000..e462c7e Binary files /dev/null and b/resources/screenshots/screen.png differ diff --git a/resources/screenshots/spartan-normals.png b/resources/screenshots/spartan-normals.png new file mode 100644 index 0000000..4b7450e Binary files /dev/null and b/resources/screenshots/spartan-normals.png differ diff --git a/resources/screenshots/spartan-phong.png b/resources/screenshots/spartan-phong.png new file mode 100644 index 0000000..b4e3eab Binary files /dev/null and b/resources/screenshots/spartan-phong.png differ diff --git a/resources/shaders/fragment/model-specular.frag b/resources/shaders/fragment/model-phong.frag similarity index 100% rename from resources/shaders/fragment/model-specular.frag rename to resources/shaders/fragment/model-phong.frag diff --git a/resources/shaders/vertex/model-specular.vert b/resources/shaders/vertex/model-phong.vert similarity index 100% rename from resources/shaders/vertex/model-specular.vert rename to resources/shaders/vertex/model-phong.vert diff --git a/resources/spartan-normals.png b/resources/spartan-normals.png deleted file mode 100644 index e27f68f..0000000 Binary files a/resources/spartan-normals.png and /dev/null differ diff --git a/resources/spartan-specular.png b/resources/spartan-specular.png deleted file mode 100644 index 1cc704f..0000000 Binary files a/resources/spartan-specular.png and /dev/null differ diff --git a/src/app/qtkmainwindow.cpp b/src/app/qtkmainwindow.cpp index 9d7d547..dee16ef 100644 --- a/src/app/qtkmainwindow.cpp +++ b/src/app/qtkmainwindow.cpp @@ -33,12 +33,14 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) // Add GUI 'view' toolbar option to show debug console. ui_->menuView->addAction(qtkWidget->getActionToggleConsole()); + // Refresh GUI widgets when scene or objects are updated. connect(qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this, &MainWindow::refreshScene); - connect(qtkWidget, &Qtk::QtkWidget::objectFocusChanged, + connect(qtkWidget, + &Qtk::QtkWidget::objectFocusChanged, ui_->qtk__ToolBox, &Qtk::ToolBox::updateFocus); } @@ -71,8 +73,8 @@ MainWindow::~MainWindow() MainWindow * MainWindow::getMainWindow() { - static MainWindow window; - return &window; + static auto * window = new MainWindow; + return window; } Qtk::QtkWidget * MainWindow::getQtkWidget(int64_t index) diff --git a/src/app/qtkmainwindow.h b/src/app/qtkmainwindow.h index 4bd2fe9..109150c 100644 --- a/src/app/qtkmainwindow.h +++ b/src/app/qtkmainwindow.h @@ -48,10 +48,9 @@ namespace Ui * Any application using a QtkWidget can set a custom scene in their main * function. See the MainWindow::MainWindow constructor as an example. */ -class EmptyScene : public Qtk::Scene { - void init() override { - setSceneName("Empty Scene"); - } +class EmptyScene : public Qtk::Scene +{ + void init() override { setSceneName("Empty Scene"); } }; /** diff --git a/src/app/qtkscene.cpp b/src/app/qtkscene.cpp index c93ab78..93eaa9f 100644 --- a/src/app/qtkscene.cpp +++ b/src/app/qtkscene.cpp @@ -260,8 +260,8 @@ void QtkScene::init() /* Test spartan Model with phong lighting, specular and normal mapping. */ model = addObject(new Qtk::Model("spartanTest", ":/models/models/spartan/spartan.obj", - ":/shaders/model-normals.vert", - ":/shaders/model-normals.frag")); + ":/shaders/model-phong.vert", + ":/shaders/model-phong.frag")); model->getTransform().setTranslation(0.0f, -1.0f, 10.0f); model->getTransform().scale(2.0f); model->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f)); diff --git a/src/app/qtkwidget.cpp b/src/app/qtkwidget.cpp index 4d0fd3c..b12cbf6 100644 --- a/src/app/qtkwidget.cpp +++ b/src/app/qtkwidget.cpp @@ -83,7 +83,11 @@ void QtkWidget::initializeGL() // Connect the frameSwapped signal to call the update() function connect(this, SIGNAL(frameSwapped()), this, SLOT(update())); - toggleConsole(); + // Add the debug console widget to the window and set its hidden state. + MainWindow::getMainWindow()->addDockWidget( + Qt::DockWidgetArea::BottomDockWidgetArea, mConsole); + mConsole->setHidden(!mConsoleActive); + // Initialize OpenGL debug context mDebugLogger = new QOpenGLDebugLogger(this); if (mDebugLogger->initialize()) { @@ -144,15 +148,8 @@ void QtkWidget::setScene(Scene * scene) void QtkWidget::toggleConsole() { - if (mConsoleActive) { - mConsole->setHidden(true); - mConsoleActive = false; - } else { - MainWindow::getMainWindow()->addDockWidget( - Qt::DockWidgetArea::BottomDockWidgetArea, mConsole); - mConsole->setHidden(false); - mConsoleActive = true; - } + mConsole->setHidden(mConsoleActive); + mConsoleActive = !mConsoleActive; } /******************************************************************************* diff --git a/src/app/qtkwidget.h b/src/app/qtkwidget.h index 2937296..f2c56e9 100644 --- a/src/app/qtkwidget.h +++ b/src/app/qtkwidget.h @@ -210,7 +210,7 @@ namespace Qtk QOpenGLDebugLogger * mDebugLogger; Qtk::Scene * mScene; Qtk::DebugConsole * mConsole; - bool mConsoleActive = false; + bool mConsoleActive = true; }; } // namespace Qtk diff --git a/src/app/treeview.cpp b/src/app/treeview.cpp index f173726..17a9a08 100644 --- a/src/app/treeview.cpp +++ b/src/app/treeview.cpp @@ -41,36 +41,33 @@ void Qtk::TreeView::updateView(const Qtk::Scene * scene) mSceneName = scene->getSceneName(); auto objects = scene->getObjects(); for (const auto & object : objects) { - auto item = - new QTreeWidgetItem(QStringList(QString(object->getName().c_str()))); - ui->treeWidget->insertTopLevelItem(0, item); + QStringList list(QStringList(QString(object->getName().c_str()))); + ui->treeWidget->insertTopLevelItem(0, new QTreeWidgetItem(list)); } } void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) { - QString name = item->text(column); + const QString & name = item->text(column); auto scene = MainWindow::getMainWindow()->getQtkWidget()->getScene(); - auto & transform = Qtk::Scene::getCamera().getTransform(); auto object = scene->getObject(name); - Transform3D * objectTransform; // If the object is a mesh or model, focus the camera on it. if (object == Q_NULLPTR) { qDebug() << "Attempt to get non-existing object with name '" << name << "'\n"; - } else if (object->getType() == Object::QTK_MESH) { - objectTransform = &dynamic_cast(object)->getTransform(); - } else if (object->getType() == Object::QTK_MODEL) { - objectTransform = &dynamic_cast(object)->getTransform(); + return; } - auto focusScale = objectTransform->getScale(); + const Transform3D & objectTransform = object->getTransform(); + + auto & camera_transform = Qtk::Scene::getCamera().getTransform(); + auto focusScale = objectTransform.getScale(); float width = focusScale.x() / 2.0f; float height = focusScale.y() / 2.0f; - QVector3D pos = objectTransform->getTranslation(); + QVector3D pos = objectTransform.getTranslation(); // pos.setX(pos.x() + width); pos.setY(pos.y() + height); - transform.setTranslation(pos); - transform.translate(0.0f, 0.0f, 3.0f); + camera_transform.setTranslation(pos); + camera_transform.translate(0.0f, 0.0f, 3.0f); // Emit signal from qtk widget for new object focus. Triggers GUI updates. emit MainWindow::getMainWindow() -> getQtkWidget()->objectFocusChanged(name); diff --git a/src/qtk/meshrenderer.h b/src/qtk/meshrenderer.h index 404ee95..858f4c4 100644 --- a/src/qtk/meshrenderer.h +++ b/src/qtk/meshrenderer.h @@ -216,7 +216,7 @@ namespace Qtk /** * @return Transform3D attached to this MeshRenderer. */ - inline Transform3D & getTransform() { return mTransform; } + inline Transform3D & getTransform() override { return mTransform; } [[nodiscard]] inline std::string getVertexShader() const override { diff --git a/src/qtk/model.h b/src/qtk/model.h index b11a5ec..d87f1cf 100644 --- a/src/qtk/model.h +++ b/src/qtk/model.h @@ -129,7 +129,7 @@ namespace Qtk /** * @return Transform3D attached to this Model. */ - inline Transform3D & getTransform() { return mTransform; } + inline Transform3D & getTransform() override { return mTransform; } [[nodiscard]] inline std::string getVertexShader() const override { diff --git a/src/qtk/object.h b/src/qtk/object.h index 1550d65..2046bd7 100644 --- a/src/qtk/object.h +++ b/src/qtk/object.h @@ -111,6 +111,11 @@ namespace Qtk return mTransform; } + [[nodiscard]] inline virtual Transform3D & getTransform() + { + return mTransform; + } + [[nodiscard]] inline virtual std::string getVertexShader() const { return "Base Object has no vertex shader."; diff --git a/src/qtk/scene.cpp b/src/qtk/scene.cpp index 6b0eb31..e0b9afa 100644 --- a/src/qtk/scene.cpp +++ b/src/qtk/scene.cpp @@ -118,11 +118,9 @@ void Scene::setSkybox(Skybox * skybox) void Scene::initSceneObjectName(Object * object) { - if (!mObjectCount.count(object->getName())) { - mObjectCount[object->getName()] = 1; - } else { - mObjectCount[object->getName()]++; - } + mObjectCount[object->getName()] = mObjectCount.count(object->getName()) + 1; + + // If the object exists make it's name unique. auto count = mObjectCount[object->getName()]; if (count > 1) { object->setName(object->getName() + " (" + std::to_string(count) + ")");