Improve cmake and GUI (#13)

+ Packaging and CI for Windows, Mac, Linux
+ Debian package, NSIS Windows installer, OSX appbundle
+ Example application using libqtk
+ Component installation for `qtk`, `libqtk`, or `collection` with cmake
This commit is contained in:
Shaun Reed 2023-03-12 02:02:26 +00:00
parent a04ebae42a
commit e889785b65
100 changed files with 5585 additions and 88181 deletions

View File

@ -7,17 +7,19 @@ on:
jobs:
Build-Qtk:
env:
CONFIG: -DQTK_UPDATE_SUBMODULES=ON -DQTK_DEBUG=OFF -DQTK_ENABLE_CCACHE=OFF -DQTK_BUILD_GUI=ON -DQTK_INSTALL_LIBRARY=ON -DQTK_INSTALL_PLUGINS=OFF
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH="/home/runner/work/qtk/Qt/6.3.1/gcc_64/"
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH="D:/a/qtk/qtk/Qt/6.3.1/mingw81_64/"
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/6.5.0/mingw81_64/ $CONFIG
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH="/home/runner/work/qtk/Qt/6.3.1/gcc_64/"
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
runs-on: ${{ matrix.os }}
steps:
@ -26,19 +28,295 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.3.1'
version: '6.5.0'
# Windows
- name: Chocolatey Action
if: matrix.os == 'windows-latest'
uses: crazy-max/ghaction-chocolatey@v2.0.0
uses: crazy-max/ghaction-chocolatey@v2
with:
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1
- name: Build Qtk
- name: Install Debian packaging dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt update -y
sudo apt install libxcb-cursor0 -y
- name: Configure Qtk Application (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: cmake -B build/ ${{ matrix.cmake }}
- name: Build Qtk Application (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: cmake --build build/ --config Release
# OSX / Linux
- name: Configure Qtk Application (OSX / Linux)
if: matrix.os != 'windows-latest'
shell: bash
run: cmake -B build/ ${{ matrix.cmake }}
- name: Build Qtk Application (OSX / Linux)
if: matrix.os != 'windows-latest'
shell: bash
run: cmake --build build/ --config Release --target qtk_app -- -j $(nproc)
# Packaging
- name: Install Qtk Application
shell: bash
run: cmake --install build/ --config Release --prefix=$(pwd)/install --component qtk
- name: Package Qtk Application
shell: bash
run: cmake --build build/ --target package --config Release
- name: Package Qtk (DEB)
if: matrix.os == 'ubuntu-latest'
shell: bash
run: |
cmake -S . -B build/ ${{ matrix.cmake }} && cmake --build build/ \
--target qtk-main
cd build
cpack -C Release -G DEB
- name: Upload package artifacts (DEB)
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v3
with:
name: qtk-app-${{ matrix.os }}
path: |
build/packages/*.deb
- name: Package Qtk (WIN)
if: matrix.os == 'windows-latest'
shell: bash
run: |
cd build
cpack -C Release -G NSIS
- name: Upload package artifacts (WIN)
if: matrix.os == 'windows-latest'
uses: actions/upload-artifact@v3
with:
name: qtk-app-${{ matrix.os }}
path: |
build/packages/*.exe
- name: Package Qtk (OSX)
if: matrix.os == 'macos-latest'
shell: bash
run: |
cd build
cpack -C Release -G TGZ
- name: Upload package artifacts (OSX)
if: matrix.os == 'macos-latest'
uses: actions/upload-artifact@v3
with:
name: qtk-app-${{ matrix.os }}
path: |
build/packages/*.tar.gz
- name: Upload Qtk install directory
uses: actions/upload-artifact@v3
with:
name: qtk-app-${{ matrix.os }}-install
path: install/*
# TODO: Enable after trimming resources.
# - name: Package Qtk Application Sources
# if: matrix.os != 'macos-latest'
# shell: bash
# run: |
# cmake --build build/ --target package_source
#
# - name: Upload package artifacts
# uses: actions/upload-artifact@v3
# with:
# name: qtk-${{ matrix.os }}-packages
# path: |
# build/packages/*
# !build/packages/_CPack_Packages/*
Build-Qtk-Library:
env:
CONFIG: -DQTK_UPDATE_SUBMODULES=ON -DQTK_DEBUG=OFF -DQTK_ENABLE_CCACHE=OFF -DQTK_BUILD_GUI=OFF -DQTK_INSTALL_LIBRARY=ON -DQTK_INSTALL_PLUGINS=OFF
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/6.5.0/mingw81_64/ $CONFIG
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.5.0'
# Windows
- name: Chocolatey Action
if: matrix.os == 'windows-latest'
uses: crazy-max/ghaction-chocolatey@v2
with:
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1
- name: Configure Qtk Library (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: cmake -B build/ ${{ matrix.cmake }}
- name: Build Qtk Library (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: cmake --build build/ --config Release
# OSX / Linux
- name: Configure Qtk Library (OSX / Linux)
if: matrix.os != 'windows-latest'
shell: bash
run: cmake -B build/ ${{ matrix.cmake }}
- name: Build Qtk Library (OSX / Linux)
if: matrix.os != 'windows-latest'
shell: bash
run: cmake --build build/ --config Release --target qtk_library -- -j $(nproc)
# Packaging
- name: Install Qtk Library
shell: bash
run: cmake --install build/ --config Release --prefix=$(pwd)/install --component libqtk
- name: Package Qtk Library
shell: bash
run: cmake --build build/ --target package --config Release
- name: Package Qtk Library (DEB)
if: matrix.os == 'ubuntu-latest'
shell: bash
run: |
cd build
cpack -C Release -G DEB
- name: Upload package artifacts (DEB)
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v3
with:
name: libqtk-${{ matrix.os }}
path: |
build/packages/*.deb
- name: Package Qtk Library (WIN)
if: matrix.os == 'windows-latest'
shell: bash
run: |
cd build
cpack -C Release -G NSIS
- name: Upload package artifacts (WIN)
if: matrix.os == 'windows-latest'
uses: actions/upload-artifact@v3
with:
name: qtk-${{ matrix.os }}
path: |
build/packages/*.exe
- name: Package Qtk Library (OSX)
if: matrix.os == 'macos-latest'
shell: bash
run: |
cd build
cpack -C Release -G TGZ
- name: Upload package artifacts (OSX)
if: matrix.os == 'macos-latest'
uses: actions/upload-artifact@v3
with:
name: qtk-${{ matrix.os }}
path: |
build/packages/*.tar.gz
- name: Upload libqtk install
uses: actions/upload-artifact@v3
if: always()
with:
name: libqtk-${{ matrix.os }}-install
path: install/*
Build-Qtk-Plugins:
env:
CONFIG: -DQTK_UPDATE_SUBMODULES=ON -DQTK_DEBUG=OFF -DQTK_ENABLE_CCACHE=OFF -DQTK_BUILD_GUI=OFF -DQTK_INSTALL_LIBRARY=OFF -DQTK_INSTALL_PLUGINS=ON
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/6.5.0/mingw81_64/ $CONFIG
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ $CONFIG
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.5.0'
# Windows
- name: Chocolatey Action
if: matrix.os == 'windows-latest'
uses: crazy-max/ghaction-chocolatey@v2
with:
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1
- name: Configure Qtk Plugins (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: cmake -B build/ ${{ matrix.cmake }}
- name: Build Qtk Plugins (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: cmake --build build/ --config Release --target qtk_collection
# OSX / Linux
- name: Configure Qtk Plugins (OSX / Linux)
if: matrix.os != 'windows-latest'
shell: bash
run: cmake -B build/ ${{ matrix.cmake }}
- name: Build Qtk Plugins (OSX / Linux)
if: matrix.os != 'windows-latest'
shell: bash
run: cmake --build build/ --config Release --target qtk_collection -- -j $(nproc)
# Packaging
- name: Install Qtk Plugins
shell: bash
run: cmake --install build/ --config Release --prefix=$(pwd)/install --component collection
Build-Qtk-Assimp-Targets:
strategy:
@ -47,9 +325,9 @@ jobs:
os: [ubuntu-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH="/home/runner/work/qtk/Qt/6.3.1/gcc_64/" -DQTK_UPDATE_SUBMODULES=OFF
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH="/home/runner/work/qtk/Qt/6.3.1/gcc_64/" -DASSIMP_NEW_INTERFACE=ON -DQTK_UPDATE_SUBMODULES=OFF
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.5.0/gcc_64/ -DASSIMP_NEW_INTERFACE=ON
runs-on: ${{ matrix.os }}
steps:
@ -58,7 +336,7 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.3.1'
version: '6.5.0'
- name: Install Assimp MacOS
if: matrix.os == 'macos-latest'
@ -72,8 +350,10 @@ jobs:
run: |
sudo apt install libassimp-dev
- name: Configure Qtk
shell: bash
run: cmake -B build/ ${{ matrix.cmake }} -DQTK_ENABLE_CCACHE=OFF
- name: Build Qtk
shell: bash
run: |
cmake -S . -B build/ ${{ matrix.cmake }} && cmake --build build/ \
--target qtk-main
run: cmake --build build/ --config Release

View File

@ -14,14 +14,15 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.3.1'
version: '6.5.0'
- name: Install Assimp Ubuntu
run: sudo apt install libassimp-dev
- name: Build Qtk
run: |
cmake -B build -DQTK_UPDATE_SUBMODULES=OFF && cmake --build build
cmake -B build -DQTK_UPDATE_SUBMODULES=OFF -DQTK_ENABLE_CCACHE=OFF
cmake --build build
- uses: cpp-linter/cpp-linter-action@v2
id: linter

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
# CMake build files
**/cmake-build-debug/**
**/build/**
install
# C++ objects and libs
*.slo

View File

@ -1,17 +1,14 @@
################################################################################
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
## ##
## 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 ##
################################################################################
cmake_minimum_required(VERSION 3.2)
project(
#[[NAME]] Qtk
VERSION 1.0
DESCRIPTION "An example project using QT and OpenGL"
LANGUAGES CXX C
)
cmake_minimum_required(VERSION 3.23)
################################################################################
# Constants
################################################################################
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
@ -19,221 +16,158 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_MACOSX_BUNDLE ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
if(WIN32)
set(CMAKE_COMPILE_WARNING_AS_ERROR OFF)
add_compile_options(/wd4131 /wd4127)
endif()
message(STATUS "[Qtk] Compiling with ${CMAKE_CXX_COMPILER_ID}")
add_compile_options(-fPIC)
# Qtk build options
option(QTK_DEBUG "Enable debugger" ON)
message(STATUS "[Qtk] Compiling with QTK_DEBUG=${QTK_DEBUG}")
option(QTK_UPDATE_SUBMODULES "Update external project (assimp) submodule" ON)
message(
STATUS
"[Qtk] Compiling with QTK_UPDATE_SUBMODULES=${QTK_UPDATE_SUBMODULES}"
################################################################################
# Project
################################################################################
project(
#[[NAME]] Qtk
VERSION 0.2
DESCRIPTION "Qt OpenGL library and desktop application."
LANGUAGES CXX C
)
# Qt options
set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/6.3.1/gcc_64/" CACHE PATH "Path to Qt6")
################################################################################
# Includes
################################################################################
include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake")
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
# Options for bringing your own assimp installation; Otherwise not needed
################################################################################
# Options
################################################################################
option(QTK_DEBUG "Enable debugger" OFF)
option(QTK_UPDATE_SUBMODULES "Update external project (assimp) submodule" OFF)
option(QTK_BUILD_GUI "Build the Qtk desktop application" ON)
option(QTK_INSTALL_LIBRARY "Install libqtk to CMAKE_INSTALL_PREFIX path." ON)
option(QTK_INSTALL_PLUGINS "Install Qtk plugin collection to Qt Creator." OFF)
option(QTK_BUILD_EXAMPLE "Build the Qtk example desktop application" ON)
option(QTK_ENABLE_CCACHE "Enable ccache" ON)
if (QTK_ENABLE_CCACHE)
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
endif()
# Install Qtk for use within Qt Creator projects only, instead of system-wide.
option(QTK_PREFIX_QTCREATOR "Install Qtk to Qt Creator. Untested." OFF)
# Option for bringing your own assimp installation; Otherwise not needed
# + If assimp is available system-wide we can just set QTK_UPDATE_SUBMODULES OFF
option(ASSIMP_NEW_INTERFACE "Use the assimp::assimp interface (WIN / OSX)" OFF)
message(
STATUS
"[Qtk] Compiling with ASSIMP_NEW_INTERFACE=${ASSIMP_NEW_INTERFACE}"
option(
QTK_ASSIMP_NEW_INTERFACE
"Use the assimp::assimp interface (WIN / OSX)"
OFF
)
################################################################################
# External Libraries
################################################################################
if(NOT QTK_DEBUG)
set(CMAKE_BUILD_TYPE Release)
else()
set(CMAKE_BUILD_TYPE Debug)
endif()
# For CLion builds, point CMAKE_PREFIX_PATH to Qt6 install directory
# + QtCreator will handle this for you if that is used instead
list(APPEND CMAKE_PREFIX_PATH "${QT_DIR}")
# This should be set to your Qt6 installation directory.
set(QT_INSTALL_DIR "$ENV{HOME}/Qt/6.5.0/gcc_64" CACHE PATH "Path to Qt6 install.")
# Point CMAKE_PREFIX_PATH to Qt6 install directory
# If Qtk is built within Qt Creator this is not required.
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
if (QTK_PREFIX_QTCREATOR)
# TODO: This might be a bit strange and needs more testing.
set(CMAKE_INSTALL_PREFIX "${QT_INSTALL_DIR}")
endif()
set(
QT_CREATOR_DIR
"${QT_INSTALL_DIR}/../../Tools/QtCreator"
CACHE PATH "Qt Creator path used to install Qtk plugins for Qt Designer."
)
# Qt Designer will look in different locations if WIN / Unix.
# These paths are for using Qt Designer integrated within Qt Creator.
# Standalone Qt Designer may use different paths.
if (WIN32)
# These paths may be different on windows. I have not tested this.
set(QT_PLUGIN_INSTALL_DIR "${QT_CREATOR_DIR}/bin/plugins/designer")
set(QT_PLUGIN_LIBRARY_DIR "${QT_CREATOR_DIR}/lib/Qt/lib")
else()
set(QT_PLUGIN_INSTALL_DIR "${QT_CREATOR_DIR}/lib/Qt/plugins/designer")
set(QT_PLUGIN_LIBRARY_DIR "${QT_CREATOR_DIR}/lib/Qt/lib")
endif()
set(QTK_PLUGIN_LIBRARY_DIR "${QT_PLUGIN_LIBRARY_DIR}")
set(QTK_PLUGIN_INSTALL_DIR "${QT_PLUGIN_INSTALL_DIR}")
message(STATUS "[Qtk] CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}")
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
set(QTK_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns)
# Print all QTK options and their values.
get_cmake_property(VAR_NAMES VARIABLES)
list(FILTER VAR_NAMES INCLUDE REGEX "^Q[tT][kK]_.*$")
list(SORT VAR_NAMES)
foreach(VAR_NAME ${VAR_NAMES})
message(STATUS "[Qtk] ${VAR_NAME}=${${VAR_NAME}}")
endforeach()
################################################################################
# External Dependencies
################################################################################
# Find Qt
find_package(Qt6 COMPONENTS OpenGLWidgets)
find_package(Qt6 COMPONENTS Core UiPlugin OpenGLWidgets)
qt_standard_project_setup()
if(NOT Qt6_FOUND)
message(
SEND_ERROR "[Qtk] Error: Unable to find Qt6 at CMAKE_PREFIX_PATH: "
"${CMAKE_PREFIX_PATH}"
)
message(
FATAL_ERROR
"[Qtk] Error: Specify path to Qt6 with `cmake "
FATAL_ERROR "[Qtk] Error: Specify path to Qt6 with `cmake "
"-DCMAKE_PREFIX_PATH=/path/to/Qt/6.x.x/gcc_64 -S /path/to/qtk -B "
"/path/to/qtk/build && cmake --build /path/to/qtk/build -j $(nprocs)`"
)
endif()
# Find Assimp.
if(QTK_UPDATE_SUBMODULES)
# Required to statically link.
add_compile_options(-fPIC)
set(BUILD_SHARED_LIBS OFF CACHE STRING "Build static assimp libs" FORCE)
set(ASSIMP_BUILD_ZLIB ON CACHE STRING "Build Zlib with assimp." FORCE)
set(
ASSIMP_INSTALL
OFF CACHE STRING "Disable to use assimp as a submodule."
FORCE
)
set(ASSIMP_NO_EXPORT ON CACHE STRING "Disable to export assimp." FORCE)
set(ASSIMP_WARNINGS_AS_ERRORS OFF CACHE STRING "No warnings as errors." FORCE)
set(ASSIMP_BUILD_TESTS OFF CACHE STRING "Do not build assimp tests." FORCE)
message(STATUS "[Qtk] Updating submodules...")
include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake")
submodule_update("${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/")
add_subdirectory(
"${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/"
EXCLUDE_FROM_ALL
)
else()
find_package(assimp REQUIRED)
endif()
if(WIN32)
find_package(OpenGL REQUIRED)
endif()
################################################################################
# Qtk
################################################################################
add_subdirectory(src)
set(
PUBLIC_HEADERS
src/qtkwidget.h
src/scene.h
src/camera3d.h
src/mesh.h
src/meshrenderer.h
src/model.h
src/object.h
src/skybox.h
src/texture.h
src/transform3D.h
)
set(
SOURCE_FILES
src/qtkwidget.cpp
src/scene.cpp
src/camera3d.cpp
src/input.cpp
src/input.h
src/mesh.cpp
src/meshrenderer.cpp
src/model.cpp
src/object.cpp
src/qtkapi.h
src/skybox.cpp
src/texture.cpp
src/transform3D.cpp
)
include(GenerateExportHeader)
add_library(qtk-widget STATIC ${PUBLIC_HEADERS} ${SOURCE_FILES})
target_include_directories(qtk-widget PRIVATE src/ app/)
set_target_properties(
qtk-widget PROPERTIES
PUBLIC_HEADER "${PUBLIC_HEADERS}"
VERSION ${PROJECT_VERSION}
)
target_link_libraries(qtk-widget PUBLIC Qt6::OpenGLWidgets)
target_link_libraries(qtk-widget PUBLIC Qt6::Widgets)
if(QTK_UPDATE_SUBMODULES OR NOT ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk-widget PUBLIC assimp)
elseif(ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk-widget PUBLIC assimp::assimp)
endif()
if(QTK_DEBUG)
message(STATUS "[Qtk] Building with QTK_DEBUG=${QTK_DEBUG}")
target_compile_definitions(qtk-widget PUBLIC QTK_DEBUG)
endif()
if(WIN32)
find_package(OpenGL REQUIRED)
target_link_libraries(qtk-widget PUBLIC OpenGL::GL)
endif()
# Install files
install(
TARGETS qtk-widget
# Associate qtk-widget target with qtk-export
EXPORT qtk-export
# <prefix>/bin on DLL systems and <prefix>/lib on non-dll systems
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static
RUNTIME DESTINATION bin
PUBLIC_HEADER DESTINATION include
)
# Install export
# qtkTargets.cmake will only be installed when one of the CONFIGURATIONS is installed
# + The generated import will only reference that qtk configuration
install(
EXPORT qtk-export
FILE qtkTargets.cmake
CONFIGURATIONS Debug|Release
DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake
)
################################################################################
# Final Application
################################################################################
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/src/qtkresources.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/src/qtkresources.h"
@ONLY
)
# Add our Qt resources.qrc file to our application
set(
QTK_APP_SOURCES
app/main.cpp
app/examplescene.cpp
app/examplescene.h
app/mainwindow.cpp
app/mainwindow.h
app/mainwindow.ui
app/resourcemanager.h
src/qtkresources.h.in
)
qt6_add_big_resources(QTK_APP_SOURCES resources.qrc)
qt_add_executable(qtk-main ${QTK_APP_SOURCES})
target_include_directories(qtk-main PRIVATE src/ app/)
# Link qtk-main executable to main qtk-widget library
target_link_libraries(qtk-main PUBLIC qtk-widget)
set_target_properties(
qtk-main PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
)
install(
TARGETS qtk-main
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
generate_export_header(qtk-widget)
if(WIN32)
get_target_property(_qt6_qmake_location Qt6::qmake IMPORTED_LOCATION)
execute_process(COMMAND "${_qt6_qmake_location}" -query QT_INSTALL_PREFIX RESULT_VARIABLE return_code OUTPUT_VARIABLE qt6_install_prefix OUTPUT_STRIP_TRAILING_WHITESPACE)
file(TO_NATIVE_PATH "${qt6_install_prefix}/bin" qt6_install_prefix)
if(TARGET Qt6::windeployqt)
add_custom_command(
TARGET qtk-main
POST_BUILD
COMMAND set PATH=%PATH%$<SEMICOLON>${qt6_install_prefix}
COMMAND Qt6::windeployqt --dir "${CMAKE_BINARY_DIR}/windeployqt" "$<TARGET_FILE_DIR:qtk-main>/$<TARGET_FILE_NAME:qtk-main>"
)
install(DIRECTORY "${CMAKE_BINARY_DIR}/windeployqt/" DESTINATION bin)
endif()
if(MSVC AND TARGET Qt6::qmake)
set(VSUSER_FILE ${CMAKE_CURRENT_BINARY_DIR}/qtk-main.vcxproj.user)
file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/extern/assimp/assimp/bin" assimp_bin)
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};${assimp_bin};$(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()
if(QTK_BUILD_EXAMPLE)
# Create a namespaced alias for linking with qtk_library in the example.
add_library(${PROJECT_NAME}::qtk_library ALIAS qtk_library)
add_subdirectory(example-app)
endif()

369
README.md
View File

@ -1,92 +1,225 @@
# Qtk
[![All Builds](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml)
[![Linting](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml)
Model loader using [Assimp](https://assimp.org/) within a Qt widget application.
Qtk is a Qt OpenGL graphics library created primarily for my own learning
purposes. The library wraps some QOpenGL functionality in convenience classes
that allow rendering geometry in 2D and 3D using custom GLSL shader programs.
You can import your own models within `app/examplescene.cpp`, inside the
`ExampleScene::init()` function. Rotations and translations
happen in `ExampleScene::update()`.
The long-term goal for this project is to create a tool that I can use to
practice shader coding or graphics programming techniques. In doing this I hope
to also learn more about the Qt UI framework, and the CMake build system.
To get textures loading on models look into [material files](http://www.paulbourke.net/dataformats/mtl/)
Key features that are planned:
* Runtime loading of `.obj` or similar 3D models.
* Drag-and-drop interaction for adding objects to the scene.
* Runtime reloading of modified GLSL shaders attached to objects within scenes.
* Multiple views of a scene at one time.
* Camera control modes such as panning, orbiting, or following objects.
* Save / load for scene data. The current inheritance model is temporary.
* Basic text editor for quickly modifying shaders attached to objects.
* Shader / object properties panel to modify related settings.
* Reduce size of application resources and git references.
The Qtk desktop application provides a model loader
using [Assimp](https://assimp.org/) within a Qt widget application.
For examples of using the Qtk API, see the `example-app` project in the root of
this repository.
To get textures loading on models look
into [material files](http://www.paulbourke.net/dataformats/mtl/)
and see some examples in the `resources/models/` directory.
The syntax for adding shapes and models is seen in the example below.
This would result in a scene with a red cube and a miniature spartan model placed on top.
```C++
// From: qtk/app/examplescene.cpp
void ExampleScene::init() {
// Add a skybox to the scene using default cube map images and settings.
setSkybox(new Qtk::Skybox("Skybox"));
/* Create a red cube with a mini master chief on top. */
auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS));
myCube->setColor(RED);
mMeshes.push_back(myCube);
auto mySpartan = new Model("My spartan", ":/models/spartan/spartan.obj");
mySpartan->getTransform().setTranslation(0.0f, 0.5f, 0.0f);
mySpartan->getTransform().setScale(0.5f);
mModels.push_back(mySpartan);
}
```
If we want to make our spartan spin, we need to apply rotation in `update`
```C++
// From: qtk/app/examplescene.cpp
void ExampleScene::update() {
auto mySpartan = Model::getInstance("My spartan");
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto myCube = MeshRenderer::getInstance("My cube");
myCube->getTransform().rotate(-0.75f, 0.0f, 1.0f, 0.0f);
}
```
### Source Builds
Builds are configured for CLion or [Qt Creator](https://github.com/qt-creator/qt-creator).
Simply open the root `CMakeLists.txt` with either of these editors and configurations will be loaded.
Qtk was developed and tested using CLion
and [Qt Creator](https://github.com/qt-creator/qt-creator).
Simply open the root `CMakeLists.txt` with either of these editors and
configurations will be loaded.
This project has been ported to Qt6, which is not yet available in Ubuntu apt repositories.
To run this project, you will *need* to install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for your system.
Be sure to take note of the Qt6 installation directory, as we will need it to correctly set our `CMAKE_PREFIX_PATH` in the next steps.
This project has been ported to **Qt 6.5.0**, which is not yet available in
Ubuntu apt repositories.
To run this project, you will *need* to
install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for
your system, **version 6.5.0** or later.
Be sure to take note of the Qt6 installation directory, as we will need it to
correctly set our `CMAKE_PREFIX_PATH` in the next steps.
#### Linux
If the build is configured with all options enabled, we can subsequently install
individual components as needed with cmake.
Once Qt6 is installed, to build and run `qtk` on Ubuntu -
```bash
sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git
git clone https://gitlab.com/shaunrd0/qtk
cmake -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64 -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nproc --ignore=2) --target qtk-main
./qtk/build/qtk-main
cmake -B build-all/ -DQTK_BUILD_GUI=ON -DQTK_INSTALL_LIBRARY=ON -DQTK_INSTALL_PLUGINS=ON
```
By default, the build will initialize Assimp as a git submodule and build from source.
We can turn this off by setting the `-DQTK_UPDATE_SUBMODULES=OFF` flag when running CMake.
This will greatly increase build speed, but we will need to make sure Assimp is available either system-wide or using a custom `CMAKE_PREFIX_PATH`.
Using `-DQTK_UPDATE_SUBMODULES=ON` supports providing assimp on cross-platform builds (Windows / Mac / Linux) and may be easier to configure.
```bash
# Install libqtk only
cmake --install build-all/ --prefix=$(pwd)/install --component libqtk
-- Install configuration: "Release"
-- Up-to-date: /home/shaun/Code/qtk/install/lib/cmake/Qtk/QtkConfig.cmake
-- Up-to-date: /home/shaun/Code/qtk/install/lib/cmake/Qtk/QtkConfigVersion.cmake
-- Up-to-date: /home/shaun/Code/qtk/install/lib/cmake/Qtk/QtkTargets.cmake
-- Up-to-date: /home/shaun/Code/qtk/install/lib/cmake/Qtk/QtkTargets-release.cmake
-- Up-to-date: /home/shaun/Code/qtk/install/lib/static/libqtk_library.a
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/camera3d.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/input.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/meshrenderer.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/model.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/modelmesh.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/object.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/qtkapi.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/qtkiostream.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/qtkiosystem.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/scene.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/shape.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/skybox.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/texture.h
-- Up-to-date: /home/shaun/Code/qtk/install/include/qtk/transform3D.h
# Install Qtk widget collection to use Qt Designer
cmake --install build-all/ --prefix=$(pwd)/install --component collection
-- Install configuration: "Release"
-- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_library.a
-- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_plugin_library.a
-- Up-to-date: /home/shaun/Qt/6.5.0/gcc_64/../../Tools/QtCreator/lib/Qt/plugins/designer/libqtk_collection.so
# Install Qtk desktop application (output removed)
cmake --install build-all/ --prefix=$(pwd)/install --component qtk
```
#### Qtk GUI
Once Qt6 is installed, to build and run `qtk` on Ubuntu -
```bash
sudo apt update -y && sudo apt install freeglut3-dev libassimp-dev cmake build-essential git
git clone https://gitlab.com/shaunrd0/qtk
cmake -DQTK_UPDATE_SUBMODULES=OFF -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64 -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nproc --ignore=2) --target qtk-main
# We can also provide a path to assimp -
#cmake -DQTK_UPDATE_SUBMODULES=OFF -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64;/path/to/assimp/ -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nproc --ignore=2) --target qtk-main
./qtk/build/qtk-main
sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git ccache -y
git clone https://github.com/shaunrd0/qtk
cmake -S qtk/ -B qtk/build/ -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64
cmake --build qtk/build/ -j $(nproc --ignore=2)
./qtk/build/bin/qtk-main
```
By default, the build will not initialize Assimp as a git submodule and build
from source.
We can turn this on by setting the `-DQTK_UPDATE_SUBMODULES=ON` flag when
running CMake.
Building using this option will fetch and build Assimp for us, but builds will
take longer as a result.
Using `-DQTK_UPDATE_SUBMODULES=ON` supports providing assimp on cross-platform
builds (Windows / Mac / Linux) and may be easier to configure.
```bash
cmake -S qtk/ -B qtk/build/ -DQTK_UPDATE_SUBMODULES=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64
cmake --build qtk/build/ -j $(nproc --ignore=2)
./qtk/build/bin/qtk-main
```
If any errors are encountered loading plugins, we can debug plugin loading by
setting the following environment variable -
```bash
QT_DEBUG_PLUGINS=1 ./qtk-main
```
#### Qtk Library
Qtk provides a simple library for working with QOpenGL.
We can install this library on a system path or a custom path and then
set `CMAKE_PREFIX_PATH` to point to this location when building an application
using libqtk.
Below is an example of installing on a system path.
```bash
cmake -S qtk/ -B qtk/build/ -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64 -DQTK_BUILD_GUI=OFF -DQTK_INSTALL_PLUGINS=OFF -DQTK_DEBUG=OFF
cmake --build qtk/build/ -j $(nproc --ignore=2)
sudo cmake --install . --prefix=/usr/local
-- Install configuration: "Release"
-- Installing: /usr/local/lib/cmake/Qtk/QtkConfig.cmake
-- Installing: /usr/local/lib/cmake/Qtk/QtkConfigVersion.cmake
-- Installing: /usr/local/lib/cmake/Qtk/QtkTargets.cmake
-- Installing: /usr/local/lib/cmake/Qtk/QtkTargets-release.cmake
-- Installing: /usr/local/lib/static/libqtk_library.a
-- Installing: /usr/local/include/qtk/camera3d.h
-- Installing: /usr/local/include/qtk/input.h
-- Installing: /usr/local/include/qtk/meshrenderer.h
-- Installing: /usr/local/include/qtk/model.h
-- Installing: /usr/local/include/qtk/modelmesh.h
-- Installing: /usr/local/include/qtk/object.h
-- Installing: /usr/local/include/qtk/qtkapi.h
-- Installing: /usr/local/include/qtk/qtkiostream.h
-- Installing: /usr/local/include/qtk/qtkiosystem.h
-- Installing: /usr/local/include/qtk/scene.h
-- Installing: /usr/local/include/qtk/shape.h
-- Installing: /usr/local/include/qtk/skybox.h
-- Installing: /usr/local/include/qtk/texture.h
-- Installing: /usr/local/include/qtk/transform3D.h
```
#### Qtk Plugin Collection
This project defines a collection of widget plugins for use with Qt Designer.
These plugins were used to build the interface for the Qtk desktop application.
Qt Designer will list Qtk widgets in the side panel when editing a UI file
within the designer.
Qtk widgets will also render and behave correctly within the UI preview in
designer.
The widgets in the Qtk collection were created by implementing
the [QDesignerCustomWidgetInterface](https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html#details)
and [QDesignerCustomWidgetCollectionInterface](https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html)
interfaces.
To build and install the Qtk plugin collection -
```bash
cmake -S /path/to/qtk -B /path/to/qtk/build -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64 -DQTK_INSTALL_PLUGINS=ON -DQTK_BUILD_GUI=OFF -DQTK_INSTALL_LIBRARY=OFF
cmake --build /path/to/qtk/build
cmake --install /path/to/qtk/build
```
To uninstall after a previous installation, we can run the following command
from the root of the repository.
```bash
xargs rm < build/install_manifest.txt
```
#### Windows / MacOS
If you are building on **Windows / Mac** and bringing your own installation of Assimp, consider setting the `-DASSIMP_NEW_INTERFACE` build flag.
If you are building on **Windows / Mac**, consider setting
the `-DASSIMP_NEW_INTERFACE` build flag.
```bash
cmake -DASSIMP_NEW_INTERFACE=ON -DQTK_UPDATE_SUBMODULES=OFF -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64;/path/to/assimp/ -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nproc --ignore=2) --target qtk-main
cmake -S qtk/ -B qtk/build/ -DASSIMP_NEW_INTERFACE=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64;/path/to/assimp/
cmake --build qtk/build/ -j $(nproc --ignore=2)
```
### Controls
You can fly around the scene if you hold the right mouse button and use WASD.
If you see a small triangle floating by a model it represents the light source
that is being used for the shader rendering the model. These appear on models
using phong, specular, and diffuse lighting techniques.
Object names can be double-clicked in the tree view panel for quick camera
navigation. All panels and toolbars are dockable widgets that can be popped out
and reorganized as needed. Panels can be stacked to create a docked widget with
tabs. The central widget that provides the camera view into the scene cannot be
detached from the main window in this way. See the `View` menu to enable debug
console widgets for open scenes or reopen previously closed panels.
![](resources/screenshot.png)
Spartan with no normals -
![](resources/spartan-specular.png)
Spartan with normals -
![](resources/spartan-normals.png)
#### Development
@ -101,7 +234,9 @@ cmake --build build -j $(nproc --ignore=2)
sudo cmake --build build -j $(nproc --ignore=2) --target install
```
If this version is any earlier than `15.0.0`, running `clang-format` will fail because this project uses configuration options made available since `15.0.0`.
If the `clang-format` version is any earlier than `15.0.0`,
running `clang-format` will fail because this project uses configuration options
made available since `15.0.0`.
```bash
clang-format --version
@ -109,7 +244,8 @@ clang-format version 15.0.5 (git@github.com:llvm/llvm-project.git 154e88af7ec97d
```
CLion has integration for IDE code reformatting actions with `clang-format`.
If you're using CLion, the `.clang-format` configuration will be picked up by CLion automatically.
If you're using CLion, the `.clang-format` configuration will be picked up by
CLion automatically.
`clang-tidy` can be run with the following commands.
@ -117,7 +253,7 @@ If you're using CLion, the `.clang-format` configuration will be picked up by CL
# Move to the root of the repo
cd qtk
# Build
cmake -B build && cmake --build build
cmake -B build && cmake --build build
clang-tidy -p build/ --fix --config-file=.clang-tidy src/*.cpp src/*.h app/*.cpp app/*.h
```
@ -146,57 +282,90 @@ changed files:
src/transform3D.h
```
### Controls
##### Packaging
You can fly around the scene if you hold the right mouse button and use WASD.
If you see a small triangle floating by a model it represents the light source
that is being used for the shader rendering the model. These appear on models
using phong, specular, and diffuse lighting techniques.
Packaging for Qtk is in early development.
This section documents how to package Qtk, but only source builds have been
verified on Windows / Mac / Linux.
For this reason, it is recommended to install Qtk by strictly building from
source at this time.
![](resources/screenshot.png)
Below are the steps to package a Qtk release.
Spartan with no normals -
```bash
cd /path/to/qtk && cmake -B build
# Package Qtk
cmake --build build --target package
# Package Qtk including source files
cmake --build build --target package_source
```
![](resources/spartan-specular.png)
Alternatively, we can use `cpack` directly -
Spartan with normals -
```bash
cd /path/to/qtk && cmake -B build
# Generate all install packages
cpack -C Release
# Generate a specific archive package (ZIP)
cpack -C Release -G ZIP
# Generate a specific archive package (TGZ)
cpack -C Release -G TGZ
# Generate debian package (DEB)
cpack -C Release -G DEB
# Generate NSIS install package (NSIS)
cpack -C Release -G NSIS
```
![](resources/spartan-normals.png)
Any of the above options can be appended with `--trace-expand` to debug package
generation issues.
The contents of all packages will depend on how the build was configured.
If we are generating packages for *only* libqtk, we
set `-DQTK_INSTALL_LIBRARY=ON`
during the cmake configuration step.
To generate packages for Qtk desktop application, we should
set `-DQTK_BUILD_GUI=ON`, and optionally `-DQTK_INSTALL_LIBRARY=ON` if we would
like to bundle libqtk with the desktop application.
### QtkWidget in Qt Creator
The NSIS installer will allow component-specific path modification for all of
these installation components through a GUI install application.
We can add more QtkWidgets to view and render the scene from multiple perspectives.
There is still some work to be done here, so there isn't a builtin way to add an additional view within the application.
##### Resources
![](resources/qtk-views.png)
Some useful links and resources that I have found while working on this project.
After building Qtk, we can drag and drop an `OpenGL Widget` onto the `mainwindow.ui`.
Then right-click the new OpenGLWidget and `Promote To->QtkWidget` to add a second view.
[Qt Designer UI file format](https://doc.qt.io/qt-6/designer-ui-file-format.html)
![](resources/qtk-views-setup.png)
If we demote or delete all widgets in `mainwindow.ui` and rebuild the project, Qt Creator will drop `QtkWidget` from the list of possible promoted widgets.
Add an `OpenGL Widget` to the UI, right-click it and navigate to `Promote Widget...` and enter the information below.
![](resources/qtk-reference.png)
After you fill out the `New Promoted Class` form, click `Add` *and*`Promote`, then rebuild.
After following these steps Qt Creator will list `QtkWidget` as an option to promote `OpenGL Widgets` again.
[QtPlugin Import / Export plugins](https://doc.qt.io/qt-6/qtplugin.html)
[KDAB](https://www.kdab.com/)
## Model Artists
"Alien Hominid" (https://skfb.ly/onStx) by Nwilly_art is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
"Alien Hominid" (https://skfb.ly/onStx) by Nwilly_art is licensed under Creative
Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
"Scythe World Of Warcraft" (https://skfb.ly/6UooG) by Warcraft-3D-Models is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
"Scythe World Of Warcraft" (https://skfb.ly/6UooG) by Warcraft-3D-Models is
licensed under Creative Commons
Attribution (http://creativecommons.org/licenses/by/4.0/).
"Spartan Armour MKV - Halo Reach" (https://skfb.ly/6QVvM) by McCarthy3D is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
"Spartan Armour MKV - Halo Reach" (https://skfb.ly/6QVvM) by McCarthy3D is
licensed under Creative Commons
Attribution (http://creativecommons.org/licenses/by/4.0/).
"Survival Guitar Backpack (Low Poly)" (https://skfb.ly/6RnCB) by Berk Gedik is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
Model by Berk Gedik, from: https://sketchfab.com/3d-models/survival-guitar-backpack-low-poly-799f8c4511f84fab8c3f12887f7e6b36
Modified (learnopengl.com) material assignment (Joey de Vries) for easier load in OpenGL model loading chapter, and renamed albedo to diffuse and metallic to specular to match non-PBR lighting setup.
"Survival Guitar Backpack (Low Poly)" (https://skfb.ly/6RnCB) by Berk Gedik is
licensed under Creative Commons
Attribution (http://creativecommons.org/licenses/by/4.0/).
Model by Berk Gedik,
from: https://sketchfab.com/3d-models/survival-guitar-backpack-low-poly-799f8c4511f84fab8c3f12887f7e6b36
Modified (learnopengl.com) material assignment (Joey de Vries) for easier load
in OpenGL model loading chapter, and renamed albedo to diffuse and metallic to
specular to match non-PBR lighting setup.
"Terror-bird (NHMW-Geo 2012/0007/0001)" (https://skfb.ly/onAWy) by Natural History Museum Vienna is licensed under Creative Commons Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/).
"Terror-bird (NHMW-Geo 2012/0007/0001)" (https://skfb.ly/onAWy) by Natural
History Museum Vienna is licensed under Creative Commons
Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/).
"Golden Lion Sitting OBJ Low Poly FREE" (https://skfb.ly/onZAH) by LordSamueliSolo is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
"Golden Lion Sitting OBJ Low Poly FREE" (https://skfb.ly/onZAH) by
LordSamueliSolo is licensed under Creative Commons
Attribution (http://creativecommons.org/licenses/by/4.0/).

View File

@ -1,40 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: MainWindow for creating an example Qt application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <mainwindow.h>
#include <qtkwidget.h>
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget * parent) :
QMainWindow(parent), ui(new Ui::MainWindow) {
// For use in design mode using Qt Creator
// + We can use the `ui` member to access nested widgets by name
ui->setupUi(this);
// Find all QtkWidgets in this QMainWindow and initialize their scenes.
for(const auto widget : ui->qWidget->children()) {
auto qtkWidget = dynamic_cast<Qtk::QtkWidget *>(widget);
if(qtkWidget != nullptr) {
std::string key = qtkWidget->objectName().toStdString();
// Initialize each scene into a map if it doesn't exist.
if(mScenes[key] == nullptr) {
mScenes[key] = new ExampleScene();
}
// Set the QtkWidget to use the scene associated with this widget.
qtkWidget->setScene(mScenes[key]);
}
}
// Set the window icon used for Qtk.
// TODO: Update this to be something other than kilroy.
setWindowIcon(QIcon("../resources/icon.png"));
}
MainWindow::~MainWindow() {
delete ui;
}

View File

@ -1,53 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: MainWindow for creating an example Qt application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <unordered_map>
#include <QMainWindow>
#include <examplescene.h>
#include "qtk-widget_export.h"
namespace Ui {
class MainWindow;
}
/**
* MainWindow class to provide an example of using a QtkWidget within a Qt
* window application.
*/
class QTK_WIDGET_EXPORT MainWindow : public QMainWindow {
Q_OBJECT
public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
/**
* This ctor also initializes the Scene for each QtkWidget in the window.
* To load a different scene this would need to be updated.
*
* @param parent The parent for this QMainWindow
*/
explicit MainWindow(QWidget * parent = nullptr);
~MainWindow() override;
private:
/***************************************************************************
* Private Members
**************************************************************************/
Ui::MainWindow * ui {};
std::unordered_map<std::string, Qtk::Scene *> mScenes {};
};
#endif // MAINWINDOW_H

