Remove large resources. (#16)
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
## Project for working with OpenGL and Qt6 widgets ##
|
||||
## ##
|
||||
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
||||
## All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||
## All Content (c) 2025 Shaun Reed, all rights reserved ##
|
||||
################################################################################
|
||||
|
||||
# Qtk Library
|
||||
@@ -13,22 +13,22 @@ install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
COMPONENT qtk_library
|
||||
COMPONENT qtk
|
||||
DESTINATION lib/cmake/${PROJECT_NAME}
|
||||
)
|
||||
install(
|
||||
EXPORT qtk_export
|
||||
FILE ${PROJECT_NAME}Targets.cmake
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
COMPONENT qtk_library
|
||||
COMPONENT qtk
|
||||
DESTINATION lib/cmake/${PROJECT_NAME}
|
||||
)
|
||||
# System install for qtk_library
|
||||
# System install for libqtk
|
||||
install(
|
||||
TARGETS qtk_library
|
||||
# Associate qtk_library target with qtk-export
|
||||
TARGETS qtk
|
||||
# Associate libqtk target with qtk-export
|
||||
EXPORT qtk_export
|
||||
COMPONENT qtk_library
|
||||
COMPONENT qtk
|
||||
FILE_SET HEADERS DESTINATION include
|
||||
INCLUDES DESTINATION include
|
||||
LIBRARY DESTINATION lib
|
||||
@@ -36,60 +36,16 @@ install(
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
||||
# Qtk Application
|
||||
if(QTK_GUI OR QTK_PLUGINS)
|
||||
add_subdirectory(app)
|
||||
endif()
|
||||
|
||||
if(QTK_PLUGINS)
|
||||
install(
|
||||
TARGETS qtk_plugins qtk_library qtk_plugin_library
|
||||
COMPONENT qtk_plugins
|
||||
LIBRARY DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
||||
ARCHIVE DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
||||
RUNTIME DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
||||
)
|
||||
# The Qtk Application's GUI requires plugins to be built, but not installed.
|
||||
# If both are disabled we can skip. If QTK_PLUGINS is set we'll install the
|
||||
# plugins to Qt Designer for use in other projects.
|
||||
if(QTK_PLUGINS OR QTK_GUI)
|
||||
add_subdirectory(designer-plugins)
|
||||
endif()
|
||||
|
||||
# Build Qtk Application only if QTK_GUI is set.
|
||||
if(QTK_GUI)
|
||||
install(
|
||||
TARGETS qtk_gui
|
||||
COMPONENT qtk_gui
|
||||
BUNDLE DESTINATION .
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
||||
qt_generate_deploy_app_script(
|
||||
TARGET qtk_gui
|
||||
OUTPUT_SCRIPT QTK_DEPLOY_SCRIPT
|
||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||
)
|
||||
install(SCRIPT ${QTK_DEPLOY_SCRIPT} COMPONENT qtk_gui)
|
||||
|
||||
if(WIN32)
|
||||
if(MSVC AND TARGET Qt6::qmake)
|
||||
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
|
||||
)
|
||||
file(TO_NATIVE_PATH "${QT6_INSTALL_PREFIX}/bin" QT6_INSTALL_PREFIX)
|
||||
|
||||
set(VSUSER_FILE "${CMAKE_CURRENT_BINARY_DIR}/qtk_gui.vcxproj.user")
|
||||
file(WRITE ${VSUSER_FILE} "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
|
||||
file(APPEND ${VSUSER_FILE} "<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n")
|
||||
file(APPEND ${VSUSER_FILE} " <PropertyGroup>\n")
|
||||
file(APPEND ${VSUSER_FILE} " <LocalDebuggerEnvironment>Path=$(SolutionDir)\\lib\\$(Configuration);${QT6_INSTALL_PREFIX};$(Path)\n")
|
||||
file(APPEND ${VSUSER_FILE} "$(LocalDebuggerEnvironment)</LocalDebuggerEnvironment>\n")
|
||||
file(APPEND ${VSUSER_FILE} " <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n")
|
||||
file(APPEND ${VSUSER_FILE} " </PropertyGroup>\n")
|
||||
file(APPEND ${VSUSER_FILE} "</Project>\n")
|
||||
endif()
|
||||
endif()
|
||||
add_subdirectory(app)
|
||||
endif()
|
||||
|
||||
write_basic_package_version_file(
|
||||
@@ -103,15 +59,19 @@ configure_package_config_file(
|
||||
)
|
||||
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
|
||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
|
||||
set(CPACK_PACKAGE_VENDOR "Shaun Reed")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Qt OpenGL 3D graphics library.")
|
||||
set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/shaunrd0/qtk")
|
||||
if (QTK_GUI)
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Qt OpenGL 3D graphics renderer.")
|
||||
else()
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Qt OpenGL 3D graphics library.")
|
||||
endif()
|
||||
set(CPACK_PACKAGE_HOMEPAGE_URL "https://git.shaunreed.com/shaunrd0/qtk")
|
||||
set(CPACK_SOURCE_IGNORE_FILES build*;install;\.git;\.github;\.idea)
|
||||
set(CPACK_PACKAGE_DIRECTORY packages/)
|
||||
set(CPACK_PACKAGE_CONTACT "shaunreed.com")
|
||||
#set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/icon.png")
|
||||
set(CPACK_THREADS 0)
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Qtk")
|
||||
|
||||
@@ -122,32 +82,71 @@ if (QTK_SUBMODULES)
|
||||
list(REMOVE_ITEM CPACK_COMPONENTS_ALL Unspecified)
|
||||
endif()
|
||||
|
||||
# Windows
|
||||
set(CPACK_NSIS_MODIFY_PATH ON)
|
||||
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
|
||||
# https://nsis.sourceforge.io/Reference/CreateShortCut
|
||||
set(
|
||||
CPACK_NSIS_CREATE_ICONS_EXTRA
|
||||
"CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Qtk.lnk' '$INSTDIR\\\\bin\\\\qtk_gui.exe'"
|
||||
)
|
||||
set(
|
||||
CPACK_NSIS_DELETE_ICONS_EXTRA
|
||||
"Delete '$SMPROGRAMS\\\\$START_MENU\\\\Qtk.lnk'"
|
||||
)
|
||||
# TODO: Icons for NSIS installer.
|
||||
#set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/resources/icon.png")
|
||||
#set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/resources/icon.png")
|
||||
if(WIN32)
|
||||
# Windows
|
||||
# Set icon for the splash image used in Windows NSIS installers.
|
||||
# TODO: Fix NSIS cmake error
|
||||
# File: "D:/a/qtk/qtk/resources/icons/win/kilroy_splash.bmp" -> no files found.
|
||||
#set(CPACK_PACKAGE_ICON "${QTK_RESOURCES}/icons/win/kilroy_splash.bmp")
|
||||
set(CPACK_NSIS_MODIFY_PATH ON)
|
||||
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
|
||||
# https://nsis.sourceforge.io/Reference/CreateShortCut
|
||||
# TODO: Use 4th parameter to pass icon.
|
||||
set(
|
||||
CPACK_NSIS_CREATE_ICONS_EXTRA
|
||||
"CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Qtk.lnk' '$INSTDIR\\\\bin\\\\qtk_gui.exe'\n\
|
||||
CreateShortCut '$DESKTOP\\\\Qtk.lnk' '$INSTDIR\\\\bin\\\\qtk_gui.exe'"
|
||||
)
|
||||
set(
|
||||
CPACK_NSIS_DELETE_ICONS_EXTRA
|
||||
"Delete '$SMPROGRAMS\\\\$START_MENU\\\\Qtk.lnk'\n\
|
||||
Delete '$DESKTOP\\\\Qtk.lnk'"
|
||||
)
|
||||
|
||||
# Debian
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PACKAGE_HOMEPAGE_URL})
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
# Set icon for the installer executable.
|
||||
set(CPACK_NSIS_MUI_ICON "${QTK_RESOURCES}/icons/win/kilroy.ico")
|
||||
# Set icon for uninstaller executable to be the same as the installer.
|
||||
set(CPACK_NSIS_MUI_UNIICON "${CPACK_NSIS_MUI_ICON}")
|
||||
elseif(APPLE)
|
||||
# OSX
|
||||
# Set icon for the splash image used in OSX DMG installers.
|
||||
set(CPACK_PACKAGE_ICON "${QTK_RESOURCES}/icons/osx/kilroy_splash.bmp")
|
||||
|
||||
# OSX
|
||||
set(CPACK_BUNDLE_NAME ${PROJECT_NAME})
|
||||
set(CPACK_BUNDLE_PLIST $<TARGET_BUNDLE_CONTENT_DIR:qtk_gui>/Info.plist)
|
||||
set(CPACK_BUNDLE_ICON ${QTK_OSX_ICONS})
|
||||
# Set additional options for the OSX bundle.
|
||||
set(CPACK_BUNDLE_NAME ${PROJECT_NAME})
|
||||
set(CPACK_BUNDLE_PLIST $<TARGET_BUNDLE_CONTENT_DIR:qtk_gui>/Info.plist)
|
||||
set(CPACK_BUNDLE_ICON "${QTK_OSX_ICONS}")
|
||||
else(UNIX)
|
||||
# Linux
|
||||
# Install icons for qtk.
|
||||
install(FILES "${QTK_RESOURCES}/icons/linux/kilroy-16.png"
|
||||
DESTINATION /usr/share/icons/hicolor/16x16/apps
|
||||
RENAME qtk.png)
|
||||
install(FILES "${QTK_RESOURCES}/icons/linux/kilroy-32.png"
|
||||
DESTINATION /usr/share/icons/hicolor/32x32/apps
|
||||
RENAME qtk.png)
|
||||
install(FILES "${QTK_RESOURCES}/icons/linux/kilroy-48.png"
|
||||
DESTINATION /usr/share/icons/hicolor/48x48/apps
|
||||
RENAME qtk.png)
|
||||
install(FILES "${QTK_RESOURCES}/icons/linux/kilroy-64.png"
|
||||
DESTINATION /usr/share/icons/hicolor/64x64/apps
|
||||
RENAME qtk.png)
|
||||
install(FILES "${QTK_RESOURCES}/icons/linux/kilroy-128.png"
|
||||
DESTINATION /usr/share/icons/hicolor/128x128/apps
|
||||
RENAME qtk.png)
|
||||
install(FILES "${QTK_RESOURCES}/icons/linux/kilroy-256.png"
|
||||
DESTINATION /usr/share/icons/hicolor/256x256/apps
|
||||
RENAME qtk.png)
|
||||
|
||||
# Install the desktop file for qtk.
|
||||
install(FILES "${QTK_RESOURCES}/qtk.desktop"
|
||||
DESTINATION /usr/share/applications)
|
||||
|
||||
# Set additional options for the debian APT package.
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PACKAGE_HOMEPAGE_URL})
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
endif()
|
||||
|
||||
# Platform defaults for source bundles.
|
||||
if(WIN32)
|
||||
set(CPACK_SOURCE_GENERATOR ZIP)
|
||||
else()
|
||||
|
||||
@@ -2,70 +2,99 @@
|
||||
## Project for working with OpenGL and Qt6 widgets ##
|
||||
## ##
|
||||
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
||||
## All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||
## All Content (c) 2025 Shaun Reed, all rights reserved ##
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
# Qtk Widget Library
|
||||
################################################################################
|
||||
# Create a library of widgets used to build Qtk GUI
|
||||
set(
|
||||
QTK_PLUGIN_LIBRARY_SOURCES
|
||||
qtkwidget.cpp
|
||||
debugconsole.cpp debugconsole.ui
|
||||
toolbox.cpp toolbox.ui
|
||||
treeview.cpp treeview.ui
|
||||
qtkmainwindow.cpp qtkmainwindow.h qtkmainwindow.ui
|
||||
)
|
||||
set(
|
||||
QTK_PLUGIN_LIBRARY_HEADERS
|
||||
qtkwidget.h
|
||||
debugconsole.h
|
||||
toolbox.h
|
||||
treeview.h
|
||||
)
|
||||
qt_add_library(qtk_plugin_library STATIC EXCLUDE_FROM_ALL)
|
||||
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)
|
||||
|
||||
################################################################################
|
||||
# Qtk Widget Plugins
|
||||
################################################################################
|
||||
# Create a Qt Designer plugin for a collection of widgets from our library.
|
||||
qt_add_plugin(qtk_plugins SHARED)
|
||||
target_sources(
|
||||
qtk_plugins PRIVATE
|
||||
widgetplugincollection.cpp widgetplugincollection.h
|
||||
widgetplugin.cpp widgetplugin.h
|
||||
)
|
||||
target_link_libraries(qtk_plugins PUBLIC qtk_plugin_library)
|
||||
|
||||
################################################################################
|
||||
# Final Qtk Application
|
||||
# Qtk Application
|
||||
################################################################################
|
||||
|
||||
set(
|
||||
QTK_GUI_SOURCES
|
||||
qtkscene.cpp qtkscene.h
|
||||
main.cpp
|
||||
)
|
||||
# Build without the example scene if QTK_GUI_SCENE is unset.
|
||||
if (QTK_GUI_SCENE)
|
||||
set(QTK_GUI_SOURCES
|
||||
qtkmainwindow.cpp qtkmainwindow.h qtkmainwindow.ui
|
||||
# This scene will clone a git repository for required 3D models.
|
||||
qtkscene.cpp qtkscene.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
qt6_add_big_resources(QTK_GUI_SOURCES "${QTK_RESOURCES}/resources.qrc")
|
||||
else()
|
||||
# The scene will use a default skybox with no models or examples.
|
||||
# Models can be added by click-and-dragging an .obj into the scene.
|
||||
set(QTK_GUI_SOURCES
|
||||
qtkmainwindow.cpp qtkmainwindow.h qtkmainwindow.ui
|
||||
main.cpp
|
||||
)
|
||||
|
||||
qt6_add_big_resources(
|
||||
QTK_GUI_SOURCES
|
||||
"${QTK_RESOURCES}/minimal_resources.qrc"
|
||||
)
|
||||
endif()
|
||||
|
||||
qt_add_executable(qtk_gui ${QTK_GUI_SOURCES})
|
||||
target_link_libraries(qtk_gui PRIVATE qtk_plugin_library)
|
||||
|
||||
set_target_properties(
|
||||
qtk_gui PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
if (QTK_GUI_SCENE)
|
||||
target_compile_definitions(qtk_gui PRIVATE -DQTK_GUI_SCENE)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set_target_properties(qtk_gui PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
elseif(APPLE)
|
||||
set_target_properties(
|
||||
qtk_gui PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_BUNDLE_NAME Qtk
|
||||
MACOSX_BUNDLE_ICON_FILE ${QTK_OSX_ICONS}
|
||||
MACOSX_BUNDLE_ICON_FILE "${QTK_OSX_ICONS}"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER ${CMAKE_PROJECT_NAME}
|
||||
MACOSX_BUNDLE_INFO_STRING ${CMAKE_PROJECT_DESCRIPTION}
|
||||
MACOSX_BUNDLE_COPYRIGHT "All Content (c) 2023 Shaun Reed, all rights reserved"
|
||||
MACOSX_BUNDLE_COPYRIGHT ${QTK_COPYRIGHT}
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||
)
|
||||
)
|
||||
endif()
|
||||
|
||||
# qt_generate_deploy_app_script supports Windows and OSX in QtCore >= 6.3.
|
||||
# qt_generate_deploy_app_script is supported on Linux in QtCore >= 6.5.0.
|
||||
if((Qt6_VERSION VERSION_GREATER_EQUAL "6.3.0" AND (WIN32 OR APPLE))
|
||||
OR Qt6_VERSION VERSION_GREATER_EQUAL "6.5.0")
|
||||
install(
|
||||
TARGETS qtk_gui
|
||||
COMPONENT qtk_gui
|
||||
BUNDLE DESTINATION .
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
qt_generate_deploy_app_script(
|
||||
TARGET qtk_gui
|
||||
OUTPUT_SCRIPT QTK_DEPLOY_SCRIPT
|
||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||
)
|
||||
install(SCRIPT ${QTK_DEPLOY_SCRIPT} COMPONENT qtk_gui)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(MSVC AND TARGET Qt6::qmake)
|
||||
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
|
||||
)
|
||||
file(TO_NATIVE_PATH "${QT6_INSTALL_PREFIX}/bin" QT6_INSTALL_PREFIX)
|
||||
|
||||
set(VSUSER_FILE "${CMAKE_CURRENT_BINARY_DIR}/qtk_gui.vcxproj.user")
|
||||
file(WRITE ${VSUSER_FILE} "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
|
||||
file(APPEND ${VSUSER_FILE} "<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n")
|
||||
file(APPEND ${VSUSER_FILE} " <PropertyGroup>\n")
|
||||
file(APPEND ${VSUSER_FILE} " <LocalDebuggerEnvironment>Path=$(SolutionDir)\\lib\\$(Configuration);${QT6_INSTALL_PREFIX};$(Path)\n")
|
||||
file(APPEND ${VSUSER_FILE} "$(LocalDebuggerEnvironment)</LocalDebuggerEnvironment>\n")
|
||||
file(APPEND ${VSUSER_FILE} " <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n")
|
||||
file(APPEND ${VSUSER_FILE} " </PropertyGroup>\n")
|
||||
file(APPEND ${VSUSER_FILE} "</Project>\n")
|
||||
endif()
|
||||
endif()
|
||||
@@ -9,20 +9,22 @@
|
||||
#include <QApplication>
|
||||
|
||||
#include "qtkmainwindow.h"
|
||||
#include "qtkscene.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
Q_INIT_RESOURCE(resources);
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
initResources();
|
||||
QApplication a(argc, argv);
|
||||
|
||||
auto window = MainWindow::getMainWindow();
|
||||
|
||||
// Qtk currently uses the decorator pattern to save / load scenes.
|
||||
// This is a temporary solution and will be improved in the future.
|
||||
auto emptyScene = new Qtk::SceneEmpty;
|
||||
window->getQtkWidget()->setScene(new QtkScene(emptyScene));
|
||||
// NOTE: We set the scene here and not in QtkMainWindow to detach the scene
|
||||
// from the QtkWidget plugin (qtk_plugin_library build target).
|
||||
// Once we can save / load scenes, this call, and QtkScene, can be removed.
|
||||
window->setScene(new AppScene);
|
||||
|
||||
window->show();
|
||||
|
||||
return QApplication::exec();
|
||||
}
|
||||
|
||||
@@ -7,16 +7,14 @@
|
||||
##############################################################################*/
|
||||
|
||||
#include "qtkmainwindow.h"
|
||||
#include "qtkscene.h"
|
||||
#include "ui_qtkmainwindow.h"
|
||||
|
||||
MainWindow * MainWindow::mainWindow_ = Q_NULLPTR;
|
||||
|
||||
/*******************************************************************************
|
||||
* Constructors / Destructors
|
||||
******************************************************************************/
|
||||
|
||||
MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
|
||||
MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent)
|
||||
{
|
||||
ui_ = new Ui::MainWindow;
|
||||
setObjectName("MainWindow");
|
||||
// For use in design mode using Qt Creator
|
||||
@@ -26,19 +24,28 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
|
||||
|
||||
// Initialize static container for all active QtkWidgets
|
||||
auto qtkWidgets = findChildren<Qtk::QtkWidget *>();
|
||||
for(auto & qtkWidget : qtkWidgets) {
|
||||
qtkWidget->setScene(new Qtk::SceneEmpty);
|
||||
for (auto & qtkWidget : qtkWidgets) {
|
||||
qtkWidget->setMainWindow(this);
|
||||
// NOTE: Set a temporary scene for the widget to use for initialization.
|
||||
// This should be replaced by loading a scene, or creating a new (unsaved)
|
||||
// scene when Qtk is opened.
|
||||
qtkWidget->setScene(new EmptyScene);
|
||||
views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget);
|
||||
|
||||
// Add GUI 'view' toolbar option to show debug console.
|
||||
ui_->menuView->addAction(qtkWidget->getActionToggleConsole());
|
||||
|
||||
// Refresh GUI widgets when scene or objects are updated.
|
||||
connect(
|
||||
qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this,
|
||||
&MainWindow::refreshScene);
|
||||
connect(
|
||||
qtkWidget, &Qtk::QtkWidget::objectFocusChanged, ui_->qtk__ToolBox,
|
||||
&Qtk::ToolBox::updateFocus);
|
||||
connect(qtkWidget->getScene(),
|
||||
&Qtk::Scene::sceneUpdated,
|
||||
this,
|
||||
&MainWindow::refreshScene);
|
||||
|
||||
// Update the ToolBox details panel when an item is double-clicked.
|
||||
connect(qtkWidget,
|
||||
&Qtk::QtkWidget::objectFocusChanged,
|
||||
ui_->qtk__ToolBox,
|
||||
&Qtk::ToolBox::updateFocus);
|
||||
}
|
||||
|
||||
// TODO: Fix / use MainWindow in Qt Designer to add these dock widgets.
|
||||
@@ -55,10 +62,11 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
|
||||
ui_->menuView->addAction(ui_->qtk__TreeView->toggleViewAction());
|
||||
|
||||
// Set the window icon used for Qtk.
|
||||
setWindowIcon(Qtk::getIcon());
|
||||
setWindowIcon(getIcon());
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
@@ -66,28 +74,41 @@ MainWindow::~MainWindow() {
|
||||
* Public Methods
|
||||
******************************************************************************/
|
||||
|
||||
MainWindow * MainWindow::getMainWindow() {
|
||||
if(mainWindow_ == Q_NULLPTR) {
|
||||
mainWindow_ = new MainWindow;
|
||||
}
|
||||
return mainWindow_;
|
||||
MainWindow * MainWindow::getMainWindow()
|
||||
{
|
||||
static auto * window = new MainWindow;
|
||||
return window;
|
||||
}
|
||||
|
||||
Qtk::QtkWidget * MainWindow::getQtkWidget(int64_t index) {
|
||||
if(views_.size() <= index) {
|
||||
Qtk::QtkWidget * MainWindow::getQtkWidget(int64_t index)
|
||||
{
|
||||
if (views_.size() <= index) {
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
return views_.begin(index)->second;
|
||||
auto it = views_.begin();
|
||||
std::advance(it, index);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name) {
|
||||
if(!views_.count(name)) {
|
||||
Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name)
|
||||
{
|
||||
if (!views_.count(name)) {
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
return views_[name];
|
||||
}
|
||||
|
||||
void MainWindow::refreshScene(const QString & sceneName) {
|
||||
void MainWindow::refreshScene(const QString & sceneName)
|
||||
{
|
||||
// TODO: Select TreeView using sceneName
|
||||
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene());
|
||||
}
|
||||
|
||||
void MainWindow::setScene(Qtk::Scene * scene)
|
||||
{
|
||||
connect(scene,
|
||||
&Qtk::Scene::sceneUpdated,
|
||||
MainWindow::getMainWindow(),
|
||||
&MainWindow::refreshScene);
|
||||
ui_->qtk__QtkWidget->setScene(scene);
|
||||
}
|
||||
|
||||
@@ -14,18 +14,81 @@
|
||||
#include <QMainWindow>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#include "debugconsole.h"
|
||||
#include "qtkwidget.h"
|
||||
#include "designer-plugins/debugconsole.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* An empty scene used for initializing all QtkWidgets within the MainWindow.
|
||||
* This serves as a temporary placeholder for QtkScene (for example), which is
|
||||
* defined in the separate qtk_gui target. The reason for this separation is to
|
||||
* support the use of QtkWidgets (the qtk_plugins target) within the Qt Designer
|
||||
* application without implementations provided in the Qtk Desktop Application.
|
||||
*
|
||||
* For the Qtk application, this should be replaced by loading the previous
|
||||
* scene or creating a new _unsaved_ scene when the application is opened.
|
||||
* Currently we have essentially hard-coded QtkScene to use as examples for
|
||||
* testing the application. This means that the only way to create or modify a
|
||||
* scene is to write code. Any modifications made in the application, such as
|
||||
* moving or resizing objects, will not persist and cannot be saved.
|
||||
*
|
||||
* For users of Qtk Designer Plugins, this means that installing
|
||||
* the `qtk_plugins` target to Qt Designer allows use all of the designer's
|
||||
* features to build an interface and position or resize a QtkWidget as needed.
|
||||
* The QtkWidget also appears as widget in the IDE's toolbars and can be added
|
||||
* to any new application easily, once the plugins are installed.
|
||||
*
|
||||
* Once the application is designed, you can define a custom scene and use the
|
||||
* Qtk API or Qt OpenGL funtions directly to render to it.
|
||||
*
|
||||
* Any application using a QtkWidget can set a custom scene in their main
|
||||
* function. See the MainWindow::MainWindow constructor as an example.
|
||||
*/
|
||||
class EmptyScene : public Qtk::Scene
|
||||
{
|
||||
void init() override
|
||||
{
|
||||
setSkybox(new Qtk::Skybox(":/textures/skybox/right.png",
|
||||
":/textures/skybox/top.png",
|
||||
":/textures/skybox/front.png",
|
||||
":/textures/skybox/left.png",
|
||||
":/textures/skybox/bottom.png",
|
||||
":/textures/skybox/back.png",
|
||||
"Skybox"));
|
||||
setSceneName("Empty Scene");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Conditionally include the QtkScene header if the example is enabled.
|
||||
* Set AppScene type to use in main() for creating the scene.
|
||||
* Define helper function to initialize Qt resources for the application.
|
||||
* These resources are different based on if the example is enabled.
|
||||
*/
|
||||
#ifdef QTK_GUI_SCENE
|
||||
#include "qtkscene.h"
|
||||
using AppScene = QtkScene;
|
||||
inline void initResources()
|
||||
{
|
||||
Q_INIT_RESOURCE(resources);
|
||||
}
|
||||
#else
|
||||
using AppScene = EmptyScene;
|
||||
inline void initResources()
|
||||
{
|
||||
Q_INIT_RESOURCE(minimal_resources);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* MainWindow class to provide an example of using a QtkWidget within a Qt
|
||||
* window application.
|
||||
*/
|
||||
class MainWindow : public QMainWindow {
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -64,6 +127,16 @@ class MainWindow : public QMainWindow {
|
||||
*/
|
||||
Qtk::QtkWidget * getQtkWidget(const QString & name);
|
||||
|
||||
/**
|
||||
* @param scene The new scene to view.
|
||||
*/
|
||||
void setScene(Qtk::Scene * scene);
|
||||
|
||||
/**
|
||||
* @return Default icon to use for Qtk desktop application.
|
||||
*/
|
||||
static QIcon getIcon() { return QIcon(":/icons/icon.png"); }
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Trigger a refresh for widgets related to a scene that has been updated.
|
||||
@@ -80,7 +153,6 @@ class MainWindow : public QMainWindow {
|
||||
MainWindow(const MainWindow &) {};
|
||||
|
||||
Ui::MainWindow * ui_ {};
|
||||
static MainWindow * mainWindow_;
|
||||
|
||||
/**
|
||||
* Maps a scene name to the QtkWidget viewing it.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset>
|
||||
<normaloff>../resources/icon.png</normaloff>../resources/icon.png</iconset>
|
||||
<normaloff>../resources/icons/icon.png</normaloff>../resources/icons/icon.png</iconset>
|
||||
</property>
|
||||
<property name="unifiedTitleAndToolBarOnMac">
|
||||
<bool>true</bool>
|
||||
@@ -310,19 +310,19 @@
|
||||
<customwidget>
|
||||
<class>Qtk::QtkWidget</class>
|
||||
<extends>QOpenGLWidget</extends>
|
||||
<header>qtkwidget.h</header>
|
||||
<header>designer-plugins/qtkwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Qtk::TreeView</class>
|
||||
<extends>QDockWidget</extends>
|
||||
<header>treeview.h</header>
|
||||
<header>designer-plugins/treeview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Qtk::ToolBox</class>
|
||||
<extends>QDockWidget</extends>
|
||||
<header>toolbox.h</header>
|
||||
<header>designer-plugins/toolbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||
##############################################################################*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
|
||||
#include "qtkscene.h"
|
||||
|
||||
using namespace Qtk;
|
||||
@@ -14,13 +18,15 @@ using namespace Qtk;
|
||||
* Constructors, Destructors
|
||||
******************************************************************************/
|
||||
|
||||
QtkScene::QtkScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) {
|
||||
QtkScene::QtkScene()
|
||||
{
|
||||
setSceneName("Qtk Scene");
|
||||
getCamera().getTransform().setTranslation(0.0f, 0.0f, 20.0f);
|
||||
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
QtkScene::~QtkScene() {
|
||||
QtkScene::~QtkScene()
|
||||
{
|
||||
delete mTestPhong;
|
||||
delete mTestSpecular;
|
||||
delete mTestDiffuse;
|
||||
@@ -31,9 +37,40 @@ QtkScene::~QtkScene() {
|
||||
* Public Member Functions
|
||||
******************************************************************************/
|
||||
|
||||
void QtkScene::init() {
|
||||
void QtkScene::init()
|
||||
{
|
||||
// Clone qtk-resources if it doesn't already exist.
|
||||
QDir repoDir("resources/");
|
||||
if (!repoDir.exists()) {
|
||||
qDebug() << "Cloning qtk-resources repository to " << repoDir.absolutePath()
|
||||
<< "...";
|
||||
|
||||
// Run git clone
|
||||
QProcess gitProcess;
|
||||
gitProcess.setWorkingDirectory(QCoreApplication::applicationDirPath());
|
||||
gitProcess.start("git",
|
||||
{"clone",
|
||||
"https://git.shaunreed.com/shaunrd0/qtk-resources.git",
|
||||
repoDir.absolutePath()});
|
||||
|
||||
// Wait indefinitely for the process to complete
|
||||
gitProcess.waitForFinished(-1);
|
||||
// Check the result
|
||||
if (gitProcess.exitCode() == 0) {
|
||||
qDebug() << "Repository cloned successfully.";
|
||||
} else {
|
||||
qDebug() << "Git clone failed:" << gitProcess.readAllStandardError();
|
||||
}
|
||||
}
|
||||
|
||||
// Add a skybox to the scene using default cube map images and settings.
|
||||
setSkybox(new Qtk::Skybox("Skybox"));
|
||||
setSkybox(new Qtk::Skybox(":/textures/skybox/right.png",
|
||||
":/textures/skybox/top.png",
|
||||
":/textures/skybox/front.png",
|
||||
":/textures/skybox/left.png",
|
||||
":/textures/skybox/bottom.png",
|
||||
":/textures/skybox/back.png",
|
||||
"Skybox"));
|
||||
|
||||
/* Create a red cube with a mini master chief on top. */
|
||||
auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS));
|
||||
@@ -41,8 +78,7 @@ void QtkScene::init() {
|
||||
myCube->getTransform().setTranslation(5.0f, 0.0f, 0.0f);
|
||||
addObject(myCube);
|
||||
|
||||
auto mySpartan =
|
||||
new Model("My spartan", ":/models/models/spartan/spartan.obj");
|
||||
auto mySpartan = new Model("My spartan", QTK_SPARTAN);
|
||||
mySpartan->getTransform().setTranslation(5.0f, 0.5f, 0.0f);
|
||||
mySpartan->getTransform().setScale(0.5f);
|
||||
addObject(mySpartan);
|
||||
@@ -79,31 +115,27 @@ void QtkScene::init() {
|
||||
//
|
||||
// 3D Model loading
|
||||
|
||||
auto model = addObject(
|
||||
new Qtk::Model("backpack", ":/models/models/backpack/backpack.obj"));
|
||||
auto model = addObject(new Qtk::Model("backpack", QTK_BACKPACK));
|
||||
// Sometimes model textures need flipped in certain directions
|
||||
model->flipTexture("diffuse.jpg", false, true);
|
||||
model->getTransform().setTranslation(0.0f, 0.0f, -10.0f);
|
||||
|
||||
model = addObject(new Qtk::Model("bird", ":/models/models/bird/bird.obj"));
|
||||
model = addObject(new Qtk::Model("bird", QTK_BIRD));
|
||||
model->getTransform().setTranslation(2.0f, 2.0f, -10.0f);
|
||||
// Sometimes the models are very large
|
||||
model->getTransform().scale(0.0025f);
|
||||
model->getTransform().rotate(-110.0f, 0.0f, 1.0f, 0.0f);
|
||||
|
||||
model = addObject(
|
||||
new Qtk::Model("alien", ":/models/models/alien-hominid/alien.obj"));
|
||||
model = addObject(new Qtk::Model("alien", QTK_ALIEN));
|
||||
model->getTransform().setTranslation(2.0f, -1.0f, -5.0f);
|
||||
model->getTransform().scale(0.15f);
|
||||
|
||||
model = addObject(
|
||||
new Qtk::Model("My scythe", ":/models/models/scythe/scythe.obj"));
|
||||
model = addObject(new Qtk::Model("My scythe", QTK_SCYTHE));
|
||||
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);
|
||||
|
||||
model = addObject(
|
||||
new Qtk::Model("masterChief", ":/models/models/spartan/spartan.obj"));
|
||||
model = addObject(new Qtk::Model("masterChief", QTK_SPARTAN));
|
||||
model->getTransform().setTranslation(-1.5f, 0.5f, -2.0f);
|
||||
|
||||
|
||||
@@ -111,13 +143,13 @@ void QtkScene::init() {
|
||||
// Simple cube lighting examples.
|
||||
|
||||
/* Phong lighting example on a basic cube. */
|
||||
mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube());
|
||||
mTestPhong = addObject(new Qtk::MeshRenderer("phongCube", Qtk::Cube()));
|
||||
mTestPhong->getTransform().setTranslation(3.0f, 0.0f, -2.0f);
|
||||
// NOTE: You no longer need to manually bind shader program to set uniforms.
|
||||
// + You can still bind it if you want to for performance reasons.
|
||||
// + Qtk will only bind / release if the shader program is not already bound.
|
||||
mTestPhong->setShaders(
|
||||
":/shaders/solid-phong.vert", ":/shaders/solid-phong.frag");
|
||||
mTestPhong->setShaders(":/shaders/solid-phong.vert",
|
||||
":/shaders/solid-phong.frag");
|
||||
|
||||
// For example this would technically not be efficient, because each one of
|
||||
// these calls will bind, set, release. We could instead bind, set N uniforms,
|
||||
@@ -143,16 +175,16 @@ void QtkScene::init() {
|
||||
/* Example of a cube with no lighting applied */
|
||||
mesh = addObject(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS)));
|
||||
mesh->getTransform().setTranslation(5.0f, 0.0f, -2.0f);
|
||||
mesh->setShaders(
|
||||
":/shaders/solid-perspective.vert", ":/shaders/solid-perspective.frag");
|
||||
mesh->setShaders(":/shaders/solid-perspective.vert",
|
||||
":/shaders/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 */
|
||||
mTestAmbient = new Qtk::MeshRenderer("ambient", Cube());
|
||||
mTestAmbient = addObject(new Qtk::MeshRenderer("ambientCube", Cube()));
|
||||
mTestAmbient->getTransform().setTranslation(7.0f, 0.0f, -2.0f);
|
||||
mTestAmbient->setShaders(
|
||||
":/shaders/solid-ambient.vert", ":/shaders/solid-ambient.frag");
|
||||
mTestAmbient->setShaders(":/shaders/solid-ambient.vert",
|
||||
":/shaders/solid-ambient.frag");
|
||||
// Changing these uniform values will alter lighting effects.
|
||||
mTestAmbient->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
|
||||
mTestAmbient->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
|
||||
@@ -161,10 +193,10 @@ void QtkScene::init() {
|
||||
// No light source needed for this lighting technique
|
||||
|
||||
/* Initialize Diffuse example cube */
|
||||
mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube());
|
||||
mTestDiffuse = addObject(new Qtk::MeshRenderer("diffuseCube", Cube()));
|
||||
mTestDiffuse->getTransform().setTranslation(9.0f, 0.0f, -2.0f);
|
||||
mTestDiffuse->setShaders(
|
||||
":/shaders/solid-diffuse.vert", ":/shaders/solid-diffuse.frag");
|
||||
mTestDiffuse->setShaders(":/shaders/solid-diffuse.vert",
|
||||
":/shaders/solid-diffuse.frag");
|
||||
mTestDiffuse->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
|
||||
mTestDiffuse->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
|
||||
mTestDiffuse->setUniform("uAmbientStrength", 0.2f);
|
||||
@@ -177,10 +209,10 @@ void QtkScene::init() {
|
||||
mesh->getTransform().scale(0.25f);
|
||||
|
||||
/* Initialize Specular example cube */
|
||||
mTestSpecular = new Qtk::MeshRenderer("specular", Cube());
|
||||
mTestSpecular = addObject(new Qtk::MeshRenderer("specularCube", Cube()));
|
||||
mTestSpecular->getTransform().setTranslation(11.0f, 0.0f, -2.0f);
|
||||
mTestSpecular->setShaders(
|
||||
":/shaders/solid-specular.vert", ":/shaders/solid-specular.frag");
|
||||
mTestSpecular->setShaders(":/shaders/solid-specular.vert",
|
||||
":/shaders/solid-specular.frag");
|
||||
mTestSpecular->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
|
||||
mTestSpecular->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
|
||||
mTestSpecular->setUniform("uAmbientStrength", 0.2f);
|
||||
@@ -228,9 +260,10 @@ void QtkScene::init() {
|
||||
// Building more complex objects for showing examples of lighting techniques
|
||||
|
||||
/* Test alien Model with phong lighting and specular mapping. */
|
||||
model = addObject(new Qtk::Model(
|
||||
"alienTest", ":/models/models/alien-hominid/alien.obj",
|
||||
":/shaders/model-specular.vert", ":/shaders/model-specular.frag"));
|
||||
model = addObject(new Qtk::Model("alienTest",
|
||||
QTK_ALIEN,
|
||||
":/shaders/model-phong.vert",
|
||||
":/shaders/model-phong.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));
|
||||
@@ -238,7 +271,7 @@ void QtkScene::init() {
|
||||
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.specularStrength", 0.5f);
|
||||
model->setUniform("uMaterial.shine", 32.0f);
|
||||
|
||||
model->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
|
||||
@@ -246,17 +279,18 @@ void QtkScene::init() {
|
||||
model->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
|
||||
|
||||
// Light source for alienTest object.
|
||||
mesh = addObject(new Qtk::MeshRenderer(
|
||||
"alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS)));
|
||||
mesh = addObject(new Qtk::MeshRenderer("alienTestLight",
|
||||
Triangle(Qtk::QTK_DRAW_ELEMENTS)));
|
||||
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
|
||||
mesh->setColor(GREEN);
|
||||
|
||||
/* Test spartan Model with phong lighting, specular and normal mapping. */
|
||||
model = addObject(new Qtk::Model(
|
||||
"spartanTest", ":/models/models/spartan/spartan.obj",
|
||||
":/shaders/model-normals.vert", ":/shaders/model-normals.frag"));
|
||||
model = addObject(new Qtk::Model("spartanTest",
|
||||
QTK_SPARTAN,
|
||||
":/shaders/model-phong.vert",
|
||||
":/shaders/model-phong.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));
|
||||
@@ -290,8 +324,8 @@ void QtkScene::init() {
|
||||
mesh->reallocateNormals(mesh->getNormals());
|
||||
|
||||
// RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS
|
||||
mesh = addObject(new Qtk::MeshRenderer(
|
||||
"rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
|
||||
mesh = addObject(new Qtk::MeshRenderer("rgbNormalsCubeElementsTest",
|
||||
Cube(QTK_DRAW_ELEMENTS_NORMALS)));
|
||||
mesh->getTransform().setTranslation(5.0f, 0.0f, 2.0f);
|
||||
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
|
||||
mesh->reallocateNormals(mesh->getNormals());
|
||||
@@ -321,8 +355,8 @@ void QtkScene::init() {
|
||||
mesh->reallocateTexCoords(mesh->getTexCoords());
|
||||
|
||||
// Test drawing a cube with texture coordinates using glDrawElements
|
||||
mesh = addObject(new Qtk::MeshRenderer(
|
||||
"uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
|
||||
mesh = addObject(new Qtk::MeshRenderer("uvCubeElementsTest",
|
||||
Cube(QTK_DRAW_ELEMENTS_NORMALS)));
|
||||
mesh->getTransform().setTranslation(-1.7f, 0.0f, -2.0f);
|
||||
mesh->setTexture(":/textures/crate.png");
|
||||
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
|
||||
@@ -339,8 +373,8 @@ void QtkScene::init() {
|
||||
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(
|
||||
":/shaders/texture-cubemap.vert", ":/shaders/texture-cubemap.frag");
|
||||
mesh->setShaders(":/shaders/texture-cubemap.vert",
|
||||
":/shaders/texture-cubemap.frag");
|
||||
mesh->setCubeMap(":/textures/crate.png");
|
||||
mesh->setUniform("uTexture", 0);
|
||||
mesh->reallocateTexCoords(mesh->getTexCoords());
|
||||
@@ -354,15 +388,15 @@ void QtkScene::init() {
|
||||
mesh->reallocateNormals(mesh->getNormals());
|
||||
|
||||
// RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS
|
||||
mesh = addObject(new Qtk::MeshRenderer(
|
||||
"rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS)));
|
||||
mesh = addObject(new Qtk::MeshRenderer("rgbTriangleArraysTest",
|
||||
Triangle(QTK_DRAW_ARRAYS)));
|
||||
mesh->getTransform().setTranslation(7.0f, 0.0f, 2.0f);
|
||||
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
|
||||
mesh->reallocateNormals(mesh->getNormals());
|
||||
|
||||
// RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS
|
||||
mesh = addObject(new Qtk::MeshRenderer(
|
||||
"rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
|
||||
mesh = addObject(new Qtk::MeshRenderer("rgbTriangleElementsTest",
|
||||
Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
|
||||
mesh->getTransform().setTranslation(7.0f, 0.0f, 4.0f);
|
||||
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
|
||||
mesh->reallocateNormals(mesh->getNormals());
|
||||
@@ -378,8 +412,8 @@ void QtkScene::init() {
|
||||
mesh->reallocateTexCoords(mesh->getTexCoords());
|
||||
|
||||
// Test drawing triangle with glDrawElements with texture coordinates
|
||||
mesh = addObject(new Qtk::MeshRenderer(
|
||||
"testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
|
||||
mesh = addObject(new Qtk::MeshRenderer("testTriangleElementsUV",
|
||||
Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
|
||||
mesh->getTransform().setTranslation(-2.5f, 0.0f, -1.0f);
|
||||
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
|
||||
mesh->setTexture(":/textures/crate.png");
|
||||
@@ -387,26 +421,25 @@ void QtkScene::init() {
|
||||
mesh->reallocateTexCoords(mesh->getTexCoords());
|
||||
}
|
||||
|
||||
void QtkScene::draw() {
|
||||
void QtkScene::draw()
|
||||
{
|
||||
// WARNING: We must call the base class draw() function first.
|
||||
// + This will handle rendering core scene components like the Skybox.
|
||||
Scene::draw();
|
||||
const QVector3D cameraPosition = getCamera().getTransform().getTranslation();
|
||||
|
||||
mTestPhong->bindShaders();
|
||||
mTestPhong->setUniform(
|
||||
"uModelInverseTransposed",
|
||||
mTestPhong->getTransform().toMatrix().normalMatrix());
|
||||
mTestPhong->setUniform("uModelInverseTransposed",
|
||||
mTestPhong->getTransform().toMatrix().normalMatrix());
|
||||
mTestPhong->setUniform(
|
||||
"uLightPosition",
|
||||
MeshRenderer::getInstance("phongLight")->getTransform().getTranslation());
|
||||
mTestPhong->setUniform(
|
||||
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||
mTestPhong->setUniform("uCameraPosition", cameraPosition);
|
||||
mTestPhong->releaseShaders();
|
||||
mTestPhong->draw();
|
||||
|
||||
mTestAmbient->bindShaders();
|
||||
mTestAmbient->setUniform(
|
||||
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||
mTestAmbient->setUniform("uCameraPosition", cameraPosition);
|
||||
mTestAmbient->releaseShaders();
|
||||
mTestAmbient->draw();
|
||||
|
||||
@@ -414,12 +447,11 @@ void QtkScene::draw() {
|
||||
mTestDiffuse->setUniform(
|
||||
"uModelInverseTransposed",
|
||||
mTestDiffuse->getTransform().toMatrix().normalMatrix());
|
||||
mTestDiffuse->setUniform(
|
||||
"uLightPosition", MeshRenderer::getInstance("diffuseLight")
|
||||
->getTransform()
|
||||
.getTranslation());
|
||||
mTestDiffuse->setUniform(
|
||||
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||
mTestDiffuse->setUniform("uLightPosition",
|
||||
MeshRenderer::getInstance("diffuseLight")
|
||||
->getTransform()
|
||||
.getTranslation());
|
||||
mTestDiffuse->setUniform("uCameraPosition", cameraPosition);
|
||||
mTestDiffuse->releaseShaders();
|
||||
mTestDiffuse->draw();
|
||||
|
||||
@@ -427,70 +459,86 @@ void QtkScene::draw() {
|
||||
mTestSpecular->setUniform(
|
||||
"uModelInverseTransposed",
|
||||
mTestSpecular->getTransform().toMatrix().normalMatrix());
|
||||
mTestSpecular->setUniform(
|
||||
"uLightPosition", MeshRenderer::getInstance("specularLight")
|
||||
->getTransform()
|
||||
.getTranslation());
|
||||
mTestSpecular->setUniform(
|
||||
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||
mTestSpecular->setUniform("uLightPosition",
|
||||
MeshRenderer::getInstance("specularLight")
|
||||
->getTransform()
|
||||
.getTranslation());
|
||||
mTestSpecular->setUniform("uCameraPosition", cameraPosition);
|
||||
mTestSpecular->releaseShaders();
|
||||
mTestSpecular->draw();
|
||||
}
|
||||
|
||||
void QtkScene::update() {
|
||||
auto mySpartan = Model::getInstance("My spartan");
|
||||
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
||||
void QtkScene::update()
|
||||
{
|
||||
auto getModel = Model::getInstance;
|
||||
const QVector3D cameraPosition = getCamera().getTransform().getTranslation();
|
||||
|
||||
auto myCube = MeshRenderer::getInstance("My cube");
|
||||
myCube->getTransform().rotate(-0.75f, 0.0f, 1.0f, 0.0f);
|
||||
// Models may have failed to load, so we should check before accessing.
|
||||
if (auto mySpartan = getModel("My spartan"); mySpartan) {
|
||||
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
auto position = MeshRenderer::getInstance("alienTestLight")
|
||||
->getTransform()
|
||||
.getTranslation();
|
||||
auto alien = Model::getInstance("alienTest");
|
||||
alien->setUniform("uLight.position", position);
|
||||
alien->setUniform(
|
||||
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||
auto posMatrix = alien->getTransform().toMatrix();
|
||||
alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
||||
alien->setUniform("uMVP.model", posMatrix);
|
||||
alien->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
||||
alien->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
||||
alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
||||
if (auto myCube = getModel("My cube"); myCube) {
|
||||
myCube->getTransform().rotate(-0.75f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
position = MeshRenderer::getInstance("spartanTestLight")
|
||||
->getTransform()
|
||||
.getTranslation();
|
||||
auto spartan = Model::getInstance("spartanTest");
|
||||
spartan->setUniform("uLight.position", position);
|
||||
spartan->setUniform(
|
||||
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||
posMatrix = spartan->getTransform().toMatrix();
|
||||
spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
||||
spartan->setUniform("uMVP.model", posMatrix);
|
||||
spartan->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
||||
spartan->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
||||
spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
||||
// Helper lambda to set the light position used by GLSL shaders on the model.
|
||||
// TODO: This could be a helper function on the Model class.
|
||||
auto setLightPosition = [](const std::string & lightName, Model * model) {
|
||||
if (auto light = Model::getInstance(lightName.c_str()); light) {
|
||||
QVector3D position = light->getTransform().getTranslation();
|
||||
model->setUniform("uLight.position", position);
|
||||
} else {
|
||||
qDebug() << "[QtkScene] Failed to set light position: "
|
||||
<< lightName.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
auto phong = MeshRenderer::getInstance("testPhong");
|
||||
phong->getTransform().rotate(0.75f, 1.0f, 0.5f, 0.0f);
|
||||
phong->bindShaders();
|
||||
position =
|
||||
MeshRenderer::getInstance("testLight")->getTransform().getTranslation();
|
||||
phong->setUniform("uLight.position", position);
|
||||
phong->setUniform(
|
||||
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||
posMatrix = phong->getTransform().toMatrix();
|
||||
phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
||||
phong->setUniform("uMVP.model", posMatrix);
|
||||
phong->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
||||
phong->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
||||
phong->releaseShaders();
|
||||
QMatrix4x4 posMatrix;
|
||||
if (auto alien = getModel("alienTest"); alien) {
|
||||
setLightPosition("alienTestLight", alien);
|
||||
|
||||
alien->setUniform("uCameraPosition", cameraPosition);
|
||||
posMatrix = alien->getTransform().toMatrix();
|
||||
alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
||||
alien->setUniform("uMVP.model", posMatrix);
|
||||
alien->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
||||
alien->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
||||
alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
if (auto spartan = getModel("spartanTest"); spartan) {
|
||||
setLightPosition("spartanTestLight", spartan);
|
||||
|
||||
spartan->setUniform("uCameraPosition", cameraPosition);
|
||||
posMatrix = spartan->getTransform().toMatrix();
|
||||
spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
||||
spartan->setUniform("uMVP.model", posMatrix);
|
||||
spartan->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
||||
spartan->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
||||
spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
if (auto phong = getModel("testPhong"); phong) {
|
||||
setLightPosition("testLight", phong);
|
||||
|
||||
phong->getTransform().rotate(0.75f, 1.0f, 0.5f, 0.0f);
|
||||
phong->bindShaders();
|
||||
phong->setUniform("uCameraPosition", cameraPosition);
|
||||
posMatrix = phong->getTransform().toMatrix();
|
||||
phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
||||
phong->setUniform("uMVP.model", posMatrix);
|
||||
phong->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
||||
phong->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
||||
phong->releaseShaders();
|
||||
}
|
||||
|
||||
// MeshRenderers are lower level opengl objects baked into the source code.
|
||||
auto getMesh = MeshRenderer::getInstance;
|
||||
|
||||
// Rotate lighting example cubes
|
||||
mTestPhong->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
||||
MeshRenderer::getInstance("noLight")->getTransform().rotate(
|
||||
0.75f, 0.5f, 0.3f, 0.2f);
|
||||
getMesh("noLight")->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
||||
mTestAmbient->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
||||
mTestDiffuse->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
||||
mTestSpecular->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
||||
@@ -498,46 +546,27 @@ void QtkScene::update() {
|
||||
// Examples of various translations and rotations
|
||||
|
||||
// Rotate in multiple directions simultaneously
|
||||
MeshRenderer::getInstance("rgbNormalsCube")
|
||||
->getTransform()
|
||||
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
|
||||
getMesh("rgbNormalsCube")->getTransform().rotate(0.75f, 0.2f, 0.4f, 0.6f);
|
||||
|
||||
// Pitch forward and roll sideways
|
||||
MeshRenderer::getInstance("leftTriangle")
|
||||
->getTransform()
|
||||
.rotate(0.75f, 1.0f, 0.0f, 0.0f);
|
||||
MeshRenderer::getInstance("rightTriangle")
|
||||
->getTransform()
|
||||
.rotate(0.75f, 0.0f, 0.0f, 1.0f);
|
||||
getMesh("leftTriangle")->getTransform().rotate(0.75f, 1.0f, 0.0f, 0.0f);
|
||||
getMesh("rightTriangle")->getTransform().rotate(0.75f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
// Move between two positions over time
|
||||
static float translateX = 0.025f;
|
||||
float limit = -9.0f; // Origin position.x - 2.0f
|
||||
float posX = MeshRenderer::getInstance("topTriangle")
|
||||
->getTransform()
|
||||
.getTranslation()
|
||||
.x();
|
||||
if(posX < limit || posX > limit + 4.0f) {
|
||||
float posX = getMesh("topTriangle")->getTransform().getTranslation().x();
|
||||
if (posX < limit || posX > limit + 4.0f) {
|
||||
translateX = -translateX;
|
||||
}
|
||||
MeshRenderer::getInstance("topTriangle")
|
||||
->getTransform()
|
||||
.translate(translateX, 0.0f, 0.0f);
|
||||
MeshRenderer::getInstance("bottomTriangle")
|
||||
->getTransform()
|
||||
.translate(-translateX, 0.0f, 0.0f);
|
||||
getMesh("topTriangle")->getTransform().translate(translateX, 0.0f, 0.0f);
|
||||
getMesh("bottomTriangle")->getTransform().translate(-translateX, 0.0f, 0.0f);
|
||||
// And lets rotate the triangles in two directions at once
|
||||
MeshRenderer::getInstance("topTriangle")
|
||||
->getTransform()
|
||||
.rotate(0.75f, 0.2f, 0.0f, 0.4f);
|
||||
MeshRenderer::getInstance("bottomTriangle")
|
||||
->getTransform()
|
||||
.rotate(0.75f, 0.0f, 0.2f, 0.4f);
|
||||
getMesh("topTriangle")->getTransform().rotate(0.75f, 0.2f, 0.0f, 0.4f);
|
||||
getMesh("bottomTriangle")->getTransform().rotate(0.75f, 0.0f, 0.2f, 0.4f);
|
||||
// And make the bottom triangle green, instead of RGB
|
||||
|
||||
// Rotate center cube in several directions simultaneously
|
||||
// + Not subject to gimbal lock since we are using quaternions :)
|
||||
MeshRenderer::getInstance("centerCube")
|
||||
->getTransform()
|
||||
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
|
||||
getMesh("centerCube")->getTransform().rotate(0.75f, 0.2f, 0.4f, 0.6f);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
#include <qtk/scene.h>
|
||||
|
||||
#define QTK_SPARTAN "resources/models/spartan/spartan.obj"
|
||||
#define QTK_BACKPACK "resources/models/backpack/backpack.obj"
|
||||
#define QTK_BIRD "resources/models/bird/bird.obj"
|
||||
#define QTK_ALIEN "resources/models/alien-hominid/alien.obj"
|
||||
#define QTK_SCYTHE "resources/models/scythe/scythe.obj"
|
||||
|
||||
/**
|
||||
* Example scene using QtkWidget to render 3D models and simple geometry within
|
||||
* QtOpenGLWidgets. This scene also shows some examples of using GLSL shaders to
|
||||
@@ -29,13 +35,14 @@
|
||||
*
|
||||
* To create your own Scene from scratch see Qtk::Scene.
|
||||
*/
|
||||
class QtkScene : public Qtk::SceneInterface {
|
||||
class QtkScene : public Qtk::Scene
|
||||
{
|
||||
public:
|
||||
/***************************************************************************
|
||||
* Contructors / Destructors
|
||||
**************************************************************************/
|
||||
|
||||
QtkScene(Qtk::Scene * scene);
|
||||
QtkScene();
|
||||
|
||||
~QtkScene();
|
||||
|
||||
|
||||
56
src/designer-plugins/CMakeLists.txt
Normal file
56
src/designer-plugins/CMakeLists.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
################################################################################
|
||||
## Project for working with OpenGL and Qt6 widgets ##
|
||||
## ##
|
||||
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
||||
## All Content (c) 2025 Shaun Reed, all rights reserved ##
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
# Qtk Widget Library
|
||||
################################################################################
|
||||
# Create a library of widgets used to build Qtk GUI
|
||||
set(
|
||||
QTK_PLUGIN_LIBRARY_SOURCES
|
||||
qtkwidget.cpp
|
||||
debugconsole.cpp debugconsole.ui
|
||||
toolbox.cpp toolbox.ui
|
||||
treeview.cpp treeview.ui
|
||||
)
|
||||
set(
|
||||
QTK_PLUGIN_LIBRARY_HEADERS
|
||||
qtkwidget.h
|
||||
debugconsole.h
|
||||
toolbox.h
|
||||
treeview.h
|
||||
)
|
||||
qt_add_library(qtk_plugin_library STATIC EXCLUDE_FROM_ALL)
|
||||
target_sources(
|
||||
qtk_plugin_library PRIVATE
|
||||
"${QTK_PLUGIN_LIBRARY_SOURCES}"
|
||||
"${QTK_PLUGIN_LIBRARY_HEADERS}"
|
||||
)
|
||||
target_link_libraries(qtk_plugin_library PUBLIC Qt6::UiPlugin qtk)
|
||||
|
||||
################################################################################
|
||||
# Qtk Widget Plugins
|
||||
################################################################################
|
||||
# Create a Qt Designer plugin for a collection of widgets from our library.
|
||||
qt_add_plugin(qtk_plugins SHARED)
|
||||
target_sources(
|
||||
qtk_plugins PRIVATE
|
||||
widgetplugincollection.cpp widgetplugincollection.h
|
||||
widgetplugin.cpp widgetplugin.h
|
||||
)
|
||||
target_link_libraries(qtk_plugins PUBLIC qtk_plugin_library)
|
||||
|
||||
# Install the Qt Designer plugins only if QTK_PLUGINS is set.
|
||||
# Otherwise, we just use them for building the Qtk desktop application.
|
||||
if(QTK_PLUGINS)
|
||||
install(
|
||||
TARGETS qtk_plugins qtk qtk_plugin_library
|
||||
COMPONENT qtk_plugins
|
||||
LIBRARY DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
||||
ARCHIVE DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
||||
RUNTIME DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
||||
)
|
||||
endif()
|
||||
@@ -15,10 +15,14 @@
|
||||
using namespace Qtk;
|
||||
|
||||
DebugConsole::DebugConsole(QWidget * owner, const QString & key) :
|
||||
DebugConsole(owner, key, key + "Debugger") {}
|
||||
DebugConsole(owner, key, key + "Debugger")
|
||||
{
|
||||
}
|
||||
|
||||
DebugConsole::DebugConsole(
|
||||
QWidget * owner, const QString & key, const QString & name) {
|
||||
DebugConsole::DebugConsole(QWidget * owner,
|
||||
const QString & key,
|
||||
const QString & name)
|
||||
{
|
||||
ui_ = new Ui::DebugConsole;
|
||||
ui_->setupUi(this);
|
||||
setObjectName(name);
|
||||
@@ -27,7 +31,7 @@ DebugConsole::DebugConsole(
|
||||
setWindowTitle(name + " Debug Console");
|
||||
|
||||
auto qtkWidget = dynamic_cast<QtkWidget *>(owner);
|
||||
if(qtkWidget) {
|
||||
if (qtkWidget) {
|
||||
connect(qtkWidget, &QtkWidget::sendLog, this, &DebugConsole::sendLog);
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,17 @@
|
||||
#include <QPlainTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "qtkwidget.h"
|
||||
#include "designer-plugins/qtkwidget.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace Ui
|
||||
{
|
||||
class DebugConsole;
|
||||
}
|
||||
|
||||
namespace Qtk {
|
||||
class DebugConsole : public QDockWidget {
|
||||
namespace Qtk
|
||||
{
|
||||
class DebugConsole : public QDockWidget
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
@@ -61,7 +64,8 @@ namespace Qtk {
|
||||
* @param context The DebugContext to use for the message.
|
||||
* Default value is Status.
|
||||
*/
|
||||
inline void sendLog(QString message, DebugContext context = Status) {
|
||||
inline void sendLog(QString message, DebugContext context = Status)
|
||||
{
|
||||
mConsole->setTextColor(logColor(context));
|
||||
mConsole->append(logPrefix(message, context));
|
||||
}
|
||||
@@ -72,7 +76,8 @@ namespace Qtk {
|
||||
*
|
||||
* @param name Base name for the DebugConsole window.
|
||||
*/
|
||||
inline void setTitle(QString name) {
|
||||
inline void setTitle(const QString & name)
|
||||
{
|
||||
setWindowTitle(name + " Debug Console");
|
||||
}
|
||||
|
||||
@@ -81,8 +86,9 @@ namespace Qtk {
|
||||
* @param context Log context severity level.
|
||||
* @return QColor corresponding with the message context.
|
||||
*/
|
||||
[[nodiscard]] QColor logColor(const DebugContext & context) const {
|
||||
switch(context) {
|
||||
[[nodiscard]] QColor logColor(const DebugContext & context) const
|
||||
{
|
||||
switch (context) {
|
||||
case Status:
|
||||
return Qt::GlobalColor::darkGray;
|
||||
case Debug:
|
||||
@@ -105,10 +111,11 @@ namespace Qtk {
|
||||
* @param context The log context severity level.
|
||||
* @return The log message prefixed with the DebugContext level.
|
||||
*/
|
||||
[[nodiscard]] QString logPrefix(
|
||||
QString & message, const DebugContext & context) {
|
||||
[[nodiscard]] QString logPrefix(QString & message,
|
||||
const DebugContext & context)
|
||||
{
|
||||
QString prefix;
|
||||
switch(context) {
|
||||
switch (context) {
|
||||
case Status:
|
||||
prefix = "[Status]: ";
|
||||
break;
|
||||
@@ -7,24 +7,21 @@
|
||||
##############################################################################*/
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QMainWindow>
|
||||
#include <QMimeData>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <qtk/input.h>
|
||||
#include <qtk/scene.h>
|
||||
#include <qtk/shape.h>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <qtk/input.h>
|
||||
#include <qtk/scene.h>
|
||||
#include <qtk/shape.h>
|
||||
#include "qtk/input.h"
|
||||
#include "qtk/scene.h"
|
||||
#include "qtk/shape.h"
|
||||
|
||||
#include "debugconsole.h"
|
||||
#include "qtkmainwindow.h"
|
||||
#include "qtkwidget.h"
|
||||
|
||||
using namespace Qtk;
|
||||
|
||||
/// Static manager for all QtkWidget instances.
|
||||
QtkWidgetManager QtkWidget::mWidgetManager;
|
||||
|
||||
/*******************************************************************************
|
||||
* Constructors, Destructors
|
||||
******************************************************************************/
|
||||
@@ -32,14 +29,19 @@ using namespace Qtk;
|
||||
QtkWidget::QtkWidget(QWidget * parent) : QtkWidget(parent, "QtkWidget") {}
|
||||
|
||||
QtkWidget::QtkWidget(QWidget * parent, const QString & name) :
|
||||
QtkWidget(parent, name, Q_NULLPTR) {}
|
||||
QtkWidget(parent, name, Q_NULLPTR)
|
||||
{
|
||||
}
|
||||
|
||||
QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
|
||||
QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR),
|
||||
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR) {
|
||||
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR)
|
||||
{
|
||||
setObjectName(name);
|
||||
mWidgetManager.add_widget(this);
|
||||
|
||||
setAcceptDrops(true);
|
||||
setScene(scene);
|
||||
setObjectName(name);
|
||||
QSurfaceFormat format;
|
||||
format.setRenderableType(QSurfaceFormat::OpenGL);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
@@ -54,7 +56,8 @@ QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
}
|
||||
|
||||
QtkWidget::~QtkWidget() {
|
||||
QtkWidget::~QtkWidget()
|
||||
{
|
||||
makeCurrent();
|
||||
teardownGL();
|
||||
}
|
||||
@@ -63,7 +66,8 @@ QtkWidget::~QtkWidget() {
|
||||
* Public Methods
|
||||
******************************************************************************/
|
||||
|
||||
QAction * QtkWidget::getActionToggleConsole() {
|
||||
QAction * QtkWidget::getActionToggleConsole()
|
||||
{
|
||||
auto action = new QAction(mScene->getSceneName() + " debug console");
|
||||
action->setCheckable(true);
|
||||
action->setChecked(mConsoleActive);
|
||||
@@ -72,20 +76,40 @@ QAction * QtkWidget::getActionToggleConsole() {
|
||||
return action;
|
||||
}
|
||||
|
||||
void QtkWidget::initializeGL() {
|
||||
void QtkWidget::initializeGL()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
// Connect the frameSwapped signal to call the update() function
|
||||
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
|
||||
|
||||
toggleConsole();
|
||||
// Add the debug console widget to the window and set its hidden state.
|
||||
if (mMainWindow != nullptr) {
|
||||
mMainWindow->addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea,
|
||||
mConsole);
|
||||
}
|
||||
mConsole->setHidden(!mConsoleActive);
|
||||
|
||||
// Initialize OpenGL debug context
|
||||
mDebugLogger = new QOpenGLDebugLogger(this);
|
||||
if(mDebugLogger->initialize()) {
|
||||
if (mDebugLogger->initialize()) {
|
||||
qDebug() << "GL_DEBUG Debug Logger" << mDebugLogger << "\n";
|
||||
connect(
|
||||
mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this,
|
||||
SLOT(messageLogged(QOpenGLDebugMessage)));
|
||||
connect(mDebugLogger,
|
||||
SIGNAL(messageLogged(QOpenGLDebugMessage)),
|
||||
this,
|
||||
SLOT(messageLogged(QOpenGLDebugMessage)));
|
||||
mDebugLogger->startLogging();
|
||||
mConsole->sendLog(
|
||||
"Object files such as .obj can be dragged into the scene to load new "
|
||||
"models.",
|
||||
DebugContext::Warn);
|
||||
mConsole->sendLog("Click and hold LMB or RMB to move the camera with WASD.",
|
||||
DebugContext::Warn);
|
||||
mConsole->sendLog(
|
||||
"Click an object name in the side panel to view or modify properties.",
|
||||
DebugContext::Warn);
|
||||
mConsole->sendLog(
|
||||
"Double click an object name to move the camera to it's position.",
|
||||
DebugContext::Warn);
|
||||
}
|
||||
|
||||
printContextInformation();
|
||||
@@ -101,63 +125,58 @@ void QtkWidget::initializeGL() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void QtkWidget::resizeGL(int width, int height) {
|
||||
void QtkWidget::resizeGL(int width, int height)
|
||||
{
|
||||
Scene::getProjectionMatrix().setToIdentity();
|
||||
Scene::getProjectionMatrix().perspective(
|
||||
45.0f, float(width) / float(height), 0.1f, 1000.0f);
|
||||
}
|
||||
|
||||
void QtkWidget::paintGL() {
|
||||
void QtkWidget::paintGL()
|
||||
{
|
||||
// Clear buffers and draw the scene if it is valid.
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
if(mScene != Q_NULLPTR) {
|
||||
if (mScene != Q_NULLPTR) {
|
||||
mScene->draw();
|
||||
}
|
||||
}
|
||||
|
||||
void QtkWidget::setScene(Scene * scene) {
|
||||
if(mScene != Q_NULLPTR) {
|
||||
delete mScene;
|
||||
connect(
|
||||
scene, &Scene::sceneUpdated, MainWindow::getMainWindow(),
|
||||
&MainWindow::refreshScene);
|
||||
}
|
||||
void QtkWidget::setScene(Scene * scene)
|
||||
{
|
||||
delete mScene;
|
||||
|
||||
|
||||
mScene = scene;
|
||||
if(mScene != Q_NULLPTR) {
|
||||
if (mScene != Q_NULLPTR) {
|
||||
mConsole->setTitle(mScene->getSceneName());
|
||||
} else {
|
||||
mConsole->setTitle("Null Scene");
|
||||
}
|
||||
}
|
||||
|
||||
void QtkWidget::toggleConsole() {
|
||||
if(mConsoleActive) {
|
||||
mConsole->setHidden(true);
|
||||
mConsoleActive = false;
|
||||
} else {
|
||||
MainWindow::getMainWindow()->addDockWidget(
|
||||
Qt::DockWidgetArea::BottomDockWidgetArea, mConsole);
|
||||
mConsole->setHidden(false);
|
||||
mConsoleActive = true;
|
||||
}
|
||||
void QtkWidget::toggleConsole()
|
||||
{
|
||||
mConsole->setHidden(mConsoleActive);
|
||||
mConsoleActive = !mConsoleActive;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Protected Methods
|
||||
******************************************************************************/
|
||||
|
||||
void QtkWidget::dragEnterEvent(QDragEnterEvent * event) {
|
||||
if(event->mimeData()->hasFormat("text/plain")) {
|
||||
void QtkWidget::dragEnterEvent(QDragEnterEvent * event)
|
||||
{
|
||||
if (event->mimeData()->hasFormat("text/plain")) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void QtkWidget::dropEvent(QDropEvent * event) {
|
||||
void QtkWidget::dropEvent(QDropEvent * event)
|
||||
{
|
||||
mConsole->sendLog(event->mimeData()->text());
|
||||
auto urls = event->mimeData()->urls();
|
||||
if(!urls.isEmpty()) {
|
||||
if(urls.size() > 1) {
|
||||
if (!urls.isEmpty()) {
|
||||
if (urls.size() > 1) {
|
||||
qDebug() << "Cannot accept drop of multiple files.";
|
||||
event->ignore();
|
||||
return;
|
||||
@@ -165,7 +184,7 @@ void QtkWidget::dropEvent(QDropEvent * event) {
|
||||
|
||||
// TODO: Support other object types.
|
||||
auto url = urls.front();
|
||||
if(url.fileName().endsWith(".obj")) {
|
||||
if (url.fileName().endsWith(".obj")) {
|
||||
mScene->loadModel(url);
|
||||
event->acceptProposedAction();
|
||||
} else {
|
||||
@@ -175,8 +194,9 @@ void QtkWidget::dropEvent(QDropEvent * event) {
|
||||
}
|
||||
}
|
||||
|
||||
void QtkWidget::keyPressEvent(QKeyEvent * event) {
|
||||
if(event->isAutoRepeat()) {
|
||||
void QtkWidget::keyPressEvent(QKeyEvent * event)
|
||||
{
|
||||
if (event->isAutoRepeat()) {
|
||||
// Do not repeat input while a key is held down
|
||||
event->ignore();
|
||||
} else {
|
||||
@@ -184,38 +204,43 @@ void QtkWidget::keyPressEvent(QKeyEvent * event) {
|
||||
}
|
||||
}
|
||||
|
||||
void QtkWidget::keyReleaseEvent(QKeyEvent * event) {
|
||||
if(event->isAutoRepeat()) {
|
||||
void QtkWidget::keyReleaseEvent(QKeyEvent * event)
|
||||
{
|
||||
if (event->isAutoRepeat()) {
|
||||
event->ignore();
|
||||
} else {
|
||||
Input::registerKeyRelease(event->key());
|
||||
}
|
||||
}
|
||||
|
||||
void QtkWidget::mousePressEvent(QMouseEvent * event) {
|
||||
void QtkWidget::mousePressEvent(QMouseEvent * event)
|
||||
{
|
||||
Input::registerMousePress(event->button());
|
||||
}
|
||||
|
||||
void QtkWidget::mouseReleaseEvent(QMouseEvent * event) {
|
||||
void QtkWidget::mouseReleaseEvent(QMouseEvent * event)
|
||||
{
|
||||
Input::registerMouseRelease(event->button());
|
||||
}
|
||||
|
||||
void QtkWidget::update() {
|
||||
void QtkWidget::update()
|
||||
{
|
||||
updateCameraInput();
|
||||
|
||||
if(mScene != Q_NULLPTR) {
|
||||
if (mScene != Q_NULLPTR) {
|
||||
mScene->update();
|
||||
}
|
||||
|
||||
QWidget::update();
|
||||
}
|
||||
|
||||
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
|
||||
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg)
|
||||
{
|
||||
QString error;
|
||||
|
||||
DebugContext context;
|
||||
// Format based on severity
|
||||
switch(msg.severity()) {
|
||||
switch (msg.severity()) {
|
||||
case QOpenGLDebugMessage::NotificationSeverity:
|
||||
error += "--";
|
||||
context = Status;
|
||||
@@ -241,7 +266,7 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
|
||||
case QOpenGLDebugMessage::c: \
|
||||
error += #c; \
|
||||
break
|
||||
switch(msg.source()) {
|
||||
switch (msg.source()) {
|
||||
CASE(APISource);
|
||||
CASE(WindowSystemSource);
|
||||
CASE(ShaderCompilerSource);
|
||||
@@ -259,7 +284,7 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
|
||||
case QOpenGLDebugMessage::c: \
|
||||
error += #c; \
|
||||
break
|
||||
switch(msg.type()) {
|
||||
switch (msg.type()) {
|
||||
CASE(InvalidType);
|
||||
CASE(ErrorType);
|
||||
CASE(DeprecatedBehaviorType);
|
||||
@@ -282,14 +307,16 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
|
||||
* Private Methods
|
||||
******************************************************************************/
|
||||
|
||||
void QtkWidget::teardownGL() { /* Nothing to teardown yet... */
|
||||
void QtkWidget::teardownGL()
|
||||
{ /* Nothing to teardown yet... */
|
||||
}
|
||||
|
||||
void QtkWidget::updateCameraInput() {
|
||||
void QtkWidget::updateCameraInput()
|
||||
{
|
||||
Input::update();
|
||||
// Camera Transformation
|
||||
if(Input::buttonPressed(Qt::LeftButton)
|
||||
|| Input::buttonPressed(Qt::RightButton)) {
|
||||
if (Input::buttonPressed(Qt::LeftButton)
|
||||
|| Input::buttonPressed(Qt::RightButton)) {
|
||||
static const float transSpeed = 0.1f;
|
||||
static const float rotSpeed = 0.5f;
|
||||
|
||||
@@ -301,29 +328,30 @@ void QtkWidget::updateCameraInput() {
|
||||
|
||||
// Handle translations
|
||||
QVector3D translation;
|
||||
if(Input::keyPressed(Qt::Key_W)) {
|
||||
if (Input::keyPressed(Qt::Key_W)) {
|
||||
translation += Scene::getCamera().getForward();
|
||||
}
|
||||
if(Input::keyPressed(Qt::Key_S)) {
|
||||
if (Input::keyPressed(Qt::Key_S)) {
|
||||
translation -= Scene::getCamera().getForward();
|
||||
}
|
||||
if(Input::keyPressed(Qt::Key_A)) {
|
||||
if (Input::keyPressed(Qt::Key_A)) {
|
||||
translation -= Scene::getCamera().getRight();
|
||||
}
|
||||
if(Input::keyPressed(Qt::Key_D)) {
|
||||
if (Input::keyPressed(Qt::Key_D)) {
|
||||
translation += Scene::getCamera().getRight();
|
||||
}
|
||||
if(Input::keyPressed(Qt::Key_Q)) {
|
||||
if (Input::keyPressed(Qt::Key_Q)) {
|
||||
translation -= Scene::getCamera().getUp() / 2.0f;
|
||||
}
|
||||
if(Input::keyPressed(Qt::Key_E)) {
|
||||
if (Input::keyPressed(Qt::Key_E)) {
|
||||
translation += Scene::getCamera().getUp() / 2.0f;
|
||||
}
|
||||
Scene::getCamera().getTransform().translate(transSpeed * translation);
|
||||
}
|
||||
}
|
||||
|
||||
void QtkWidget::printContextInformation() {
|
||||
void QtkWidget::printContextInformation()
|
||||
{
|
||||
QString glType;
|
||||
QString glVersion;
|
||||
QString glProfile;
|
||||
@@ -342,7 +370,7 @@ void QtkWidget::printContextInformation() {
|
||||
case QSurfaceFormat::c: \
|
||||
glProfile = #c; \
|
||||
break
|
||||
switch(format().profile()) {
|
||||
switch (format().profile()) {
|
||||
CASE(NoProfile);
|
||||
CASE(CoreProfile);
|
||||
CASE(CompatibilityProfile);
|
||||
@@ -17,10 +17,11 @@
|
||||
#include <QOpenGLWidget>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#include <qtk/qtkapi.h>
|
||||
#include <qtk/scene.h>
|
||||
#include "qtk/qtkapi.h"
|
||||
#include "qtk/scene.h"
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
class DebugConsole;
|
||||
|
||||
/**
|
||||
@@ -29,7 +30,8 @@ namespace Qtk {
|
||||
* 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 QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
|
||||
class QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
@@ -103,7 +105,8 @@ namespace Qtk {
|
||||
/**
|
||||
* @return Pointer to the QOpenGLDebugLogger attached to this widget.
|
||||
*/
|
||||
inline QOpenGLDebugLogger * getOpenGLDebugLogger() {
|
||||
inline QOpenGLDebugLogger * getOpenGLDebugLogger()
|
||||
{
|
||||
return mDebugLogger;
|
||||
}
|
||||
|
||||
@@ -116,6 +119,18 @@ namespace Qtk {
|
||||
*/
|
||||
void setScene(Qtk::Scene * scene);
|
||||
|
||||
/**
|
||||
* @param window The QMainWindow that owns this QtkWidget.
|
||||
*/
|
||||
void setMainWindow(QMainWindow * window) { mMainWindow = window; }
|
||||
|
||||
/*************************************************************************
|
||||
* Public Members
|
||||
************************************************************************/
|
||||
|
||||
/// Static manager for all QtkWidget instances.
|
||||
static class QtkWidgetManager mWidgetManager;
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
@@ -133,7 +148,7 @@ namespace Qtk {
|
||||
|
||||
// TODO: Use this signal in treeview and toolbox to update object
|
||||
// properties
|
||||
void objectFocusChanged(const QString objectName);
|
||||
void objectFocusChanged(QString objectName);
|
||||
|
||||
protected:
|
||||
/*************************************************************************
|
||||
@@ -207,7 +222,56 @@ namespace Qtk {
|
||||
QOpenGLDebugLogger * mDebugLogger;
|
||||
Qtk::Scene * mScene;
|
||||
Qtk::DebugConsole * mConsole;
|
||||
bool mConsoleActive = false;
|
||||
bool mConsoleActive = true;
|
||||
QMainWindow * mMainWindow = Q_NULLPTR;
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct to help manage QtkWidget instances.
|
||||
* When a QtkWidget is constructed we register it by calling add_widget().
|
||||
* Widgets can be retrieved by their unique name.
|
||||
*/
|
||||
struct QtkWidgetManager {
|
||||
/**
|
||||
* Get a QtkWidget by name.
|
||||
*
|
||||
* @param name The name of the QtkWidget to retrieve.
|
||||
*/
|
||||
QtkWidget * get_widget(const QString & name = "QtkWidget")
|
||||
{
|
||||
if (mQtkWidgets.count(name) == 0) {
|
||||
qDebug() << this
|
||||
<< " Attempt to get QtkWidget that doesn't exist: " << name;
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
return mQtkWidgets[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a QtkWidget with this manager.
|
||||
*
|
||||
* If the QObject::objectName is unset the widget and QObject is renamed
|
||||
* 'Unamed-QtkWidget'.
|
||||
*
|
||||
* @param widget Pointer to the QtkWidget to register to this manager.
|
||||
*/
|
||||
void add_widget(QtkWidget * widget)
|
||||
{
|
||||
auto name = widget->objectName();
|
||||
name = name == "" ? "Unamed-QtkWidget" : name;
|
||||
if (mQtkWidgets.count(name)) {
|
||||
qDebug()
|
||||
<< "[QtkWidgetManager " << this
|
||||
<< " ] Failed to add a QtkWidget with the previously used name '"
|
||||
<< name << "'";
|
||||
return;
|
||||
}
|
||||
qDebug() << this << " Adding new QtkWidget named '" << name << "'";
|
||||
mQtkWidgets[name] = widget;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<QString, QtkWidget *> mQtkWidgets;
|
||||
};
|
||||
} // namespace Qtk
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "toolbox.h"
|
||||
#include "qtkmainwindow.h"
|
||||
#include "qtkwidget.h"
|
||||
#include "ui_toolbox.h"
|
||||
|
||||
#include <QFormLayout>
|
||||
@@ -16,34 +16,39 @@
|
||||
|
||||
using namespace Qtk;
|
||||
|
||||
ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), ui(new Ui::ToolBox) {
|
||||
ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), ui(new Ui::ToolBox)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setMinimumWidth(350);
|
||||
}
|
||||
|
||||
void ToolBox::updateFocus(const QString & name) {
|
||||
void ToolBox::updateFocus(const QString & name)
|
||||
{
|
||||
auto object =
|
||||
MainWindow::getMainWindow()->getQtkWidget()->getScene()->getObject(name);
|
||||
if(object != Q_NULLPTR) {
|
||||
Qtk::QtkWidget::mWidgetManager.get_widget()->getScene()->getObject(name);
|
||||
if (object != Q_NULLPTR) {
|
||||
removePages();
|
||||
createPageProperties(object);
|
||||
createPageShader(object);
|
||||
}
|
||||
}
|
||||
|
||||
ToolBox::~ToolBox() {
|
||||
ToolBox::~ToolBox()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ToolBox::removePages() {
|
||||
void ToolBox::removePages()
|
||||
{
|
||||
// Remove all existing pages.
|
||||
for(size_t i = 0; i < ui->toolBox->count(); i++) {
|
||||
for (size_t i = 0; i < ui->toolBox->count(); i++) {
|
||||
delete ui->toolBox->widget(i);
|
||||
ui->toolBox->removeItem(i);
|
||||
}
|
||||
}
|
||||
|
||||
void ToolBox::createPageProperties(const Object * object) {
|
||||
void ToolBox::createPageProperties(const Object * object)
|
||||
{
|
||||
auto transform = object->getTransform();
|
||||
auto type = object->getType();
|
||||
auto * widget = new QWidget;
|
||||
@@ -51,17 +56,16 @@ void ToolBox::createPageProperties(const Object * object) {
|
||||
ui->toolBox->setCurrentWidget(widget);
|
||||
|
||||
auto * layout = new QFormLayout;
|
||||
layout->addRow(
|
||||
new QLabel(tr("Name:")), new QLabel(object->getName().c_str()));
|
||||
layout->addRow(new QLabel(tr("Name:")),
|
||||
new QLabel(object->getName().c_str()));
|
||||
|
||||
layout->addRow(
|
||||
new QLabel(tr("Type:")),
|
||||
new QLabel(type == Object::Type::QTK_MESH ? "Mesh" : "Model"));
|
||||
layout->addRow(new QLabel(tr("Type:")),
|
||||
new QLabel(type == Object::Type::QTK_MESH ? "Mesh" : "Model"));
|
||||
|
||||
auto rowLayout = new QHBoxLayout;
|
||||
rowLayout->addWidget(new QLabel(tr("Translation:")));
|
||||
int minWidth = 75;
|
||||
for(size_t i = 0; i < 3; i++) {
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
auto spinBox = new QDoubleSpinBox;
|
||||
spinBox->setMinimum(std::numeric_limits<double>::lowest());
|
||||
spinBox->setSingleStep(0.1);
|
||||
@@ -69,25 +73,28 @@ void ToolBox::createPageProperties(const Object * object) {
|
||||
spinBox->setFixedWidth(minWidth);
|
||||
rowLayout->addWidget(spinBox);
|
||||
|
||||
if(i == 0) {
|
||||
connect(
|
||||
spinBox, &QDoubleSpinBox::valueChanged, object,
|
||||
&Object::setTranslationX);
|
||||
} else if(i == 1) {
|
||||
connect(
|
||||
spinBox, &QDoubleSpinBox::valueChanged, object,
|
||||
&Object::setTranslationY);
|
||||
} else if(i == 2) {
|
||||
connect(
|
||||
spinBox, &QDoubleSpinBox::valueChanged, object,
|
||||
&Object::setTranslationZ);
|
||||
if (i == 0) {
|
||||
connect(spinBox,
|
||||
&QDoubleSpinBox::valueChanged,
|
||||
object,
|
||||
&Object::setTranslationX);
|
||||
} else if (i == 1) {
|
||||
connect(spinBox,
|
||||
&QDoubleSpinBox::valueChanged,
|
||||
object,
|
||||
&Object::setTranslationY);
|
||||
} else if (i == 2) {
|
||||
connect(spinBox,
|
||||
&QDoubleSpinBox::valueChanged,
|
||||
object,
|
||||
&Object::setTranslationZ);
|
||||
}
|
||||
}
|
||||
layout->addRow(rowLayout);
|
||||
|
||||
rowLayout = new QHBoxLayout;
|
||||
rowLayout->addWidget(new QLabel(tr("Scale:")));
|
||||
for(size_t i = 0; i < 3; i++) {
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
auto spinBox = new QDoubleSpinBox;
|
||||
spinBox->setMinimum(std::numeric_limits<double>::lowest());
|
||||
spinBox->setSingleStep(0.1);
|
||||
@@ -95,13 +102,13 @@ void ToolBox::createPageProperties(const Object * object) {
|
||||
spinBox->setFixedWidth(minWidth);
|
||||
rowLayout->addWidget(spinBox);
|
||||
|
||||
if(i == 0) {
|
||||
if (i == 0) {
|
||||
connect(
|
||||
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleX);
|
||||
} else if(i == 1) {
|
||||
} else if (i == 1) {
|
||||
connect(
|
||||
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleY);
|
||||
} else if(i == 2) {
|
||||
} else if (i == 2) {
|
||||
connect(
|
||||
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleZ);
|
||||
}
|
||||
@@ -110,7 +117,8 @@ void ToolBox::createPageProperties(const Object * object) {
|
||||
widget->setLayout(layout);
|
||||
}
|
||||
|
||||
void ToolBox::createPageShader(const Object * object) {
|
||||
void ToolBox::createPageShader(const Object * object)
|
||||
{
|
||||
// Shaders page.
|
||||
auto widget = new QWidget;
|
||||
ui->toolBox->addItem(widget, "Shaders");
|
||||
@@ -122,13 +130,8 @@ void ToolBox::createPageShader(const Object * object) {
|
||||
|
||||
auto shaderView = new QTextEdit;
|
||||
shaderView->setReadOnly(true);
|
||||
auto vertexFile = QFile(object->getVertexShader().c_str());
|
||||
if(vertexFile.exists()) {
|
||||
vertexFile.open(QIODeviceBase::ReadOnly);
|
||||
shaderView->setText(vertexFile.readAll());
|
||||
vertexFile.close();
|
||||
mainLayout->addRow(shaderView);
|
||||
}
|
||||
shaderView->setText(object->getVertexShaderSourceCode().c_str());
|
||||
mainLayout->addRow(shaderView);
|
||||
|
||||
rowLayout = new QHBoxLayout;
|
||||
rowLayout->addWidget(new QLabel("Fragment Shader:"));
|
||||
@@ -137,13 +140,8 @@ void ToolBox::createPageShader(const Object * object) {
|
||||
|
||||
shaderView = new QTextEdit;
|
||||
shaderView->setReadOnly(true);
|
||||
auto fragmentfile = QFile(object->getFragmentShader().c_str());
|
||||
if(fragmentfile.exists()) {
|
||||
fragmentfile.open(QIODeviceBase::ReadOnly);
|
||||
shaderView->setText(fragmentfile.readAll());
|
||||
fragmentfile.close();
|
||||
mainLayout->addRow(shaderView);
|
||||
}
|
||||
shaderView->setText(object->getFragmentShaderSourceCode().c_str());
|
||||
mainLayout->addRow(shaderView);
|
||||
|
||||
widget->setLayout(mainLayout);
|
||||
}
|
||||
@@ -18,12 +18,15 @@
|
||||
|
||||
#include "qtk/scene.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace Ui
|
||||
{
|
||||
class ToolBox;
|
||||
}
|
||||
|
||||
namespace Qtk {
|
||||
class ToolBox : public QDockWidget {
|
||||
namespace Qtk
|
||||
{
|
||||
class ToolBox : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "treeview.h"
|
||||
#include "qtkmainwindow.h"
|
||||
#include "qtkwidget.h"
|
||||
#include "ui_treeview.h"
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -16,14 +16,19 @@
|
||||
******************************************************************************/
|
||||
|
||||
Qtk::TreeView::TreeView(QWidget * parent) :
|
||||
QDockWidget(parent), ui(new Ui::TreeView) {
|
||||
QDockWidget(parent), ui(new Ui::TreeView)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(
|
||||
ui->treeWidget, &QTreeWidget::itemDoubleClicked, this,
|
||||
&TreeView::itemFocus);
|
||||
ui->treeWidget, &QTreeWidget::itemClicked, this, &TreeView::itemSelect);
|
||||
connect(ui->treeWidget,
|
||||
&QTreeWidget::itemDoubleClicked,
|
||||
this,
|
||||
&TreeView::itemFocus);
|
||||
}
|
||||
|
||||
Qtk::TreeView::~TreeView() {
|
||||
Qtk::TreeView::~TreeView()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
@@ -31,42 +36,48 @@ Qtk::TreeView::~TreeView() {
|
||||
* Public Methods
|
||||
******************************************************************************/
|
||||
|
||||
void Qtk::TreeView::updateView(const Qtk::Scene * scene) {
|
||||
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().c_str())));
|
||||
ui->treeWidget->insertTopLevelItem(0, item);
|
||||
for (const auto & object : objects) {
|
||||
QStringList list(QStringList(QString(object->getName().c_str())));
|
||||
ui->treeWidget->insertTopLevelItem(0, new QTreeWidgetItem(list));
|
||||
}
|
||||
}
|
||||
|
||||
void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) {
|
||||
QString name = item->text(column);
|
||||
auto scene = MainWindow::getMainWindow()->getQtkWidget()->getScene();
|
||||
auto & transform = scene->getCamera().getTransform();
|
||||
void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column)
|
||||
{
|
||||
const QString & name = item->text(column);
|
||||
auto scene = QtkWidget::mWidgetManager.get_widget()->getScene();
|
||||
auto object = scene->getObject(name);
|
||||
Transform3D * objectTransform;
|
||||
// If the object is a mesh or model, focus the camera on it.
|
||||
if(object == Q_NULLPTR) {
|
||||
if (object == Q_NULLPTR) {
|
||||
qDebug() << "Attempt to get non-existing object with name '" << name
|
||||
<< "'\n";
|
||||
} else if(object->getType() == Object::QTK_MESH) {
|
||||
objectTransform = &dynamic_cast<MeshRenderer *>(object)->getTransform();
|
||||
} else if(object->getType() == Object::QTK_MODEL) {
|
||||
objectTransform = &dynamic_cast<Model *>(object)->getTransform();
|
||||
return;
|
||||
}
|
||||
auto focusScale = objectTransform->getScale();
|
||||
const Transform3D & objectTransform = object->getTransform();
|
||||
|
||||
auto & camera_transform = Qtk::Scene::getCamera().getTransform();
|
||||
auto focusScale = objectTransform.getScale();
|
||||
float width = focusScale.x() / 2.0f;
|
||||
float height = focusScale.y() / 2.0f;
|
||||
QVector3D pos = objectTransform->getTranslation();
|
||||
QVector3D pos = objectTransform.getTranslation();
|
||||
// pos.setX(pos.x() + width);
|
||||
pos.setY(pos.y() + height);
|
||||
transform.setTranslation(pos);
|
||||
transform.translate(0.0f, 0.0f, 3.0f);
|
||||
camera_transform.setTranslation(pos);
|
||||
camera_transform.translate(0.0f, 0.0f, 3.0f);
|
||||
|
||||
// Emit signal from qtk widget for new object focus. Triggers GUI updates.
|
||||
emit MainWindow::getMainWindow()->getQtkWidget()->objectFocusChanged(name);
|
||||
emit QtkWidget::mWidgetManager.get_widget()->objectFocusChanged(name);
|
||||
}
|
||||
|
||||
void Qtk::TreeView::itemSelect(QTreeWidgetItem * item, int column)
|
||||
{
|
||||
// Emit signal from qtk widget for new object focus. Triggers GUI updates.
|
||||
const QString & name = item->text(column);
|
||||
emit QtkWidget::mWidgetManager.get_widget()->objectFocusChanged(name);
|
||||
}
|
||||
@@ -13,16 +13,19 @@
|
||||
#include <QDesignerCustomWidgetInterface>
|
||||
#include <QDesignerExportWidget>
|
||||
#include <QDockWidget>
|
||||
|
||||
#include <qtk/scene.h>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
namespace Ui {
|
||||
#include "qtk/scene.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class TreeView;
|
||||
}
|
||||
|
||||
namespace Qtk {
|
||||
class TreeView : public QDockWidget {
|
||||
namespace Qtk
|
||||
{
|
||||
class TreeView : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -55,6 +58,16 @@ namespace Qtk {
|
||||
*/
|
||||
void itemFocus(QTreeWidgetItem * item, int column);
|
||||
|
||||
/**
|
||||
* Set the object to show details for.
|
||||
* Triggered by QTreeWidget::itemClicked signal.
|
||||
*
|
||||
* @param item The item that was clicked
|
||||
* @param column The column of the item that was double clicked.
|
||||
* This param is currently not used but required for this signal.
|
||||
*/
|
||||
void itemSelect(QTreeWidgetItem * item, int column);
|
||||
|
||||
private:
|
||||
/*************************************************************************
|
||||
* Private Members
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <QtPlugin>
|
||||
#include <utility>
|
||||
|
||||
#include <qtk/qtkapi.h>
|
||||
#include "qtk/qtkapi.h"
|
||||
|
||||
#include "widgetplugin.h"
|
||||
|
||||
@@ -19,12 +19,15 @@
|
||||
* Constructors, Destructors
|
||||
******************************************************************************/
|
||||
|
||||
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) {}
|
||||
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(m_className)
|
||||
{
|
||||
}
|
||||
|
||||
WidgetPlugin::WidgetPlugin(QObject * parent) : QObject(parent) {}
|
||||
|
||||
@@ -32,50 +35,61 @@ WidgetPlugin::WidgetPlugin(QObject * parent) : QObject(parent) {}
|
||||
* Public Methods
|
||||
******************************************************************************/
|
||||
|
||||
QString WidgetPlugin::group() const {
|
||||
QString WidgetPlugin::group() const
|
||||
{
|
||||
return m_group;
|
||||
}
|
||||
|
||||
QString WidgetPlugin::name() const {
|
||||
QString WidgetPlugin::name() const
|
||||
{
|
||||
return m_className;
|
||||
}
|
||||
|
||||
QString WidgetPlugin::includeFile() const {
|
||||
QString WidgetPlugin::includeFile() const
|
||||
{
|
||||
return m_includeFile;
|
||||
}
|
||||
|
||||
QWidget * WidgetPlugin::createWidget(QWidget * parent) {
|
||||
QWidget * WidgetPlugin::createWidget(QWidget * parent)
|
||||
{
|
||||
return m_factory(parent);
|
||||
}
|
||||
|
||||
QString WidgetPlugin::toolTip() const {
|
||||
QString WidgetPlugin::toolTip() const
|
||||
{
|
||||
return QStringLiteral("A custom widget tool tip.");
|
||||
}
|
||||
|
||||
QString WidgetPlugin::whatsThis() const {
|
||||
QString WidgetPlugin::whatsThis() const
|
||||
{
|
||||
return QStringLiteral("Custom widget what's this?");
|
||||
}
|
||||
|
||||
QIcon WidgetPlugin::icon() const {
|
||||
return Qtk::getIcon();
|
||||
QIcon WidgetPlugin::icon() const
|
||||
{
|
||||
return QIcon(":/icons/icon.png");
|
||||
}
|
||||
|
||||
bool WidgetPlugin::isContainer() const {
|
||||
bool WidgetPlugin::isContainer() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WidgetPlugin::isInitialized() const {
|
||||
bool WidgetPlugin::isInitialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
void WidgetPlugin::initialize(QDesignerFormEditorInterface *) {
|
||||
if(m_initialized) {
|
||||
void WidgetPlugin::initialize(QDesignerFormEditorInterface *)
|
||||
{
|
||||
if (m_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
QString WidgetPlugin::domXml() const {
|
||||
QString WidgetPlugin::domXml() const
|
||||
{
|
||||
return
|
||||
"<ui language=\"c++\">\n"
|
||||
" <widget class=\"" + m_className + "\" name=\"" + m_objectName + "\">\n"
|
||||
@@ -15,7 +15,8 @@
|
||||
|
||||
class QDESIGNER_WIDGET_EXPORT WidgetPlugin :
|
||||
public QObject,
|
||||
public QDesignerCustomWidgetInterface {
|
||||
public QDesignerCustomWidgetInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QDesignerCustomWidgetInterface)
|
||||
|
||||
@@ -26,8 +27,10 @@ class QDESIGNER_WIDGET_EXPORT WidgetPlugin :
|
||||
* Contructors / Destructors
|
||||
**************************************************************************/
|
||||
|
||||
WidgetPlugin(
|
||||
QString group, QString class_name, QString include, Factory factory);
|
||||
WidgetPlugin(QString group,
|
||||
QString class_name,
|
||||
QString include,
|
||||
Factory factory);
|
||||
|
||||
explicit WidgetPlugin(QObject * parent = nullptr);
|
||||
|
||||
@@ -19,17 +19,25 @@
|
||||
******************************************************************************/
|
||||
|
||||
WidgetPluginCollection::WidgetPluginCollection(QObject * parent) :
|
||||
QObject(parent), m_collectionName("Qtk Widget Collection") {
|
||||
QObject(parent), m_collectionName("Qtk Widget Collection")
|
||||
{
|
||||
m_collection = {
|
||||
new WidgetPlugin(
|
||||
m_collectionName, "Qtk::QtkWidget", "qtkwidget.h",
|
||||
m_collectionName,
|
||||
"Qtk::QtkWidget",
|
||||
"qtkwidget.h",
|
||||
[](QWidget * parent) { return new Qtk::QtkWidget(parent); }),
|
||||
new WidgetPlugin(
|
||||
m_collectionName, "Qtk::TreeView", "treeview.h",
|
||||
m_collectionName,
|
||||
"Qtk::TreeView",
|
||||
"treeview.h",
|
||||
[](QWidget * parent) { return new Qtk::TreeView(parent); }),
|
||||
new WidgetPlugin(
|
||||
m_collectionName, "Qtk::ToolBox", "toolbox.h",
|
||||
m_collectionName,
|
||||
"Qtk::ToolBox",
|
||||
"toolbox.h",
|
||||
[](QWidget * parent) { return new Qtk::ToolBox(parent); }),
|
||||
// TODO: Add and test DebugConsole.
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,6 +46,7 @@ WidgetPluginCollection::WidgetPluginCollection(QObject * parent) :
|
||||
******************************************************************************/
|
||||
|
||||
QList<QDesignerCustomWidgetInterface *> WidgetPluginCollection::customWidgets()
|
||||
const {
|
||||
const
|
||||
{
|
||||
return m_collection;
|
||||
}
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
class WidgetPluginCollection :
|
||||
public QObject,
|
||||
public QDesignerCustomWidgetCollectionInterface {
|
||||
public QDesignerCustomWidgetCollectionInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
// Since we're exporting a collection, this is the only plugin metadata
|
||||
// needed. We don't need this for-each widget in the collection.
|
||||
@@ -36,7 +37,8 @@ class WidgetPluginCollection :
|
||||
/**
|
||||
* @return QList of all custom widgets pointers.
|
||||
*/
|
||||
[[nodiscard]] QList<QDesignerCustomWidgetInterface *> customWidgets() const;
|
||||
[[nodiscard]] QList<QDesignerCustomWidgetInterface *> customWidgets()
|
||||
const override;
|
||||
|
||||
private:
|
||||
/***************************************************************************
|
||||
@@ -2,7 +2,7 @@
|
||||
## Project for working with OpenGL and Qt6 widgets ##
|
||||
## ##
|
||||
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
||||
## All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||
## All Content (c) 2025 Shaun Reed, all rights reserved ##
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
@@ -24,6 +24,7 @@ set(
|
||||
skybox.h
|
||||
texture.h
|
||||
transform3D.h
|
||||
shaders.h
|
||||
)
|
||||
|
||||
set(
|
||||
@@ -43,11 +44,10 @@ set(
|
||||
transform3D.cpp
|
||||
)
|
||||
|
||||
qt6_add_big_resources(QTK_LIBRARY_SOURCES "${QTK_RESOURCES}/resources.qrc")
|
||||
qt_add_library(qtk_library STATIC EXCLUDE_FROM_ALL)
|
||||
target_sources(qtk_library PRIVATE ${QTK_LIBRARY_SOURCES})
|
||||
qt_add_library(qtk STATIC EXCLUDE_FROM_ALL)
|
||||
target_sources(qtk PRIVATE ${QTK_LIBRARY_SOURCES})
|
||||
target_sources(
|
||||
qtk_library PUBLIC
|
||||
qtk PUBLIC
|
||||
FILE_SET HEADERS
|
||||
BASE_DIRS $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
|
||||
BASE_DIRS $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
|
||||
@@ -55,25 +55,25 @@ target_sources(
|
||||
)
|
||||
|
||||
if(QTK_DEBUG)
|
||||
target_compile_definitions(qtk_library PUBLIC QTK_DEBUG)
|
||||
target_compile_definitions(qtk PUBLIC -DQTK_DEBUG)
|
||||
endif()
|
||||
|
||||
set_target_properties(
|
||||
qtk_library PROPERTIES
|
||||
qtk PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
qtk_library PUBLIC
|
||||
qtk PUBLIC
|
||||
Qt6::Core Qt6::OpenGLWidgets Qt6::Widgets
|
||||
)
|
||||
|
||||
if(QTK_SUBMODULES OR NOT QTK_ASSIMP_NEW_INTERFACE)
|
||||
target_link_libraries(qtk_library PUBLIC assimp)
|
||||
target_link_libraries(qtk PUBLIC assimp)
|
||||
elseif(QTK_ASSIMP_NEW_INTERFACE)
|
||||
target_link_libraries(qtk_library PUBLIC assimp::assimp)
|
||||
target_link_libraries(qtk PUBLIC assimp::assimp)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(qtk_library PUBLIC OpenGL::GL)
|
||||
target_link_libraries(qtk PUBLIC OpenGL::GL)
|
||||
endif()
|
||||
|
||||
@@ -22,7 +22,8 @@ const QVector3D Camera3D::LocalRight(1.0f, 0.0f, 0.0f);
|
||||
* Public Methods
|
||||
******************************************************************************/
|
||||
|
||||
const QMatrix4x4 & Camera3D::toMatrix() {
|
||||
const QMatrix4x4 & Camera3D::toMatrix()
|
||||
{
|
||||
mWorld.setToIdentity();
|
||||
// Qt6 renamed QMatrix4x4::conjugate() to conjugated()
|
||||
mWorld.rotate(mTransform.getRotation().conjugated());
|
||||
@@ -34,17 +35,20 @@ const QMatrix4x4 & Camera3D::toMatrix() {
|
||||
* Qt Streams
|
||||
******************************************************************************/
|
||||
|
||||
QDataStream & operator<<(QDataStream & out, Camera3D & transform) {
|
||||
QDataStream & operator<<(QDataStream & out, Camera3D & transform)
|
||||
{
|
||||
out << transform.getTransform();
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream & operator>>(QDataStream & in, Camera3D & transform) {
|
||||
QDataStream & operator>>(QDataStream & in, Camera3D & transform)
|
||||
{
|
||||
in >> transform.getTransform();
|
||||
return in;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug dbg, const Camera3D & transform) {
|
||||
QDebug operator<<(QDebug dbg, const Camera3D & transform)
|
||||
{
|
||||
dbg << "Camera3D\n{\n";
|
||||
dbg << "Position: <" << transform.getTranslation().x() << ", "
|
||||
<< transform.getTranslation().y() << ", "
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
#include "qtkapi.h"
|
||||
#include "transform3D.h"
|
||||
|
||||
namespace Qtk {
|
||||
class QTKAPI Camera3D {
|
||||
namespace Qtk
|
||||
{
|
||||
class QTKAPI Camera3D
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Static Public Constants
|
||||
@@ -37,35 +39,40 @@ namespace Qtk {
|
||||
/**
|
||||
* @return Current translation of the camera as a QVector3D.
|
||||
*/
|
||||
[[nodiscard]] inline const QVector3D & getTranslation() const {
|
||||
[[nodiscard]] inline const QVector3D & getTranslation() const
|
||||
{
|
||||
return mTransform.getTranslation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current rotation of this camera as a QQuaternion.
|
||||
*/
|
||||
[[nodiscard]] inline const QQuaternion & getRotation() const {
|
||||
[[nodiscard]] inline const QQuaternion & getRotation() const
|
||||
{
|
||||
return mTransform.getRotation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QVector3D for the forward vector of the camera.
|
||||
*/
|
||||
[[nodiscard]] inline QVector3D getForward() const {
|
||||
[[nodiscard]] inline QVector3D getForward() const
|
||||
{
|
||||
return mTransform.getRotation().rotatedVector(LocalForward);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QVector3D for the right vector of the camera.
|
||||
*/
|
||||
[[nodiscard]] inline QVector3D getRight() const {
|
||||
[[nodiscard]] inline QVector3D getRight() const
|
||||
{
|
||||
return mTransform.getRotation().rotatedVector(LocalRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QVector3D for the up vector of the camera.
|
||||
*/
|
||||
[[nodiscard]] inline QVector3D getUp() const {
|
||||
[[nodiscard]] inline QVector3D getUp() const
|
||||
{
|
||||
return mTransform.getRotation().rotatedVector(LocalUp);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,10 +33,13 @@ template <typename T> struct InputInstance : std::pair<T, Input::InputState> {
|
||||
inline InputInstance(T value) : base_class(value, Input::InputInvalid) {}
|
||||
|
||||
inline InputInstance(T value, Input::InputState state) :
|
||||
base_class(value, state) {}
|
||||
base_class(value, state)
|
||||
{
|
||||
}
|
||||
|
||||
// Allows use of std::find to search for a key's InputInstance
|
||||
inline bool operator==(const InputInstance & rhs) const {
|
||||
inline bool operator==(const InputInstance & rhs) const
|
||||
{
|
||||
return this->first == rhs.first;
|
||||
}
|
||||
};
|
||||
@@ -67,7 +70,8 @@ static QPoint sg_mouseDelta;
|
||||
* @param value The key to search for.
|
||||
* @return Iterator to the found element or the end iterator if not found.
|
||||
*/
|
||||
static inline KeyContainer::iterator FindKey(Qt::Key value) {
|
||||
static inline KeyContainer::iterator FindKey(Qt::Key value)
|
||||
{
|
||||
return std::find(sg_keyInstances.begin(), sg_keyInstances.end(), value);
|
||||
}
|
||||
|
||||
@@ -77,7 +81,8 @@ static inline KeyContainer::iterator FindKey(Qt::Key value) {
|
||||
* @param value The mouse button to search for.
|
||||
* @return Iterator to the found element or the end iterator if not found.
|
||||
*/
|
||||
static inline ButtonContainer::iterator FindButton(Qt::MouseButton value) {
|
||||
static inline ButtonContainer::iterator FindButton(Qt::MouseButton value)
|
||||
{
|
||||
return std::find(sg_buttonInstances.begin(), sg_buttonInstances.end(), value);
|
||||
}
|
||||
|
||||
@@ -89,7 +94,8 @@ static inline ButtonContainer::iterator FindButton(Qt::MouseButton value) {
|
||||
* @return True if the InputInstance is in the released state.
|
||||
*/
|
||||
template <typename TPair>
|
||||
static inline bool CheckReleased(const TPair & instance) {
|
||||
static inline bool CheckReleased(const TPair & instance)
|
||||
{
|
||||
return instance.second == Input::InputReleased;
|
||||
}
|
||||
|
||||
@@ -99,8 +105,9 @@ static inline bool CheckReleased(const TPair & instance) {
|
||||
* @tparam TPair KeyInstance or ButtonInstance.
|
||||
* @param instance The InputInstance to update.
|
||||
*/
|
||||
template <typename TPair> static inline void UpdateStates(TPair & instance) {
|
||||
switch(instance.second) {
|
||||
template <typename TPair> static inline void UpdateStates(TPair & instance)
|
||||
{
|
||||
switch (instance.second) {
|
||||
case Input::InputRegistered:
|
||||
instance.second = Input::InputTriggered;
|
||||
break;
|
||||
@@ -121,7 +128,8 @@ template <typename TPair> static inline void UpdateStates(TPair & instance) {
|
||||
* @tparam Container The type of container, KeyContainer or ButtonContainer.
|
||||
* @param container The InputInstance container to update.
|
||||
*/
|
||||
template <typename Container> static inline void Update(Container & container) {
|
||||
template <typename Container> static inline void Update(Container & container)
|
||||
{
|
||||
typedef typename Container::iterator Iter;
|
||||
typedef typename Container::value_type TPair;
|
||||
|
||||
@@ -138,7 +146,8 @@ template <typename Container> static inline void Update(Container & container) {
|
||||
* Static Public Methods
|
||||
******************************************************************************/
|
||||
|
||||
void Input::update() {
|
||||
void Input::update()
|
||||
{
|
||||
// Update Mouse Delta
|
||||
sg_mousePrevPosition = sg_mouseCurrPosition;
|
||||
sg_mouseCurrPosition = QCursor::pos();
|
||||
@@ -149,53 +158,62 @@ void Input::update() {
|
||||
Update(sg_keyInstances);
|
||||
}
|
||||
|
||||
void Input::registerKeyPress(int k) {
|
||||
void Input::registerKeyPress(int k)
|
||||
{
|
||||
auto it = FindKey((Qt::Key)k);
|
||||
if(it == sg_keyInstances.end()) {
|
||||
sg_keyInstances.push_back(KeyInstance((Qt::Key)k, InputRegistered));
|
||||
if (it == sg_keyInstances.end()) {
|
||||
sg_keyInstances.emplace_back((Qt::Key)k, InputRegistered);
|
||||
}
|
||||
}
|
||||
|
||||
void Input::registerKeyRelease(int k) {
|
||||
void Input::registerKeyRelease(int k)
|
||||
{
|
||||
auto it = FindKey((Qt::Key)k);
|
||||
if(it != sg_keyInstances.end()) {
|
||||
if (it != sg_keyInstances.end()) {
|
||||
it->second = InputUnregistered;
|
||||
}
|
||||
}
|
||||
|
||||
void Input::registerMousePress(Qt::MouseButton btn) {
|
||||
auto it = FindButton(btn);
|
||||
if(it == sg_buttonInstances.end()) {
|
||||
sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered));
|
||||
void Input::registerMousePress(Qt::MouseButton button)
|
||||
{
|
||||
auto it = FindButton(button);
|
||||
if (it == sg_buttonInstances.end()) {
|
||||
sg_buttonInstances.emplace_back(button, InputRegistered);
|
||||
}
|
||||
}
|
||||
|
||||
void Input::registerMouseRelease(Qt::MouseButton btn) {
|
||||
auto it = FindButton(btn);
|
||||
if(it != sg_buttonInstances.end()) {
|
||||
void Input::registerMouseRelease(Qt::MouseButton button)
|
||||
{
|
||||
auto it = FindButton(button);
|
||||
if (it != sg_buttonInstances.end()) {
|
||||
it->second = InputUnregistered;
|
||||
}
|
||||
}
|
||||
|
||||
void Input::reset() {
|
||||
void Input::reset()
|
||||
{
|
||||
sg_keyInstances.clear();
|
||||
sg_buttonInstances.clear();
|
||||
}
|
||||
|
||||
Input::InputState Input::keyState(Qt::Key k) {
|
||||
Input::InputState Input::keyState(Qt::Key k)
|
||||
{
|
||||
auto it = FindKey(k);
|
||||
return (it != sg_keyInstances.end()) ? it->second : InputInvalid;
|
||||
}
|
||||
|
||||
Input::InputState Input::buttonState(Qt::MouseButton k) {
|
||||
auto it = FindButton(k);
|
||||
Input::InputState Input::buttonState(Qt::MouseButton button)
|
||||
{
|
||||
auto it = FindButton(button);
|
||||
return (it != sg_buttonInstances.end()) ? it->second : InputInvalid;
|
||||
}
|
||||
|
||||
QPoint Input::mousePosition() {
|
||||
QPoint Input::mousePosition()
|
||||
{
|
||||
return QCursor::pos();
|
||||
}
|
||||
|
||||
QPoint Input::mouseDelta() {
|
||||
QPoint Input::mouseDelta()
|
||||
{
|
||||
return sg_mouseDelta;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
|
||||
#include "qtkapi.h"
|
||||
|
||||
namespace Qtk {
|
||||
class QTKAPI Input {
|
||||
namespace Qtk
|
||||
{
|
||||
class QTKAPI Input
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Typedefs
|
||||
@@ -84,7 +86,8 @@ namespace Qtk {
|
||||
* @param key Key to check state.
|
||||
* @return True if the key is in InputTriggered state.
|
||||
*/
|
||||
inline static bool keyTriggered(Qt::Key key) {
|
||||
inline static bool keyTriggered(Qt::Key key)
|
||||
{
|
||||
return keyState(key) == InputTriggered;
|
||||
}
|
||||
|
||||
@@ -92,7 +95,8 @@ namespace Qtk {
|
||||
* @param key Key to check state.
|
||||
* @return True if the key is in InputPressed state.
|
||||
*/
|
||||
inline static bool keyPressed(Qt::Key key) {
|
||||
inline static bool keyPressed(Qt::Key key)
|
||||
{
|
||||
return keyState(key) == InputPressed;
|
||||
}
|
||||
|
||||
@@ -100,7 +104,8 @@ namespace Qtk {
|
||||
* @param key Key to check state.
|
||||
* @return True if the key is in InputReleased state.
|
||||
*/
|
||||
inline static bool keyReleased(Qt::Key key) {
|
||||
inline static bool keyReleased(Qt::Key key)
|
||||
{
|
||||
return keyState(key) == InputReleased;
|
||||
}
|
||||
|
||||
@@ -108,7 +113,8 @@ namespace Qtk {
|
||||
* @param button Mouse button to check state.
|
||||
* @return True if the key is in InputTriggered state.
|
||||
*/
|
||||
inline static bool buttonTriggered(Qt::MouseButton button) {
|
||||
inline static bool buttonTriggered(Qt::MouseButton button)
|
||||
{
|
||||
return buttonState(button) == InputTriggered;
|
||||
}
|
||||
|
||||
@@ -116,7 +122,8 @@ namespace Qtk {
|
||||
* @param button Mouse button to check state.
|
||||
* @return True if the key is in InputPressed state.
|
||||
*/
|
||||
inline static bool buttonPressed(Qt::MouseButton button) {
|
||||
inline static bool buttonPressed(Qt::MouseButton button)
|
||||
{
|
||||
return buttonState(button) == InputPressed;
|
||||
}
|
||||
|
||||
@@ -124,7 +131,8 @@ namespace Qtk {
|
||||
* @param button Mouse button to check state.
|
||||
* @return True if the key is in InputReleased state.
|
||||
*/
|
||||
inline static bool buttonReleased(Qt::MouseButton button) {
|
||||
inline static bool buttonReleased(Qt::MouseButton button)
|
||||
{
|
||||
return buttonState(button) == InputReleased;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "meshrenderer.h"
|
||||
#include "scene.h"
|
||||
#include "shaders.h"
|
||||
#include "texture.h"
|
||||
|
||||
using namespace Qtk;
|
||||
@@ -21,23 +22,29 @@ Qtk::MeshRenderer::MeshManager Qtk::MeshRenderer::sInstances;
|
||||
* Constructors / Destructors
|
||||
******************************************************************************/
|
||||
|
||||
MeshRenderer::MeshRenderer(
|
||||
const char * name, Vertices vertices, Indices indices, DrawMode mode) :
|
||||
MeshRenderer(
|
||||
name, ShapeBase(mode, std::move(vertices), std::move(indices))) {}
|
||||
MeshRenderer::MeshRenderer(const char * name,
|
||||
Vertices vertices,
|
||||
Indices indices,
|
||||
DrawMode mode) :
|
||||
MeshRenderer(name, ShapeBase(mode, std::move(vertices), std::move(indices)))
|
||||
{
|
||||
}
|
||||
|
||||
MeshRenderer::MeshRenderer(const char * name) :
|
||||
MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS)) {}
|
||||
MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS))
|
||||
{
|
||||
}
|
||||
|
||||
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
|
||||
Object(name, shape, QTK_MESH), mVertexShader(":/shaders/multi-color.vert"),
|
||||
mFragmentShader(":/shaders/multi-color.frag"), mDrawType(GL_TRIANGLES) {
|
||||
Object(name, shape, QTK_MESH), mDrawType(GL_TRIANGLES)
|
||||
{
|
||||
mShape = Shape(shape);
|
||||
init();
|
||||
sInstances.insert(name, this);
|
||||
}
|
||||
|
||||
MeshRenderer::~MeshRenderer() {
|
||||
MeshRenderer::~MeshRenderer()
|
||||
{
|
||||
sInstances.remove(mName.c_str());
|
||||
}
|
||||
|
||||
@@ -45,14 +52,15 @@ MeshRenderer::~MeshRenderer() {
|
||||
* Public Methods
|
||||
******************************************************************************/
|
||||
|
||||
void MeshRenderer::init() {
|
||||
if(mVAO.isCreated()) {
|
||||
void MeshRenderer::init()
|
||||
{
|
||||
if (mVAO.isCreated()) {
|
||||
mVAO.destroy();
|
||||
}
|
||||
if(mProgram.isLinked()) {
|
||||
if (mProgram.isLinked()) {
|
||||
mProgram.removeAllShaders();
|
||||
}
|
||||
if(mVBO.isCreated()) {
|
||||
if (mVBO.isCreated()) {
|
||||
mVBO.destroy();
|
||||
}
|
||||
|
||||
@@ -60,10 +68,22 @@ void MeshRenderer::init() {
|
||||
mVAO.bind();
|
||||
|
||||
mProgram.create();
|
||||
mProgram.addShaderFromSourceFile(
|
||||
QOpenGLShader::Vertex, mVertexShader.c_str());
|
||||
mProgram.addShaderFromSourceFile(
|
||||
QOpenGLShader::Fragment, mFragmentShader.c_str());
|
||||
// If no shader is provided, use a default one.
|
||||
if (mVertexShader.empty()) {
|
||||
mProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,
|
||||
QTK_SHADER_VERTEX_MESH);
|
||||
} else {
|
||||
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
|
||||
mVertexShader.c_str());
|
||||
}
|
||||
|
||||
if (mFragmentShader.empty()) {
|
||||
mProgram.addShaderFromSourceCode(QOpenGLShader::Fragment,
|
||||
QTK_SHADER_FRAGMENT_MESH);
|
||||
} else {
|
||||
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
|
||||
mFragmentShader.c_str());
|
||||
}
|
||||
mProgram.link();
|
||||
mProgram.bind();
|
||||
|
||||
@@ -84,9 +104,11 @@ void MeshRenderer::init() {
|
||||
mProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D));
|
||||
// Enable color attribute, setting offset to total size of vertices()
|
||||
mProgram.enableAttributeArray(1);
|
||||
mProgram.setAttributeBuffer(
|
||||
1, GL_FLOAT, getVertices().size() * sizeof(getVertices()[0]), 3,
|
||||
sizeof(QVector3D));
|
||||
mProgram.setAttributeBuffer(1,
|
||||
GL_FLOAT,
|
||||
getVertices().size() * sizeof(getVertices()[0]),
|
||||
3,
|
||||
sizeof(QVector3D));
|
||||
|
||||
mVBO.release();
|
||||
|
||||
@@ -94,59 +116,59 @@ void MeshRenderer::init() {
|
||||
mVAO.release();
|
||||
}
|
||||
|
||||
void MeshRenderer::draw() {
|
||||
void MeshRenderer::draw()
|
||||
{
|
||||
bindShaders();
|
||||
mVAO.bind();
|
||||
|
||||
if(mTexture.hasTexture()) {
|
||||
mTexture.getOpenGLTexture().bind();
|
||||
}
|
||||
mTexture.bind();
|
||||
|
||||
// TODO: Automate uniforms some other way
|
||||
setUniformMVP();
|
||||
|
||||
if(mShape.mDrawMode == QTK_DRAW_ARRAYS) {
|
||||
if (mShape.mDrawMode == QTK_DRAW_ARRAYS) {
|
||||
glDrawArrays(mDrawType, 0, getVertices().size());
|
||||
} else if(
|
||||
mShape.mDrawMode == QTK_DRAW_ELEMENTS
|
||||
|| mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) {
|
||||
glDrawElements(
|
||||
mDrawType, mShape.mIndices.size(), GL_UNSIGNED_INT,
|
||||
mShape.mIndices.data());
|
||||
} else if (mShape.mDrawMode == QTK_DRAW_ELEMENTS
|
||||
|| mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) {
|
||||
glDrawElements(mDrawType,
|
||||
mShape.mIndices.size(),
|
||||
GL_UNSIGNED_INT,
|
||||
mShape.mIndices.data());
|
||||
}
|
||||
|
||||
if(mTexture.hasTexture()) {
|
||||
mTexture.getOpenGLTexture().release();
|
||||
}
|
||||
mTexture.bind();
|
||||
|
||||
mVAO.release();
|
||||
releaseShaders();
|
||||
}
|
||||
|
||||
void MeshRenderer::enableAttributeArray(int location) {
|
||||
void MeshRenderer::enableAttributeArray(int location)
|
||||
{
|
||||
ShaderBindScope lock(&mProgram, mBound);
|
||||
mVAO.bind();
|
||||
mProgram.enableAttributeArray(location);
|
||||
mVAO.release();
|
||||
}
|
||||
|
||||
void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims) {
|
||||
void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims)
|
||||
{
|
||||
mVAO.bind();
|
||||
mNBO.destroy();
|
||||
mNBO.create();
|
||||
mNBO.bind();
|
||||
mNBO.allocate(t.data(), t.size() * sizeof(t[0]));
|
||||
enableAttributeArray(1);
|
||||
if(dims == 2) {
|
||||
if (dims == 2) {
|
||||
setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D));
|
||||
} else if(dims == 3) {
|
||||
} else if (dims == 3) {
|
||||
setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D));
|
||||
}
|
||||
mNBO.release();
|
||||
mVAO.release();
|
||||
}
|
||||
|
||||
void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims) {
|
||||
void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims)
|
||||
{
|
||||
// TODO: Store state to track if buffer objects are bound
|
||||
mVAO.bind();
|
||||
mNBO.destroy();
|
||||
@@ -154,42 +176,47 @@ void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims) {
|
||||
mNBO.bind();
|
||||
mNBO.allocate(n.data(), n.size() * sizeof(n[0]));
|
||||
enableAttributeArray(1);
|
||||
if(dims == 2) {
|
||||
if (dims == 2) {
|
||||
setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D));
|
||||
} else if(dims == 3) {
|
||||
} else if (dims == 3) {
|
||||
setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D));
|
||||
}
|
||||
mNBO.release();
|
||||
mVAO.release();
|
||||
}
|
||||
|
||||
void MeshRenderer::setShaders(
|
||||
const std::string & vert, const std::string & frag) {
|
||||
void MeshRenderer::setShaders(const std::string & vert,
|
||||
const std::string & frag)
|
||||
{
|
||||
mVertexShader = vert;
|
||||
mFragmentShader = frag;
|
||||
init();
|
||||
}
|
||||
|
||||
void MeshRenderer::setUniformMVP(
|
||||
const char * model, const char * view, const char * projection) {
|
||||
void MeshRenderer::setUniformMVP(const char * model,
|
||||
const char * view,
|
||||
const char * projection)
|
||||
{
|
||||
ShaderBindScope lock(&mProgram, mBound);
|
||||
mProgram.setUniformValue(projection, Scene::getProjectionMatrix());
|
||||
mProgram.setUniformValue(view, Scene::getViewMatrix());
|
||||
mProgram.setUniformValue(model, mTransform.toMatrix());
|
||||
}
|
||||
|
||||
void MeshRenderer::setShape(const Shape & value) {
|
||||
void MeshRenderer::setShape(const Shape & value)
|
||||
{
|
||||
Object::setShape(value);
|
||||
init();
|
||||
}
|
||||
|
||||
void MeshRenderer::setColor(const QVector3D & color) {
|
||||
if(mShape.mColors.empty()) {
|
||||
for(const auto & vertex : mShape.getVertices()) {
|
||||
void MeshRenderer::setColor(const QVector3D & color)
|
||||
{
|
||||
if (mShape.mColors.empty()) {
|
||||
for (const auto & vertex : mShape.getVertices()) {
|
||||
mShape.mColors.push_back(color);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < mShape.getColors().size(); i++) {
|
||||
for (int i = 0; i < mShape.getColors().size(); i++) {
|
||||
mShape.mColors[i] = color;
|
||||
}
|
||||
}
|
||||
@@ -198,7 +225,8 @@ void MeshRenderer::setColor(const QVector3D & color) {
|
||||
}
|
||||
|
||||
void MeshRenderer::setAttributeBuffer(
|
||||
int location, GLenum type, int offset, int tupleSize, int stride) {
|
||||
int location, GLenum type, int offset, int tupleSize, int stride)
|
||||
{
|
||||
ShaderBindScope lock(&mProgram, mBound);
|
||||
mVAO.bind();
|
||||
mProgram.setAttributeBuffer(location, type, offset, tupleSize, stride);
|
||||
@@ -210,8 +238,9 @@ void MeshRenderer::setAttributeBuffer(
|
||||
******************************************************************************/
|
||||
|
||||
// Static member function to retrieve instances of MeshRenderers
|
||||
MeshRenderer * MeshRenderer::getInstance(const QString & name) {
|
||||
if(!sInstances.contains(name)) {
|
||||
MeshRenderer * MeshRenderer::getInstance(const QString & name)
|
||||
{
|
||||
if (!sInstances.contains(name)) {
|
||||
#if QTK_DEBUG
|
||||
qDebug() << "Attempt to access MeshRenderer instance that does not exist! ("
|
||||
<< qPrintable(name) << ")\n";
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
#include "qtkapi.h"
|
||||
#include "shape.h"
|
||||
|
||||
namespace Qtk {
|
||||
class QTKAPI MeshRenderer : public Object {
|
||||
namespace Qtk
|
||||
{
|
||||
class QTKAPI MeshRenderer : public Object
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Typedefs
|
||||
@@ -38,9 +40,10 @@ namespace Qtk {
|
||||
* @param indices Indicess to use for initializes geometry shape.
|
||||
* @param mode OpenGL draw mode. Supported modes are prefixed with QTK_*
|
||||
*/
|
||||
MeshRenderer(
|
||||
const char * name, Vertices vertices, Indices indices,
|
||||
DrawMode mode = QTK_DRAW_ARRAYS);
|
||||
MeshRenderer(const char * name,
|
||||
Vertices vertices,
|
||||
Indices indices,
|
||||
DrawMode mode = QTK_DRAW_ARRAYS);
|
||||
|
||||
/**
|
||||
* Delegate constructor.
|
||||
@@ -112,14 +115,16 @@ namespace Qtk {
|
||||
/**
|
||||
* @param vert Path to vertex shader to use for this MeshRenderer.
|
||||
*/
|
||||
inline void setShaderVertex(const std::string & vert) {
|
||||
inline void setShaderVertex(const std::string & vert)
|
||||
{
|
||||
mVertexShader = vert;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param frag Path to fragment shader to use for this MeshRenderer.
|
||||
*/
|
||||
inline void setShaderFragment(const std::string & frag) {
|
||||
inline void setShaderFragment(const std::string & frag)
|
||||
{
|
||||
mFragmentShader = frag;
|
||||
}
|
||||
|
||||
@@ -134,7 +139,8 @@ namespace Qtk {
|
||||
* @param location Index location of the uniform value we are setting.
|
||||
* @param value The value to use for the uniform.
|
||||
*/
|
||||
template <typename T> inline void setUniform(int location, T value) {
|
||||
template <typename T> inline void setUniform(int location, T value)
|
||||
{
|
||||
ShaderBindScope lock(&mProgram, mBound);
|
||||
mProgram.setUniformValue(location, value);
|
||||
}
|
||||
@@ -145,7 +151,8 @@ namespace Qtk {
|
||||
* @param value The value to use for the uniform.
|
||||
*/
|
||||
template <typename T>
|
||||
inline void setUniform(const char * location, T value) {
|
||||
inline void setUniform(const char * location, T value)
|
||||
{
|
||||
ShaderBindScope lock(&mProgram, mBound);
|
||||
mProgram.setUniformValue(location, value);
|
||||
}
|
||||
@@ -159,9 +166,9 @@ namespace Qtk {
|
||||
* @param view Name of the uniform to store the View matrix.
|
||||
* @param projection Name of the uniform to store the Projection matrix.
|
||||
*/
|
||||
void setUniformMVP(
|
||||
const char * model = "uModel", const char * view = "uView",
|
||||
const char * projection = "uProjection");
|
||||
void setUniformMVP(const char * model = "uModel",
|
||||
const char * view = "uView",
|
||||
const char * projection = "uProjection");
|
||||
|
||||
/**
|
||||
* Sets the shape of the MeshRenderer using the Object base class method.
|
||||
@@ -209,13 +216,15 @@ namespace Qtk {
|
||||
/**
|
||||
* @return Transform3D attached to this MeshRenderer.
|
||||
*/
|
||||
inline Transform3D & getTransform() { return mTransform; }
|
||||
inline Transform3D & getTransform() override { return mTransform; }
|
||||
|
||||
inline std::string getVertexShader() const override {
|
||||
[[nodiscard]] inline std::string getVertexShader() const override
|
||||
{
|
||||
return mVertexShader;
|
||||
}
|
||||
|
||||
inline std::string getFragmentShader() const override {
|
||||
[[nodiscard]] inline std::string getFragmentShader() const override
|
||||
{
|
||||
return mFragmentShader;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,25 +21,28 @@ Model::ModelManager Model::mManager;
|
||||
* Public Member Functions
|
||||
******************************************************************************/
|
||||
|
||||
void Model::draw() {
|
||||
for(auto & mesh : mMeshes) {
|
||||
void Model::draw()
|
||||
{
|
||||
for (auto & mesh : mMeshes) {
|
||||
mesh.mTransform = mTransform;
|
||||
mesh.draw();
|
||||
}
|
||||
}
|
||||
|
||||
void Model::draw(QOpenGLShaderProgram & shader) {
|
||||
for(auto & mesh : mMeshes) {
|
||||
void Model::draw(QOpenGLShaderProgram & shader)
|
||||
{
|
||||
for (auto & mesh : mMeshes) {
|
||||
mesh.mTransform = mTransform;
|
||||
mesh.draw(shader);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
|
||||
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
|
||||
{
|
||||
bool modified = false;
|
||||
std::string fullPath = mDirectory + '/' + fileName;
|
||||
for(auto & texture : mTexturesLoaded) {
|
||||
if(texture.mPath == fileName) {
|
||||
for (auto & texture : mTexturesLoaded) {
|
||||
if (texture.mPath == fileName) {
|
||||
texture.mTexture->destroy();
|
||||
texture.mTexture->create();
|
||||
texture.mTexture->setData(
|
||||
@@ -47,14 +50,15 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
if(!modified) {
|
||||
if (!modified) {
|
||||
qDebug() << "Attempt to flip texture that doesn't exist: "
|
||||
<< fullPath.c_str() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Static function to access ModelManager for getting Models by name
|
||||
Model * Qtk::Model::getInstance(const char * name) {
|
||||
Model * Qtk::Model::getInstance(const char * name)
|
||||
{
|
||||
return mManager[name];
|
||||
}
|
||||
|
||||
@@ -62,10 +66,11 @@ Model * Qtk::Model::getInstance(const char * name) {
|
||||
* Private Member Functions
|
||||
******************************************************************************/
|
||||
|
||||
void Model::loadModel(const std::string & path) {
|
||||
void Model::loadModel(const std::string & path)
|
||||
{
|
||||
Assimp::Importer import;
|
||||
// If using a Qt Resource path, use QtkIOSystem for file handling.
|
||||
if(path.front() == ':') {
|
||||
if (path.front() == ':') {
|
||||
import.SetIOHandler(new QtkIOSystem());
|
||||
}
|
||||
// Used as base path for loading model textures.
|
||||
@@ -75,13 +80,14 @@ void Model::loadModel(const std::string & path) {
|
||||
// + And flipping texture UVs, etc..
|
||||
// Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
|
||||
const aiScene * scene = import.ReadFile(
|
||||
path.c_str(), aiProcess_Triangulate | aiProcess_FlipUVs
|
||||
| aiProcess_GenSmoothNormals
|
||||
| aiProcess_CalcTangentSpace | aiProcess_OptimizeMeshes
|
||||
| aiProcess_SplitLargeMeshes);
|
||||
path.c_str(),
|
||||
aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals
|
||||
| aiProcess_CalcTangentSpace | aiProcess_OptimizeMeshes
|
||||
| aiProcess_SplitLargeMeshes);
|
||||
|
||||
// If there were errors, print and return
|
||||
if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
|
||||
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE
|
||||
|| !scene->mRootNode) {
|
||||
qDebug() << "Error::ASSIMP::" << import.GetErrorString() << "\n";
|
||||
return;
|
||||
}
|
||||
@@ -99,26 +105,28 @@ void Model::loadModel(const std::string & path) {
|
||||
mManager.insert(getName().c_str(), this);
|
||||
}
|
||||
|
||||
void Model::processNode(aiNode * node, const aiScene * scene) {
|
||||
void Model::processNode(aiNode * node, const aiScene * scene)
|
||||
{
|
||||
// Process each mesh that is available for this node
|
||||
for(GLuint i = 0; i < node->mNumMeshes; i++) {
|
||||
for (GLuint i = 0; i < node->mNumMeshes; i++) {
|
||||
aiMesh * mesh = scene->mMeshes[node->mMeshes[i]];
|
||||
mMeshes.push_back(processMesh(mesh, scene));
|
||||
}
|
||||
|
||||
// Process each child node for this mesh using recursion
|
||||
for(GLuint i = 0; i < node->mNumChildren; i++) {
|
||||
for (GLuint i = 0; i < node->mNumChildren; i++) {
|
||||
processNode(node->mChildren[i], scene);
|
||||
}
|
||||
}
|
||||
|
||||
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
|
||||
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
|
||||
{
|
||||
ModelMesh::Vertices vertices;
|
||||
ModelMesh::Indices indices;
|
||||
ModelMesh::Textures textures;
|
||||
|
||||
// For each vertex in the aiMesh
|
||||
for(GLuint i = 0; i < mesh->mNumVertices; i++) {
|
||||
for (GLuint i = 0; i < mesh->mNumVertices; i++) {
|
||||
// Create a local vertex object for positions, normals, and texture coords
|
||||
ModelVertex vertex;
|
||||
|
||||
@@ -132,7 +140,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
|
||||
// Set the position of our local vertex to the local vector object
|
||||
vertex.mPosition = vector3D;
|
||||
|
||||
if(mesh->HasNormals()) {
|
||||
if (mesh->HasNormals()) {
|
||||
// Initialize vertex normal
|
||||
vector3D.setX(mesh->mNormals[i].x);
|
||||
vector3D.setY(mesh->mNormals[i].y);
|
||||
@@ -142,7 +150,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
|
||||
}
|
||||
|
||||
// Initialize texture coordinates, if any are available
|
||||
if(mesh->mTextureCoords[0]) {
|
||||
if (mesh->mTextureCoords[0]) {
|
||||
QVector2D vector2D;
|
||||
// Texture coordinates
|
||||
vector2D.setX(mesh->mTextureCoords[0][i].x);
|
||||
@@ -169,16 +177,16 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
|
||||
}
|
||||
|
||||
// For each face on the mesh, process its indices
|
||||
for(GLuint i = 0; i < mesh->mNumFaces; i++) {
|
||||
for (GLuint i = 0; i < mesh->mNumFaces; i++) {
|
||||
aiFace face = mesh->mFaces[i];
|
||||
for(GLuint j = 0; j < face.mNumIndices; j++) {
|
||||
for (GLuint j = 0; j < face.mNumIndices; j++) {
|
||||
// Add the index to out container of indices
|
||||
indices.push_back(face.mIndices[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// Process material
|
||||
if(mesh->mMaterialIndex >= 0) {
|
||||
if (mesh->mMaterialIndex >= 0) {
|
||||
// Get the material attached to the model using Assimp
|
||||
aiMaterial * material = scene->mMaterials[mesh->mMaterialIndex];
|
||||
// Get all diffuse textures from the material
|
||||
@@ -200,25 +208,29 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
|
||||
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
|
||||
}
|
||||
|
||||
return {
|
||||
vertices, indices, textures, mVertexShader.c_str(),
|
||||
mFragmentShader.c_str()};
|
||||
return {vertices,
|
||||
indices,
|
||||
textures,
|
||||
mVertexShader.c_str(),
|
||||
mFragmentShader.c_str()};
|
||||
}
|
||||
|
||||
ModelMesh::Textures Model::loadMaterialTextures(
|
||||
aiMaterial * mat, aiTextureType type, const std::string & typeName) {
|
||||
ModelMesh::Textures Model::loadMaterialTextures(aiMaterial * mat,
|
||||
aiTextureType type,
|
||||
const std::string & typeName)
|
||||
{
|
||||
ModelMesh::Textures textures;
|
||||
|
||||
for(GLuint i = 0; i < mat->GetTextureCount(type); i++) {
|
||||
for (GLuint i = 0; i < mat->GetTextureCount(type); i++) {
|
||||
// Call GetTexture to get the name of the texture file to load
|
||||
aiString fileName;
|
||||
mat->GetTexture(type, i, &fileName);
|
||||
|
||||
// Check if we have already loaded this texture
|
||||
bool skip = false;
|
||||
for(auto & j : mTexturesLoaded) {
|
||||
for (auto & j : mTexturesLoaded) {
|
||||
// If the path to the texture already exists in m_texturesLoaded, skip it
|
||||
if(std::strcmp(j.mPath.data(), fileName.C_Str()) == 0) {
|
||||
if (std::strcmp(j.mPath.data(), fileName.C_Str()) == 0) {
|
||||
textures.push_back(j);
|
||||
// If we have loaded the texture, do not load it again
|
||||
skip = true;
|
||||
@@ -227,10 +239,11 @@ ModelMesh::Textures Model::loadMaterialTextures(
|
||||
}
|
||||
|
||||
// If the texture has not yet been loaded
|
||||
if(!skip) {
|
||||
if (!skip) {
|
||||
ModelTexture texture;
|
||||
texture.mTexture = OpenGLTextureFactory::initTexture(
|
||||
std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false,
|
||||
std::string(mDirectory + '/' + fileName.C_Str()).c_str(),
|
||||
false,
|
||||
false);
|
||||
texture.mID = texture.mTexture->textureId();
|
||||
texture.mType = typeName;
|
||||
@@ -246,7 +259,8 @@ ModelMesh::Textures Model::loadMaterialTextures(
|
||||
return textures;
|
||||
}
|
||||
|
||||
void Model::sortModelMeshes() {
|
||||
void Model::sortModelMeshes()
|
||||
{
|
||||
auto cameraPos = Scene::getCamera().getTransform();
|
||||
auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) {
|
||||
// Sort by the first vertex position in the model
|
||||
|
||||
@@ -24,12 +24,14 @@
|
||||
#include "modelmesh.h"
|
||||
#include "qtkapi.h"
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
/**
|
||||
* Model object that has a ModelMesh.
|
||||
* Top-level object that represents 3D models stored within a scene.
|
||||
*/
|
||||
class QTKAPI Model : public Object {
|
||||
class QTKAPI Model : public Object
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Typedefs
|
||||
@@ -51,13 +53,13 @@ namespace Qtk {
|
||||
* @param vertexShader Optional path to custom vertex shader.
|
||||
* @param fragmentShader Optional path to custom fragment shader.
|
||||
*/
|
||||
inline Model(
|
||||
const char * name, const char * path,
|
||||
const char * vertexShader = ":/shaders/model-basic.vert",
|
||||
const char * fragmentShader = ":/shaders/model-basic.frag") :
|
||||
Object(name, QTK_MODEL),
|
||||
mModelPath(path), mVertexShader(vertexShader),
|
||||
mFragmentShader(fragmentShader) {
|
||||
inline Model(const char * name,
|
||||
const char * path,
|
||||
const char * vertexShader = "",
|
||||
const char * fragmentShader = "") :
|
||||
Object(name, QTK_MODEL), mModelPath(path),
|
||||
mVertexShader(vertexShader), mFragmentShader(fragmentShader)
|
||||
{
|
||||
loadModel(mModelPath);
|
||||
}
|
||||
|
||||
@@ -86,8 +88,9 @@ namespace Qtk {
|
||||
* @param flipX Flip the texture along the X axis
|
||||
* @param flipY Flip the texture along the Y axis
|
||||
*/
|
||||
void flipTexture(
|
||||
const std::string & fileName, bool flipX = false, bool flipY = true);
|
||||
void flipTexture(const std::string & fileName,
|
||||
bool flipX = false,
|
||||
bool flipY = true);
|
||||
|
||||
/*************************************************************************
|
||||
* Setters
|
||||
@@ -101,8 +104,9 @@ namespace Qtk {
|
||||
* @param value The value to assign to the uniform
|
||||
*/
|
||||
template <typename T>
|
||||
inline void setUniform(const char * location, T value) {
|
||||
for(auto & mesh : mMeshes) {
|
||||
inline void setUniform(const char * location, T value)
|
||||
{
|
||||
for (auto & mesh : mMeshes) {
|
||||
mesh.mProgram->bind();
|
||||
mesh.mProgram->setUniformValue(location, value);
|
||||
mesh.mProgram->release();
|
||||
@@ -125,13 +129,15 @@ namespace Qtk {
|
||||
/**
|
||||
* @return Transform3D attached to this Model.
|
||||
*/
|
||||
inline Transform3D & getTransform() { return mTransform; }
|
||||
inline Transform3D & getTransform() override { return mTransform; }
|
||||
|
||||
inline std::string getVertexShader() const override {
|
||||
[[nodiscard]] inline std::string getVertexShader() const override
|
||||
{
|
||||
return mVertexShader;
|
||||
}
|
||||
|
||||
inline std::string getFragmentShader() const override {
|
||||
[[nodiscard]] inline std::string getFragmentShader() const override
|
||||
{
|
||||
return mFragmentShader;
|
||||
}
|
||||
|
||||
@@ -183,8 +189,9 @@ namespace Qtk {
|
||||
* @param typeName Texture type name in string format.
|
||||
* @return Collection of all textures for a single ModelMesh.
|
||||
*/
|
||||
ModelMesh::Textures loadMaterialTextures(
|
||||
aiMaterial * mat, aiTextureType type, const std::string & typeName);
|
||||
ModelMesh::Textures loadMaterialTextures(aiMaterial * mat,
|
||||
aiTextureType type,
|
||||
const std::string & typeName);
|
||||
|
||||
/**
|
||||
* Sorts each mesh in the Model based on distance from the camera.
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "modelmesh.h"
|
||||
#include "scene.h"
|
||||
#include "shaders.h"
|
||||
|
||||
using namespace Qtk;
|
||||
|
||||
@@ -15,7 +16,8 @@ using namespace Qtk;
|
||||
* Public Member Functions
|
||||
******************************************************************************/
|
||||
|
||||
void ModelMesh::draw(QOpenGLShaderProgram & shader) {
|
||||
void ModelMesh::draw(QOpenGLShaderProgram & shader)
|
||||
{
|
||||
mVAO->bind();
|
||||
// Bind shader
|
||||
shader.bind();
|
||||
@@ -28,7 +30,7 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
|
||||
GLuint diffuseCount = 1;
|
||||
GLuint specularCount = 1;
|
||||
GLuint normalCount = 1;
|
||||
for(GLuint i = 0; i < mTextures.size(); i++) {
|
||||
for (GLuint i = 0; i < mTextures.size(); i++) {
|
||||
// Activate the current texture index by adding offset to GL_TEXTURE0
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
mTextures[i].mTexture->bind();
|
||||
@@ -38,13 +40,13 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
|
||||
// Specular: material.texture_specular1, material.texture_specular2, ...
|
||||
std::string number;
|
||||
std::string name = mTextures[i].mType;
|
||||
if(name == "texture_diffuse") {
|
||||
if (name == "texture_diffuse") {
|
||||
number = std::to_string(diffuseCount++);
|
||||
}
|
||||
if(name == "texture_specular") {
|
||||
if (name == "texture_specular") {
|
||||
number = std::to_string(specularCount++);
|
||||
}
|
||||
if(name == "texture_normal") {
|
||||
if (name == "texture_normal") {
|
||||
number = std::to_string(normalCount++);
|
||||
}
|
||||
|
||||
@@ -61,7 +63,7 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
|
||||
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
|
||||
|
||||
// Release shader, textures
|
||||
for(const auto & texture : mTextures) {
|
||||
for (const auto & texture : mTextures) {
|
||||
texture.mTexture->release();
|
||||
}
|
||||
shader.release();
|
||||
@@ -72,11 +74,12 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
|
||||
* Private Member Functions
|
||||
******************************************************************************/
|
||||
|
||||
void ModelMesh::initMesh(const char * vert, const char * frag) {
|
||||
void ModelMesh::initMesh(const std::string & vert, const std::string & frag)
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
// Create VAO, VBO, EBO
|
||||
bool status = mVAO->create();
|
||||
mVAO->create();
|
||||
mVBO->create();
|
||||
mEBO->create();
|
||||
|
||||
@@ -95,10 +98,26 @@ void ModelMesh::initMesh(const char * vert, const char * frag) {
|
||||
mEBO->release();
|
||||
|
||||
// Load and link shaders
|
||||
mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert);
|
||||
mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag);
|
||||
mProgram->link();
|
||||
mProgram->bind();
|
||||
if (!vert.empty()) {
|
||||
mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert.c_str());
|
||||
} else {
|
||||
mProgram->addShaderFromSourceCode(QOpenGLShader::Vertex,
|
||||
QTK_SHADER_VERTEX_MODEL);
|
||||
}
|
||||
|
||||
if (!frag.empty()) {
|
||||
mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag.c_str());
|
||||
} else {
|
||||
mProgram->addShaderFromSourceCode(QOpenGLShader::Fragment,
|
||||
QTK_SHADER_FRAGMENT_MODEL);
|
||||
}
|
||||
|
||||
if (!mProgram->link()) {
|
||||
qDebug() << "Failed to link shader: " << mProgram->log();
|
||||
}
|
||||
if (!mProgram->bind()) {
|
||||
qDebug() << "Failed to bind shader: " << mProgram->log();
|
||||
}
|
||||
|
||||
// Positions
|
||||
mProgram->enableAttributeArray(0);
|
||||
@@ -112,9 +131,11 @@ void ModelMesh::initMesh(const char * vert, const char * frag) {
|
||||
|
||||
// Texture Coordinates
|
||||
mProgram->enableAttributeArray(2);
|
||||
mProgram->setAttributeBuffer(
|
||||
2, GL_FLOAT, offsetof(ModelVertex, mTextureCoord), 2,
|
||||
sizeof(ModelVertex));
|
||||
mProgram->setAttributeBuffer(2,
|
||||
GL_FLOAT,
|
||||
offsetof(ModelVertex, mTextureCoord),
|
||||
2,
|
||||
sizeof(ModelVertex));
|
||||
|
||||
// Vertex tangents
|
||||
mProgram->enableAttributeArray(3);
|
||||
|
||||
@@ -9,12 +9,15 @@
|
||||
#ifndef QTK_MODELMESH_H
|
||||
#define QTK_MODELMESH_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
#include "object.h"
|
||||
#include "transform3D.h"
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
/**
|
||||
* 3D models will store this data for each vertex in geometry.
|
||||
*/
|
||||
@@ -39,8 +42,9 @@ namespace Qtk {
|
||||
* @param type Type of texture in string format.
|
||||
* @param path Path to the texture on disk.
|
||||
*/
|
||||
ModelTexture(const std::string & type, const std::string & path) :
|
||||
mType(type), mPath(path) {
|
||||
ModelTexture(std::string type, const std::string & path) :
|
||||
mType(std::move(type)), mPath(path)
|
||||
{
|
||||
mTexture = OpenGLTextureFactory::initTexture(path.c_str());
|
||||
mID = mTexture->textureId();
|
||||
}
|
||||
@@ -64,7 +68,8 @@ namespace Qtk {
|
||||
* Mesh class specialized for storing 3D model data.
|
||||
* Eventually this can be consolidated into a more generic class.
|
||||
*/
|
||||
class QTKAPI ModelMesh : protected QOpenGLFunctions {
|
||||
class QTKAPI ModelMesh : protected QOpenGLFunctions
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Typedefs
|
||||
@@ -89,16 +94,18 @@ namespace Qtk {
|
||||
* @param vertexShader Path to vertex shader for this ModelMesh.
|
||||
* @param fragmentShader Path to fragment shader for this ModelMesh.
|
||||
*/
|
||||
ModelMesh(
|
||||
Vertices vertices, Indices indices, Textures textures,
|
||||
const char * vertexShader = ":/model-basic.vert",
|
||||
const char * fragmentShader = ":/model-basic.frag") :
|
||||
ModelMesh(Vertices vertices,
|
||||
Indices indices,
|
||||
Textures textures,
|
||||
const char * vertexShader = "",
|
||||
const char * fragmentShader = "") :
|
||||
mProgram(new QOpenGLShaderProgram),
|
||||
mVAO(new QOpenGLVertexArrayObject),
|
||||
mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)),
|
||||
mEBO(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer)),
|
||||
mVertices(std::move(vertices)), mIndices(std::move(indices)),
|
||||
mTextures(std::move(textures)) {
|
||||
mTextures(std::move(textures))
|
||||
{
|
||||
initMesh(vertexShader, fragmentShader);
|
||||
}
|
||||
|
||||
@@ -139,7 +146,7 @@ namespace Qtk {
|
||||
* @param vert Path to vertex shader to use for this model.
|
||||
* @param frag Path to fragment shader to use for this model.
|
||||
*/
|
||||
void initMesh(const char * vert, const char * frag);
|
||||
void initMesh(const std::string & vert, const std::string & frag);
|
||||
|
||||
/*************************************************************************
|
||||
* Private Members
|
||||
|
||||
@@ -9,3 +9,15 @@
|
||||
#include "object.h"
|
||||
|
||||
using namespace Qtk;
|
||||
|
||||
std::string Object::getShaderSourceCode(
|
||||
QOpenGLShader::ShaderType shader_type) const
|
||||
{
|
||||
for (const auto & shader : mProgram.shaders()) {
|
||||
if (shader->shaderType() == shader_type) {
|
||||
return shader->sourceCode().toStdString();
|
||||
}
|
||||
}
|
||||
qDebug() << "Failed to find shader of type " << shader_type;
|
||||
return "";
|
||||
}
|
||||
|
||||
130
src/qtk/object.h
130
src/qtk/object.h
@@ -17,14 +17,16 @@
|
||||
#include "shape.h"
|
||||
#include "texture.h"
|
||||
|
||||
namespace Qtk {
|
||||
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.
|
||||
*/
|
||||
class QTKAPI Object : public QObject {
|
||||
class QTKAPI Object : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -47,16 +49,16 @@ namespace Qtk {
|
||||
// Initialize an object with no shape data assigned
|
||||
explicit Object(const char * name, Type type) :
|
||||
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false),
|
||||
mType(type) {
|
||||
initResources();
|
||||
mType(type)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
// Initialize an object with shape data assigned
|
||||
Object(const char * name, const ShapeBase & shape, Type type) :
|
||||
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape),
|
||||
mBound(false), mType(type) {
|
||||
initResources();
|
||||
mBound(false), mType(type)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
@@ -66,29 +68,35 @@ namespace Qtk {
|
||||
* Accessors
|
||||
************************************************************************/
|
||||
|
||||
[[nodiscard]] inline const Colors & getColors() const {
|
||||
[[nodiscard]] inline const Colors & getColors() const
|
||||
{
|
||||
return mShape.mColors;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const Indices & getIndexData() const {
|
||||
[[nodiscard]] inline const Indices & getIndexData() const
|
||||
{
|
||||
return mShape.mIndices;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const Normals & getNormals() const {
|
||||
[[nodiscard]] inline const Normals & getNormals() const
|
||||
{
|
||||
return mShape.mNormals;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const Shape & getShape() const { return mShape; }
|
||||
|
||||
[[nodiscard]] inline const TexCoords & getTexCoords() const {
|
||||
[[nodiscard]] inline const TexCoords & getTexCoords() const
|
||||
{
|
||||
return mShape.mTexCoords;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const Texture & getTexture() const {
|
||||
[[nodiscard]] inline const Texture & getTexture() const
|
||||
{
|
||||
return mTexture;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const Vertices & getVertices() const {
|
||||
[[nodiscard]] inline const Vertices & getVertices() const
|
||||
{
|
||||
return mShape.mVertices;
|
||||
}
|
||||
|
||||
@@ -96,102 +104,142 @@ namespace Qtk {
|
||||
|
||||
[[nodiscard]] inline const Type & getType() const { return mType; }
|
||||
|
||||
[[nodiscard]] inline virtual const Transform3D & getTransform() const {
|
||||
[[nodiscard]] inline virtual const Transform3D & getTransform() const
|
||||
{
|
||||
return mTransform;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline virtual std::string getVertexShader() const {
|
||||
[[nodiscard]] inline virtual Transform3D & getTransform()
|
||||
{
|
||||
return mTransform;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline virtual std::string getVertexShader() const
|
||||
{
|
||||
return "Base Object has no vertex shader.";
|
||||
}
|
||||
|
||||
virtual inline std::string getFragmentShader() const {
|
||||
[[nodiscard]] virtual inline std::string getFragmentShader() const
|
||||
{
|
||||
return "Base Object has no fragment shader.";
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual std::string getShaderSourceCode(
|
||||
QOpenGLShader::ShaderType shader_type) const;
|
||||
|
||||
|
||||
[[nodiscard]] virtual inline std::string getVertexShaderSourceCode() const
|
||||
{
|
||||
return getShaderSourceCode(QOpenGLShader::Vertex);
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual inline std::string getFragmentShaderSourceCode()
|
||||
const
|
||||
{
|
||||
return getShaderSourceCode(QOpenGLShader::Fragment);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Setters
|
||||
************************************************************************/
|
||||
|
||||
virtual inline void setName(const std::string & name) { mName = name; }
|
||||
|
||||
virtual inline void setColors(const Colors & value) {
|
||||
virtual inline void setColors(const Colors & value)
|
||||
{
|
||||
mShape.mColors = value;
|
||||
}
|
||||
|
||||
virtual inline void setIndices(const Indices & value) {
|
||||
virtual inline void setIndices(const Indices & value)
|
||||
{
|
||||
mShape.mIndices = value;
|
||||
}
|
||||
|
||||
virtual inline void setNormals(const Normals & value) {
|
||||
virtual inline void setNormals(const Normals & value)
|
||||
{
|
||||
mShape.mNormals = value;
|
||||
}
|
||||
|
||||
virtual inline void setShape(const Shape & value) { mShape = value; }
|
||||
|
||||
virtual inline void setTexCoords(const TexCoords & value) {
|
||||
virtual inline void setTexCoords(const TexCoords & value)
|
||||
{
|
||||
mShape.mTexCoords = value;
|
||||
}
|
||||
|
||||
virtual inline void setTexture(
|
||||
const char * path, bool flipX = false, bool flipY = false) {
|
||||
virtual inline void setTexture(const char * path,
|
||||
bool flipX = false,
|
||||
bool flipY = false)
|
||||
{
|
||||
mTexture.setTexture(path, flipX, flipY);
|
||||
}
|
||||
|
||||
virtual inline void setCubeMap(const char * path) {
|
||||
virtual inline void setCubeMap(const char * path)
|
||||
{
|
||||
mTexture.setCubeMap(path);
|
||||
}
|
||||
|
||||
virtual inline void setTexture(const Texture & t) {
|
||||
virtual inline void setTexture(const Texture & t)
|
||||
{
|
||||
mTexture.setTexture(t.getPath());
|
||||
}
|
||||
|
||||
virtual inline void setVertices(const Vertices & value) {
|
||||
virtual inline void setVertices(const Vertices & value)
|
||||
{
|
||||
mShape.mVertices = value;
|
||||
}
|
||||
|
||||
inline void setScaleX(double x) {
|
||||
inline void setScaleX(double x)
|
||||
{
|
||||
mTransform.setScale(
|
||||
x, mTransform.getScale().y(), mTransform.getScale().z());
|
||||
}
|
||||
|
||||
inline void setScaleY(double y) {
|
||||
inline void setScaleY(double y)
|
||||
{
|
||||
mTransform.setScale(
|
||||
mTransform.getScale().x(), y, mTransform.getScale().z());
|
||||
}
|
||||
|
||||
inline void setScaleZ(double z) {
|
||||
inline void setScaleZ(double z)
|
||||
{
|
||||
mTransform.setScale(
|
||||
mTransform.getScale().x(), mTransform.getScale().y(), z);
|
||||
}
|
||||
|
||||
inline void setTranslationX(double x) {
|
||||
mTransform.setTranslation(
|
||||
x, mTransform.getTranslation().y(),
|
||||
mTransform.getTranslation().z());
|
||||
inline void setTranslationX(double x)
|
||||
{
|
||||
mTransform.setTranslation(x,
|
||||
mTransform.getTranslation().y(),
|
||||
mTransform.getTranslation().z());
|
||||
}
|
||||
|
||||
inline void setTranslationY(double y) {
|
||||
mTransform.setTranslation(
|
||||
mTransform.getTranslation().x(), y,
|
||||
mTransform.getTranslation().z());
|
||||
inline void setTranslationY(double y)
|
||||
{
|
||||
mTransform.setTranslation(mTransform.getTranslation().x(),
|
||||
y,
|
||||
mTransform.getTranslation().z());
|
||||
}
|
||||
|
||||
inline void setTranslationZ(double z) {
|
||||
mTransform.setTranslation(
|
||||
mTransform.getTranslation().x(), mTransform.getTranslation().y(),
|
||||
z);
|
||||
inline void setTranslationZ(double z)
|
||||
{
|
||||
mTransform.setTranslation(mTransform.getTranslation().x(),
|
||||
mTransform.getTranslation().y(),
|
||||
z);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Public Methods
|
||||
************************************************************************/
|
||||
|
||||
virtual inline void bindShaders() {
|
||||
virtual inline void bindShaders()
|
||||
{
|
||||
mBound = true;
|
||||
mProgram.bind();
|
||||
}
|
||||
|
||||
virtual inline void releaseShaders() {
|
||||
virtual inline void releaseShaders()
|
||||
{
|
||||
mBound = false;
|
||||
mProgram.release();
|
||||
}
|
||||
|
||||
@@ -22,16 +22,8 @@
|
||||
#define QTKAPI
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize Qt resources required by the Qtk library.
|
||||
* This cannot be defined within any namespace, but can be called by ctors.
|
||||
* See object.h for example.
|
||||
*/
|
||||
inline void initResources() {
|
||||
Q_INIT_RESOURCE(resources);
|
||||
}
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
/**
|
||||
* Flag to set context for debug messages.
|
||||
*/
|
||||
@@ -43,20 +35,14 @@ namespace Qtk {
|
||||
* @param widget Widget to start the search from.
|
||||
* @return Top level parent widget or Q_NULLPTR if no parent
|
||||
*/
|
||||
static QWidget * topLevelParent(QWidget * widget) {
|
||||
static QWidget * topLevelParent(QWidget * widget)
|
||||
{
|
||||
QString name = widget->objectName();
|
||||
while(widget->parentWidget() != Q_NULLPTR) {
|
||||
while (widget->parentWidget() != Q_NULLPTR) {
|
||||
widget = widget->parentWidget();
|
||||
}
|
||||
return widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Default icon to use for Qtk desktop application.
|
||||
*/
|
||||
static QIcon getIcon() {
|
||||
return QIcon(":/icons/icon.png");
|
||||
}
|
||||
} // namespace Qtk
|
||||
|
||||
#endif // QTK_QTKAPI_H
|
||||
|
||||
@@ -14,23 +14,23 @@ using namespace Qtk;
|
||||
* Constructors, Destructors
|
||||
******************************************************************************/
|
||||
|
||||
QtkIOStream::QtkIOStream(const char * pFile, const char * pMode) :
|
||||
mFile(pFile) {
|
||||
QtkIOStream::QtkIOStream(const char * pFile, const char * pMode) : mFile(pFile)
|
||||
{
|
||||
QString mode(pMode);
|
||||
bool open = false;
|
||||
if(mode == "w" || mode == "wb") {
|
||||
if (mode == "w" || mode == "wb") {
|
||||
open = mFile.open(QIODeviceBase::WriteOnly);
|
||||
} else if(mode == "r" || mode == "rb") {
|
||||
} else if (mode == "r" || mode == "rb") {
|
||||
open = mFile.open(QIODeviceBase::ReadOnly);
|
||||
} else if(mode == "wt") {
|
||||
} else if (mode == "wt") {
|
||||
open = mFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text);
|
||||
} else if(mode == "rt") {
|
||||
} else if (mode == "rt") {
|
||||
open = mFile.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text);
|
||||
} else {
|
||||
open = false;
|
||||
qDebug() << "[Qtk::QtkIOStream] Invalid file open mode: " << mode << "\n";
|
||||
}
|
||||
if(!open) {
|
||||
if (!open) {
|
||||
qDebug() << "[Qtk::QtkIOStream] Could not open file: " << QString(pFile)
|
||||
<< "\n";
|
||||
}
|
||||
@@ -40,9 +40,10 @@ QtkIOStream::QtkIOStream(const char * pFile, const char * pMode) :
|
||||
* Public Member Functions
|
||||
******************************************************************************/
|
||||
|
||||
size_t QtkIOStream::Read(void * pvBuffer, size_t pSize, size_t pCount) {
|
||||
size_t QtkIOStream::Read(void * pvBuffer, size_t pSize, size_t pCount)
|
||||
{
|
||||
qint64 readSize = mFile.read((char *)pvBuffer, pSize * pCount);
|
||||
if(readSize < 0) {
|
||||
if (readSize < 0) {
|
||||
qDebug() << "[Qtk::QtkIOStream] Failed to read (" << pSize
|
||||
<< ") bytes from file at: " << mFile.filesystemFileName().c_str()
|
||||
<< "\n";
|
||||
@@ -51,9 +52,10 @@ size_t QtkIOStream::Read(void * pvBuffer, size_t pSize, size_t pCount) {
|
||||
return readSize;
|
||||
}
|
||||
|
||||
size_t QtkIOStream::Write(const void * pvBuffer, size_t pSize, size_t pCount) {
|
||||
size_t QtkIOStream::Write(const void * pvBuffer, size_t pSize, size_t pCount)
|
||||
{
|
||||
qint64 writeSize = mFile.write((char *)pvBuffer, pSize * pCount);
|
||||
if(writeSize < 0) {
|
||||
if (writeSize < 0) {
|
||||
qDebug() << "[Qtk::QtkIOStream] Failed to write buffer with size (" << pSize
|
||||
<< ") to file at: " << mFile.filesystemFileName().c_str() << "\n";
|
||||
return -1;
|
||||
@@ -61,18 +63,22 @@ size_t QtkIOStream::Write(const void * pvBuffer, size_t pSize, size_t pCount) {
|
||||
return writeSize;
|
||||
}
|
||||
|
||||
aiReturn QtkIOStream::Seek(size_t pOffset, aiOrigin pOrigin) {
|
||||
aiReturn QtkIOStream::Seek(size_t pOffset, aiOrigin pOrigin)
|
||||
{
|
||||
return mFile.seek(pOffset) ? aiReturn_SUCCESS : aiReturn_FAILURE;
|
||||
}
|
||||
|
||||
size_t QtkIOStream::Tell() const {
|
||||
size_t QtkIOStream::Tell() const
|
||||
{
|
||||
return mFile.pos();
|
||||
}
|
||||
|
||||
size_t QtkIOStream::FileSize() const {
|
||||
size_t QtkIOStream::FileSize() const
|
||||
{
|
||||
return mFile.size();
|
||||
}
|
||||
|
||||
void QtkIOStream::Flush() {
|
||||
void QtkIOStream::Flush()
|
||||
{
|
||||
mFile.flush();
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
#ifndef QTK_QTKIOSTREAM_H
|
||||
#define QTK_QTKIOSTREAM_H
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
/**
|
||||
* Custom Assimp IO stream to support QtkIOSystem file handling.
|
||||
* Allows direct use of Qt Resource paths for loading models in Assimp.
|
||||
*/
|
||||
class QtkIOStream : public Assimp::IOStream {
|
||||
class QtkIOStream : public Assimp::IOStream
|
||||
{
|
||||
friend class QtkIOSystem;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -15,22 +15,26 @@ using namespace Qtk;
|
||||
* Public Member Functions
|
||||
******************************************************************************/
|
||||
|
||||
bool QtkIOSystem::Exists(const char * pFile) const {
|
||||
bool QtkIOSystem::Exists(const char * pFile) const
|
||||
{
|
||||
return QFileInfo::exists(pFile);
|
||||
}
|
||||
|
||||
char QtkIOSystem::getOsSeparator() const {
|
||||
char QtkIOSystem::getOsSeparator() const
|
||||
{
|
||||
return QDir::separator().toLatin1();
|
||||
}
|
||||
|
||||
Assimp::IOStream * QtkIOSystem::Open(const char * pFile, const char * pMode) {
|
||||
if(!Exists(pFile)) {
|
||||
Assimp::IOStream * QtkIOSystem::Open(const char * pFile, const char * pMode)
|
||||
{
|
||||
if (!Exists(pFile)) {
|
||||
qDebug() << "[Qtk::QtkIOSystem] failed to open file: " << pFile << "\n";
|
||||
return nullptr;
|
||||
}
|
||||
return new QtkIOStream(pFile, pMode);
|
||||
}
|
||||
|
||||
void QtkIOSystem::Close(Assimp::IOStream * pFile) {
|
||||
void QtkIOSystem::Close(Assimp::IOStream * pFile)
|
||||
{
|
||||
delete pFile;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
#ifndef QTK_QTKIOSYSTEM_H
|
||||
#define QTK_QTKIOSYSTEM_H
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
/**
|
||||
* Assimp IO system for loading models with assimp, using Qt Resource paths.
|
||||
*/
|
||||
class QtkIOSystem : public Assimp::IOSystem {
|
||||
class QtkIOSystem : public Assimp::IOSystem
|
||||
{
|
||||
public:
|
||||
QtkIOSystem() = default;
|
||||
~QtkIOSystem() = default;
|
||||
@@ -40,8 +42,8 @@ namespace Qtk {
|
||||
* @param pMode Mode to open file. See `man fopen`.
|
||||
* @return QtkIOStream for the opened file.
|
||||
*/
|
||||
Assimp::IOStream * Open(
|
||||
const char * pFile, const char * pMode = "rb") override;
|
||||
Assimp::IOStream * Open(const char * pFile,
|
||||
const char * pMode = "rb") override;
|
||||
|
||||
/**
|
||||
* @param pFile File to close.
|
||||
|
||||
@@ -18,16 +18,18 @@ QMatrix4x4 Scene::mProjection;
|
||||
* Constructors / Destructors
|
||||
******************************************************************************/
|
||||
|
||||
Scene::Scene() : mSceneName("Default Scene") {
|
||||
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) {
|
||||
Scene::~Scene()
|
||||
{
|
||||
for (auto & mesh : mMeshes) {
|
||||
delete mesh;
|
||||
}
|
||||
for(auto & model : mModels) {
|
||||
for (auto & model : mModels) {
|
||||
delete model;
|
||||
}
|
||||
delete mSkybox;
|
||||
@@ -37,83 +39,88 @@ Scene::~Scene() {
|
||||
* Public Methods
|
||||
******************************************************************************/
|
||||
|
||||
template <> MeshRenderer * Scene::addObject(MeshRenderer * object) {
|
||||
template <> MeshRenderer * Scene::addObject(MeshRenderer * object)
|
||||
{
|
||||
initSceneObjectName(object);
|
||||
mMeshes.push_back(object);
|
||||
sceneUpdated(mSceneName);
|
||||
emit sceneUpdated(mSceneName);
|
||||
return object;
|
||||
}
|
||||
|
||||
template <> Model * Scene::addObject(Model * object) {
|
||||
template <> Model * Scene::addObject(Model * object)
|
||||
{
|
||||
initSceneObjectName(object);
|
||||
mModels.push_back(object);
|
||||
sceneUpdated(mSceneName);
|
||||
emit sceneUpdated(mSceneName);
|
||||
return object;
|
||||
}
|
||||
|
||||
void Scene::draw() {
|
||||
if(!mInit) {
|
||||
void Scene::draw()
|
||||
{
|
||||
if (!mInit) {
|
||||
initializeOpenGLFunctions();
|
||||
init();
|
||||
mInit = true;
|
||||
}
|
||||
|
||||
while(!mModelLoadQueue.empty()) {
|
||||
// Check if there were new models added that still need to be loaded.
|
||||
// This is for objects added at runtime via click-and-drag events, etc.
|
||||
while (!mModelLoadQueue.empty()) {
|
||||
auto modelSpec = mModelLoadQueue.front();
|
||||
// Load the model and add it to the scene.
|
||||
addObject(new Model(modelSpec.first.c_str(), modelSpec.second.c_str()));
|
||||
mModelLoadQueue.pop();
|
||||
}
|
||||
|
||||
if(mPause) {
|
||||
if (mPause) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(mSkybox != Q_NULLPTR) {
|
||||
if (mSkybox != Q_NULLPTR) {
|
||||
mSkybox->draw();
|
||||
}
|
||||
for(const auto & model : mModels) {
|
||||
for (const auto & model : mModels) {
|
||||
model->draw();
|
||||
}
|
||||
for(const auto & mesh : mMeshes) {
|
||||
for (const auto & mesh : mMeshes) {
|
||||
mesh->draw();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Object *> Scene::getObjects() const {
|
||||
std::vector<Object *> Scene::getObjects() const
|
||||
{
|
||||
// All scene objects must inherit from Qtk::Object.
|
||||
std::vector<Object *> objects(mMeshes.begin(), mMeshes.end());
|
||||
for(const auto & model : mModels) {
|
||||
for (const auto & model : mModels) {
|
||||
objects.push_back(model);
|
||||
if(objects.back() == nullptr) {
|
||||
if (objects.back() == nullptr) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
Object * Scene::getObject(const QString & name) const {
|
||||
for(const auto & object : getObjects()) {
|
||||
if(object->getName() == name.toStdString()) {
|
||||
Object * Scene::getObject(const QString & name) const
|
||||
{
|
||||
for (const auto & object : getObjects()) {
|
||||
if (object->getName() == name.toStdString()) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
void Scene::setSkybox(Skybox * skybox) {
|
||||
void Scene::setSkybox(Skybox * skybox)
|
||||
{
|
||||
delete mSkybox;
|
||||
mSkybox = skybox;
|
||||
}
|
||||
|
||||
void Scene::initSceneObjectName(Object * object) {
|
||||
if(!mObjectCount.count(object->getName())) {
|
||||
mObjectCount[object->getName()] = 1;
|
||||
} else {
|
||||
mObjectCount[object->getName()]++;
|
||||
}
|
||||
auto count = mObjectCount[object->getName()];
|
||||
if(count > 1) {
|
||||
void Scene::initSceneObjectName(Object * object)
|
||||
{
|
||||
// If the object name exists make it unique.
|
||||
auto count = ++mObjectCount[object->getName()];
|
||||
if (count > 1) {
|
||||
object->setName(object->getName() + " (" + std::to_string(count) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
#include "model.h"
|
||||
#include "skybox.h"
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
/**
|
||||
* An abstract Scene class to inherit from when building new scenes.
|
||||
*
|
||||
@@ -43,7 +44,8 @@ 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 : public QObject, protected QOpenGLFunctions {
|
||||
class Scene : public QObject, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -53,7 +55,7 @@ namespace Qtk {
|
||||
|
||||
Scene();
|
||||
|
||||
virtual ~Scene();
|
||||
~Scene() override;
|
||||
|
||||
/*************************************************************************
|
||||
* Public Methods
|
||||
@@ -73,18 +75,23 @@ namespace Qtk {
|
||||
|
||||
/**
|
||||
* Function called to update the QOpenGLWidget. Does not trigger a redraw.
|
||||
*
|
||||
* This method can translate or rotate objects to simulate movement.
|
||||
* Calling this several times will still result in only one repaint.
|
||||
*
|
||||
* It's very possible a client will not want to move objects in the scene
|
||||
* using this method. This is intentially not pure virtual.
|
||||
*/
|
||||
virtual void update() {}
|
||||
|
||||
void loadModel(const QUrl & url) {
|
||||
void loadModel(const QUrl & url)
|
||||
{
|
||||
auto fileName = url.fileName().replace(".obj", "").toStdString();
|
||||
auto filePath = url.toLocalFile().toStdString();
|
||||
loadModel(fileName, filePath);
|
||||
}
|
||||
|
||||
void loadModel(const std::string & name, const std::string & path) {
|
||||
void loadModel(const std::string & name, const std::string & path)
|
||||
{
|
||||
// Add the dropped model to the load queue.
|
||||
// This is consumed during rendering of the scene if not empty.
|
||||
mModelLoadQueue.emplace(name, path);
|
||||
@@ -111,7 +118,8 @@ namespace Qtk {
|
||||
/**
|
||||
* @return The number of objects within the scene with the given name.
|
||||
*/
|
||||
[[nodiscard]] uint64_t getObjectCount(const QString & name) {
|
||||
[[nodiscard]] uint64_t getObjectCount(const QString & name)
|
||||
{
|
||||
return mObjectCount.count(name.toStdString())
|
||||
? mObjectCount[name.toStdString()]
|
||||
: 0;
|
||||
@@ -125,14 +133,16 @@ namespace Qtk {
|
||||
/**
|
||||
* @return View matrix for the camera attached to this scene.
|
||||
*/
|
||||
[[nodiscard]] inline static QMatrix4x4 getViewMatrix() {
|
||||
[[nodiscard]] inline static QMatrix4x4 getViewMatrix()
|
||||
{
|
||||
return mCamera.toMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Projection matrix for the current view into the scene.
|
||||
*/
|
||||
[[nodiscard]] inline static QMatrix4x4 & getProjectionMatrix() {
|
||||
[[nodiscard]] inline static QMatrix4x4 & getProjectionMatrix()
|
||||
{
|
||||
return mProjection;
|
||||
}
|
||||
|
||||
@@ -150,15 +160,16 @@ namespace Qtk {
|
||||
/**
|
||||
* @return All MeshRenderers within the scene.
|
||||
*/
|
||||
[[nodiscard]] inline const std::vector<MeshRenderer *> & getMeshes()
|
||||
const {
|
||||
[[nodiscard]] inline const std::vector<MeshRenderer *> & getMeshes() const
|
||||
{
|
||||
return mMeshes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All Models within the scene.
|
||||
*/
|
||||
[[nodiscard]] inline const std::vector<Model *> & getModels() const {
|
||||
[[nodiscard]] inline const std::vector<Model *> & getModels() const
|
||||
{
|
||||
return mModels;
|
||||
}
|
||||
|
||||
@@ -239,30 +250,6 @@ namespace Qtk {
|
||||
/* Track count of objects with same initial name. */
|
||||
std::unordered_map<std::string, uint64_t> mObjectCount;
|
||||
};
|
||||
|
||||
class SceneEmpty : public Scene {
|
||||
public:
|
||||
void init() override { setSceneName("Empty Scene"); }
|
||||
|
||||
void draw() override { Scene::draw(); }
|
||||
|
||||
void update() override { Scene::update(); }
|
||||
};
|
||||
|
||||
class SceneInterface : public Scene {
|
||||
public:
|
||||
explicit SceneInterface(Scene * scene) : mScene(scene) {}
|
||||
|
||||
void init() override { mScene->init(); }
|
||||
|
||||
void draw() override { mScene->draw(); }
|
||||
|
||||
void update() override { mScene->update(); }
|
||||
|
||||
protected:
|
||||
Scene * mScene;
|
||||
};
|
||||
|
||||
} // namespace Qtk
|
||||
|
||||
#endif // QTK_SCENE_H
|
||||
|
||||
120
src/qtk/shaders.h
Normal file
120
src/qtk/shaders.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*##############################################################################
|
||||
## Author: Shaun Reed ##
|
||||
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
|
||||
## About: Default GLSL shaders to use for objects if no shader if provided. ##
|
||||
## ##
|
||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||
##############################################################################*/
|
||||
#ifndef QTK_SHADERS_H
|
||||
#define QTK_SHADERS_H
|
||||
|
||||
//
|
||||
// Model
|
||||
|
||||
#define QTK_SHADER_VERTEX_MODEL \
|
||||
R"(
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPosition;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
layout (location = 2) in vec2 aTextureCoord;
|
||||
|
||||
out vec2 vTextureCoord;
|
||||
|
||||
uniform mat4 uModel;
|
||||
uniform mat4 uView;
|
||||
uniform mat4 uProjection;
|
||||
|
||||
void main()
|
||||
{
|
||||
vTextureCoord = aTextureCoord;
|
||||
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
|
||||
}
|
||||
)"
|
||||
|
||||
#define QTK_SHADER_FRAGMENT_MODEL \
|
||||
R"(
|
||||
#version 330 core
|
||||
out vec4 fColor;
|
||||
|
||||
in vec2 vTextureCoord;
|
||||
|
||||
uniform sampler2D texture_diffuse1;
|
||||
|
||||
void main()
|
||||
{
|
||||
fColor = texture(texture_diffuse1, vTextureCoord);
|
||||
}
|
||||
)"
|
||||
|
||||
//
|
||||
// MeshRenderer
|
||||
|
||||
#define QTK_SHADER_VERTEX_MESH \
|
||||
R"(
|
||||
#version 330
|
||||
layout(location = 0) in vec3 aPosition;
|
||||
layout(location = 1) in vec3 aColor;
|
||||
|
||||
out vec4 vColor;
|
||||
|
||||
uniform mat4 uModel; // Model
|
||||
uniform mat4 uView; // View
|
||||
uniform mat4 uProjection; // Projection
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
|
||||
|
||||
vColor = vec4(aColor, 1.0f);
|
||||
}
|
||||
)"
|
||||
|
||||
#define QTK_SHADER_FRAGMENT_MESH \
|
||||
R"(
|
||||
#version 330
|
||||
in vec4 vColor;
|
||||
|
||||
out vec4 fColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
fColor = vColor;
|
||||
}
|
||||
)"
|
||||
|
||||
//
|
||||
// Skybox
|
||||
|
||||
#define QTK_SHADER_VERTEX_SKYBOX \
|
||||
R"(
|
||||
#version 330
|
||||
layout(location = 0) in vec3 aPosition;
|
||||
|
||||
out vec3 vTexCoord;
|
||||
|
||||
uniform mat4 uProjectionMatrix;
|
||||
uniform mat4 uViewMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
// Strip translation column from camera's 4x4 matrix
|
||||
mat4 view = mat4(mat3(uViewMatrix));
|
||||
gl_Position = uProjectionMatrix * view * vec4(aPosition, 1.0);
|
||||
vTexCoord = aPosition;
|
||||
}
|
||||
)"
|
||||
|
||||
#define QTK_SHADER_FRAGMENT_SKYBOX \
|
||||
R"(
|
||||
#version 330
|
||||
uniform samplerCube uTexture;
|
||||
|
||||
varying vec3 vTexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture(uTexture, vTexCoord);
|
||||
}
|
||||
)"
|
||||
|
||||
#endif // QTK_SHADERS_H
|
||||
@@ -10,9 +10,10 @@
|
||||
|
||||
using namespace Qtk;
|
||||
|
||||
Cube::Cube(DrawMode mode) {
|
||||
Cube::Cube(DrawMode mode)
|
||||
{
|
||||
mDrawMode = mode;
|
||||
switch(mode) {
|
||||
switch (mode) {
|
||||
// The order of the following assignment values helps to visualize.
|
||||
// clang-format off
|
||||
|
||||
@@ -203,10 +204,11 @@ Cube::Cube(DrawMode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
Triangle::Triangle(DrawMode mode) {
|
||||
Triangle::Triangle(DrawMode mode)
|
||||
{
|
||||
mDrawMode = mode;
|
||||
const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f);
|
||||
switch(mode) {
|
||||
switch (mode) {
|
||||
// clang-format off
|
||||
|
||||
case QTK_DRAW_ARRAYS:
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
#define UV_RIGHT QVector2D(0.0f, 1.0f)
|
||||
#define UV_CORNER QVector2D(1.0f, 1.0f)
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
class MeshRenderer;
|
||||
|
||||
class Object;
|
||||
@@ -114,13 +115,17 @@ namespace Qtk {
|
||||
* @param t Texture coordinates for this shape.
|
||||
* @param n Normals for this shape.
|
||||
*/
|
||||
explicit ShapeBase(
|
||||
DrawMode mode = QTK_DRAW_ARRAYS, Vertices v = {}, Indices i = {},
|
||||
Colors c = {}, TexCoords t = {}, Normals n = {}) :
|
||||
mDrawMode(mode),
|
||||
mVertices(std::move(v)), mColors(std::move(c)),
|
||||
explicit ShapeBase(DrawMode mode = QTK_DRAW_ARRAYS,
|
||||
Vertices v = {},
|
||||
Indices i = {},
|
||||
Colors c = {},
|
||||
TexCoords t = {},
|
||||
Normals n = {}) :
|
||||
mDrawMode(mode), mVertices(std::move(v)), mColors(std::move(c)),
|
||||
mIndices(std::move(i)), mTexCoords(std::move(t)),
|
||||
mNormals(std::move(n)) {}
|
||||
mNormals(std::move(n))
|
||||
{
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Accessors
|
||||
@@ -129,14 +134,16 @@ namespace Qtk {
|
||||
/**
|
||||
* @return Vertex data for this shape.
|
||||
*/
|
||||
[[nodiscard]] inline const Vertices & getVertices() const {
|
||||
[[nodiscard]] inline const Vertices & getVertices() const
|
||||
{
|
||||
return mVertices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Index data for this shape.
|
||||
*/
|
||||
[[nodiscard]] inline const Indices & getIndexData() const {
|
||||
[[nodiscard]] inline const Indices & getIndexData() const
|
||||
{
|
||||
return mIndices;
|
||||
}
|
||||
|
||||
@@ -148,21 +155,24 @@ namespace Qtk {
|
||||
/**
|
||||
* @return Texture coordinates for this shape.
|
||||
*/
|
||||
[[nodiscard]] inline const TexCoords & getTexCoords() const {
|
||||
[[nodiscard]] inline const TexCoords & getTexCoords() const
|
||||
{
|
||||
return mTexCoords;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Normals for this shape.
|
||||
*/
|
||||
[[nodiscard]] inline const Normals & getNormals() const {
|
||||
[[nodiscard]] inline const Normals & getNormals() const
|
||||
{
|
||||
return mNormals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Stride for texture coordinates on this shape.
|
||||
*/
|
||||
[[nodiscard]] inline size_t getTexCoordsStride() const {
|
||||
[[nodiscard]] inline size_t getTexCoordsStride() const
|
||||
{
|
||||
return mTexCoords.size() * sizeof(mTexCoords[0]);
|
||||
}
|
||||
|
||||
@@ -204,14 +214,16 @@ namespace Qtk {
|
||||
/**
|
||||
* @param value Vertex data to use for this shape.
|
||||
*/
|
||||
virtual inline void setVertices(const Vertices & value) {
|
||||
virtual inline void setVertices(const Vertices & value)
|
||||
{
|
||||
mVertices = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value Index data to use for this shape.
|
||||
*/
|
||||
virtual inline void setIndices(const Indices & value) {
|
||||
virtual inline void setIndices(const Indices & value)
|
||||
{
|
||||
mIndices = value;
|
||||
}
|
||||
|
||||
@@ -223,14 +235,16 @@ namespace Qtk {
|
||||
/**
|
||||
* @param value Texture coordinates to use for this shape.
|
||||
*/
|
||||
virtual inline void setTexCoords(const TexCoords & value) {
|
||||
virtual inline void setTexCoords(const TexCoords & value)
|
||||
{
|
||||
mTexCoords = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value Normals to use for this shape.
|
||||
*/
|
||||
virtual inline void setNormals(const Normals & value) {
|
||||
virtual inline void setNormals(const Normals & value)
|
||||
{
|
||||
mNormals = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "skybox.h"
|
||||
#include "scene.h"
|
||||
#include "shaders.h"
|
||||
#include "texture.h"
|
||||
|
||||
using namespace Qtk;
|
||||
@@ -17,42 +18,62 @@ using namespace Qtk;
|
||||
******************************************************************************/
|
||||
|
||||
Skybox::Skybox(const std::string & name) :
|
||||
Skybox(
|
||||
":/textures/skybox/right.png", ":/textures/skybox/top.png",
|
||||
":/textures/skybox/front.png", ":/textures/skybox/left.png",
|
||||
":/textures/skybox/bottom.png", ":/textures/skybox/back.png", name) {}
|
||||
|
||||
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) {
|
||||
mTexture.setTexture(cubeMap);
|
||||
mVBO(QOpenGLBuffer::VertexBuffer),
|
||||
mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
|
||||
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData())
|
||||
{
|
||||
QImage image({1024, 1024}, QImage::Format_RGBA8888);
|
||||
image.fill(Qt::darkGray);
|
||||
mTexture.setCubeMap(image, image, image, image, image, image);
|
||||
init();
|
||||
}
|
||||
|
||||
Skybox::Skybox(
|
||||
const std::string & right, const std::string & top,
|
||||
const std::string & front, const std::string & left,
|
||||
const std::string & bottom, const std::string & back,
|
||||
const std::string & name) :
|
||||
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) :
|
||||
mVBO(QOpenGLBuffer::VertexBuffer),
|
||||
mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
|
||||
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData()) {
|
||||
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData())
|
||||
{
|
||||
if (cubeMap == Q_NULLPTR) {
|
||||
qDebug()
|
||||
<< "[Qtk] Failed to set cubemap for skybox with null QOpenGLTexture.";
|
||||
} else {
|
||||
mTexture.setTexture(cubeMap);
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
Skybox::Skybox(const std::string & right,
|
||||
const std::string & top,
|
||||
const std::string & front,
|
||||
const std::string & left,
|
||||
const std::string & bottom,
|
||||
const std::string & back,
|
||||
const std::string & name) :
|
||||
mVBO(QOpenGLBuffer::VertexBuffer),
|
||||
mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
|
||||
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData())
|
||||
{
|
||||
mTexture.setCubeMap(QImage(right.c_str()).mirrored(),
|
||||
QImage(top.c_str()),
|
||||
QImage(front.c_str()),
|
||||
QImage(left.c_str()),
|
||||
QImage(bottom.c_str()),
|
||||
QImage(back.c_str()));
|
||||
init();
|
||||
mTexture.setCubeMap(
|
||||
QImage(right.c_str()).mirrored(), QImage(top.c_str()),
|
||||
QImage(front.c_str()), QImage(left.c_str()), QImage(bottom.c_str()),
|
||||
QImage(back.c_str()));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Public Member Functions
|
||||
******************************************************************************/
|
||||
|
||||
void Skybox::draw() {
|
||||
void Skybox::draw()
|
||||
{
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
mVAO.bind();
|
||||
mProgram.bind();
|
||||
mTexture.getOpenGLTexture().bind();
|
||||
mTexture.bind();
|
||||
|
||||
mProgram.setUniformValue("uProjectionMatrix", Scene::getProjectionMatrix());
|
||||
mProgram.setUniformValue("uViewMatrix", Scene::getCamera().toMatrix());
|
||||
@@ -60,7 +81,7 @@ void Skybox::draw() {
|
||||
glDrawElements(
|
||||
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
|
||||
|
||||
mTexture.getOpenGLTexture().bind();
|
||||
mTexture.bind();
|
||||
mProgram.release();
|
||||
mVAO.release();
|
||||
|
||||
@@ -73,15 +94,16 @@ void Skybox::draw() {
|
||||
* Private Member Functions
|
||||
******************************************************************************/
|
||||
|
||||
void Skybox::init() {
|
||||
void Skybox::init()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
// Set up shader program
|
||||
mProgram.create();
|
||||
mProgram.addShaderFromSourceFile(
|
||||
QOpenGLShader::Vertex, ":/shaders/skybox.vert");
|
||||
mProgram.addShaderFromSourceFile(
|
||||
QOpenGLShader::Fragment, ":/shaders/skybox.frag");
|
||||
mProgram.addShaderFromSourceCode(QOpenGLShader::Fragment,
|
||||
QTK_SHADER_FRAGMENT_SKYBOX);
|
||||
mProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,
|
||||
QTK_SHADER_VERTEX_SKYBOX);
|
||||
mProgram.link();
|
||||
mProgram.bind();
|
||||
|
||||
|
||||
@@ -20,22 +20,22 @@
|
||||
#include "shape.h"
|
||||
#include "texture.h"
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
/**
|
||||
* Skybox object for rendering a skybox within a Scene.
|
||||
* A skybox is typically implemented using a cube map texture centered around
|
||||
* the camera and projected outwards in all directions.
|
||||
*/
|
||||
class QTKAPI Skybox : protected QOpenGLFunctions {
|
||||
class QTKAPI Skybox : protected QOpenGLFunctions
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Constructors / Destructors
|
||||
************************************************************************/
|
||||
|
||||
// Delegate this constructor to use default skybox images
|
||||
|
||||
/**
|
||||
* Construct Skybox using default images.
|
||||
* Construct a skybox with a default texture.
|
||||
*
|
||||
* @param name The objectName to use for the Skybox.
|
||||
*/
|
||||
@@ -48,8 +48,8 @@ namespace Qtk {
|
||||
* @param cubeMap QOpenGLTexture to use for the new Skybox.
|
||||
* @param name The objectName to use for the Skybox.
|
||||
*/
|
||||
explicit Skybox(
|
||||
QOpenGLTexture * cubeMap, const std::string & name = "Skybox");
|
||||
explicit Skybox(QOpenGLTexture * cubeMap,
|
||||
const std::string & name = "Skybox");
|
||||
|
||||
/**
|
||||
* Construct a Skybox.
|
||||
@@ -62,11 +62,13 @@ namespace Qtk {
|
||||
* @param back Image to use for the back side of the Skybox.
|
||||
* @param name The objectName to use for this Skybox.
|
||||
*/
|
||||
Skybox(
|
||||
const std::string & right, const std::string & top,
|
||||
const std::string & front, const std::string & left,
|
||||
const std::string & bottom, const std::string & back,
|
||||
const std::string & name = "Skybox");
|
||||
Skybox(const std::string & right,
|
||||
const std::string & top,
|
||||
const std::string & front,
|
||||
const std::string & left,
|
||||
const std::string & bottom,
|
||||
const std::string & back,
|
||||
const std::string & name = "Skybox");
|
||||
|
||||
~Skybox() = default;
|
||||
|
||||
|
||||
@@ -8,52 +8,87 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QImageReader>
|
||||
#include <QPainter>
|
||||
|
||||
#include "app/qtkmainwindow.h"
|
||||
#include "texture.h"
|
||||
|
||||
using namespace Qtk;
|
||||
|
||||
QImage OpenGLTextureFactory::initImage(
|
||||
const char * image, bool flipX, bool flipY) {
|
||||
QImage OpenGLTextureFactory::initImage(const char * image,
|
||||
bool flipX,
|
||||
bool flipY)
|
||||
{
|
||||
// Qt6 limits loaded images to 256MB by default
|
||||
QImageReader::setAllocationLimit(1024);
|
||||
auto loadedImage = QImage(image).mirrored(flipX, flipY);
|
||||
if(loadedImage.isNull()) {
|
||||
if (loadedImage.isNull()) {
|
||||
return defaultTexture();
|
||||
}
|
||||
|
||||
return loadedImage;
|
||||
}
|
||||
|
||||
QOpenGLTexture * OpenGLTextureFactory::initTexture(
|
||||
const char * texture, bool flipX, bool flipY) {
|
||||
QOpenGLTexture * OpenGLTextureFactory::initTexture(const char * texture,
|
||||
bool flipX,
|
||||
bool flipY)
|
||||
{
|
||||
QImage image = initImage(texture, flipX, flipY);
|
||||
auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
newTexture->setData(image);
|
||||
newTexture->setWrapMode(QOpenGLTexture::Repeat);
|
||||
newTexture->setMinMagFilters(
|
||||
QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
|
||||
newTexture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,
|
||||
QOpenGLTexture::Linear);
|
||||
return newTexture;
|
||||
}
|
||||
|
||||
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * tile) {
|
||||
return initCubeMap(
|
||||
QImage(tile), QImage(tile), QImage(tile), QImage(tile), QImage(tile),
|
||||
QImage(tile));
|
||||
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * tile)
|
||||
{
|
||||
return initCubeMap(QImage(tile),
|
||||
QImage(tile),
|
||||
QImage(tile),
|
||||
QImage(tile),
|
||||
QImage(tile),
|
||||
QImage(tile));
|
||||
}
|
||||
|
||||
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
|
||||
const char * right, const char * top, const char * front, const char * left,
|
||||
const char * bottom, const char * back) {
|
||||
return initCubeMap(
|
||||
QImage(right), QImage(top), QImage(front), QImage(left), QImage(bottom),
|
||||
QImage(back));
|
||||
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * right,
|
||||
const char * top,
|
||||
const char * front,
|
||||
const char * left,
|
||||
const char * bottom,
|
||||
const char * back)
|
||||
{
|
||||
return initCubeMap(QImage(right),
|
||||
QImage(top),
|
||||
QImage(front),
|
||||
QImage(left),
|
||||
QImage(bottom),
|
||||
QImage(back));
|
||||
}
|
||||
|
||||
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
|
||||
const QImage & right, const QImage & top, const QImage & front,
|
||||
const QImage & left, const QImage & bottom, const QImage & back) {
|
||||
QImage OpenGLTextureFactory::defaultTexture()
|
||||
{
|
||||
QImage image({256, 256}, QImage::Format_RGBA8888);
|
||||
image.fill(Qt::lightGray);
|
||||
|
||||
// Draw a red '?' to the center of the image.
|
||||
QPainter painter(&image);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setPen(Qt::red);
|
||||
painter.setFont({"Helvetica", 100, QFont::Bold});
|
||||
constexpr QRect rect(0, 0, 256, 256);
|
||||
painter.drawText(rect, Qt::AlignCenter, "?");
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const QImage & right,
|
||||
const QImage & top,
|
||||
const QImage & front,
|
||||
const QImage & left,
|
||||
const QImage & bottom,
|
||||
const QImage & back)
|
||||
{
|
||||
auto texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
|
||||
std::vector<QImage> faceTextures = {right, top, front, left, bottom, back};
|
||||
// Initialize skybox cubemap texture
|
||||
@@ -61,20 +96,23 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
|
||||
texture->bind();
|
||||
// For each cube map face
|
||||
std::vector<QOpenGLTexture::CubeMapFace> faces = {
|
||||
QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::CubeMapPositiveY,
|
||||
QOpenGLTexture::CubeMapPositiveZ, QOpenGLTexture::CubeMapNegativeX,
|
||||
QOpenGLTexture::CubeMapNegativeY, QOpenGLTexture::CubeMapNegativeZ};
|
||||
QOpenGLTexture::CubeMapPositiveX,
|
||||
QOpenGLTexture::CubeMapPositiveY,
|
||||
QOpenGLTexture::CubeMapPositiveZ,
|
||||
QOpenGLTexture::CubeMapNegativeX,
|
||||
QOpenGLTexture::CubeMapNegativeY,
|
||||
QOpenGLTexture::CubeMapNegativeZ};
|
||||
int i = 0;
|
||||
for(const auto & face : faces) {
|
||||
QImage faceImage(faceTextures[i]);
|
||||
if(faceImage.isNull()) {
|
||||
qDebug() << "Error loading cube map image\n";
|
||||
for (const auto & face : faces) {
|
||||
QImage & faceImage = faceTextures[i];
|
||||
if (faceImage.isNull()) {
|
||||
qDebug() << "[libqtk] Error loading cube map image\n";
|
||||
faceImage = defaultTexture();
|
||||
}
|
||||
faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888);
|
||||
|
||||
// On the first iteration, set format and allocate texture storage
|
||||
if(face == QOpenGLTexture::CubeMapPositiveX) {
|
||||
if (face == QOpenGLTexture::CubeMapPositiveX) {
|
||||
// This also needs to happen on the first iteration, anyways
|
||||
texture->setSize(
|
||||
faceImage.width(), faceImage.height(), faceImage.depth());
|
||||
@@ -82,9 +120,12 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
|
||||
texture->allocateStorage();
|
||||
}
|
||||
|
||||
texture->setData(
|
||||
0, 0, face, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
|
||||
faceImage.constBits());
|
||||
texture->setData(0,
|
||||
0,
|
||||
face,
|
||||
QOpenGLTexture::RGBA,
|
||||
QOpenGLTexture::UInt8,
|
||||
faceImage.constBits());
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,30 +16,33 @@
|
||||
|
||||
#include "qtkapi.h"
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
/**
|
||||
* Binds shader programs until the end of scope.
|
||||
* Does nothing if the shader program was already bound.
|
||||
*
|
||||
* See MeshRenderer::setUniform() for example.
|
||||
*/
|
||||
class QTKAPI ShaderBindScope {
|
||||
class QTKAPI ShaderBindScope
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Constructors / Destructors
|
||||
************************************************************************/
|
||||
|
||||
explicit ShaderBindScope(
|
||||
QOpenGLShaderProgram * program, bool was_locked) :
|
||||
mWasBound(was_locked) {
|
||||
explicit ShaderBindScope(QOpenGLShaderProgram * program,
|
||||
bool was_locked) : mWasBound(was_locked)
|
||||
{
|
||||
mProgram = program;
|
||||
if(!mWasBound) {
|
||||
if (!mWasBound) {
|
||||
mProgram->bind();
|
||||
}
|
||||
}
|
||||
|
||||
~ShaderBindScope() {
|
||||
if(!mWasBound) {
|
||||
~ShaderBindScope()
|
||||
{
|
||||
if (!mWasBound) {
|
||||
mProgram->release();
|
||||
}
|
||||
}
|
||||
@@ -56,7 +59,8 @@ namespace Qtk {
|
||||
/**
|
||||
* Factories for initializing various OpenGL textures
|
||||
*/
|
||||
class QTKAPI OpenGLTextureFactory {
|
||||
class QTKAPI OpenGLTextureFactory
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Constructors / Destructors
|
||||
@@ -76,8 +80,9 @@ namespace Qtk {
|
||||
* @param flipY If true the image will be flipped on Y axis.
|
||||
* @return QImage object.
|
||||
*/
|
||||
static QImage initImage(
|
||||
const char * image, bool flipX = false, bool flipY = false);
|
||||
static QImage initImage(const char * image,
|
||||
bool flipX = false,
|
||||
bool flipY = false);
|
||||
|
||||
/**
|
||||
* QOpenGLTexture factory
|
||||
@@ -88,8 +93,9 @@ namespace Qtk {
|
||||
* @param flipY If true the image will be flipped on Y axis.
|
||||
* @return Pointer to an initialized QOpenGLTexture object.
|
||||
*/
|
||||
static QOpenGLTexture * initTexture(
|
||||
const char * texture, bool flipX = false, bool flipY = false);
|
||||
static QOpenGLTexture * initTexture(const char * texture,
|
||||
bool flipX = false,
|
||||
bool flipY = false);
|
||||
|
||||
/**
|
||||
* Cube map factory for initializing all sides of a CubeMap.
|
||||
@@ -103,9 +109,12 @@ namespace Qtk {
|
||||
* @param back Path to image for the back side of the CubeMap.
|
||||
* @return Pointer to an initialized QOpenGLTexture object.
|
||||
*/
|
||||
static QOpenGLTexture * initCubeMap(
|
||||
const QImage & right, const QImage & top, const QImage & front,
|
||||
const QImage & left, const QImage & bottom, const QImage & back);
|
||||
static QOpenGLTexture * initCubeMap(const QImage & right,
|
||||
const QImage & top,
|
||||
const QImage & front,
|
||||
const QImage & left,
|
||||
const QImage & bottom,
|
||||
const QImage & back);
|
||||
|
||||
/**
|
||||
* CubeMap factory for tiling the same image on all sides.
|
||||
@@ -128,17 +137,15 @@ namespace Qtk {
|
||||
* @param back Path to image for the back side of the CubeMap.
|
||||
* @return Pointer to an initialized QOpenGLTexture object.
|
||||
*/
|
||||
static QOpenGLTexture * initCubeMap(
|
||||
const char * right, const char * top, const char * front,
|
||||
const char * left, const char * bottom, const char * back);
|
||||
static QOpenGLTexture * initCubeMap(const char * right,
|
||||
const char * top,
|
||||
const char * front,
|
||||
const char * left,
|
||||
const char * bottom,
|
||||
const char * back);
|
||||
|
||||
/// The texture used in place of a missing texture.
|
||||
static QImage defaultTexture() {
|
||||
// Use plaster for default texture if image fails to load.
|
||||
// This prevents segfaults when loading a texture that doesn't exist.
|
||||
// TODO: Replace with a '?' texture to indicate missing texture.
|
||||
return QImage(":/textures/plaster.png");
|
||||
}
|
||||
static QImage defaultTexture();
|
||||
|
||||
private:
|
||||
// Private ctor to prevent creating instances of this class
|
||||
@@ -151,7 +158,8 @@ namespace Qtk {
|
||||
* TODO: Struct for (re)storing texture state
|
||||
* A struct to store flipX, flipY and other initial state needed to copy
|
||||
*/
|
||||
class Texture {
|
||||
class Texture
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Typedefs
|
||||
@@ -169,7 +177,8 @@ namespace Qtk {
|
||||
*
|
||||
* @param value Texture to copy.
|
||||
*/
|
||||
Texture(const Texture & value) {
|
||||
Texture(const Texture & value)
|
||||
{
|
||||
mOpenGLTexture = OpenGLTextureFactory::initTexture(value.mPath);
|
||||
mPath = value.mPath;
|
||||
}
|
||||
@@ -179,10 +188,13 @@ namespace Qtk {
|
||||
* @param flipX True if texture is to be flipped on the X axis.
|
||||
* @param flipY True if texture is to be flipped on the Y axis.
|
||||
*/
|
||||
explicit Texture(
|
||||
const char * path, bool flipX = false, bool flipY = false) :
|
||||
explicit Texture(const char * path,
|
||||
bool flipX = false,
|
||||
bool flipY = false) :
|
||||
mOpenGLTexture(OpenGLTextureFactory::initTexture(path, flipX, flipY)),
|
||||
mPath(path) {}
|
||||
mPath(path)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a Texture using an existing QOpenGLTexture.
|
||||
@@ -200,10 +212,25 @@ namespace Qtk {
|
||||
/**
|
||||
* @return True if the OpenGL texture has been initialized.
|
||||
*/
|
||||
[[nodiscard]] inline bool hasTexture() const {
|
||||
[[nodiscard]] inline bool hasTexture() const
|
||||
{
|
||||
return mOpenGLTexture != Q_NULLPTR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the OpenGL texture if it exists, avoiding segmentation faults.
|
||||
*/
|
||||
bool bind() const
|
||||
{
|
||||
if (hasTexture()) {
|
||||
// TODO: It would be nice to warn here but some objects may not have
|
||||
// a texture. Factor Texture out of those objects so we don't bind.
|
||||
mOpenGLTexture->bind();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Accessors
|
||||
************************************************************************/
|
||||
@@ -211,7 +238,8 @@ namespace Qtk {
|
||||
/**
|
||||
* @return QOpenGLTexture associated with this Texture.
|
||||
*/
|
||||
[[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const {
|
||||
[[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const
|
||||
{
|
||||
return *mOpenGLTexture;
|
||||
}
|
||||
|
||||
@@ -231,8 +259,10 @@ namespace Qtk {
|
||||
* @param flipX True if texture is to be flipped on the X axis.
|
||||
* @param flipY True if texture is to be flipped on the Y axis.
|
||||
*/
|
||||
inline void setTexture(
|
||||
const std::string & path, bool flipX = false, bool flipY = false) {
|
||||
inline void setTexture(const std::string & path,
|
||||
bool flipX = false,
|
||||
bool flipY = false)
|
||||
{
|
||||
setTexture(path.c_str(), flipX, flipY);
|
||||
}
|
||||
|
||||
@@ -241,8 +271,10 @@ namespace Qtk {
|
||||
* @param flipX True if texture is to be flipped on the X axis.
|
||||
* @param flipY True if texture is to be flipped on the Y axis.
|
||||
*/
|
||||
inline void setTexture(
|
||||
const char * path, bool flipX = false, bool flipY = false) {
|
||||
inline void setTexture(const char * path,
|
||||
bool flipX = false,
|
||||
bool flipY = false)
|
||||
{
|
||||
mOpenGLTexture = OpenGLTextureFactory::initTexture(path, flipX, flipY);
|
||||
mPath = path;
|
||||
}
|
||||
@@ -252,7 +284,8 @@ namespace Qtk {
|
||||
*
|
||||
* @param path Path to texture to use for all sides of the cube map.
|
||||
*/
|
||||
virtual inline void setCubeMap(const char * path) {
|
||||
virtual inline void setCubeMap(const char * path)
|
||||
{
|
||||
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(path);
|
||||
mPath = path;
|
||||
}
|
||||
@@ -267,9 +300,13 @@ namespace Qtk {
|
||||
* @param bottom Path to texture to use for bottom cube map side.
|
||||
* @param back Path to texture to use for back cube map side.
|
||||
*/
|
||||
virtual inline void setCubeMap(
|
||||
const char * right, const char * top, const char * front,
|
||||
const char * left, const char * bottom, const char * back) {
|
||||
virtual inline void setCubeMap(const char * right,
|
||||
const char * top,
|
||||
const char * front,
|
||||
const char * left,
|
||||
const char * bottom,
|
||||
const char * back)
|
||||
{
|
||||
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(
|
||||
right, top, front, left, bottom, back);
|
||||
}
|
||||
@@ -277,16 +314,20 @@ namespace Qtk {
|
||||
/**
|
||||
* Sets this Texture to be a cube map with provided sides.
|
||||
*
|
||||
* @param right Path to texture to use for right cube map side.
|
||||
* @param top Path to texture to use for top cube map side.
|
||||
* @param front Path to texture to use for front cube map side.
|
||||
* @param left Path to texture to use for left cube map side.
|
||||
* @param bottom Path to texture to use for bottom cube map side.
|
||||
* @param back Path to texture to use for back cube map side.
|
||||
* @param right QImage texture to use for right cube map side.
|
||||
* @param top QImage texture to use for top cube map side.
|
||||
* @param front QImage texture to use for front cube map side.
|
||||
* @param left QImage texture to use for left cube map side.
|
||||
* @param bottom QImage texture to use for bottom cube map side.
|
||||
* @param back QImage texture to use for back cube map side.
|
||||
*/
|
||||
virtual inline void setCubeMap(
|
||||
const QImage & right, const QImage & top, const QImage & front,
|
||||
const QImage & left, const QImage & bottom, const QImage & back) {
|
||||
virtual inline void setCubeMap(const QImage & right,
|
||||
const QImage & top,
|
||||
const QImage & front,
|
||||
const QImage & left,
|
||||
const QImage & bottom,
|
||||
const QImage & back)
|
||||
{
|
||||
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(
|
||||
right, top, front, left, bottom, back);
|
||||
}
|
||||
@@ -299,7 +340,8 @@ namespace Qtk {
|
||||
/**
|
||||
* @param texture QOpenGLTexture to use for this Texture.
|
||||
*/
|
||||
inline void setTexture(QOpenGLTexture * texture) {
|
||||
inline void setTexture(QOpenGLTexture * texture)
|
||||
{
|
||||
mOpenGLTexture = texture;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,44 +19,52 @@ const QVector3D Transform3D::LocalRight(1.0f, 0.0f, 0.0f);
|
||||
* Public Methods
|
||||
******************************************************************************/
|
||||
|
||||
void Transform3D::translate(const QVector3D & dt) {
|
||||
void Transform3D::translate(const QVector3D & dt)
|
||||
{
|
||||
m_dirty = true;
|
||||
mTranslation += dt;
|
||||
}
|
||||
|
||||
void Transform3D::scale(const QVector3D & ds) {
|
||||
void Transform3D::scale(const QVector3D & ds)
|
||||
{
|
||||
m_dirty = true;
|
||||
mScale *= ds;
|
||||
}
|
||||
|
||||
|
||||
void Transform3D::grow(const QVector3D & ds) {
|
||||
void Transform3D::grow(const QVector3D & ds)
|
||||
{
|
||||
m_dirty = true;
|
||||
mScale += ds;
|
||||
}
|
||||
|
||||
void Transform3D::rotate(const QQuaternion & dr) {
|
||||
void Transform3D::rotate(const QQuaternion & dr)
|
||||
{
|
||||
m_dirty = true;
|
||||
mRotation = dr * mRotation;
|
||||
}
|
||||
|
||||
void Transform3D::setTranslation(const QVector3D & t) {
|
||||
void Transform3D::setTranslation(const QVector3D & t)
|
||||
{
|
||||
m_dirty = true;
|
||||
mTranslation = t;
|
||||
}
|
||||
|
||||
void Transform3D::setScale(const QVector3D & s) {
|
||||
void Transform3D::setScale(const QVector3D & s)
|
||||
{
|
||||
m_dirty = true;
|
||||
mScale = s;
|
||||
}
|
||||
|
||||
void Transform3D::setRotation(const QQuaternion & r) {
|
||||
void Transform3D::setRotation(const QQuaternion & r)
|
||||
{
|
||||
m_dirty = true;
|
||||
mRotation = r;
|
||||
}
|
||||
|
||||
const QMatrix4x4 & Transform3D::toMatrix() {
|
||||
if(m_dirty) {
|
||||
const QMatrix4x4 & Transform3D::toMatrix()
|
||||
{
|
||||
if (m_dirty) {
|
||||
m_dirty = false;
|
||||
mWorld.setToIdentity();
|
||||
mWorld.translate(mTranslation);
|
||||
@@ -66,15 +74,18 @@ const QMatrix4x4 & Transform3D::toMatrix() {
|
||||
return mWorld;
|
||||
}
|
||||
|
||||
QVector3D Transform3D::getForward() const {
|
||||
QVector3D Transform3D::getForward() const
|
||||
{
|
||||
return mRotation.rotatedVector(LocalForward);
|
||||
}
|
||||
|
||||
QVector3D Transform3D::getUp() const {
|
||||
QVector3D Transform3D::getUp() const
|
||||
{
|
||||
return mRotation.rotatedVector(LocalUp);
|
||||
}
|
||||
|
||||
QVector3D Transform3D::getRight() const {
|
||||
QVector3D Transform3D::getRight() const
|
||||
{
|
||||
return mRotation.rotatedVector(LocalRight);
|
||||
}
|
||||
|
||||
@@ -82,10 +93,12 @@ QVector3D Transform3D::getRight() const {
|
||||
* Private Methods
|
||||
******************************************************************************/
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
|
||||
QDebug operator<<(QDebug dbg, const Transform3D & transform) {
|
||||
QDebug operator<<(QDebug dbg, const Transform3D & transform)
|
||||
{
|
||||
dbg << "Transform3D\n{\n";
|
||||
dbg << "Position: <" << transform.getTranslation().x() << ", "
|
||||
<< transform.getTranslation().y() << ", "
|
||||
@@ -102,14 +115,16 @@ namespace Qtk {
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_DATASTREAM
|
||||
QDataStream & operator<<(QDataStream & out, const Transform3D & transform) {
|
||||
QDataStream & operator<<(QDataStream & out, const Transform3D & transform)
|
||||
{
|
||||
out << transform.mTranslation;
|
||||
out << transform.mScale;
|
||||
out << transform.mRotation;
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream & operator>>(QDataStream & in, Transform3D & transform) {
|
||||
QDataStream & operator>>(QDataStream & in, Transform3D & transform)
|
||||
{
|
||||
in >> transform.mTranslation;
|
||||
in >> transform.mScale;
|
||||
in >> transform.mRotation;
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
|
||||
#include "qtkapi.h"
|
||||
|
||||
namespace Qtk {
|
||||
namespace Qtk
|
||||
{
|
||||
/**
|
||||
* Transform3D class to represent and modify object position in 3D space.
|
||||
*/
|
||||
class QTKAPI Transform3D {
|
||||
class QTKAPI Transform3D
|
||||
{
|
||||
public:
|
||||
/*************************************************************************
|
||||
* Constructors, Destructors
|
||||
@@ -32,7 +34,9 @@ namespace Qtk {
|
||||
|
||||
inline Transform3D() :
|
||||
m_dirty(true), mScale(1.0f, 1.0f, 1.0f),
|
||||
mTranslation(0.0f, 0.0f, 0.0f) {}
|
||||
mTranslation(0.0f, 0.0f, 0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Public Methods
|
||||
@@ -48,7 +52,8 @@ namespace Qtk {
|
||||
* @param dy Y translation from last to current position.
|
||||
* @param dz Z translation from last to current position.
|
||||
*/
|
||||
inline void translate(float dx, float dy, float dz) {
|
||||
inline void translate(float dx, float dy, float dz)
|
||||
{
|
||||
translate(QVector3D(dx, dy, dz));
|
||||
}
|
||||
|
||||
@@ -66,7 +71,8 @@ namespace Qtk {
|
||||
* @param dy Amount to scale on the Y axis.
|
||||
* @param dz Amount to scale on the Z axis.
|
||||
*/
|
||||
inline void scale(float dx, float dy, float dz) {
|
||||
inline void scale(float dx, float dy, float dz)
|
||||
{
|
||||
scale(QVector3D(dx, dy, dz));
|
||||
}
|
||||
|
||||
@@ -75,7 +81,8 @@ namespace Qtk {
|
||||
*
|
||||
* @param factor Scalar to apply to all axis of the object.
|
||||
*/
|
||||
inline void scale(float factor) {
|
||||
inline void scale(float factor)
|
||||
{
|
||||
scale(QVector3D(factor, factor, factor));
|
||||
}
|
||||
|
||||
@@ -89,14 +96,16 @@ namespace Qtk {
|
||||
* @param dy Amount to grow Y axis.
|
||||
* @param dz Amount to grow Z axis.
|
||||
*/
|
||||
inline void grow(float dx, float dy, float dz) {
|
||||
inline void grow(float dx, float dy, float dz)
|
||||
{
|
||||
grow(QVector3D(dx, dy, dz));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param factor Amount to grow all axis equally.
|
||||
*/
|
||||
inline void grow(float factor) {
|
||||
inline void grow(float factor)
|
||||
{
|
||||
grow(QVector3D(factor, factor, factor));
|
||||
}
|
||||
|
||||
@@ -109,7 +118,8 @@ namespace Qtk {
|
||||
* @param angle Angle to rotate.
|
||||
* @param axis Axis to rotate apply the rotation on.
|
||||
*/
|
||||
inline void rotate(float angle, const QVector3D & axis) {
|
||||
inline void rotate(float angle, const QVector3D & axis)
|
||||
{
|
||||
rotate(QQuaternion::fromAxisAndAngle(axis, angle));
|
||||
}
|
||||
|
||||
@@ -121,7 +131,8 @@ namespace Qtk {
|
||||
* @param ay Y axis to apply the rotation on.
|
||||
* @param az Z axis to apply the rotation on.
|
||||
*/
|
||||
inline void rotate(float angle, float ax, float ay, float az) {
|
||||
inline void rotate(float angle, float ax, float ay, float az)
|
||||
{
|
||||
rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
|
||||
}
|
||||
|
||||
@@ -139,7 +150,8 @@ namespace Qtk {
|
||||
* @param y Y position to set transform.
|
||||
* @param z Z position to set transform.
|
||||
*/
|
||||
inline void setTranslation(float x, float y, float z) {
|
||||
inline void setTranslation(float x, float y, float z)
|
||||
{
|
||||
setTranslation(QVector3D(x, y, z));
|
||||
}
|
||||
|
||||
@@ -153,7 +165,8 @@ namespace Qtk {
|
||||
* @param y Y axis scale to set for this transform.
|
||||
* @param z Z axis scale to set for this transform.
|
||||
*/
|
||||
inline void setScale(float x, float y, float z) {
|
||||
inline void setScale(float x, float y, float z)
|
||||
{
|
||||
setScale(QVector3D(x, y, z));
|
||||
}
|
||||
|
||||
@@ -171,7 +184,8 @@ namespace Qtk {
|
||||
* @param angle Angle to set for rotation.
|
||||
* @param axis Axis to set rotation for.
|
||||
*/
|
||||
inline void setRotation(float angle, const QVector3D & axis) {
|
||||
inline void setRotation(float angle, const QVector3D & axis)
|
||||
{
|
||||
setRotation(QQuaternion::fromAxisAndAngle(axis, angle));
|
||||
}
|
||||
|
||||
@@ -183,7 +197,8 @@ namespace Qtk {
|
||||
* @param ay Y axis to set angle for.
|
||||
* @param az Z axis to set angle for.
|
||||
*/
|
||||
inline void setRotation(float angle, float ax, float ay, float az) {
|
||||
inline void setRotation(float angle, float ax, float ay, float az)
|
||||
{
|
||||
setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
|
||||
}
|
||||
|
||||
@@ -194,7 +209,8 @@ namespace Qtk {
|
||||
/**
|
||||
* @return Translation for this transform.
|
||||
*/
|
||||
[[nodiscard]] inline const QVector3D & getTranslation() const {
|
||||
[[nodiscard]] inline const QVector3D & getTranslation() const
|
||||
{
|
||||
return mTranslation;
|
||||
}
|
||||
|
||||
@@ -206,7 +222,8 @@ namespace Qtk {
|
||||
/**
|
||||
* @return Rotation for this transform.
|
||||
*/
|
||||
[[nodiscard]] inline const QQuaternion & getRotation() const {
|
||||
[[nodiscard]] inline const QQuaternion & getRotation() const
|
||||
{
|
||||
return mRotation;
|
||||
}
|
||||
|
||||
@@ -250,10 +267,10 @@ namespace Qtk {
|
||||
bool m_dirty;
|
||||
|
||||
#ifndef QT_NO_DATASTREAM
|
||||
friend QDataStream & operator<<(
|
||||
QDataStream & out, const Transform3D & transform);
|
||||
friend QDataStream & operator>>(
|
||||
QDataStream & in, Transform3D & transform);
|
||||
friend QDataStream & operator<<(QDataStream & out,
|
||||
const Transform3D & transform);
|
||||
friend QDataStream & operator>>(QDataStream & in,
|
||||
Transform3D & transform);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user