Compare commits

..

8 Commits

Author SHA1 Message Date
Shaun Reed f5de113c9a Clean up cmake options 2023-04-09 20:41:02 -04:00
Shaun Reed 98793f7a75 ci
+ Windows failure fixed by
https://codereview.qt-project.org/c/qt/qtbase/+/468903
https://bugreports.qt.io/browse/QTBUG-112204
2023-04-09 18:21:32 -04:00
Shaun Reed cd641072ab Don't build example on windows CI 2023-04-02 13:34:20 -04:00
Shaun Reed 460a44e7e6 Update CI 2023-04-02 13:15:46 -04:00
Shaun Reed b0e123e6dd Format and more CI 2023-04-02 12:31:23 -04:00
Shaun Reed be69d2d242 Add packaging for example app
+ Update CI workflows
2023-04-02 11:43:59 -04:00
Shaun Reed 3c2f7e8b5d CI 2023-04-01 13:56:43 -04:00
Shaun Reed fc1ded833d WIP 2023-04-01 12:43:30 -04:00
34 changed files with 199 additions and 615 deletions

View File

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

3
.gitignore vendored
View File

@ -1,9 +1,6 @@
# 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()
@ -84,30 +84,18 @@ endif ()
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources") set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
set(QTK_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns) 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. # 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) get_cmake_property(VAR_NAMES VARIABLES)
list(FILTER VAR_NAMES INCLUDE REGEX "^[qQ][tT][kK]_.*$") list(FILTER VAR_NAMES INCLUDE REGEX "^Q[tT][kK]_.*$")
list(SORT VAR_NAMES) list(SORT VAR_NAMES)
################################################################################ ################################################################################
# External Dependencies # 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 Qt
find_package(Qt6 COMPONENTS Core UiPlugin OpenGLWidgets) find_package(Qt6 COMPONENTS Core UiPlugin OpenGLWidgets)
qt_standard_project_setup() qt_standard_project_setup()
@ -123,6 +111,12 @@ 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.
@ -143,10 +137,8 @@ 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 QT_INSTALL_DIR QT6_INSTALL_PLUGINS
) )
# Add QT6_INSTALL_PLUGINS to VAR_NAMES so it is printed at end of configuration.
list(APPEND VAR_NAMES QT6_INSTALL_PLUGINS)
# Find Assimp. # Find Assimp.
if(QTK_SUBMODULES) if(QTK_SUBMODULES)
@ -187,8 +179,6 @@ if(QTK_EXAMPLE)
add_subdirectory(example-app EXCLUDE_FROM_ALL) add_subdirectory(example-app EXCLUDE_FROM_ALL)
endif() 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) foreach(VAR_NAME IN LISTS VAR_NAMES VAR_PATHS)
if(VAR_NAME IN_LIST VAR_PATHS) if(VAR_NAME IN_LIST VAR_PATHS)
# Print absolute if variable is path # 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 Simply open the root `CMakeLists.txt` with either of these editors and
configurations will be loaded. configurations will be loaded.
This project has been ported to **Qt 6.6.0**, which is not yet available in This project has been ported to **Qt 6.5.0**, which is not yet available in
Ubuntu apt repositories. Ubuntu apt repositories.
To run this project, you will *need* to To run this project, you will *need* to
install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for
your system, **version 6.6.0** or later. your system, **version 6.5.0** or later.
Be sure to take note of the Qt6 installation directory, as we will need it to 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. correctly set our `CMAKE_PREFIX_PATH` in the next steps.
If you are building on **Windows / Mac**, consider setting If you are building on **Windows / Mac**, consider setting
the `-DQTK_ASSIMP_NEW_INTERFACE` cmake build option. the `-DASSIMP_NEW_INTERFACE` build flag.
If the build is configured with all options enabled, we can subsequently install If the build is configured with all options enabled, we can subsequently install
individual components as needed with cmake. individual components as needed with cmake.
```bash ```bash
sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git ccache libgl1-mesa-dev libglvnd-dev zlib1g-dev -y sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git ccache libgl1-mesa-dev libglvnd-dev -y
git clone https://github.com/shaunrd0/qtk git clone https://github.com/shaunrd0/qtk
cd qtk cd qtk
# Configure the build with all components enabled # 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.6.0/gcc_64 cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64
# Build all targets # Build all targets
cmake --build build-all/ cmake --build build-all/
```` ````
@ -75,7 +75,7 @@ Windows / Mac / Linux) and may be easier
to configure. to configure.
```bash ```bash
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 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
``` ```
#### Qtk GUI #### 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 # The path here should be initialized during build configuration, so no need for --prefix
cmake --install build-all/ --component qtk_plugins cmake --install build-all/ --component qtk_plugins
-- Install configuration: "Release" -- Install configuration: "Release"
-- 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.5.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.5.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 -- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/plugins/designer/libqtk_collection.so
``` ```
To uninstall after a previous installation, we can run the following command 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"); auto skybox = new Qtk::Skybox("Skybox");
setSkybox(skybox); setSkybox(skybox);
std::string spartanPath = QTK_EXAMPLE_SOURCE_DIR; auto spartan = new Model(
spartanPath += "/resources/models/spartan/spartan.obj"; "spartan", std::string(QTK_EXAMPLE_SOURCE_DIR)
auto spartan = new Model("spartan", spartanPath.c_str()); + "/../resources/models/spartan/spartan.obj");
addObject(spartan); addObject(spartan);
spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f); spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f);