View File

@ -1,114 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Qtk - MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="qWidget" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>801</width>
<height>561</height>
</rect>
</property>
<widget class="Qtk::QtkWidget" name="openGLWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>781</width>
<height>541</height>
</rect>
</property>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuTest">
<property name="title">
<string>File</string>
</property>
<addaction name="actionOpen"/>
<addaction name="actionSave_2"/>
<addaction name="actionSave_as"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
<addaction name="actionShow_Console"/>
</widget>
<addaction name="menuTest"/>
<addaction name="menuView"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionOtherTest">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionSave">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionOpen">
<property name="text">
<string>Open...</string>
</property>
</action>
<action name="actionSave_2">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionSave_as">
<property name="text">
<string>Save as...</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionShow_Console">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show Console</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>Qtk::QtkWidget</class>
<extends>QOpenGLWidget</extends>
<header>qtkwidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,48 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Manage files and resources used by qtk ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <string>
#include <src/qtkresources.h>
#ifndef QTK_RESOURCEMANAGER_H
#define QTK_RESOURCEMANAGER_H
/**
* ResourceManager class used to construct absolute paths to files within the Qt
* resources path. There is no need to manually call this method.
* Model::loadModel(...) will use this method if a Qt resource path is provided.
* The Model constructor behaves the same. If a path is prefixed with `:/` this
* static method will be used to resolve a full system path.
*
* This will likely be deprecated. It has a single call site and it is not
* meant for public use. It is public only for convenience.
*
* RM::getPath(":/models/alien-hominid/alien.obj") =
* /full/path/to/models/alien-hominid/alien.obj
*/
typedef class ResourceManager {
public:
/**
* Takes a path using qrc format and constructs full system path to qtk
* assets Qrc format prefix ':/' is trimmed from the path for the caller
* Assets used with RM may (or may not) appear in qtk/resources.qrc
*
* @param path Path relative to qtk/resources/; ie)
* ':/models/backpack/backpack.obj' An asset at location
* qtk/resources/path/to/asset.obj Should be given in qrc format:
* ':/path/to/asset.obj'
* @return Absolute system path to a qtk asset
*/
static std::string getPath(const std::string & path) {
// Only construct qtk resource path if in qrc format; else return it as-is
return path[0] == ':' ? QTK_RESOURCES + path.substr(1) : path;
}
} RM;
#endif // QTK_RESOURCEMANAGER_H

View File

