Compare commits

..

2 Commits

Author SHA1 Message Date
Transporter d0c8316f79 QtkIOSystem improvements 2024-01-25 00:21:03 -05:00
Shaun Reed ad59d9149e Drag and drop model loading (#14) 2023-12-27 19:36:47 +00:00
34 changed files with 615 additions and 199 deletions

View File

@ -5,6 +5,9 @@ on:
pull_request:
workflow_dispatch:
env:
QT_VERSION: 6.6.0
jobs:
Qtk:
env:
@ -15,13 +18,13 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/6.5.0/mingw81_64/ $CONFIG
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG
flags: ''
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
runs-on: ${{ matrix.os }}
@ -31,7 +34,7 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.5.0'
version: ${{ env.QT_VERSION }}
# Windows
@ -118,7 +121,7 @@ jobs:
- name: Upload Qtk install directory
uses: actions/upload-artifact@v3
with:
name: qtk-gui-${{ matrix.os }}-installdir
name: qtk-gui-${{ matrix.os }}-install
path: install/*
# TODO: Enable after trimming resources.
@ -145,13 +148,13 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/6.5.0/mingw81_64/ $CONFIG
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG
flags: ''
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
runs-on: ${{ matrix.os }}
@ -161,7 +164,7 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.5.0'
version: ${{ env.QT_VERSION }}
# Windows
@ -238,7 +241,7 @@ jobs:
uses: actions/upload-artifact@v3
if: always()
with:
name: libqtk-${{ matrix.os }}-installdir
name: libqtk-${{ matrix.os }}-install
path: install/*
Qtk-Plugins:
@ -250,13 +253,13 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/6.5.0/mingw81_64/ $CONFIG
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG
flags: ''
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
runs-on: ${{ matrix.os }}
@ -266,7 +269,7 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.5.0'
version: ${{ env.QT_VERSION }}
- name: Chocolatey Action
if: matrix.os == 'windows-latest'
@ -295,9 +298,9 @@ jobs:
os: [ubuntu-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ -DASSIMP_NEW_INTERFACE=ON
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/
runs-on: ${{ matrix.os }}
steps:
@ -306,7 +309,7 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.5.0'
version: ${{ env.QT_VERSION }}
- name: Install Assimp MacOS
if: matrix.os == 'macos-latest'
@ -322,7 +325,7 @@ jobs:
- name: Configure Qtk
shell: bash
run: cmake -B build/ ${{ matrix.cmake }} -DQTK_CCACHE=OFF
run: cmake -B build/ ${{ matrix.cmake }} -DQTK_CCACHE=OFF -DQTK_ASSIMP_NEW_INTERFACE=ON
- name: Build Qtk
shell: bash

3
.gitignore vendored
View File

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

View File

@ -67,7 +67,7 @@ option(
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(CMAKE_BUILD_TYPE Debug)
else()
@ -84,18 +84,30 @@ endif ()
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
set(QTK_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns)
# Point CMAKE_PREFIX_PATH to Qt6 install directory
# If Qtk is built within Qt Creator this is not required.
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
if (QTK_PREFIX_QTCREATOR)
# TODO: This might be a bit strange and needs more testing.
set(CMAKE_INSTALL_PREFIX "${QT_INSTALL_DIR}")
endif()
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 at the end of configuration.
# We initialize this list here so that we can append to it as needed.
# All variables in this list will be printed at the end of configuration.
get_cmake_property(VAR_NAMES VARIABLES)
list(FILTER VAR_NAMES INCLUDE REGEX "^Q[tT][kK]_.*$")
list(FILTER VAR_NAMES INCLUDE REGEX "^[qQ][tT][kK]_.*$")
list(SORT VAR_NAMES)
################################################################################
# External Dependencies
################################################################################
# Point CMAKE_PREFIX_PATH to Qt6 install directory
# If Qtk is built within Qt Creator this is not required.
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
# Find Qt
find_package(Qt6 COMPONENTS Core UiPlugin OpenGLWidgets)
qt_standard_project_setup()
@ -111,12 +123,6 @@ if(NOT Qt6_FOUND)
)
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
# Or, we can install plugins to the designer for use across all projects.
@ -137,8 +143,10 @@ set(
set(
VAR_PATHS
CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX QTK_PLUGIN_INSTALL_DIR QT6_INSTALL_PREFIX
QT_INSTALL_DIR QT6_INSTALL_PLUGINS
QT_INSTALL_DIR
)
# Add QT6_INSTALL_PLUGINS to VAR_NAMES so it is printed at end of configuration.
list(APPEND VAR_NAMES QT6_INSTALL_PLUGINS)
# Find Assimp.
if(QTK_SUBMODULES)
@ -179,6 +187,8 @@ if(QTK_EXAMPLE)
add_subdirectory(example-app EXCLUDE_FROM_ALL)
endif()
# Print all QTK options and their values at the end of configuration. This also
# prints any additional variables that we have added to VAR_NAMES and VAR_PATHS.
foreach(VAR_NAME IN LISTS VAR_NAMES VAR_PATHS)
if(VAR_NAME IN_LIST VAR_PATHS)
# Print absolute if variable is path

View File

@ -40,26 +40,26 @@ and [Qt Creator](https://github.com/qt-creator/qt-creator).
Simply open the root `CMakeLists.txt` with either of these editors and
configurations will be loaded.
This project has been ported to **Qt 6.5.0**, which is not yet available in
This project has been ported to **Qt 6.6.0**, which is not yet available in
Ubuntu apt repositories.
To run this project, you will *need* to
install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for
your system, **version 6.5.0** or later.
your system, **version 6.6.0** or later.
Be sure to take note of the Qt6 installation directory, as we will need it to
correctly set our `CMAKE_PREFIX_PATH` in the next steps.
If you are building on **Windows / Mac**, consider setting
the `-DASSIMP_NEW_INTERFACE` build flag.
the `-DQTK_ASSIMP_NEW_INTERFACE` cmake build option.
If the build is configured with all options enabled, we can subsequently install
individual components as needed with cmake.
```bash
sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git ccache libgl1-mesa-dev libglvnd-dev -y
sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git ccache libgl1-mesa-dev libglvnd-dev zlib1g-dev -y
git clone https://github.com/shaunrd0/qtk
cd qtk
# Configure the build with all components enabled
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.6.0/gcc_64
# Build all targets
cmake --build build-all/
````
@ -75,7 +75,7 @@ Windows / Mac / Linux) and may be easier
to configure.
```bash
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DQTK_SUBMODULES=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DQTK_SUBMODULES=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.6.0/gcc_64
```
#### Qtk GUI
@ -149,9 +149,9 @@ cmake --build build-all/ --target qtk_plugins -- -j $(nproc)
# The path here should be initialized during build configuration, so no need for --prefix
cmake --install build-all/ --component qtk_plugins
-- Install configuration: "Release"
-- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_library.a
-- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_plugin_library.a
-- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/plugins/designer/libqtk_collection.so
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_library.a
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_plugin_library.a
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/plugins/designer/libqtk_collection.so
```
To uninstall after a previous installation, we can run the following command

View File

@ -23,9 +23,9 @@ void ExampleScene::init() {
auto skybox = new Qtk::Skybox("Skybox");
setSkybox(skybox);
auto spartan = new Model(
"spartan", std::string(QTK_EXAMPLE_SOURCE_DIR)
+ "/../resources/models/spartan/spartan.obj");
std::string spartanPath = QTK_EXAMPLE_SOURCE_DIR;
spartanPath += "/resources/models/spartan/spartan.obj";
auto spartan = new Model("spartan", spartanPath.c_str());
addObject(spartan);
spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f);

View File

@ -1,6 +1,6 @@
#ifndef QTK_RESOURCES_H_IN_H
#define QTK_RESOURCES_H_IN_H
#define QTK_EXAMPLE_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@"
#define QTK_EXAMPLE_SOURCE_DIR "@CMAKE_SOURCE_DIR@"
#endif // QTK_RESOURCES_H_IN_H

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/textures">
<file alias="plaster.png">images/plaster.png</file>
<file alias="crate.png">images/crate.png</file>
<file alias="stone.png">images/stone.png</file>
<file alias="wood.png">images/wood.png</file>

View File

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

View File

@ -29,17 +29,30 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
for(auto & qtkWidget : qtkWidgets) {
qtkWidget->setScene(new Qtk::SceneEmpty);
views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget);
// 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, ui_->qtk__ToolBox,
&Qtk::ToolBox::updateFocus);
}
auto docks = findChildren<QDockWidget *>();
for(auto & dock : docks) {
addDockWidget(Qt::RightDockWidgetArea, dock);
ui_->menuView->addAction(dock->toggleViewAction());
}
// 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
// areas below will override the designer settings.
// Dock the toolbox widget to the main window.
addDockWidget(Qt::LeftDockWidgetArea, ui_->qtk__ToolBox);
// Add an option to toggle active widgets in the GUI's toolbar 'view' menu.
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.
setWindowIcon(Qtk::getIcon());
@ -74,7 +87,7 @@ Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name) {
return views_[name];
}
void MainWindow::refreshScene(QString sceneName) {
// TODO: Select TreeView using sceneName>
void MainWindow::refreshScene(const QString & sceneName) {
// TODO: Select TreeView using sceneName
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene());
}

View File

@ -69,7 +69,7 @@ class MainWindow : public QMainWindow {
* Trigger a refresh for widgets related to a scene that has been updated.
* @param sceneName The name of the scene that has been modified.
*/
void refreshScene(QString sceneName);
void refreshScene(const QString & sceneName);
private:
/***************************************************************************

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>824</width>
<width>1034</width>
<height>601</height>
</rect>
</property>
@ -28,11 +28,27 @@
</property>
<widget class="QWidget" name="centralwidget">
<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>Object details and configuration panel.</string>
</property>
<property name="whatsThis">
<string>When an object is double-clicked in the TreeView for a scene, this panel will display relevant details and options.</string>
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<horstretch>3</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
@ -50,10 +66,10 @@
<item>
<widget class="Qtk::QtkWidget" name="qtk::QtkWidget">
<property name="toolTip">
<string>A custom widget tool tip.</string>
<string/>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
<string>Qtk scene view rendered using OpenGL.</string>
</property>
</widget>
</item>
@ -66,30 +82,22 @@
</widget>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="Qtk::TreeView" name="qtk::TreeView">
<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>
<string>TreeView of objects within the current scene.</string>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
<string>TreeView of objects within the current scene. Double-click to select an object and snap to it's position.</string>
</property>
</widget>
</item>
<item>
<widget class="Qtk::ToolBox" name="qtk::ToolBox">
<property name="toolTip">
<string>A custom widget tool tip.</string>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
@ -97,7 +105,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>824</width>
<width>1034</width>
<height>22</height>
</rect>
</property>
@ -179,7 +187,7 @@
</widget>
<action name="actionOpen">
<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>
</property>
<property name="text">
@ -188,7 +196,7 @@
</action>
<action name="actionSave">
<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>
</property>
<property name="text">
@ -215,7 +223,7 @@
</action>
<action name="actionLoad_Model">
<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>
</property>
<property name="text">
@ -227,7 +235,7 @@
</action>
<action name="actionDelete_Object">
<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>
</property>
<property name="text">
@ -318,9 +326,7 @@
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../resources/resources.qrc"/>
</resources>
<resources/>
<connections>
<connection>
<sender>actionExit</sender>

View File

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

View File

@ -7,6 +7,12 @@
##############################################################################*/
#include <QKeyEvent>
#include <QMimeData>
#include <QVBoxLayout>
#include <qtk/input.h>
#include <qtk/scene.h>
#include <qtk/shape.h>
#include <QVBoxLayout>
#include <qtk/input.h>
@ -31,6 +37,7 @@ QtkWidget::QtkWidget(QWidget * parent, const QString & name) :
QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR),
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR) {
setAcceptDrops(true);
setScene(scene);
setObjectName(name);
QSurfaceFormat format;
@ -70,6 +77,7 @@ void QtkWidget::initializeGL() {
// Connect the frameSwapped signal to call the update() function
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
toggleConsole();
// Initialize OpenGL debug context
mDebugLogger = new QOpenGLDebugLogger(this);
if(mDebugLogger->initialize()) {
@ -107,11 +115,11 @@ void QtkWidget::paintGL() {
}
}
void QtkWidget::setScene(Qtk::Scene * scene) {
void QtkWidget::setScene(Scene * scene) {
if(mScene != Q_NULLPTR) {
delete mScene;
connect(
scene, &Qtk::Scene::sceneUpdated, MainWindow::getMainWindow(),
scene, &Scene::sceneUpdated, MainWindow::getMainWindow(),
&MainWindow::refreshScene);
}
@ -129,8 +137,7 @@ void QtkWidget::toggleConsole() {
mConsoleActive = false;
} else {
MainWindow::getMainWindow()->addDockWidget(
Qt::DockWidgetArea::BottomDockWidgetArea,
dynamic_cast<QDockWidget *>(mConsole));
Qt::DockWidgetArea::BottomDockWidgetArea, mConsole);
mConsole->setHidden(false);
mConsoleActive = true;
}
@ -140,6 +147,34 @@ void QtkWidget::toggleConsole() {
* 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());
auto urls = event->mimeData()->urls();
if(!urls.isEmpty()) {
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: " + url.fileName() + "\n";
event->ignore();
}
}
}
void QtkWidget::keyPressEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
// Do not repeat input while a key is held down
@ -253,7 +288,8 @@ void QtkWidget::teardownGL() { /* Nothing to teardown yet... */
void QtkWidget::updateCameraInput() {
Input::update();
// 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 rotSpeed = 0.5f;

View File

@ -131,11 +131,19 @@ namespace Qtk {
*/
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 Methods
************************************************************************/
void dragEnterEvent(QDragEnterEvent * event) override;
void dropEvent(QDropEvent * event) override;
/**
* @param event Key press event to update camera input manager.
*/
@ -166,6 +174,7 @@ namespace Qtk {
/**
* Called when the `messageLogged` signal is caught.
* See definition of initializeGL()
* https://doc.qt.io/qt-6/qopengldebuglogger.html#signals
*
* @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,142 @@
*/
#include "toolbox.h"
#include "qtkmainwindow.h"
#include "ui_toolbox.h"
Qtk::ToolBox::ToolBox(QWidget * parent) :
QDockWidget(parent), ui(new Ui::ToolBox) {
#include <QFormLayout>
#include <QLabel>
using namespace Qtk;
ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), ui(new Ui::ToolBox) {
ui->setupUi(this);
setMinimumWidth(350);
}
Qtk::ToolBox::~ToolBox() {
void ToolBox::updateFocus(const QString & name) {
auto object =
MainWindow::getMainWindow()->getQtkWidget()->getScene()->getObject(name);
if(object != Q_NULLPTR) {
removePages();
createPageProperties(object);
createPageShader(object);
}
}
ToolBox::~ToolBox() {
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 <QDockWidget>
#include <QDoubleSpinBox>
#include <QGroupBox>
#include "qtk/scene.h"
namespace Ui {
class ToolBox;
@ -30,6 +35,15 @@ namespace Qtk {
~ToolBox();
void removePages();
void createPageProperties(const Object * object);
void createPageShader(const Object * object);
void updateFocus(const QString & name);
private:
/*************************************************************************
* Private Members

View File

@ -10,17 +10,57 @@
<height>300</height>
</rect>
</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">
<string>Object Details</string>
</property>
<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>
<widget class="QToolBox" name="toolBox">
<property name="currentIndex">
<number>0</number>
</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">
<rect>
<x>0</x>
@ -33,19 +73,6 @@
<string>Shaders</string>
</attribute>
</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>
</item>
</layout>

View File

@ -48,16 +48,25 @@ void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) {
auto scene = MainWindow::getMainWindow()->getQtkWidget()->getScene();
auto & transform = 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";
}
Transform3D * objectTransform;
if(object->getType() == Object::QTK_MESH) {
} 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();
}
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);
// Emit signal from qtk widget for new object focus. Triggers GUI updates.
emit MainWindow::getMainWindow()->getQtkWidget()->objectFocusChanged(name);
}

View File

@ -68,9 +68,9 @@ target_link_libraries(
Qt6::Core Qt6::OpenGLWidgets Qt6::Widgets
)
if(QTK_SUBMODULES OR NOT ASSIMP_NEW_INTERFACE)
if(QTK_SUBMODULES OR NOT QTK_ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk_library PUBLIC assimp)
elseif(ASSIMP_NEW_INTERFACE)
elseif(QTK_ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk_library PUBLIC assimp::assimp)
endif()

View File

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

View File

@ -43,7 +43,7 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
texture.mTexture->destroy();
texture.mTexture->create();
texture.mTexture->setData(
*OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY));
OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY));
modified = true;
}
}
@ -238,7 +238,7 @@ ModelMesh::Textures Model::loadMaterialTextures(
// Add the texture to the textures container
textures.push_back(texture);
// Add the texture to the loaded textures to avoid loading it twice
mTexturesLoaded.push_back(texture);
mTexturesLoaded.push_back(textures.back());
}
}

View File

@ -18,6 +18,9 @@
#include <assimp/Importer.hpp>
// Qtk
#include <QFileInfo>
#include "modelmesh.h"
#include "qtkapi.h"
@ -58,14 +61,6 @@ namespace Qtk {
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()); }
/*************************************************************************
@ -132,6 +127,14 @@ namespace Qtk {
*/
inline Transform3D & getTransform() { return mTransform; }
inline std::string getVertexShader() const override {
return mVertexShader;
}
inline std::string getFragmentShader() const override {
return mFragmentShader;
}
private:
/*************************************************************************
* Private Methods

View File

@ -52,6 +52,10 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
shader.setUniformValue((name + number).c_str(), i);
}
// Always reset active texture to GL_TEXTURE0 before we draw.
// This is important for models with no textures.
glActiveTexture(GL_TEXTURE0);
// Draw the mesh
glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
@ -62,7 +66,6 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
}
shader.release();
mVAO->release();
glActiveTexture(GL_TEXTURE0);
}
/*******************************************************************************

View File

@ -30,6 +30,21 @@ namespace Qtk {
* Struct to store model textures. 3D Models may have multiple.
*/
struct QTKAPI ModelTexture {
ModelTexture() = default;
/**
* Construct a ModelTexture.
*
* @param id Texture ID for this texture.
* @param type Type of texture in string format.
* @param path Path to the texture on disk.
*/
ModelTexture(const std::string & type, const std::string & path) :
mType(type), mPath(path) {
mTexture = OpenGLTextureFactory::initTexture(path.c_str());
mID = mTexture->textureId();
}
/** Texture ID for for this texture. */
GLuint mID {};
QOpenGLTexture * mTexture {};

View File

@ -96,10 +96,24 @@ namespace Qtk {
[[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
************************************************************************/
virtual inline void setName(const std::string & name) { mName = name; }
virtual inline void setColors(const Colors & value) {
mShape.mColors = value;
}
@ -135,6 +149,39 @@ namespace Qtk {
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
************************************************************************/

View File

@ -17,17 +17,23 @@ using namespace Qtk;
QtkIOStream::QtkIOStream(const char * pFile, const char * pMode) :
mFile(pFile) {
QString mode(pMode);
bool read = mode.contains('r');
bool write = mode.contains('w');
if(read && write) {
mFile.open(QIODevice::ReadWrite);
} else if(read) {
mFile.open(QIODevice::ReadOnly);
} else if(write) {
mFile.open(QIODevice::WriteOnly);
bool open = false;
if(mode == "w" || mode == "wb") {
open = mFile.open(QIODeviceBase::WriteOnly);
} else if(mode == "r" || mode == "rb") {
open = mFile.open(QIODeviceBase::ReadOnly);
} else if(mode == "wt") {
open = mFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text);
} else if(mode == "rt") {
open = mFile.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text);
} else {
open = false;
qDebug() << "[Qtk::QtkIOStream] Invalid file open mode: " << mode << "\n";
}
if(!open) {
qDebug() << "[Qtk::QtkIOStream] Could not open file: " << QString(pFile)
<< "\n";
}
}
/*******************************************************************************
@ -35,34 +41,24 @@ QtkIOStream::QtkIOStream(const char * pFile, const char * pMode) :
******************************************************************************/
size_t QtkIOStream::Read(void * pvBuffer, size_t pSize, size_t pCount) {
size_t read = 0;
do {
auto readSize = mFile.read((char *)pvBuffer + read, pSize);
qint64 readSize = mFile.read((char *)pvBuffer, pSize * pCount);
if(readSize < 0) {
qDebug() << "[Qtk::QtkIOStream] Failed to read (" << pSize
<< ") bytes from file at: " << mFile.filesystemFileName().c_str()
<< "\n";
return -1;
}
read += readSize;
} while(pCount--);
return read;
return readSize;
}
size_t QtkIOStream::Write(const void * pvBuffer, size_t pSize, size_t pCount) {
size_t wrote = 0;
do {
auto writeSize = mFile.write((char *)pvBuffer + wrote, pSize);
qint64 writeSize = mFile.write((char *)pvBuffer, pSize * pCount);
if(writeSize < 0) {
qDebug() << "[Qtk::QtkIOStream] Failed to write buffer with size ("
<< pSize
<< ") to file at: " << mFile.filesystemFileName().c_str()
<< "\n";
qDebug() << "[Qtk::QtkIOStream] Failed to write buffer with size (" << pSize
<< ") to file at: " << mFile.filesystemFileName().c_str() << "\n";
return -1;
}
wrote += writeSize;
} while(pCount--);
return wrote;
return writeSize;
}
aiReturn QtkIOStream::Seek(size_t pOffset, aiOrigin pOrigin) {

View File

@ -7,6 +7,7 @@
##############################################################################*/
#include "qtkiosystem.h"
#include <QDir>
using namespace Qtk;
@ -19,15 +20,11 @@ bool QtkIOSystem::Exists(const char * pFile) const {
}
char QtkIOSystem::getOsSeparator() const {
#ifndef _WIN32
return '/';
#else
return '\\';
#endif
return QDir::separator().toLatin1();
}
Assimp::IOStream * QtkIOSystem::Open(const char * pFile, const char * pMode) {
if(!QFileInfo::exists(pFile)) {
if(!Exists(pFile)) {
qDebug() << "[Qtk::QtkIOSystem] failed to open file: " << pFile << "\n";
return nullptr;
}

View File

@ -37,16 +37,42 @@ Scene::~Scene() {
* 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() {
if(!mInit) {
initializeOpenGLFunctions();
init();
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) {
mSkybox->draw();
}
for(auto & model : mModels) {
for(const auto & model : mModels) {
model->draw();
}
for(const auto & mesh : mMeshes) {
@ -57,8 +83,8 @@ void Scene::draw() {
std::vector<Object *> Scene::getObjects() const {
// All scene objects must inherit from Qtk::Object.
std::vector<Object *> objects(mMeshes.begin(), mMeshes.end());
for(auto model : mModels) {
objects.push_back(dynamic_cast<Object *>(model));
for(const auto & model : mModels) {
objects.push_back(model);
if(objects.back() == nullptr) {
return {};
}
@ -66,8 +92,8 @@ std::vector<Object *> Scene::getObjects() const {
return objects;
}
Object * Scene::getObject(const QString & name) {
for(auto object : getObjects()) {
Object * Scene::getObject(const QString & name) const {
for(const auto & object : getObjects()) {
if(object->getName() == name.toStdString()) {
return object;
}
@ -80,14 +106,14 @@ void Scene::setSkybox(Skybox * skybox) {
mSkybox = skybox;
}
template <> MeshRenderer * Scene::addObject(MeshRenderer * object) {
mMeshes.push_back(object);
sceneUpdated(mSceneName);
return object;
}
template <> Model * Scene::addObject(Model * object) {
mModels.push_back(object);
sceneUpdated(mSceneName);
return object;
void Scene::initSceneObjectName(Object * object) {
if(!mObjectCount.count(object->getName())) {
mObjectCount[object->getName()] = 1;
} else {
mObjectCount[object->getName()]++;
}
auto count = mObjectCount[object->getName()];
if(count > 1) {
object->setName(object->getName() + " (" + std::to_string(count) + ")");
}
}

View File

@ -10,7 +10,10 @@
#define QTK_SCENE_H
#include <QMatrix4x4>
#include <QUrl>
#include <queue>
#include <unordered_map>
#include <utility>
#include "camera3d.h"
@ -75,6 +78,18 @@ namespace Qtk {
*/
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.emplace(name, path);
}
/*************************************************************************
* Accessors
************************************************************************/
@ -91,7 +106,16 @@ namespace Qtk {
* @param name The objectName to look for within this scene.
* @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.
@ -166,6 +190,8 @@ namespace Qtk {
*/
inline void setSceneName(QString name) { mSceneName = std::move(name); }
inline void setPause(bool pause) { mPause = pause; }
signals:
/**
* Signal thrown when the scene is modified by adding or removing objects.
@ -175,7 +201,26 @@ namespace Qtk {
*/
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:
/**
* 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
************************************************************************/
@ -183,14 +228,16 @@ namespace Qtk {
static Camera3D mCamera;
static QMatrix4x4 mProjection;
bool mInit = false;
/* Pause rendering of the scene. */
bool mPause = false;
QString mSceneName;
/* The skybox for this scene. */
Skybox * mSkybox {};
/* MeshRenderers used simple geometry. */
std::vector<MeshRenderer *> mMeshes {};
/* Models used for storing 3D models in the scene. */
std::vector<Model *> mModels {};
/* Track count of objects with same initial name. */
std::unordered_map<std::string, uint64_t> mObjectCount;
};
class SceneEmpty : public Scene {

View File

@ -9,19 +9,18 @@
#include <QDebug>
#include <QImageReader>
#include "app/qtkmainwindow.h"
#include "texture.h"
using namespace Qtk;
QImage * OpenGLTextureFactory::initImage(
QImage OpenGLTextureFactory::initImage(
const char * image, bool flipX, bool flipY) {
// Qt6 limits loaded images to 256MB by default
QImageReader::setAllocationLimit(512);
auto loadedImage = new QImage(QImage(image).mirrored(flipX, flipY));
if(loadedImage->isNull()) {
qDebug() << "[Qtk::OpenGLTextureFactory] Error loading image: " << image
<< "\nSupported types: " << QImageReader::supportedImageFormats();
return Q_NULLPTR;
QImageReader::setAllocationLimit(1024);
auto loadedImage = QImage(image).mirrored(flipX, flipY);
if(loadedImage.isNull()) {
return defaultTexture();
}
return loadedImage;
@ -29,13 +28,12 @@ QImage * OpenGLTextureFactory::initImage(
QOpenGLTexture * OpenGLTextureFactory::initTexture(
const char * texture, bool flipX, bool flipY) {
QImage * image = initImage(texture, flipX, flipY);
QImage image = initImage(texture, flipX, flipY);
auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
newTexture->setData(*image);
newTexture->setData(image);
newTexture->setWrapMode(QOpenGLTexture::Repeat);
newTexture->setMinMagFilters(
QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
delete image;
return newTexture;
}
@ -71,6 +69,7 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
QImage faceImage(faceTextures[i]);
if(faceImage.isNull()) {
qDebug() << "Error loading cube map image\n";
faceImage = defaultTexture();
}
faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888);

View File

@ -74,9 +74,9 @@ namespace Qtk {
* Can be absolute or Qt resource path.
* @param flipX If true the image will be flipped on X axis.
* @param flipY If true the image will be flipped on Y axis.
* @return Pointer to an initialized QImage object.
* @return QImage object.
*/
static QImage * initImage(
static QImage initImage(
const char * image, bool flipX = false, bool flipY = false);
/**
@ -132,6 +132,14 @@ namespace Qtk {
const char * right, const char * top, const char * front,
const char * left, const char * bottom, const char * back);
/// The texture used in place of a missing texture.
static QImage defaultTexture() {
// Use plaster for default texture if image fails to load.
// This prevents segfaults when loading a texture that doesn't exist.
// TODO: Replace with a '?' texture to indicate missing texture.
return QImage(":/textures/plaster.png");
}
private:
// Private ctor to prevent creating instances of this class
OpenGLTextureFactory() = default;