README screenshots.

This commit is contained in:
Shaun Reed 2025-03-09 11:43:38 -04:00
parent 78639cf1c2
commit ea25ba312a
30 changed files with 79 additions and 66 deletions

View File

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

View File

@ -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 |
|-------------------------------------------------------|-------------------------------------------------------|--------------------------------------------------------|
| <img src="resources/screenshots/screen-ambient.png"/> | <img src="resources/screenshots/screen-diffuse.png"/> | <img src="resources/screenshots/screen-specular.png"/> |
And more advanced techniques like Phong lighting (ambient + diffuse + specular) and normal mapping.
![](resources/screenshots/screen-phong.png)
| Normal Mapping Disabled | Normal Mapping Enabled |
|------------------------------------------------------|--------------------------------------------------------|
| <img src="resources/screenshots/spartan-phong.png"/> | <img src="resources/screenshots/spartan-normals.png"/> |
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.

View File

@ -46,8 +46,8 @@
<file alias="solid-phong.vert">shaders/vertex/solid-phong.vert</file>
<file alias="model-basic.frag">shaders/fragment/model-basic.frag</file>
<file alias="model-basic.vert">shaders/vertex/model-basic.vert</file>
<file alias="model-specular.frag">shaders/fragment/model-specular.frag</file>
<file alias="model-specular.vert">shaders/vertex/model-specular.vert</file>
<file alias="model-phong.frag">shaders/fragment/model-phong.frag</file>
<file alias="model-phong.vert">shaders/vertex/model-phong.vert</file>
<file alias="model-normals.frag">shaders/fragment/model-normals.frag</file>
<file alias="model-normals.vert">shaders/vertex/model-normals.vert</file>
<file alias="skybox.frag">skybox/skybox.frag</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

View File

Before

Width:  |  Height:  |  Size: 263 KiB

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 646 KiB

View File

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

View File

@ -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"); }
};
/**

View File

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

View File

@ -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;
}
/*******************************************************************************

View File

@ -210,7 +210,7 @@ namespace Qtk
QOpenGLDebugLogger * mDebugLogger;
Qtk::Scene * mScene;
Qtk::DebugConsole * mConsole;
bool mConsoleActive = false;
bool mConsoleActive = true;
};
} // namespace Qtk

View File

@ -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<MeshRenderer *>(object)->getTransform();
} else if (object->getType() == Object::QTK_MODEL) {
objectTransform = &dynamic_cast<Model *>(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);

View File

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

View File

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

View File

@ -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.";

View File

@ -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) + ")");