Compare commits

...

33 Commits

Author SHA1 Message Date
Shaun Reed 0ef62ebfa5 Add object details
+ Object and Shader detail panels
+ Connect model data to details panel
+ Ability to move and scale objects in GUI
+ View of shader code for selected object
2023-12-26 21:32:19 -05:00
Shaun Reed 2476d125f7 Fix drag and drop model loading 2023-04-29 10:02:40 -04:00
Shaun Reed 5118726cde Merge branch 'builds' into drag-and-drop 2023-04-14 14:50:10 -04:00
Shaun Reed b3f9e3230e clean up 2023-04-09 21:44:27 -04:00
Shaun Reed 5015c5c3a4 WIP drag and drop 2023-03-11 21:14:48 -05:00
Shaun Reed 2087f10681 README 2023-03-11 20:23:25 -05:00
Shaun Reed e6b197d6fa Fix CI 2023-03-11 19:59:53 -05:00
Shaun Reed 6a52eee501 Debian packaging CI 2023-03-11 18:46:11 -05:00
Shaun Reed 6d51aef9cf Cleanup 2023-03-11 18:26:58 -05:00
Shaun Reed 5bde82d956 Format 2023-03-11 12:31:09 -05:00
Shaun Reed ae5abb9939 Windows CI 2023-03-11 12:18:16 -05:00
Shaun Reed d9c59a04ec Update CI for new targets 2023-03-11 11:30:17 -05:00
Shaun Reed e6bcd131b7 Connect TreeWidget when scene is updated. 2023-03-11 11:21:26 -05:00
Shaun Reed 0659df94bd Add SceneInterface
+ Renames binaries qtk_main->qtk_app and example->example_app
2023-03-11 10:59:33 -05:00
Shaun Reed 0dcb6d337b CI 2023-02-11 10:01:52 -05:00
Shaun Reed 002bedd7ef Mac packaging 2023-02-05 10:21:38 -05:00
Shaun Reed 6ce71dda86 Update CI for new targets 2023-01-29 20:08:02 -05:00
Shaun Reed b3484ced03 CI 2023-01-29 13:46:52 -05:00
Shaun Reed 5e886672da Packaging / install updates 2023-01-29 13:26:25 -05:00
Shaun Reed 126cd438e1 Cmake install components 2023-01-28 22:27:01 -05:00
Shaun Reed 39fa8e8cdc Fix windows packaging 2023-01-28 15:12:45 -05:00
Shaun Reed 48719412f2 CMake updates 2023-01-16 19:34:50 -05:00
Shaun Reed aa32cbcc17 OSX packaging 2023-01-16 10:53:15 -05:00
Shaun Reed cfefc49c53 CMake target renaming to avoid C++ errors with `-` 2023-01-15 17:04:06 -05:00
Shaun Reed 195a4ef30d Assimp IOSystem for Qt Resource paths 2023-01-15 16:06:15 -05:00
Shaun Reed 55dd8e5c3c Test packaging
+ Update assimp to latest
2023-01-15 10:14:24 -05:00
Shaun Reed cf433ad7fc CMake packaging updates 2023-01-14 16:33:06 -05:00
Shaun Reed 4bc0ae22c6 Format 2023-01-02 22:19:38 -05:00
Shaun Reed faa9fe28f7 CMake packaging 2023-01-02 22:18:53 -05:00
Shaun Reed f83f68207d Add fontawesome icons
+ Clean up resource prefixes
2023-01-01 23:44:43 -05:00
Shaun Reed 85c9e2eac1 Clean and comment code 2023-01-01 22:51:46 -05:00
Shaun Reed 194888ed19 Refactor build system and UI
+ Install configs for Qt Designer plugins and Qtk application
+ Add Qtk plugin collection for Qt Designer
+ QtkWidget plugin
+ TreeView widget plugin
+ DebugConsole widget plugin
+ All widgets are fully integrated with Qt Designer
+ All widgets can be popped out or docked within the window
+ All widgets can be stacked to use tab view for side panels
+ All widgets can be toggled on/off through the view context menu
+ QtkWidget debug console
+ QtkWidget active scene TreeVew
+ QtkWidget dockable tool bar
+ Double-click an object name in the TreeView to focus camera
+ Separate libaray from widgets

There is still a lot to do here, but the major refactoring should be
done after this commit. Some of the new features were put together as
POC for working with the new UI. A few placeholder buttons were added
that have no functionality.
2023-01-01 22:49:04 -05:00
Shaun Reed c948d9e1a6 Fix loading of QtkWidget in mainwindow 2022-12-11 12:36:07 -05:00
20 changed files with 495 additions and 104 deletions