View File

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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

View File

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

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 ARCHIVE DESTINATION lib/static
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 ARCHIVE DESTINATION lib/static
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
) )

View File

@ -29,30 +29,17 @@ 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);
} }
// TODO: Fix / use MainWindow in Qt Designer to add these dock widgets. auto docks = findChildren<QDockWidget *>();
// For now we will add them manually, but we should be able to do this in the for(auto & dock : docks) {
// designer. At the moment if you edit the UI in designer the dock widget addDockWidget(Qt::RightDockWidgetArea, dock);
// areas below will override the designer settings. ui_->menuView->addAction(dock->toggleViewAction());
}
// 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. // Set the window icon used for Qtk.
setWindowIcon(Qtk::getIcon()); setWindowIcon(Qtk::getIcon());
@ -87,7 +74,7 @@ Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name) {
return views_[name]; return views_[name];
} }
void MainWindow::refreshScene(const QString & sceneName) { void MainWindow::refreshScene(QString sceneName) {
// TODO: Select TreeView using sceneName // TODO: Select TreeView using sceneName>
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene()); 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. * Trigger a refresh for widgets related to a scene that has been updated.
* @param sceneName The name of the scene that has been modified. * @param sceneName The name of the scene that has been modified.
*/ */
void refreshScene(const QString & sceneName); void refreshScene(QString sceneName);
private: private:
/*************************************************************************** /***************************************************************************

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1034</width> <width>824</width>
<height>601</height> <height>601</height>
</rect> </rect>
</property> </property>
@ -28,27 +28,11 @@
</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>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> <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>3</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
@ -66,10 +50,10 @@
<item> <item>
<widget class="Qtk::QtkWidget" name="qtk::QtkWidget"> <widget class="Qtk::QtkWidget" name="qtk::QtkWidget">
<property name="toolTip"> <property name="toolTip">
<string/> <string>A custom widget tool tip.</string>
</property> </property>
<property name="whatsThis"> <property name="whatsThis">
<string>Qtk scene view rendered using OpenGL.</string> <string>Custom widget what's this?</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -83,20 +67,28 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="Qtk::TreeView" name="qtk::TreeView"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="sizePolicy"> <item>
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <widget class="Qtk::TreeView" name="qtk::TreeView">
<horstretch>1</horstretch> <property name="toolTip">
<verstretch>0</verstretch> <string>A custom widget tool tip.</string>
</sizepolicy> </property>
</property> <property name="whatsThis">
<property name="toolTip"> <string>Custom widget what's this?</string>
<string>TreeView of objects within the current scene.</string> </property>
</property> </widget>
<property name="whatsThis"> </item>
<string>TreeView of objects within the current scene. Double-click to select an object and snap to it's position.</string> <item>
</property> <widget class="Qtk::ToolBox" name="qtk::ToolBox">
</widget> <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> </item>
</layout> </layout>
</widget> </widget>
@ -105,7 +97,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1034</width> <width>824</width>
<height>22</height> <height>22</height>
</rect> </rect>
</property> </property>
@ -187,7 +179,7 @@
</widget> </widget>
<action name="actionOpen"> <action name="actionOpen">
<property name="icon"> <property name="icon">
<iconset> <iconset resource="../../resources/resources.qrc">
<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">
@ -196,7 +188,7 @@
</action> </action>
<action name="actionSave"> <action name="actionSave">
<property name="icon"> <property name="icon">
<iconset> <iconset resource="../../resources/resources.qrc">
<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">
@ -223,7 +215,7 @@
</action> </action>
<action name="actionLoad_Model"> <action name="actionLoad_Model">
<property name="icon"> <property name="icon">
<iconset> <iconset resource="../../resources/resources.qrc">
<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">
@ -235,7 +227,7 @@
</action> </action>
<action name="actionDelete_Object"> <action name="actionDelete_Object">
<property name="icon"> <property name="icon">
<iconset> <iconset resource="../../resources/resources.qrc">
<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">
@ -326,7 +318,9 @@
<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,12 +38,11 @@ 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(5.0f, 0.5f, 0.0f); mySpartan->getTransform().setTranslation(0.0f, 0.5f, 0.0f);
mySpartan->getTransform().setScale(0.5f); mySpartan->getTransform().setScale(0.5f);
addObject(mySpartan); addObject(mySpartan);

View File

@ -7,12 +7,6 @@
##############################################################################*/ ##############################################################################*/
#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>
@ -37,7 +31,6 @@ 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,7 +70,6 @@ void QtkWidget::initializeGL() {
// Connect the frameSwapped signal to call the update() function // Connect the frameSwapped signal to call the update() function
connect(this, SIGNAL(frameSwapped()), this, SLOT(update())); connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
toggleConsole();
// Initialize OpenGL debug context // Initialize OpenGL debug context
mDebugLogger = new QOpenGLDebugLogger(this); mDebugLogger = new QOpenGLDebugLogger(this);
if(mDebugLogger->initialize()) { if(mDebugLogger->initialize()) {
@ -115,11 +107,11 @@ void QtkWidget::paintGL() {
} }
} }
void QtkWidget::setScene(Scene * scene) { void QtkWidget::setScene(Qtk::Scene * scene) {
if(mScene != Q_NULLPTR) { if(mScene != Q_NULLPTR) {
delete mScene; delete mScene;
connect( connect(
scene, &Scene::sceneUpdated, MainWindow::getMainWindow(), scene, &Qtk::Scene::sceneUpdated, MainWindow::getMainWindow(),
&MainWindow::refreshScene); &MainWindow::refreshScene);
} }
@ -137,7 +129,8 @@ void QtkWidget::toggleConsole() {
mConsoleActive = false; mConsoleActive = false;
} else { } else {
MainWindow::getMainWindow()->addDockWidget( MainWindow::getMainWindow()->addDockWidget(
Qt::DockWidgetArea::BottomDockWidgetArea, mConsole); Qt::DockWidgetArea::BottomDockWidgetArea,
dynamic_cast<QDockWidget *>(mConsole));
mConsole->setHidden(false); mConsole->setHidden(false);
mConsoleActive = true; mConsoleActive = true;
} }
@ -147,34 +140,6 @@ 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());
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) { 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
@ -288,8 +253,7 @@ 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::LeftButton) if(Input::buttonPressed(Qt::RightButton)) {
|| 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,19 +131,11 @@ 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.
*/ */
@ -174,7 +166,6 @@ 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.
*/ */

View File

@ -1,6 +0,0 @@
#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,142 +8,13 @@
*/ */
#include "toolbox.h" #include "toolbox.h"
#include "qtkmainwindow.h"
#include "ui_toolbox.h" #include "ui_toolbox.h"
#include <QFormLayout> Qtk::ToolBox::ToolBox(QWidget * parent) :
#include <QLabel> QDockWidget(parent), ui(new Ui::ToolBox) {
using namespace Qtk;
ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), ui(new Ui::ToolBox) {
ui->setupUi(this); ui->setupUi(this);
setMinimumWidth(350);
} }
void ToolBox::updateFocus(const QString & name) { Qtk::ToolBox::~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,11 +12,6 @@
#include <QDesignerExportWidget> #include <QDesignerExportWidget>
#include <QDockWidget> #include <QDockWidget>
#include <QDoubleSpinBox>
#include <QGroupBox>
#include "qtk/scene.h"
namespace Ui { namespace Ui {
class ToolBox; class ToolBox;
@ -35,15 +30,6 @@ 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,57 +10,17 @@
<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">
<property name="sizePolicy"> <layout class="QHBoxLayout" name="horizontalLayout">
<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_properties"> <widget class="QWidget" name="page">
<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>
@ -73,6 +33,19 @@
<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,25 +48,16 @@ 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();
} }
auto focusScale = objectTransform->getScale(); transform.setTranslation(objectTransform->getTranslation());
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

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

View File

@ -211,14 +211,6 @@ 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

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

View File

@ -18,9 +18,6 @@
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
// Qtk // Qtk
#include <QFileInfo>
#include "modelmesh.h" #include "modelmesh.h"
#include "qtkapi.h" #include "qtkapi.h"
@ -61,6 +58,14 @@ 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()); }
/************************************************************************* /*************************************************************************
@ -127,14 +132,6 @@ 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

@ -52,10 +52,6 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
shader.setUniformValue((name + number).c_str(), i); 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 // Draw the mesh
glDrawElements( glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data()); GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
@ -66,6 +62,7 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
} }
shader.release(); shader.release();
mVAO->release(); mVAO->release();
glActiveTexture(GL_TEXTURE0);
} }
/******************************************************************************* /*******************************************************************************

View File

@ -30,21 +30,6 @@ namespace Qtk {
* Struct to store model textures. 3D Models may have multiple. * Struct to store model textures. 3D Models may have multiple.
*/ */
struct QTKAPI ModelTexture { 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. */ /** Texture ID for for this texture. */
GLuint mID {}; GLuint mID {};
QOpenGLTexture * mTexture {}; QOpenGLTexture * mTexture {};

View File

@ -96,24 +96,10 @@ 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;
} }
@ -149,39 +135,6 @@ 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

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