@ -0,0 +1,9 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/QtkTargets.cmake")
set_and_check(QTK_EXECUTABLE "${PACKAGE_PREFIX_DIR}/bin/qtk_app")
set_and_check(QTK_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")
set_and_check(QTK_LIBRARIES "${PACKAGE_PREFIX_DIR}/lib")
check_required_components(Qtk)

View File

@ -0,0 +1,70 @@
################################################################################
## Example client project using qtk ##
## ##
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
## All Content (c) 2023 Shaun Reed, all rights reserved ##
################################################################################
cmake_minimum_required(VERSION 3.23)
################################################################################
# Constants
################################################################################
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_compile_options(/wd4131 /wd4127)
endif()
# If you did not install Qtk on a system path, point cmake to installation.
set(QTK_PATH /usr/local CACHE PATH "Path to installation of Qtk")
# If you did not install Qt6 on a system path, point cmake to installation.
set(QT_INSTALL_DIR "$ENV{HOME}/Qt/6.5.0/gcc_64/" CACHE PATH "Path to Qt6")
################################################################################
# Project
################################################################################
project(
#[[NAME]] QtkClient
VERSION 0.1
DESCRIPTION "An example project using Qtk"
LANGUAGES CXX C
)
list(APPEND CMAKE_PREFIX_PATH "${QTK_PATH}")
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
# Allow add_subdirectory on this project to use target ALIAS if available.
# If this example project is opened standalone we will use find_package.
if(NOT TARGET Qtk::qtk_library)
find_package(Qtk 0.2 REQUIRED)
endif()
# Print all QTK variables
if (NOT Qtk_IS_TOP_LEVEL)
get_cmake_property(VAR_NAMES VARIABLES)
list(FILTER VAR_NAMES INCLUDE REGEX "^Q[tT][kK]_.*$")
list(SORT VAR_NAMES)
foreach(VAR_NAME ${VAR_NAMES})
message(STATUS "[Qtk] ${VAR_NAME}=${${VAR_NAME}}")
endforeach()
endif()
find_package(Qt6 COMPONENTS Core Widgets OpenGLWidgets REQUIRED)
set(
EXAMPLE_SOURCES
main.cpp
examplescene.cpp examplescene.h
examplewidget.cpp examplewidget.h
)
add_executable(example_app ${EXAMPLE_SOURCES})
target_link_libraries(example_app PUBLIC Qt6::Widgets Qt6::OpenGLWidgets Qt6::Core)
target_link_libraries(example_app PUBLIC Qtk::qtk_library)

72
example-app/README.md Normal file
View File

@ -0,0 +1,72 @@
This is an example application that is using the Qtk API to create custom Qt
OpenGL widgets. This is very similar to `QtkWidget` in the Qtk desktop
application source code, but could be modified for different uses if needed.
There are no camera controls supported in this example. The camera is fixed.
If these controls are desired, they can be implemented by the client.
You can import your own models within `examplescene.cpp`, inside the
`ExampleScene::init()` function. Rotations and translations
are applied in `ExampleScene::update()`.
The syntax for adding shapes and models is seen in the example below.
This would result in a scene with a red cube and a miniature spartan model
placed on top.
```C++
void ExampleScene::init() {
// Add a skybox to the scene using default cube map images and settings.
setSkybox(new Qtk::Skybox("Skybox"));
/* Create a red cube with a mini master chief on top. */
auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS));
myCube->setColor(RED);
mMeshes.push_back(myCube);
auto mySpartan = new Model("My spartan", "/path/to/spartan/spartan.obj");
mySpartan->getTransform().setTranslation(0.0f, 0.5f, 0.0f);
mySpartan->getTransform().setScale(0.5f);
mModels.push_back(mySpartan);
}
```
If we want to make our spartan spin, we need to apply rotation in `update`
```C++
void ExampleScene::update() {
auto mySpartan = Model::getInstance("My spartan");
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto myCube = MeshRenderer::getInstance("My cube");
myCube->getTransform().rotate(-0.75f, 0.0f, 1.0f, 0.0f);
}
```
Other examples can be found in the source files for this example project.
## Build Instructions
Currently, this application requires manual build and installation of Qtk.
In the future, once a release is published, I will be able to use `FetchContent`
or similar cmake functionality to remove this requirement.
For Qtk build instructions, see the README in the root of this repository.
```bash
cmake -S /path/to/qtk/example-app/ -B /path/to/qtk/example-app/build
cmake --build /path/to/qtk/example-app/build
```
If Qtk was not installed system-wide, we can set `QTK_PATH` to point to the
custom installation directory.
```bash
cmake -S /path/to/qtk/example-app/ -B /path/to/qtk/example-app/build -DQTK_PATH=/path/to/qtk/install/
cmake --build /path/to/qtk/example-app/build
```
After this, we can run the example application -
```bash
./path/to/qtk/example-app/build/bin/example
```

View File

@ -0,0 +1,94 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Example Qtk scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include "examplescene.h"
using namespace Qtk;
ExampleScene::ExampleScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) {
setSceneName("Example Scene");
getCamera().getTransform().setTranslation(-8.0f, 0.0f, 10.0f);
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
ExampleScene::~ExampleScene() {}
void ExampleScene::init() {
auto skybox = new Qtk::Skybox("Skybox");
setSkybox(skybox);
auto spartan = new Model(
"spartan", "/home/kapper/Code/qtk/resources/models/spartan/spartan.obj");
addObject(spartan);
auto mesh = addObject(
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ARRAYS)));
mesh->getTransform().setTranslation(-5.0f, 0.0f, -2.0f);
// QTK_DRAW_ARRAYS is the default for generic shapes in qtk/shape.h
addObject(new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ARRAYS)))
->getTransform()
.setTranslation(-7.0f, 0.0f, -2.0f);
mesh = addObject(
new Qtk::MeshRenderer("leftTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(-9.0f, 0.0f, -2.0f);
mesh->setDrawType(GL_LINE_LOOP);
mesh = addObject(
new Qtk::MeshRenderer("topTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(-7.0f, 2.0f, -2.0f);
mesh->getTransform().scale(0.25f);
mesh = addObject(
new Qtk::MeshRenderer("bottomTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(-7.0f, -2.0f, -2.0f);
mesh->getTransform().scale(0.25f);
mesh->setDrawType(GL_LINE_LOOP);
mesh->setColor(GREEN);
}
void ExampleScene::draw() {
Scene::draw();
}
void ExampleScene::update() {
// 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);
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) {
translateX = -translateX;
}
MeshRenderer::getInstance("topTriangle")
->getTransform()
.translate(translateX, 0.0f, 0.0f);
MeshRenderer::getInstance("bottomTriangle")
->getTransform()
.translate(-translateX, 0.0f, 0.0f);
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);
MeshRenderer::getInstance("centerCube")
->getTransform()
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
}

View File

@ -0,0 +1,27 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Example Qtk scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_EXAMPLE_SCENE_H
#define QTK_EXAMPLE_SCENE_H
#include <qtk/scene.h>
class ExampleScene : public Qtk::SceneInterface {
public:
ExampleScene(Qtk::Scene * scene);
~ExampleScene();
void init() override;
void draw() override;
void update() override;
};
#endif // QTK_EXAMPLE_SCENE_H

View File

@ -0,0 +1,57 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Example Qtk widget ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <qtk/scene.h>
#include "examplewidget.h"
ExampleWidget::ExampleWidget(QWidget * parent) :
QOpenGLWidget(parent), mScene(new ExampleScene(new Qtk::SceneEmpty)) {
// NOTE: The decorator pattern is used to save / load scenes in Qtk currently.
// The initializer above sets mScene to the concrete decorator ExampleScene.
// Qtk::SceneEmpty provides an empty scene as the concrete component.
// ExampleScene is defined in client source, deriving Qtk::SceneInterface.
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(4, 6);
format.setSamples(4);
format.setDepthBufferSize(16);
setFormat(format);
setFocusPolicy(Qt::ClickFocus);
}
void ExampleWidget::initializeGL() {
initializeOpenGLFunctions();
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
glEnable(GL_MULTISAMPLE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL);
glDepthRange(0.1f, 1.0f);
glClearDepth(1.0f);
glClearColor(0.0f, 0.25f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void ExampleWidget::resizeGL(int width, int height) {
Qtk::Scene::getProjectionMatrix().setToIdentity();
Qtk::Scene::getProjectionMatrix().perspective(
45.0f, float(width) / float(height), 0.1f, 1000.0f);
}
void ExampleWidget::paintGL() {
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
mScene->draw();
}
void ExampleWidget::update() {
mScene->update();
QWidget::update();
}

View File

@ -0,0 +1,38 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Example Qtk widget ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTKCLIENT_EXAMPLEWIDGET_H
#define QTKCLIENT_EXAMPLEWIDGET_H
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include "examplescene.h"
class ExampleWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT;
public:
explicit ExampleWidget(QWidget * parent = nullptr);
~ExampleWidget() = default;
void initializeGL() override;
void resizeGL(int width, int height) override;
void paintGL() override;
protected slots:
void update();
private:
Qtk::Scene * mScene;
};
#endif // QTKCLIENT_EXAMPLEWIDGET_H

View File

@ -1,23 +1,22 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main window for Qt6 OpenGL widget application ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Example Qt desktop application using Qtk ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_QTKAPI_H
#define QTK_QTKAPI_H
#include <QtCore/QtGlobal>
#include <QApplication>
#include <QMainWindow>
#ifdef QTK_SHARED
#if defined(QTK_EXPORT)
#define QTKAPI Q_DECL_EXPORT
#else
#define QTKAPI Q_DECL_IMPORT
#endif
#else
#define QTKAPI
#endif
#include "examplewidget.h"
#endif // QTK_QTKAPI_H
int main(int argc, char * argv[]) {
QApplication app(argc, argv);
auto window = new QMainWindow;
window->setCentralWidget(new ExampleWidget);
window->show();
app.exec();
}

@ -1 +1 @@
Subproject commit bd64cc88dff17f118ecf32ebcbacaf566f6b6449
Subproject commit eb328ce69dd7b06977aed125e967a41e835b8431

View File

@ -1,81 +0,0 @@
<RCC>
<qresource prefix="/">
<!--3DModel test shader-->
<file alias="model.frag">resources/shaders/fragment/model.frag</file>
<file alias="model.vert">resources/shaders/vertex/model.vert</file>
<!--Phong test shader-->
<file alias="phong.frag">resources/shaders/fragment/phong.frag</file>
<file alias="phong.vert">resources/shaders/vertex/phong.vert</file>
<!--Simple Solid Shader-->
<file alias="solid.frag">resources/shaders/fragment/solid.frag</file>
<file alias="solid.vert">resources/shaders/vertex/solid.vert</file>
<!--Solid Color Shader-->
<file alias="solid-perspective.frag">resources/shaders/fragment/solid-perspective.frag</file>
<file alias="solid-perspective.vert">resources/shaders/vertex/solid-perspective.vert</file>
<!--Multi-color Shader-->
<file alias="multi-color.frag">resources/shaders/fragment/multi-color.frag</file>
<file alias="multi-color.vert">resources/shaders/vertex/multi-color.vert</file>
<!--RGB Normals Shader-->
<file alias="rgb-normals.frag">resources/shaders/fragment/rgb-normals.frag</file>
<file alias="rgb-normals.vert">resources/shaders/vertex/rgb-normals.vert</file>
<!--CubeMap Texture Shader-->
<file alias="texture-cubemap.frag">resources/shaders/fragment/texture-cubemap.frag</file>
<file alias="texture-cubemap.vert">resources/shaders/vertex/texture-cubemap.vert</file>
<!--2D Texture Shader-->
<file alias="texture2d.frag">resources/shaders/fragment/texture2d.frag</file>
<file alias="texture2d.vert">resources/shaders/vertex/texture2d.vert</file>
<!--Ambient Shader-->
<file alias="solid-ambient.frag">resources/shaders/fragment/solid-ambient.frag</file>
<file alias="solid-ambient.vert">resources/shaders/vertex/solid-ambient.vert</file>
<!--Diffuse Shader-->
<file alias="solid-diffuse.frag">resources/shaders/fragment/solid-diffuse.frag</file>
<file alias="solid-diffuse.vert">resources/shaders/vertex/solid-diffuse.vert</file>
<!--Specular Shader-->
<file alias="solid-specular.frag">resources/shaders/fragment/solid-specular.frag</file>
<file alias="solid-specular.vert">resources/shaders/vertex/solid-specular.vert</file>
<!--Basic Phong Shader-->
<file alias="solid-phong.frag">resources/shaders/fragment/solid-phong.frag</file>
<file alias="solid-phong.vert">resources/shaders/vertex/solid-phong.vert</file>
<!--3DModel Basic Shader-->
<file alias="model-basic.frag">resources/shaders/fragment/model-basic.frag</file>
<file alias="model-basic.vert">resources/shaders/vertex/model-basic.vert</file>
<!--3DModel shader with specular mapping-->
<file alias="model-specular.frag">resources/shaders/fragment/model-specular.frag</file>
<file alias="model-specular.vert">resources/shaders/vertex/model-specular.vert</file>
<!--3DModel shader with normal mapping-->
<file alias="model-normals.frag">resources/shaders/fragment/model-normals.frag</file>
<file alias="model-normals.vert">resources/shaders/vertex/model-normals.vert</file>
<!-- Skybox Shaders-->
<file alias="skybox.frag">resources/skybox/skybox.frag</file>
<file alias="skybox.vert">resources/skybox/skybox.vert</file>
<!--Texture Images-->
<file alias="crate.png">resources/images/crate.png</file>
<file alias="stone.png">resources/images/stone.png</file>
<file alias="wood.png">resources/images/wood.png</file>
<!-- Skybox Images-->
<file alias="back.png">resources/skybox/back.png</file>
<file alias="bottom.png">resources/skybox/bottom.png</file>
<file alias="front.png">resources/skybox/front.png</file>
<file alias="left.png">resources/skybox/left.png</file>
<file alias="right.png">resources/skybox/right.png</file>
<file alias="top.png">resources/skybox/top.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,165 @@
Fonticons, Inc. (https://fontawesome.com)
--------------------------------------------------------------------------------
Font Awesome Free License
Font Awesome Free is free, open source, and GPL friendly. You can use it for
commercial projects, open source projects, or really almost whatever you want.
Full Font Awesome Free license: https://fontawesome.com/license/free.
--------------------------------------------------------------------------------
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
The Font Awesome Free download is licensed under a Creative Commons
Attribution 4.0 International License and applies to all icons packaged
as SVG and JS file types.
--------------------------------------------------------------------------------
# Fonts: SIL OFL 1.1 License
In the Font Awesome Free download, the SIL OFL license applies to all icons
packaged as web and desktop font files.
Copyright (c) 2022 Fonticons, Inc. (https://fontawesome.com)
with Reserved Font Name: "Font Awesome".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
SIL OPEN FONT LICENSE
Version 1.1 - 26 February 2007
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting — in part or in whole — any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
--------------------------------------------------------------------------------
# Code: MIT License (https://opensource.org/licenses/MIT)
In the Font Awesome Free download, the MIT license applies to all non-font and
non-icon files.
Copyright 2022 Fonticons, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in the
Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
# Attribution
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
Awesome Free files already contain embedded comments with sufficient
attribution, so you shouldn't need to do anything additional when using these
files normally.
We've kept attribution comments terse, so we ask that you do not actively work
to remove them from files, especially code. They're a great way for folks to
learn about Font Awesome.
--------------------------------------------------------------------------------
# Brand Icons
All brand icons are trademarks of their respective owners. The use of these
trademarks does not indicate endorsement of the trademark holder by Font
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
to represent the company, product, or service to which they refer.**

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M439.55 236.05L244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>

After

Width:  |  Height:  |  Size: 749 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M224 256c-35.2 0-64 28.8-64 64c0 35.2 28.8 64 64 64c35.2 0 64-28.8 64-64C288 284.8 259.2 256 224 256zM433.1 129.1l-83.9-83.9C341.1 37.06 328.8 32 316.1 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h320c35.35 0 64-28.65 64-64V163.9C448 151.2 442.9 138.9 433.1 129.1zM128 80h144V160H128V80zM400 416c0 8.836-7.164 16-16 16H64c-8.836 0-16-7.164-16-16V96c0-8.838 7.164-16 16-16h16v104c0 13.25 10.75 24 24 24h192C309.3 208 320 197.3 320 184V83.88l78.25 78.25C399.4 163.2 400 164.8 400 166.3V416z"/></svg>

After

Width:  |  Height:  |  Size: 789 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M572.6 270.3l-96 192C471.2 473.2 460.1 480 447.1 480H64c-35.35 0-64-28.66-64-64V96c0-35.34 28.65-64 64-64h117.5c16.97 0 33.25 6.742 45.26 18.75L275.9 96H416c35.35 0 64 28.66 64 64v32h-48V160c0-8.824-7.178-16-16-16H256L192.8 84.69C189.8 81.66 185.8 80 181.5 80H64C55.18 80 48 87.18 48 96v288l71.16-142.3C124.6 230.8 135.7 224 147.8 224h396.2C567.7 224 583.2 249 572.6 270.3z"/></svg>

After

Width:  |  Height:  |  Size: 664 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M160 400C160 408.8 152.8 416 144 416C135.2 416 128 408.8 128 400V192C128 183.2 135.2 176 144 176C152.8 176 160 183.2 160 192V400zM240 400C240 408.8 232.8 416 224 416C215.2 416 208 408.8 208 400V192C208 183.2 215.2 176 224 176C232.8 176 240 183.2 240 192V400zM320 400C320 408.8 312.8 416 304 416C295.2 416 288 408.8 288 400V192C288 183.2 295.2 176 304 176C312.8 176 320 183.2 320 192V400zM317.5 24.94L354.2 80H424C437.3 80 448 90.75 448 104C448 117.3 437.3 128 424 128H416V432C416 476.2 380.2 512 336 512H112C67.82 512 32 476.2 32 432V128H24C10.75 128 0 117.3 0 104C0 90.75 10.75 80 24 80H93.82L130.5 24.94C140.9 9.357 158.4 0 177.1 0H270.9C289.6 0 307.1 9.358 317.5 24.94H317.5zM151.5 80H296.5L277.5 51.56C276 49.34 273.5 48 270.9 48H177.1C174.5 48 171.1 49.34 170.5 51.56L151.5 80zM80 432C80 449.7 94.33 464 112 464H336C353.7 464 368 449.7 368 432V128H80V432z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M234.5 5.7c13.9-5 29.1-5 43.1 0l192 68.6C495 83.4 512 107.5 512 134.6V377.4c0 27-17 51.2-42.5 60.3l-192 68.6c-13.9 5-29.1 5-43.1 0l-192-68.6C17 428.6 0 404.5 0 377.4V134.6c0-27 17-51.2 42.5-60.3l192-68.6zM256 66L82.3 128 256 190l173.7-62L256 66zm32 368.6l160-57.1v-188L288 246.6v188z"/></svg>

After

Width:  |  Height:  |  Size: 574 B

View File

@ -0,0 +1,13 @@
```bash
sudo apt install icnsutils
```
```bash
convert icon.png -resize 32x32 kilroy_32.png
convert icon.png -resize 16x16 kilroy_16.png
convert icon.png -resize 48x48 kilroy_48.png
convert icon.png -resize 128x128 kilroy_128.png
convert icon.png -resize 256x256 kilroy_256.png
png2icns png2icns kilroy.icns kilroy_*.png
```

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,5 +1,5 @@
# Created by RapidCompact v4.3.2 | www.rapidcompact.com
mtllib rapid.mtl
mtllib bird.mtl
o mesh_0
v 290.631131412626928068 748.484123578408002686 -224.342122210487644907
v 282.356783853739386814 751.945844816198814442 -222.136356657249308455

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

View File

@ -1,16 +0,0 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl Lion
Ns 500.000001
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.800000 0.800000 0.800000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Bump normal.jpg
map_Kd diffuse.jpg
map_Ns roughness.jpg
refl specular.jpg

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

130
resources/resources.qrc Normal file
View File

@ -0,0 +1,130 @@
<RCC>
<qresource prefix="/textures">
<file alias="crate.png">images/crate.png</file>
<file alias="stone.png">images/stone.png</file>
<file alias="wood.png">images/wood.png</file>
<file>skybox/back.png</file>
<file>skybox/bottom.png</file>
<file>skybox/front.png</file>
<file>skybox/left.png</file>
<file>skybox/right.png</file>
<file>skybox/top.png</file>
</qresource>
<qresource prefix="/icons">
<file>fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</file>
<file>fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</file>
<file>fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</file>
<file>fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</file>
<file>fontawesome-free-6.2.1-desktop/svgs/brands/git-alt.svg</file>
<file>icon.png</file>
</qresource>
<qresource prefix="/shaders">
<file alias="model.frag">shaders/fragment/model.frag</file>
<file alias="model.vert">shaders/vertex/model.vert</file>
<file alias="phong.frag">shaders/fragment/phong.frag</file>
<file alias="phong.vert">shaders/vertex/phong.vert</file>
<file alias="solid.frag">shaders/fragment/solid.frag</file>
<file alias="solid.vert">shaders/vertex/solid.vert</file>
<file alias="solid-perspective.frag">shaders/fragment/solid-perspective.frag</file>
<file alias="solid-perspective.vert">shaders/vertex/solid-perspective.vert</file>
<file alias="multi-color.frag">shaders/fragment/multi-color.frag</file>
<file alias="multi-color.vert">shaders/vertex/multi-color.vert</file>
<file alias="rgb-normals.frag">shaders/fragment/rgb-normals.frag</file>
<file alias="rgb-normals.vert">shaders/vertex/rgb-normals.vert</file>
<file alias="texture-cubemap.frag">shaders/fragment/texture-cubemap.frag</file>
<file alias="texture-cubemap.vert">shaders/vertex/texture-cubemap.vert</file>
<file alias="texture2d.frag">shaders/fragment/texture2d.frag</file>
<file alias="texture2d.vert">shaders/vertex/texture2d.vert</file>
<file alias="solid-ambient.frag">shaders/fragment/solid-ambient.frag</file>
<file alias="solid-ambient.vert">shaders/vertex/solid-ambient.vert</file>
<file alias="solid-diffuse.frag">shaders/fragment/solid-diffuse.frag</file>
<file alias="solid-diffuse.vert">shaders/vertex/solid-diffuse.vert</file>
<file alias="solid-specular.frag">shaders/fragment/solid-specular.frag</file>
<file alias="solid-specular.vert">shaders/vertex/solid-specular.vert</file>
<file alias="solid-phong.frag">shaders/fragment/solid-phong.frag</file>
<file alias="solid-phong.vert">shaders/vertex/solid-phong.vert</file>
<file alias="model-basic.frag">shaders/fragment/model-basic.frag</file>
<file alias="model-basic.vert">shaders/vertex/model-basic.vert</file>
<file alias="model-specular.frag">shaders/fragment/model-specular.frag</file>
<file alias="model-specular.vert">shaders/vertex/model-specular.vert</file>
<file alias="model-normals.frag">shaders/fragment/model-normals.frag</file>
<file alias="model-normals.vert">shaders/vertex/model-normals.vert</file>
<file alias="skybox.frag">skybox/skybox.frag</file>
<file alias="skybox.vert">skybox/skybox.vert</file>
</qresource>
<qresource prefix="/models">
<file>models/spartan/spartan.obj</file>
<file>models/alien-hominid/alien.obj</file>
<file>models/spartan/spartan.mtl</file>
<file>models/spartan/Spartan_Ears_Mat_BaseColor.png</file>
<file>models/spartan/Spartan_Undersuit_Mat_Specular.png</file>
<file>models/spartan/Spartan_Undersuit_Mat_Roughness.png</file>
<file>models/spartan/Spartan_Undersuit_Mat_Normal.png</file>
<file>models/spartan/Spartan_Undersuit_Mat_Metallic.png</file>
<file>models/spartan/Spartan_Undersuit_Mat_BaseColor.png</file>
<file>models/spartan/Spartan_Undersuit_Mat_AO.png</file>
<file>models/spartan/Spartan_Shoulder_Mat_Specular.png</file>
<file>models/spartan/Spartan_Shoulder_Mat_Roughness.png</file>
<file>models/spartan/Spartan_Shoulder_Mat_Normal.png</file>
<file>models/spartan/Spartan_Shoulder_Mat_Metallic.png</file>
<file>models/spartan/Spartan_Shoulder_Mat_BaseColor.png</file>
<file>models/spartan/Spartan_Shoulder_Mat_AO.png</file>
<file>models/spartan/Spartan_Legs_Mat_Specular.png</file>
<file>models/spartan/Spartan_Legs_Mat_Roughness.png</file>
<file>models/spartan/Spartan_Legs_Mat_Normal.png</file>
<file>models/spartan/Spartan_Legs_Mat_Metallic.png</file>
<file>models/spartan/Spartan_Legs_Mat_BaseColor.png</file>
<file>models/spartan/Spartan_Legs_Mat_AO.png</file>
<file>models/spartan/Spartan_Helmet_Mat_Specular.png</file>
<file>models/spartan/Spartan_Helmet_Mat_Roughness.png</file>
<file>models/spartan/Spartan_Helmet_Mat_Normal.png</file>
<file>models/spartan/Spartan_Helmet_Mat_Metallic.png</file>
<file>models/spartan/Spartan_Helmet_Mat_BaseColor.png</file>
<file>models/spartan/Spartan_Helmet_Mat_AO.png</file>
<file>models/spartan/Spartan_Ears_Mat_Specular.png</file>
<file>models/spartan/Spartan_Ears_Mat_Roughness.png</file>
<file>models/spartan/Spartan_Ears_Mat_Normal.png</file>
<file>models/spartan/Spartan_Ears_Mat_Metallic.png</file>
<file>models/spartan/Spartan_Ears_Mat_AO.png</file>
<file>models/spartan/Spartan_Chest_Mat_Specular.png</file>
<file>models/spartan/Spartan_Chest_Mat_Roughness.png</file>
<file>models/spartan/Spartan_Chest_Mat_Normal.png</file>
<file>models/spartan/Spartan_Chest_Mat_Metallic.png</file>
<file>models/spartan/Spartan_Chest_Mat_BaseColor.png</file>
<file>models/spartan/Spartan_Chest_Mat_AO.png</file>
<file>models/spartan/Spartan_Arms_Mat_Specular.png</file>
<file>models/spartan/Spartan_Arms_Mat_Roughness.png</file>
<file>models/spartan/Spartan_Arms_Mat_Normal.png</file>
<file>models/spartan/Spartan_Arms_Mat_Metallic.png</file>
<file>models/spartan/Spartan_Arms_Mat_BaseColor.png</file>
<file>models/spartan/Spartan_Arms_Mat_AO.png</file>
<file>models/spartan/lambert1_Roughness.png</file>
<file>models/spartan/lambert1_Nrm.png</file>
<file>models/spartan/lambert1_Colour-Opacity.png</file>
<file>models/backpack/specular.jpg</file>
<file>models/backpack/roughness.jpg</file>
<file>models/backpack/diffuse.jpg</file>
<file>models/backpack/normal.png</file>
<file>models/backpack/backpack.obj</file>
<file>models/backpack/backpack.mtl</file>
<file>models/backpack/ao.jpg</file>
<file>models/alien-hominid/specular.png</file>
<file>models/alien-hominid/roughness.png</file>
<file>models/alien-hominid/normal.png</file>
<file>models/alien-hominid/diffuse.png</file>
<file>models/alien-hominid/blaster_specular.png</file>
<file>models/alien-hominid/blaster_roughness.png</file>
<file>models/alien-hominid/blaster_normal.png</file>
<file>models/alien-hominid/blaster_emissive.png</file>
<file>models/alien-hominid/blaster_diffuse.png</file>
<file>models/alien-hominid/alien.mtl</file>
<file>models/scythe/scythe.obj</file>
<file>models/scythe/scythe.mtl</file>
<file>models/scythe/diffuse.png</file>
<file>models/bird/occlusion.jpg</file>
<file>models/bird/normal.jpg</file>
<file>models/bird/diffuse.jpg</file>
<file>models/bird/bird.obj</file>
<file>models/bird/bird.mtl</file>
</qresource>
</RCC>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

After

Width:  |  Height:  |  Size: 316 KiB

165
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,165 @@
################################################################################
## 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 ##
################################################################################
# Qtk Library
# We always build libqtk since the plugins and GUI both depend on it.
add_subdirectory(qtk)
install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
COMPONENT libqtk
DESTINATION lib/cmake/${PROJECT_NAME}
)
install(
EXPORT qtk_export
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
COMPONENT libqtk
DESTINATION lib/cmake/${PROJECT_NAME}
)
# System install for qtk_library
install(
TARGETS qtk_library
# Associate qtk_library target with qtk-export
EXPORT qtk_export
COMPONENT libqtk
FILE_SET HEADERS DESTINATION include
INCLUDES DESTINATION include
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static
RUNTIME DESTINATION bin
)
# Qtk Application
if(QTK_BUILD_GUI OR QTK_INSTALL_PLUGINS)
add_subdirectory(app)
endif()
if(QTK_INSTALL_PLUGINS)
# Optionally install custom Qtk plugins for Qt Designer.
install(
TARGETS qtk_library qtk_plugin_library
COMPONENT collection
LIBRARY DESTINATION "${QTK_PLUGIN_LIBRARY_DIR}"
ARCHIVE DESTINATION "${QTK_PLUGIN_LIBRARY_DIR}"
RUNTIME DESTINATION "${QTK_PLUGIN_LIBRARY_DIR}"
)
install(
TARGETS qtk_collection
COMPONENT collection
LIBRARY DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
ARCHIVE DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
RUNTIME DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
)
endif()
if(QTK_BUILD_GUI)
install(
TARGETS qtk_app
COMPONENT qtk
BUNDLE DESTINATION .
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static
RUNTIME DESTINATION bin
)
qt_generate_deploy_app_script(
TARGET qtk_app
OUTPUT_SCRIPT QTK_DEPLOY_SCRIPT
NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${QTK_DEPLOY_SCRIPT} COMPONENT qtk)
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_app.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()
endif()
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
COMPATIBILITY AnyNewerVersion
)
configure_package_config_file(
"${CMAKE_SOURCE_DIR}/cmake/templates/Config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}
)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
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")
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")
# Remove any assimp components if defined by submodule.
if (QTK_UPDATE_SUBMODULES)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(FILTER CPACK_COMPONENTS_ALL EXCLUDE REGEX .*assimp.*)
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_app.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")
# Debian
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PACKAGE_HOMEPAGE_URL})
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
# OSX
set(CPACK_BUNDLE_NAME ${PROJECT_NAME})
set(CPACK_BUNDLE_PLIST $<TARGET_BUNDLE_CONTENT_DIR:qtk_app>/Info.plist)
set(CPACK_BUNDLE_ICON ${QTK_OSX_ICONS})
# Platform defaults for source bundles.
if(WIN32)
set(CPACK_SOURCE_GENERATOR ZIP)
else()
set(CPACK_SOURCE_GENERATOR TGZ)
endif()
include(CPack)

71
src/app/CMakeLists.txt Normal file
View File

@ -0,0 +1,71 @@
################################################################################
## 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 ##
################################################################################
################################################################################
# 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 Collection Plugin
################################################################################
# Create a Qt Designer plugin for a collection of widgets from our library.
qt_add_plugin(qtk_collection SHARED)
target_sources(
qtk_collection PRIVATE
widgetplugincollection.cpp widgetplugincollection.h
widgetplugin.cpp widgetplugin.h
)
target_link_libraries(qtk_collection PUBLIC qtk_plugin_library)
################################################################################
# Final Qtk Application
################################################################################
set(
QTK_APP_SOURCES
qtkscene.cpp qtkscene.h
main.cpp
)
qt_add_executable(qtk_app ${QTK_APP_SOURCES})
target_link_libraries(qtk_app PRIVATE qtk_plugin_library)
set_target_properties(
qtk_app PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_NAME Qtk
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_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
)

33
src/app/debugconsole.cpp Normal file
View File

@ -0,0 +1,33 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Debug console for qtk views ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <QMainWindow>
#include <QWindow>
#include "debugconsole.h"
#include "ui_debugconsole.h"
using namespace Qtk;
DebugConsole::DebugConsole(QWidget * owner, const QString & key) :
DebugConsole(owner, key, key + "Debugger") {}
DebugConsole::DebugConsole(
QWidget * owner, const QString & key, const QString & name) {
ui_ = new Ui::DebugConsole;
ui_->setupUi(this);
setObjectName(name);
mConsole = ui_->textEdit;
setWidget(mConsole);
setWindowTitle(name + " Debug Console");
auto qtkWidget = dynamic_cast<QtkWidget *>(owner);
if(qtkWidget) {
connect(qtkWidget, &QtkWidget::sendLog, this, &DebugConsole::sendLog);
}
}

140
src/app/debugconsole.h Normal file
View File

@ -0,0 +1,140 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Debug console for qtk views ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_DEBUGCONSOLE_H
#define QTK_DEBUGCONSOLE_H
#include <QApplication>
#include <QDockWidget>
#include <QPlainTextEdit>
#include <QVBoxLayout>
#include "qtkwidget.h"
namespace Ui {
class DebugConsole;
}
namespace Qtk {
class DebugConsole : public QDockWidget {
Q_OBJECT;
public:
/**
* Construct a new DebugConsole.
* Assigns a default name to the console using `key + "Debugger"`
*
* @param owner Parent widget for this console or nullptr if no parent.
* If this parameter inherits from QMainWindow we will add this dock
* widget to the window.
* @param key The objectName associated with the attached QtkWidget.
*/
DebugConsole(QWidget * owner, const QString & key);
/**
* Construct a new DebugConsole.
*
* @param owner Parent widget for this console or nullptr if no parent.
* If this parameter inherits from QMainWindow we will add this dock
* widget to the window.
* @param key The objectName associated with the attached QtkWidget.
* @param name The objectName to associate with this DebugConsole.
*/
DebugConsole(QWidget * owner, const QString & key, const QString & name);
~DebugConsole() = default;
public slots:
/*************************************************************************
* Public Qt slots
************************************************************************/
/**
* Log a message to the DebugConsole text view.
*
* @param message The message to log.
* @param context The DebugContext to use for the message.
* Default value is Status.
*/
inline void sendLog(QString message, DebugContext context = Status) {
mConsole->setTextColor(logColor(context));
mConsole->append(logPrefix(message, context));
}
/**
* Sets the window title for the DebugConsole. This will appear in the
* widget title bar and within any context menu actions.
*
* @param name Base name for the DebugConsole window.
*/
inline void setTitle(QString name) {
setWindowTitle(name + " Debug Console");
}
private:
/**
* @param context Log context severity level.
* @return QColor corresponding with the message context.
*/
[[nodiscard]] QColor logColor(const DebugContext & context) const {
switch(context) {
case Status:
return Qt::GlobalColor::darkGray;
case Debug:
return Qt::GlobalColor::white;
case Warn:
return Qt::GlobalColor::yellow;
case Error:
return Qt::GlobalColor::red;
case Fatal:
return Qt::GlobalColor::magenta;
default:
return Qt::GlobalColor::darkYellow;
}
}
/**
* Prefixes a log message to add context level.
*
* @param message The message to prefix.
* @param context The log context severity level.
* @return The log message prefixed with the DebugContext level.
*/
[[nodiscard]] QString logPrefix(
QString & message, const DebugContext & context) {
QString prefix;
switch(context) {
case Status:
prefix = "[Status]: ";
break;
case Debug:
prefix = "[Debug]: ";
break;
case Warn:
prefix = "[Warn]: ";
break;
case Error:
prefix = "[Error]: ";
break;
case Fatal:
prefix = "[Fatal]: ";
break;
default:
prefix = "[No Context]: ";
break;
}
message = prefix + message.replace("\n", "\t\n" + prefix);
return message;
}
Ui::DebugConsole * ui_;
QTextEdit * mConsole;
};
} // namespace Qtk
#endif // QTK_DEBUGCONSOLE_H

33
src/app/debugconsole.ui Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DebugConsole</class>
<widget class="QDockWidget" name="DebugConsole">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Debug Console</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTextEdit" name="textEdit">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,24 +1,28 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Main program for practice using Qt6 widgets and OpenGL ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <QApplication>
#include <QLabel>
#include <mainwindow.h>
#include <qtkwidget.h>
#include <QSurfaceFormat>
#include "qtkmainwindow.h"
#include "qtkscene.h"
int main(int argc, char * argv[]) {
Q_INIT_RESOURCE(resources);
QApplication a(argc, argv);
// Create window for Qt application using custom mainwindow.h
MainWindow w;
w.show();
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));
window->show();
return QApplication::exec();
}

80
src/app/qtkmainwindow.cpp Normal file
View File

@ -0,0 +1,80 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: MainWindow for Qtk application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include "qtkmainwindow.h"
#include "qtkscene.h"
#include "ui_qtkmainwindow.h"
MainWindow * MainWindow::mainWindow_ = Q_NULLPTR;
/*******************************************************************************
* Constructors / Destructors
******************************************************************************/
MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
ui_ = new Ui::MainWindow;
setObjectName("MainWindow");
// For use in design mode using Qt Creator
// + We can use the `ui` member to access nested widgets by name
ui_->setupUi(this);
ui_->menuView->addAction(ui_->toolBar->toggleViewAction());
// Initialize static container for all active QtkWidgets
auto qtkWidgets = findChildren<Qtk::QtkWidget *>();
for(auto & qtkWidget : qtkWidgets) {
qtkWidget->setScene(new Qtk::SceneEmpty);
views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget);
ui_->menuView->addAction(qtkWidget->getActionToggleConsole());
connect(
qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this,
&MainWindow::refreshScene);
}
auto docks = findChildren<QDockWidget *>();
for(auto & dock : docks) {
addDockWidget(Qt::RightDockWidgetArea, dock);
ui_->menuView->addAction(dock->toggleViewAction());
}
// Set the window icon used for Qtk.
setWindowIcon(Qtk::getIcon());
}
MainWindow::~MainWindow() {
delete ui_;
}
/*******************************************************************************
* Public Methods
******************************************************************************/
MainWindow * MainWindow::getMainWindow() {
if(mainWindow_ == Q_NULLPTR) {
mainWindow_ = new MainWindow;
}
return mainWindow_;
}
Qtk::QtkWidget * MainWindow::getQtkWidget(int64_t index) {
if(views_.size() <= index) {
return Q_NULLPTR;
}
return views_.begin(index)->second;
}
Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name) {
if(!views_.count(name)) {
return Q_NULLPTR;
}
return views_[name];
}
void MainWindow::refreshScene(QString sceneName) {
// TODO: Select TreeView using sceneName>
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene());
}