View File

@ -118,7 +118,7 @@ jobs:
- name: Upload Qtk install directory - name: Upload Qtk install directory
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: qtk-gui-${{ matrix.os }}-installdir name: qtk-gui-${{ matrix.os }}-install
path: install/* path: install/*
# TODO: Enable after trimming resources. # TODO: Enable after trimming resources.
@ -238,7 +238,7 @@ jobs:
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
name: libqtk-${{ matrix.os }}-installdir name: libqtk-${{ matrix.os }}-install
path: install/* path: install/*
Qtk-Plugins: Qtk-Plugins:

3
.gitignore vendored
View File

@ -1,6 +1,9 @@
# CLion # CLion
**/.idea/** **/.idea/**
# VS Code
**/.vscode/**
# CMake build files # CMake build files
**/cmake-build-debug/** **/cmake-build-debug/**
**/build/** **/build/**

View File

@ -67,7 +67,7 @@ option(
OFF OFF
) )
if(QTK_DEBUG OR CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") if(QTK_DEBUG OR CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$")
set(QTK_DEBUG ON) set(QTK_DEBUG ON)
set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
else() else()
@ -96,6 +96,21 @@ list(SORT VAR_NAMES)
# If Qtk is built within Qt Creator this is not required. # If Qtk is built within Qt Creator this is not required.
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}") list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
set(
QT_CREATOR_DIR
"${QT_INSTALL_DIR}/../../Tools/QtCreator"
CACHE PATH "Qt Creator path used to install Qtk plugins for Qt Designer."
)
# Print all QTK options and their values.
get_cmake_property(VAR_NAMES VARIABLES)
list(FILTER VAR_NAMES INCLUDE REGEX "^Q[tT][kK]_.*$")
list(SORT VAR_NAMES)
################################################################################
# External Dependencies
################################################################################
# Find Qt # Find Qt
find_package(Qt6 COMPONENTS Core UiPlugin OpenGLWidgets) find_package(Qt6 COMPONENTS Core UiPlugin OpenGLWidgets)
qt_standard_project_setup() qt_standard_project_setup()
@ -111,12 +126,6 @@ if(NOT Qt6_FOUND)
) )
endif() endif()
# TODO: This might be a bit strange and needs more testing.
if (QTK_PREFIX_QTCREATOR)
set(CMAKE_INSTALL_PREFIX "${QT_INSTALL_DIR}")
endif()
# #
# To use custom plugins, set QT_PLUGIN_PATH environment variable before running designer # To use custom plugins, set QT_PLUGIN_PATH environment variable before running designer
# Or, we can install plugins to the designer for use across all projects. # Or, we can install plugins to the designer for use across all projects.
@ -137,8 +146,9 @@ set(
set( set(
VAR_PATHS VAR_PATHS
CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX QTK_PLUGIN_INSTALL_DIR QT6_INSTALL_PREFIX CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX QTK_PLUGIN_INSTALL_DIR QT6_INSTALL_PREFIX
QT_INSTALL_DIR QT6_INSTALL_PLUGINS QT_INSTALL_DIR
) )
list(APPEND VAR_NAMES QT6_INSTALL_PLUGINS)
# Find Assimp. # Find Assimp.
if(QTK_SUBMODULES) if(QTK_SUBMODULES)

@ -1 +1 @@
Subproject commit eb328ce69dd7b06977aed125e967a41e835b8431 Subproject commit 5d5496f1ad895297cede723b3c96b513263f82ed

View File

@ -32,7 +32,7 @@ install(
FILE_SET HEADERS DESTINATION include FILE_SET HEADERS DESTINATION include
INCLUDES DESTINATION include INCLUDES DESTINATION include
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
) )
@ -57,7 +57,7 @@ if(QTK_GUI)
COMPONENT qtk_gui COMPONENT qtk_gui
BUNDLE DESTINATION . BUNDLE DESTINATION .
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
) )

View File

@ -29,17 +29,25 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
for(auto & qtkWidget : qtkWidgets) { for(auto & qtkWidget : qtkWidgets) {
qtkWidget->setScene(new Qtk::SceneEmpty); qtkWidget->setScene(new Qtk::SceneEmpty);
views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget); views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget);
// Add GUI 'view' toolbar option to show debug console.
ui_->menuView->addAction(qtkWidget->getActionToggleConsole()); ui_->menuView->addAction(qtkWidget->getActionToggleConsole());
// Refresh GUI widgets when scene or objects are updated.
connect( connect(
qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this, qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this,
&MainWindow::refreshScene); &MainWindow::refreshScene);
connect(
qtkWidget, &Qtk::QtkWidget::objectFocusChanged, ui_->qtk__ToolBox,
&Qtk::ToolBox::updateFocus);
} }
auto docks = findChildren<QDockWidget *>(); // Dock the toolbox widget to the main window.
for(auto & dock : docks) { addDockWidget(Qt::LeftDockWidgetArea, ui_->qtk__ToolBox);
addDockWidget(Qt::RightDockWidgetArea, dock); // Add an option to toggle active widgets in the GUI's toolbar 'view' menu.
ui_->menuView->addAction(dock->toggleViewAction()); ui_->menuView->addAction(ui_->qtk__ToolBox->toggleViewAction());
}
addDockWidget(Qt::RightDockWidgetArea, ui_->qtk__TreeView);
ui_->menuView->addAction(ui_->qtk__TreeView->toggleViewAction());
// Set the window icon used for Qtk. // Set the window icon used for Qtk.
setWindowIcon(Qtk::getIcon()); setWindowIcon(Qtk::getIcon());

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>824</width> <width>1034</width>
<height>601</height> <height>601</height>
</rect> </rect>
</property> </property>
@ -28,11 +28,27 @@
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="Qtk::ToolBox" name="qtk::ToolBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>A custom widget tool tip.</string>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>3</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
@ -67,28 +83,20 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <widget class="Qtk::TreeView" name="qtk::TreeView">
<item> <property name="sizePolicy">
<widget class="Qtk::TreeView" name="qtk::TreeView"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<property name="toolTip"> <horstretch>1</horstretch>
<string>A custom widget tool tip.</string> <verstretch>0</verstretch>
</property> </sizepolicy>
<property name="whatsThis"> </property>
<string>Custom widget what's this?</string> <property name="toolTip">
</property> <string>A custom widget tool tip.</string>
</widget> </property>
</item> <property name="whatsThis">
<item> <string>Custom widget what's this?</string>
<widget class="Qtk::ToolBox" name="qtk::ToolBox"> </property>
<property name="toolTip"> </widget>
<string>A custom widget tool tip.</string>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -97,7 +105,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>824</width> <width>1034</width>
<height>22</height> <height>22</height>
</rect> </rect>
</property> </property>
@ -179,7 +187,7 @@
</widget> </widget>
<action name="actionOpen"> <action name="actionOpen">
<property name="icon"> <property name="icon">
<iconset resource="../../resources/resources.qrc"> <iconset>
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</iconset> <normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
@ -188,7 +196,7 @@
</action> </action>
<action name="actionSave"> <action name="actionSave">
<property name="icon"> <property name="icon">
<iconset resource="../../resources/resources.qrc"> <iconset>
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</iconset> <normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
@ -215,7 +223,7 @@
</action> </action>
<action name="actionLoad_Model"> <action name="actionLoad_Model">
<property name="icon"> <property name="icon">
<iconset resource="../../resources/resources.qrc"> <iconset>
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</iconset> <normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
@ -227,7 +235,7 @@
</action> </action>
<action name="actionDelete_Object"> <action name="actionDelete_Object">
<property name="icon"> <property name="icon">
<iconset resource="../../resources/resources.qrc"> <iconset>
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</iconset> <normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
@ -318,9 +326,7 @@
<container>1</container> <container>1</container>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources/>
<include location="../../resources/resources.qrc"/>
</resources>
<connections> <connections>
<connection> <connection>
<sender>actionExit</sender> <sender>actionExit</sender>

View File

@ -38,11 +38,12 @@ void QtkScene::init() {
/* Create a red cube with a mini master chief on top. */ /* Create a red cube with a mini master chief on top. */
auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS)); auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS));
myCube->setColor(RED); myCube->setColor(RED);
myCube->getTransform().setTranslation(5.0f, 0.0f, 0.0f);
addObject(myCube); addObject(myCube);
auto mySpartan = auto mySpartan =
new Model("My spartan", ":/models/models/spartan/spartan.obj"); new Model("My spartan", ":/models/models/spartan/spartan.obj");
mySpartan->getTransform().setTranslation(0.0f, 0.5f, 0.0f); mySpartan->getTransform().setTranslation(5.0f, 0.5f, 0.0f);
mySpartan->getTransform().setScale(0.5f); mySpartan->getTransform().setScale(0.5f);
addObject(mySpartan); addObject(mySpartan);