View File

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

View File

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

View File

@ -10,10 +10,7 @@
#define QTK_SCENE_H #define QTK_SCENE_H
#include <QMatrix4x4> #include <QMatrix4x4>
#include <QUrl>
#include <queue>
#include <unordered_map>
#include <utility> #include <utility>
#include "camera3d.h" #include "camera3d.h"
@ -78,18 +75,6 @@ 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.emplace(name, path);
}
/************************************************************************* /*************************************************************************
* Accessors * Accessors
************************************************************************/ ************************************************************************/
@ -106,16 +91,7 @@ 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) const; [[nodiscard]] Object * getObject(const QString & name);
/**
* @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.
@ -190,8 +166,6 @@ 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.
@ -201,26 +175,7 @@ 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
************************************************************************/ ************************************************************************/
@ -228,16 +183,14 @@ 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 {};
/* Track count of objects with same initial name. */ /* Models used for storing 3D models in the scene. */
std::unordered_map<std::string, uint64_t> mObjectCount; std::vector<Model *> mModels {};
}; };
class SceneEmpty : public Scene { class SceneEmpty : public Scene {

View File

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

View File

@ -74,9 +74,9 @@ namespace Qtk {
* Can be absolute or Qt resource path. * Can be absolute or Qt resource path.
* @param flipX If true the image will be flipped on X axis. * @param flipX If true the image will be flipped on X axis.
* @param flipY If true the image will be flipped on Y axis. * @param flipY If true the image will be flipped on Y axis.
* @return QImage object. * @return Pointer to an initialized QImage object.
*/ */
static QImage initImage( static QImage * initImage(
const char * image, bool flipX = false, bool flipY = false); const char * image, bool flipX = false, bool flipY = false);
/** /**
@ -132,14 +132,6 @@ namespace Qtk {
const char * right, const char * top, const char * front, const char * right, const char * top, const char * front,
const char * left, const char * bottom, const char * back); 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:
// Private ctor to prevent creating instances of this class // Private ctor to prevent creating instances of this class
OpenGLTextureFactory() = default; OpenGLTextureFactory() = default;