92
src/app/qtkmainwindow.h Normal file
View File

@ -0,0 +1,92 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: MainWindow for Qtk application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <unordered_map>
#include <QMainWindow>
#include <QPlainTextEdit>
#include "debugconsole.h"
#include "qtkwidget.h"
namespace Ui {
class MainWindow;
}
/**
* MainWindow class to provide an example of using a QtkWidget within a Qt
* window application.
*/
class MainWindow : public QMainWindow {
Q_OBJECT
public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
/**
* This ctor also initializes the Scene for each QtkWidget in the window.
* To load a different scene this would need to be updated.
*
* @param parent The parent for this QMainWindow
*/
explicit MainWindow(QWidget * parent = nullptr);
~MainWindow() override;
/***************************************************************************
* Public Methods
**************************************************************************/
/**
* Allows widgets to retrieve an instance of this root QMainWindow.
* @return this
*/
static MainWindow * getMainWindow();
Qtk::QtkWidget * getQtkWidget(int64_t index = 0);
/**
* Accessor for retrieving a QtkWidget by it's objectName.
* This function will not construct a new QtkWidget if none is found.
*
* @param name The objectName associated with the QtkWidget.
* @return Pointer to an active QtkWidget or Q_NULLPTR is not found.
*/
Qtk::QtkWidget * getQtkWidget(const QString & name);
public slots:
/**
* Trigger a refresh for widgets related to a scene that has been updated.
* @param sceneName The name of the scene that has been modified.
*/
void refreshScene(QString sceneName);
private:
/***************************************************************************
* Private Members
**************************************************************************/
/** Do not allow copying */
MainWindow(const MainWindow &) {};
Ui::MainWindow * ui_ {};
static MainWindow * mainWindow_;
/**
* Maps a scene name to the QtkWidget viewing it.
* TODO: Value should be a vector of QtkWidget * for multiple scene views.
*/
std::unordered_map<QString, Qtk::QtkWidget *> views_ {};
};
#endif // MAINWINDOW_H

342
src/app/qtkmainwindow.ui Normal file
View File

