Compare commits

4 Commits

Author SHA1 Message Date
64c7b3d2eb old WIP code
Some checks failed
All Builds / Qtk (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), ubuntu-latest) (push) Failing after 1m23s
All Builds / Qtk-Library (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), ubuntu-latest) (push) Failing after 21s
All Builds / Qtk-Plugins (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), ubuntu-latest) (push) Failing after 18s
All Builds / Qtk-Assimp-Targets (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/, ubuntu-latest) (push) Failing after 18s
Linting / Tidy (push) Failing after 53s
Linting / Format (app) (push) Failing after 31s
Linting / Format (src) (push) Failing after 25s
All Builds / Qtk (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), macos-latest) (push) Has been cancelled
All Builds / Qtk (-DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG, , windows-latest) (push) Has been cancelled
All Builds / Qtk-Library (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), macos-latest) (push) Has been cancelled
All Builds / Qtk-Library (-DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG, , windows-latest) (push) Has been cancelled
All Builds / Qtk-Plugins (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), macos-latest) (push) Has been cancelled
All Builds / Qtk-Plugins (-DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG, , windows-latest) (push) Has been cancelled
All Builds / Qtk-Assimp-Targets (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/, macos-latest) (push) Has been cancelled
2025-02-13 20:12:28 -05:00
2087b306b9 Fix order of arguments for rotation functions. 2024-03-10 15:22:31 -04:00
Transporter
d0c8316f79 QtkIOSystem improvements 2024-01-25 00:21:03 -05:00
ad59d9149e Drag and drop model loading (#14) 2023-12-27 19:36:47 +00:00
46 changed files with 1064 additions and 419 deletions

View File

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

3
.gitignore vendored
View File

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

View File

@@ -67,7 +67,7 @@ option(
OFF OFF
) )
if(QTK_DEBUG OR CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") if(QTK_DEBUG OR CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$")
set(QTK_DEBUG ON) set(QTK_DEBUG ON)
set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
else() else()
@@ -84,18 +84,30 @@ 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 "^Q[tT][kK]_.*$") list(FILTER VAR_NAMES INCLUDE REGEX "^[qQ][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()
@@ -111,12 +123,6 @@ if(NOT Qt6_FOUND)
) )
endif() endif()
# TODO: This might be a bit strange and needs more testing.
if (QTK_PREFIX_QTCREATOR)
set(CMAKE_INSTALL_PREFIX "${QT_INSTALL_DIR}")
endif()
# #
# To use custom plugins, set QT_PLUGIN_PATH environment variable before running designer # To use custom plugins, set QT_PLUGIN_PATH environment variable before running designer
# Or, we can install plugins to the designer for use across all projects. # Or, we can install plugins to the designer for use across all projects.
@@ -137,8 +143,10 @@ set(
set( set(
VAR_PATHS VAR_PATHS
CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX QTK_PLUGIN_INSTALL_DIR QT6_INSTALL_PREFIX CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX QTK_PLUGIN_INSTALL_DIR QT6_INSTALL_PREFIX
QT_INSTALL_DIR QT6_INSTALL_PLUGINS QT_INSTALL_DIR
) )
# 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)
@@ -179,6 +187,8 @@ 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.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. 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.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 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 `-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 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 -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 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.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 # 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.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 #### 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.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_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/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/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

@@ -13,8 +13,8 @@ using namespace Qtk;
ExampleScene::ExampleScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) { ExampleScene::ExampleScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) {
setSceneName("Example Scene"); setSceneName("Example Scene");
getCamera().getTransform().setTranslation(-8.0f, 0.0f, 10.0f); getCamera().setTranslation({-8.0f, 0.0f, 10.0f});
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); getCamera().setRotation(0.0f, 1.0f, 0.0f, -5.0f);
} }
ExampleScene::~ExampleScene() {} ExampleScene::~ExampleScene() {}
@@ -23,9 +23,9 @@ void ExampleScene::init() {
auto skybox = new Qtk::Skybox("Skybox"); auto skybox = new Qtk::Skybox("Skybox");
setSkybox(skybox); setSkybox(skybox);
auto spartan = new Model( std::string spartanPath = QTK_EXAMPLE_SOURCE_DIR;
"spartan", std::string(QTK_EXAMPLE_SOURCE_DIR) spartanPath += "/resources/models/spartan/spartan.obj";
+ "/../resources/models/spartan/spartan.obj"); auto spartan = new Model("spartan", spartanPath.c_str());
addObject(spartan); addObject(spartan);
spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f); spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f);
@@ -64,10 +64,10 @@ void ExampleScene::update() {
// Pitch forward and roll sideways // Pitch forward and roll sideways
MeshRenderer::getInstance("leftTriangle") MeshRenderer::getInstance("leftTriangle")
->getTransform() ->getTransform()
.rotate(0.75f, 1.0f, 0.0f, 0.0f); .rotate(1.0f, 0.0f, 0.0f, 0.75f);
MeshRenderer::getInstance("rightTriangle") MeshRenderer::getInstance("rightTriangle")
->getTransform() ->getTransform()
.rotate(0.75f, 0.0f, 0.0f, 1.0f); .rotate(0.0f, 0.0f, 1.0f, 0.75f);
static float translateX = 0.025f; static float translateX = 0.025f;
float limit = -9.0f; // Origin position.x - 2.0f float limit = -9.0f; // Origin position.x - 2.0f
@@ -86,12 +86,12 @@ void ExampleScene::update() {
.translate(-translateX, 0.0f, 0.0f); .translate(-translateX, 0.0f, 0.0f);
MeshRenderer::getInstance("topTriangle") MeshRenderer::getInstance("topTriangle")
->getTransform() ->getTransform()
.rotate(0.75f, 0.2f, 0.0f, 0.4f); .rotate(0.2f, 0.0f, 0.4f, 0.75f);
MeshRenderer::getInstance("bottomTriangle") MeshRenderer::getInstance("bottomTriangle")
->getTransform() ->getTransform()
.rotate(0.75f, 0.0f, 0.2f, 0.4f); .rotate(0.0f, 0.2f, 0.4f, 0.75f);
MeshRenderer::getInstance("centerCube") MeshRenderer::getInstance("centerCube")
->getTransform() ->getTransform()
.rotate(0.75f, 0.2f, 0.4f, 0.6f); .rotate(0.2f, 0.4f, 0.6f, 0.75f);
} }

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_CURRENT_SOURCE_DIR@" #define QTK_EXAMPLE_SOURCE_DIR "@CMAKE_SOURCE_DIR@"
#endif // QTK_RESOURCES_H_IN_H #endif // QTK_RESOURCES_H_IN_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

View File

@@ -1,5 +1,6 @@
<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/static ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
) )
@@ -57,7 +57,7 @@ if(QTK_GUI)
COMPONENT qtk_gui COMPONENT qtk_gui
BUNDLE DESTINATION . BUNDLE DESTINATION .
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
) )

View File

@@ -47,13 +47,13 @@ target_link_libraries(qtk_plugins PUBLIC qtk_plugin_library)
################################################################################ ################################################################################
# Final Qtk Application # Final Qtk Application
################################################################################ ################################################################################
# Source files for the main window and core application
set( set(
QTK_GUI_SOURCES QTK_GUI_SOURCES
qtkscene.cpp qtkscene.h qtkscene.cpp qtkscene.h
# logger.cpp logger.h
main.cpp main.cpp
) )
qt_add_executable(qtk_gui ${QTK_GUI_SOURCES}) qt_add_executable(qtk_gui ${QTK_GUI_SOURCES})
target_link_libraries(qtk_gui PRIVATE qtk_plugin_library) target_link_libraries(qtk_gui PRIVATE qtk_plugin_library)

View File

@@ -28,6 +28,7 @@ DebugConsole::DebugConsole(
auto qtkWidget = dynamic_cast<QtkWidget *>(owner); auto qtkWidget = dynamic_cast<QtkWidget *>(owner);
if(qtkWidget) { if(qtkWidget) {
connect(qtkWidget, &QtkWidget::sendLog, this, &DebugConsole::sendLog); connect(
qtkWidget->getLogger(), &Logger::sendLog, this, &DebugConsole::sendLog);
} }
} }

View File

@@ -61,7 +61,8 @@ namespace Qtk {
* @param context The DebugContext to use for the message. * @param context The DebugContext to use for the message.
* Default value is Status. * Default value is Status.
*/ */
inline void sendLog(QString message, DebugContext context = Status) { inline void sendLog(QString message, Qtk::DebugContext context = Status) {
// qDebug() << "[LOGGER]: " << qPrintable(message) << "; LOGGER";
mConsole->setTextColor(logColor(context)); mConsole->setTextColor(logColor(context));
mConsole->append(logPrefix(message, context)); mConsole->append(logPrefix(message, context));
} }

107
src/app/logger.cpp Normal file
View File

@@ -0,0 +1,107 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Logger for Qtk ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include "logger.h"
#include "qtkwidget.h"
using namespace Qtk;
Logger::Logger(Qtk::QtkWidget * parent) : QObject(parent) {
// connect(
// this, SIGNAL(sendLog(QString, Qtk::DebugContext)), this,
// SLOT(log(QString, Qtk::DebugContext)));
if(parent != Q_NULLPTR) {
connect(
parent, SIGNAL(sendLog(QString, Qtk::DebugContext)), this,
SLOT(log(QString, Qtk::DebugContext)));
}
}
std::pair<QString, DebugContext> Logger::log(const QOpenGLDebugMessage & msg) {
QString error_msg;
DebugContext context;
// Format based on severity
switch(msg.severity()) {
case QOpenGLDebugMessage::NotificationSeverity:
error_msg += "--";
context = Status;
break;
case QOpenGLDebugMessage::HighSeverity:
error_msg += "!!";
context = Fatal;
break;
case QOpenGLDebugMessage::MediumSeverity:
error_msg += "!~";
context = Error;
break;
case QOpenGLDebugMessage::LowSeverity:
error_msg += "~~";
context = Warn;
break;
case QOpenGLDebugMessage::InvalidSeverity:
error_msg += "??";
context = Invalid;
break;
case QOpenGLDebugMessage::AnySeverity:
error_msg += "**";
context = Any;
break;
}
error_msg += " (";
// Format based on source
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error_msg += #c; \
break
switch(msg.source()) {
CASE(APISource);
CASE(WindowSystemSource);
CASE(ShaderCompilerSource);
CASE(ThirdPartySource);
CASE(ApplicationSource);
CASE(OtherSource);
CASE(InvalidSource);
CASE(AnySource);
}
#undef CASE
error_msg += " : ";
// Format based on type
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error_msg += #c; \
break
switch(msg.type()) {
CASE(InvalidType);
CASE(ErrorType);
CASE(DeprecatedBehaviorType);
CASE(UndefinedBehaviorType);
CASE(PortabilityType);
CASE(PerformanceType);
CASE(OtherType);
CASE(MarkerType);
CASE(GroupPushType);
CASE(GroupPopType);
CASE(AnyType);
}
#undef CASE
error_msg += ")\n" + msg.message();
log(error_msg, context);
return {error_msg, context};
}
void Logger::log(const QString & message, DebugContext context) {
const QString log_msg = "[LOGGER]: " + message + "; LOGGER";
qDebug() << qPrintable(log_msg);
emit sendLog(log_msg, context);
}

37
src/app/logger.h Normal file
View File

@@ -0,0 +1,37 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Logger for Qtk ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_LOGGER_H
#define QTK_LOGGER_H
#include <QOpenGLDebugMessage>
#include <QString>
#include "qtk/qtkapi.h"
namespace Qtk {
class QtkWidget;
class Logger : public QObject {
Q_OBJECT
public:
explicit Logger(Qtk::QtkWidget * parent = nullptr);
public slots:
std::pair<QString, DebugContext> log(
const QOpenGLDebugMessage & msg);
void log(
const QString & message,
Qtk::DebugContext context = Qtk::DebugContext::Status);
signals:
void sendLog(const QString & message, Qtk::DebugContext context);
};
} // namespace Qtk
#endif // QTK_LOGGER_H

View File

@@ -29,17 +29,30 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
for(auto & qtkWidget : qtkWidgets) { for(auto & qtkWidget : qtkWidgets) {
qtkWidget->setScene(new Qtk::SceneEmpty); qtkWidget->setScene(new Qtk::SceneEmpty);
views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget); views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget);
// Add GUI 'view' toolbar option to show debug console.
ui_->menuView->addAction(qtkWidget->getActionToggleConsole()); ui_->menuView->addAction(qtkWidget->getActionToggleConsole());
// Refresh GUI widgets when scene or objects are updated.
connect( connect(
qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this, qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this,
&MainWindow::refreshScene); &MainWindow::refreshScene);
connect(
qtkWidget, &Qtk::QtkWidget::objectFocusChanged, ui_->qtk__ToolBox,
&Qtk::ToolBox::updateFocus);
} }
auto docks = findChildren<QDockWidget *>(); // TODO: Fix / use MainWindow in Qt Designer to add these dock widgets.
for(auto & dock : docks) { // For now we will add them manually, but we should be able to do this in the
addDockWidget(Qt::RightDockWidgetArea, dock); // designer. At the moment if you edit the UI in designer the dock widget
ui_->menuView->addAction(dock->toggleViewAction()); // 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. // Set the window icon used for Qtk.
setWindowIcon(Qtk::getIcon()); setWindowIcon(Qtk::getIcon());
@@ -74,7 +87,7 @@ Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name) {
return views_[name]; return views_[name];
} }
void MainWindow::refreshScene(QString sceneName) { void MainWindow::refreshScene(const 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(QString sceneName); void refreshScene(const 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>824</width> <width>1034</width>
<height>601</height> <height>601</height>
</rect> </rect>
</property> </property>
@@ -28,11 +28,27 @@
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="Qtk::ToolBox" name="qtk::ToolBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>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>0</horstretch> <horstretch>3</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
@@ -50,10 +66,10 @@
<item> <item>
<widget class="Qtk::QtkWidget" name="qtk::QtkWidget"> <widget class="Qtk::QtkWidget" name="qtk::QtkWidget">
<property name="toolTip"> <property name="toolTip">
<string>A custom widget tool tip.</string> <string/>
</property> </property>
<property name="whatsThis"> <property name="whatsThis">
<string>Custom widget what's this?</string> <string>Qtk scene view rendered using OpenGL.</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -67,28 +83,20 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <widget class="Qtk::TreeView" name="qtk::TreeView">
<item> <property name="sizePolicy">
<widget class="Qtk::TreeView" name="qtk::TreeView"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<property name="toolTip"> <horstretch>1</horstretch>
<string>A custom widget tool tip.</string> <verstretch>0</verstretch>
</property> </sizepolicy>
<property name="whatsThis"> </property>
<string>Custom widget what's this?</string> <property name="toolTip">
</property> <string>TreeView of objects within the current scene.</string>
</widget> </property>
</item> <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>
<widget class="Qtk::ToolBox" name="qtk::ToolBox"> </property>
<property name="toolTip"> </widget>
<string>A custom widget tool tip.</string>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
@@ -97,7 +105,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>824</width> <width>1034</width>
<height>22</height> <height>22</height>
</rect> </rect>
</property> </property>
@@ -179,7 +187,7 @@
</widget> </widget>
<action name="actionOpen"> <action name="actionOpen">
<property name="icon"> <property name="icon">
<iconset resource="../../resources/resources.qrc"> <iconset>
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</iconset> <normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
@@ -188,7 +196,7 @@
</action> </action>
<action name="actionSave"> <action name="actionSave">
<property name="icon"> <property name="icon">
<iconset resource="../../resources/resources.qrc"> <iconset>
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</iconset> <normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
@@ -215,7 +223,7 @@
</action> </action>
<action name="actionLoad_Model"> <action name="actionLoad_Model">
<property name="icon"> <property name="icon">
<iconset resource="../../resources/resources.qrc"> <iconset>
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</iconset> <normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
@@ -227,7 +235,7 @@
</action> </action>
<action name="actionDelete_Object"> <action name="actionDelete_Object">
<property name="icon"> <property name="icon">
<iconset resource="../../resources/resources.qrc"> <iconset>
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</iconset> <normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
@@ -318,9 +326,7 @@
<container>1</container> <container>1</container>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources/>
<include location="../../resources/resources.qrc"/>
</resources>
<connections> <connections>
<connection> <connection>
<sender>actionExit</sender> <sender>actionExit</sender>

View File

@@ -16,8 +16,9 @@ using namespace Qtk;
QtkScene::QtkScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) { QtkScene::QtkScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) {
setSceneName("Qtk Scene"); setSceneName("Qtk Scene");
getCamera().getTransform().setTranslation(0.0f, 0.0f, 20.0f); getCamera().setTranslation({0.0f, 0.0f, 20.0f});
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); getCamera().setRotation(
QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -5.0f));
} }
QtkScene::~QtkScene() { QtkScene::~QtkScene() {
@@ -38,11 +39,12 @@ void QtkScene::init() {
/* Create a red cube with a mini master chief on top. */ /* Create a red cube with a mini master chief on top. */
auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS)); auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS));
myCube->setColor(RED); myCube->setColor(RED);
myCube->getTransform().setTranslation(5.0f, 0.0f, 0.0f);
addObject(myCube); addObject(myCube);
auto mySpartan = auto mySpartan =
new Model("My spartan", ":/models/models/spartan/spartan.obj"); new Model("My spartan", ":/models/models/spartan/spartan.obj");
mySpartan->getTransform().setTranslation(0.0f, 0.5f, 0.0f); mySpartan->getTransform().setTranslation(5.0f, 0.5f, 0.0f);
mySpartan->getTransform().setScale(0.5f); mySpartan->getTransform().setScale(0.5f);
addObject(mySpartan); addObject(mySpartan);
@@ -88,7 +90,7 @@ void QtkScene::init() {
model->getTransform().setTranslation(2.0f, 2.0f, -10.0f); model->getTransform().setTranslation(2.0f, 2.0f, -10.0f);
// Sometimes the models are very large // Sometimes the models are very large
model->getTransform().scale(0.0025f); model->getTransform().scale(0.0025f);
model->getTransform().rotate(-110.0f, 0.0f, 1.0f, 0.0f); model->getTransform().rotate(0.0f, 1.0f, 0.0f, -110.0f);
model = addObject( model = addObject(
new Qtk::Model("alien", ":/models/models/alien-hominid/alien.obj")); new Qtk::Model("alien", ":/models/models/alien-hominid/alien.obj"));
@@ -98,8 +100,8 @@ void QtkScene::init() {
model = addObject( model = addObject(
new Qtk::Model("My scythe", ":/models/models/scythe/scythe.obj")); new Qtk::Model("My scythe", ":/models/models/scythe/scythe.obj"));
model->getTransform().setTranslation(-6.0f, 0.0f, -10.0f); model->getTransform().setTranslation(-6.0f, 0.0f, -10.0f);
model->getTransform().rotate(-90.0f, 1.0f, 0.0f, 0.0f); model->getTransform().rotate(1.0f, 0.0f, 0.0f, -90.0f);
model->getTransform().rotate(90.0f, 0.0f, 1.0f, 0.0f); model->getTransform().rotate(0.0f, 1.0f, 0.0f, 90.0f);
model = addObject( model = addObject(
new Qtk::Model("masterChief", ":/models/models/spartan/spartan.obj")); new Qtk::Model("masterChief", ":/models/models/spartan/spartan.obj"));
@@ -330,14 +332,14 @@ void QtkScene::init() {
mesh->reallocateNormals(mesh->getNormals()); mesh->reallocateNormals(mesh->getNormals());
mesh->reallocateTexCoords(mesh->getTexCoords(), 3); mesh->reallocateTexCoords(mesh->getTexCoords(), 3);
mesh->releaseShaders(); mesh->releaseShaders();
mesh->getTransform().rotate(45.0f, 0.0f, 1.0f, 0.0f); mesh->getTransform().rotate(0.0f, 1.0f, 0.0f, 45.0f);
// Texturing a cube using a cube map // Texturing a cube using a cube map
// + Cube map texturing works with both QTK_DRAW_ARRAYS and QTK_DRAW_ELEMENTS // + Cube map texturing works with both QTK_DRAW_ARRAYS and QTK_DRAW_ELEMENTS
mesh = mesh =
addObject(new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS))); addObject(new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(-3.0f, 1.0f, -2.0f); mesh->getTransform().setTranslation(-3.0f, 1.0f, -2.0f);
mesh->getTransform().setRotation(45.0f, 0.0f, 1.0f, 0.0f); mesh->getTransform().setRotation(0.0f, 1.0f, 0.0f, 45.0f);
mesh->setShaders( mesh->setShaders(
":/shaders/texture-cubemap.vert", ":/shaders/texture-cubemap.frag"); ":/shaders/texture-cubemap.vert", ":/shaders/texture-cubemap.frag");
mesh->setCubeMap(":/textures/crate.png"); mesh->setCubeMap(":/textures/crate.png");
@@ -438,10 +440,10 @@ void QtkScene::draw() {
void QtkScene::update() { void QtkScene::update() {
auto mySpartan = Model::getInstance("My spartan"); auto mySpartan = Model::getInstance("My spartan");
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f); mySpartan->getTransform().rotate(0.0f, 1.0f, 0.0f, 0.75f);
auto myCube = MeshRenderer::getInstance("My cube"); auto myCube = MeshRenderer::getInstance("My cube");
myCube->getTransform().rotate(-0.75f, 0.0f, 1.0f, 0.0f); myCube->getTransform().rotate(-0.0f, 1.0f, 0.0f, 0.75f);
auto position = MeshRenderer::getInstance("alienTestLight") auto position = MeshRenderer::getInstance("alienTestLight")
->getTransform() ->getTransform()
@@ -455,7 +457,7 @@ void QtkScene::update() {
alien->setUniform("uMVP.model", posMatrix); alien->setUniform("uMVP.model", posMatrix);
alien->setUniform("uMVP.view", QtkScene::getCamera().toMatrix()); alien->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
alien->setUniform("uMVP.projection", QtkScene::getProjectionMatrix()); alien->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f); alien->getTransform().rotate(0.0f, 1.0f, 0.0f, 0.75f);
position = MeshRenderer::getInstance("spartanTestLight") position = MeshRenderer::getInstance("spartanTestLight")
->getTransform() ->getTransform()
@@ -469,10 +471,10 @@ void QtkScene::update() {
spartan->setUniform("uMVP.model", posMatrix); spartan->setUniform("uMVP.model", posMatrix);
spartan->setUniform("uMVP.view", QtkScene::getCamera().toMatrix()); spartan->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
spartan->setUniform("uMVP.projection", QtkScene::getProjectionMatrix()); spartan->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f); spartan->getTransform().rotate(0.0f, 1.0f, 0.0f, 0.75f);
auto phong = MeshRenderer::getInstance("testPhong"); auto phong = MeshRenderer::getInstance("testPhong");
phong->getTransform().rotate(0.75f, 1.0f, 0.5f, 0.0f); phong->getTransform().rotate(1.0f, 0.5f, 0.0f, 0.75f);
phong->bindShaders(); phong->bindShaders();
position = position =
MeshRenderer::getInstance("testLight")->getTransform().getTranslation(); MeshRenderer::getInstance("testLight")->getTransform().getTranslation();
@@ -487,27 +489,27 @@ void QtkScene::update() {
phong->releaseShaders(); phong->releaseShaders();
// Rotate lighting example cubes // Rotate lighting example cubes
mTestPhong->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f); mTestPhong->getTransform().rotate(0.5f, 0.3f, 0.2f, 0.75f);
MeshRenderer::getInstance("noLight")->getTransform().rotate( MeshRenderer::getInstance("noLight")->getTransform().rotate(
0.75f, 0.5f, 0.3f, 0.2f); 0.5f, 0.3f, 0.2f, 0.75f);
mTestAmbient->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f); mTestAmbient->getTransform().rotate(0.5f, 0.3f, 0.2f, 0.75f);
mTestDiffuse->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f); mTestDiffuse->getTransform().rotate(0.5f, 0.3f, 0.2f, 0.75f);
mTestSpecular->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f); mTestSpecular->getTransform().rotate(0.5f, 0.3f, 0.2f, 0.75f);
// Examples of various translations and rotations // Examples of various translations and rotations
// Rotate in multiple directions simultaneously // Rotate in multiple directions simultaneously
MeshRenderer::getInstance("rgbNormalsCube") MeshRenderer::getInstance("rgbNormalsCube")
->getTransform() ->getTransform()
.rotate(0.75f, 0.2f, 0.4f, 0.6f); .rotate(0.2f, 0.4f, 0.6f, 0.75f);
// Pitch forward and roll sideways // Pitch forward and roll sideways
MeshRenderer::getInstance("leftTriangle") MeshRenderer::getInstance("leftTriangle")
->getTransform() ->getTransform()
.rotate(0.75f, 1.0f, 0.0f, 0.0f); .rotate(1.0f, 0.0f, 0.0f, 0.75f);
MeshRenderer::getInstance("rightTriangle") MeshRenderer::getInstance("rightTriangle")
->getTransform() ->getTransform()
.rotate(0.75f, 0.0f, 0.0f, 1.0f); .rotate(0.0f, 0.0f, 1.0f, 0.75f);
// Move between two positions over time // Move between two positions over time
static float translateX = 0.025f; static float translateX = 0.025f;
@@ -528,15 +530,15 @@ void QtkScene::update() {
// And lets rotate the triangles in two directions at once // And lets rotate the triangles in two directions at once
MeshRenderer::getInstance("topTriangle") MeshRenderer::getInstance("topTriangle")
->getTransform() ->getTransform()
.rotate(0.75f, 0.2f, 0.0f, 0.4f); .rotate(0.2f, 0.0f, 0.4f, 0.75f);
MeshRenderer::getInstance("bottomTriangle") MeshRenderer::getInstance("bottomTriangle")
->getTransform() ->getTransform()
.rotate(0.75f, 0.0f, 0.2f, 0.4f); .rotate(0.0f, 0.2f, 0.4f, 0.75f);
// And make the bottom triangle green, instead of RGB // And make the bottom triangle green, instead of RGB
// Rotate center cube in several directions simultaneously // Rotate center cube in several directions simultaneously
// + Not subject to gimbal lock since we are using quaternions :) // + Not subject to gimbal lock since we are using quaternions :)
MeshRenderer::getInstance("centerCube") MeshRenderer::getInstance("centerCube")
->getTransform() ->getTransform()
.rotate(0.75f, 0.2f, 0.4f, 0.6f); .rotate(0.2f, 0.4f, 0.6f, 0.75f);
} }

View File

@@ -7,6 +7,7 @@
##############################################################################*/ ##############################################################################*/
#include <QKeyEvent> #include <QKeyEvent>
#include <QMimeData>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <qtk/input.h> #include <qtk/input.h>
@@ -14,6 +15,7 @@
#include <qtk/shape.h> #include <qtk/shape.h>
#include "debugconsole.h" #include "debugconsole.h"
#include "qtk/qtkmessagelogger.h"
#include "qtkmainwindow.h" #include "qtkmainwindow.h"
#include "qtkwidget.h" #include "qtkwidget.h"
@@ -29,8 +31,10 @@ QtkWidget::QtkWidget(QWidget * parent, const QString & name) :
QtkWidget(parent, name, Q_NULLPTR) {} QtkWidget(parent, name, Q_NULLPTR) {}
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(new QOpenGLDebugLogger(this)),
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR) { mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR),
mLogger(new Qtk::Logger(this)) {
setAcceptDrops(true);
setScene(scene); setScene(scene);
setObjectName(name); setObjectName(name);
QSurfaceFormat format; QSurfaceFormat format;
@@ -70,14 +74,22 @@ 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);
if(mDebugLogger->initialize()) { if(mDebugLogger->initialize()) {
qDebug() << "GL_DEBUG Debug Logger" << mDebugLogger << "\n";
connect( connect(
mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this, mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), mLogger,
SLOT(messageLogged(QOpenGLDebugMessage))); SLOT(log(QOpenGLDebugMessage)));
// connect(
// Qtk::QtkMessageLogger::get(),
// &Qtk::QtkMessageLogger::messageLogged, mLogger,
// &Qtk::Logger::parseError);
mDebugLogger->startLogging(); mDebugLogger->startLogging();
QString msg;
QTextStream stream(&msg);
stream << "Logging started on GL_DEBUG Debug Logger: " << mDebugLogger;
mDebugLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(
stream.string()->toStdString().c_str()));
} }
printContextInformation(); printContextInformation();
@@ -107,11 +119,11 @@ void QtkWidget::paintGL() {
} }
} }
void QtkWidget::setScene(Qtk::Scene * scene) { void QtkWidget::setScene(Scene * scene) {
if(mScene != Q_NULLPTR) { if(mScene != Q_NULLPTR) {
delete mScene; delete mScene;
connect( connect(
scene, &Qtk::Scene::sceneUpdated, MainWindow::getMainWindow(), scene, &Scene::sceneUpdated, MainWindow::getMainWindow(),
&MainWindow::refreshScene); &MainWindow::refreshScene);
} }
@@ -129,8 +141,7 @@ void QtkWidget::toggleConsole() {
mConsoleActive = false; mConsoleActive = false;
} else { } else {
MainWindow::getMainWindow()->addDockWidget( MainWindow::getMainWindow()->addDockWidget(
Qt::DockWidgetArea::BottomDockWidgetArea, Qt::DockWidgetArea::BottomDockWidgetArea, mConsole);
dynamic_cast<QDockWidget *>(mConsole));
mConsole->setHidden(false); mConsole->setHidden(false);
mConsoleActive = true; mConsoleActive = true;
} }
@@ -140,6 +151,34 @@ void QtkWidget::toggleConsole() {
* Protected Methods * Protected Methods
******************************************************************************/ ******************************************************************************/
void QtkWidget::dragEnterEvent(QDragEnterEvent * event) {
if(event->mimeData()->hasFormat("text/plain")) {
event->acceptProposedAction();
}
}
void QtkWidget::dropEvent(QDropEvent * event) {
mConsole->sendLog(event->mimeData()->text());
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
@@ -175,74 +214,6 @@ void QtkWidget::update() {
QWidget::update(); QWidget::update();
} }
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
QString error;
DebugContext context;
// Format based on severity
switch(msg.severity()) {
case QOpenGLDebugMessage::NotificationSeverity:
error += "--";
context = Status;
break;
case QOpenGLDebugMessage::HighSeverity:
error += "!!";
context = Fatal;
break;
case QOpenGLDebugMessage::MediumSeverity:
error += "!~";
context = Error;
break;
case QOpenGLDebugMessage::LowSeverity:
error += "~~";
context = Warn;
break;
}
error += " (";
// Format based on source
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error += #c; \
break
switch(msg.source()) {
CASE(APISource);
CASE(WindowSystemSource);
CASE(ShaderCompilerSource);
CASE(ThirdPartySource);
CASE(ApplicationSource);
CASE(OtherSource);
CASE(InvalidSource);
}
#undef CASE
error += " : ";
// Format based on type
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error += #c; \
break
switch(msg.type()) {
CASE(InvalidType);
CASE(ErrorType);
CASE(DeprecatedBehaviorType);
CASE(UndefinedBehaviorType);
CASE(PortabilityType);
CASE(PerformanceType);
CASE(OtherType);
CASE(MarkerType);
CASE(GroupPushType);
CASE(GroupPopType);
}
#undef CASE
error += ")\n" + msg.message() + "\n";
qDebug() << qPrintable(error);
sendLog("(OpenGL) " + error.replace("\n", "\n(OpenGL) "), context);
}
/******************************************************************************* /*******************************************************************************
* Private Methods * Private Methods
******************************************************************************/ ******************************************************************************/
@@ -253,15 +224,16 @@ void QtkWidget::teardownGL() { /* Nothing to teardown yet... */
void QtkWidget::updateCameraInput() { void QtkWidget::updateCameraInput() {
Input::update(); Input::update();
// Camera Transformation // Camera Transformation
if(Input::buttonPressed(Qt::RightButton)) { if(Input::buttonPressed(Qt::LeftButton)
|| Input::buttonPressed(Qt::RightButton)) {
static const float transSpeed = 0.1f; static const float transSpeed = 0.1f;
static const float rotSpeed = 0.5f; static const float rotSpeed = 0.5f;
// Handle rotations // Handle rotations
Scene::getCamera().getTransform().rotate( Scene::getCamera().rotate(QQuaternion::fromAxisAndAngle(
-rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp); Camera3D::LocalUp, -rotSpeed * Input::mouseDelta().x()));
Scene::getCamera().getTransform().rotate( Scene::getCamera().rotate(QQuaternion::fromAxisAndAngle(
-rotSpeed * Input::mouseDelta().y(), Scene::getCamera().getRight()); Scene::getCamera().getRight(), -rotSpeed * Input::mouseDelta().y()));
// Handle translations // Handle translations
QVector3D translation; QVector3D translation;
@@ -283,7 +255,7 @@ void QtkWidget::updateCameraInput() {
if(Input::keyPressed(Qt::Key_E)) { if(Input::keyPressed(Qt::Key_E)) {
translation += Scene::getCamera().getUp() / 2.0f; translation += Scene::getCamera().getUp() / 2.0f;
} }
Scene::getCamera().getTransform().translate(transSpeed * translation); Scene::getCamera().translate(transSpeed * translation);
} }
} }
@@ -316,6 +288,5 @@ void QtkWidget::printContextInformation() {
auto message = QString(glType) + glVersion + "(" + glProfile + ")" auto message = QString(glType) + glVersion + "(" + glProfile + ")"
+ "\nOpenGL Vendor: " + glVendor + "\nOpenGL Vendor: " + glVendor
+ "\nRendering Device: " + glRenderer; + "\nRendering Device: " + glRenderer;
qDebug() << qPrintable(message); emit sendLog("(OpenGL) " + message.replace("\n", "\n(OpenGL) "), Status);
sendLog("(OpenGL) " + message.replace("\n", "\n(OpenGL) "), Status);
} }

View File

@@ -19,6 +19,7 @@
#include <qtk/qtkapi.h> #include <qtk/qtkapi.h>
#include <qtk/scene.h> #include <qtk/scene.h>
#include "logger.h"
namespace Qtk { namespace Qtk {
class DebugConsole; class DebugConsole;
@@ -107,6 +108,8 @@ namespace Qtk {
return mDebugLogger; return mDebugLogger;
} }
inline Logger * getLogger() { return mLogger; }
/************************************************************************* /*************************************************************************
* Setters * Setters
************************************************************************/ ************************************************************************/
@@ -129,13 +132,21 @@ namespace Qtk {
* @param message The message to log. * @param message The message to log.
* @param context The context of the log message. * @param context The context of the log message.
*/ */
void sendLog(const QString & message, DebugContext context = Status); void sendLog(const QString & message, Qtk::DebugContext context = Status);
// TODO: Use this signal in treeview and toolbox to update object
// properties
void objectFocusChanged(const QString objectName);
protected: protected:
/************************************************************************* /*************************************************************************
* Protected Methods * Protected Methods
************************************************************************/ ************************************************************************/
void dragEnterEvent(QDragEnterEvent * event) override;
void dropEvent(QDropEvent * event) override;
/** /**
* @param event Key press event to update camera input manager. * @param event Key press event to update camera input manager.
*/ */
@@ -166,10 +177,11 @@ 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.
*/ */
void messageLogged(const QOpenGLDebugMessage & msg); // void messageLogged(const QOpenGLDebugMessage & msg);
private: private:
/************************************************************************* /*************************************************************************
@@ -195,6 +207,7 @@ namespace Qtk {
* Private Members * Private Members
************************************************************************/ ************************************************************************/
Qtk::Logger * mLogger;
QOpenGLDebugLogger * mDebugLogger; QOpenGLDebugLogger * mDebugLogger;
Qtk::Scene * mScene; Qtk::Scene * mScene;
Qtk::DebugConsole * mConsole; Qtk::DebugConsole * mConsole;

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

View File

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

View File

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

View File

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

@@ -24,6 +24,8 @@ set(
skybox.h skybox.h
texture.h texture.h
transform3D.h transform3D.h
qtkmessagelogger.h
../app/logger.h
) )
set( set(
@@ -41,6 +43,8 @@ set(
skybox.cpp skybox.cpp
texture.cpp texture.cpp
transform3D.cpp transform3D.cpp
qtkmessagelogger.cpp
../app/logger.cpp
) )
qt6_add_big_resources(QTK_LIBRARY_SOURCES "${QTK_RESOURCES}/resources.qrc") qt6_add_big_resources(QTK_LIBRARY_SOURCES "${QTK_RESOURCES}/resources.qrc")
@@ -68,9 +72,9 @@ target_link_libraries(
Qt6::Core Qt6::OpenGLWidgets Qt6::Widgets 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) target_link_libraries(qtk_library PUBLIC assimp)
elseif(ASSIMP_NEW_INTERFACE) elseif(QTK_ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk_library PUBLIC assimp::assimp) target_link_libraries(qtk_library PUBLIC assimp::assimp)
endif() endif()

View File

@@ -29,28 +29,3 @@ const QMatrix4x4 & Camera3D::toMatrix() {
mWorld.translate(-mTransform.getTranslation()); mWorld.translate(-mTransform.getTranslation());
return mWorld; return mWorld;
} }
/*******************************************************************************
* Qt Streams
******************************************************************************/
QDataStream & operator<<(QDataStream & out, Camera3D & transform) {
out << transform.getTransform();
return out;
}
QDataStream & operator>>(QDataStream & in, Camera3D & transform) {
in >> transform.getTransform();
return in;
}
QDebug operator<<(QDebug dbg, const Camera3D & transform) {
dbg << "Camera3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
return dbg;
}

View File

@@ -32,7 +32,9 @@ namespace Qtk {
/** /**
* @return Transform3D associated with this camera. * @return Transform3D associated with this camera.
*/ */
inline Transform3D & getTransform() { return mTransform; } [[nodiscard]] inline const Transform3D & getTransform() const {
return mTransform;
}
/** /**
* @return Current translation of the camera as a QVector3D. * @return Current translation of the camera as a QVector3D.
@@ -78,33 +80,85 @@ namespace Qtk {
*/ */
const QMatrix4x4 & toMatrix(); const QMatrix4x4 & toMatrix();
/**
* Set the translation for this camera.
* TODO: Replace these methods by inheriting from a base class.
*
* @param translation QVector3D for the new translation.
*/
inline void setTranslation(const QVector3D & translation) {
mTransform.setTranslation(translation);
}
/**
* Set the rotation for this camera.
*
* @param rotation QQuaternion for the new rotation.
*/
inline void setRotation(const QQuaternion & rotation) {
mTransform.setRotation(rotation);
}
/**
* Sets a rotation upon an axis represented by the 3D vector (x, y, z)
*
* @param ax X axis to set angle for.
* @param ay Y axis to set angle for.
* @param az Z axis to set angle for.
* @param angle Angle to set rotation.
*/
inline void setRotation(float ax, float ay, float az, float angle) {
mTransform.setRotation(ax, ay, az, angle);
}
/**
* Translate the camera by the given position.
*
* @param position QVector3D for the position to translate by.
*/
inline void translate(const QVector3D & position) {
mTransform.translate(position);
}
/**
* Rotate the camera by the given rotation.
*
* @param rotation QQaternion for the rotation to apply.
*/
inline void rotate(const QQuaternion & rotation) {
mTransform.rotate(rotation);
}
/**
* Sets a rotation upon an axis represented by the 3D vector (x, y, z)
*
* @param ax X axis to set angle for.
* @param ay Y axis to set angle for.
* @param az Z axis to set angle for.
* @param angle Angle to set rotation.
*/
inline void rotate(float ax, float ay, float az, float angle) {
mTransform.rotate(ax, ay, az, angle);
}
private: private:
/*************************************************************************
* Private Methods
************************************************************************/
#ifndef QT_NO_DATASTREAM
friend QDataStream & operator<<(QDataStream & out, Camera3D & transform);
friend QDataStream & operator>>(QDataStream & in, Camera3D & transform);
#endif
/************************************************************************* /*************************************************************************
* Private Members * Private Members
************************************************************************/ ************************************************************************/
Transform3D mTransform; Transform3D mTransform;
QMatrix4x4 mWorld; QMatrix4x4 mWorld;
};
// Qt Streams /*************************************************************************
* Qt Streams
************************************************************************/
#ifndef QT_NO_DATASTREAM #ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Camera3D & transform); friend QDataStream & operator<<(
QDataStream & operator>>(QDataStream & in, Camera3D & transform); QDataStream & out, const Camera3D & camera);
#endif friend QDataStream & operator>>(QDataStream & in, Camera3D & camera);
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Camera3D & transform);
#endif #endif
};
} // namespace Qtk } // namespace Qtk
Q_DECLARE_TYPEINFO(Qtk::Camera3D, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(Qtk::Camera3D, Q_MOVABLE_TYPE);

View File

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

View File

@@ -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(texture); mTexturesLoaded.push_back(textures.back());
} }
} }

View File

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

View File

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

View File

@@ -22,6 +22,8 @@
#define QTKAPI #define QTKAPI
#endif #endif
#include "qtk/qtkmessagelogger.h"
/** /**
* Initialize Qt resources required by the Qtk library. * Initialize Qt resources required by the Qtk library.
* This cannot be defined within any namespace, but can be called by ctors. * This cannot be defined within any namespace, but can be called by ctors.
@@ -35,7 +37,7 @@ namespace Qtk {
/** /**
* Flag to set context for debug messages. * Flag to set context for debug messages.
*/ */
enum DebugContext { Status, Debug, Warn, Error, Fatal }; enum DebugContext { Status, Debug, Warn, Error, Fatal, Invalid, Any };
/** /**
* Find top level parent for a widget. * Find top level parent for a widget.

View File

@@ -17,17 +17,23 @@ 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 read = mode.contains('r'); bool open = false;
bool write = mode.contains('w'); if(mode == "w" || mode == "wb") {
if(read && write) { open = mFile.open(QIODeviceBase::WriteOnly);
mFile.open(QIODevice::ReadWrite); } else if(mode == "r" || mode == "rb") {
} else if(read) { open = mFile.open(QIODeviceBase::ReadOnly);
mFile.open(QIODevice::ReadOnly); } else if(mode == "wt") {
} else if(write) { open = mFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text);
mFile.open(QIODevice::WriteOnly); } else if(mode == "rt") {
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";
}
} }
/******************************************************************************* /*******************************************************************************
@@ -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 QtkIOStream::Read(void * pvBuffer, size_t pSize, size_t pCount) {
size_t read = 0; qint64 readSize = mFile.read((char *)pvBuffer, pSize * pCount);
do { if(readSize < 0) {
auto readSize = mFile.read((char *)pvBuffer + read, pSize); qDebug() << "[Qtk::QtkIOStream] Failed to read (" << pSize
if(readSize < 0) { << ") bytes from file at: " << mFile.filesystemFileName().c_str()
qDebug() << "[Qtk::QtkIOStream] Failed to read (" << pSize << "\n";
<< ") bytes from file at: " << mFile.filesystemFileName().c_str() return -1;
<< "\n"; }
return -1; return readSize;
}
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) {
size_t wrote = 0; qint64 writeSize = mFile.write((char *)pvBuffer, pSize * pCount);
do { if(writeSize < 0) {
auto writeSize = mFile.write((char *)pvBuffer + wrote, pSize); qDebug() << "[Qtk::QtkIOStream] Failed to write buffer with size (" << pSize
if(writeSize < 0) { << ") to file at: " << mFile.filesystemFileName().c_str() << "\n";
qDebug() << "[Qtk::QtkIOStream] Failed to write buffer with size (" return -1;
<< pSize }
<< ") to file at: " << mFile.filesystemFileName().c_str() return writeSize;
<< "\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,6 +7,7 @@
##############################################################################*/ ##############################################################################*/
#include "qtkiosystem.h" #include "qtkiosystem.h"
#include <QDir>
using namespace Qtk; using namespace Qtk;
@@ -19,15 +20,11 @@ bool QtkIOSystem::Exists(const char * pFile) const {
} }
char QtkIOSystem::getOsSeparator() const { char QtkIOSystem::getOsSeparator() const {
#ifndef _WIN32 return QDir::separator().toLatin1();
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(!QFileInfo::exists(pFile)) { if(!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

@@ -0,0 +1,61 @@
#include "qtkmessagelogger.h"
#include "camera3d.h"
#include "transform3D.h"
Qtk::QtkDebug Qtk::QtkDebug::operator<<(const Qtk::Transform3D & transform) {}
#ifndef QT_NO_DEBUG_STREAM
QDebug Qtk::operator<<(QDebug dbg, const Qtk::Transform3D & transform) {
dbg << "Transform3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Scale: <" << transform.getScale().x() << ", "
<< transform.getScale().y() << ", " << transform.getScale().z() << ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
return dbg;
}
QDebug Qtk::operator<<(QDebug dbg, const Qtk::Camera3D & transform) {
dbg << "Camera3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
return dbg;
}
#endif
#ifndef QT_NO_DATASTREAM
QDataStream & Qtk::operator<<(
QDataStream & out, const Qtk::Transform3D & transform) {
out << transform.mTranslation;
out << transform.mScale;
out << transform.mRotation;
return out;
}
QDataStream & Qtk::operator>>(QDataStream & in, Qtk::Transform3D & transform) {
in >> transform.mTranslation;
in >> transform.mScale;
in >> transform.mRotation;
transform.m_dirty = true;
return in;
}
QDataStream & Qtk::operator<<(QDataStream & out, const Qtk::Camera3D & camera) {
out << camera.getTransform();
return out;
}
QDataStream & Qtk::operator>>(QDataStream & in, Qtk::Camera3D & camera) {
in >> camera.mTransform;
return in;
}
#endif

View File

@@ -0,0 +1,96 @@
#ifndef QTK_QTKMESSAGELOGGER_H
#define QTK_QTKMESSAGELOGGER_H
#include "qtkapi.h"
#include <QDebug>
#include <QMessageLogger>
#include <Qt>
// #define qtkDebug Qtk::QtkMessageLogger::get().debug
// #define qtkInfo Qtk::QtkMessageLogger::get().info
// #define qtkWarning Qtk::QtkMessageLogger::get().warning
// #define qtkCritical Qtk::QtkMessageLogger::get().critical
// #define qtkFatal Qtk::QtkMessageLogger::get().fatal
#define qtkDebug \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.debug
#define qtkInfo \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.info
#define qtkWarning \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.warning
#define qtkCritical \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.critical
#define qtkFatal \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.fatal
namespace Qtk {
class Transform3D;
class Camera3D;
class QTKAPI QtkDebug : public QDebug {
public:
QtkDebug operator<<(const Transform3D & transform);
};
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform);
QDebug operator<<(QDebug dbg, const Camera3D & camera);
#endif
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Transform3D & transform);
QDataStream & operator>>(QDataStream & in, Transform3D & transform);
QDataStream & operator<<(QDataStream & out, const Camera3D & camera);
QDataStream & operator>>(QDataStream & in, Camera3D & camera);
#endif
class QTKAPI QtkMessageLogger {
public:
QtkMessageLogger(const char * file, int line, const char * function) :
mQMessageLogger(file, line, function) {
qDebug();
}
// static QtkMessageLogger * get() {
// if(mQtkMessageLogger == Q_NULLPTR) {
// mQtkMessageLogger = new QtkMessageLogger();
// }
// return mQtkMessageLogger;
// }
// QtkDebug debug() const {}
[[nodiscard]] inline const QMessageLogger & logger() const {
return mQMessageLogger;
}
inline QtkDebug debug() const { return {}; };
static QtkMessageLogger * mQtkMessageLogger;
QMessageLogger mQMessageLogger;
};
} // namespace Qtk
#endif // QTK_QTKMESSAGELOGGER_H

View File

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

View File

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

View File

@@ -9,19 +9,18 @@
#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(512); QImageReader::setAllocationLimit(1024);
auto loadedImage = new QImage(QImage(image).mirrored(flipX, flipY)); auto loadedImage = QImage(image).mirrored(flipX, flipY);
if(loadedImage->isNull()) { if(loadedImage.isNull()) {
qDebug() << "[Qtk::OpenGLTextureFactory] Error loading image: " << image return defaultTexture();
<< "\nSupported types: " << QImageReader::supportedImageFormats();
return Q_NULLPTR;
} }
return loadedImage; return loadedImage;
@@ -29,13 +28,12 @@ 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;
} }
@@ -71,6 +69,7 @@ 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 Pointer to an initialized QImage object. * @return 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,6 +132,14 @@ 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;

View File

@@ -42,6 +42,7 @@ void Transform3D::rotate(const QQuaternion & dr) {
void Transform3D::setTranslation(const QVector3D & t) { void Transform3D::setTranslation(const QVector3D & t) {
m_dirty = true; m_dirty = true;
qtkDebug() << "Setting translation to " << t;
mTranslation = t; mTranslation = t;
} }
@@ -77,45 +78,3 @@ QVector3D Transform3D::getUp() const {
QVector3D Transform3D::getRight() const { QVector3D Transform3D::getRight() const {
return mRotation.rotatedVector(LocalRight); return mRotation.rotatedVector(LocalRight);
} }
/*******************************************************************************
* Private Methods
******************************************************************************/
namespace Qtk {
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform) {
dbg << "Transform3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Scale: <" << transform.getScale().x() << ", "
<< transform.getScale().y() << ", " << transform.getScale().z()
<< ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
return dbg;
}
#endif
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Transform3D & transform) {
out << transform.mTranslation;
out << transform.mScale;
out << transform.mRotation;
return out;
}
QDataStream & operator>>(QDataStream & in, Transform3D & transform) {
in >> transform.mTranslation;
in >> transform.mScale;
in >> transform.mRotation;
transform.m_dirty = true;
return in;
}
#endif
} // namespace Qtk

View File

@@ -116,12 +116,12 @@ namespace Qtk {
/** /**
* Apply rotation upon an axis represented by the 3D vector (x, y, z) * Apply rotation upon an axis represented by the 3D vector (x, y, z)
* *
* @param angle Angle to rotate.
* @param ax X axis to apply the rotation on. * @param ax X axis to apply the rotation on.
* @param ay Y axis to apply the rotation on. * @param ay Y axis to apply the rotation on.
* @param az Z axis to apply the rotation on. * @param az Z axis to apply the rotation on.
* @param angle Angle to rotate.
*/ */
inline void rotate(float angle, float ax, float ay, float az) { inline void rotate(float ax, float ay, float az, float angle) {
rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle)); rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
} }
@@ -178,12 +178,12 @@ namespace Qtk {
/** /**
* Sets a rotation upon an axis represented by the 3D vector (x, y, z) * Sets a rotation upon an axis represented by the 3D vector (x, y, z)
* *
* @param angle Angle to set rotation.
* @param ax X axis to set angle for. * @param ax X axis to set angle for.
* @param ay Y axis to set angle for. * @param ay Y axis to set angle for.
* @param az Z axis to set angle for. * @param az Z axis to set angle for.
* @param angle Angle to set rotation.
*/ */
inline void setRotation(float angle, float ax, float ay, float az) { inline void setRotation(float ax, float ay, float az, float angle) {
setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle)); setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
} }
@@ -249,6 +249,10 @@ namespace Qtk {
bool m_dirty; bool m_dirty;
/*************************************************************************
* Qt Streams
************************************************************************/
#ifndef QT_NO_DATASTREAM #ifndef QT_NO_DATASTREAM
friend QDataStream & operator<<( friend QDataStream & operator<<(
QDataStream & out, const Transform3D & transform); QDataStream & out, const Transform3D & transform);
@@ -256,15 +260,6 @@ namespace Qtk {
QDataStream & in, Transform3D & transform); QDataStream & in, Transform3D & transform);
#endif #endif
}; };
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform);
#endif
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Transform3D & transform);
QDataStream & operator>>(QDataStream & in, Transform3D & transform);
#endif
} // namespace Qtk } // namespace Qtk
Q_DECLARE_TYPEINFO(Qtk::Transform3D, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(Qtk::Transform3D, Q_MOVABLE_TYPE);