From 194888ed1901165677202cdd011abfc9ffca9a88 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sun, 18 Dec 2022 09:19:04 -0500 Subject: [PATCH] Refactor build system and UI + Install configs for Qt Designer plugins and Qtk application + Add Qtk plugin collection for Qt Designer + QtkWidget plugin + TreeView widget plugin + DebugConsole widget plugin + All widgets are fully integrated with Qt Designer + All widgets can be popped out or docked within the window + All widgets can be stacked to use tab view for side panels + All widgets can be toggled on/off through the view context menu + QtkWidget debug console + QtkWidget active scene TreeVew + QtkWidget dockable tool bar + Double-click an object name in the TreeView to focus camera + Separate libaray from widgets There is still a lot to do here, but the major refactoring should be done after this commit. Some of the new features were put together as POC for working with the new UI. A few placeholder buttons were added that have no functionality. --- .gitignore | 1 + CMakeLists.txt | 246 +++++-------- app/mainwindow.cpp | 39 -- app/mainwindow.ui | 114 ------ app/resourcemanager.h | 48 --- src/CMakeLists.txt | 23 ++ src/app/CMakeLists.txt | 95 +++++ src/app/debugconsole.cpp | 44 +++ src/app/debugconsole.h | 129 +++++++ src/app/debugconsole.ui | 33 ++ {app => src/app}/examplescene.cpp | 371 ++++++++++---------- {app => src/app}/examplescene.h | 8 +- {app => src/app}/main.cpp | 10 +- src/app/qtkmainwindow.cpp | 52 +++ app/mainwindow.h => src/app/qtkmainwindow.h | 52 ++- src/app/qtkmainwindow.ui | 336 ++++++++++++++++++ src/{ => app}/qtkwidget.cpp | 114 ++++-- src/{ => app}/qtkwidget.h | 62 ++-- src/app/resources.h.in | 8 + src/{qtkapi.h => app/toolbox.cpp} | 27 +- src/app/toolbox.h | 33 ++ src/app/toolbox.ui | 56 +++ src/app/treeview.cpp | 58 +++ src/app/treeview.h | 61 ++++ src/app/treeview.ui | 44 +++ src/app/widgetplugin.cpp | 88 +++++ src/app/widgetplugin.h | 114 ++++++ src/app/widgetplugincollection.cpp | 34 ++ src/app/widgetplugincollection.h | 34 ++ src/qtk/CMakeLists.txt | 96 +++++ src/{ => qtk}/camera3d.cpp | 2 +- src/{ => qtk}/camera3d.h | 4 +- src/{ => qtk}/input.cpp | 2 +- src/{ => qtk}/input.h | 9 +- src/{ => qtk}/mesh.cpp | 2 +- src/{ => qtk}/mesh.h | 7 +- src/{ => qtk}/meshrenderer.cpp | 8 +- src/{ => qtk}/meshrenderer.h | 8 +- src/{ => qtk}/model.cpp | 12 +- src/{ => qtk}/model.h | 22 +- src/{ => qtk}/object.cpp | 2 +- src/{ => qtk}/object.h | 28 +- src/qtk/qtkapi.h | 49 +++ src/qtk/scene.cpp | 101 ++++++ src/{ => qtk}/scene.h | 83 ++++- src/{ => qtk}/skybox.cpp | 6 +- src/{ => qtk}/skybox.h | 8 +- src/{ => qtk}/texture.cpp | 7 +- src/{ => qtk}/texture.h | 7 +- src/{ => qtk}/transform3D.cpp | 2 +- src/{ => qtk}/transform3D.h | 4 +- src/qtkresources.h.in | 2 - src/scene.cpp | 53 --- 53 files changed, 2079 insertions(+), 779 deletions(-) delete mode 100644 app/mainwindow.cpp delete mode 100644 app/mainwindow.ui delete mode 100644 app/resourcemanager.h create mode 100644 src/CMakeLists.txt create mode 100644 src/app/CMakeLists.txt create mode 100644 src/app/debugconsole.cpp create mode 100644 src/app/debugconsole.h create mode 100644 src/app/debugconsole.ui rename {app => src/app}/examplescene.cpp (59%) rename {app => src/app}/examplescene.h (97%) rename {app => src/app}/main.cpp (79%) create mode 100644 src/app/qtkmainwindow.cpp rename app/mainwindow.h => src/app/qtkmainwindow.h (50%) create mode 100644 src/app/qtkmainwindow.ui rename src/{ => app}/qtkwidget.cpp (77%) rename src/{ => app}/qtkwidget.h (72%) create mode 100644 src/app/resources.h.in rename src/{qtkapi.h => app/toolbox.cpp} (60%) create mode 100644 src/app/toolbox.h create mode 100644 src/app/toolbox.ui create mode 100644 src/app/treeview.cpp create mode 100644 src/app/treeview.h create mode 100644 src/app/treeview.ui create mode 100644 src/app/widgetplugin.cpp create mode 100644 src/app/widgetplugin.h create mode 100644 src/app/widgetplugincollection.cpp create mode 100644 src/app/widgetplugincollection.h create mode 100644 src/qtk/CMakeLists.txt rename src/{ => qtk}/camera3d.cpp (98%) rename src/{ => qtk}/camera3d.h (98%) rename src/{ => qtk}/input.cpp (99%) rename src/{ => qtk}/input.h (90%) rename src/{ => qtk}/mesh.cpp (99%) rename src/{ => qtk}/mesh.h (99%) rename src/{ => qtk}/meshrenderer.cpp (97%) rename src/{ => qtk}/meshrenderer.h (98%) rename src/{ => qtk}/model.cpp (98%) rename src/{ => qtk}/model.h (95%) rename src/{ => qtk}/object.cpp (96%) rename src/{ => qtk}/object.h (88%) create mode 100644 src/qtk/qtkapi.h create mode 100644 src/qtk/scene.cpp rename src/{ => qtk}/scene.h (68%) rename src/{ => qtk}/skybox.cpp (98%) rename src/{ => qtk}/skybox.h (96%) rename src/{ => qtk}/texture.cpp (99%) rename src/{ => qtk}/texture.h (99%) rename src/{ => qtk}/transform3D.cpp (99%) rename src/{ => qtk}/transform3D.h (99%) delete mode 100644 src/qtkresources.h.in delete mode 100644 src/scene.cpp diff --git a/.gitignore b/.gitignore index ec5f059..4a6a3a8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # CMake build files **/cmake-build-debug/** **/build/** +install # C++ objects and libs *.slo diff --git a/CMakeLists.txt b/CMakeLists.txt index af0f0dc..c9f7a34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,15 +3,16 @@ ## ## ## Project for working with OpenGL and Qt6 widgets ## ################################################################################ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.23) -project( - #[[NAME]] Qtk - VERSION 1.0 - DESCRIPTION "An example project using QT and OpenGL" - LANGUAGES CXX C -) +################################################################################ +# Includes +################################################################################ +include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake") +################################################################################ +# Constants +################################################################################ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) @@ -23,207 +24,127 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") add_compile_options(/wd4131 /wd4127) endif() -message(STATUS "[Qtk] Compiling with ${CMAKE_CXX_COMPILER_ID}") -# Qtk build options -option(QTK_DEBUG "Enable debugger" ON) -message(STATUS "[Qtk] Compiling with QTK_DEBUG=${QTK_DEBUG}") -option(QTK_UPDATE_SUBMODULES "Update external project (assimp) submodule" ON) -message( - STATUS - "[Qtk] Compiling with QTK_UPDATE_SUBMODULES=${QTK_UPDATE_SUBMODULES}" +################################################################################ +# Project +################################################################################ +project( + #[[NAME]] Qtk + VERSION 1.0 + DESCRIPTION "An example project using QT and OpenGL" + LANGUAGES CXX C ) -# Qt options -set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/6.3.1/gcc_64/" CACHE PATH "Path to Qt6") +################################################################################ +# Options +################################################################################ +option(QTK_DEBUG "Enable debugger" ON) +option(QTK_UPDATE_SUBMODULES "Update external project (assimp) submodule" OFF) +option(QTK_SHARED "Build shared libraries" ON) +option(QTK_BUILD_GUI "Build the Qtk desktop application" ON) # Options for bringing your own assimp installation; Otherwise not needed # + If assimp is available system-wide we can just set QTK_UPDATE_SUBMODULES OFF option(ASSIMP_NEW_INTERFACE "Use the assimp::assimp interface (WIN / OSX)" OFF) -message( - STATUS - "[Qtk] Compiling with ASSIMP_NEW_INTERFACE=${ASSIMP_NEW_INTERFACE}" +message(STATUS + "[Qtk] ASSIMP_NEW_INTERFACE=${ASSIMP_NEW_INTERFACE}" ) -################################################################################ -# External Libraries -################################################################################ +set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/" CACHE PATH "Path to Qt6") +# Qt Designer will look in different locations if WIN / Unix. +# These paths are for using Qt Designer integrated within Qt Creator. +# Standalone Qt Designer may use different paths. +if (WIN32) + set(QT_PLUGIN_INSTALL_DIR + "${QT_DIR}/Tools/QtCreator/bin/plugins/designer" + ) + # This path may be different on windows. I have not tested this. + set(QT_PLUGIN_LIBRARY_DIR + "${QT_DIR}/Tools/QtCreator/lib/Qt/lib" + ) +else() + set(QT_PLUGIN_INSTALL_DIR + "${QT_DIR}/Tools/QtCreator/lib/Qt/plugins/designer" + ) + set(QT_PLUGIN_LIBRARY_DIR + "${QT_DIR}/Tools/QtCreator/lib/Qt/lib" + ) +endif() +# This should be set to your Qt6 installation directory. +set(QT_INSTALL_DIR "${QT_DIR}/6.3.1/gcc_64/" CACHE PATH "Path to Qt6 install") # For CLion builds, point CMAKE_PREFIX_PATH to Qt6 install directory # + QtCreator will handle this for you if that is used instead -list(APPEND CMAKE_PREFIX_PATH "${QT_DIR}") +list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}") +set(QTK_PLUGIN_LIBRARY_DIR "${QT_PLUGIN_LIBRARY_DIR}") +set(QTK_PLUGIN_INSTALL_DIR "${QT_PLUGIN_INSTALL_DIR}") +set(QTK_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install") +#set(QTK_INSTALL_PREFIX "${QT_INSTALL_DIR}") +#set(QTK_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) +set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources") + +# Print all QTK options and their values. +get_cmake_property(VAR_NAMES VARIABLES) +list(FILTER VAR_NAMES INCLUDE REGEX "^QTK_.*$") +list(SORT VAR_NAMES) +foreach(VAR_NAME ${VAR_NAMES}) + message(STATUS "[Qtk] ${VAR_NAME}=${${VAR_NAME}}") +endforeach() + +################################################################################ +# External Dependencies +################################################################################ # Find Qt -find_package(Qt6 COMPONENTS OpenGLWidgets) +find_package(Qt6 COMPONENTS Core UiPlugin OpenGLWidgets) if(NOT Qt6_FOUND) - message( - SEND_ERROR "[Qtk] Error: Unable to find Qt6 at CMAKE_PREFIX_PATH: " + message(SEND_ERROR + "[Qtk] Error: Unable to find Qt6 at CMAKE_PREFIX_PATH: " "${CMAKE_PREFIX_PATH}" ) - message( - FATAL_ERROR + message(FATAL_ERROR "[Qtk] Error: Specify path to Qt6 with `cmake " "-DCMAKE_PREFIX_PATH=/path/to/Qt/6.x.x/gcc_64 -S /path/to/qtk -B " "/path/to/qtk/build && cmake --build /path/to/qtk/build -j $(nprocs)`" ) endif() +# Find Assimp if(QTK_UPDATE_SUBMODULES) message(STATUS "[Qtk] Updating submodules...") - include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake") submodule_update("${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/") else() find_package(assimp REQUIRED) endif() +if(WIN32) + find_package(OpenGL REQUIRED) +endif() ################################################################################ # Qtk ################################################################################ +add_subdirectory(src) -set( - PUBLIC_HEADERS - src/qtkwidget.h - src/scene.h - src/camera3d.h - src/mesh.h - src/meshrenderer.h - src/model.h - src/object.h - src/skybox.h - src/texture.h - src/transform3D.h -) - -set( - SOURCE_FILES - src/qtkwidget.cpp - src/scene.cpp - src/camera3d.cpp - src/input.cpp - src/input.h - src/mesh.cpp - src/meshrenderer.cpp - src/model.cpp - src/object.cpp - src/qtkapi.h - src/skybox.cpp - src/texture.cpp - src/transform3D.cpp -) - -include(GenerateExportHeader) - -add_library(qtk-widget STATIC ${PUBLIC_HEADERS} ${SOURCE_FILES}) -target_include_directories(qtk-widget PRIVATE src/ app/) - -set_target_properties( - qtk-widget PROPERTIES - PUBLIC_HEADER "${PUBLIC_HEADERS}" - VERSION ${PROJECT_VERSION} -) - -target_link_libraries(qtk-widget PUBLIC Qt6::OpenGLWidgets) -target_link_libraries(qtk-widget PUBLIC Qt6::Widgets) - -if(QTK_UPDATE_SUBMODULES OR NOT ASSIMP_NEW_INTERFACE) - target_link_libraries(qtk-widget PUBLIC assimp) -elseif(ASSIMP_NEW_INTERFACE) - target_link_libraries(qtk-widget PUBLIC assimp::assimp) -endif() - -if(QTK_DEBUG) - message(STATUS "[Qtk] Building with QTK_DEBUG=${QTK_DEBUG}") - target_compile_definitions(qtk-widget PUBLIC QTK_DEBUG) -endif() - - -if(WIN32) - find_package(OpenGL REQUIRED) - target_link_libraries(qtk-widget PUBLIC OpenGL::GL) -endif() - -# Install files -install( - TARGETS qtk-widget - # Associate qtk-widget target with qtk-export - EXPORT qtk-export - # /bin on DLL systems and /lib on non-dll systems - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin - PUBLIC_HEADER DESTINATION include -) - -# Install export -# qtkTargets.cmake will only be installed when one of the CONFIGURATIONS is installed -# + The generated import will only reference that qtk configuration -install( - EXPORT qtk-export - FILE qtkTargets.cmake - CONFIGURATIONS Debug|Release - DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake -) - -################################################################################ -# Final Application -################################################################################ - -set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources") -configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/src/qtkresources.h.in" - "${CMAKE_CURRENT_BINARY_DIR}/src/qtkresources.h" - @ONLY -) - -# Add our Qt resources.qrc file to our application -set( - QTK_APP_SOURCES - app/main.cpp - app/examplescene.cpp - app/examplescene.h - app/mainwindow.cpp - app/mainwindow.h - app/mainwindow.ui - app/resourcemanager.h - src/qtkresources.h.in -) -qt6_add_big_resources(QTK_APP_SOURCES resources.qrc) - -qt_add_executable(qtk-main ${QTK_APP_SOURCES}) -target_include_directories(qtk-main PRIVATE src/ app/) - -# Link qtk-main executable to main qtk-widget library -target_link_libraries(qtk-main PUBLIC qtk-widget) - -set_target_properties( - qtk-main PROPERTIES - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} - MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} -) -install( - TARGETS qtk-main - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} -) - -generate_export_header(qtk-widget) if(WIN32) get_target_property(_qt6_qmake_location Qt6::qmake IMPORTED_LOCATION) - execute_process(COMMAND "${_qt6_qmake_location}" -query QT_INSTALL_PREFIX RESULT_VARIABLE return_code OUTPUT_VARIABLE qt6_install_prefix OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND "${_qt6_qmake_location}" -query QT_INSTALL_PREFIX + RESULT_VARIABLE return_code + OUTPUT_VARIABLE qt6_install_prefix + OUTPUT_STRIP_TRAILING_WHITESPACE + ) file(TO_NATIVE_PATH "${qt6_install_prefix}/bin" qt6_install_prefix) + if(TARGET Qt6::windeployqt) - add_custom_command( - TARGET qtk-main + add_custom_command(TARGET qtk-main POST_BUILD COMMAND set PATH=%PATH%$${qt6_install_prefix} COMMAND Qt6::windeployqt --dir "${CMAKE_BINARY_DIR}/windeployqt" "$/$" ) install(DIRECTORY "${CMAKE_BINARY_DIR}/windeployqt/" DESTINATION bin) endif() + if(MSVC AND TARGET Qt6::qmake) set(VSUSER_FILE ${CMAKE_CURRENT_BINARY_DIR}/qtk-main.vcxproj.user) file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/extern/assimp/assimp/bin" assimp_bin) @@ -236,4 +157,5 @@ if(WIN32) file(APPEND ${VSUSER_FILE} " \n") file(APPEND ${VSUSER_FILE} "\n") endif() + endif() diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp deleted file mode 100644 index 445a3bb..0000000 --- a/app/mainwindow.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/*############################################################################## -## Author: Shaun Reed ## -## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## -## About: MainWindow for creating an example Qt application ## -## ## -## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## -##############################################################################*/ - -#include -#include -#include "ui_mainwindow.h" - -MainWindow::MainWindow(QWidget * parent) : - QMainWindow(parent), ui(new Ui::MainWindow) { - // For use in design mode using Qt Creator - // + We can use the `ui` member to access nested widgets by name - ui->setupUi(this); - - // Find all QtkWidgets in this QMainWindow and initialize their scenes. - auto children = ui->centralwidget->findChildren(); - for(const auto qtkWidget : children) { - std::string key = qtkWidget->objectName().toStdString(); - // Initialize each scene into a map if it doesn't exist. - if(mScenes[key] == nullptr) { - mScenes[key] = new ExampleScene(); - } - // Set the QtkWidget to use the scene associated with this widget. - qtkWidget->setScene(mScenes[key]); - } - // TODO: Use QPlainTextEdit or similar for debug console - - // Set the window icon used for Qtk. - // TODO: Update this to be something other than kilroy. - setWindowIcon(QIcon("../resources/icon.png")); -} - -MainWindow::~MainWindow() { - delete ui; -} diff --git a/app/mainwindow.ui b/app/mainwindow.ui deleted file mode 100644 index ded93d0..0000000 --- a/app/mainwindow.ui +++ /dev/null @@ -1,114 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 800 - 600 - - - - Qtk - MainWindow - - - - - - 0 - 0 - 801 - 561 - - - - - - 10 - 10 - 781 - 541 - - - - - - - - - 0 - 0 - 800 - 22 - - - - - File - - - - - - - - - View - - - - - - - - - - Save - - - - - Save - - - - - Open... - - - - - Save - - - - - Save as... - - - - - Exit - - - - - true - - - Show Console - - - - - - Qtk::QtkWidget - QOpenGLWidget -
qtkwidget.h
-
-
- - -
diff --git a/app/resourcemanager.h b/app/resourcemanager.h deleted file mode 100644 index 2df646e..0000000 --- a/app/resourcemanager.h +++ /dev/null @@ -1,48 +0,0 @@ -/*############################################################################## -## Author: Shaun Reed ## -## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## -## About: Manage files and resources used by qtk ## -## ## -## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## -##############################################################################*/ - -#include - -#include - -#ifndef QTK_RESOURCEMANAGER_H -#define QTK_RESOURCEMANAGER_H - -/** - * ResourceManager class used to construct absolute paths to files within the Qt - * resources path. There is no need to manually call this method. - * Model::loadModel(...) will use this method if a Qt resource path is provided. - * The Model constructor behaves the same. If a path is prefixed with `:/` this - * static method will be used to resolve a full system path. - * - * This will likely be deprecated. It has a single call site and it is not - * meant for public use. It is public only for convenience. - * - * RM::getPath(":/models/alien-hominid/alien.obj") = - * /full/path/to/models/alien-hominid/alien.obj - */ -typedef class ResourceManager { - public: - /** - * Takes a path using qrc format and constructs full system path to qtk - * assets Qrc format prefix ':/' is trimmed from the path for the caller - * Assets used with RM may (or may not) appear in qtk/resources.qrc - * - * @param path Path relative to qtk/resources/; ie) - * ':/models/backpack/backpack.obj' An asset at location - * qtk/resources/path/to/asset.obj Should be given in qrc format: - * ':/path/to/asset.obj' - * @return Absolute system path to a qtk asset - */ - static std::string getPath(const std::string & path) { - // Only construct qtk resource path if in qrc format; else return it as-is - return path[0] == ':' ? QTK_RESOURCES + path.substr(1) : path; - } -} RM; - -#endif // QTK_RESOURCEMANAGER_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..f7230f0 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,23 @@ +################################################################################ +## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ## +## ## +## Project for working with OpenGL and Qt6 widgets ## +################################################################################ + +# Qtk Library +add_subdirectory(qtk) + +# Qtk Application +if (QTK_BUILD_GUI) + #TODO: Use this to test local builds? +# SET(CMAKE_INSTALL_RPATH "${QTK_INSTALL_PREFIX}/lib") + add_subdirectory(app) +endif() + +# Install export +install(EXPORT qtk-export + FILE QtkTargets.cmake + NAMESPACE Qtk:: + CONFIGURATIONS Debug|Release + DESTINATION ${QTK_INSTALL_PREFIX}/cmake +) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt new file mode 100644 index 0000000..f1b99e4 --- /dev/null +++ b/src/app/CMakeLists.txt @@ -0,0 +1,95 @@ +################################################################################ +## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ## +## ## +## Project for working with OpenGL and Qt6 widgets ## +################################################################################ + +################################################################################ +# Qtk Widget Library +################################################################################ +# Create a library of widgets used to build Qtk GUI +set(QTK_PLUGIN_LIBRARY_SOURCES + debugconsole.cpp debugconsole.ui + toolbox.cpp toolbox.ui + treeview.cpp treeview.ui +) +set(QTK_PLUGIN_LIBRARY_HEADERS + debugconsole.h + toolbox.h + treeview.h +) +qt_add_library(qtk-plugin-library SHARED) +target_sources(qtk-plugin-library PRIVATE + ${QTK_PLUGIN_LIBRARY_SOURCES} + ${QTK_PLUGIN_LIBRARY_HEADERS} +) +target_link_libraries(qtk-plugin-library PUBLIC Qt6::UiPlugin qtk-library) + +install(TARGETS qtk-plugin-library + EXPORT qtk-export + BUNDLE DESTINATION ${QTK_INSTALL_PREFIX}/lib + LIBRARY DESTINATION ${QTK_INSTALL_PREFIX}/lib + ARCHIVE DESTINATION ${QTK_INSTALL_PREFIX}/lib/static + RUNTIME DESTINATION ${QTK_INSTALL_PREFIX}/bin +) + +install(TARGETS qtk-plugin-library + BUNDLE DESTINATION ${QTK_PLUGIN_LIBRARY_DIR} + LIBRARY DESTINATION ${QTK_PLUGIN_LIBRARY_DIR} + RUNTIME DESTINATION ${QTK_PLUGIN_LIBRARY_DIR} + ARCHIVE DESTINATION ${QTK_PLUGIN_LIBRARY_DIR} +) + +################################################################################ +# Qtk Widget Collection Plugin +################################################################################ +# Create a Qt Designer plugin for a collection of widgets from our library. +qt_add_plugin(qtk-collection) +target_sources(qtk-collection PRIVATE + widgetplugincollection.cpp widgetplugincollection.h + widgetplugin.cpp widgetplugin.h +) +target_link_libraries(qtk-collection PUBLIC qtk-plugin-library) + +install(TARGETS qtk-collection + RUNTIME DESTINATION ${QTK_PLUGIN_INSTALL_DIR} + BUNDLE DESTINATION ${QTK_PLUGIN_INSTALL_DIR} + LIBRARY DESTINATION ${QTK_PLUGIN_INSTALL_DIR} +) + +################################################################################ +# Final Qtk Application +################################################################################ + +set(QTK_APP_SOURCES + qtkwidget.cpp qtkwidget.h + examplescene.cpp examplescene.h + qtkmainwindow.cpp qtkmainwindow.ui + qtkmainwindow.h + main.cpp +) +qt6_add_resources(QTK_APP_SOURCES ${CMAKE_SOURCE_DIR}/resources.qrc) +configure_file( + "resources.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/resources.h" + @ONLY +) + +qt_add_executable(qtk-main ${QTK_APP_SOURCES}) +# Link qtk-main executable to main qtk-library library +target_link_libraries(qtk-main PRIVATE qtk-plugin-library) +target_include_directories(qtk-main PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + +set_target_properties(qtk-main PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} +) + +# TODO: Fix install for qtk-main unable to find so files +install(TARGETS qtk-main + BUNDLE DESTINATION ${QTK_INSTALL_PREFIX}/bin + RUNTIME DESTINATION ${QTK_INSTALL_PREFIX}/bin + LIBRARY DESTINATION ${QTK_INSTALL_PREFIX}/bin/lib +) diff --git a/src/app/debugconsole.cpp b/src/app/debugconsole.cpp new file mode 100644 index 0000000..a459815 --- /dev/null +++ b/src/app/debugconsole.cpp @@ -0,0 +1,44 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: MainWindow for creating an example Qt application ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +##############################################################################*/ + +#include +#include + +#include "debugconsole.h" +#include "ui_debugconsole.h" + +using namespace Qtk; + +DebugConsole::DebugConsole(QWidget * owner, const QString & key) : + DebugConsole(owner, key, key + "Debugger") {} + +DebugConsole::DebugConsole( + QWidget * owner, const QString & key, const QString & name) { + ui_ = new Ui::DebugConsole; + ui_->setupUi(this); + setObjectName(name); + mConsole = ui_->textEdit; + setWidget(mConsole); + setWindowTitle(name + " Debug Console"); + + auto qtkWidget = dynamic_cast(owner); + if(qtkWidget) { + connect(qtkWidget, &QtkWidget::sendLog, this, &DebugConsole::sendLog); + sendLog( + "Debug console (" + name + ") attached to QtkWidget: '" + + qtkWidget->objectName() + "'"); + sendLog("Test\nLogging\t\n\tStuff", Status); + sendLog("Test\nLogging\t\n\tStuff", Debug); + sendLog("Test\nLogging\t\n\tStuff", Warn); + sendLog("Test\nLogging\t\n\tStuff", Error); + sendLog( + "Test\nLogging\t\n\tStuff that is really long and will wrap around but " + "it might not you don't know until you try", + Fatal); + } +} diff --git a/src/app/debugconsole.h b/src/app/debugconsole.h new file mode 100644 index 0000000..07a3679 --- /dev/null +++ b/src/app/debugconsole.h @@ -0,0 +1,129 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: Debug console for qtk views ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +##############################################################################*/ + +#ifndef QTK_DEBUGCONSOLE_H +#define QTK_DEBUGCONSOLE_H + +#include +#include +#include +#include + +#include "qtkwidget.h" + +namespace Ui { + class DebugConsole; +} + +namespace Qtk { + class DebugConsole : public QDockWidget { + Q_OBJECT; + + public: + /** + * Construct a new DebugConsole. + * Assigns a default name to the console using `key + "Debugger"` + * + * @param owner Parent widget for this console or nullptr if no parent. + * If this parameter inherits from QMainWindow we will add this dock + * widget to the window. + * @param key The objectName associated with the attached QtkWidget. + */ + DebugConsole(QWidget * owner, const QString & key); + + /** + * Construct a new DebugConsole. + * + * @param owner Parent widget for this console or nullptr if no parent. + * If this parameter inherits from QMainWindow we will add this dock + * widget to the window. + * @param key The objectName associated with the attached QtkWidget. + * @param name The objectName to associate with this DebugConsole. + */ + DebugConsole(QWidget * owner, const QString & key, const QString & name); + + ~DebugConsole() = default; + + public slots: + /************************************************************************* + * Public Qt slots + ************************************************************************/ + + /** + * Log a message to the DebugConsole text view. + * + * @param message The message to log. + * @param context The DebugContext to use for the message. + * Default value is Status. + */ + inline void sendLog(QString message, DebugContext context = Status) { + mConsole->setTextColor(logColor(context)); + mConsole->append(logPrefix(message, context)); + } + + /** + * Sets the window title for the DebugConsole. This will appear in the + * widget title bar and within any context menu actions. + * + * @param name Base name for the DebugConsole window. + */ + inline void setTitle(QString name) { + setWindowTitle(name + "Debug Console"); + } + + private: + [[nodiscard]] QColor logColor(const DebugContext & context) const { + switch(context) { + case Status: + return Qt::GlobalColor::darkGray; + case Debug: + return Qt::GlobalColor::white; + case Warn: + return Qt::GlobalColor::yellow; + case Error: + return Qt::GlobalColor::red; + case Fatal: + return Qt::GlobalColor::magenta; + default: + return Qt::GlobalColor::darkYellow; + } + } + + [[nodiscard]] QString logPrefix( + QString & message, const DebugContext & context) { + QString prefix; + switch(context) { + case Status: + prefix = "[Status]: "; + break; + case Debug: + prefix = "[Debug]: "; + break; + case Warn: + prefix = "[Warn]: "; + break; + case Error: + prefix = "[Error]: "; + break; + case Fatal: + prefix = "[Fatal]: "; + break; + default: + prefix = "[No Context]: "; + break; + } + message = prefix + message.replace("\n", "\t\n" + prefix); + return message; + } + + QTextEdit * mConsole; + Ui::DebugConsole * ui_; + }; +} // namespace Qtk + +#endif // QTK_DEBUGCONSOLE_H diff --git a/src/app/debugconsole.ui b/src/app/debugconsole.ui new file mode 100644 index 0000000..be68030 --- /dev/null +++ b/src/app/debugconsole.ui @@ -0,0 +1,33 @@ + + + DebugConsole + + + + 0 + 0 + 400 + 300 + + + + Debug Console + + + + + + + true + + + true + + + + + + + + + diff --git a/app/examplescene.cpp b/src/app/examplescene.cpp similarity index 59% rename from app/examplescene.cpp rename to src/app/examplescene.cpp index 2c5d8f3..9c687cc 100644 --- a/app/examplescene.cpp +++ b/src/app/examplescene.cpp @@ -6,13 +6,12 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include + +#include "examplescene.h" +#include "resources.h" using namespace Qtk; @@ -21,6 +20,7 @@ using namespace Qtk; ******************************************************************************/ ExampleScene::ExampleScene() { + setSceneName("Example Scene"); getCamera().getTransform().setTranslation(0.0f, 0.0f, 20.0f); getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); } @@ -30,13 +30,6 @@ ExampleScene::~ExampleScene() { delete mTestSpecular; delete mTestDiffuse; delete mTestAmbient; - for(auto & mesh : mMeshes) { - delete mesh; - } - for(auto & model : mModels) { - delete model; - } - delete mSkybox; } /******************************************************************************* @@ -50,74 +43,76 @@ void ExampleScene::init() { /* Create a red cube with a mini master chief on top. */ auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS)); myCube->setColor(RED); - mMeshes.push_back(myCube); + addObject(myCube); - auto mySpartan = new Model("My spartan", ":/models/spartan/spartan.obj"); + auto mySpartan = new Model("My spartan", PATH("/models/spartan/spartan.obj")); mySpartan->getTransform().setTranslation(0.0f, 0.5f, 0.0f); mySpartan->getTransform().setScale(0.5f); - mModels.push_back(mySpartan); + addObject(mySpartan); // // Create simple shapes using MeshRenderer class and data in mesh.h - mMeshes.push_back( + auto mesh = addObject( new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(-5.0f, 0.0f, -2.0f); + mesh->getTransform().setTranslation(-5.0f, 0.0f, -2.0f); - mMeshes.push_back( - new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(-7.0f, 0.0f, -2.0f); + mesh = + addObject(new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS))); + mesh->getTransform().setTranslation(-7.0f, 0.0f, -2.0f); - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("leftTriangle", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(-9.0f, 0.0f, -2.0f); - mMeshes.back()->setDrawType(GL_LINE_LOOP); + mesh->getTransform().setTranslation(-9.0f, 0.0f, -2.0f); + mesh->setDrawType(GL_LINE_LOOP); - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("topTriangle", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(-7.0f, 2.0f, -2.0f); - mMeshes.back()->getTransform().scale(0.25f); + mesh->getTransform().setTranslation(-7.0f, 2.0f, -2.0f); + mesh->getTransform().scale(0.25f); - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("bottomTriangle", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(-7.0f, -2.0f, -2.0f); - mMeshes.back()->getTransform().scale(0.25f); - mMeshes.back()->setDrawType(GL_LINE_LOOP); - mMeshes.back()->setColor(GREEN); + mesh->getTransform().setTranslation(-7.0f, -2.0f, -2.0f); + mesh->getTransform().scale(0.25f); + mesh->setDrawType(GL_LINE_LOOP); + mesh->setColor(GREEN); // // 3D Model loading - mModels.push_back( - new Qtk::Model("backpack", ":/models/backpack/backpack.obj")); + auto model = addObject( + new Qtk::Model("backpack", PATH("/models/backpack/backpack.obj"))); + auto test = PATH("/models/backpack/backpack.obj"); // Sometimes model textures need flipped in certain directions - mModels.back()->flipTexture("diffuse.jpg", false, true); - mModels.back()->getTransform().setTranslation(0.0f, 0.0f, -10.0f); + model->flipTexture("diffuse.jpg", false, true); + model->getTransform().setTranslation(0.0f, 0.0f, -10.0f); - mModels.push_back(new Qtk::Model("bird", ":/models/bird/bird.obj")); - mModels.back()->getTransform().setTranslation(2.0f, 2.0f, -10.0f); + model = addObject(new Qtk::Model("bird", PATH("/models/bird/bird.obj"))); + model->getTransform().setTranslation(2.0f, 2.0f, -10.0f); // Sometimes the models are very large - mModels.back()->getTransform().scale(0.0025f); - mModels.back()->getTransform().rotate(-110.0f, 0.0f, 1.0f, 0.0f); + model->getTransform().scale(0.0025f); + model->getTransform().rotate(-110.0f, 0.0f, 1.0f, 0.0f); - mModels.push_back(new Qtk::Model("lion", ":/models/lion/lion.obj")); - mModels.back()->getTransform().setTranslation(-3.0f, -1.0f, -10.0f); - mModels.back()->getTransform().scale(0.15f); + model = addObject(new Qtk::Model("lion", PATH("/models/lion/lion.obj"))); + model->getTransform().setTranslation(-3.0f, -1.0f, -10.0f); + model->getTransform().scale(0.15f); - mModels.push_back( - new Qtk::Model("alien", ":/models/alien-hominid/alien.obj")); - mModels.back()->getTransform().setTranslation(2.0f, -1.0f, -5.0f); - mModels.back()->getTransform().scale(0.15f); + model = addObject( + new Qtk::Model("alien", PATH("/models/alien-hominid/alien.obj"))); + model->getTransform().setTranslation(2.0f, -1.0f, -5.0f); + model->getTransform().scale(0.15f); - mModels.push_back(new Qtk::Model("scythe", ":/models/scythe/scythe.obj")); - mModels.back()->getTransform().setTranslation(-6.0f, 0.0f, -10.0f); - mModels.back()->getTransform().rotate(-90.0f, 1.0f, 0.0f, 0.0f); - mModels.back()->getTransform().rotate(90.0f, 0.0f, 1.0f, 0.0f); + model = + addObject(new Qtk::Model("scythe", PATH("/models/scythe/scythe.obj"))); + model->getTransform().setTranslation(-6.0f, 0.0f, -10.0f); + model->getTransform().rotate(-90.0f, 1.0f, 0.0f, 0.0f); + model->getTransform().rotate(90.0f, 0.0f, 1.0f, 0.0f); - mModels.push_back( - new Qtk::Model("masterChief", ":/models/spartan/spartan.obj")); - mModels.back()->getTransform().setTranslation(-1.5f, 0.5f, -2.0f); + model = addObject( + new Qtk::Model("masterChief", PATH("/models/spartan/spartan.obj"))); + model->getTransform().setTranslation(-1.5f, 0.5f, -2.0f); // @@ -147,17 +142,16 @@ void ExampleScene::init() { // Phong lighting example light source. This is just for visual reference. // + We refer to the position of this object in draw() to update lighting. - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("phongLight", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(3.0f, 2.0f, -2.0f); - mMeshes.back()->getTransform().scale(0.25f); + mesh->getTransform().setTranslation(3.0f, 2.0f, -2.0f); + mesh->getTransform().scale(0.25f); /* Example of a cube with no lighting applied */ - mMeshes.push_back(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, -2.0f); - mMeshes.back()->setShaders( - ":/solid-perspective.vert", ":/solid-perspective.frag"); - mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); + mesh = addObject(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS))); + mesh->getTransform().setTranslation(5.0f, 0.0f, -2.0f); + mesh->setShaders(":/solid-perspective.vert", ":/solid-perspective.frag"); + mesh->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); // No light source needed for this lighting technique /* Initialize Ambient example cube */ @@ -181,10 +175,10 @@ void ExampleScene::init() { mTestDiffuse->reallocateNormals(mTestDiffuse->getNormals()); // Diffuse lighting example light source. This is just for visual reference. - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("diffuseLight", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(9.0f, 2.0f, -2.0f); - mMeshes.back()->getTransform().scale(0.25f); + mesh->getTransform().setTranslation(9.0f, 2.0f, -2.0f); + mesh->getTransform().scale(0.25f); /* Initialize Specular example cube */ mTestSpecular = new Qtk::MeshRenderer("specular", Cube()); @@ -198,112 +192,112 @@ void ExampleScene::init() { mTestSpecular->reallocateNormals(mTestSpecular->getNormals()); // Specular lighting example light source. This is just for visual reference. - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("specularLight", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(11.0f, 2.0f, -2.0f); - mMeshes.back()->getTransform().scale(0.25f); + mesh->getTransform().setTranslation(11.0f, 2.0f, -2.0f); + mesh->getTransform().scale(0.25f); /* Test basic cube with phong.vert and phong.frag shaders */ - mMeshes.push_back(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS))); - mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, 10.0f); - mMeshes.back()->setShaders(":/phong.vert", ":/phong.frag"); + mesh = addObject(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS))); + mesh->getTransform().setTranslation(5.0f, 0.0f, 10.0f); + mesh->setShaders(":/phong.vert", ":/phong.frag"); // WARNING: Set color before reallocating normals. - mMeshes.back()->setColor(QVector3D(0.0f, 0.25f, 0.0f)); - mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); + mesh->setColor(QVector3D(0.0f, 0.25f, 0.0f)); + mesh->reallocateNormals(mesh->getNormals()); - mMeshes.back()->setUniform("uMaterial.ambient", QVector3D(0.0f, 0.3f, 0.0f)); - mMeshes.back()->setUniform("uMaterial.diffuse", QVector3D(0.0f, 0.2f, 0.0f)); - mMeshes.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f)); - mMeshes.back()->setUniform("uMaterial.ambientStrength", 1.0f); - mMeshes.back()->setUniform("uMaterial.diffuseStrength", 1.0f); - mMeshes.back()->setUniform("uMaterial.specularStrength", 1.0f); - mMeshes.back()->setUniform("uMaterial.shine", 64.0f); - mMeshes.back()->setUniform("uLight.ambient", QVector3D(0.25f, 0.2f, 0.075f)); - mMeshes.back()->setUniform("uLight.diffuse", QVector3D(0.75f, 0.6f, 0.22f)); - mMeshes.back()->setUniform("uLight.specular", QVector3D(0.62f, 0.55f, 0.37f)); - mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); + mesh->setUniform("uMaterial.ambient", QVector3D(0.0f, 0.3f, 0.0f)); + mesh->setUniform("uMaterial.diffuse", QVector3D(0.0f, 0.2f, 0.0f)); + mesh->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f)); + mesh->setUniform("uMaterial.ambientStrength", 1.0f); + mesh->setUniform("uMaterial.diffuseStrength", 1.0f); + mesh->setUniform("uMaterial.specularStrength", 1.0f); + mesh->setUniform("uMaterial.shine", 64.0f); + mesh->setUniform("uLight.ambient", QVector3D(0.25f, 0.2f, 0.075f)); + mesh->setUniform("uLight.diffuse", QVector3D(0.75f, 0.6f, 0.22f)); + mesh->setUniform("uLight.specular", QVector3D(0.62f, 0.55f, 0.37f)); + mesh->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); // Light source for testPhong cube - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("testLight", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(5.0f, 1.25f, 10.0f); - mMeshes.back()->getTransform().scale(0.25f); - mMeshes.back()->setDrawType(GL_LINE_LOOP); - mMeshes.back()->setColor(RED); + mesh->getTransform().setTranslation(5.0f, 1.25f, 10.0f); + mesh->getTransform().scale(0.25f); + mesh->setDrawType(GL_LINE_LOOP); + mesh->setColor(RED); // // Building more complex objects for showing examples of lighting techniques /* Test alien Model with phong lighting and specular mapping. */ - mModels.push_back(new Qtk::Model( - "alienTest", ":/models/alien-hominid/alien.obj", ":/model-specular.vert", - ":/model-specular.frag")); - mModels.back()->getTransform().setTranslation(3.0f, -1.0f, 10.0f); - mModels.back()->getTransform().scale(0.15f); - mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uMaterial.ambientStrength", 0.8f); - mModels.back()->setUniform("uMaterial.diffuseStrength", 0.8f); - mModels.back()->setUniform("uMaterial.specularStrength", 1.0f); - mModels.back()->setUniform("uMaterial.shine", 32.0f); + model = addObject(new Qtk::Model( + "alienTest", PATH("/models/alien-hominid/alien.obj"), + ":/model-specular.vert", ":/model-specular.frag")); + model->getTransform().setTranslation(3.0f, -1.0f, 10.0f); + model->getTransform().scale(0.15f); + model->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uMaterial.ambientStrength", 0.8f); + model->setUniform("uMaterial.diffuseStrength", 0.8f); + model->setUniform("uMaterial.specularStrength", 1.0f); + model->setUniform("uMaterial.shine", 32.0f); - mModels.back()->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f)); // Light source for alienTest object. - mMeshes.push_back(new Qtk::MeshRenderer( + mesh = addObject(new Qtk::MeshRenderer( "alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(4.0f, 1.5f, 10.0f); - mMeshes.back()->getTransform().scale(0.25f); + mesh->getTransform().setTranslation(4.0f, 1.5f, 10.0f); + mesh->getTransform().scale(0.25f); // This function changes values we have allocated in a buffer, so init() after - mMeshes.back()->setColor(GREEN); + mesh->setColor(GREEN); /* Test spartan Model with phong lighting, specular and normal mapping. */ - mModels.push_back(new Qtk::Model( - "spartanTest", ":/models/spartan/spartan.obj", ":/model-normals.vert", - ":/model-normals.frag")); - mModels.back()->getTransform().setTranslation(0.0f, -1.0f, 10.0f); - mModels.back()->getTransform().scale(2.0f); - mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uMaterial.ambientStrength", 1.0f); - mModels.back()->setUniform("uMaterial.diffuseStrength", 1.0f); - mModels.back()->setUniform("uMaterial.specularStrength", 1.0f); - mModels.back()->setUniform("uMaterial.shine", 128.0f); - mModels.back()->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); - mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f)); + model = addObject(new Qtk::Model( + "spartanTest", PATH("/models/spartan/spartan.obj"), + ":/model-normals.vert", ":/model-normals.frag")); + model->getTransform().setTranslation(0.0f, -1.0f, 10.0f); + model->getTransform().scale(2.0f); + model->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uMaterial.ambientStrength", 1.0f); + model->setUniform("uMaterial.diffuseStrength", 1.0f); + model->setUniform("uMaterial.specularStrength", 1.0f); + model->setUniform("uMaterial.shine", 128.0f); + model->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); + model->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f)); // Light source for spartanTest object. - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(1.0f, 1.5f, 10.0f); - mMeshes.back()->getTransform().scale(0.25f); + mesh->getTransform().setTranslation(1.0f, 1.5f, 10.0f); + mesh->getTransform().scale(0.25f); // This function changes values we have allocated in a buffer, so init() after - mMeshes.back()->setColor(GREEN); + mesh->setColor(GREEN); // // Test drawing simple geometry with various OpenGL drawing modes // RGB Normals cube to show normals are correct with QTK_DRAW_ARRAYS - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); - mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, 4.0f); - mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); + mesh->getTransform().setTranslation(5.0f, 0.0f, 4.0f); + mesh->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); + mesh->reallocateNormals(mesh->getNormals()); // RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS - mMeshes.push_back(new Qtk::MeshRenderer( + mesh = addObject(new Qtk::MeshRenderer( "rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); - mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, 2.0f); - mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); + mesh->getTransform().setTranslation(5.0f, 0.0f, 2.0f); + mesh->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); + mesh->reallocateNormals(mesh->getNormals()); Texture crateTexture; crateTexture.setTexture(":/crate.png"); @@ -314,86 +308,85 @@ void ExampleScene::init() { m->setTexture(crateTexture); m->setUniform("uTexture", 0); m->reallocateTexCoords(cube.getTexCoords()); - mMeshes.push_back(m); + addObject(m); // Texturing a cube using texture coordinates and glDrawArrays // + Texturing with UVs using glDrawElements requires // QTK_DRAW_ELEMENTS_NORMALS // + UVs required duplicating element position data from QTK_DRAW_ELEMENTS // + This is because the same position must use different UV coordinates - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); - mMeshes.back()->getTransform().setTranslation(-3.0f, 0.0f, -2.0f); - mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->setTexture(crateTexture); - mMeshes.back()->setUniform("uTexture", 0); - mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); + mesh->getTransform().setTranslation(-3.0f, 0.0f, -2.0f); + mesh->setShaders(":/texture2d.vert", ":/texture2d.frag"); + mesh->setTexture(crateTexture); + mesh->setUniform("uTexture", 0); + mesh->reallocateTexCoords(mesh->getTexCoords()); // Test drawing a cube with texture coordinates using glDrawElements - mMeshes.push_back(new Qtk::MeshRenderer( + mesh = addObject(new Qtk::MeshRenderer( "uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); - mMeshes.back()->getTransform().setTranslation(-1.7f, 0.0f, -2.0f); - mMeshes.back()->setTexture(":/crate.png"); - mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->bindShaders(); - mMeshes.back()->setUniform("uTexture", 0); - mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); - mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords(), 3); - mMeshes.back()->releaseShaders(); - mMeshes.back()->getTransform().rotate(45.0f, 0.0f, 1.0f, 0.0f); + mesh->getTransform().setTranslation(-1.7f, 0.0f, -2.0f); + mesh->setTexture(":/crate.png"); + mesh->setShaders(":/texture2d.vert", ":/texture2d.frag"); + mesh->bindShaders(); + mesh->setUniform("uTexture", 0); + mesh->reallocateNormals(mesh->getNormals()); + mesh->reallocateTexCoords(mesh->getTexCoords(), 3); + mesh->releaseShaders(); + mesh->getTransform().rotate(45.0f, 0.0f, 1.0f, 0.0f); // Texturing a cube using a cube map // + Cube map texturing works with both QTK_DRAW_ARRAYS and QTK_DRAW_ELEMENTS - mMeshes.push_back( - new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS))); - mMeshes.back()->getTransform().setTranslation(-3.0f, 1.0f, -2.0f); - mMeshes.back()->getTransform().setRotation(45.0f, 0.0f, 1.0f, 0.0f); - mMeshes.back()->setShaders( - ":/texture-cubemap.vert", ":/texture-cubemap.frag"); - mMeshes.back()->setCubeMap(":/crate.png"); - mMeshes.back()->setUniform("uTexture", 0); - mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); + mesh = + addObject(new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS))); + mesh->getTransform().setTranslation(-3.0f, 1.0f, -2.0f); + mesh->getTransform().setRotation(45.0f, 0.0f, 1.0f, 0.0f); + mesh->setShaders(":/texture-cubemap.vert", ":/texture-cubemap.frag"); + mesh->setCubeMap(":/crate.png"); + mesh->setUniform("uTexture", 0); + mesh->reallocateTexCoords(mesh->getTexCoords()); // Create a cube with custom shaders // + Apply RGB normals shader and spin the cube for a neat effect - mMeshes.push_back( - new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS))); - mMeshes.back()->getTransform().setTranslation(5.0f, 2.0f, -2.0f); - mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); + mesh = + addObject(new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS))); + mesh->getTransform().setTranslation(5.0f, 2.0f, -2.0f); + mesh->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); + mesh->reallocateNormals(mesh->getNormals()); // RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS - mMeshes.push_back(new Qtk::MeshRenderer( + mesh = addObject(new Qtk::MeshRenderer( "rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS))); - mMeshes.back()->getTransform().setTranslation(7.0f, 0.0f, 2.0f); - mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); + mesh->getTransform().setTranslation(7.0f, 0.0f, 2.0f); + mesh->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); + mesh->reallocateNormals(mesh->getNormals()); // RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS - mMeshes.push_back(new Qtk::MeshRenderer( + mesh = addObject(new Qtk::MeshRenderer( "rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS))); - mMeshes.back()->getTransform().setTranslation(7.0f, 0.0f, 4.0f); - mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); + mesh->getTransform().setTranslation(7.0f, 0.0f, 4.0f); + mesh->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); + mesh->reallocateNormals(mesh->getNormals()); // Test drawing triangle with glDrawArrays with texture coordinates - mMeshes.push_back( + mesh = addObject( new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS))); - mMeshes.back()->getTransform().setTranslation(-3.0f, 2.0f, -2.0f); - mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); + mesh->getTransform().setTranslation(-3.0f, 2.0f, -2.0f); + mesh->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->setTexture(":/crate.png"); - mMeshes.back()->setUniform("uTexture", 0); - mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); + mesh->setTexture(":/crate.png"); + mesh->setUniform("uTexture", 0); + mesh->reallocateTexCoords(mesh->getTexCoords()); // Test drawing triangle with glDrawElements with texture coordinates - mMeshes.push_back(new Qtk::MeshRenderer( + mesh = addObject(new Qtk::MeshRenderer( "testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS))); - mMeshes.back()->getTransform().setTranslation(-2.5f, 0.0f, -1.0f); - mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->setTexture(":/crate.png"); - mMeshes.back()->setUniform("uTexture", 0); - mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); + mesh->getTransform().setTranslation(-2.5f, 0.0f, -1.0f); + mesh->setShaders(":/texture2d.vert", ":/texture2d.frag"); + mesh->setTexture(":/crate.png"); + mesh->setUniform("uTexture", 0); + mesh->reallocateTexCoords(mesh->getTexCoords()); } void ExampleScene::draw() { @@ -401,11 +394,11 @@ void ExampleScene::draw() { // + This will handle rendering core scene components like the Skybox. Scene::draw(); - for(const auto & model : mModels) { + for(const auto & model : getModels()) { model->draw(); } - for(const auto & mesh : mMeshes) { + for(const auto & mesh : getMeshes()) { mesh->draw(); } diff --git a/app/examplescene.h b/src/app/examplescene.h similarity index 97% rename from app/examplescene.h rename to src/app/examplescene.h index 9ea0cb2..9c182a4 100644 --- a/app/examplescene.h +++ b/src/app/examplescene.h @@ -9,12 +9,12 @@ #ifndef QTK_EXAMPLE_SCENE_H #define QTK_EXAMPLE_SCENE_H -#include -#include -#include - #include +#include +#include +#include + /** * Example scene using QtkWidget to render 3D models and simple geometry within * QtOpenGLWidgets. This scene also shows some examples of using GLSL shaders to diff --git a/app/main.cpp b/src/app/main.cpp similarity index 79% rename from app/main.cpp rename to src/app/main.cpp index 3fe4a92..90136d5 100644 --- a/app/main.cpp +++ b/src/app/main.cpp @@ -7,18 +7,14 @@ ##############################################################################*/ #include -#include -#include -#include -#include +#include "qtkmainwindow.h" int main(int argc, char * argv[]) { QApplication a(argc, argv); - // Create window for Qt application using custom mainwindow.h - MainWindow w; - w.show(); + auto window = MainWindow::getMainWindow(); + window->show(); return QApplication::exec(); } diff --git a/src/app/qtkmainwindow.cpp b/src/app/qtkmainwindow.cpp new file mode 100644 index 0000000..67596ba --- /dev/null +++ b/src/app/qtkmainwindow.cpp @@ -0,0 +1,52 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: MainWindow for Qtk application ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +##############################################################################*/ + +#include + +#include "examplescene.h" +#include "qtkmainwindow.h" +#include "ui_qtkmainwindow.h" + +MainWindow * MainWindow::mainWindow_ = Q_NULLPTR; + +MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) { + ui_ = new Ui::MainWindow; + setObjectName("MainWindow"); + // For use in design mode using Qt Creator + // + We can use the `ui` member to access nested widgets by name + ui_->setupUi(this); + ui_->menuView->addAction(ui_->toolBar->toggleViewAction()); + + // Initialize static container for all active QtkWidgets + auto qtkWidgets = findChildren(); + for(auto & qtkWidget : qtkWidgets) { + qtkWidget->setScene(new ExampleScene); + views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget); + ui_->menuView->addAction(qtkWidget->getActionToggleConsole()); + connect( + qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this, + &MainWindow::refreshScene); + } + + auto docks = findChildren(); + for(auto & dock : docks) { + addDockWidget(Qt::RightDockWidgetArea, dock); + ui_->menuView->addAction(dock->toggleViewAction()); + } + + // Set the window icon used for Qtk. + setWindowIcon(Qtk::getIcon()); +} + +MainWindow::~MainWindow() { + delete ui_; +} + +void MainWindow::refreshScene(QString sceneName) { + ui_->qtk__TreeView->updateView(getQtkWidget(sceneName)->getScene()); +} diff --git a/app/mainwindow.h b/src/app/qtkmainwindow.h similarity index 50% rename from app/mainwindow.h rename to src/app/qtkmainwindow.h index dc83675..f62e310 100644 --- a/app/mainwindow.h +++ b/src/app/qtkmainwindow.h @@ -1,7 +1,7 @@ /*############################################################################## ## Author: Shaun Reed ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## -## About: MainWindow for creating an example Qt application ## +## About: MainWindow for Qtk application ## ## ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ @@ -12,9 +12,10 @@ #include #include +#include -#include -#include "qtk-widget_export.h" +#include "debugconsole.h" +#include "qtkwidget.h" namespace Ui { class MainWindow; @@ -24,7 +25,7 @@ namespace Ui { * MainWindow class to provide an example of using a QtkWidget within a Qt * window application. */ -class QTK_WIDGET_EXPORT MainWindow : public QMainWindow { +class MainWindow : public QMainWindow { Q_OBJECT public: @@ -41,13 +42,52 @@ class QTK_WIDGET_EXPORT MainWindow : public QMainWindow { explicit MainWindow(QWidget * parent = nullptr); ~MainWindow() override; + /*************************************************************************** + * Public Static Methods + **************************************************************************/ + + /** + * Allows widgets to retrieve an instance of this root QMainWindow. + * @return this + */ + inline static MainWindow * getMainWindow() { + if(mainWindow_ == Q_NULLPTR) { + mainWindow_ = new MainWindow; + } + return mainWindow_; + } + + /** + * Accessor for retrieving a QtkWidget by it's objectName. + * This function will not construct a new QtkWidget if none is found. + * + * @param name The objectName associated with the QtkWidget. + * @return Pointer to an active QtkWidget or Q_NULLPTR is not found. + */ + inline Qtk::QtkWidget * getQtkWidget(const QString & name) { + if(!views_.count(name)) { + return Q_NULLPTR; + } + return views_[name]; + } + + public slots: + void refreshScene(QString sceneName); + private: + MainWindow(const MainWindow &) {}; /*************************************************************************** * Private Members **************************************************************************/ - Ui::MainWindow * ui {}; - std::unordered_map mScenes {}; + Ui::MainWindow * ui_ {}; + static MainWindow * mainWindow_; + + /** + * Maps a scene name to the QtkWidget viewing it. + * TODO: Value should be a vector of QtkWidget * for multiple scene views. + */ + std::unordered_map views_ {}; }; #endif // MAINWINDOW_H diff --git a/src/app/qtkmainwindow.ui b/src/app/qtkmainwindow.ui new file mode 100644 index 0000000..fc0c14b --- /dev/null +++ b/src/app/qtkmainwindow.ui @@ -0,0 +1,336 @@ + + + MainWindow + + + + 0 + 0 + 824 + 601 + + + + + 1 + 1 + + + + Qtk - MainWindow + + + + ../resources/icon.png../resources/icon.png + + + true + + + + + + + + 0 + 0 + + + + 0 + + + true + + + + View 1 + + + + + + A custom widget tool tip. + + + Custom widget what's this? + + + + + + + + View 2 + + + + + + + + + + A custom widget tool tip. + + + Custom widget what's this? + + + + + + + A custom widget tool tip. + + + Custom widget what's this? + + + + + + + + + + + 0 + 0 + 824 + 22 + + + + + File + + + + + + + + + + + + View + + + + Tab Position + + + + + + + + + + + Edit + + + + + + + Help + + + + + + + + + + + toolBar + + + true + + + true + + + + 24 + 24 + + + + Qt::ToolButtonIconOnly + + + true + + + TopToolBarArea + + + false + + + + + + + Open... + + + + + + :/icons/resources/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg:/icons/resources/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg + + + Save + + + + + Save as... + + + + + Exit + + + + + true + + + Show Console + + + + + + :/icons/resources/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg:/icons/resources/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg + + + Load Model + + + + + + + + + :/icons/resources/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg:/icons/resources/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg + + + Delete Object + + + + + New + + + + + true + + + Top + + + + + true + + + Bottom + + + + + true + + + Left + + + + + true + + + Right + + + + + About + + + + + true + + + true + + + Nested Widgets + + + + + Undo + + + + + Redo + + + + + + Qtk::QtkWidget + QOpenGLWidget +
qtkwidget.h
+ 1 +
+ + Qtk::TreeView + QDockWidget +
treeview.h
+ 1 +
+ + Qtk::ToolBox + QDockWidget +
toolbox.h
+ 1 +
+
+ + + + actionExit + triggered() + MainWindow + close() + + + -1 + -1 + + + 411 + 300 + + + + +
diff --git a/src/qtkwidget.cpp b/src/app/qtkwidget.cpp similarity index 77% rename from src/qtkwidget.cpp rename to src/app/qtkwidget.cpp index 14ffccc..01bf9e8 100644 --- a/src/qtkwidget.cpp +++ b/src/app/qtkwidget.cpp @@ -7,11 +7,15 @@ ##############################################################################*/ #include +#include -#include -#include -#include -#include +#include "qtk/input.h" +#include "qtk/mesh.h" +#include "qtk/scene.h" + +#include "debugconsole.h" +#include "qtkmainwindow.h" +#include "qtkwidget.h" using namespace Qtk; @@ -19,17 +23,26 @@ using namespace Qtk; * Constructors, Destructors ******************************************************************************/ -QtkWidget::QtkWidget() : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) { - initializeWidget(); -} +QtkWidget::QtkWidget(QWidget * parent, const QString & name) : + QtkWidget(parent, name, Q_NULLPTR) {} -QtkWidget::QtkWidget(QWidget * parent) : - QOpenGLWidget(parent), mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) { - initializeWidget(); -} - -QtkWidget::QtkWidget(const QSurfaceFormat & format) : - mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) { +QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) : + QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR), + mConsole(new DebugConsole(this, name)) { + setScene(scene); + setObjectName(name); + QSurfaceFormat format; + format.setRenderableType(QSurfaceFormat::OpenGL); + format.setProfile(QSurfaceFormat::CoreProfile); + format.setVersion(4, 6); + // Set the number of samples used for glEnable(GL_MULTISAMPLING) + format.setSamples(4); + // Set the size of the depth bufer for glEnable(GL_DEPTH_TEST) + format.setDepthBufferSize(16); + // If QTK_DEBUG is set, enable debug context +#ifdef QTK_DEBUG + format.setOption(QSurfaceFormat::DebugContext); +#endif setFormat(format); setFocusPolicy(Qt::ClickFocus); } @@ -39,6 +52,19 @@ QtkWidget::~QtkWidget() { teardownGL(); } +/******************************************************************************* + * Public Methods + ******************************************************************************/ + +QAction * QtkWidget::getActionToggleConsole() { + auto action = new QAction(mScene->getSceneName() + " debug console"); + action->setCheckable(true); + action->setChecked(mConsoleActive); + action->setStatusTip("Add a debug console for this QtkWidget."); + connect(action, &QAction::triggered, this, &QtkWidget::toggleConsole); + return action; +} + /******************************************************************************* * Public Inherited Virtual Methods ******************************************************************************/ @@ -104,19 +130,24 @@ void QtkWidget::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; } @@ -159,8 +190,9 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) { } #undef CASE - error += ")"; - qDebug() << qPrintable(error) << "\n" << qPrintable(msg.message()) << "\n"; + error += ")\n" + msg.message() + "\n"; + qDebug() << qPrintable(error); + sendLog("(OpenGL) " + error.replace("\n", "\n(OpenGL) "), context); } /******************************************************************************* @@ -196,23 +228,6 @@ void QtkWidget::mouseReleaseEvent(QMouseEvent * event) { * Private Methods ******************************************************************************/ -void QtkWidget::initializeWidget() { - QSurfaceFormat format; - format.setRenderableType(QSurfaceFormat::OpenGL); - format.setProfile(QSurfaceFormat::CoreProfile); - format.setVersion(4, 6); - // Set the number of samples used for glEnable(GL_MULTISAMPLING) - format.setSamples(4); - // Set the size of the depth bufer for glEnable(GL_DEPTH_TEST) - format.setDepthBufferSize(16); - // If QTK_DEBUG is set, enable debug context -#ifdef QTK_DEBUG - format.setOption(QSurfaceFormat::DebugContext); -#endif - setFormat(format); - setFocusPolicy(Qt::ClickFocus); -} - void QtkWidget::printContextInformation() { QString glType; QString glVersion; @@ -239,11 +254,11 @@ void QtkWidget::printContextInformation() { } #undef CASE - // qPrintable() will print our QString w/o quotes around it. - qDebug() << qPrintable(glType) << qPrintable(glVersion) << "(" - << qPrintable(glProfile) << ")" - << "\nOpenGL Vendor: " << qPrintable(glVendor) - << "\nRendering Device: " << qPrintable(glRenderer) << "\n"; + auto message = QString(glType) + glVersion + "(" + glProfile + ")" + + "\nOpenGL Vendor: " + glVendor + + "\nRendering Device: " + glRenderer; + qDebug() << qPrintable(message); + sendLog("(OpenGL) " + message.replace("\n", "\n(OpenGL) "), Status); } void QtkWidget::updateCameraInput() { @@ -282,3 +297,26 @@ void QtkWidget::updateCameraInput() { Scene::getCamera().getTransform().translate(transSpeed * translation); } } + +void QtkWidget::toggleConsole() { + if(mConsoleActive) { + mConsole->setHidden(true); + mConsoleActive = false; + } else { + MainWindow::getMainWindow()->addDockWidget( + Qt::DockWidgetArea::BottomDockWidgetArea, + dynamic_cast(mConsole)); + mConsole->setHidden(false); + mConsoleActive = true; + } +} + +void QtkWidget::setScene(Qtk::Scene * scene) { + delete mScene; + mScene = scene; + if(mScene != Q_NULLPTR) { + mConsole->setTitle(mScene->getSceneName()); + } else { + mConsole->setTitle("Null Scene"); + } +} diff --git a/src/qtkwidget.h b/src/app/qtkwidget.h similarity index 72% rename from src/qtkwidget.h rename to src/app/qtkwidget.h index a19e7b6..83a2c12 100644 --- a/src/qtkwidget.h +++ b/src/app/qtkwidget.h @@ -10,22 +10,26 @@ #include +#include #include #include #include #include +#include -#include -#include +#include "qtk/qtkapi.h" +#include "qtk/scene.h" namespace Qtk { + class DebugConsole; + /** * QtkWidget class to define required QOpenGLWidget functionality. * * This object has a Scene attached which manages the objects to render. * Client input is passed through this widget to control the camera view. */ - class QTKAPI QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions { + class QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT; public: @@ -33,26 +37,36 @@ namespace Qtk { * Contructors / Destructors ************************************************************************/ - /** - * Default ctor will configure a QSurfaceFormat with default settings. - */ - QtkWidget(); - /** * Qt Designer will call this ctor when creating this widget as a child. * - * @param parent The parent QWidget + * @param parent Pointer to a parent widget for this QtkWidget or nullptr. */ - explicit QtkWidget(QWidget * parent); + explicit QtkWidget(QWidget * parent = nullptr) : + QtkWidget(parent, "QtkWidget") {} /** - * Allow constructing the widget with a preconfigured QSurfaceFormat. + * Default construct a QtkWidget. * - * @param format QSurfaceFormat already configured by the caller. + * @param parent Pointer to a parent widget or nullptr if no parent. + * @param name An objectName for the new QtkWidget. */ - explicit QtkWidget(const QSurfaceFormat & format); + explicit QtkWidget(QWidget * parent, const QString & name); + + /** + * Construct a custom QtkWidget. + * + * @param parent Pointer to a parent widget or nullptr if no parent. + * @param name An objectName for the new QtkWidget. + * @param scene Pointer to a custom class inheriting from Qtk::Scene. + */ + QtkWidget(QWidget * parent, const QString & name, Qtk::Scene * scene); ~QtkWidget() override; + /************************************************************************* + * Public Methods + ************************************************************************/ + QAction * getActionToggleConsole(); private: /************************************************************************* @@ -60,7 +74,7 @@ namespace Qtk { ************************************************************************/ // clang-format off - void teardownGL() { /* Nothing to teardown yet... */ } + void teardownGL() { /* Nothing to teardown yet... */ } // clang-format on public: @@ -96,10 +110,12 @@ namespace Qtk { * Setters ************************************************************************/ - inline void setScene(Qtk::Scene * scene) { - delete mScene; - mScene = scene; - } + void setScene(Qtk::Scene * scene); + public slots: + /** + * Toggle visibility of the DebugConsole associated with this QtkWidget. + */ + void toggleConsole(); protected slots: /************************************************************************* @@ -119,8 +135,12 @@ namespace Qtk { * * @param msg The message logged. */ - static void messageLogged(const QOpenGLDebugMessage & msg); + void messageLogged(const QOpenGLDebugMessage & msg); #endif + public: + signals: + void sendLog( + const QString & message, Qtk::DebugContext context = Qtk::Status); protected: /************************************************************************* @@ -137,19 +157,19 @@ namespace Qtk { * Private Methods ************************************************************************/ - void initializeWidget(); static void updateCameraInput(); #ifdef QTK_DEBUG void printContextInformation(); QOpenGLDebugLogger * mDebugLogger; #endif - /************************************************************************* * Private Members ************************************************************************/ + bool mConsoleActive = false; Qtk::Scene * mScene; + Qtk::DebugConsole * mConsole; }; } // namespace Qtk diff --git a/src/app/resources.h.in b/src/app/resources.h.in new file mode 100644 index 0000000..cc89316 --- /dev/null +++ b/src/app/resources.h.in @@ -0,0 +1,8 @@ +#ifndef QTK_RESOURCES_H_IN_H +#define QTK_RESOURCES_H_IN_H + +#define RESOURCES "@CMAKE_SOURCE_DIR@/resources" + +#define PATH(a) RESOURCES a + +#endif // QTK_RESOURCES_H_IN_H diff --git a/src/qtkapi.h b/src/app/toolbox.cpp similarity index 60% rename from src/qtkapi.h rename to src/app/toolbox.cpp index 088a1fd..ef986fc 100644 --- a/src/qtkapi.h +++ b/src/app/toolbox.cpp @@ -1,23 +1,20 @@ /*############################################################################## ## Author: Shaun Reed ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## -## About: Main window for Qt6 OpenGL widget application ## +## About: Toolbox plugin for object details and options ## ## ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## -##############################################################################*/ -#ifndef QTK_QTKAPI_H -#define QTK_QTKAPI_H +################################################################################ +*/ -#include +#include "toolbox.h" +#include "ui_toolbox.h" -#ifdef QTK_SHARED -#if defined(QTK_EXPORT) -#define QTKAPI Q_DECL_EXPORT -#else -#define QTKAPI Q_DECL_IMPORT -#endif -#else -#define QTKAPI -#endif +Qtk::ToolBox::ToolBox(QWidget * parent) : + QDockWidget(parent), ui(new Ui::ToolBox) { + ui->setupUi(this); +} -#endif // QTK_QTKAPI_H +Qtk::ToolBox::~ToolBox() { + delete ui; +} diff --git a/src/app/toolbox.h b/src/app/toolbox.h new file mode 100644 index 0000000..12a76a2 --- /dev/null +++ b/src/app/toolbox.h @@ -0,0 +1,33 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: Toolbox plugin for object details and options ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ + +#ifndef TOOLBOX_H +#define TOOLBOX_H + +#include +#include + +namespace Ui { + class ToolBox; +} + +namespace Qtk { + class QDESIGNER_WIDGET_EXPORT ToolBox : public QDockWidget { + Q_OBJECT + + public: + explicit ToolBox(QWidget * parent = nullptr); + ~ToolBox(); + + private: + Ui::ToolBox * ui; + }; +} // namespace Qtk + +#endif // TOOLBOX_H diff --git a/src/app/toolbox.ui b/src/app/toolbox.ui new file mode 100644 index 0000000..0de2672 --- /dev/null +++ b/src/app/toolbox.ui @@ -0,0 +1,56 @@ + + + ToolBox + + + + 0 + 0 + 400 + 300 + + + + Object Details + + + + + + + 0 + + + + + 0 + 0 + 382 + 201 + + + + Shaders + + + + + + 0 + 0 + 382 + 201 + + + + Properties + + + + + + + + + + diff --git a/src/app/treeview.cpp b/src/app/treeview.cpp new file mode 100644 index 0000000..b9c37dc --- /dev/null +++ b/src/app/treeview.cpp @@ -0,0 +1,58 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: TreeView plugin for scene hierarchy ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ + +#include "treeview.h" +#include "qtkmainwindow.h" +#include "ui_treeview.h" + +/******************************************************************************* + * Constructors, Destructors + ******************************************************************************/ + +Qtk::TreeView::TreeView(QWidget * parent) : + QDockWidget(parent), ui(new Ui::TreeView) { + ui->setupUi(this); + connect( + ui->treeWidget, &QTreeWidget::itemDoubleClicked, this, + &TreeView::itemFocus); +} + +Qtk::TreeView::~TreeView() { + delete ui; +} + +/******************************************************************************* + * Public Members + ******************************************************************************/ +void Qtk::TreeView::updateView(const Qtk::Scene * scene) { + ui->treeWidget->clear(); + ui->treeWidget->setColumnCount(1); + mSceneName = scene->getSceneName(); + auto objects = scene->getObjects(); + for(const auto & object : objects) { + auto item = new QTreeWidgetItem(QStringList(QString(object->getName()))); + ui->treeWidget->insertTopLevelItem(0, item); + } +} + +void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) { + QString name = item->text(column); + auto scene = + MainWindow::getMainWindow()->getQtkWidget(mSceneName)->getScene(); + auto & transform = scene->getCamera().getTransform(); + auto object = scene->getObject(name); + Transform3D * objectTransform; + if(object->getType() == Object::MESH) { + objectTransform = &dynamic_cast(object)->getTransform(); + } else if(object->getType() == Object::MODEL) { + objectTransform = &dynamic_cast(object)->getTransform(); + } + transform.setTranslation(objectTransform->getTranslation()); + transform.translate(0.0f, 0.0f, 3.0f); +} diff --git a/src/app/treeview.h b/src/app/treeview.h new file mode 100644 index 0000000..2afcffd --- /dev/null +++ b/src/app/treeview.h @@ -0,0 +1,61 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: TreeView plugin for scene hierarchy ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ + +#ifndef TREEVIEW_H +#define TREEVIEW_H + +#include +#include +#include + +#include +#include + +namespace Ui { + class TreeView; +} + +namespace Qtk { + class QDESIGNER_WIDGET_EXPORT TreeView : public QDockWidget { + Q_OBJECT + + public: + explicit TreeView(QWidget * parent = nullptr); + + ~TreeView(); + + /** + * Updates the QTreeWidget with all objects within the scene. + * @param scene The scene to load objects from. + */ + void updateView(const Scene * scene); + + public slots: + /** + * Focus the camera on an item when it is double clicked. + * Triggered by QTreeWidget::itemDoubleClicked signal. + * + * @param item The item that was double clicked + * @param column The column of the item that was double clicked. + * This param is currently not used but required for this signal. + */ + void itemFocus(QTreeWidgetItem * item, int column); + + private: + Ui::TreeView * ui; + + /** + * The name of the scene last loaded by this TreeWidget. + * Used to load object data from a target scene. + */ + QString mSceneName; + }; +} // namespace Qtk + +#endif // TREEVIEW_H diff --git a/src/app/treeview.ui b/src/app/treeview.ui new file mode 100644 index 0000000..354fa29 --- /dev/null +++ b/src/app/treeview.ui @@ -0,0 +1,44 @@ + + + TreeView + + + + 0 + 0 + 400 + 300 + + + + Scene Tree View + + + + + + + true + + + 10 + + + true + + + false + + + + 1 + + + + + + + + + + diff --git a/src/app/widgetplugin.cpp b/src/app/widgetplugin.cpp new file mode 100644 index 0000000..1fc6f49 --- /dev/null +++ b/src/app/widgetplugin.cpp @@ -0,0 +1,88 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: Generic Qt Designer widget plugin ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ + +#include +#include +#include + +#include "qtk/qtkapi.h" + +#include "widgetplugin.h" + +WidgetPlugin::WidgetPlugin( + QString group, QString class_name, QString include, + WidgetPlugin::Factory factory) : + m_group(std::move(group)), + m_className(std::move(class_name)), m_includeFile(std::move(include)), + m_factory(std::move(factory)), m_objectName(class_name) {} + +QString WidgetPlugin::toolTip() const { + return QStringLiteral("A custom widget tool tip."); +} + +QString WidgetPlugin::whatsThis() const { + return QStringLiteral("Custom widget what's this?"); +} + +QIcon WidgetPlugin::icon() const { + return Qtk::getIcon(); +} + +bool WidgetPlugin::isContainer() const { + return true; +} + +QString WidgetPlugin::group() const { + return m_group; +} + +QString WidgetPlugin::name() const { + return m_className; +} + +QString WidgetPlugin::includeFile() const { + return m_includeFile; +} + +QWidget * WidgetPlugin::createWidget(QWidget * parent) { + return m_factory(parent); +} + +bool WidgetPlugin::isInitialized() const { + return m_initialized; +} + +void WidgetPlugin::initialize(QDesignerFormEditorInterface *) { + if(m_initialized) { + return; + } + + m_initialized = true; +} +QString WidgetPlugin::domXml() const { + return + "\n" + " \n" + " \n" + " \n" + " 0\n" + " 0\n" + " 100\n" + " 100\n" + " \n" + " \n" + " \n" + " " + toolTip() + "\n" + " \n" + " \n" + " " + whatsThis() + "\n" + " \n" + " \n" + "\n"; +} diff --git a/src/app/widgetplugin.h b/src/app/widgetplugin.h new file mode 100644 index 0000000..351d079 --- /dev/null +++ b/src/app/widgetplugin.h @@ -0,0 +1,114 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: Generic Qt Designer widget plugin ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ + +#ifndef QTK_WIDGETPLUGIN_H +#define QTK_WIDGETPLUGIN_H + +#include +#include + +class QDESIGNER_WIDGET_EXPORT WidgetPlugin : + public QObject, + public QDesignerCustomWidgetInterface { + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + + using Factory = std::function; + + public: + WidgetPlugin( + QString group, QString class_name, QString include, Factory factory); + + explicit WidgetPlugin(QObject * parent = nullptr) : QObject(parent) {} + + ~WidgetPlugin() = default; + + public: + /** + * @return The name of the group to which this widget belongs. + */ + [[nodiscard]] QString group() const override; + + /** + * Must return the _class name_ of the widget. + * + * @return The class name for the associated widget. + */ + [[nodiscard]] QString name() const override; + + /** + * If this path changes for a custom widget, it must be removed and added + * back in Qt Designer for the XML surrounding this value to be regenerated. + * + * See the `` XML in any `.ui` file using a custom widget. + * + * @return Path to the include file for UIC to use when generating code. + */ + [[nodiscard]] QString includeFile() const override; + + /** + * @param parent Parent widget to the new instance of this widget. + * @return A new instance of this custom widget. + */ + QWidget * createWidget(QWidget * parent) override; + + /** + * @return Short description used in Qt Designer tool tips. + */ + [[nodiscard]] QString toolTip() const override; + + /** + * @return Widget description used in `What's this?` within Qt Creator. + */ + [[nodiscard]] QString whatsThis() const override; + + /** + * @return Icon used to represent the widget in Qt Designer's GUI. + */ + [[nodiscard]] QIcon icon() const override; + + /** + * Whether or not this widget should act as a container for other widgets. + * + * @return True if this custom widget is meant to be a container. + */ + [[nodiscard]] bool isContainer() const override; + + /** + * @return True if this widget has been initialized. + */ + [[nodiscard]] bool isInitialized() const override; + + /** + * Initializes an instance of this custom widget. + * @param core + */ + void initialize(QDesignerFormEditorInterface * core) override; + + /** + * Default XML for an instance of this custom widget within a `.ui` file. + * + * Any property available for the widget in Qt Designer can be set using XML + * properties, as seen here with `toolTip` and `whatsThis`. + * + * @return XML inserted for each instance of this widget. + */ + [[nodiscard]] QString domXml() const override; + + private: + bool m_initialized = false; + + QString m_group; + QString m_className; + QString m_objectName; + QString m_includeFile; + Factory m_factory; +}; + +#endif // QTK_WIDGETPLUGIN_H diff --git a/src/app/widgetplugincollection.cpp b/src/app/widgetplugincollection.cpp new file mode 100644 index 0000000..c1b0906 --- /dev/null +++ b/src/app/widgetplugincollection.cpp @@ -0,0 +1,34 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: Collection of widget plugins for Qt Designer ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ + +#include "widgetplugincollection.h" +#include "debugconsole.h" +#include "qtkwidget.h" +#include "toolbox.h" +#include "treeview.h" +#include "widgetplugin.h" + +WidgetPluginCollection::WidgetPluginCollection(QObject * parent) : + QObject(parent), m_collectionName("Qtk Widget Collection") { + m_collection = { + new WidgetPlugin( + m_collectionName, "Qtk::QtkWidget", "qtkwidget.h", + [](QWidget * parent) { return new Qtk::QtkWidget(parent); }), + new WidgetPlugin( + m_collectionName, "Qtk::TreeView", "treeview.h", + [](QWidget * parent) { return new Qtk::TreeView(parent); }), + new WidgetPlugin( + m_collectionName, "Qtk::ToolBox", "toolbox.h", + [](QWidget * parent) { return new Qtk::ToolBox(parent); }), + }; +} +QList WidgetPluginCollection::customWidgets() + const { + return m_collection; +} diff --git a/src/app/widgetplugincollection.h b/src/app/widgetplugincollection.h new file mode 100644 index 0000000..20e4faa --- /dev/null +++ b/src/app/widgetplugincollection.h @@ -0,0 +1,34 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: Collection of widget plugins for Qt Designer ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ + +#ifndef QTK_WIDGETPLUGINCOLLECTION_H +#define QTK_WIDGETPLUGINCOLLECTION_H + +#include + +class WidgetPluginCollection : + public QObject, + public QDesignerCustomWidgetCollectionInterface { + Q_OBJECT + // Since we're exporting a collection, this is the only plugin metadata + // needed. + Q_PLUGIN_METADATA(IID "com.Klips.WidgetPluginCollection") + // Tell Qt Object system that we're implementing an interface. + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) + + public: + explicit WidgetPluginCollection(QObject * parent = nullptr); + [[nodiscard]] QList customWidgets() const; + + private: + QList m_collection; + QString m_collectionName; +}; + +#endif // QTK_WIDGETPLUGINCOLLECTION_H diff --git a/src/qtk/CMakeLists.txt b/src/qtk/CMakeLists.txt new file mode 100644 index 0000000..e4cfda2 --- /dev/null +++ b/src/qtk/CMakeLists.txt @@ -0,0 +1,96 @@ +################################################################################ +## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ## +## ## +## Project for working with OpenGL and Qt6 widgets ## +################################################################################ + +# TODO: Provide option for linking MainWindow with plugin statically +# TODO: QtkWidget plugin should instantiate a ptr to DebugConsole and store it as a member. +# Then we can connect a signal from MainWindow for creating a console to QtkWidget +# When triggered QtkWidget slot will send signal to MainWindow +# MainWindow catches signal and runs slot to attach DebugConsole to MainWindow as QDockWidget +# TODO: Create a second repository for working on QtkApplication (MainWindow) +################################################################################ +# Qtk Library +################################################################################ +set(PUBLIC_HEADERS + camera3d.h + input.h + mesh.h + meshrenderer.h + model.h + object.h + qtkapi.h + scene.h + skybox.h + texture.h + transform3D.h +) + +set(SOURCE_FILES + camera3d.cpp + input.cpp + mesh.cpp + meshrenderer.cpp + model.cpp + object.cpp + scene.cpp + skybox.cpp + texture.cpp + transform3D.cpp +) + +qt_add_library(qtk-library SHARED) +target_sources(qtk-library PRIVATE ${SOURCE_FILES}) +target_sources(qtk-library PUBLIC + FILE_SET HEADERS + BASE_DIRS ${CMAKE_SOURCE_DIR}/src + FILES ${PUBLIC_HEADERS} +) + +if(QTK_DEBUG) + target_compile_definitions(qtk-library PUBLIC QTK_DEBUG) +endif() +if(QTK_SHARED) + target_compile_definitions(qtk-library PUBLIC QTK_SHARED) +endif() + +set_target_properties(qtk-library PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE + VERSION ${PROJECT_VERSION} +) + +target_link_libraries(qtk-library PUBLIC + Qt6::Core Qt6::OpenGLWidgets Qt6::Widgets +) + +if(QTK_UPDATE_SUBMODULES OR NOT ASSIMP_NEW_INTERFACE) + target_link_libraries(qtk-library PUBLIC assimp) +elseif(ASSIMP_NEW_INTERFACE) + target_link_libraries(qtk-library PUBLIC assimp::assimp) +endif() + +if(WIN32) + target_link_libraries(qtk-library PUBLIC OpenGL::GL) +endif() + +# System install for qtk-library +# TODO: Use RUNTIME_DEPENDENCY_SET to install Qt libraries for local builds? +install(TARGETS qtk-library + # Associate qtk-library target with qtk-export + EXPORT qtk-export + FILE_SET HEADERS DESTINATION "${QTK_INSTALL_PREFIX}/include" + BUNDLE DESTINATION ${QTK_INSTALL_PREFIX}/lib + LIBRARY DESTINATION ${QTK_INSTALL_PREFIX}/lib + ARCHIVE DESTINATION ${QTK_INSTALL_PREFIX}/lib/static + RUNTIME DESTINATION ${QTK_INSTALL_PREFIX}/bin +) + +## Install qtk-library to Qt Designer to support widget plugins. +install(TARGETS qtk-library + BUNDLE DESTINATION ${QTK_PLUGIN_LIBRARY_DIR} + LIBRARY DESTINATION ${QTK_PLUGIN_LIBRARY_DIR} + ARCHIVE DESTINATION ${QTK_PLUGIN_LIBRARY_DIR} + RUNTIME DESTINATION ${QTK_PLUGIN_LIBRARY_DIR} +) diff --git a/src/camera3d.cpp b/src/qtk/camera3d.cpp similarity index 98% rename from src/camera3d.cpp rename to src/qtk/camera3d.cpp index 68ce14c..ec0da3b 100644 --- a/src/camera3d.cpp +++ b/src/qtk/camera3d.cpp @@ -6,7 +6,7 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include +#include "camera3d.h" using namespace Qtk; diff --git a/src/camera3d.h b/src/qtk/camera3d.h similarity index 98% rename from src/camera3d.h rename to src/qtk/camera3d.h index 89b714d..809ce38 100644 --- a/src/camera3d.h +++ b/src/qtk/camera3d.h @@ -11,8 +11,8 @@ #include -#include -#include +#include "qtkapi.h" +#include "transform3D.h" namespace Qtk { class QTKAPI Camera3D { diff --git a/src/input.cpp b/src/qtk/input.cpp similarity index 99% rename from src/input.cpp rename to src/qtk/input.cpp index a568ac3..2be197a 100644 --- a/src/input.cpp +++ b/src/qtk/input.cpp @@ -11,7 +11,7 @@ #include -#include +#include "input.h" using namespace Qtk; diff --git a/src/input.h b/src/qtk/input.h similarity index 90% rename from src/input.h rename to src/qtk/input.h index b9cdfec..5873e0d 100644 --- a/src/input.h +++ b/src/qtk/input.h @@ -12,8 +12,7 @@ #include #include -#include -#include +#include "qtkapi.h" namespace Qtk { class QTKAPI Input { @@ -21,7 +20,6 @@ namespace Qtk { /************************************************************************* * Typedefs ************************************************************************/ - friend class Qtk::QtkWidget; /** * Possible key states @@ -71,11 +69,6 @@ namespace Qtk { static QPoint mousePosition(); static QPoint mouseDelta(); - private: - /************************************************************************* - * Private Methods - ************************************************************************/ - // State updating static void update(); static void registerKeyPress(int key); diff --git a/src/mesh.cpp b/src/qtk/mesh.cpp similarity index 99% rename from src/mesh.cpp rename to src/qtk/mesh.cpp index a714582..3af81c9 100644 --- a/src/mesh.cpp +++ b/src/qtk/mesh.cpp @@ -6,7 +6,7 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include +#include "mesh.h" using namespace Qtk; diff --git a/src/mesh.h b/src/qtk/mesh.h similarity index 99% rename from src/mesh.h rename to src/qtk/mesh.h index 14934be..819e386 100644 --- a/src/mesh.h +++ b/src/qtk/mesh.h @@ -8,13 +8,14 @@ #ifndef QTK_MESH_H #define QTK_MESH_H +#include + #include #include #include -#include -#include -#include +#include "qtkapi.h" +#include "transform3D.h" namespace Qtk { class MeshRenderer; diff --git a/src/meshrenderer.cpp b/src/qtk/meshrenderer.cpp similarity index 97% rename from src/meshrenderer.cpp rename to src/qtk/meshrenderer.cpp index 7372cf4..66992ee 100644 --- a/src/meshrenderer.cpp +++ b/src/qtk/meshrenderer.cpp @@ -8,9 +8,9 @@ #include -#include -#include -#include +#include "meshrenderer.h" +#include "scene.h" +#include "texture.h" using namespace Qtk; @@ -18,7 +18,7 @@ using namespace Qtk; Qtk::MeshRenderer::MeshManager Qtk::MeshRenderer::sInstances; MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) : - Object(name, shape), mVertexShader(":/multi-color.vert"), + Object(name, shape, MESH), mVertexShader(":/multi-color.vert"), mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES) { mShape = Shape(shape); init(); diff --git a/src/meshrenderer.h b/src/qtk/meshrenderer.h similarity index 98% rename from src/meshrenderer.h rename to src/qtk/meshrenderer.h index f8a64b9..a2e55b1 100644 --- a/src/meshrenderer.h +++ b/src/qtk/meshrenderer.h @@ -8,12 +8,12 @@ #ifndef QTK_MESHRENDERER_H #define QTK_MESHRENDERER_H -#include -#include -#include - #include +#include "mesh.h" +#include "object.h" +#include "qtkapi.h" + namespace Qtk { class QTKAPI MeshRenderer : public Object { public: diff --git a/src/model.cpp b/src/qtk/model.cpp similarity index 98% rename from src/model.cpp rename to src/qtk/model.cpp index 8d6f520..5930319 100644 --- a/src/model.cpp +++ b/src/qtk/model.cpp @@ -9,10 +9,9 @@ #include -#include -#include -#include -#include +#include "model.h" +#include "scene.h" +#include "texture.h" using namespace Qtk; @@ -186,8 +185,7 @@ void Model::loadModel(const std::string & path) { // JIC a relative path was used, get the absolute file path QFileInfo info(path.c_str()); info.makeAbsolute(); - mDirectory = path[0] == ':' ? RM::getPath(path) - : info.absoluteFilePath().toStdString(); + mDirectory = info.absoluteFilePath().toStdString(); // Import the model, converting non-triangular geometry to triangles // + And flipping texture UVs, etc.. @@ -215,7 +213,7 @@ void Model::loadModel(const std::string & path) { sortModels(); // Object finished loading, insert it into ModelManager - mManager.insert(mName, this); + mManager.insert(getName(), this); } void Model::processNode(aiNode * node, const aiScene * scene) { diff --git a/src/model.h b/src/qtk/model.h similarity index 95% rename from src/model.h rename to src/qtk/model.h index 4c654bd..e2c5dbd 100644 --- a/src/model.h +++ b/src/qtk/model.h @@ -23,9 +23,9 @@ #include // QTK -#include -#include -#include +#include "object.h" +#include "qtkapi.h" +#include "transform3D.h" namespace Qtk { /** @@ -122,9 +122,7 @@ namespace Qtk { * Model object that has a ModelMesh. * Top-level object that represents 3D models stored within a scene. */ - class QTKAPI Model : public QObject { - Q_OBJECT - + class QTKAPI Model : public Object { public: /************************************************************************* * Typedefs @@ -142,13 +140,13 @@ namespace Qtk { const char * name, const char * path, const char * vertexShader = ":/model-basic.vert", const char * fragmentShader = ":/model-basic.frag") : - mName(name), + Object(name, MODEL), mModelPath(path), mVertexShader(vertexShader), mFragmentShader(fragmentShader) { - loadModel(path); + loadModel(mModelPath); } - inline ~Model() override { mManager.remove(mName); } + inline ~Model() override { mManager.remove(getName()); } /************************************************************************* * Public Methods @@ -239,10 +237,6 @@ namespace Qtk { /************************************************************************* * Private Members ************************************************************************/ - - /* The position of this model in 3D space */ - Transform3D mTransform; - /* Static QHash used to store and access models globally. */ static ModelManager mManager; @@ -254,8 +248,6 @@ namespace Qtk { std::string mDirectory {}; /* File names for shaders and 3D model on disk. */ const char *mVertexShader, *mFragmentShader, *mModelPath; - /* Name of the model object within the scene. */ - const char * mName; }; } // namespace Qtk diff --git a/src/object.cpp b/src/qtk/object.cpp similarity index 96% rename from src/object.cpp rename to src/qtk/object.cpp index eb2d546..ea44ab2 100644 --- a/src/object.cpp +++ b/src/qtk/object.cpp @@ -6,6 +6,6 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include +#include "object.h" using namespace Qtk; diff --git a/src/object.h b/src/qtk/object.h similarity index 88% rename from src/object.h rename to src/qtk/object.h index e281d40..0e99bc2 100644 --- a/src/object.h +++ b/src/qtk/object.h @@ -13,11 +13,13 @@ #include #include -#include -#include -#include +#include "mesh.h" +#include "qtkapi.h" +#include "texture.h" namespace Qtk { + class Model; + /** * Object base class for objects that can exist within a scene. * An object could be a Cube, Skybox, 3D Model, or other standalone entities. @@ -31,19 +33,26 @@ namespace Qtk { ************************************************************************/ friend MeshRenderer; + friend Model; + + /** + * Enum flag to identify Object type without casting. + */ + enum Type { OBJECT, MESH, MODEL }; /************************************************************************* * Constructors / Destructors ************************************************************************/ // Initialize an object with no shape data assigned - explicit Object(const char * name) : - mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false) {} + explicit Object(const char * name, Type type) : + mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false), + mType(type) {} // Initialize an object with shape data assigned - Object(const char * name, const ShapeBase & shape) : + Object(const char * name, const ShapeBase & shape, Type type) : mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape), - mBound(false) {} + mBound(false), mType(type) {} ~Object() override = default; @@ -77,6 +86,10 @@ namespace Qtk { return mShape.mVertices; } + [[nodiscard]] inline const char * getName() const { return mName; } + + [[nodiscard]] inline const Type & getType() const { return mType; } + /************************************************************************* * Setters ************************************************************************/ @@ -143,6 +156,7 @@ namespace Qtk { Texture mTexture; const char * mName; bool mBound; + Type mType = OBJECT; }; } // namespace Qtk diff --git a/src/qtk/qtkapi.h b/src/qtk/qtkapi.h new file mode 100644 index 0000000..d5f5968 --- /dev/null +++ b/src/qtk/qtkapi.h @@ -0,0 +1,49 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: Main window for Qt6 OpenGL widget application ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +##############################################################################*/ +#ifndef QTK_QTKAPI_H +#define QTK_QTKAPI_H + +#include +#include + +#ifdef QTK_SHARED +#if defined(QTK_EXPORT) +#define QTKAPI Q_DECL_EXPORT +#else +#define QTKAPI Q_DECL_IMPORT +#endif +#else +#define QTKAPI +#endif + +namespace Qtk { + /** + * Flag to set context for debug messages. + */ + enum DebugContext { Status, Debug, Warn, Error, Fatal }; + + /** + * Find top level parent for a widget. + * + * @param widget Widget to start the search from. + * @return Top level parent widget or Q_NULLPTR if no parent + */ + static QWidget * topLevelParent(QWidget * widget) { + QString name = widget->objectName(); + while(widget->parentWidget() != Q_NULLPTR) { + widget = widget->parentWidget(); + } + return widget; + } + + static QIcon getIcon() { + return QIcon(":/icon.png"); + } +} // namespace Qtk + +#endif // QTK_QTKAPI_H diff --git a/src/qtk/scene.cpp b/src/qtk/scene.cpp new file mode 100644 index 0000000..8495c4e --- /dev/null +++ b/src/qtk/scene.cpp @@ -0,0 +1,101 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: Classes for managing objects and data within a scene ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +##############################################################################*/ + +#include "scene.h" +#include "camera3d.h" + +using namespace Qtk; + +Camera3D Scene::mCamera; +QMatrix4x4 Scene::mProjection; + +/******************************************************************************* + * Constructors, Destructors + ******************************************************************************/ + +Scene::Scene() : mSceneName("Default Scene") { + mCamera.getTransform().setTranslation(0.0f, 0.0f, 20.0f); + mCamera.getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); +} + +Scene::~Scene() { + for(auto & mesh : mMeshes) { + delete mesh; + } + for(auto & model : mModels) { + delete model; + } + delete mSkybox; +} + +/******************************************************************************* + * Accessors + ******************************************************************************/ + +std::vector Scene::getObjects() const { + // All scene objects must inherit from Qtk::Object. + std::vector objects(mMeshes.begin(), mMeshes.end()); + for(auto model : mModels) { + objects.push_back(dynamic_cast(model)); + if(objects.back() == nullptr) { + return {}; + } + } + return objects; +} + +/******************************************************************************* + * Setters + ******************************************************************************/ + +void Scene::setSkybox(Skybox * skybox) { + delete mSkybox; + mSkybox = skybox; +} + +template <> MeshRenderer * Scene::addObject(MeshRenderer * object) { + mMeshes.push_back(object); + sceneUpdated(mSceneName); + return object; +} + +template <> Model * Scene::addObject(Model * object) { + mModels.push_back(object); + sceneUpdated(mSceneName); + return object; +} + +/******************************************************************************* + * Private Methods + ******************************************************************************/ + +void Scene::privateDraw() { + if(!mInit) { + initializeOpenGLFunctions(); + init(); + mInit = true; + } + if(mSkybox != Q_NULLPTR) { + mSkybox->draw(); + } + for(auto & model : mModels) { + model->draw(); + } + for(const auto & mesh : mMeshes) { + mesh->draw(); + } +} + +Object * Scene::getObject(const QString & name) { + for(auto object : getObjects()) { + if(object->getName() == name) { + return object; + } + } + return Q_NULLPTR; +} diff --git a/src/scene.h b/src/qtk/scene.h similarity index 68% rename from src/scene.h rename to src/qtk/scene.h index 439b6f9..2cbc994 100644 --- a/src/scene.h +++ b/src/qtk/scene.h @@ -9,12 +9,13 @@ #ifndef QTK_SCENE_H #define QTK_SCENE_H -#include -#include -#include -#include - #include +#include + +#include "camera3d.h" +#include "meshrenderer.h" +#include "model.h" +#include "skybox.h" namespace Qtk { /** @@ -38,7 +39,9 @@ namespace Qtk { * If the child scene adds any objects which are not managed (drawn) by this * base class, the child scene class must also override the `draw()` method. */ - class Scene : protected QOpenGLFunctions { + class Scene : public QObject, protected QOpenGLFunctions { + Q_OBJECT + public: /************************************************************************* * Contructors / Destructors @@ -46,7 +49,7 @@ namespace Qtk { Scene(); - ~Scene(); + virtual ~Scene(); /************************************************************************* * Public Methods @@ -75,19 +78,78 @@ namespace Qtk { * Accessors ************************************************************************/ + /** + * @return Camera attached to this scene. + */ static Camera3D & getCamera() { return mCamera; } + /** + * @return View matrix for the camera attached to this scene. + */ static QMatrix4x4 getViewMatrix() { return mCamera.toMatrix(); } + /** + * @return Projection matrix for the current view into the scene. + */ static QMatrix4x4 & getProjectionMatrix() { return mProjection; } + /** + * @return The active skybox for this scene. + */ inline Skybox * getSkybox() { return mSkybox; } + /** + * @return The name for this scene. This is entirely user defined and not + * a Qt objectName. + */ + [[nodiscard]] inline QString getSceneName() const { return mSceneName; } + + /** + * @return All MeshRenderers within the scene. + */ + [[nodiscard]] inline const std::vector & getMeshes() + const { + return mMeshes; + } + + /** + * @return All Models within the scene. + */ + [[nodiscard]] inline const std::vector & getModels() const { + return mModels; + } + + /** + * @return All Qtk::Objects within the scene. + * If any object is invalid, we return an empty vector. + */ + [[nodiscard]] std::vector getObjects() const; + + [[nodiscard]] Object * getObject(const QString & name); + /************************************************************************* * Setters ************************************************************************/ - inline void setSkybox(Skybox * skybox) { mSkybox = skybox; } + /** + * @param skybox New skybox to use for this scene. + */ + void setSkybox(Skybox * skybox); + + /** + * Adds objects to the scene. + * @param object The new object to add to the scene. + * @return The object added to the scene. + */ + template T * addObject(T * object); + + /** + * @param name The name to use for this scene. + */ + inline void setSceneName(QString name) { mSceneName = std::move(name); } + + signals: + void sceneUpdated(QString sceneName); private: /************************************************************************* @@ -108,11 +170,12 @@ namespace Qtk { */ void privateDraw(); - protected: + private: /************************************************************************* - * Protected Members + * Private Members ************************************************************************/ + QString mSceneName; /* The skybox for this scene. */ Skybox * mSkybox {}; /* MeshRenderers used simple geometry. */ diff --git a/src/skybox.cpp b/src/qtk/skybox.cpp similarity index 98% rename from src/skybox.cpp rename to src/qtk/skybox.cpp index 29cda26..62168cb 100644 --- a/src/skybox.cpp +++ b/src/qtk/skybox.cpp @@ -6,9 +6,9 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include -#include -#include +#include "skybox.h" +#include "scene.h" +#include "texture.h" using namespace Qtk; diff --git a/src/skybox.h b/src/qtk/skybox.h similarity index 96% rename from src/skybox.h rename to src/qtk/skybox.h index 6033c19..7a3b99c 100644 --- a/src/skybox.h +++ b/src/qtk/skybox.h @@ -15,10 +15,10 @@ #include #include -#include -#include -#include -#include +#include "camera3d.h" +#include "mesh.h" +#include "qtkapi.h" +#include "texture.h" namespace Qtk { /** diff --git a/src/texture.cpp b/src/qtk/texture.cpp similarity index 99% rename from src/texture.cpp rename to src/qtk/texture.cpp index 5745bf0..003faef 100644 --- a/src/texture.cpp +++ b/src/qtk/texture.cpp @@ -6,11 +6,12 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include -#include #include -#include +#include +#include + +#include "texture.h" using namespace Qtk; diff --git a/src/texture.h b/src/qtk/texture.h similarity index 99% rename from src/texture.h rename to src/qtk/texture.h index 548968f..2593c40 100644 --- a/src/texture.h +++ b/src/qtk/texture.h @@ -9,11 +9,12 @@ #ifndef QTOPENGL_TEXTURE_H #define QTOPENGL_TEXTURE_H -#include -#include #include -#include +#include +#include + +#include "qtkapi.h" namespace Qtk { /** diff --git a/src/transform3D.cpp b/src/qtk/transform3D.cpp similarity index 99% rename from src/transform3D.cpp rename to src/qtk/transform3D.cpp index 7d4d041..6ebbb64 100644 --- a/src/transform3D.cpp +++ b/src/qtk/transform3D.cpp @@ -7,7 +7,7 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include +#include "transform3D.h" using namespace Qtk; diff --git a/src/transform3D.h b/src/qtk/transform3D.h similarity index 99% rename from src/transform3D.h rename to src/qtk/transform3D.h index 81c815d..cc7992e 100644 --- a/src/transform3D.h +++ b/src/qtk/transform3D.h @@ -15,12 +15,10 @@ #include #ifndef QT_NO_DEBUG_STREAM - #include - #endif -#include +#include "qtkapi.h" namespace Qtk { /** diff --git a/src/qtkresources.h.in b/src/qtkresources.h.in deleted file mode 100644 index b5ab997..0000000 --- a/src/qtkresources.h.in +++ /dev/null @@ -1,2 +0,0 @@ - -#define QTK_RESOURCES "@QTK_RESOURCES@" diff --git a/src/scene.cpp b/src/scene.cpp deleted file mode 100644 index 9a05e66..0000000 --- a/src/scene.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/*############################################################################## -## Author: Shaun Reed ## -## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## -## About: Classes for managing objects and data within a scene ## -## ## -## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## -##############################################################################*/ - -#include -#include -#include -#include - -using namespace Qtk; - -Camera3D Scene::mCamera; -QMatrix4x4 Scene::mProjection; - -/******************************************************************************* - * Constructors, Destructors - ******************************************************************************/ - -Scene::Scene() { - mCamera.getTransform().setTranslation(0.0f, 0.0f, 20.0f); - mCamera.getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); -} - -Scene::~Scene() { - for(auto & mesh : mMeshes) { - delete mesh; - } - for(auto & model : mModels) { - delete model; - } - delete mSkybox; -} - -void Scene::privateDraw() { - if(!mInit) { - initializeOpenGLFunctions(); - init(); - mInit = true; - } - if(mSkybox != Q_NULLPTR) { - mSkybox->draw(); - } - for(auto & model : mModels) { - model->draw(); - } - for(const auto & mesh : mMeshes) { - mesh->draw(); - } -}