@ -0,0 +1,342 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>824</width>
<height>601</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Qtk - MainWindow</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>../resources/icon.png</normaloff>../resources/icon.png</iconset>
</property>
<property name="unifiedTitleAndToolBarOnMac">
<bool>true</bool>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="movable">
<bool>true</bool>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>View 1</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="Qtk::QtkWidget" name="qtk::QtkWidget">
<property name="toolTip">
<string>A custom widget tool tip.</string>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>View 2</string>
</attribute>
</widget>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="Qtk::TreeView" name="qtk::TreeView">
<property name="toolTip">
<string>A custom widget tool tip.</string>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
</property>
</widget>
</item>
<item>
<widget class="Qtk::ToolBox" name="qtk::ToolBox">
<property name="toolTip">
<string>A custom widget tool tip.</string>
</property>
<property name="whatsThis">
<string>Custom widget what's this?</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>824</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuTest">
<property name="title">
<string>File</string>
</property>
<addaction name="actionNew"/>
<addaction name="actionOpen"/>
<addaction name="separator"/>
<addaction name="actionSave"/>
<addaction name="actionSave_as"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
<widget class="QMenu" name="menuTab_Position">
<property name="title">
<string>Tab Position</string>
</property>
<addaction name="actionTop"/>
<addaction name="actionBottom"/>
<addaction name="actionLeft"/>
<addaction name="actionRight"/>
</widget>
<addaction name="menuTab_Position"/>
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>Edit</string>
</property>
<addaction name="actionUndo"/>
<addaction name="actionRedo"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuTest"/>
<addaction name="menuEdit"/>
<addaction name="menuView"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="movable">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
<property name="floatable">
<bool>true</bool>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionLoad_Model"/>
<addaction name="actionDelete_Object"/>
</widget>
<action name="actionOpen">
<property name="icon">
<iconset resource="../../resources/resources.qrc">
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</iconset>
</property>
<property name="text">
<string>Open...</string>
</property>
</action>
<action name="actionSave">
<property name="icon">
<iconset resource="../../resources/resources.qrc">
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</iconset>
</property>
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionSave_as">
<property name="text">
<string>Save as...</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionShow_Console">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show Console</string>
</property>
</action>
<action name="actionLoad_Model">
<property name="icon">
<iconset resource="../../resources/resources.qrc">
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</iconset>
</property>
<property name="text">
<string>Load Model</string>
</property>
<property name="font">
<font/>
</property>
</action>
<action name="actionDelete_Object">
<property name="icon">
<iconset resource="../../resources/resources.qrc">
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</iconset>
</property>
<property name="text">
<string>Delete Object</string>
</property>
</action>
<action name="actionNew">
<property name="text">
<string>New</string>
</property>
</action>
<action name="actionTop">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Top</string>
</property>
</action>
<action name="actionBottom">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Bottom</string>
</property>
</action>
<action name="actionLeft">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Left</string>
</property>
</action>
<action name="actionRight">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Right</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About</string>
</property>
</action>
<action name="actionNested_Widgets">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Nested Widgets</string>
</property>
</action>
<action name="actionUndo">
<property name="text">
<string>Undo</string>
</property>
</action>
<action name="actionRedo">
<property name="text">
<string>Redo</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>Qtk::QtkWidget</class>
<extends>QOpenGLWidget</extends>
<header>qtkwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>Qtk::TreeView</class>
<extends>QDockWidget</extends>
<header>treeview.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>Qtk::ToolBox</class>
<extends>QDockWidget</extends>
<header>toolbox.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../resources/resources.qrc"/>
</resources>
<connections>
<connection>
<sender>actionExit</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>411</x>
<y>300</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -1,18 +1,12 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Classes for managing objects and data within a scene ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Example Qtk scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <camera3d.h>
#include <examplescene.h>
#include <meshrenderer.h>
#include <model.h>
#include <resourcemanager.h>
#include <scene.h>
#include <texture.h>
#include "qtkscene.h"
using namespace Qtk;
@ -20,104 +14,96 @@ using namespace Qtk;
* Constructors, Destructors
******************************************************************************/
ExampleScene::ExampleScene() {
QtkScene::QtkScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) {
setSceneName("Qtk Scene");
getCamera().getTransform().setTranslation(0.0f, 0.0f, 20.0f);
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
ExampleScene::~ExampleScene() {
QtkScene::~QtkScene() {
delete mTestPhong;
delete mTestSpecular;
delete mTestDiffuse;
delete mTestAmbient;
for(auto & mesh : mMeshes) {
delete mesh;
}
for(auto & model : mModels) {
delete model;
}
delete mSkybox;
}
/*******************************************************************************
* Public Member Functions
******************************************************************************/
void ExampleScene::init() {
void QtkScene::init() {
// Add a skybox to the scene using default cube map images and settings.
setSkybox(new Qtk::Skybox("Skybox"));
/* Create a red cube with a mini master chief on top. */
auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS));
myCube->setColor(RED);
mMeshes.push_back(myCube);
addObject(myCube);
auto mySpartan = new Model("My spartan", ":/models/spartan/spartan.obj");
auto mySpartan =
new Model("My spartan", ":/models/models/spartan/spartan.obj");
mySpartan->getTransform().setTranslation(0.0f, 0.5f, 0.0f);
mySpartan->getTransform().setScale(0.5f);
mModels.push_back(mySpartan);
addObject(mySpartan);
//
// Create simple shapes using MeshRenderer class and data in mesh.h
mMeshes.push_back(
auto mesh = addObject(
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-5.0f, 0.0f, -2.0f);
mesh->getTransform().setTranslation(-5.0f, 0.0f, -2.0f);
mMeshes.push_back(
new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-7.0f, 0.0f, -2.0f);
mesh =
addObject(new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(-7.0f, 0.0f, -2.0f);
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("leftTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-9.0f, 0.0f, -2.0f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
mesh->getTransform().setTranslation(-9.0f, 0.0f, -2.0f);
mesh->setDrawType(GL_LINE_LOOP);
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("topTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-7.0f, 2.0f, -2.0f);
mMeshes.back()->getTransform().scale(0.25f);
mesh->getTransform().setTranslation(-7.0f, 2.0f, -2.0f);
mesh->getTransform().scale(0.25f);
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("bottomTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-7.0f, -2.0f, -2.0f);
mMeshes.back()->getTransform().scale(0.25f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
mMeshes.back()->setColor(GREEN);
mesh->getTransform().setTranslation(-7.0f, -2.0f, -2.0f);
mesh->getTransform().scale(0.25f);
mesh->setDrawType(GL_LINE_LOOP);
mesh->setColor(GREEN);
//
// 3D Model loading
mModels.push_back(
new Qtk::Model("backpack", ":/models/backpack/backpack.obj"));
auto model = addObject(
new Qtk::Model("backpack", ":/models/models/backpack/backpack.obj"));
// Sometimes model textures need flipped in certain directions
mModels.back()->flipTexture("diffuse.jpg", false, true);
mModels.back()->getTransform().setTranslation(0.0f, 0.0f, -10.0f);
model->flipTexture("diffuse.jpg", false, true);
model->getTransform().setTranslation(0.0f, 0.0f, -10.0f);
mModels.push_back(new Qtk::Model("bird", ":/models/bird/bird.obj"));
mModels.back()->getTransform().setTranslation(2.0f, 2.0f, -10.0f);
model = addObject(new Qtk::Model("bird", ":/models/models/bird/bird.obj"));
model->getTransform().setTranslation(2.0f, 2.0f, -10.0f);
// Sometimes the models are very large
mModels.back()->getTransform().scale(0.0025f);
mModels.back()->getTransform().rotate(-110.0f, 0.0f, 1.0f, 0.0f);
model->getTransform().scale(0.0025f);
model->getTransform().rotate(-110.0f, 0.0f, 1.0f, 0.0f);
mModels.push_back(new Qtk::Model("lion", ":/models/lion/lion.obj"));
mModels.back()->getTransform().setTranslation(-3.0f, -1.0f, -10.0f);
mModels.back()->getTransform().scale(0.15f);
model = addObject(
new Qtk::Model("alien", ":/models/models/alien-hominid/alien.obj"));
model->getTransform().setTranslation(2.0f, -1.0f, -5.0f);
model->getTransform().scale(0.15f);
mModels.push_back(
new Qtk::Model("alien", ":/models/alien-hominid/alien.obj"));
mModels.back()->getTransform().setTranslation(2.0f, -1.0f, -5.0f);
mModels.back()->getTransform().scale(0.15f);
model = addObject(
new Qtk::Model("My scythe", ":/models/models/scythe/scythe.obj"));
model->getTransform().setTranslation(-6.0f, 0.0f, -10.0f);
model->getTransform().rotate(-90.0f, 1.0f, 0.0f, 0.0f);
model->getTransform().rotate(90.0f, 0.0f, 1.0f, 0.0f);
mModels.push_back(new Qtk::Model("scythe", ":/models/scythe/scythe.obj"));
mModels.back()->getTransform().setTranslation(-6.0f, 0.0f, -10.0f);
mModels.back()->getTransform().rotate(-90.0f, 1.0f, 0.0f, 0.0f);
mModels.back()->getTransform().rotate(90.0f, 0.0f, 1.0f, 0.0f);
mModels.push_back(
new Qtk::Model("masterChief", ":/models/spartan/spartan.obj"));
mModels.back()->getTransform().setTranslation(-1.5f, 0.5f, -2.0f);
model = addObject(
new Qtk::Model("masterChief", ":/models/models/spartan/spartan.obj"));
model->getTransform().setTranslation(-1.5f, 0.5f, -2.0f);
//
@ -129,7 +115,8 @@ void ExampleScene::init() {
// 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(":/solid-phong.vert", ":/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,
@ -147,23 +134,24 @@ void ExampleScene::init() {
// Phong lighting example light source. This is just for visual reference.
// + We refer to the position of this object in draw() to update lighting.
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("phongLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(3.0f, 2.0f, -2.0f);
mMeshes.back()->getTransform().scale(0.25f);
mesh->getTransform().setTranslation(3.0f, 2.0f, -2.0f);
mesh->getTransform().scale(0.25f);
/* Example of a cube with no lighting applied */
mMeshes.push_back(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, -2.0f);
mMeshes.back()->setShaders(
":/solid-perspective.vert", ":/solid-perspective.frag");
mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mesh = addObject(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(5.0f, 0.0f, -2.0f);
mesh->setShaders(
":/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->getTransform().setTranslation(7.0f, 0.0f, -2.0f);
mTestAmbient->setShaders(":/solid-ambient.vert", ":/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));
@ -174,22 +162,24 @@ void ExampleScene::init() {
/* Initialize Diffuse example cube */
mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube());
mTestDiffuse->getTransform().setTranslation(9.0f, 0.0f, -2.0f);
mTestDiffuse->setShaders(":/solid-diffuse.vert", ":/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);
mTestDiffuse->reallocateNormals(mTestDiffuse->getNormals());
// Diffuse lighting example light source. This is just for visual reference.
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("diffuseLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(9.0f, 2.0f, -2.0f);
mMeshes.back()->getTransform().scale(0.25f);
mesh->getTransform().setTranslation(9.0f, 2.0f, -2.0f);
mesh->getTransform().scale(0.25f);
/* Initialize Specular example cube */
mTestSpecular = new Qtk::MeshRenderer("specular", Cube());
mTestSpecular->getTransform().setTranslation(11.0f, 0.0f, -2.0f);
mTestSpecular->setShaders(":/solid-specular.vert", ":/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);
@ -198,217 +188,209 @@ void ExampleScene::init() {
mTestSpecular->reallocateNormals(mTestSpecular->getNormals());
// Specular lighting example light source. This is just for visual reference.
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("specularLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(11.0f, 2.0f, -2.0f);
mMeshes.back()->getTransform().scale(0.25f);
mesh->getTransform().setTranslation(11.0f, 2.0f, -2.0f);
mesh->getTransform().scale(0.25f);
/* Test basic cube with phong.vert and phong.frag shaders */
mMeshes.push_back(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, 10.0f);
mMeshes.back()->setShaders(":/phong.vert", ":/phong.frag");
mesh = addObject(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS)));
mesh->getTransform().setTranslation(5.0f, 0.0f, 10.0f);
mesh->setShaders(":/shaders/phong.vert", ":/shaders/phong.frag");
// WARNING: Set color before reallocating normals.
mMeshes.back()->setColor(QVector3D(0.0f, 0.25f, 0.0f));
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mesh->setColor(QVector3D(0.0f, 0.25f, 0.0f));
mesh->reallocateNormals(mesh->getNormals());
mMeshes.back()->setUniform("uMaterial.ambient", QVector3D(0.0f, 0.3f, 0.0f));
mMeshes.back()->setUniform("uMaterial.diffuse", QVector3D(0.0f, 0.2f, 0.0f));
mMeshes.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
mMeshes.back()->setUniform("uMaterial.ambientStrength", 1.0f);
mMeshes.back()->setUniform("uMaterial.diffuseStrength", 1.0f);
mMeshes.back()->setUniform("uMaterial.specularStrength", 1.0f);
mMeshes.back()->setUniform("uMaterial.shine", 64.0f);
mMeshes.back()->setUniform("uLight.ambient", QVector3D(0.25f, 0.2f, 0.075f));
mMeshes.back()->setUniform("uLight.diffuse", QVector3D(0.75f, 0.6f, 0.22f));
mMeshes.back()->setUniform("uLight.specular", QVector3D(0.62f, 0.55f, 0.37f));
mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mesh->setUniform("uMaterial.ambient", QVector3D(0.0f, 0.3f, 0.0f));
mesh->setUniform("uMaterial.diffuse", QVector3D(0.0f, 0.2f, 0.0f));
mesh->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
mesh->setUniform("uMaterial.ambientStrength", 1.0f);
mesh->setUniform("uMaterial.diffuseStrength", 1.0f);
mesh->setUniform("uMaterial.specularStrength", 1.0f);
mesh->setUniform("uMaterial.shine", 64.0f);
mesh->setUniform("uLight.ambient", QVector3D(0.25f, 0.2f, 0.075f));
mesh->setUniform("uLight.diffuse", QVector3D(0.75f, 0.6f, 0.22f));
mesh->setUniform("uLight.specular", QVector3D(0.62f, 0.55f, 0.37f));
mesh->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
// Light source for testPhong cube
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("testLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(5.0f, 1.25f, 10.0f);
mMeshes.back()->getTransform().scale(0.25f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
mMeshes.back()->setColor(RED);
mesh->getTransform().setTranslation(5.0f, 1.25f, 10.0f);
mesh->getTransform().scale(0.25f);
mesh->setDrawType(GL_LINE_LOOP);
mesh->setColor(RED);
//
// Building more complex objects for showing examples of lighting techniques
/* Test alien Model with phong lighting and specular mapping. */
mModels.push_back(new Qtk::Model(
"alienTest", ":/models/alien-hominid/alien.obj", ":/model-specular.vert",
":/model-specular.frag"));
mModels.back()->getTransform().setTranslation(3.0f, -1.0f, 10.0f);
mModels.back()->getTransform().scale(0.15f);
mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.ambientStrength", 0.8f);
mModels.back()->setUniform("uMaterial.diffuseStrength", 0.8f);
mModels.back()->setUniform("uMaterial.specularStrength", 1.0f);
mModels.back()->setUniform("uMaterial.shine", 32.0f);
model = addObject(new Qtk::Model(
"alienTest", ":/models/models/alien-hominid/alien.obj",
":/shaders/model-specular.vert", ":/shaders/model-specular.frag"));
model->getTransform().setTranslation(3.0f, -1.0f, 10.0f);
model->getTransform().scale(0.15f);
model->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uMaterial.ambientStrength", 0.8f);
model->setUniform("uMaterial.diffuseStrength", 0.8f);
model->setUniform("uMaterial.specularStrength", 1.0f);
model->setUniform("uMaterial.shine", 32.0f);
mModels.back()->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
// Light source for alienTest object.
mMeshes.push_back(new Qtk::MeshRenderer(
mesh = addObject(new Qtk::MeshRenderer(
"alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(4.0f, 1.5f, 10.0f);
mMeshes.back()->getTransform().scale(0.25f);
mesh->getTransform().setTranslation(4.0f, 1.5f, 10.0f);
mesh->getTransform().scale(0.25f);
// This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN);
mesh->setColor(GREEN);
/* Test spartan Model with phong lighting, specular and normal mapping. */
mModels.push_back(new Qtk::Model(
"spartanTest", ":/models/spartan/spartan.obj", ":/model-normals.vert",
":/model-normals.frag"));
mModels.back()->getTransform().setTranslation(0.0f, -1.0f, 10.0f);
mModels.back()->getTransform().scale(2.0f);
mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.ambientStrength", 1.0f);
mModels.back()->setUniform("uMaterial.diffuseStrength", 1.0f);
mModels.back()->setUniform("uMaterial.specularStrength", 1.0f);
mModels.back()->setUniform("uMaterial.shine", 128.0f);
mModels.back()->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
model = addObject(new Qtk::Model(
"spartanTest", ":/models/models/spartan/spartan.obj",
":/shaders/model-normals.vert", ":/shaders/model-normals.frag"));
model->getTransform().setTranslation(0.0f, -1.0f, 10.0f);
model->getTransform().scale(2.0f);
model->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uMaterial.ambientStrength", 1.0f);
model->setUniform("uMaterial.diffuseStrength", 1.0f);
model->setUniform("uMaterial.specularStrength", 1.0f);
model->setUniform("uMaterial.shine", 128.0f);
model->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
// Light source for spartanTest object.
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(1.0f, 1.5f, 10.0f);
mMeshes.back()->getTransform().scale(0.25f);
mesh->getTransform().setTranslation(1.0f, 1.5f, 10.0f);
mesh->getTransform().scale(0.25f);
// This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN);
mesh->setColor(GREEN);
//
// Test drawing simple geometry with various OpenGL drawing modes
// RGB Normals cube to show normals are correct with QTK_DRAW_ARRAYS
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, 4.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mesh->getTransform().setTranslation(5.0f, 0.0f, 4.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS
mMeshes.push_back(new Qtk::MeshRenderer(
mesh = addObject(new Qtk::MeshRenderer(
"rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, 2.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mesh->getTransform().setTranslation(5.0f, 0.0f, 2.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
Texture crateTexture;
crateTexture.setTexture(":/crate.png");
crateTexture.setTexture(":/textures/crate.png");
Cube cube;
auto * m = new MeshRenderer("Test Crate", Cube(QTK_DRAW_ARRAYS));
m->getTransform().setTranslation(0, 0, 13);
m->setShaders(":/texture2d.vert", ":/texture2d.frag");
m->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
m->setTexture(crateTexture);
m->setUniform("uTexture", 0);
m->reallocateTexCoords(cube.getTexCoords());
mMeshes.push_back(m);
addObject(m);
// Texturing a cube using texture coordinates and glDrawArrays
// + Texturing with UVs using glDrawElements requires
// QTK_DRAW_ELEMENTS_NORMALS
// + UVs required duplicating element position data from QTK_DRAW_ELEMENTS
// + This is because the same position must use different UV coordinates
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->getTransform().setTranslation(-3.0f, 0.0f, -2.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->setTexture(crateTexture);
mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords());
mesh->getTransform().setTranslation(-3.0f, 0.0f, -2.0f);
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
mesh->setTexture(crateTexture);
mesh->setUniform("uTexture", 0);
mesh->reallocateTexCoords(mesh->getTexCoords());
// Test drawing a cube with texture coordinates using glDrawElements
mMeshes.push_back(new Qtk::MeshRenderer(
mesh = addObject(new Qtk::MeshRenderer(
"uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->getTransform().setTranslation(-1.7f, 0.0f, -2.0f);
mMeshes.back()->setTexture(":/crate.png");
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->bindShaders();
mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords(), 3);
mMeshes.back()->releaseShaders();
mMeshes.back()->getTransform().rotate(45.0f, 0.0f, 1.0f, 0.0f);
mesh->getTransform().setTranslation(-1.7f, 0.0f, -2.0f);
mesh->setTexture(":/textures/crate.png");
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
mesh->bindShaders();
mesh->setUniform("uTexture", 0);
mesh->reallocateNormals(mesh->getNormals());
mesh->reallocateTexCoords(mesh->getTexCoords(), 3);
mesh->releaseShaders();
mesh->getTransform().rotate(45.0f, 0.0f, 1.0f, 0.0f);
// Texturing a cube using a cube map
// + Cube map texturing works with both QTK_DRAW_ARRAYS and QTK_DRAW_ELEMENTS
mMeshes.push_back(
new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-3.0f, 1.0f, -2.0f);
mMeshes.back()->getTransform().setRotation(45.0f, 0.0f, 1.0f, 0.0f);
mMeshes.back()->setShaders(
":/texture-cubemap.vert", ":/texture-cubemap.frag");
mMeshes.back()->setCubeMap(":/crate.png");
mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords());
mesh =
addObject(new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(-3.0f, 1.0f, -2.0f);
mesh->getTransform().setRotation(45.0f, 0.0f, 1.0f, 0.0f);
mesh->setShaders(
":/shaders/texture-cubemap.vert", ":/shaders/texture-cubemap.frag");
mesh->setCubeMap(":/textures/crate.png");
mesh->setUniform("uTexture", 0);
mesh->reallocateTexCoords(mesh->getTexCoords());
// Create a cube with custom shaders
// + Apply RGB normals shader and spin the cube for a neat effect
mMeshes.push_back(
new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->getTransform().setTranslation(5.0f, 2.0f, -2.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mesh =
addObject(new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS)));
mesh->getTransform().setTranslation(5.0f, 2.0f, -2.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS
mMeshes.push_back(new Qtk::MeshRenderer(
mesh = addObject(new Qtk::MeshRenderer(
"rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS)));
mMeshes.back()->getTransform().setTranslation(7.0f, 0.0f, 2.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mesh->getTransform().setTranslation(7.0f, 0.0f, 2.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS
mMeshes.push_back(new Qtk::MeshRenderer(
mesh = addObject(new Qtk::MeshRenderer(
"rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->getTransform().setTranslation(7.0f, 0.0f, 4.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mesh->getTransform().setTranslation(7.0f, 0.0f, 4.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
// Test drawing triangle with glDrawArrays with texture coordinates
mMeshes.push_back(
mesh = addObject(
new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS)));
mMeshes.back()->getTransform().setTranslation(-3.0f, 2.0f, -2.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mesh->getTransform().setTranslation(-3.0f, 2.0f, -2.0f);
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
mMeshes.back()->setTexture(":/crate.png");
mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords());
mesh->setTexture(":/textures/crate.png");
mesh->setUniform("uTexture", 0);
mesh->reallocateTexCoords(mesh->getTexCoords());
// Test drawing triangle with glDrawElements with texture coordinates
mMeshes.push_back(new Qtk::MeshRenderer(
mesh = addObject(new Qtk::MeshRenderer(
"testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->getTransform().setTranslation(-2.5f, 0.0f, -1.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->setTexture(":/crate.png");
mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords());
mesh->getTransform().setTranslation(-2.5f, 0.0f, -1.0f);
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
mesh->setTexture(":/textures/crate.png");
mesh->setUniform("uTexture", 0);
mesh->reallocateTexCoords(mesh->getTexCoords());
}
void ExampleScene::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();
for(const auto & model : mModels) {
model->draw();
}
for(const auto & mesh : mMeshes) {
mesh->draw();
}
mTestPhong->bindShaders();
mTestPhong->setUniform(
"uModelInverseTransposed",
@ -417,15 +399,13 @@ void ExampleScene::draw() {
"uLightPosition",
MeshRenderer::getInstance("phongLight")->getTransform().getTranslation());
mTestPhong->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestPhong->releaseShaders();
mTestPhong->draw();
mTestAmbient->bindShaders();
mTestAmbient->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestAmbient->releaseShaders();
mTestAmbient->draw();
@ -438,8 +418,7 @@ void ExampleScene::draw() {
->getTransform()
.getTranslation());
mTestDiffuse->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestDiffuse->releaseShaders();
mTestDiffuse->draw();
@ -452,13 +431,12 @@ void ExampleScene::draw() {
->getTransform()
.getTranslation());
mTestSpecular->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestSpecular->releaseShaders();
mTestSpecular->draw();
}
void ExampleScene::update() {
void QtkScene::update() {
auto mySpartan = Model::getInstance("My spartan");
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
@ -471,13 +449,12 @@ void ExampleScene::update() {
auto alien = Model::getInstance("alienTest");
alien->setUniform("uLight.position", position);
alien->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
"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", ExampleScene::getCamera().toMatrix());
alien->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix());
alien->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
alien->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
position = MeshRenderer::getInstance("spartanTestLight")
@ -486,13 +463,12 @@ void ExampleScene::update() {
auto spartan = Model::getInstance("spartanTest");
spartan->setUniform("uLight.position", position);
spartan->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
posMatrix = spartan->getTransform().toMatrix();
spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
spartan->setUniform("uMVP.model", posMatrix);
spartan->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix());
spartan->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix());
spartan->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
spartan->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto phong = MeshRenderer::getInstance("testPhong");
@ -502,13 +478,12 @@ void ExampleScene::update() {
MeshRenderer::getInstance("testLight")->getTransform().getTranslation();
phong->setUniform("uLight.position", position);
phong->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
posMatrix = phong->getTransform().toMatrix();
phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
phong->setUniform("uMVP.model", posMatrix);
phong->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix());
phong->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix());
phong->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
phong->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
phong->releaseShaders();
// Rotate lighting example cubes

View File

@ -1,7 +1,7 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Classes for managing objects and data within a scene ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Example Qtk scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
@ -9,11 +9,7 @@
#ifndef QTK_EXAMPLE_SCENE_H
#define QTK_EXAMPLE_SCENE_H
#include <camera3d.h>
#include <scene.h>
#include <skybox.h>
#include <QMatrix4x4>
#include <qtk/scene.h>
/**
* Example scene using QtkWidget to render 3D models and simple geometry within
@ -33,14 +29,15 @@
*
* To create your own Scene from scratch see Qtk::Scene.
*/
class ExampleScene : public Qtk::Scene {
class QtkScene : public Qtk::SceneInterface {
public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
ExampleScene();
~ExampleScene();
QtkScene(Qtk::Scene * scene);
~QtkScene();
/***************************************************************************
* Inherited Public Overrides
@ -50,6 +47,7 @@ class ExampleScene : public Qtk::Scene {
* Initialize objects within the scene
*/
void init() override;
/**
* Called when OpenGL repaints the widget.
*/

View File

@ -1,17 +1,21 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main window for Qt6 OpenGL widget application ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: QtkWidget for Qt desktop application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <QKeyEvent>
#include <QVBoxLayout>
#include <input.h>
#include <mesh.h>
#include <qtkwidget.h>
#include <scene.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;
@ -19,17 +23,26 @@ using namespace Qtk;
* Constructors, Destructors
******************************************************************************/
QtkWidget::QtkWidget() : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
initializeWidget();
}
QtkWidget::QtkWidget(QWidget * parent) : QtkWidget(parent, "QtkWidget") {}
QtkWidget::QtkWidget(QWidget * parent) :
QOpenGLWidget(parent), mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
initializeWidget();
}
QtkWidget::QtkWidget(QWidget * parent, const QString & name) :
QtkWidget(parent, name, Q_NULLPTR) {}
QtkWidget::QtkWidget(const QSurfaceFormat & format) :
mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR),
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR) {
setScene(scene);
setObjectName(name);
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(4, 6);
// Set the number of samples used for glEnable(GL_MULTISAMPLING)
format.setSamples(4);
// Set the size of the depth bufer for glEnable(GL_DEPTH_TEST)
format.setDepthBufferSize(16);
// If QTK_DEBUG is set, enable debug context
format.setOption(QSurfaceFormat::DebugContext);
setFormat(format);
setFocusPolicy(Qt::ClickFocus);
}
@ -40,16 +53,24 @@ QtkWidget::~QtkWidget() {
}
/*******************************************************************************
* Public Inherited Virtual Methods
* Public Methods
******************************************************************************/
QAction * QtkWidget::getActionToggleConsole() {
auto action = new QAction(mScene->getSceneName() + " debug console");
action->setCheckable(true);
action->setChecked(mConsoleActive);
action->setStatusTip("Add a debug console for this QtkWidget.");
connect(action, &QAction::triggered, this, &QtkWidget::toggleConsole);
return action;
}
void QtkWidget::initializeGL() {
initializeOpenGLFunctions();
// Connect the frameSwapped signal to call the update() function
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
// Initialize OpenGL debug context
#ifdef QTK_DEBUG
mDebugLogger = new QOpenGLDebugLogger(this);
if(mDebugLogger->initialize()) {
qDebug() << "GL_DEBUG Debug Logger" << mDebugLogger << "\n";
@ -58,7 +79,6 @@ void QtkWidget::initializeGL() {
SLOT(messageLogged(QOpenGLDebugMessage)));
mDebugLogger->startLogging();
}
#endif // QTK_DEBUG
printContextInformation();
@ -87,10 +107,64 @@ void QtkWidget::paintGL() {
}
}
void QtkWidget::setScene(Qtk::Scene * scene) {
if(mScene != Q_NULLPTR) {
delete mScene;
connect(
scene, &Qtk::Scene::sceneUpdated, MainWindow::getMainWindow(),
&MainWindow::refreshScene);
}
mScene = scene;
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,
dynamic_cast<QDockWidget *>(mConsole));
mConsole->setHidden(false);
mConsoleActive = true;
}
}
/*******************************************************************************
* Protected Slots
* Protected Methods
******************************************************************************/
void QtkWidget::keyPressEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
// Do not repeat input while a key is held down
event->ignore();
} else {
Input::registerKeyPress(event->key());
}
}
void QtkWidget::keyReleaseEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
event->ignore();
} else {
Input::registerKeyRelease(event->key());
}
}
void QtkWidget::mousePressEvent(QMouseEvent * event) {
Input::registerMousePress(event->button());
}
void QtkWidget::mouseReleaseEvent(QMouseEvent * event) {
Input::registerMouseRelease(event->button());
}
void QtkWidget::update() {
updateCameraInput();
@ -104,19 +178,24 @@ void QtkWidget::update() {
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
QString error;
DebugContext context;
// Format based on severity
switch(msg.severity()) {
case QOpenGLDebugMessage::NotificationSeverity:
error += "--";
context = Status;
break;
case QOpenGLDebugMessage::HighSeverity:
error += "!!";
context = Fatal;
break;
case QOpenGLDebugMessage::MediumSeverity:
error += "!~";
context = Error;
break;
case QOpenGLDebugMessage::LowSeverity:
error += "~~";
context = Warn;
break;
}
@ -159,58 +238,53 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
}
#undef CASE
error += ")";
qDebug() << qPrintable(error) << "\n" << qPrintable(msg.message()) << "\n";
}
/*******************************************************************************
* Protected Methods
******************************************************************************/
void QtkWidget::keyPressEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
// Do not repeat input while a key is held down
event->ignore();
} else {
Input::registerKeyPress(event->key());
}
}
void QtkWidget::keyReleaseEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
event->ignore();
} else {
Input::registerKeyRelease(event->key());
}
}
void QtkWidget::mousePressEvent(QMouseEvent * event) {
Input::registerMousePress(event->button());
}
void QtkWidget::mouseReleaseEvent(QMouseEvent * event) {
Input::registerMouseRelease(event->button());
error += ")\n" + msg.message() + "\n";
qDebug() << qPrintable(error);
sendLog("(OpenGL) " + error.replace("\n", "\n(OpenGL) "), context);
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void QtkWidget::initializeWidget() {
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(4, 6);
// Set the number of samples used for glEnable(GL_MULTISAMPLING)
format.setSamples(4);
// Set the size of the depth bufer for glEnable(GL_DEPTH_TEST)
format.setDepthBufferSize(16);
// If QTK_DEBUG is set, enable debug context
#ifdef QTK_DEBUG
format.setOption(QSurfaceFormat::DebugContext);
#endif
setFormat(format);
setFocusPolicy(Qt::ClickFocus);
void QtkWidget::teardownGL() { /* Nothing to teardown yet... */
}
void QtkWidget::updateCameraInput() {
Input::update();
// Camera Transformation
if(Input::buttonPressed(Qt::RightButton)) {
static const float transSpeed = 0.1f;
static const float rotSpeed = 0.5f;
// Handle rotations
Scene::getCamera().getTransform().rotate(
-rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp);
Scene::getCamera().getTransform().rotate(
-rotSpeed * Input::mouseDelta().y(), Scene::getCamera().getRight());
// Handle translations
QVector3D translation;
if(Input::keyPressed(Qt::Key_W)) {
translation += Scene::getCamera().getForward();
}
if(Input::keyPressed(Qt::Key_S)) {
translation -= Scene::getCamera().getForward();
}
if(Input::keyPressed(Qt::Key_A)) {
translation -= Scene::getCamera().getRight();
}
if(Input::keyPressed(Qt::Key_D)) {
translation += Scene::getCamera().getRight();
}
if(Input::keyPressed(Qt::Key_Q)) {
translation -= Scene::getCamera().getUp() / 2.0f;
}
if(Input::keyPressed(Qt::Key_E)) {
translation += Scene::getCamera().getUp() / 2.0f;
}
Scene::getCamera().getTransform().translate(transSpeed * translation);
}
}
void QtkWidget::printContextInformation() {
@ -239,46 +313,9 @@ void QtkWidget::printContextInformation() {
}
#undef CASE
// qPrintable() will print our QString w/o quotes around it.
qDebug() << qPrintable(glType) << qPrintable(glVersion) << "("
<< qPrintable(glProfile) << ")"
<< "\nOpenGL Vendor: " << qPrintable(glVendor)
<< "\nRendering Device: " << qPrintable(glRenderer) << "\n";
}
void QtkWidget::updateCameraInput() {
Input::update();
// Camera Transformation
if(Input::buttonPressed(Qt::RightButton)) {
static const float transSpeed = 0.1f;
static const float rotSpeed = 0.5f;
// Handle rotations
Scene::getCamera().getTransform().rotate(
-rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp);
Scene::getCamera().getTransform().rotate(
-rotSpeed * Input::mouseDelta().y(), Scene::getCamera().right());
// Handle translations
QVector3D translation;
if(Input::keyPressed(Qt::Key_W)) {
translation += Scene::getCamera().forward();
}
if(Input::keyPressed(Qt::Key_S)) {
translation -= Scene::getCamera().forward();
}
if(Input::keyPressed(Qt::Key_A)) {
translation -= Scene::getCamera().right();
}
if(Input::keyPressed(Qt::Key_D)) {
translation += Scene::getCamera().right();
}
if(Input::keyPressed(Qt::Key_Q)) {
translation -= Scene::getCamera().up() / 2.0f;
}
if(Input::keyPressed(Qt::Key_E)) {
translation += Scene::getCamera().up() / 2.0f;
}
Scene::getCamera().getTransform().translate(transSpeed * translation);
}
auto message = QString(glType) + glVersion + "(" + glProfile + ")"
+ "\nOpenGL Vendor: " + glVendor
+ "\nRendering Device: " + glRenderer;
qDebug() << qPrintable(message);
sendLog("(OpenGL) " + message.replace("\n", "\n(OpenGL) "), Status);
}

View File

@ -1,7 +1,7 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main window for Qt6 OpenGL widget application ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: QtkWidget for Qt desktop application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
@ -10,22 +10,26 @@
#include <iostream>
#include <QDockWidget>
#include <QMatrix4x4>
#include <QOpenGLDebugLogger>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <QPlainTextEdit>
#include <qtkapi.h>
#include <scene.h>
#include <qtk/qtkapi.h>
#include <qtk/scene.h>
namespace Qtk {
class DebugConsole;
/**
* QtkWidget class to define required QOpenGLWidget functionality.
*
* This object has a Scene attached which manages the objects to render.
* Client input is passed through this widget to control the camera view.
*/
class QTKAPI QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
class QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT;
public:
@ -33,40 +37,41 @@ namespace Qtk {
* Contructors / Destructors
************************************************************************/
/**
* Default ctor will configure a QSurfaceFormat with default settings.
*/
QtkWidget();
/**
* Qt Designer will call this ctor when creating this widget as a child.
*
* @param parent The parent QWidget
* @param parent Pointer to a parent widget for this QtkWidget or nullptr.
*/
explicit QtkWidget(QWidget * parent);
explicit QtkWidget(QWidget * parent = nullptr);
/**
* Allow constructing the widget with a preconfigured QSurfaceFormat.
* Default construct a QtkWidget.
*
* @param format QSurfaceFormat already configured by the caller.
* @param parent Pointer to a parent widget or nullptr if no parent.
* @param name An objectName for the new QtkWidget.
*/
explicit QtkWidget(const QSurfaceFormat & format);
explicit QtkWidget(QWidget * parent, const QString & name);
~QtkWidget() override;
/**
* Construct a custom QtkWidget.
*
* @param parent Pointer to a parent widget or nullptr if no parent.
* @param name An objectName for the new QtkWidget.
* @param scene Pointer to a custom class inheriting from Qtk::Scene.
*/
QtkWidget(QWidget * parent, const QString & name, Qtk::Scene * scene);
~QtkWidget();
private:
/*************************************************************************
* Private Methods
* Public Methods
************************************************************************/
// clang-format off
void teardownGL() { /* Nothing to teardown yet... */ }
// clang-format on
public:
/*************************************************************************
* Public Inherited Virtual Methods
************************************************************************/
/**
* Constructs a QAction to hide / show this DebugConsole.
* @return QAction to toggle visibility of this DebugConsole.
*/
QAction * getActionToggleConsole();
/**
* Called when the widget is first constructed.
@ -90,66 +95,110 @@ namespace Qtk {
* Accessors
************************************************************************/
/**
* @return The active scene being viewed in this widget.
*/
inline Qtk::Scene * getScene() { return mScene; }
/**
* @return Pointer to the QOpenGLDebugLogger attached to this widget.
*/
inline QOpenGLDebugLogger * getOpenGLDebugLogger() {
return mDebugLogger;
}
/*************************************************************************
* Setters
************************************************************************/
inline void setScene(Qtk::Scene * scene) {
delete mScene;
mScene = scene;
}
/**
* @param scene The new scene to view.
*/
void setScene(Qtk::Scene * scene);
protected slots:
/*************************************************************************
* Qt Slots
************************************************************************/
public slots:
/**
* Called when the `frameSwapped` signal is caught.
* See definition of initializeGL()
* Toggle visibility of the DebugConsole associated with this QtkWidget.
*/
void update();
void toggleConsole();
#ifdef QTK_DEBUG
signals:
/**
* Called when the `messageLogged` signal is caught.
* See definition of initializeGL()
*
* @param msg The message logged.
* Log a message to the DebugConsole associated with this widget.
* @param message The message to log.
* @param context The context of the log message.
*/
static void messageLogged(const QOpenGLDebugMessage & msg);
#endif
void sendLog(const QString & message, DebugContext context = Status);
protected:
/*************************************************************************
* Protected Methods
************************************************************************/
/**
* @param event Key press event to update camera input manager.
*/
void keyPressEvent(QKeyEvent * event) override;
/**
* @param event Key release event to update camera input manager.
*/
void keyReleaseEvent(QKeyEvent * event) override;
/**
* @param event Mouse button press event to update camera input manager.
*/
void mousePressEvent(QMouseEvent * event) override;
/**
* @param event Mouse button release event to update camera input manager.
*/
void mouseReleaseEvent(QMouseEvent * event) override;
protected slots:
/**
* Called when the `frameSwapped` signal is caught.
* See definition of initializeGL()
*/
void update();
/**
* Called when the `messageLogged` signal is caught.
* See definition of initializeGL()
*
* @param msg The message logged.
*/
void messageLogged(const QOpenGLDebugMessage & msg);
private:
/*************************************************************************
* Private Methods
************************************************************************/
void initializeWidget();
/**
* Deconstruct any resources we have allocated for this widget.
*/
void teardownGL();
/**
* Callback function to update input for camera controls
*/
static void updateCameraInput();
#ifdef QTK_DEBUG
/**
* Prints OpenGL context information at start of debug session.
*/
void printContextInformation();
QOpenGLDebugLogger * mDebugLogger;
#endif
/*************************************************************************
* Private Members
************************************************************************/
QOpenGLDebugLogger * mDebugLogger;
Qtk::Scene * mScene;
Qtk::DebugConsole * mConsole;
bool mConsoleActive = false;
};
} // namespace Qtk

6
src/app/resources.h.in Normal file
View File

@ -0,0 +1,6 @@
#ifndef QTK_RESOURCES_H_IN_H
#define QTK_RESOURCES_H_IN_H
// Not currently in use, but will be in the future.
#endif // QTK_RESOURCES_H_IN_H

20
src/app/toolbox.cpp Normal file
View File

@ -0,0 +1,20 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Toolbox plugin for object details and options ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "toolbox.h"
#include "ui_toolbox.h"
Qtk::ToolBox::ToolBox(QWidget * parent) :
QDockWidget(parent), ui(new Ui::ToolBox) {
ui->setupUi(this);
}
Qtk::ToolBox::~ToolBox() {
delete ui;
}

42
src/app/toolbox.h Normal file
View File

@ -0,0 +1,42 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Toolbox plugin for object details and options ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef TOOLBOX_H
#define TOOLBOX_H
#include <QDesignerExportWidget>
#include <QDockWidget>
namespace Ui {
class ToolBox;
}
namespace Qtk {
class ToolBox : public QDockWidget {
Q_OBJECT
public:
/*************************************************************************
* Contructors / Destructors
*************************************************************************/
explicit ToolBox(QWidget * parent = nullptr);
~ToolBox();
private:
/*************************************************************************
* Private Members
************************************************************************/
Ui::ToolBox * ui;
};
} // namespace Qtk
#endif // TOOLBOX_H

56
src/app/toolbox.ui Normal file
View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ToolBox</class>
<widget class="QDockWidget" name="ToolBox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Object Details</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolBox" name="toolBox">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>382</width>
<height>201</height>
</rect>
</property>
<attribute name="label">
<string>Shaders</string>
</attribute>
</widget>
<widget class="QWidget" name="page_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>382</width>
<height>201</height>
</rect>
</property>
<attribute name="label">
<string>Properties</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

63
src/app/treeview.cpp Normal file
View File

@ -0,0 +1,63 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: TreeView plugin for scene hierarchy ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "treeview.h"
#include "qtkmainwindow.h"
#include "ui_treeview.h"
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
Qtk::TreeView::TreeView(QWidget * parent) :
QDockWidget(parent), ui(new Ui::TreeView) {
ui->setupUi(this);
connect(
ui->treeWidget, &QTreeWidget::itemDoubleClicked, this,
&TreeView::itemFocus);
}
Qtk::TreeView::~TreeView() {
delete ui;
}
/*******************************************************************************
* Public Methods
******************************************************************************/
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);
}
}
void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) {
QString name = item->text(column);
auto scene = MainWindow::getMainWindow()->getQtkWidget()->getScene();
auto & transform = scene->getCamera().getTransform();
auto object = scene->getObject(name);
if(object == Q_NULLPTR) {
qDebug() << "Attempt to get non-existing object with name '" << name
<< "'\n";
}
Transform3D * objectTransform;
if(object->getType() == Object::QTK_MESH) {
objectTransform = &dynamic_cast<MeshRenderer *>(object)->getTransform();
} else if(object->getType() == Object::QTK_MODEL) {
objectTransform = &dynamic_cast<Model *>(object)->getTransform();
}
transform.setTranslation(objectTransform->getTranslation());
transform.translate(0.0f, 0.0f, 3.0f);
}

73
src/app/treeview.h Normal file
View File

@ -0,0 +1,73 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: TreeView plugin for scene hierarchy ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef TREEVIEW_H
#define TREEVIEW_H
#include <QDesignerCustomWidgetInterface>
#include <QDesignerExportWidget>
#include <QDockWidget>
#include <qtk/scene.h>
#include <QTreeWidgetItem>
namespace Ui {
class TreeView;
}
namespace Qtk {
class TreeView : public QDockWidget {
Q_OBJECT
public:
/*************************************************************************
* Constructors / Destructors
************************************************************************/
explicit TreeView(QWidget * parent = nullptr);
~TreeView();
/*************************************************************************
* Public Methods
************************************************************************/
/**
* Updates the QTreeWidget with all objects within the scene.
* @param scene The scene to load objects from.
*/
void updateView(const Scene * scene);
public slots:
/**
* Focus the camera on an item when it is double clicked.
* Triggered by QTreeWidget::itemDoubleClicked signal.
*
* @param item The item that was double clicked
* @param column The column of the item that was double clicked.
* This param is currently not used but required for this signal.
*/
void itemFocus(QTreeWidgetItem * item, int column);
private:
/*************************************************************************
* Private Members
************************************************************************/
Ui::TreeView * ui;
/**
* The name of the scene last loaded by this TreeWidget.
* Used to load object data from a target scene.
*/
QString mSceneName;
};
} // namespace Qtk
#endif // TREEVIEW_H

44
src/app/treeview.ui Normal file
View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TreeView</class>
<widget class="QDockWidget" name="TreeView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Scene Tree View</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeWidget" name="treeWidget">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="indentation">
<number>10</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

98
src/app/widgetplugin.cpp Normal file
View File

@ -0,0 +1,98 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Generic Qt Designer widget plugin ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include <QIcon>
#include <QtPlugin>
#include <utility>
#include <qtk/qtkapi.h>
#include "widgetplugin.h"
/*******************************************************************************
* 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(QObject * parent) : QObject(parent) {}
/*******************************************************************************
* Public Methods
******************************************************************************/
QString WidgetPlugin::group() const {
return m_group;
}
QString WidgetPlugin::name() const {
return m_className;
}
QString WidgetPlugin::includeFile() const {
return m_includeFile;
}
QWidget * WidgetPlugin::createWidget(QWidget * parent) {
return m_factory(parent);
}
QString WidgetPlugin::toolTip() const {
return QStringLiteral("A custom widget tool tip.");
}
QString WidgetPlugin::whatsThis() const {
return QStringLiteral("Custom widget what's this?");
}
QIcon WidgetPlugin::icon() const {
return Qtk::getIcon();
}
bool WidgetPlugin::isContainer() const {
return true;
}
bool WidgetPlugin::isInitialized() const {
return m_initialized;
}
void WidgetPlugin::initialize(QDesignerFormEditorInterface *) {
if(m_initialized) {
return;
}
m_initialized = true;
}
QString WidgetPlugin::domXml() const {
return
"<ui language=\"c++\">\n"
" <widget class=\"" + m_className + "\" name=\"" + m_objectName + "\">\n"
" <property name=\"geometry\">\n"
" <rect>\n"
" <x>0</x>\n"
" <y>0</y>\n"
" <width>100</width>\n"
" <height>100</height>\n"
" </rect>\n"
" </property>\n"
" <property name=\"toolTip\" >\n"
" <string>" + toolTip() + "</string>\n"
" </property>\n"
" <property name=\"whatsThis\" >\n"
" <string>" + whatsThis() + "</string>\n"
" </property>\n"
" </widget>\n"
"</ui>\n";
}

124
src/app/widgetplugin.h Normal file
View File

@ -0,0 +1,124 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Generic Qt Designer widget plugin ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef QTK_WIDGETPLUGIN_H
#define QTK_WIDGETPLUGIN_H
#include <QDesignerCustomWidgetInterface>
#include <QDesignerExportWidget>
class QDESIGNER_WIDGET_EXPORT WidgetPlugin :
public QObject,
public QDesignerCustomWidgetInterface {
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)
using Factory = std::function<QWidget *(QWidget *)>;
public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
WidgetPlugin(
QString group, QString class_name, QString include, Factory factory);
explicit WidgetPlugin(QObject * parent = nullptr);
~WidgetPlugin() = default;
/***************************************************************************
* Public Methods
**************************************************************************/
/**
* @return The name of the group to which this widget belongs.
*/
[[nodiscard]] QString group() const override;
/**
* Must return the _class name_ of the widget.
*
* @return The class name for the associated widget.
*/
[[nodiscard]] QString name() const override;
/**
* If this path changes for a custom widget, it must be removed and added
* back in Qt Designer for the XML surrounding this value to be regenerated.
*
* See the `<customwidget>` XML in any `.ui` file using a custom widget.
*
* @return Path to the include file for UIC to use when generating code.
*/
[[nodiscard]] QString includeFile() const override;
/**
* @param parent Parent widget to the new instance of this widget.
* @return A new instance of this custom widget.
*/
[[nodiscard]] QWidget * createWidget(QWidget * parent) override;
/**
* @return Short description used in Qt Designer tool tips.
*/
[[nodiscard]] QString toolTip() const override;
/**
* @return Widget description used in `What's this?` within Qt Creator.
*/
[[nodiscard]] QString whatsThis() const override;
/**
* @return Icon used to represent the widget in Qt Designer's GUI.
*/
[[nodiscard]] QIcon icon() const override;
/**
* Whether or not this widget should act as a container for other widgets.
*
* @return True if this custom widget is meant to be a container.
*/
[[nodiscard]] bool isContainer() const override;
/**
* @return True if this widget has been initialized.
*/
[[nodiscard]] bool isInitialized() const override;
/**
* Initializes an instance of this custom widget.
* @param core
*/
void initialize(QDesignerFormEditorInterface * core) override;
/**
* Default XML for an instance of this custom widget within a `.ui` file.
*
* Any property available for the widget in Qt Designer can be set using XML
* properties, as seen here with `toolTip` and `whatsThis`.
*
* @return XML inserted for each instance of this widget.
*/
[[nodiscard]] QString domXml() const override;
private:
/***************************************************************************
* Private Members
**************************************************************************/
bool m_initialized = false;
QString m_group;
QString m_className;
QString m_objectName;
QString m_includeFile;
Factory m_factory;
};
#endif // QTK_WIDGETPLUGIN_H

View File

@ -0,0 +1,43 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Collection of widget plugins for Qt Designer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widgetplugincollection.h"
#include "debugconsole.h"
#include "qtkwidget.h"
#include "toolbox.h"
#include "treeview.h"
#include "widgetplugin.h"
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
WidgetPluginCollection::WidgetPluginCollection(QObject * parent) :
QObject(parent), m_collectionName("Qtk Widget Collection") {
m_collection = {
new WidgetPlugin(
m_collectionName, "Qtk::QtkWidget", "qtkwidget.h",
[](QWidget * parent) { return new Qtk::QtkWidget(parent); }),
new WidgetPlugin(
m_collectionName, "Qtk::TreeView", "treeview.h",
[](QWidget * parent) { return new Qtk::TreeView(parent); }),
new WidgetPlugin(
m_collectionName, "Qtk::ToolBox", "toolbox.h",
[](QWidget * parent) { return new Qtk::ToolBox(parent); }),
};
}
/*******************************************************************************
* Public Methods
******************************************************************************/
QList<QDesignerCustomWidgetInterface *> WidgetPluginCollection::customWidgets()
const {
return m_collection;
}

View File

@ -0,0 +1,50 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Collection of widget plugins for Qt Designer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef QTK_WIDGETPLUGINCOLLECTION_H
#define QTK_WIDGETPLUGINCOLLECTION_H
#include <QDesignerCustomWidgetCollectionInterface>
class WidgetPluginCollection :
public QObject,
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.
Q_PLUGIN_METADATA(IID "com.Klips.WidgetPluginCollection")
// Tell Qt Object system that we're implementing an interface.
Q_INTERFACES(QDesignerCustomWidgetCollectionInterface)
public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
explicit WidgetPluginCollection(QObject * parent = nullptr);
/***************************************************************************
* Public Methods
**************************************************************************/
/**
* @return QList of all custom widgets pointers.
*/
[[nodiscard]] QList<QDesignerCustomWidgetInterface *> customWidgets() const;
private:
/***************************************************************************
* Private Members
**************************************************************************/
QList<QDesignerCustomWidgetInterface *> m_collection;
QString m_collectionName;
};
#endif // QTK_WIDGETPLUGINCOLLECTION_H

View File

@ -1,89 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Input class from tutorials followed at trentreed.net ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTOPENGL_INPUT_H
#define QTOPENGL_INPUT_H
#include <QPoint>
#include <Qt>
#include <qtkapi.h>
#include <qtkwidget.h>
namespace Qtk {
class QTKAPI Input {
public:
/*************************************************************************
* Typedefs
************************************************************************/
friend class Qtk::QtkWidget;
/**
* Possible key states
*/
enum InputState {
InputInvalid,
InputRegistered,
InputUnregistered,
InputTriggered,
InputPressed,
InputReleased
};
/*************************************************************************
* Public Methods
************************************************************************/
// State checking
inline static bool keyTriggered(Qt::Key key) {
return keyState(key) == InputTriggered;
}
inline static bool keyPressed(Qt::Key key) {
return keyState(key) == InputPressed;
}
inline static bool keyReleased(Qt::Key key) {
return keyState(key) == InputReleased;
}
inline static bool buttonTriggered(Qt::MouseButton button) {
return buttonState(button) == InputTriggered;
}
inline static bool buttonPressed(Qt::MouseButton button) {
return buttonState(button) == InputPressed;
}
inline static bool buttonReleased(Qt::MouseButton button) {
return buttonState(button) == InputReleased;
}
// Implementation
static InputState keyState(Qt::Key key);
static InputState buttonState(Qt::MouseButton button);
static QPoint mousePosition();
static QPoint mouseDelta();
private:
/*************************************************************************
* Private Methods
************************************************************************/
// State updating
static void update();
static void registerKeyPress(int key);
static void registerKeyRelease(int key);
static void registerMousePress(Qt::MouseButton button);
static void registerMouseRelease(Qt::MouseButton button);
static void reset();
};
} // namespace Qtk
#endif // QTOPENGL_INPUT_H

View File

@ -1,137 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: MeshRenderer class for quick object creation and drawing ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_MESHRENDERER_H
#define QTK_MESHRENDERER_H
#include <mesh.h>
#include <object.h>
#include <qtkapi.h>
#include <utility>
namespace Qtk {
class QTKAPI MeshRenderer : public Object {
public:
/*************************************************************************
* Typedefs
************************************************************************/
/* Static QHash of all mesh objects within the scene. */
typedef QHash<QString, MeshRenderer *> MeshManager;
/*************************************************************************
* Constructors / Destructors
************************************************************************/
// Delegate constructors
MeshRenderer(
const char * name, Vertices vertices, Indices indices,
DrawMode mode = QTK_DRAW_ARRAYS) :
MeshRenderer(
name, ShapeBase(mode, std::move(vertices), std::move(indices))) {}
explicit MeshRenderer(const char * name) :
MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS)) {}
// Constructor
MeshRenderer(const char * name, const ShapeBase & shape);
~MeshRenderer() override;
/*************************************************************************
* Public Methods
************************************************************************/
void init();
void draw();
inline void enableAttributeArray(int location) {
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.enableAttributeArray(location);
mVAO.release();
}
void reallocateTexCoords(const TexCoords & t, unsigned dims = 2);
void reallocateNormals(const Normals & n, unsigned dims = 3);
/*************************************************************************
* Setters
************************************************************************/
// Draw types like GL_TRIANGLES, GL_POINTS, GL_LINES, etc
void setDrawType(int drawType) { mDrawType = drawType; }
// Shader settings
inline void setShaderVertex(const std::string & vert) {
mVertexShader = vert;
}
inline void setShaderFragment(const std::string & frag) {
mFragmentShader = frag;
}
void setShaders(const std::string & vert, const std::string & frag);
template <typename T> inline void setUniform(int location, T value) {
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(location, value);
}
template <typename T>
inline void setUniform(const char * location, T value) {
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(location, value);
}
// Set MVP matrix using this Object's transform
// + View and projection provided by MainWidget static members
void setUniformMVP(
const char * model = "uModel", const char * view = "uView",
const char * projection = "uProjection");
// These functions modify data stored in a VBO
// + After calling them, the VBO will need to be reallocated
void setShape(const Shape & value) override;
void setColor(const QVector3D & color);
void setAttributeBuffer(
int location, GLenum type, int offset, int tupleSize,
int stride = 0) {
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.setAttributeBuffer(location, type, offset, tupleSize, stride);
mVAO.release();
}
/*************************************************************************
* Accessors
************************************************************************/
/**
* Retrieve a mesh by name stored within static QHash private member
* @param name The name of the MeshRenderer we want to retrieve.
* @return Pointer to the MeshRenderer, or nullptr if not found.
*/
static MeshRenderer * getInstance(const QString & name);
Transform3D & getTransform() { return mTransform; }
private:
/*************************************************************************
* Private Members
************************************************************************/
static MeshManager sInstances;
int mDrawType {};
std::string mVertexShader {}, mFragmentShader {};
};
} // namespace Qtk
#endif // QTK_MESHRENDERER_H

View File

@ -1,262 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Model classes for importing with Assimp ##
## From following tutorials on learnopengl.com ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_MODEL_H
#define QTK_MODEL_H
// QT
#include <QObject>
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
// Assimp
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/Importer.hpp>
// QTK
#include <object.h>
#include <qtkapi.h>
#include <transform3D.h>
namespace Qtk {
/**
* 3D models will store this data for each vertex in geometry.
*/
struct QTKAPI ModelVertex {
QVector3D mPosition;
QVector3D mNormal;
QVector2D mTextureCoord;
QVector3D mTangent;
QVector3D mBitangent;
};
/**
* Struct to store model textures. 3D Models may have multiple.
*/
struct QTKAPI ModelTexture {
GLuint mID {};
QOpenGLTexture * mTexture {};
std::string mType {};
std::string mPath {};
};
class Model;
/**
* Mesh class specialized for storing 3D model data.
* Eventually this can be consolidated into a more generic class.
*/
class QTKAPI ModelMesh : protected QOpenGLFunctions {
public:
/*************************************************************************
* Typedefs
************************************************************************/
friend Model;
typedef std::vector<ModelVertex> Vertices;
typedef std::vector<GLuint> Indices;
typedef std::vector<ModelTexture> Textures;
/*************************************************************************
* Constructors, Destructors
************************************************************************/
ModelMesh(
Vertices vertices, Indices indices, Textures textures,
const char * vertexShader = ":/model-basic.vert",
const char * fragmentShader = ":/model-basic.frag") :
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)) {
initMesh(vertexShader, fragmentShader);
}
~ModelMesh() = default;
/*************************************************************************
* Public Methods
************************************************************************/
inline void draw() { draw(*mProgram); }
void draw(QOpenGLShaderProgram & shader);
/*************************************************************************
* Public Members
************************************************************************/
Vertices mVertices {};
Indices mIndices {};
Textures mTextures {};
Transform3D mTransform;
private:
/*************************************************************************
* Private Methods
************************************************************************/
void initMesh(const char * vert, const char * frag);
/*************************************************************************
* Private Members
************************************************************************/
QOpenGLBuffer *mVBO, *mEBO;
QOpenGLVertexArrayObject * mVAO;
QOpenGLShaderProgram * mProgram;
};
/**
* Model object that has a ModelMesh.
* Top-level object that represents 3D models stored within a scene.
*/
class QTKAPI Model : public QObject {
Q_OBJECT
public:
/*************************************************************************
* Typedefs
************************************************************************/
/* ModelManager typedef that will manage global model access. */
typedef QHash<QString, Model *> ModelManager;
/*************************************************************************
* Constructors, Destructors
************************************************************************/
// Default model shaders are provided but we can override them in the ctor
inline Model(
const char * name, const char * path,
const char * vertexShader = ":/model-basic.vert",
const char * fragmentShader = ":/model-basic.frag") :
mName(name),
mModelPath(path), mVertexShader(vertexShader),
mFragmentShader(fragmentShader) {
loadModel(path);
}
inline ~Model() override { mManager.remove(mName); }
/*************************************************************************
* Public Methods
************************************************************************/
void draw();
void draw(QOpenGLShaderProgram & shader);
/**
* Flip a texture associated with this model
*
* @param fileName The name of the texture to flip as it is stored on disk
* @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);
/*************************************************************************
* Setters
************************************************************************/
/**
* Sets a uniform value
*
* @tparam T The type of the value we are settings
* @param location The uniform location
* @param value The value to assign to the uniform
*/
template <typename T> void setUniform(const char * location, T value) {
for(auto & mesh : mMeshes) {
mesh.mProgram->bind();
mesh.mProgram->setUniformValue(location, value);
mesh.mProgram->release();
}
}
/*************************************************************************
* Accessors
************************************************************************/
/**
* Accessor function for retrieving a ModelMesh globally.
* The mesh is retrieved from the mManager private member.
*
* @param name The name of the model to load as it was constructed.
* @return Pointer to the model stored within the scene.
*/
static Model * getInstance(const char * name);
Transform3D & getTransform() { return mTransform; }
private:
/*************************************************************************
* Private Methods
************************************************************************/
/**
* Loads a model in .obj, .fbx, .gltf, and other formats.
* For a full list of formats see assimp documentation:
* https://github.com/assimp/assimp/blob/master/doc/Fileformats.md
*
* Models should not be loaded into Qt resource system.
* Instead pass an *absolute* path to this function.
* Relative paths will break if Qtk is executed from different locations.
*
* Models can also be loaded from the `qtk/resource` directory using qrc
* format loadModel(":/models/backpack/backpack.obj").
* This does not use Qt resource system, it just provides similar syntax
* for accessing files within the same `resources/` directory.
*
* See resourcemanager.h for more information on how this works.
*
* @param path Absolute path to a model in .obj or another format accepted
* by assimp.
*/
void loadModel(const std::string & path);
void processNode(aiNode * node, const aiScene * scene);
ModelMesh processMesh(aiMesh * mesh, const aiScene * scene);
ModelMesh::Textures loadMaterialTextures(
aiMaterial * mat, aiTextureType type, const std::string & typeName);
void sortModels();
/*************************************************************************
* Private Members
************************************************************************/
/* The position of this model in 3D space */
Transform3D mTransform;
/* Static QHash used to store and access models globally. */
static ModelManager mManager;
/* Container to store N loaded textures for this model. */
ModelMesh::Textures mTexturesLoaded {};
/* Container to store N loaded meshes for this model. */
std::vector<ModelMesh> mMeshes {};
/* The directory this model and it's textures are stored. */
std::string mDirectory {};
/* File names for shaders and 3D model on disk. */
const char *mVertexShader, *mFragmentShader, *mModelPath;
/* Name of the model object within the scene. */
const char * mName;
};
} // namespace Qtk
#endif // QTK_MODEL_H

79
src/qtk/CMakeLists.txt Normal file
View File

@ -0,0 +1,79 @@
################################################################################
## 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 ##
################################################################################
################################################################################
# Qtk Library
################################################################################
set(
QTK_LIBRARY_PUBLIC_HEADERS
camera3d.h
input.h
meshrenderer.h
model.h
modelmesh.h
object.h
qtkapi.h
qtkiostream.h
qtkiosystem.h
scene.h
shape.h
skybox.h
texture.h
transform3D.h
)
set(
QTK_LIBRARY_SOURCES
camera3d.cpp
input.cpp
meshrenderer.cpp
model.cpp
modelmesh.cpp
object.cpp
qtkiostream.cpp
qtkiosystem.cpp
scene.cpp
shape.cpp
skybox.cpp
texture.cpp
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})
target_sources(
qtk_library PUBLIC
FILE_SET HEADERS
BASE_DIRS $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
BASE_DIRS $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
FILES ${QTK_LIBRARY_PUBLIC_HEADERS}
)
if(QTK_DEBUG)
target_compile_definitions(qtk_library PUBLIC QTK_DEBUG)
endif()
set_target_properties(
qtk_library PROPERTIES
VERSION ${PROJECT_VERSION}
)
target_link_libraries(
qtk_library PUBLIC
Qt6::Core Qt6::OpenGLWidgets Qt6::Widgets
)
if(QTK_UPDATE_SUBMODULES OR NOT ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk_library PUBLIC assimp)
elseif(ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk_library PUBLIC assimp::assimp)
endif()
if(WIN32)
target_link_libraries(qtk_library PUBLIC OpenGL::GL)
endif()

View File

@ -1,24 +1,27 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Fly camera class from tutorials followed at trentreed.net ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <camera3d.h>
#include "camera3d.h"
using namespace Qtk;
/*******************************************************************************
* Static Public Constants
******************************************************************************/
const QVector3D Camera3D::LocalForward(0.0f, 0.0f, -1.0f);
const QVector3D Camera3D::LocalUp(0.0f, 1.0f, 0.0f);
const QVector3D Camera3D::LocalRight(1.0f, 0.0f, 0.0f);
/*******************************************************************************
* Accessors
* Public Methods
******************************************************************************/
// Produces worldToView matrix
const QMatrix4x4 & Camera3D::toMatrix() {
mWorld.setToIdentity();
// Qt6 renamed QMatrix4x4::conjugate() to conjugated()

View File

@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Fly camera class from tutorials followed at trentreed.net ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@ -11,14 +11,14 @@
#include <QDebug>
#include <qtkapi.h>
#include <transform3D.h>
#include "qtkapi.h"
#include "transform3D.h"
namespace Qtk {
class QTKAPI Camera3D {
public:
/*************************************************************************
* Constants
* Static Public Constants
************************************************************************/
static const QVector3D LocalForward;
@ -29,39 +29,56 @@ namespace Qtk {
* Accessors
************************************************************************/
/**
* @return Transform3D associated with this camera.
*/
inline Transform3D & getTransform() { return mTransform; }
/**
* @return Current translation of the camera as a QVector3D.
*/
[[nodiscard]] inline const QVector3D & getTranslation() const {
return mTransform.getTranslation();
}
/**
* @return Current rotation of this camera as a QQuaternion.
*/
[[nodiscard]] inline const QQuaternion & getRotation() const {
return mTransform.getRotation();
}
const QMatrix4x4 & toMatrix();
// Queries
[[nodiscard]] inline QVector3D forward() const {
/**
* @return QVector3D for the forward vector of the camera.
*/
[[nodiscard]] inline QVector3D getForward() const {
return mTransform.getRotation().rotatedVector(LocalForward);
}
[[nodiscard]] inline QVector3D right() const {
/**
* @return QVector3D for the right vector of the camera.
*/
[[nodiscard]] inline QVector3D getRight() const {
return mTransform.getRotation().rotatedVector(LocalRight);
}
[[nodiscard]] inline QVector3D up() const {
/**
* @return QVector3D for the up vector of the camera.
*/
[[nodiscard]] inline QVector3D getUp() const {
return mTransform.getRotation().rotatedVector(LocalUp);
}
private:
/*************************************************************************
* Private Members
* Public Methods
************************************************************************/
Transform3D mTransform;
QMatrix4x4 mWorld;
/**
* @return World To View matrix for this camera.
*/
const QMatrix4x4 & toMatrix();
private:
/*************************************************************************
* Private Methods
************************************************************************/
@ -70,6 +87,13 @@ namespace Qtk {
friend QDataStream & operator<<(QDataStream & out, Camera3D & transform);
friend QDataStream & operator>>(QDataStream & in, Camera3D & transform);
#endif
/*************************************************************************
* Private Members
************************************************************************/
Transform3D mTransform;
QMatrix4x4 mWorld;
};
// Qt Streams

View File

@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Input class from tutorials followed at trentreed.net ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@ -11,13 +11,20 @@
#include <QCursor>
#include <input.h>
#include "input.h"
using namespace Qtk;
/*******************************************************************************
* Static Helper Structs
******************************************************************************/
/**
* Struct to hold key input state. When a key is pressed we construct this and
* store it within a KeyContainer (or ButtonContainer for mouse buttons).
*
* @tparam T Qt::Key or Qt::MouseButton input type for this instance.
*/
template <typename T> struct InputInstance : std::pair<T, Input::InputState> {
typedef std::pair<T, Input::InputState> base_class;
@ -28,6 +35,7 @@ template <typename T> struct InputInstance : std::pair<T, Input::InputState> {
inline InputInstance(T value, Input::InputState state) :
base_class(value, state) {}
// Allows use of std::find to search for a key's InputInstance
inline bool operator==(const InputInstance & rhs) const {
return this->first == rhs.first;
}
@ -53,14 +61,44 @@ static QPoint sg_mouseDelta;
* Static Inline Helper Functions
******************************************************************************/
/**
* Search for the InputInstance of a key.
*
* @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) {
return std::find(sg_keyInstances.begin(), sg_keyInstances.end(), value);
}
/**
* Search for the InputInstance of a mouse button.
*
* @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) {
return std::find(sg_buttonInstances.begin(), sg_buttonInstances.end(), value);
}
/**
* Check an InputInstance for the InputReleased state.
*
* @tparam TPair KeyInstance or ButtonInstance
* @param instance Instance to check for InputReleased state.
* @return True if the InputInstance is in the released state.
*/
template <typename TPair>
static inline bool CheckReleased(const TPair & instance) {
return instance.second == Input::InputReleased;
}
/**
* Updates an InputInstance and applies transitions if needed.
*
* @tparam TPair KeyInstance or ButtonInstance.
* @param instance The InputInstance to update.
*/
template <typename TPair> static inline void UpdateStates(TPair & instance) {
switch(instance.second) {
case Input::InputRegistered:
@ -77,11 +115,12 @@ template <typename TPair> static inline void UpdateStates(TPair & instance) {
}
}
template <typename TPair>
static inline bool CheckReleased(const TPair & instance) {
return instance.second == Input::InputReleased;
}
/**
* Updates InputInstance containers to track input state.
*
* @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) {
typedef typename Container::iterator Iter;
typedef typename Container::value_type TPair;
@ -96,27 +135,9 @@ template <typename Container> static inline void Update(Container & container) {
}
/*******************************************************************************
* Input Implementation
* Static Public Methods
******************************************************************************/
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);
return (it != sg_buttonInstances.end()) ? it->second : InputInvalid;
}
QPoint Input::mousePosition() {
return QCursor::pos();
}
QPoint Input::mouseDelta() {
return sg_mouseDelta;
}
void Input::update() {
// Update Mouse Delta
sg_mousePrevPosition = sg_mouseCurrPosition;
@ -160,3 +181,21 @@ void Input::reset() {
sg_keyInstances.clear();
sg_buttonInstances.clear();
}
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);
return (it != sg_buttonInstances.end()) ? it->second : InputInvalid;
}
QPoint Input::mousePosition() {
return QCursor::pos();
}
QPoint Input::mouseDelta() {
return sg_mouseDelta;
}

155
src/qtk/input.h Normal file
View File

@ -0,0 +1,155 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Input class from tutorials followed at trentreed.net ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTOPENGL_INPUT_H
#define QTOPENGL_INPUT_H
#include <QPoint>
#include <Qt>
#include "qtkapi.h"
namespace Qtk {
class QTKAPI Input {
public:
/*************************************************************************
* Typedefs
************************************************************************/
/**
* Possible key states. See UpdateStates for state transitions.
*
* When a key is pressed we enter states Registered->Triggered->Pressed.
* InputTriggered state will be met only once if a key is pressed or held.
* While a key is held down the state is InputPressed.
*
* When a key is released we enter InputUnregistered->InputReleased
* When an active InputInstance for a key has this state it is removed.
*/
enum InputState {
InputInvalid,
InputRegistered, // Initial state. Transitions to InputTriggered
InputUnregistered, // Transition to InputReleased
InputTriggered, // Transition to InputPressed
InputPressed, // State of a key while it is held down.
InputReleased // Released keys are removed from state containers.
};
/*************************************************************************
* Public Methods
************************************************************************/
//
// State updating.
/**
* Update state for all mouse button and key instances.
*/
static void update();
/**
* @param key Key to set InputRegistered state.
*/
static void registerKeyPress(int key);
/**
* @param key Key to set InputReleased state.
*/
static void registerKeyRelease(int key);
/**
* @param button Mouse button to set InputRegistered state.
*/
static void registerMousePress(Qt::MouseButton button);
/**
* @param button Mouse button to set InputReleased state.
*/
static void registerMouseRelease(Qt::MouseButton button);
/**
* Reset input state for all key and mouse buttons.
*/
static void reset();
//
// State Checking.
/**
* @param key Key to check state.
* @return True if the key is in InputTriggered state.
*/
inline static bool keyTriggered(Qt::Key key) {
return keyState(key) == InputTriggered;
}
/**
* @param key Key to check state.
* @return True if the key is in InputPressed state.
*/
inline static bool keyPressed(Qt::Key key) {
return keyState(key) == InputPressed;
}
/**
* @param key Key to check state.
* @return True if the key is in InputReleased state.
*/
inline static bool keyReleased(Qt::Key key) {
return keyState(key) == InputReleased;
}
/**
* @param button Mouse button to check state.
* @return True if the key is in InputTriggered state.
*/
inline static bool buttonTriggered(Qt::MouseButton button) {
return buttonState(button) == InputTriggered;
}
/**
* @param button Mouse button to check state.
* @return True if the key is in InputPressed state.
*/
inline static bool buttonPressed(Qt::MouseButton button) {
return buttonState(button) == InputPressed;
}
/**
* @param button Mouse button to check state.
* @return True if the key is in InputReleased state.
*/
inline static bool buttonReleased(Qt::MouseButton button) {
return buttonState(button) == InputReleased;
}
/**
* @param key The key to check InputState.
* @return The current InputState for the given key.
*/
static InputState keyState(Qt::Key key);
/**
* @param button The mouse button to check InputState.
* @return The current InputState for the mouse button.
*/
static InputState buttonState(Qt::MouseButton button);
/**
* @return QPoint representing the mouse position within the widget.
*/
static QPoint mousePosition();
/**
* @return Delta movement of mouse from previous to current position.
*/
static QPoint mouseDelta();
};
} // namespace Qtk
#endif // QTOPENGL_INPUT_H

View File

@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: MeshRenderer class for quick object creation and drawing ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@ -8,41 +8,41 @@
#include <QImageReader>
#include <meshrenderer.h>
#include <scene.h>
#include <texture.h>
#include "meshrenderer.h"
#include "scene.h"
#include "texture.h"
using namespace Qtk;
// Static QHash that holds all MeshRenderer instances using their mName as keys
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) :
MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS)) {}
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
Object(name, shape), mVertexShader(":/multi-color.vert"),
mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES) {
Object(name, shape, QTK_MESH), mVertexShader(":/shaders/multi-color.vert"),
mFragmentShader(":/shaders/multi-color.frag"), mDrawType(GL_TRIANGLES) {
mShape = Shape(shape);
init();
sInstances.insert(name, this);
}
MeshRenderer::~MeshRenderer() {
sInstances.remove(mName);
}
// Static member function to retrieve instances of MeshRenderers
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";
#endif
return nullptr;
}
return sInstances[name];
sInstances.remove(mName.c_str());
}
/*******************************************************************************
* Public Member Functions
* Public Methods
******************************************************************************/
void MeshRenderer::init() {
@ -123,33 +123,11 @@ void MeshRenderer::draw() {
releaseShaders();
}
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::enableAttributeArray(int location) {
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(projection, Scene::getProjectionMatrix());
mProgram.setUniformValue(view, Scene::getViewMatrix());
mProgram.setUniformValue(model, mTransform.toMatrix());
}
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++) {
mShape.mColors[i] = color;
}
}
// TODO: Factor this out so we don't need to reinitialize
init();
mVAO.bind();
mProgram.enableAttributeArray(location);
mVAO.release();
}
void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims) {
@ -185,11 +163,60 @@ void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims) {
mVAO.release();
}
/*******************************************************************************
* Inherited Virtual Member Functions
******************************************************************************/
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) {
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) {
Object::setShape(value);
init();
}
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++) {
mShape.mColors[i] = color;
}
}
// TODO: Factor this out so we don't need to reinitialize
init();
}
void MeshRenderer::setAttributeBuffer(
int location, GLenum type, int offset, int tupleSize, int stride) {
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.setAttributeBuffer(location, type, offset, tupleSize, stride);
mVAO.release();
}
/*******************************************************************************
* Static Public Methods
******************************************************************************/
// Static member function to retrieve instances of MeshRenderers
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";
#endif
return nullptr;
}
return sInstances[name];
}

226
src/qtk/meshrenderer.h Normal file
View File

@ -0,0 +1,226 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: MeshRenderer class for quick object creation and drawing ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_MESHRENDERER_H
#define QTK_MESHRENDERER_H
#include <utility>
#include "object.h"
#include "qtkapi.h"
#include "shape.h"
namespace Qtk {
class QTKAPI MeshRenderer : public Object {
public:
/*************************************************************************
* Typedefs
************************************************************************/
/** Static QHash of all mesh objects within the scene. */
typedef QHash<QString, MeshRenderer *> MeshManager;
/*************************************************************************
* Constructors / Destructors
************************************************************************/
/**
* Delegate constructor.
* Constructs a MeshRenderer with custom vertices and indices for more
* complex geometry.
*
* @param name Name to use for the new QObject.
* @param vertices Vertices to use for initializing geometry shape.
* @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);
/**
* Delegate constructor.
* Constructs a MeshRenderer with a default shape of a cube.
*
* @param name Name to use for the new QObject.
*/
explicit MeshRenderer(const char * name);
/**
* Construct a MeshRenderer.
* Default shaders will be used unless subsequently set by the caller.
*
* @param name Name to use for the new QObject.
* @param shape The shape of the MeshRenderer.
* For models this can be set using ShapeBase ctors.
*/
MeshRenderer(const char * name, const ShapeBase & shape);
~MeshRenderer();
/*************************************************************************
* Public Methods
************************************************************************/
/**
* Initializes OpenGL buffers and settings for this MeshRenderer.
*/
void init();
/**
* Draws this MeshRenderer.
*/
void draw();
/**
* Enables shader attribute array from the MeshRenderer's VAO.
* @param location Index location of the attribute array to enable.
*/
void enableAttributeArray(int location);
/**
* Reallocates texture coordinates to the mNBO member object.
*
* @param t Texture coordinates to reallocate.
* @param dims Number of dimensions to use for the coordinates.
*/
void reallocateTexCoords(const TexCoords & t, unsigned dims = 2);
/**
* Reallocates normals to the mNBO member object.
*
* @param n Normal coordinate to reallocate.
* @param dims Number of dimensions to use for the coordinates.
*/
void reallocateNormals(const Normals & n, unsigned dims = 3);
/*************************************************************************
* Setters
************************************************************************/
/**
* Set OpenGL draw type. GL_TRIANGLES, GL_POINTS, GL_LINES, etc.
*
* @param drawType The draw type to use for this MeshRenderer.
*/
inline void setDrawType(int drawType) { mDrawType = drawType; }
/**
* @param vert Path to vertex shader to use for this MeshRenderer.
*/
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) {
mFragmentShader = frag;
}
/**
* @param vert Path to vertex shader to use for this MeshRenderer.
* @param frag Path to fragment shader to use for this MeshRenderer.
*/
void setShaders(const std::string & vert, const std::string & frag);
/**
* @tparam T Type of the uniform value to set.
* @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) {
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(location, value);
}
/**
* @tparam T Type of the uniform value to set.
* @param location Name of the uniform value we are setting.
* @param value The value to use for the uniform.
*/
template <typename T>
inline void setUniform(const char * location, T value) {
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(location, value);
}
/**
* Sets the MVP matrices for this object within the scene.
* Model matrix is provided by this model's transform.
* View and Projection matrices are provided by the scene.
*
* @param model Name of the uniform to store the Model matrix.
* @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");
/**
* Sets the shape of the MeshRenderer using the Object base class method.
* The MeshRenderer will be reinitialized after this call using `init()`.
*
* @param value Shape to use for this MeshRenderer.
*/
void setShape(const Shape & value) override;
/**
* Sets all vertices in the mesh to a color.
* The MeshRenderer will be reinitialized after this call using `init()`.
*
* @param color The color to use for the entire mesh.
*/
void setColor(const QVector3D & color);
/**
* Updates an attribute buffer. This should be called whenever related
* buffers are reallocated. If the new buffer uses an identical format
* this may not be required.
*
* @param location Index location of the attribute buffer to set.
* @param type The type of the values within the attribute buffer.
* @param offset Offset to the beginning of the buffer.
* @param tupleSize Size of each group of elements in the buffer.
* For (x, y) positions this would be 2, (x, y, z) would be 3, etc.
* @param stride Stride between groups of elements in the buffer.
* For example (x, y) data stride is `2 * sizeof(type)`
*/
void setAttributeBuffer(
int location, GLenum type, int offset, int tupleSize, int stride = 0);
/*************************************************************************
* Accessors
************************************************************************/
/**
* Retrieve a mesh by name stored within static QHash private member
* @param name The name of the MeshRenderer we want to retrieve.
* @return Pointer to the MeshRenderer, or nullptr if not found.
*/
static MeshRenderer * getInstance(const QString & name);
/**
* @return Transform3D attached to this MeshRenderer.
*/
inline Transform3D & getTransform() { return mTransform; }
private:
/*************************************************************************
* Private Members
************************************************************************/
static MeshManager sInstances;
int mDrawType {};
std::string mVertexShader {}, mFragmentShader {};
};
} // namespace Qtk
#endif // QTK_MESHRENDERER_H

View File

@ -1,160 +1,37 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Model classes for importing with Assimp ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Model class for importing with Assimp ##
## From following tutorials on learnopengl.com ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <QFileInfo>
#include <model.h>
#include <resourcemanager.h>
#include <scene.h>
#include <texture.h>
#include "model.h"
#include "qtkiosystem.h"
#include "scene.h"
#include "texture.h"
using namespace Qtk;
/** Static QHash used to store and access models globally. */
Model::ModelManager Model::mManager;
// Static function to access ModelManager for getting Models by name
Model * Model::getInstance(const char * name) {
return mManager[name];
}
/*******************************************************************************
* ModelMesh Private Member Functions
******************************************************************************/
void ModelMesh::initMesh(const char * vert, const char * frag) {
initializeOpenGLFunctions();
// Create VAO, VBO, EBO
mVAO->create();
mVBO->create();
mEBO->create();
mVAO->bind();
// Allocate VBO
mVBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
mVBO->bind();
mVBO->allocate(mVertices.data(), mVertices.size() * sizeof(mVertices[0]));
// Allocate EBO
mEBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
mEBO->bind();
mEBO->allocate(mIndices.data(), mIndices.size() * sizeof(mIndices[0]));
mEBO->release();
// Load and link shaders
mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert);
mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag);
mProgram->link();
mProgram->bind();
// Positions
mProgram->enableAttributeArray(0);
mProgram->setAttributeBuffer(
0, GL_FLOAT, offsetof(ModelVertex, mPosition), 3, sizeof(ModelVertex));
// Normals
mProgram->enableAttributeArray(1);
mProgram->setAttributeBuffer(
1, GL_FLOAT, offsetof(ModelVertex, mNormal), 3, sizeof(ModelVertex));
// Texture Coordinates
mProgram->enableAttributeArray(2);
mProgram->setAttributeBuffer(
2, GL_FLOAT, offsetof(ModelVertex, mTextureCoord), 2,
sizeof(ModelVertex));
// Vertex tangents
mProgram->enableAttributeArray(3);
mProgram->setAttributeBuffer(
3, GL_FLOAT, offsetof(ModelVertex, mTangent), 3, sizeof(ModelVertex));
// Vertex bitangents
mProgram->enableAttributeArray(4);
mProgram->setAttributeBuffer(
4, GL_FLOAT, offsetof(ModelVertex, mBitangent), 3, sizeof(ModelVertex));
mProgram->release();
mVBO->release();
mVAO->release();
}
/*******************************************************************************
* ModelMesh Public Member Functions
******************************************************************************/
void ModelMesh::draw(QOpenGLShaderProgram & shader) {
mVAO->bind();
// Bind shader
shader.bind();
// Set Model View Projection values
shader.setUniformValue("uModel", mTransform.toMatrix());
shader.setUniformValue("uView", Scene::getViewMatrix());
shader.setUniformValue("uProjection", Scene::getProjectionMatrix());
GLuint diffuseCount = 1;
GLuint specularCount = 1;
GLuint normalCount = 1;
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();
// Get a name for the texture using a known convention -
// Diffuse: material.texture_diffuse1, material.texture_diffuse2, ...
// Specular: material.texture_specular1, material.texture_specular2, ...
std::string number;
std::string name = mTextures[i].mType;
if(name == "texture_diffuse") {
number = std::to_string(diffuseCount++);
}
if(name == "texture_specular") {
number = std::to_string(specularCount++);
}
if(name == "texture_normal") {
number = std::to_string(normalCount++);
}
// Set the uniform to track this texture ID using our naming convention
shader.setUniformValue((name + number).c_str(), i);
}
// Draw the mesh
glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
// Release shader, textures
for(const auto & texture : mTextures) {
texture.mTexture->release();
}
shader.release();
mVAO->release();
glActiveTexture(GL_TEXTURE0);
}
/*******************************************************************************
* Model Public Member Functions
* Public Member Functions
******************************************************************************/
void Model::draw() {
for(auto & mMeshe : mMeshes) {
mMeshe.mTransform = mTransform;
mMeshe.draw();
for(auto & mesh : mMeshes) {
mesh.mTransform = mTransform;
mesh.draw();
}
}
void Model::draw(QOpenGLShaderProgram & shader) {
for(auto & mMeshe : mMeshes) {
mMeshe.mTransform = mTransform;
mMeshe.draw(shader);
for(auto & mesh : mMeshes) {
mesh.mTransform = mTransform;
mesh.draw(shader);
}
}
@ -176,34 +53,38 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
}
}
// Static function to access ModelManager for getting Models by name
Model * Qtk::Model::getInstance(const char * name) {
return mManager[name];
}
/*******************************************************************************
* Model Private Member Functions
* Private Member Functions
******************************************************************************/
void Model::loadModel(const std::string & path) {
Assimp::Importer import;
// JIC a relative path was used, get the absolute file path
QFileInfo info(path.c_str());
info.makeAbsolute();
mDirectory = path[0] == ':' ? RM::getPath(path)
: info.absoluteFilePath().toStdString();
// If using a Qt Resource path, use QtkIOSystem for file handling.
if(path.front() == ':') {
import.SetIOHandler(new QtkIOSystem());
}
// Used as base path for loading model textures.
mDirectory = path.substr(0, path.find_last_of('/'));
// Import the model, converting non-triangular geometry to triangles
// + And flipping texture UVs, etc..
// Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
const aiScene * scene = import.ReadFile(
mDirectory, 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) {
qDebug() << "Error::ASSIMP::" << import.GetErrorString() << "\n";
return;
}
// If there were no errors, find the directory that contains this model
mDirectory = mDirectory.substr(0, mDirectory.find_last_of('/'));
// Pass the pointers to the root node and the scene to recursive function
// + Base case breaks when no nodes left to process on model
@ -212,10 +93,10 @@ void Model::loadModel(const std::string & path) {
// Sort models by their distance from the camera
// Optimizes drawing so that overlapping objects are not overwritten
// + Since the topmost object will be drawn first
sortModels();
sortModelMeshes();
// Object finished loading, insert it into ModelManager
mManager.insert(mName, this);
mManager.insert(getName().c_str(), this);
}
void Model::processNode(aiNode * node, const aiScene * scene) {
@ -300,7 +181,6 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
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
ModelMesh::Textures diffuseMaps = loadMaterialTextures(
material, aiTextureType_DIFFUSE, "texture_diffuse");
@ -320,7 +200,9 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
}
return {vertices, indices, textures, mVertexShader, mFragmentShader};
return {
vertices, indices, textures, mVertexShader.c_str(),
mFragmentShader.c_str()};
}
ModelMesh::Textures Model::loadMaterialTextures(
@ -364,7 +246,7 @@ ModelMesh::Textures Model::loadMaterialTextures(
return textures;
}
void Model::sortModels() {
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

204
src/qtk/model.h Normal file
View File

@ -0,0 +1,204 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Model class for importing with Assimp ##
## From following tutorials on learnopengl.com ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_MODEL_H
#define QTK_MODEL_H
// Qt
#include <QOpenGLFunctions>
// Assimp
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/Importer.hpp>
// Qtk
#include "modelmesh.h"
#include "qtkapi.h"
namespace Qtk {
/**
* Model object that has a ModelMesh.
* Top-level object that represents 3D models stored within a scene.
*/
class QTKAPI Model : public Object {
public:
/*************************************************************************
* Typedefs
************************************************************************/
/** ModelManager typedef that will manage global model access. */
typedef QHash<QString, Model *> ModelManager;
/*************************************************************************
* Constructors, Destructors
************************************************************************/
/**
* Constructs a Model
* If no shaders are provided we will use default shaders.
*
* @param name Name to use for the Model's objectName.
* @param path Path to the model to load for construction.
* @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) {
loadModel(mModelPath);
}
inline ~Model() override { mManager.remove(getName().c_str()); }
/*************************************************************************
* Public Methods
************************************************************************/
/**
* Draws the model using attached shader program.
*/
void draw();
/**
* Draws the model using a custom shader program.
*
* @param shader Shader program to use to draw the model.
*/
void draw(QOpenGLShaderProgram & shader);
/**
* Flip a texture associated with this model
*
* @param fileName The name of the texture to flip as it is stored on disk
* @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);
/*************************************************************************
* Setters
************************************************************************/
/**
* Sets a uniform value for each ModelMesh within this Model.
*
* @tparam T The type of the value we are settings
* @param location The uniform location
* @param value The value to assign to the uniform
*/
template <typename T>
inline void setUniform(const char * location, T value) {
for(auto & mesh : mMeshes) {
mesh.mProgram->bind();
mesh.mProgram->setUniformValue(location, value);
mesh.mProgram->release();
}
}
/*************************************************************************
* Accessors
************************************************************************/
/**
* Accessor function for retrieving a ModelMesh globally.
* The mesh is retrieved from the mManager private member.
*
* @param name The name of the model to load as it was constructed.
* @return Pointer to the model stored within the scene.
*/
[[nodiscard]] static Model * getInstance(const char * name);
/**
* @return Transform3D attached to this Model.
*/
inline Transform3D & getTransform() { return mTransform; }
private:
/*************************************************************************
* Private Methods
************************************************************************/
/**
* Loads a model in .obj, .fbx, .gltf, and other formats.
* For a full list of formats see assimp documentation:
* https://github.com/assimp/assimp/blob/master/doc/Fileformats.md
*
* Large models should not be loaded into Qt resource system.
* Instead pass an *absolute* path to this function.
* Relative paths will break if Qtk is executed from different locations.
*
* @param path Absolute path to a model in .obj or another format accepted
* by assimp.
*/
void loadModel(const std::string & path);
/**
* Process a node in the model's geometry using Assimp.
*
* @param node The Assimp node to process.
* @param scene The Assimp scene for the loaded model.
*/
void processNode(aiNode * node, const aiScene * scene);
/**
* Process a mesh within a node using Assimp.
*
* @param mesh The Assimp mesh to process.
* @param scene The Assimp scene for the loaded model.
* @return
*/
ModelMesh processMesh(aiMesh * mesh, const aiScene * scene);
/**
* Load a collection of material texture using Assimp.
* This function loads diffuse, specular, and narmal material textures.
* A Mesh may have many of any or all of the texture types above.
* Models can have many Meshes attached.
* This function returns all textures for a single Mesh within a Model.
*
* @param mat Loaded Assimp material.
* @param type Type of the material.
* @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);
/**
* Sorts each mesh in the Model based on distance from the camera.
* This is for efficient drawing in OpenGL by preventing the drawing of
* objects not visible due to being partially or entirely behind another
* object.
*/
void sortModelMeshes();
/*************************************************************************
* Private Members
************************************************************************/
/** Static QHash used to store and access models globally. */
static ModelManager mManager;
/** Container to store N loaded textures for this model. */
ModelMesh::Textures mTexturesLoaded {};
/** Container to store N loaded meshes for this model. */
std::vector<ModelMesh> mMeshes {};
/** The directory this model and it's textures are stored. */
std::string mDirectory {};
/** File names for shaders and 3D model on disk. */
std::string mVertexShader, mFragmentShader, mModelPath;
};
} // namespace Qtk
#endif // QTK_MODEL_H

129
src/qtk/modelmesh.cpp Normal file
View File

@ -0,0 +1,129 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: ModelMesh class for importing 3D models with Assimp ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include "modelmesh.h"
#include "scene.h"
using namespace Qtk;
/*******************************************************************************
* Public Member Functions
******************************************************************************/
void ModelMesh::draw(QOpenGLShaderProgram & shader) {
mVAO->bind();
// Bind shader
shader.bind();
// Set Model View Projection values
shader.setUniformValue("uModel", mTransform.toMatrix());
shader.setUniformValue("uView", Scene::getViewMatrix());
shader.setUniformValue("uProjection", Scene::getProjectionMatrix());
GLuint diffuseCount = 1;
GLuint specularCount = 1;
GLuint normalCount = 1;
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();
// Get a name for the texture using a known convention -
// Diffuse: material.texture_diffuse1, material.texture_diffuse2, ...
// Specular: material.texture_specular1, material.texture_specular2, ...
std::string number;
std::string name = mTextures[i].mType;
if(name == "texture_diffuse") {
number = std::to_string(diffuseCount++);
}
if(name == "texture_specular") {
number = std::to_string(specularCount++);
}
if(name == "texture_normal") {
number = std::to_string(normalCount++);
}
// Set the uniform to track this texture ID using our naming convention
shader.setUniformValue((name + number).c_str(), i);
}
// Draw the mesh
glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
// Release shader, textures
for(const auto & texture : mTextures) {
texture.mTexture->release();
}
shader.release();
mVAO->release();
glActiveTexture(GL_TEXTURE0);
}
/*******************************************************************************
* Private Member Functions
******************************************************************************/
void ModelMesh::initMesh(const char * vert, const char * frag) {
initializeOpenGLFunctions();
// Create VAO, VBO, EBO
bool status = mVAO->create();
mVBO->create();
mEBO->create();
mVAO->bind();
// Allocate VBO
mVBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
mVBO->bind();
mVBO->allocate(mVertices.data(), mVertices.size() * sizeof(mVertices[0]));
// Allocate EBO
mEBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
mEBO->bind();
mEBO->allocate(mIndices.data(), mIndices.size() * sizeof(mIndices[0]));
mEBO->release();
// Load and link shaders
mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert);
mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag);
mProgram->link();
mProgram->bind();
// Positions
mProgram->enableAttributeArray(0);
mProgram->setAttributeBuffer(
0, GL_FLOAT, offsetof(ModelVertex, mPosition), 3, sizeof(ModelVertex));
// Normals
mProgram->enableAttributeArray(1);
mProgram->setAttributeBuffer(
1, GL_FLOAT, offsetof(ModelVertex, mNormal), 3, sizeof(ModelVertex));
// Texture Coordinates
mProgram->enableAttributeArray(2);
mProgram->setAttributeBuffer(
2, GL_FLOAT, offsetof(ModelVertex, mTextureCoord), 2,
sizeof(ModelVertex));
// Vertex tangents
mProgram->enableAttributeArray(3);
mProgram->setAttributeBuffer(
3, GL_FLOAT, offsetof(ModelVertex, mTangent), 3, sizeof(ModelVertex));
// Vertex bitangents
mProgram->enableAttributeArray(4);
mProgram->setAttributeBuffer(
4, GL_FLOAT, offsetof(ModelVertex, mBitangent), 3, sizeof(ModelVertex));
mProgram->release();
mVBO->release();
mVAO->release();
}

139
src/qtk/modelmesh.h Normal file
View File

@ -0,0 +1,139 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: ModelMesh class for importing 3D models with Assimp ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_MODELMESH_H
#define QTK_MODELMESH_H
#include <QOpenGLFunctions>
#include "object.h"
#include "transform3D.h"
namespace Qtk {
/**
* 3D models will store this data for each vertex in geometry.
*/
struct QTKAPI ModelVertex {
QVector3D mPosition;
QVector3D mNormal;
QVector2D mTextureCoord;
QVector3D mTangent;
QVector3D mBitangent;
};
/**
* Struct to store model textures. 3D Models may have multiple.
*/
struct QTKAPI ModelTexture {
/** Texture ID for for this texture. */
GLuint mID {};
QOpenGLTexture * mTexture {};
/**
* Type of this texture in string format.
* See calls to Model::loadMaterialTexture in Model::processMesh
*/
std::string mType {};
/** Path to the model on disk. */
std::string mPath {};
};
class Model;
/**
* Mesh class specialized for storing 3D model data.
* Eventually this can be consolidated into a more generic class.
*/
class QTKAPI ModelMesh : protected QOpenGLFunctions {
public:
/*************************************************************************
* Typedefs
************************************************************************/
friend Model;
typedef std::vector<ModelVertex> Vertices;
typedef std::vector<GLuint> Indices;
typedef std::vector<ModelTexture> Textures;
/*************************************************************************
* Constructors, Destructors
************************************************************************/
/**
* Construct a ModelMesh.
* If no shaders are provided defaults will be used.
*
* @param vertices Vertex data to use for this ModelMesh.
* @param indices Index data to use for this ModelMesh.
* @param textures Collection of ModelTextures for this ModelMesh.
* @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") :
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)) {
initMesh(vertexShader, fragmentShader);
}
~ModelMesh() = default;
/*************************************************************************
* Public Methods
************************************************************************/
/**
* Draw the model with the attached shader program.
*/
inline void draw() { draw(*mProgram); }
/**
* Draw the model with a custom shader program.
* @param shader The shader program to use for drawing the object.
*/
void draw(QOpenGLShaderProgram & shader);
/*************************************************************************
* Public Members
************************************************************************/
Vertices mVertices {};
Indices mIndices {};
Textures mTextures {};
Transform3D mTransform;
private:
/*************************************************************************
* Private Methods
************************************************************************/
/**
* Initializes the buffers and shaders for this model mesh.
*
* @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);
/*************************************************************************
* Private Members
************************************************************************/
QOpenGLBuffer *mVBO, *mEBO;
QOpenGLVertexArrayObject * mVAO;
QOpenGLShaderProgram * mProgram;
};
} // namespace Qtk
#endif // QTK_MODELMESH_H

View File

@ -1,11 +1,11 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Object class for storing object data ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <object.h>
#include "object.h"
using namespace Qtk;

View File

@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Object class for storing object data ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@ -13,11 +13,13 @@
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <mesh.h>
#include <qtkapi.h>
#include <texture.h>
#include "qtkapi.h"
#include "shape.h"
#include "texture.h"
namespace Qtk {
class Model;
/**
* Object base class for objects that can exist within a scene.
* An object could be a Cube, Skybox, 3D Model, or other standalone entities.
@ -31,19 +33,32 @@ namespace Qtk {
************************************************************************/
friend MeshRenderer;
friend Model;
/**
* Enum flag to identify Object type without casting.
*/
enum Type { QTK_OBJECT, QTK_MESH, QTK_MODEL };
/*************************************************************************
* Constructors / Destructors
************************************************************************/
// Initialize an object with no shape data assigned
explicit Object(const char * name) :
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false) {}
explicit Object(const char * name, Type type) :
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false),
mType(type) {
initResources();
setObjectName(name);
}
// Initialize an object with shape data assigned
Object(const char * name, const ShapeBase & shape) :
Object(const char * name, const ShapeBase & shape, Type type) :
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape),
mBound(false) {}
mBound(false), mType(type) {
initResources();
setObjectName(name);
}
~Object() override = default;
@ -77,6 +92,10 @@ namespace Qtk {
return mShape.mVertices;
}
[[nodiscard]] inline std::string getName() const { return mName; }
[[nodiscard]] inline const Type & getType() const { return mType; }
/*************************************************************************
* Setters
************************************************************************/
@ -141,8 +160,9 @@ namespace Qtk {
Transform3D mTransform;
Shape mShape;
Texture mTexture;
const char * mName;
std::string mName;
bool mBound;
Type mType = QTK_OBJECT;
};
} // namespace Qtk

62
src/qtk/qtkapi.h Normal file
View File

@ -0,0 +1,62 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Main window for Qt6 OpenGL widget application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_QTKAPI_H
#define QTK_QTKAPI_H
#include <QFile>
#include <QWidget>
#include <QtCore/QtGlobal>
#ifdef QTK_SHARED
#if defined(QTK_EXPORT)
#define QTKAPI Q_DECL_EXPORT
#else
#define QTKAPI Q_DECL_IMPORT
#endif
#else
#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 {
/**
* Flag to set context for debug messages.
*/
enum DebugContext { Status, Debug, Warn, Error, Fatal };
/**
* Find top level parent for a widget.
*
* @param widget Widget to start the search from.
* @return Top level parent widget or Q_NULLPTR if no parent
*/
static QWidget * topLevelParent(QWidget * widget) {
QString name = widget->objectName();
while(widget->parentWidget() != Q_NULLPTR) {
widget = widget->parentWidget();
}
return widget;
}
/**
* @return Default icon to use for Qtk desktop application.
*/
static QIcon getIcon() {
return QIcon(":/icons/icon.png");
}
} // namespace Qtk
#endif // QTK_QTKAPI_H

82
src/qtk/qtkiostream.cpp Normal file
View File

@ -0,0 +1,82 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Custom IO stream for Qtk to support Qt Resource paths in Assimp ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include "qtkiostream.h"
using namespace Qtk;
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
QtkIOStream::QtkIOStream(const char * pFile, const char * pMode) :
mFile(pFile) {
QString mode(pMode);
bool read = mode.contains('r');
bool write = mode.contains('w');
if(read && write) {
mFile.open(QIODevice::ReadWrite);
} else if(read) {
mFile.open(QIODevice::ReadOnly);
} else if(write) {
mFile.open(QIODevice::WriteOnly);
} else {
qDebug() << "[Qtk::QtkIOStream] Invalid file open mode: " << mode << "\n";
}
}
/*******************************************************************************
* Public Member Functions
******************************************************************************/
size_t QtkIOStream::Read(void * pvBuffer, size_t pSize, size_t pCount) {
size_t read = 0;
do {
auto readSize = mFile.read((char *)pvBuffer + read, pSize);
if(readSize < 0) {
qDebug() << "[Qtk::QtkIOStream] Failed to read (" << pSize
<< ") bytes from file at: " << mFile.filesystemFileName().c_str()
<< "\n";
return -1;
}
read += readSize;
} while(pCount--);
return read;
}
size_t QtkIOStream::Write(const void * pvBuffer, size_t pSize, size_t pCount) {
size_t wrote = 0;
do {
auto writeSize = mFile.write((char *)pvBuffer + wrote, pSize);
if(writeSize < 0) {
qDebug() << "[Qtk::QtkIOStream] Failed to write buffer with size ("
<< pSize
<< ") to file at: " << mFile.filesystemFileName().c_str()
<< "\n";
return -1;
}
wrote += writeSize;
} while(pCount--);
return wrote;
}
aiReturn QtkIOStream::Seek(size_t pOffset, aiOrigin pOrigin) {
return mFile.seek(pOffset) ? aiReturn_SUCCESS : aiReturn_FAILURE;
}
size_t QtkIOStream::Tell() const {
return mFile.pos();
}
size_t QtkIOStream::FileSize() const {
return mFile.size();
}
void QtkIOStream::Flush() {
mFile.flush();
}

85
src/qtk/qtkiostream.h Normal file
View File

@ -0,0 +1,85 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Custom IO stream for Qtk to support Qt Resource paths in Assimp ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <QFile>
#include <QFileInfo>
#include <assimp/IOStream.hpp>
#include <assimp/IOSystem.hpp>
#ifndef QTK_QTKIOSTREAM_H
#define QTK_QTKIOSTREAM_H
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 {
friend class QtkIOSystem;
protected:
/** Constructor protected for private usage by QtkIOSystem */
QtkIOStream(const char * pFile, const char * pMode);
public:
~QtkIOStream() = default;
/**
* Reads data into pvBuffer in pCount batches of length pSize.
* The final pvBuffer will contain data read from all batches.
*
* @param pvBuffer Buffer to read data into.
* @param pSize Size in bytes for each read.
* @param pCount Number of reads to perform.
* @return Length of total bytes read into pvBuffer, or -1 on failure.
*/
size_t Read(void * pvBuffer, size_t pSize, size_t pCount) override;
/**
* Writes data from pvBuffer in pCount batches of length pSize.
* The final mFile member will contain all input data from pvBuffer.
*
* @param pvBuffer Buffer to write data from.
* @param pSize Size in bytes for each write.
* @param pCount Number of writes to perfom.
* @return Length of total bytes wrote into buffer, or -1 on failure.
*/
size_t Write(const void * pvBuffer, size_t pSize, size_t pCount) override;
/**
* Change the current read position in the mFile Qt resource.
*
* @param pOffset Offset position to set.
* @param pOrigin Origin position to use for relative offset.
* @return aiReturn_SUCCESS, or aiReturn_FAILURE on failure.
*/
aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override;
/**
* @return The current position in mFile.
*/
[[nodiscard]] size_t Tell() const override;
/**
* @return The total size of mFile.
*/
[[nodiscard]] size_t FileSize() const override;
/**
* Flushes buffered data to mFile.
*/
void Flush() override;
private:
// Corresponding file for Qt Resource path.
QFile mFile;
};
} // namespace Qtk
#endif // QTK_QTKIOSTREAM_H

39
src/qtk/qtkiosystem.cpp Normal file
View File

@ -0,0 +1,39 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Custom IO system for Qtk to support Qt Resource paths in Assimp ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include "qtkiosystem.h"
using namespace Qtk;
/*******************************************************************************
* Public Member Functions
******************************************************************************/
bool QtkIOSystem::Exists(const char * pFile) const {
return QFileInfo::exists(pFile);
}
char QtkIOSystem::getOsSeparator() const {
#ifndef _WIN32
return '/';
#else
return '\\';
#endif
}
Assimp::IOStream * QtkIOSystem::Open(const char * pFile, const char * pMode) {
if(!QFileInfo::exists(pFile)) {
qDebug() << "[Qtk::QtkIOSystem] failed to open file: " << pFile << "\n";
return nullptr;
}
return new QtkIOStream(pFile, pMode);
}
void QtkIOSystem::Close(Assimp::IOStream * pFile) {
delete pFile;
}

53
src/qtk/qtkiosystem.h Normal file
View File

@ -0,0 +1,53 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Custom IO system for Qtk to support Qt Resource paths in Assimp ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <assimp/IOStream.hpp>
#include <assimp/IOSystem.hpp>
#include "qtkapi.h"
#include "qtkiostream.h"
#ifndef QTK_QTKIOSYSTEM_H
#define QTK_QTKIOSYSTEM_H
namespace Qtk {
/**
* Assimp IO system for loading models with assimp, using Qt Resource paths.
*/
class QtkIOSystem : public Assimp::IOSystem {
public:
QtkIOSystem() = default;
~QtkIOSystem() = default;
/**
* @param pFile File path to check.
* @return True if the file exists, else false.
*/
bool Exists(const char * pFile) const override;
/**
* @return Path separator for platform OS.
*/
[[nodiscard]] char getOsSeparator() const override;
/**
* @param pFile File to open for read / writing.
* @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;
/**
* @param pFile File to close.
*/
void Close(Assimp::IOStream * pFile) override;
};
} // namespace Qtk
#endif // QTK_QTKIOSYSTEM_H

93
src/qtk/scene.cpp Normal file
View File

@ -0,0 +1,93 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Classes for managing objects and data within a scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include "scene.h"
#include "camera3d.h"
using namespace Qtk;
Camera3D Scene::mCamera;
QMatrix4x4 Scene::mProjection;
/*******************************************************************************
* Constructors / Destructors
******************************************************************************/
Scene::Scene() : mSceneName("Default Scene") {
mCamera.getTransform().setTranslation(0.0f, 0.0f, 20.0f);
mCamera.getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
Scene::~Scene() {
for(auto & mesh : mMeshes) {
delete mesh;
}
for(auto & model : mModels) {
delete model;
}
delete mSkybox;
}
/*******************************************************************************
* Public Methods
******************************************************************************/
void Scene::draw() {
if(!mInit) {
initializeOpenGLFunctions();
init();
mInit = true;
}
if(mSkybox != Q_NULLPTR) {
mSkybox->draw();
}
for(auto & model : mModels) {
model->draw();
}
for(const auto & mesh : mMeshes) {
mesh->draw();
}
}
std::vector<Object *> Scene::getObjects() const {
// All scene objects must inherit from Qtk::Object.
std::vector<Object *> objects(mMeshes.begin(), mMeshes.end());
for(auto model : mModels) {
objects.push_back(dynamic_cast<Object *>(model));
if(objects.back() == nullptr) {
return {};
}
}
return objects;
}
Object * Scene::getObject(const QString & name) {
for(auto object : getObjects()) {
if(object->getName() == name.toStdString()) {
return object;
}
}
return Q_NULLPTR;
}
void Scene::setSkybox(Skybox * skybox) {
delete mSkybox;
mSkybox = skybox;
}
template <> MeshRenderer * Scene::addObject(MeshRenderer * object) {
mMeshes.push_back(object);
sceneUpdated(mSceneName);
return object;
}
template <> Model * Scene::addObject(Model * object) {
mModels.push_back(object);
sceneUpdated(mSceneName);
return object;
}

221
src/qtk/scene.h Normal file
View File

@ -0,0 +1,221 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Classes for managing objects and data within a scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_SCENE_H
#define QTK_SCENE_H
#include <QMatrix4x4>
#include <utility>
#include "camera3d.h"
#include "meshrenderer.h"
#include "model.h"
#include "skybox.h"
namespace Qtk {
/**
* An abstract Scene class to inherit from when building new scenes.
*
* This class provides the following objects to any inheriting scene:
* Skybox, Camera
* This class also provides containers for N instances of these objects:
* MeshRenderers, Models
*
* To inherit from this class and define our own scene we must:
*
* Override and define the `init()` virtual member function. If we want our
* scene to render using a Skybox, we should also initialize the mSkybox
* member within the overridden definition of `init()` using
* `Scene::setSkybox(...)`
*
* If the scene is to render any kind of movement we are required to override
* the `update()` virtual method.
*
* 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 {
Q_OBJECT
public:
/*************************************************************************
* Contructors / Destructors
************************************************************************/
Scene();
virtual ~Scene();
/*************************************************************************
* Public Methods
************************************************************************/
/**
* Initialize objects within the scene
*/
virtual void init() = 0;
/**
* Function called during OpenGL drawing event.
*
* This function is only called when the widget is redrawn.
*/
virtual void draw();
/**
* Function called to update the QOpenGLWidget. Does not trigger a redraw.
*
* Calling this several times will still result in only one repaint.
*/
virtual void update() {}
/*************************************************************************
* Accessors
************************************************************************/
/**
* @return All Qtk::Objects within the scene.
* If any object is invalid, we return an empty vector.
*/
[[nodiscard]] std::vector<Object *> getObjects() const;
/**
* Retrieve and object from the scene by it's objectName.
*
* @param name The objectName to look for within this scene.
* @return The found object or Q_NULLPTR if none found.
*/
[[nodiscard]] Object * getObject(const QString & name);
/**
* @return Camera attached to this scene.
*/
[[nodiscard]] inline static Camera3D & getCamera() { return mCamera; }
/**
* @return View matrix for the camera attached to this scene.
*/
[[nodiscard]] inline static QMatrix4x4 getViewMatrix() {
return mCamera.toMatrix();
}
/**
* @return Projection matrix for the current view into the scene.
*/
[[nodiscard]] inline static QMatrix4x4 & getProjectionMatrix() {
return mProjection;
}
/**
* @return The active skybox for this scene.
*/
[[nodiscard]] inline Skybox * getSkybox() { return mSkybox; }
/**
* @return The name for this scene. This is entirely user defined and not
* a Qt objectName.
*/
[[nodiscard]] inline QString getSceneName() const { return mSceneName; }
/**
* @return All MeshRenderers within the scene.
*/
[[nodiscard]] inline const std::vector<MeshRenderer *> & getMeshes()
const {
return mMeshes;
}
/**
* @return All Models within the scene.
*/
[[nodiscard]] inline const std::vector<Model *> & getModels() const {
return mModels;
}
/*************************************************************************
* Setters
************************************************************************/
/**
* @param skybox New skybox to use for this scene.
*/
void setSkybox(Skybox * skybox);
/**
* Adds objects to the scene.
* This template provides explicit specializations for valid types.
* Adding any object other than these types will cause errors.
* TODO: Refactor to use Object base class container for scene objects.
*
* If creating a new object type for a scene, it must inherit Qtk::Object
* and provide a specialization for this method.
*
* @param object The new object to add to the scene.
* @return The object added to the scene.
*/
template <typename T> T * addObject(T * object);
/**
* @param name The name to use for this scene.
*/
inline void setSceneName(QString name) { mSceneName = std::move(name); }
signals:
/**
* Signal thrown when the scene is modified by adding or removing objects.
* This can be caught by a main application to update any associated data.
*
* @param sceneName The scene that has been updated.
*/
void sceneUpdated(QString sceneName);
private:
/*************************************************************************
* Private Members
************************************************************************/
static Camera3D mCamera;
static QMatrix4x4 mProjection;
bool mInit = false;
QString mSceneName;
/* The skybox for this scene. */
Skybox * mSkybox {};
/* MeshRenderers used simple geometry. */
std::vector<MeshRenderer *> mMeshes {};
/* Models used for storing 3D models in the scene. */
std::vector<Model *> mModels {};
};
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

View File

@ -1,12 +1,12 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Collection of static mesh data for quick initialization ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <mesh.h>
#include "shape.h"
using namespace Qtk;

View File

@ -1,25 +1,21 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Collection of static mesh data for quick initialization ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_MESH_H
#define QTK_MESH_H
#ifndef QTK_SHAPE_H
#define QTK_SHAPE_H
#include <utility>
#include <QOpenGLWidget>
#include <QVector2D>
#include <QVector3D>
#include <utility>
#include <qtkapi.h>
#include <transform3D.h>
namespace Qtk {
class MeshRenderer;
class Object;
#include "qtkapi.h"
#include "transform3D.h"
// Define vertices for drawing a cube using two faces (8 vertex points)
// Front Vertices
@ -62,7 +58,6 @@ namespace Qtk {
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK
// clang-format on
// Colors using QVector3Ds as RGB values
#define WHITE VECTOR_ONE
#define BLACK VECTOR_ZERO
@ -78,6 +73,11 @@ namespace Qtk {
#define UV_RIGHT QVector2D(0.0f, 1.0f)
#define UV_CORNER QVector2D(1.0f, 1.0f)
namespace Qtk {
class MeshRenderer;
class Object;
// TODO: Vertices.getData(); Vertices.getStride();
typedef std::vector<QVector3D> Vertices;
typedef std::vector<QVector3D> Colors;
@ -105,6 +105,15 @@ namespace Qtk {
* Constructors / Destructors
************************************************************************/
/**
*
* @param mode OpenGL draw mode to use for this shape.
* @param v Vertex data for this shape.
* @param i Index data for this shape.
* @param c Color data for this shape.
* @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 = {}) :
@ -117,24 +126,42 @@ namespace Qtk {
* Accessors
************************************************************************/
/**
* @return Vertex data for this shape.
*/
[[nodiscard]] inline const Vertices & getVertices() const {
return mVertices;
}
/**
* @return Index data for this shape.
*/
[[nodiscard]] inline const Indices & getIndexData() const {
return mIndices;
}
/**
* @return Color data for this shape.
*/
[[nodiscard]] inline const Colors & getColors() const { return mColors; }
/**
* @return Texture coordinates for this shape.
*/
[[nodiscard]] inline const TexCoords & getTexCoords() const {
return mTexCoords;
}
/**
* @return Normals for this shape.
*/
[[nodiscard]] inline const Normals & getNormals() const {
return mNormals;
}
/**
* @return Stride for texture coordinates on this shape.
*/
[[nodiscard]] inline size_t getTexCoordsStride() const {
return mTexCoords.size() * sizeof(mTexCoords[0]);
}
@ -145,7 +172,6 @@ namespace Qtk {
************************************************************************/
DrawMode mDrawMode;
Vertices mVertices {};
Colors mColors {};
Indices mIndices {};
@ -160,6 +186,7 @@ namespace Qtk {
************************************************************************/
friend MeshRenderer;
friend Object;
/*************************************************************************
@ -174,30 +201,45 @@ namespace Qtk {
* Setters
************************************************************************/
/**
* @param value Vertex data to use for this shape.
*/
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) {
mIndices = value;
}
/**
* @param value Color data to use for this shape.
*/
virtual inline void setColors(const Colors & value) { mColors = value; }
/**
* @param value Texture coordinates to use for this shape.
*/
virtual inline void setTexCoords(const TexCoords & value) {
mTexCoords = value;
}
/**
* @param value Normals to use for this shape.
*/
virtual inline void setNormals(const Normals & value) {
mNormals = value;
}
/**
* @param value Shape to copy into this Shape.
*/
virtual inline void setShape(const Shape & value) { *this = value; }
};
/* Primitives inherit from ShapeBase, doesn't allow setting shape values. */
class QTKAPI Mesh {};
/* Simple Cube shape. */
struct QTKAPI Cube : public ShapeBase {
explicit Cube(DrawMode mode = QTK_DRAW_ARRAYS);
@ -209,4 +251,4 @@ namespace Qtk {
};
} // namespace Qtk
#endif // QTK_MESH_H
#endif // QTK_SHAPE_H

View File

@ -1,17 +1,32 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Skybox class using QtOpenGL ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <scene.h>
#include <skybox.h>
#include <texture.h>
#include "skybox.h"
#include "scene.h"
#include "texture.h"
using namespace Qtk;
/*******************************************************************************
* Constructors / Destructors
******************************************************************************/
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);
init();
}
Skybox::Skybox(
const std::string & right, const std::string & top,
const std::string & front, const std::string & left,
@ -27,16 +42,6 @@ Skybox::Skybox(
QImage(back.c_str()));
}
Skybox::Skybox(const std::string & name) :
Skybox(
":/right.png", ":/top.png", ":/front.png", ":/left.png", ":/bottom.png",
":/back.png", name) {}
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) {
mTexture.setTexture(cubeMap);
init();
}
/*******************************************************************************
* Public Member Functions
******************************************************************************/
@ -73,8 +78,10 @@ void Skybox::init() {
// Set up shader program
mProgram.create();
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/skybox.vert");
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/skybox.frag");
mProgram.addShaderFromSourceFile(
QOpenGLShader::Vertex, ":/shaders/skybox.vert");
mProgram.addShaderFromSourceFile(
QOpenGLShader::Fragment, ":/shaders/skybox.frag");
mProgram.link();
mProgram.bind();

View File

@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Skybox class using QtOpenGL ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@ -15,10 +15,10 @@
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <camera3d.h>
#include <mesh.h>
#include <qtkapi.h>
#include <texture.h>
#include "camera3d.h"
#include "qtkapi.h"
#include "shape.h"
#include "texture.h"
namespace Qtk {
/**
@ -33,11 +33,35 @@ namespace Qtk {
************************************************************************/
// Delegate this constructor to use default skybox images
/**
* Construct Skybox using default images.
*
* @param name The objectName to use for the Skybox.
*/
explicit Skybox(const std::string & name = "Skybox");
/**
* Construct a skybox with an existing QOpenGLTexture.
* The texture should be a fully initialized cube map.
*
* @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");
/**
* Construct a Skybox.
*
* @param right Image to use for the right side of the Skybox.
* @param top Image to use for the top side of the Skybox.
* @param front Image to use for the front side of the Skybox.
* @param left Image to use for the left side of the Skybox.
* @param bottom Image to use for the bottom side of the Skybox.
* @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,
@ -50,6 +74,9 @@ namespace Qtk {
* Public Methods
************************************************************************/
/**
* Draws the skybox.
*/
void draw();
private:
@ -57,6 +84,9 @@ namespace Qtk {
* Private Methods
************************************************************************/
/**
* Initializes OpenGL buffers and shaders for this skybox.
*/
void init();
/*************************************************************************

View File

@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Texture class to help with texture and image initializations ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@ -8,9 +8,8 @@
#include <QDebug>
#include <QImageReader>
#include <utility>
#include <texture.h>
#include "texture.h"
using namespace Qtk;
@ -20,8 +19,8 @@ QImage * OpenGLTextureFactory::initImage(
QImageReader::setAllocationLimit(512);
auto loadedImage = new QImage(QImage(image).mirrored(flipX, flipY));
if(loadedImage->isNull()) {
qDebug() << "Error loading image: " << image << "\n";
qDebug() << QImageReader::supportedImageFormats();
qDebug() << "[Qtk::OpenGLTextureFactory] Error loading image: " << image
<< "\nSupported types: " << QImageReader::supportedImageFormats();
return Q_NULLPTR;
}
@ -58,9 +57,7 @@ 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 = {std::move(right), std::move(top),
std::move(front), std::move(left),
std::move(bottom), std::move(back)};
std::vector<QImage> faceTextures = {right, top, front, left, bottom, back};
// Initialize skybox cubemap texture
texture->create();
texture->bind();

View File

@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Texture class to help with texture and image initializations ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@ -9,11 +9,12 @@
#ifndef QTOPENGL_TEXTURE_H
#define QTOPENGL_TEXTURE_H
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <utility>
#include <qtkapi.h>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include "qtkapi.h"
namespace Qtk {
/**
@ -155,52 +156,109 @@ namespace Qtk {
Texture() = default;
/**
* Copies an existing Texture object.
*
* @param value Texture to copy.
*/
Texture(const Texture & value) {
mOpenGLTexture = OpenGLTextureFactory::initTexture(value.mPath);
mPath = value.mPath;
}
/**
* @param path Path to texture to load on disk.
* @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) :
mOpenGLTexture(OpenGLTextureFactory::initTexture(path, flipX, flipY)),
mPath(path) {}
/**
* Construct a Texture using an existing QOpenGLTexture.
*
* @param texture OpenGL texture to use for this Texture.
*/
explicit Texture(QOpenGLTexture * texture) : mOpenGLTexture(texture) {}
~Texture() { mOpenGLTexture->destroy(); }
/*************************************************************************
* Public Methods
************************************************************************/
/**
* @return True if the OpenGL texture has been initialized.
*/
[[nodiscard]] inline bool hasTexture() const {
return mOpenGLTexture != Q_NULLPTR;
}
/*************************************************************************
* Accessors
************************************************************************/
/**
* @return QOpenGLTexture associated with this Texture.
*/
[[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const {
return *mOpenGLTexture;
}
/**
* @return Path to this Texture on disk.
*/
[[nodiscard]] inline std::string getPath() const { return mPath; }
/*************************************************************************
* Setters
************************************************************************/
void setTexture(
/**
* Replaces the current texture with a new texture.
*
* @param path Path to the new texture to load.
* @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) {
mOpenGLTexture =
OpenGLTextureFactory::initTexture(path.data(), flipX, flipY);
mPath = path.data();
setTexture(path.c_str(), flipX, flipY);
}
void setTexture(
/**
* @param path Path to the new texture to load.
* @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) {
mOpenGLTexture = OpenGLTextureFactory::initTexture(path, flipX, flipY);
mPath = path;
}
/**
* Sets this Texture to be a cube map with all identical sides.
*
* @param path Path to texture to use for all sides of the cube map.
*/
virtual inline void setCubeMap(const char * path) {
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(path);
mPath = path;
}
/**
* 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.
*/
virtual inline void setCubeMap(
const char * right, const char * top, const char * front,
const char * left, const char * bottom, const char * back) {
@ -208,6 +266,16 @@ namespace Qtk {
right, top, front, left, bottom, back);
}
/**
* 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.
*/
virtual inline void setCubeMap(
const QImage & right, const QImage & top, const QImage & front,
const QImage & left, const QImage & bottom, const QImage & back) {
@ -215,18 +283,14 @@ namespace Qtk {
right, top, front, left, bottom, back);
}
/*************************************************************************
* Public Methods
************************************************************************/
[[nodiscard]] inline bool hasTexture() const {
return mOpenGLTexture != Q_NULLPTR;
}
private:
/*************************************************************************
* Private Members
************************************************************************/
/**
* @param texture QOpenGLTexture to use for this Texture.
*/
inline void setTexture(QOpenGLTexture * texture) {
mOpenGLTexture = texture;
}
@ -235,7 +299,6 @@ namespace Qtk {
/* Path to this texture on disk or Qt resource. */
const char * mPath {};
};
} // namespace Qtk
#endif // QTOPENGL_TEXTURE_H

View File

@ -1,13 +1,13 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Transform3D class to represent object position in 3D space ##
## From following tutorials at trentreed.net ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <transform3D.h>
#include "transform3D.h"
using namespace Qtk;
@ -16,7 +16,7 @@ const QVector3D Transform3D::LocalUp(0.0f, 1.0f, 0.0f);
const QVector3D Transform3D::LocalRight(1.0f, 0.0f, 0.0f);
/*******************************************************************************
* Transformations
* Public Methods
******************************************************************************/
void Transform3D::translate(const QVector3D & dt) {
@ -29,19 +29,16 @@ void Transform3D::scale(const QVector3D & ds) {
mScale *= ds;
}
void Transform3D::rotate(const QQuaternion & dr) {
m_dirty = true;
mRotation = dr * mRotation;
}
void Transform3D::grow(const QVector3D & ds) {
m_dirty = true;
mScale += ds;
}
/*******************************************************************************
* Setters
******************************************************************************/
void Transform3D::rotate(const QQuaternion & dr) {
m_dirty = true;
mRotation = dr * mRotation;
}
void Transform3D::setTranslation(const QVector3D & t) {
m_dirty = true;
@ -58,12 +55,6 @@ void Transform3D::setRotation(const QQuaternion & r) {
mRotation = r;
}
/*******************************************************************************
* Accessors
******************************************************************************/
// Produces modelToWorld matrix using current set of transformations
// Transformation * rotation * scale = modelToWorld
const QMatrix4x4 & Transform3D::toMatrix() {
if(m_dirty) {
m_dirty = false;
@ -75,10 +66,6 @@ const QMatrix4x4 & Transform3D::toMatrix() {
return mWorld;
}
/*******************************************************************************
* Queries
******************************************************************************/
QVector3D Transform3D::getForward() const {
return mRotation.rotatedVector(LocalForward);
}
@ -89,13 +76,10 @@ QVector3D Transform3D::getUp() const {
QVector3D Transform3D::getRight() const {
return mRotation.rotatedVector(LocalRight);
while(true) {
int xx;
};
}
/*******************************************************************************
* QT Streams
* Private Methods
******************************************************************************/
namespace Qtk {

View File

@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Transform3D class to represent object position in 3D space ##
## From following tutorials at trentreed.net ##
## ##
@ -15,12 +15,10 @@
#include <QVector3D>
#ifndef QT_NO_DEBUG_STREAM
#include <QDebug>
#endif
#include <qtkapi.h>
#include "qtkapi.h"
namespace Qtk {
/**
@ -37,75 +35,154 @@ namespace Qtk {
mTranslation(0.0f, 0.0f, 0.0f) {}
/*************************************************************************
* Transformations
* Public Methods
************************************************************************/
/**
* @param dt Translation from last to current position.
*/
void translate(const QVector3D & dt);
/**
* @param dx X translation from last to current position.
* @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) {
translate(QVector3D(dx, dy, dz));
}
// Scale object with multiplication
/**
* Scale the object size.
*
* @param ds Scalar vector to apply to the transform.
*/
void scale(const QVector3D & ds);
/**
* Scale the object size.
*
* @param dx Amount to scale on the X axis.
* @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) {
scale(QVector3D(dx, dy, dz));
}
/**
* Scale the object size.
*
* @param factor Scalar to apply to all axis of the object.
*/
inline void scale(float factor) {
scale(QVector3D(factor, factor, factor));
}
// Multiplying by a rotation
void rotate(const QQuaternion & dr);
inline void rotate(float angle, const QVector3D & axis) {
rotate(QQuaternion::fromAxisAndAngle(axis, angle));
}
inline void rotate(float angle, float ax, float ay, float az) {
rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
}
// Scale object by addition
/**
* @param ds 3D vector to add to scale axis.
*/
void grow(const QVector3D & ds);
/**
* @param dx Amount to grow X axis.
* @param dy Amount to grow Y axis.
* @param dz Amount to grow Z axis.
*/
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) {
grow(QVector3D(factor, factor, factor));
}
/**
* @param dr Rotation to apply to the transform.
*/
void rotate(const QQuaternion & dr);
/**
* @param angle Angle to rotate.
* @param axis Axis to rotate apply the rotation on.
*/
inline void rotate(float angle, const QVector3D & axis) {
rotate(QQuaternion::fromAxisAndAngle(axis, angle));
}
/**
* Apply rotation upon an axis represented by the 3D vector (x, y, z)
*
* @param angle Angle to rotate.
* @param ax X axis to apply the rotation on.
* @param 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) {
rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
}
/*************************************************************************
* Setters
************************************************************************/
// Set object position
/**
* @param t Position to move the transform to.
*/
void setTranslation(const QVector3D & t);
/**
* @param x X position to set transform.
* @param y Y position to set transform.
* @param z Z position to set transform.
*/
inline void setTranslation(float x, float y, float z) {
setTranslation(QVector3D(x, y, z));
}
// Set object scale
/**
* @param s Scale to set for this transform.
*/
void setScale(const QVector3D & s);
/**
* @param x X axis scale to set for this transform.
* @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) {
setScale(QVector3D(x, y, z));
}
/**
* @param k Scale to set for all axis on this transform.
*/
inline void setScale(float k) { setScale(QVector3D(k, k, k)); }
// Set object rotation
/**
* @param r Rotation to set for this transform.
*/
void setRotation(const QQuaternion & r);
/**
* @param angle Angle to set for rotation.
* @param axis Axis to set rotation for.
*/
inline void setRotation(float angle, const QVector3D & axis) {
setRotation(QQuaternion::fromAxisAndAngle(axis, angle));
}
/**
* Sets a rotation upon an axis represented by the 3D vector (x, y, z)
*
* @param angle Angle to set rotation.
* @param ax X axis to set angle for.
* @param 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) {
setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
}
@ -114,31 +191,55 @@ namespace Qtk {
* Getters
************************************************************************/
/**
* @return Translation for this transform.
*/
[[nodiscard]] inline const QVector3D & getTranslation() const {
return mTranslation;
}
/**
* @return Scale for this transform.
*/
[[nodiscard]] inline const QVector3D & getScale() const { return mScale; }
/**
* @return Rotation for this transform.
*/
[[nodiscard]] inline const QQuaternion & getRotation() const {
return mRotation;
}
/**
* @return Model to world matrix for this transform.
* transformation * rotation * scale = ModelToWorld
*/
const QMatrix4x4 & toMatrix();
/**
* @return Forward vector for this transform.
*/
[[nodiscard]] QVector3D getForward() const;
/**
* @return Up vector for this transform.
*/
[[nodiscard]] QVector3D getUp() const;
/**
* @return Right vector for this transform.
*/
[[nodiscard]] QVector3D getRight() const;
/*************************************************************************
* Public members
* Public Members
************************************************************************/
static const QVector3D LocalForward, LocalUp, LocalRight;
private:
/*************************************************************************
* Private members
* Private Members
************************************************************************/
QVector3D mTranslation;
@ -156,7 +257,6 @@ namespace Qtk {
#endif
};
// Qt Streams
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform);
#endif

View File

@ -1,2 +0,0 @@
#define QTK_RESOURCES "@QTK_RESOURCES@"

View File

@ -1,53 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Classes for managing objects and data within a scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <camera3d.h>
#include <resourcemanager.h>
#include <scene.h>
#include <texture.h>
using namespace Qtk;
Camera3D Scene::mCamera;
QMatrix4x4 Scene::mProjection;
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
Scene::Scene() {
mCamera.getTransform().setTranslation(0.0f, 0.0f, 20.0f);
mCamera.getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
Scene::~Scene() {
for(auto & mesh : mMeshes) {
delete mesh;
}
for(auto & model : mModels) {
delete model;
}
delete mSkybox;
}
void Scene::privateDraw() {
if(!mInit) {
initializeOpenGLFunctions();
init();
mInit = true;
}
if(mSkybox != Q_NULLPTR) {
mSkybox->draw();
}
for(auto & model : mModels) {
model->draw();
}
for(const auto & mesh : mMeshes) {
mesh->draw();
}
}

View File

@ -1,125 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Classes for managing objects and data within a scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_SCENE_H
#define QTK_SCENE_H
#include <camera3d.h>
#include <meshrenderer.h>
#include <model.h>
#include <skybox.h>
#include <QMatrix4x4>
namespace Qtk {
/**
* An abstract Scene class to inherit from when building new scenes.
*
* This class provides the following objects to any inheriting scene:
* Skybox, Camera
* This class also provides containers for N instances of these objects:
* MeshRenderers, Models
*
* To inherit from this class and define our own scene we must:
*
* Override and define the `init()` virtual member function. If we want our
* scene to render using a Skybox, we should also initialize the mSkybox
* member within the overridden definition of `init()` using
* `Scene::setSkybox(...)`
*
* If the scene is to render any kind of movement we are required to override
* the `update()` virtual method.
*
* If the child scene adds any objects which are not managed (drawn) by this
* base class, the child scene class must also override the `draw()` method.
*/
class Scene : protected QOpenGLFunctions {
public:
/*************************************************************************
* Contructors / Destructors
************************************************************************/
Scene();
~Scene();
/*************************************************************************
* Public Methods
************************************************************************/
/**
* Initialize objects within the scene
*/
virtual void init() = 0;
/**
* Function called during OpenGL drawing event.
*
* This function is only called when the widget is redrawn.
*/
virtual void draw() { privateDraw(); };
/**
* Function called to update the QOpenGLWidget. Does not trigger a redraw.
*
* Calling this several times will still result in only one repaint.
*/
virtual void update() {}
/*************************************************************************
* Accessors
************************************************************************/
static Camera3D & getCamera() { return mCamera; }
static QMatrix4x4 getViewMatrix() { return mCamera.toMatrix(); }
static QMatrix4x4 & getProjectionMatrix() { return mProjection; }
inline Skybox * getSkybox() { return mSkybox; }
/*************************************************************************
* Setters
************************************************************************/
inline void setSkybox(Skybox * skybox) { mSkybox = skybox; }
private:
/*************************************************************************
* Private Members
************************************************************************/
static Camera3D mCamera;
static QMatrix4x4 mProjection;
bool mInit = false;
/*************************************************************************
* Private Methods
************************************************************************/
/**
* Handles drawing members encapsulated by this base class.
* Child classes do not need to draw these objects manually.
*/
void privateDraw();
protected:
/*************************************************************************
* Protected Members
************************************************************************/
/* The skybox for this scene. */
Skybox * mSkybox {};
/* MeshRenderers used simple geometry. */
std::vector<MeshRenderer *> mMeshes {};
/* Models used for storing 3D models in the scene. */
std::vector<Model *> mModels {};
};
} // namespace Qtk
#endif // QTK_SCENE_H