View File

@ -7,6 +7,12 @@
##############################################################################*/ ##############################################################################*/
#include <QKeyEvent> #include <QKeyEvent>
#include <QMimeData>
#include <QVBoxLayout>
#include <qtk/input.h>
#include <qtk/scene.h>
#include <qtk/shape.h>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <qtk/input.h> #include <qtk/input.h>
@ -31,6 +37,7 @@ QtkWidget::QtkWidget(QWidget * parent, const QString & name) :
QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) : QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR), QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR),
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR) { mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR) {
setAcceptDrops(true);
setScene(scene); setScene(scene);
setObjectName(name); setObjectName(name);
QSurfaceFormat format; QSurfaceFormat format;
@ -77,6 +84,9 @@ void QtkWidget::initializeGL() {
connect( connect(
mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this, mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this,
SLOT(messageLogged(QOpenGLDebugMessage))); SLOT(messageLogged(QOpenGLDebugMessage)));
// connect(
// mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)),
// mConsole, SLOT(sendLog(QOpenGLDebugMessage)));
mDebugLogger->startLogging(); mDebugLogger->startLogging();
} }
@ -107,11 +117,11 @@ void QtkWidget::paintGL() {
} }
} }
void QtkWidget::setScene(Qtk::Scene * scene) { void QtkWidget::setScene(Scene * scene) {
if(mScene != Q_NULLPTR) { if(mScene != Q_NULLPTR) {
delete mScene; delete mScene;
connect( connect(
scene, &Qtk::Scene::sceneUpdated, MainWindow::getMainWindow(), scene, &Scene::sceneUpdated, MainWindow::getMainWindow(),
&MainWindow::refreshScene); &MainWindow::refreshScene);
} }
@ -129,8 +139,7 @@ void QtkWidget::toggleConsole() {
mConsoleActive = false; mConsoleActive = false;
} else { } else {
MainWindow::getMainWindow()->addDockWidget( MainWindow::getMainWindow()->addDockWidget(
Qt::DockWidgetArea::BottomDockWidgetArea, Qt::DockWidgetArea::BottomDockWidgetArea, mConsole);
dynamic_cast<QDockWidget *>(mConsole));
mConsole->setHidden(false); mConsole->setHidden(false);
mConsoleActive = true; mConsoleActive = true;
} }
@ -140,6 +149,34 @@ void QtkWidget::toggleConsole() {
* Protected Methods * Protected Methods
******************************************************************************/ ******************************************************************************/
void QtkWidget::dragEnterEvent(QDragEnterEvent * event) {
if(event->mimeData()->hasFormat("text/plain")) {
event->acceptProposedAction();
}
}
void QtkWidget::dropEvent(QDropEvent * event) {
mConsole->sendLog(event->mimeData()->text());
if(event->mimeData()->hasUrls()) {
auto urls = event->mimeData()->urls();
if(urls.size() > 1) {
qDebug() << "Cannot accept drop of multiple files.";
event->ignore();
return;
}
// TODO: Support other object types.
auto url = urls.front();
if(url.fileName().endsWith(".obj")) {
mScene->loadModel(url);
event->acceptProposedAction();
} else {
qDebug() << "Unsupported file type.";
event->ignore();
}
}
}
void QtkWidget::keyPressEvent(QKeyEvent * event) { void QtkWidget::keyPressEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) { if(event->isAutoRepeat()) {
// Do not repeat input while a key is held down // Do not repeat input while a key is held down
@ -253,7 +290,8 @@ void QtkWidget::teardownGL() { /* Nothing to teardown yet... */
void QtkWidget::updateCameraInput() { void QtkWidget::updateCameraInput() {
Input::update(); Input::update();
// Camera Transformation // Camera Transformation
if(Input::buttonPressed(Qt::RightButton)) { if(Input::buttonPressed(Qt::LeftButton)
|| Input::buttonPressed(Qt::RightButton)) {
static const float transSpeed = 0.1f; static const float transSpeed = 0.1f;
static const float rotSpeed = 0.5f; static const float rotSpeed = 0.5f;

View File

@ -131,11 +131,19 @@ namespace Qtk {
*/ */
void sendLog(const QString & message, DebugContext context = Status); void sendLog(const QString & message, DebugContext context = Status);
// TODO: Use this signal in treeview and toolbox to update object
// properties
void objectFocusChanged(const QString objectName);
protected: protected:
/************************************************************************* /*************************************************************************
* Protected Methods * Protected Methods
************************************************************************/ ************************************************************************/
void dragEnterEvent(QDragEnterEvent * event) override;
void dropEvent(QDropEvent * event) override;
/** /**
* @param event Key press event to update camera input manager. * @param event Key press event to update camera input manager.
*/ */
@ -166,6 +174,7 @@ namespace Qtk {
/** /**
* Called when the `messageLogged` signal is caught. * Called when the `messageLogged` signal is caught.
* See definition of initializeGL() * See definition of initializeGL()
* https://doc.qt.io/qt-6/qopengldebuglogger.html#signals
* *
* @param msg The message logged. * @param msg The message logged.
*/ */

6
src/app/resources.h.in Normal file
View File

@ -0,0 +1,6 @@
#ifndef QTK_RESOURCES_H_IN_H
#define QTK_RESOURCES_H_IN_H
// Not currently in use, but will be in the future.
#endif // QTK_RESOURCES_H_IN_H

View File

@ -8,13 +8,143 @@
*/ */
#include "toolbox.h" #include "toolbox.h"
#include "qtkmainwindow.h"
#include "ui_toolbox.h" #include "ui_toolbox.h"
Qtk::ToolBox::ToolBox(QWidget * parent) : #include <QFormLayout>
QDockWidget(parent), ui(new Ui::ToolBox) { #include <QLabel>
using namespace Qtk;
ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), ui(new Ui::ToolBox) {
ui->setupUi(this); ui->setupUi(this);
setMinimumWidth(350);
} }
Qtk::ToolBox::~ToolBox() { void ToolBox::updateFocus(const QString & name) {
qDebug() << "Called updateFocus on Toolbox.";
auto object =
MainWindow::getMainWindow()->getQtkWidget()->getScene()->getObject(name);
if(object != Q_NULLPTR) {
removePages();
createPageProperties(object);
createPageShader(object);
}
}
ToolBox::~ToolBox() {
delete ui; delete ui;
} }
void ToolBox::removePages() {
// Remove all existing pages.
for(size_t i = 0; i < ui->toolBox->count(); i++) {
delete ui->toolBox->widget(i);
ui->toolBox->removeItem(i);
}
}
void ToolBox::createPageProperties(const Object * object) {
auto transform = object->getTransform();
auto type = object->getType();
auto * widget = new QWidget;
ui->toolBox->addItem(widget, "Properties");
ui->toolBox->setCurrentWidget(widget);
auto * layout = new QFormLayout;
layout->addRow(
new QLabel(tr("Name:")), new QLabel(object->getName().c_str()));
layout->addRow(
new QLabel(tr("Type:")),
new QLabel(type == Object::Type::QTK_MESH ? "Mesh" : "Model"));
auto rowLayout = new QHBoxLayout;
rowLayout->addWidget(new QLabel(tr("Translation:")));
int minWidth = 75;
for(size_t i = 0; i < 3; i++) {
auto spinBox = new QDoubleSpinBox;
spinBox->setMinimum(std::numeric_limits<double>::lowest());
spinBox->setSingleStep(0.1);
spinBox->setValue(transform.getTranslation()[i]);
spinBox->setFixedWidth(minWidth);
rowLayout->addWidget(spinBox);
if(i == 0) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object,
&Object::setTranslationX);
} else if(i == 1) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object,
&Object::setTranslationY);
} else if(i == 2) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object,
&Object::setTranslationZ);
}
}
layout->addRow(rowLayout);
rowLayout = new QHBoxLayout;
rowLayout->addWidget(new QLabel(tr("Scale:")));
for(size_t i = 0; i < 3; i++) {
auto spinBox = new QDoubleSpinBox;
spinBox->setMinimum(std::numeric_limits<double>::lowest());
spinBox->setSingleStep(0.1);
spinBox->setValue(transform.getScale()[i]);
spinBox->setFixedWidth(minWidth);
rowLayout->addWidget(spinBox);
if(i == 0) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleX);
} else if(i == 1) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleY);
} else if(i == 2) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleZ);
}
}
layout->addRow(rowLayout);
widget->setLayout(layout);
}
void ToolBox::createPageShader(const Object * object) {
// Shaders page.
auto widget = new QWidget;
ui->toolBox->addItem(widget, "Shaders");
auto mainLayout = new QFormLayout;
auto rowLayout = new QHBoxLayout;
rowLayout->addWidget(new QLabel("Vertex Shader:"));
rowLayout->addWidget(new QLabel(object->getVertexShader().c_str()));
mainLayout->addRow(rowLayout);
auto shaderView = new QTextEdit;
shaderView->setReadOnly(true);
auto vertexFile = QFile(object->getVertexShader().c_str());
if(vertexFile.exists()) {
vertexFile.open(QIODeviceBase::ReadOnly);
shaderView->setText(vertexFile.readAll());
vertexFile.close();
mainLayout->addRow(shaderView);
}
rowLayout = new QHBoxLayout;
rowLayout->addWidget(new QLabel("Fragment Shader:"));
rowLayout->addWidget(new QLabel(object->getFragmentShader().c_str()));
mainLayout->addRow(rowLayout);
shaderView = new QTextEdit;
shaderView->setReadOnly(true);
auto fragmentfile = QFile(object->getFragmentShader().c_str());
if(fragmentfile.exists()) {
fragmentfile.open(QIODeviceBase::ReadOnly);
shaderView->setText(fragmentfile.readAll());
fragmentfile.close();
mainLayout->addRow(shaderView);
}
widget->setLayout(mainLayout);
}

View File

@ -12,6 +12,11 @@
#include <QDesignerExportWidget> #include <QDesignerExportWidget>
#include <QDockWidget> #include <QDockWidget>
#include <QDoubleSpinBox>
#include <QGroupBox>
#include "qtk/scene.h"
namespace Ui { namespace Ui {
class ToolBox; class ToolBox;
@ -30,6 +35,15 @@ namespace Qtk {
~ToolBox(); ~ToolBox();
void removePages();
void createPageProperties(const Object * object);
void createPageShader(const Object * object);
void updateFocus(const QString & name);
private: private:
/************************************************************************* /*************************************************************************
* Private Members * Private Members

View File

@ -10,17 +10,57 @@
<height>300</height> <height>300</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>86</width>
<height>167</height>
</size>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Object Details</string> <string>Object Details</string>
</property> </property>
<widget class="QWidget" name="dockWidgetContents"> <widget class="QWidget" name="dockWidgetContents">
<layout class="QHBoxLayout" name="horizontalLayout"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QToolBox" name="toolBox"> <widget class="QToolBox" name="toolBox">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="page"> <widget class="QWidget" name="page_properties">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>382</width>
<height>201</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="label">
<string>Properties</string>
</attribute>
</widget>
<widget class="QWidget" name="page_shaders">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
@ -33,19 +73,6 @@
<string>Shaders</string> <string>Shaders</string>
</attribute> </attribute>
</widget> </widget>
<widget class="QWidget" name="page_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>382</width>
<height>201</height>
</rect>
</property>
<attribute name="label">
<string>Properties</string>
</attribute>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@ -48,16 +48,25 @@ void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) {
auto scene = MainWindow::getMainWindow()->getQtkWidget()->getScene(); auto scene = MainWindow::getMainWindow()->getQtkWidget()->getScene();
auto & transform = scene->getCamera().getTransform(); auto & transform = scene->getCamera().getTransform();
auto object = scene->getObject(name); auto object = scene->getObject(name);
Transform3D * objectTransform;
// If the object is a mesh or model, focus the camera on it.
if(object == Q_NULLPTR) { if(object == Q_NULLPTR) {
qDebug() << "Attempt to get non-existing object with name '" << name qDebug() << "Attempt to get non-existing object with name '" << name
<< "'\n"; << "'\n";
} } else if(object->getType() == Object::QTK_MESH) {
Transform3D * objectTransform;
if(object->getType() == Object::QTK_MESH) {
objectTransform = &dynamic_cast<MeshRenderer *>(object)->getTransform(); objectTransform = &dynamic_cast<MeshRenderer *>(object)->getTransform();
} else if(object->getType() == Object::QTK_MODEL) { } else if(object->getType() == Object::QTK_MODEL) {
objectTransform = &dynamic_cast<Model *>(object)->getTransform(); objectTransform = &dynamic_cast<Model *>(object)->getTransform();
} }
transform.setTranslation(objectTransform->getTranslation()); auto focusScale = objectTransform->getScale();
float width = focusScale.x() / 2.0f;
float height = focusScale.y() / 2.0f;
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); 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

@ -211,6 +211,14 @@ namespace Qtk {
*/ */
inline Transform3D & getTransform() { return mTransform; } inline Transform3D & getTransform() { return mTransform; }
inline std::string getVertexShader() const override {
return mVertexShader;
}
inline std::string getFragmentShader() const override {
return mFragmentShader;
}
private: private:
/************************************************************************* /*************************************************************************
* Private Members * Private Members

View File

@ -18,6 +18,9 @@
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
// Qtk // Qtk
#include <QFileInfo>
#include "modelmesh.h" #include "modelmesh.h"
#include "qtkapi.h" #include "qtkapi.h"
@ -58,14 +61,6 @@ namespace Qtk {
loadModel(mModelPath); loadModel(mModelPath);
} }
inline Model(
std::string name, std::string path,
std::string vertexShader = ":/shaders/model-basic.vert",
std::string fragmentShader = ":/shaders/model-basic.frag") :
Model(
name.c_str(), path.c_str(), vertexShader.c_str(),
fragmentShader.c_str()) {}
inline ~Model() override { mManager.remove(getName().c_str()); } inline ~Model() override { mManager.remove(getName().c_str()); }
/************************************************************************* /*************************************************************************
@ -132,6 +127,14 @@ namespace Qtk {
*/ */
inline Transform3D & getTransform() { return mTransform; } inline Transform3D & getTransform() { return mTransform; }
inline std::string getVertexShader() const override {
return mVertexShader;
}
inline std::string getFragmentShader() const override {
return mFragmentShader;
}
private: private:
/************************************************************************* /*************************************************************************
* Private Methods * Private Methods

View File

@ -96,10 +96,24 @@ namespace Qtk {
[[nodiscard]] inline const Type & getType() const { return mType; } [[nodiscard]] inline const Type & getType() const { return mType; }
[[nodiscard]] inline virtual const Transform3D & getTransform() const {
return mTransform;
}
[[nodiscard]] inline virtual std::string getVertexShader() const {
return "Base Object has no vertex shader.";
}
virtual inline std::string getFragmentShader() const {
return "Base Object has no fragment shader.";
}
/************************************************************************* /*************************************************************************
* Setters * Setters
************************************************************************/ ************************************************************************/
virtual inline void setName(const std::string & name) { mName = name; }
virtual inline void setColors(const Colors & value) { virtual inline void setColors(const Colors & value) {
mShape.mColors = value; mShape.mColors = value;
} }
@ -135,6 +149,39 @@ namespace Qtk {
mShape.mVertices = value; mShape.mVertices = value;
} }
inline void setScaleX(double x) {
mTransform.setScale(
x, mTransform.getScale().y(), mTransform.getScale().z());
}
inline void setScaleY(double y) {
mTransform.setScale(
mTransform.getScale().x(), y, mTransform.getScale().z());
}
inline void setScaleZ(double z) {
mTransform.setScale(
mTransform.getScale().x(), mTransform.getScale().y(), z);
}
inline void setTranslationX(double x) {
mTransform.setTranslation(
x, mTransform.getTranslation().y(),
mTransform.getTranslation().z());
}
inline void setTranslationY(double y) {
mTransform.setTranslation(
mTransform.getTranslation().x(), y,
mTransform.getTranslation().z());
}
inline void setTranslationZ(double z) {
mTransform.setTranslation(
mTransform.getTranslation().x(), mTransform.getTranslation().y(),
z);
}
/************************************************************************* /*************************************************************************
* Public Methods * Public Methods
************************************************************************/ ************************************************************************/

View File

@ -37,16 +37,42 @@ Scene::~Scene() {
* Public Methods * Public Methods
******************************************************************************/ ******************************************************************************/
template <> MeshRenderer * Scene::addObject(MeshRenderer * object) {
initSceneObjectName(object);
mMeshes.push_back(object);
sceneUpdated(mSceneName);
return object;
}
template <> Model * Scene::addObject(Model * object) {
initSceneObjectName(object);
mModels.push_back(object);
sceneUpdated(mSceneName);
return object;
}
void Scene::draw() { void Scene::draw() {
if(!mInit) { if(!mInit) {
initializeOpenGLFunctions(); initializeOpenGLFunctions();
init(); init();
mInit = true; mInit = true;
} }
while(!mModelLoadQueue.empty()) {
auto modelSpec = mModelLoadQueue.front();
// Load the model and add it to the scene.
addObject(new Model(modelSpec.first.c_str(), modelSpec.second.c_str()));
mModelLoadQueue.pop();
}
if(mPause) {
return;
}
if(mSkybox != Q_NULLPTR) { if(mSkybox != Q_NULLPTR) {
mSkybox->draw(); mSkybox->draw();
} }
for(auto & model : mModels) { for(const auto & model : mModels) {
model->draw(); model->draw();
} }
for(const auto & mesh : mMeshes) { for(const auto & mesh : mMeshes) {
@ -57,8 +83,8 @@ void Scene::draw() {
std::vector<Object *> Scene::getObjects() const { std::vector<Object *> Scene::getObjects() const {
// All scene objects must inherit from Qtk::Object. // All scene objects must inherit from Qtk::Object.
std::vector<Object *> objects(mMeshes.begin(), mMeshes.end()); std::vector<Object *> objects(mMeshes.begin(), mMeshes.end());
for(auto model : mModels) { for(const auto & model : mModels) {
objects.push_back(dynamic_cast<Object *>(model)); objects.push_back(model);
if(objects.back() == nullptr) { if(objects.back() == nullptr) {
return {}; return {};
} }
@ -66,8 +92,8 @@ std::vector<Object *> Scene::getObjects() const {
return objects; return objects;
} }
Object * Scene::getObject(const QString & name) { Object * Scene::getObject(const QString & name) const {
for(auto object : getObjects()) { for(const auto & object : getObjects()) {
if(object->getName() == name.toStdString()) { if(object->getName() == name.toStdString()) {
return object; return object;
} }
@ -80,14 +106,14 @@ void Scene::setSkybox(Skybox * skybox) {
mSkybox = skybox; mSkybox = skybox;
} }
template <> MeshRenderer * Scene::addObject(MeshRenderer * object) { void Scene::initSceneObjectName(Object * object) {
mMeshes.push_back(object); if(!mObjectCount.count(object->getName())) {
sceneUpdated(mSceneName); mObjectCount[object->getName()] = 1;
return object; } else {
} mObjectCount[object->getName()]++;
}
template <> Model * Scene::addObject(Model * object) { auto count = mObjectCount[object->getName()];
mModels.push_back(object); if(count > 1) {
sceneUpdated(mSceneName); object->setName(object->getName() + " (" + std::to_string(count) + ")");
return object; }
} }

View File

@ -10,7 +10,9 @@
#define QTK_SCENE_H #define QTK_SCENE_H
#include <QMatrix4x4> #include <QMatrix4x4>
#include <QUrl>
#include <queue>
#include <utility> #include <utility>
#include "camera3d.h" #include "camera3d.h"
@ -75,6 +77,18 @@ namespace Qtk {
*/ */
virtual void update() {} virtual void update() {}
void loadModel(const QUrl & url) {
auto fileName = url.fileName().replace(".obj", "").toStdString();
auto filePath = url.toLocalFile().toStdString();
loadModel(fileName, filePath);
}
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.push({name, path});
}
/************************************************************************* /*************************************************************************
* Accessors * Accessors
************************************************************************/ ************************************************************************/
@ -91,7 +105,16 @@ namespace Qtk {
* @param name The objectName to look for within this scene. * @param name The objectName to look for within this scene.
* @return The found object or Q_NULLPTR if none found. * @return The found object or Q_NULLPTR if none found.
*/ */
[[nodiscard]] Object * getObject(const QString & name); [[nodiscard]] Object * getObject(const QString & name) const;
/**
* @return The number of objects within the scene with the given name.
*/
[[nodiscard]] uint64_t getObjectCount(const QString & name) {
return mObjectCount.count(name.toStdString())
? mObjectCount[name.toStdString()]
: 0;
}
/** /**
* @return Camera attached to this scene. * @return Camera attached to this scene.
@ -166,6 +189,8 @@ namespace Qtk {
*/ */
inline void setSceneName(QString name) { mSceneName = std::move(name); } inline void setSceneName(QString name) { mSceneName = std::move(name); }
inline void setPause(bool pause) { mPause = pause; }
signals: signals:
/** /**
* Signal thrown when the scene is modified by adding or removing objects. * Signal thrown when the scene is modified by adding or removing objects.
@ -175,7 +200,26 @@ namespace Qtk {
*/ */
void sceneUpdated(QString sceneName); void sceneUpdated(QString sceneName);
/*************************************************************************
* Public Members
************************************************************************/
public:
/* Models used for storing 3D models in the scene. */
std::vector<Model *> mModels {};
/* Queue of models requested to load at runtime. */
std::queue<std::pair<std::string, std::string>> mModelLoadQueue;
private: private:
/**
* Initialize an object name relative to other objects already loaded.
* Protects against having two objects with the same name.
*
* @param object Qtk Object to name within this scene.
*/
void initSceneObjectName(Qtk::Object * object);
/************************************************************************* /*************************************************************************
* Private Members * Private Members
************************************************************************/ ************************************************************************/
@ -183,14 +227,16 @@ namespace Qtk {
static Camera3D mCamera; static Camera3D mCamera;
static QMatrix4x4 mProjection; static QMatrix4x4 mProjection;
bool mInit = false; bool mInit = false;
/* Pause rendering of the scene. */
bool mPause = false;
QString mSceneName; QString mSceneName;
/* The skybox for this scene. */ /* The skybox for this scene. */
Skybox * mSkybox {}; Skybox * mSkybox {};
/* MeshRenderers used simple geometry. */ /* MeshRenderers used simple geometry. */
std::vector<MeshRenderer *> mMeshes {}; std::vector<MeshRenderer *> mMeshes {};
/* Models used for storing 3D models in the scene. */ /* Track count of objects with same initial name. */
std::vector<Model *> mModels {}; std::unordered_map<std::string, uint64_t> mObjectCount;
}; };
class SceneEmpty : public Scene { class SceneEmpty : public Scene {