Compare commits

..

1 Commits

Author SHA1 Message Date
Shaun Reed f95fef4d7c Embed Qtk widget into QMainWindow application
+ Fix keyboard input focus to bind on mouse click
+ Fix missing call to `setFormat` in `MainWidget` ctor
+ Add placeholder toolbar options
2022-03-14 02:08:17 -04:00
121 changed files with 89505 additions and 8813 deletions

View File

@ -1,73 +0,0 @@
---
BasedOnStyle: Google
AlignAfterOpenBracket: AlwaysBreak
AlignArrayOfStructures: Left
AlignConsecutiveAssignments: None
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: Consecutive
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakTemplateDeclarations: MultiLine
InsertBraces: true
IndentAccessModifiers: true
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
BreakStringLiterals: true
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
QualifierAlignment: Left
ReferenceAlignment: Middle
DerivePointerAlignment: false
SpaceAroundPointerQualifiers: Both
FixNamespaceComments: true
IncludeBlocks: Preserve
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
ObjCBlockIndentWidth: 2
PointerAlignment: Middle
ReflowComments: true
SortIncludes: CaseSensitive
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeParens: Never
SpaceBeforeRangeBasedForLoopColon: true
SpacesBeforeTrailingComments: 2
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Latest
TabWidth: 2
UseTab: Never
...

View File

@ -1,146 +0,0 @@
# Generated from CLion Inspection settings
---
HeaderFilterRegex: "*.h"
UseColor: true
Checks: '-*,
bugprone-argument-comment,
bugprone-assert-side-effect,
bugprone-bad-signal-to-kill-thread,
bugprone-branch-clone,
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
bugprone-fold-init-type,
bugprone-forward-declaration-namespace,
bugprone-forwarding-reference-overload,
bugprone-inaccurate-erase,
bugprone-incorrect-roundings,
bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-arithmetic-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multiple-statement-macro,
bugprone-no-escape,
bugprone-not-null-terminated-result,
bugprone-parent-virtual-call,
bugprone-posix-return,
bugprone-reserved-identifier,
bugprone-sizeof-container,
bugprone-sizeof-expression,
bugprone-spuriously-wake-up-functions,
bugprone-string-constructor,
bugprone-string-integer-assignment,
bugprone-string-literal-with-embedded-nul,
bugprone-suspicious-enum-usage,
bugprone-suspicious-include,
bugprone-suspicious-memory-comparison,
bugprone-suspicious-memset-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-semicolon,
bugprone-suspicious-string-compare,
bugprone-swapped-arguments,
bugprone-terminating-continue,
bugprone-throw-keyword-missing,
bugprone-too-small-loop-variable,
bugprone-undefined-memory-manipulation,
bugprone-undelegated-constructor,
bugprone-unhandled-self-assignment,
bugprone-unused-raii,
bugprone-unused-return-value,
bugprone-use-after-move,
bugprone-virtual-near-miss,
cert-dcl21-cpp,
cert-dcl58-cpp,
cert-err34-c,
cert-err52-cpp,
cert-err60-cpp,
cert-flp30-c,
cert-msc50-cpp,
cert-msc51-cpp,
cert-str34-c,
cppcoreguidelines-interfaces-global-init,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-slicing,
google-explicit-constructor,
google-runtime-operator,
hicpp-exception-baseclass,
hicpp-multiway-paths-covered,
misc-misplaced-const,
misc-new-delete-overloads,
misc-non-copyable-objects,
misc-throw-by-value-catch-by-reference,
misc-unconventional-assign-operator,
misc-uniqueptr-reset-release,
modernize-avoid-bind,
modernize-concat-nested-namespaces,
modernize-deprecated-headers,
modernize-deprecated-ios-base-aliases,
modernize-loop-convert,
modernize-make-shared,
modernize-make-unique,
modernize-pass-by-value,
modernize-raw-string-literal,
modernize-redundant-void-arg,
modernize-replace-auto-ptr,
modernize-replace-disallow-copy-and-assign-macro,
modernize-replace-random-shuffle,
modernize-return-braced-init-list,
modernize-shrink-to-fit,
modernize-unary-static-assert,
modernize-use-auto,
modernize-use-bool-literals,
modernize-use-emplace,
modernize-use-equals-default,
modernize-use-equals-delete,
modernize-use-nodiscard,
modernize-use-noexcept,
modernize-use-nullptr,
modernize-use-override,
modernize-use-transparent-functors,
modernize-use-uncaught-exceptions,
mpi-buffer-deref,
mpi-type-mismatch,
openmp-use-default-none,
performance-faster-string-find,
performance-for-range-copy,
performance-implicit-conversion-in-loop,
performance-inefficient-algorithm,
performance-inefficient-string-concatenation,
performance-inefficient-vector-operation,
performance-move-const-arg,
performance-move-constructor-init,
performance-no-automatic-move,
performance-noexcept-move-constructor,
performance-trivially-destructible,
performance-type-promotion-in-math-fn,
performance-unnecessary-copy-initialization,
performance-unnecessary-value-param,
portability-simd-intrinsics,
readability-avoid-const-params-in-decls,
readability-const-return-type,
readability-container-size-empty,
readability-delete-null-pointer,
readability-deleted-default,
readability-inconsistent-declaration-parameter-name,
readability-make-member-function-const,
readability-misleading-indentation,
readability-misplaced-array-index,
readability-non-const-parameter,
readability-redundant-control-flow,
readability-redundant-declaration,
readability-redundant-function-ptr-dereference,
readability-braces-around-statements,
readability-redundant-smartptr-get,
readability-redundant-string-cstr,
readability-redundant-string-init,
readability-simplify-subscript-expr,
readability-static-accessed-through-instance,
readability-static-definition-in-anonymous-namespace,
readability-string-compare,
readability-uniqueptr-delete-release,
readability-use-anyofallof'

View File

@ -1,332 +0,0 @@
name: All Builds
on:
push:
pull_request:
workflow_dispatch:
env:
QT_VERSION: 6.6.0
jobs:
Qtk:
env:
CONFIG: -DQTK_SUBMODULES=ON -DQTK_DEBUG=OFF -DQTK_CCACHE=OFF -DQTK_GUI=ON -DQTK_PLUGINS=OFF -DQTK_EXAMPLE=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/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG
flags: ''
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: ${{ env.QT_VERSION }}
# 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: Install Debian packaging dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt update -y
sudo apt install libxcb-cursor0 -y
- name: Configure Qtk Application
shell: bash
run: cmake -B build/ ${{ matrix.cmake }}
- name: Build Qtk Application
shell: bash
run: cmake --build build/ --config Release --target qtk_gui ${{ matrix.flags }}
- name: Build Qtk Example
if: matrix.os != 'windows-latest'
shell: bash
run: cmake --build build/ --config Release --target qtk_example ${{ matrix.flags }}
# Packaging
- name: Install Qtk Application
shell: bash
run: cmake --install build/ --config Release --component qtk_gui
- 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: |
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-gui-${{ 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-gui-${{ 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-gui-${{ matrix.os }}
path: |
build/packages/*.tar.gz
- name: Upload Qtk install directory
uses: actions/upload-artifact@v3
with:
name: qtk-gui-${{ 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/*
Qtk-Library:
env:
CONFIG: -DQTK_SUBMODULES=ON -DQTK_DEBUG=OFF -DQTK_CCACHE=OFF -DQTK_GUI=OFF -DQTK_PLUGINS=OFF -DQTK_EXAMPLE=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/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG
flags: ''
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: ${{ env.QT_VERSION }}
# 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
shell: bash
run: cmake -B build/ ${{ matrix.cmake }}
- name: Build Qtk Library
shell: bash
run: cmake --build build/ --config Release --target qtk_library -- ${{ matrix.flags }}
# Packaging
- name: Install Qtk Library
shell: bash
run: cmake --install build/ --config Release --prefix=$(pwd)/install --component qtk_library
- 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/*
Qtk-Plugins:
env:
CONFIG: -DQTK_SUBMODULES=ON -DQTK_DEBUG=OFF -DQTK_CCACHE=OFF -DQTK_GUI=OFF -DQTK_PLUGINS=ON -DQTK_EXAMPLE=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/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG
flags: ''
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG
flags: -j $(nproc)
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: ${{ env.QT_VERSION }}
- 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
shell: bash
run: cmake -B build/ ${{ matrix.cmake }}
- name: Build Qtk Plugins
shell: bash
run: cmake --build build/ --config Release --target qtk_plugins -- ${{ matrix.flags }}
# Packaging
- name: Install Qtk Plugins
shell: bash
run: cmake --install build/ --config Release --component qtk_plugins
Qtk-Assimp-Targets:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: ${{ env.QT_VERSION }}
- name: Install Assimp MacOS
if: matrix.os == 'macos-latest'
shell: bash
run: |
brew install assimp
- name: Install Assimp Ubuntu
if: matrix.os == 'ubuntu-latest'
shell: bash
run: |
sudo apt install libassimp-dev
- name: Configure Qtk
shell: bash
run: cmake -B build/ ${{ matrix.cmake }} -DQTK_CCACHE=OFF -DQTK_ASSIMP_NEW_INTERFACE=ON
- name: Build Qtk
shell: bash
run: cmake --build build/ --config Release

View File

@ -1,68 +0,0 @@
name: Linting
on:
push:
pull_request:
workflow_dispatch:
jobs:
Tidy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.5.0'
- name: Install Assimp Ubuntu
run: sudo apt install libassimp-dev
- name: Build Qtk
run: |
cmake -B build -DQTK_SUBMODULES=OFF -DQTK_CCACHE=OFF -DQTK_PLUGINS=OFF -DQTK_GUI=ON
cmake --build build --target qtk_gui -- -j $(nproc)
- uses: cpp-linter/cpp-linter-action@v2
id: linter
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Use clang-tools 14
version: '14'
# Don't use clang-format with this action
# + Set to `file` to use .clang-format (Qtk formats with clang 15)
style: ''
# Use .clang-tidy file
tidy-checks: ''
# Check the entire repo for source files to tidy
files-changed-only: false
# Ignore qtk build and external assimp directories
ignore: '.github|build|extern/assimp/assimp'
# Point to compile_commands.json produced by build
database: 'build'
# Use thread comments as feedback
thread-comments: true
# Show file annotations on GH
file-annotations: true
- name: Fail CI if checks don't pass
if: steps.linter.outputs.checks-failed != 0
run: exit 1
Format:
runs-on: ubuntu-latest
strategy:
matrix:
path:
- 'src'
- 'app'
steps:
- uses: actions/checkout@v3
- name: clang-format Check
uses: jidicula/clang-format-action@v4.9.0
with:
clang-format-version: '15'
check-path: ${{ matrix.path }}

7
.gitignore vendored
View File

@ -1,13 +1,8 @@
# CLion
**/.idea/**
# VS Code
**/.vscode/**
# CMake build files
**/cmake-build-debug/**
**/build/**
install
# C++ objects and libs
*.slo
@ -40,7 +35,7 @@ ui_*.h
*.qmlc
*.jsc
Makefile*
/*build-*
*build-*
*.qm
*.prl

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "extern/assimp/assimp"]
path = extern/assimp/assimp
url = https://github.com/assimp/assimp.git

View File

@ -1,200 +1,86 @@
################################################################################
## 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 ##
## ##
## Project for working with OpenGL and Qt6 widgets ##
################################################################################
cmake_minimum_required(VERSION 3.23)
cmake_minimum_required(VERSION 3.5)
project(
#[[NAME]] Qtk
VERSION 1.0
DESCRIPTION "An example project using QT and OpenGL"
LANGUAGES CXX C
)
################################################################################
# Constants
################################################################################
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 11)
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(WIN32)
set(CMAKE_COMPILE_WARNING_AS_ERROR OFF)
add_compile_options(/wd4131 /wd4127)
endif()
add_compile_options(-fPIC)
#find_package(QT NAMES Qt6 COMPONENTS Widgets REQUIRED)
find_package(Qt6 COMPONENTS Widgets OpenGLWidgets OpenGL REQUIRED)
################################################################################
# Project
################################################################################
project(
#[[NAME]] Qtk
VERSION 0.2
DESCRIPTION "Qt OpenGL library and desktop application."
LANGUAGES CXX C
# Add our Qt resources.qrc file to our application
set(SOURCES app/main.cpp)
qt6_add_resources(SOURCES resources.qrc)
add_executable(
qtk # Executable name
${SOURCES} # Executable source code
)
################################################################################
# Includes
# External Libraries
################################################################################
include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake")
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
# Find and link OpenGL package; Otherwise show an error
set(OpenGL_GL_PREFERENCE LEGACY)
find_package(OpenGL REQUIRED)
# https://github.com/assimp/assimp/commit/6ac8279977c3a54118551e549d77329497116f66
find_package(assimp REQUIRED)
################################################################################
# Options
# Custom Libraries
################################################################################
option(QTK_DEBUG "Enable debugger" OFF)
option(QTK_SUBMODULES "Update external project (assimp) submodule" OFF)
option(QTK_GUI "Build the Qtk desktop application" ON)
option(QTK_PLUGINS "Install Qtk plugins to Qt Creator path." OFF)
option(QTK_EXAMPLE "Build the Qtk example desktop application" ON)
option(QTK_CCACHE "Enable ccache" ON)
if (QTK_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_SUBMODULES OFF
option(
QTK_ASSIMP_NEW_INTERFACE
"Use the assimp::assimp interface (WIN / OSX)"
OFF
# Mainwidget
include(GenerateExportHeader)
add_library(main-widget SHARED
src/mainwidget.cpp src/mainwidget.h
src/mainwindow.cpp src/mainwindow.h src/mainwindow.ui
src/input.cpp src/input.h
src/mesh.cpp src/mesh.h
src/texture.cpp src/texture.h
src/object.cpp src/object.h
src/meshrenderer.cpp src/meshrenderer.h
src/camera3d.cpp src/camera3d.h
src/skybox.cpp src/skybox.h
src/transform3D.cpp src/transform3D.h
src/model.cpp src/model.h
src/scene.cpp src/scene.h
)
if(QTK_DEBUG OR CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$")
set(QTK_DEBUG ON)
set(CMAKE_BUILD_TYPE Debug)
else()
set(QTK_DEBUG OFF)
set(CMAKE_BUILD_TYPE Release)
endif()
target_include_directories(main-widget PUBLIC src/)
target_include_directories(main-widget PRIVATE ${OPENGL_INCLUDE_DIR})
target_link_libraries(main-widget PRIVATE ${OPENGL_LIBRARIES})
target_link_libraries(main-widget PRIVATE assimp)
target_link_libraries(main-widget PUBLIC Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(main-widget PUBLIC Qt${QT_VERSION_MAJOR}::OpenGLWidgets)
# This should be set to your Qt6 installation directory.
set(QT_INSTALL_DIR "$ENV{HOME}/Qt/6.5.0/gcc_64/lib/cmake" CACHE PATH "Path to Qt6 install.")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install")
endif ()
################################################################################
# Final Application
################################################################################
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
set(QTK_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns)
# Link qtk executable to main main-widget library
target_link_libraries(qtk PUBLIC main-widget)
# 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."
set_target_properties(qtk PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
# Print all QTK options and their values at the end of configuration.
# We initialize this list here so that we can append to it as needed.
# All variables in this list will be printed at the end of configuration.
get_cmake_property(VAR_NAMES VARIABLES)
list(FILTER VAR_NAMES INCLUDE REGEX "^[qQ][tT][kK]_.*$")
list(SORT VAR_NAMES)
################################################################################
# External Dependencies
################################################################################
# Find Qt
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 "
"-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()
#
# To use custom plugins, set QT_PLUGIN_PATH environment variable before running designer
# Or, we can install plugins to the designer for use across all projects.
# Qt Creator on linux will look here for widget plugins in the integrated designer
# /home/shaun/Qt/Tools/QtCreator/lib/Qt/lib
# Qt Designer will use the following path on linux
# /home/shaun/Qt/6.5.0/gcc_64/plugins/designer/
# We can use this path after find_package(Qt6) to install our plugins on all systems
# ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_PLUGINS}/designer
# And run designer at ${QT6_INSTALL_PREFIX}/bin/designer
# Use cmake -DQTK_PLUGIN_INSTALL_DIR=/some/path to override this install path
set(
QTK_PLUGIN_INSTALL_DIR
"${QT6_INSTALL_PREFIX}/${QT6_INSTALL_PLUGINS}/designer" CACHE PATH
"Path to install Qtk plugin collection."
)
# See cmake configure output for values of these variables on your system
set(
VAR_PATHS
CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX QTK_PLUGIN_INSTALL_DIR QT6_INSTALL_PREFIX
QT_INSTALL_DIR
)
# Add QT6_INSTALL_PLUGINS to VAR_NAMES so it is printed at end of configuration.
list(APPEND VAR_NAMES QT6_INSTALL_PLUGINS)
# Find Assimp.
if(QTK_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...")
submodule_update("${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)
if(QTK_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 EXCLUDE_FROM_ALL)
endif()
# Print all QTK options and their values at the end of configuration. This also
# prints any additional variables that we have added to VAR_NAMES and VAR_PATHS.
foreach(VAR_NAME IN LISTS VAR_NAMES VAR_PATHS)
if(VAR_NAME IN_LIST VAR_PATHS)
# Print absolute if variable is path
get_filename_component(VAR_REALPATH "${${VAR_NAME}}" REALPATH)
message(STATUS "[Qtk] ${VAR_NAME}=${VAR_REALPATH}")
else()
message(STATUS "[Qtk] ${VAR_NAME}=${${VAR_NAME}}")
endif()
endforeach()
generate_export_header(main-widget)

348
README.md
View File

@ -1,196 +1,39 @@
# 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)
Practice project for learning about using OpenGL in Qt widget applications.
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 `mainwdget.cpp`, inside the
`MainWidget::initObjects()` function. I've commented throughout the code there
to explain which model or example I'm modifying. Rotations and translations
happen in `MainWidget::update()`, 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. For more in-depth examples, see
`scene.h` and `scene.cpp`
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.
Can be built with cmake manually or using
[Qt Creator](https://github.com/qt-creator/qt-creator).
For the build to be successful, I've found through testing on VMs that the system requires around 6GB of RAM.
This is mostly due to the large .obj files that are built into the project using [Qt Resource System](https://doc.qt.io/qt-6/resources.html)
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.
### Source Builds
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 **Qt 6.6.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.6.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.
If you are building on **Windows / Mac**, consider setting
the `-DQTK_ASSIMP_NEW_INTERFACE` cmake build option.
If the build is configured with all options enabled, we can subsequently install
individual components as needed with cmake.
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.
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 ccache libgl1-mesa-dev libglvnd-dev zlib1g-dev -y
git clone https://github.com/shaunrd0/qtk
cd qtk
# Configure the build with all components enabled
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.6.0/gcc_64
# Build all targets
cmake --build build-all/
````
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_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_SUBMODULES=ON` supports providing assimp on cross-platform builds (
Windows / Mac / Linux) and may be easier
to configure.
```bash
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DQTK_SUBMODULES=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.6.0/gcc_64
sudo apt update -y && sudo apt install freeglut3-dev libassimp-dev cmake build-essential git
git clone https://gitlab.com/shaunrd0/qtk && cd qtk
mkdir build && cd build
cmake .. -DCMAKE_PREFIX_PATH=$HOME/Qt6/6.2.3/gcc_64 && cmake --build .
./qtk
```
#### Qtk GUI
```bash
cmake --build build-all/ --target qtk_gui -- -j $(nproc)
# Install Qtk desktop application (output removed)
# Installation prefix path must be absolute, since Qtk uses Qt deploy tools.
cmake --install build-all/ --component qtk_gui --prefix=$(pwd)/install
./install/bin/qtk_gui
```
If any errors are encountered loading plugins, we can debug plugin loading by
setting the following environment variable -
```bash
QT_DEBUG_PLUGINS=1 ./install/bin/qtk_gui
```
#### 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.
```bash
# Install libqtk only
cmake --build build-all/ --target qtk_library -- -j $(nproc)
cmake --install build-all/ --component qtk_library --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 --build build-all/ --target qtk_plugins -- -j $(nproc)
# Install Qtk widget collection to use Qt Designer
# The path here should be initialized during build configuration, so no need for --prefix
cmake --install build-all/ --component qtk_plugins
-- Install configuration: "Release"
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_library.a
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_plugin_library.a
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/plugins/designer/libqtk_collection.so
```
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
```
#### Qtk Example
There is a simple example of using libqtk in the [example-app/](example-app)
directory. The example can be built standalone using `find_package` or as a
target within any qtk build.
```bash
# Build the example from a configured qtk build tree
cmake --build build-all/ --target qtk_example -- -j $(nproc)
cmake --install build-all/ --component qtk_example --prefix=install
./install/bin/qtk_example
```
See the README in the [example-app/](example-app) subdirectory for instructions
on standalone builds.
### 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 -
@ -201,147 +44,20 @@ Spartan with normals -
![](resources/spartan-normals.png)
#### Development
This project uses version `15.0.5` of `clang-format`.
Before merging any branch we should run `clang-tidy` followed by `clang-format`.
```bash
git clone git@github.com:llvm/llvm-project.git -b llvmorg-15.0.5
cd llvm-project
cmake -B build -DLLVM_ENABLE_PROJECTS=clang -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" llvm
cmake --build build -j $(nproc --ignore=2)
sudo cmake --build build -j $(nproc --ignore=2) --target install
```
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
clang-format version 15.0.5 (git@github.com:llvm/llvm-project.git 154e88af7ec97d9b9f389e55d45bf07108a9a097)
```
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.
`clang-tidy` can be run with the following commands.
```bash
# Move to the root of the repo
cd qtk
# Build
cmake -B build && cmake --build build -- -j $(nproc)
clang-tidy -p build/ --fix --config-file=.clang-tidy src/*.cpp src/*.h app/*.cpp app/*.h
```
Last we need to run `clang-format`, this can be done with the command directly.
This will reformat all the code in the repository.
```bash
clang-format -i --style=file:.clang-format src/app/*.cpp src/app/*.h src/qtk/*.cpp src/qtk/*.h example-app/*.cpp example-app/*.h
```
`clang-format` can be run with git integration (or CLion if you prefer).
Git will only reformat the lines you modified, which can be useful.
```bash
# If we want to format the last N commits
# git clang-format HEAD~N
# 3 commits
git clang-format HEAD~3
changed files:
app/examplescene.h
app/mainwindow.h
src/abstractscene.cpp
src/skybox.h
src/texture.cpp
src/texture.h
src/transform3D.h
```
##### Packaging
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.
Below are the steps to package a Qtk release.
```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
```
Alternatively, we can use `cpack` directly -
```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
```
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.
To generate packages for Qtk desktop application, we should
set `-DQTK_GUI=ON`. If this option is not set we will only package libqtk.
The NSIS installer will allow component-specific path modification for all of
these installation components through a GUI install application.
##### Resources
Some useful links and resources that I have found while working on this project.
[Qt Designer UI file format](https://doc.qt.io/qt-6/designer-ui-file-format.html)
[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/).
"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/).
"Alien Hominid" (https://skfb.ly/onStx) by Nwilly_art 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/).
"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/).
"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.
"Spartan Armour MKV - Halo Reach" (https://skfb.ly/6QVvM) by McCarthy3D is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/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/).
"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/).
"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/).

40
app/main.cpp Normal file
View File

@ -0,0 +1,40 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 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 <mainwidget.h>
#include <mainwindow.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Set OpenGL Version information
// Note: This format must be set before show() is called.
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(4,5);
// 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);
#ifdef QTK_DEBUG
format.setOption(QSurfaceFormat::DebugContext);
#endif // QTK_DEBUG
// Create window for Qt application using custom mainwindow.h
MainWindow w;
w.show();
return a.exec();
}

View File

@ -1,32 +0,0 @@
################################################################################
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
## ##
## CMake function to update git submodules ##
################################################################################
include_guard()
find_package(Git)
# _PATH: Path to git submodule location that we want to update
# + submodule_update(extern/assimp)
function(submodule_update _PATH)
if (NOT QTK_SUBMODULES)
return()
endif()
if (NOT GIT_FOUND)
message(FATAL_ERROR "[Qtk] Error: No git executable found")
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} submodule update --init --force "${_PATH}"
RESULT_VARIABLE result
)
if (NOT result EQUAL 0)
message(
FATAL_ERROR
"[Qtk] Error: Unable to update git submodule at ${_PATH}"
)
endif()
endfunction()

View File

@ -1,13 +0,0 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/QtkTargets.cmake")
set_and_check(QTK_EXECUTABLE "${PACKAGE_PREFIX_DIR}/bin/qtk_gui")
set_and_check(QTK_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")
set_and_check(QTK_LIBRARIES "${PACKAGE_PREFIX_DIR}/lib")
set_and_check(Qtk_EXECUTABLE "${PACKAGE_PREFIX_DIR}/bin/qtk_gui")
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

@ -1,96 +0,0 @@
################################################################################
## 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 ../build/install/lib/cmake/Qtk
CACHE PATH "Path to installation of Qtk"
FORCE
)
# 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}")
# 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()
# 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()
find_package(Qt6 COMPONENTS Core Widgets OpenGLWidgets REQUIRED)
set(
EXAMPLE_SOURCES
main.cpp
examplescene.cpp examplescene.h
examplewidget.cpp examplewidget.h
)
configure_file(
#[[INPUT]] "${CMAKE_CURRENT_SOURCE_DIR}/resources.h.in"
#[[OUTPUT]] "${CMAKE_CURRENT_BINARY_DIR}/resources.h"
@ONLY
)
qt_add_executable(qtk_example ${EXAMPLE_SOURCES})
target_link_libraries(qtk_example PUBLIC Qt6::Widgets Qt6::OpenGLWidgets Qt6::Core)
target_link_libraries(qtk_example PUBLIC Qtk::qtk_library)
target_include_directories(qtk_example PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
install(
TARGETS qtk_example
COMPONENT qtk_example
BUNDLE DESTINATION .
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static
RUNTIME DESTINATION bin
)
qt_generate_deploy_app_script(
TARGET qtk_example
OUTPUT_SCRIPT QTK_EXAMPLE_DEPLOY_SCRIPT
NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${QTK_EXAMPLE_DEPLOY_SCRIPT} COMPONENT qtk_example)

View File

@ -1,73 +0,0 @@
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 --target qtk_example -- -j $(nproc)
cmake --install build/ --component qtk_example
```
After this, we can run the example application -
```bash
./path/to/qtk/example-app/build/install/bin/example
```

View File

@ -1,97 +0,0 @@
/*##############################################################################
## 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"
#include <resources.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);
std::string spartanPath = QTK_EXAMPLE_SOURCE_DIR;
spartanPath += "/resources/models/spartan/spartan.obj";
auto spartan = new Model("spartan", spartanPath.c_str());
addObject(spartan);
spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f);
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

@ -1,27 +0,0 @@
/*##############################################################################
## 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

@ -1,57 +0,0 @@
/*##############################################################################
## 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

@ -1,38 +0,0 @@
/*##############################################################################
## 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,22 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## 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 ##
##############################################################################*/
#include <QApplication>
#include <QMainWindow>
#include "examplewidget.h"
int main(int argc, char * argv[]) {
QApplication app(argc, argv);
auto window = new QMainWindow;
window->setCentralWidget(new ExampleWidget);
window->show();
app.exec();
}

View File

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

@ -1 +0,0 @@
Subproject commit 5d5496f1ad895297cede723b3c96b513263f82ed

81
resources.qrc Normal file
View File

@ -0,0 +1,81 @@
<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

@ -1,165 +0,0 @@
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

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 749 B

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 789 B

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 664 B

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 574 B

View File

@ -1,13 +0,0 @@
```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.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

View File

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

BIN
resources/models/lion/diffuse.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
resources/models/lion/height.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

16
resources/models/lion/lion.mtl Executable file
View File

@ -0,0 +1,16 @@
# 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

86063
resources/models/lion/lion.obj Executable file

File diff suppressed because it is too large Load Diff

BIN
resources/models/lion/normal.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

View File

@ -1,131 +0,0 @@
<RCC>
<qresource prefix="/textures">
<file alias="plaster.png">images/plaster.png</file>
<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: 316 KiB

After

Width:  |  Height:  |  Size: 406 KiB

View File

@ -1,157 +0,0 @@
################################################################################
## 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 qtk_library
DESTINATION lib/cmake/${PROJECT_NAME}
)
install(
EXPORT qtk_export
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
COMPONENT qtk_library
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 qtk_library
FILE_SET HEADERS DESTINATION include
INCLUDES DESTINATION include
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
# Qtk Application
if(QTK_GUI OR QTK_PLUGINS)
add_subdirectory(app)
endif()
if(QTK_PLUGINS)
install(
TARGETS qtk_plugins qtk_library qtk_plugin_library
COMPONENT qtk_plugins
LIBRARY DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
ARCHIVE DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
RUNTIME DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
)
endif()
if(QTK_GUI)
install(
TARGETS qtk_gui
COMPONENT qtk_gui
BUNDLE DESTINATION .
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
qt_generate_deploy_app_script(
TARGET qtk_gui
OUTPUT_SCRIPT QTK_DEPLOY_SCRIPT
NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${QTK_DEPLOY_SCRIPT} COMPONENT qtk_gui)
if(WIN32)
if(MSVC AND TARGET Qt6::qmake)
get_target_property(QT6_QMAKE_LOCATION Qt6::qmake IMPORTED_LOCATION)
execute_process(
COMMAND "${QT6_QMAKE_LOCATION}" -query QT_INSTALL_PREFIX
RESULT_VARIABLE return_code
OUTPUT_VARIABLE QT6_INSTALL_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
)
file(TO_NATIVE_PATH "${QT6_INSTALL_PREFIX}/bin" QT6_INSTALL_PREFIX)
set(VSUSER_FILE "${CMAKE_CURRENT_BINARY_DIR}/qtk_gui.vcxproj.user")
file(WRITE ${VSUSER_FILE} "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
file(APPEND ${VSUSER_FILE} "<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n")
file(APPEND ${VSUSER_FILE} " <PropertyGroup>\n")
file(APPEND ${VSUSER_FILE} " <LocalDebuggerEnvironment>Path=$(SolutionDir)\\lib\\$(Configuration);${QT6_INSTALL_PREFIX};$(Path)\n")
file(APPEND ${VSUSER_FILE} "$(LocalDebuggerEnvironment)</LocalDebuggerEnvironment>\n")
file(APPEND ${VSUSER_FILE} " <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n")
file(APPEND ${VSUSER_FILE} " </PropertyGroup>\n")
file(APPEND ${VSUSER_FILE} "</Project>\n")
endif()
endif()
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_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_gui.exe'"
)
set(
CPACK_NSIS_DELETE_ICONS_EXTRA
"Delete '$SMPROGRAMS\\\\$START_MENU\\\\Qtk.lnk'"
)
# TODO: Icons for NSIS installer.
#set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/resources/icon.png")
#set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/resources/icon.png")
# 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_gui>/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)

View File

@ -1,71 +0,0 @@
################################################################################
## 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 Plugins
################################################################################
# Create a Qt Designer plugin for a collection of widgets from our library.
qt_add_plugin(qtk_plugins SHARED)
target_sources(
qtk_plugins PRIVATE
widgetplugincollection.cpp widgetplugincollection.h
widgetplugin.cpp widgetplugin.h
)
target_link_libraries(qtk_plugins PUBLIC qtk_plugin_library)
################################################################################
# Final Qtk Application
################################################################################
set(
QTK_GUI_SOURCES
qtkscene.cpp qtkscene.h
main.cpp
)
qt_add_executable(qtk_gui ${QTK_GUI_SOURCES})
target_link_libraries(qtk_gui PRIVATE qtk_plugin_library)
set_target_properties(
qtk_gui PROPERTIES
WIN32_EXECUTABLE TRUE
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}
)

View File

@ -1,33 +0,0 @@
/*##############################################################################
## 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);
}
}

View File

@ -1,140 +0,0 @@
/*##############################################################################
## 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

View File

@ -1,33 +0,0 @@
<?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,28 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## 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 "qtkmainwindow.h"
#include "qtkscene.h"
int main(int argc, char * argv[]) {
Q_INIT_RESOURCE(resources);
QApplication a(argc, argv);
auto window = MainWindow::getMainWindow();
// Qtk currently uses the decorator pattern to save / load scenes.
// This is a temporary solution and will be improved in the future.
auto emptyScene = new Qtk::SceneEmpty;
window->getQtkWidget()->setScene(new QtkScene(emptyScene));
window->show();
return QApplication::exec();
}

View File

@ -1,93 +0,0 @@
/*##############################################################################
## 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);
// Add GUI 'view' toolbar option to show debug console.
ui_->menuView->addAction(qtkWidget->getActionToggleConsole());
// Refresh GUI widgets when scene or objects are updated.
connect(
qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this,
&MainWindow::refreshScene);
connect(
qtkWidget, &Qtk::QtkWidget::objectFocusChanged, ui_->qtk__ToolBox,
&Qtk::ToolBox::updateFocus);
}
// TODO: Fix / use MainWindow in Qt Designer to add these dock widgets.
// For now we will add them manually, but we should be able to do this in the
// designer. At the moment if you edit the UI in designer the dock widget
// areas below will override the designer settings.
// Dock the toolbox widget to the main window.
addDockWidget(Qt::LeftDockWidgetArea, ui_->qtk__ToolBox);
// Add an option to toggle active widgets in the GUI's toolbar 'view' menu.
ui_->menuView->addAction(ui_->qtk__ToolBox->toggleViewAction());
addDockWidget(Qt::RightDockWidgetArea, ui_->qtk__TreeView);
ui_->menuView->addAction(ui_->qtk__TreeView->toggleViewAction());
// Set the window icon used for Qtk.
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(const QString & sceneName) {
// TODO: Select TreeView using sceneName
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene());
}

View File

@ -1,92 +0,0 @@
/*##############################################################################
## 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(const 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

View File

@ -1,348 +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>1034</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="Qtk::ToolBox" name="qtk::ToolBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Object details and configuration panel.</string>
</property>
<property name="whatsThis">
<string>When an object is double-clicked in the TreeView for a scene, this panel will display relevant details and options.</string>
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>3</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/>
</property>
<property name="whatsThis">
<string>Qtk scene view rendered using OpenGL.</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>View 2</string>
</attribute>
</widget>
</widget>
</item>
<item>
<widget class="Qtk::TreeView" name="qtk::TreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>TreeView of objects within the current scene.</string>
</property>
<property name="whatsThis">
<string>TreeView of objects within the current scene. Double-click to select an object and snap to it's position.</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1034</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>
<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>
<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>
<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>
<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/>
<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,543 +0,0 @@
/*##############################################################################
## 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 "qtkscene.h"
using namespace Qtk;
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
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);
}
QtkScene::~QtkScene() {
delete mTestPhong;
delete mTestSpecular;
delete mTestDiffuse;
delete mTestAmbient;
}
/*******************************************************************************
* Public Member Functions
******************************************************************************/
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);
myCube->getTransform().setTranslation(5.0f, 0.0f, 0.0f);
addObject(myCube);
auto mySpartan =
new Model("My spartan", ":/models/models/spartan/spartan.obj");
mySpartan->getTransform().setTranslation(5.0f, 0.5f, 0.0f);
mySpartan->getTransform().setScale(0.5f);
addObject(mySpartan);
//
// Create simple shapes using MeshRenderer class and data in mesh.h
auto mesh = addObject(
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(-5.0f, 0.0f, -2.0f);
mesh =
addObject(new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS)));
mesh->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);
//
// 3D Model loading
auto model = addObject(
new Qtk::Model("backpack", ":/models/models/backpack/backpack.obj"));
// Sometimes model textures need flipped in certain directions
model->flipTexture("diffuse.jpg", false, true);
model->getTransform().setTranslation(0.0f, 0.0f, -10.0f);
model = addObject(new Qtk::Model("bird", ":/models/models/bird/bird.obj"));
model->getTransform().setTranslation(2.0f, 2.0f, -10.0f);
// Sometimes the models are very large
model->getTransform().scale(0.0025f);
model->getTransform().rotate(-110.0f, 0.0f, 1.0f, 0.0f);
model = addObject(
new Qtk::Model("alien", ":/models/models/alien-hominid/alien.obj"));
model->getTransform().setTranslation(2.0f, -1.0f, -5.0f);
model->getTransform().scale(0.15f);
model = addObject(
new Qtk::Model("My scythe", ":/models/models/scythe/scythe.obj"));
model->getTransform().setTranslation(-6.0f, 0.0f, -10.0f);
model->getTransform().rotate(-90.0f, 1.0f, 0.0f, 0.0f);
model->getTransform().rotate(90.0f, 0.0f, 1.0f, 0.0f);
model = addObject(
new Qtk::Model("masterChief", ":/models/models/spartan/spartan.obj"));
model->getTransform().setTranslation(-1.5f, 0.5f, -2.0f);
//
// Simple cube lighting examples.
/* Phong lighting example on a basic cube. */
mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube());
mTestPhong->getTransform().setTranslation(3.0f, 0.0f, -2.0f);
// NOTE: You no longer need to manually bind shader program to set uniforms.
// + You can still bind it if you want to for performance reasons.
// + Qtk will only bind / release if the shader program is not already bound.
mTestPhong->setShaders(
":/shaders/solid-phong.vert", ":/shaders/solid-phong.frag");
// 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,
// and release when we are finished.
// mTestPhong->bindShaders();
mTestPhong->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestPhong->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestPhong->setUniform("uAmbientStrength", 0.2f);
mTestPhong->setUniform("uSpecularStrength", 0.50f);
mTestPhong->setUniform("uSpecularShine", 256);
// mTestPhong->releaseShaders();
mTestPhong->reallocateNormals(mTestPhong->getNormals());
// NOTE: This is only an example and I won't worry about this kind of
// efficiency while initializing the following objects.
// Phong lighting example light source. This is just for visual reference.
// + We refer to the position of this object in draw() to update lighting.
mesh = addObject(
new Qtk::MeshRenderer("phongLight", Triangle(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(3.0f, 2.0f, -2.0f);
mesh->getTransform().scale(0.25f);
/* Example of a cube with no lighting applied */
mesh = addObject(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(5.0f, 0.0f, -2.0f);
mesh->setShaders(
":/shaders/solid-perspective.vert", ":/shaders/solid-perspective.frag");
mesh->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(
":/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));
mTestAmbient->setUniform("uAmbientStrength", 0.2f);
mTestAmbient->reallocateNormals(mTestAmbient->getNormals());
// No light source needed for this lighting technique
/* Initialize Diffuse example cube */
mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube());
mTestDiffuse->getTransform().setTranslation(9.0f, 0.0f, -2.0f);
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.
mesh = addObject(
new Qtk::MeshRenderer("diffuseLight", Triangle(QTK_DRAW_ELEMENTS)));
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(
":/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);
mTestSpecular->setUniform("uSpecularStrength", 0.50f);
mTestSpecular->setUniform("uSpecularShine", 256);
mTestSpecular->reallocateNormals(mTestSpecular->getNormals());
// Specular lighting example light source. This is just for visual reference.
mesh = addObject(
new Qtk::MeshRenderer("specularLight", Triangle(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(11.0f, 2.0f, -2.0f);
mesh->getTransform().scale(0.25f);
/* Test basic cube with phong.vert and phong.frag shaders */
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.
mesh->setColor(QVector3D(0.0f, 0.25f, 0.0f));
mesh->reallocateNormals(mesh->getNormals());
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
mesh = addObject(
new Qtk::MeshRenderer("testLight", Triangle(QTK_DRAW_ELEMENTS)));
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. */
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);
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.
mesh = addObject(new Qtk::MeshRenderer(
"alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(4.0f, 1.5f, 10.0f);
mesh->getTransform().scale(0.25f);
// This function changes values we have allocated in a buffer, so init() after
mesh->setColor(GREEN);
/* Test spartan Model with phong lighting, specular and normal mapping. */
model = addObject(new Qtk::Model(
"spartanTest", ":/models/models/spartan/spartan.obj",
":/shaders/model-normals.vert", ":/shaders/model-normals.frag"));
model->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.
mesh = addObject(
new Qtk::MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS)));
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
mesh->setColor(GREEN);
//
// Test drawing simple geometry with various OpenGL drawing modes
// RGB Normals cube to show normals are correct with QTK_DRAW_ARRAYS
mesh = addObject(
new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS)));
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
mesh = addObject(new Qtk::MeshRenderer(
"rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(5.0f, 0.0f, 2.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
Texture crateTexture;
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(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
m->setTexture(crateTexture);
m->setUniform("uTexture", 0);
m->reallocateTexCoords(cube.getTexCoords());
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
mesh = addObject(
new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS)));
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
mesh = addObject(new Qtk::MeshRenderer(
"uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(-1.7f, 0.0f, -2.0f);
mesh->setTexture(":/textures/crate.png");
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
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
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
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
mesh = addObject(new Qtk::MeshRenderer(
"rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS)));
mesh->getTransform().setTranslation(7.0f, 0.0f, 2.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS
mesh = addObject(new Qtk::MeshRenderer(
"rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->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
mesh = addObject(
new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS)));
mesh->getTransform().setTranslation(-3.0f, 2.0f, -2.0f);
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
mesh->setTexture(":/textures/crate.png");
mesh->setUniform("uTexture", 0);
mesh->reallocateTexCoords(mesh->getTexCoords());
// Test drawing triangle with glDrawElements with texture coordinates
mesh = addObject(new Qtk::MeshRenderer(
"testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(-2.5f, 0.0f, -1.0f);
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
mesh->setTexture(":/textures/crate.png");
mesh->setUniform("uTexture", 0);
mesh->reallocateTexCoords(mesh->getTexCoords());
}
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();
mTestPhong->bindShaders();
mTestPhong->setUniform(
"uModelInverseTransposed",
mTestPhong->getTransform().toMatrix().normalMatrix());
mTestPhong->setUniform(
"uLightPosition",
MeshRenderer::getInstance("phongLight")->getTransform().getTranslation());
mTestPhong->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestPhong->releaseShaders();
mTestPhong->draw();
mTestAmbient->bindShaders();
mTestAmbient->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestAmbient->releaseShaders();
mTestAmbient->draw();
mTestDiffuse->bindShaders();
mTestDiffuse->setUniform(
"uModelInverseTransposed",
mTestDiffuse->getTransform().toMatrix().normalMatrix());
mTestDiffuse->setUniform(
"uLightPosition", MeshRenderer::getInstance("diffuseLight")
->getTransform()
.getTranslation());
mTestDiffuse->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestDiffuse->releaseShaders();
mTestDiffuse->draw();
mTestSpecular->bindShaders();
mTestSpecular->setUniform(
"uModelInverseTransposed",
mTestSpecular->getTransform().toMatrix().normalMatrix());
mTestSpecular->setUniform(
"uLightPosition", MeshRenderer::getInstance("specularLight")
->getTransform()
.getTranslation());
mTestSpecular->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestSpecular->releaseShaders();
mTestSpecular->draw();
}
void QtkScene::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);
auto position = MeshRenderer::getInstance("alienTestLight")
->getTransform()
.getTranslation();
auto alien = Model::getInstance("alienTest");
alien->setUniform("uLight.position", position);
alien->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
auto posMatrix = alien->getTransform().toMatrix();
alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
alien->setUniform("uMVP.model", posMatrix);
alien->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
alien->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
position = MeshRenderer::getInstance("spartanTestLight")
->getTransform()
.getTranslation();
auto spartan = Model::getInstance("spartanTest");
spartan->setUniform("uLight.position", position);
spartan->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
posMatrix = spartan->getTransform().toMatrix();
spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
spartan->setUniform("uMVP.model", posMatrix);
spartan->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
spartan->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto phong = MeshRenderer::getInstance("testPhong");
phong->getTransform().rotate(0.75f, 1.0f, 0.5f, 0.0f);
phong->bindShaders();
position =
MeshRenderer::getInstance("testLight")->getTransform().getTranslation();
phong->setUniform("uLight.position", position);
phong->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
posMatrix = phong->getTransform().toMatrix();
phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
phong->setUniform("uMVP.model", posMatrix);
phong->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
phong->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
phong->releaseShaders();
// Rotate lighting example cubes
mTestPhong->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
MeshRenderer::getInstance("noLight")->getTransform().rotate(
0.75f, 0.5f, 0.3f, 0.2f);
mTestAmbient->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
mTestDiffuse->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
mTestSpecular->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
// Examples of various translations and rotations
// Rotate in multiple directions simultaneously
MeshRenderer::getInstance("rgbNormalsCube")
->getTransform()
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
// Pitch forward and roll sideways
MeshRenderer::getInstance("leftTriangle")
->getTransform()
.rotate(0.75f, 1.0f, 0.0f, 0.0f);
MeshRenderer::getInstance("rightTriangle")
->getTransform()
.rotate(0.75f, 0.0f, 0.0f, 1.0f);
// Move between two positions over time
static float translateX = 0.025f;
float limit = -9.0f; // Origin position.x - 2.0f
float posX = MeshRenderer::getInstance("topTriangle")
->getTransform()
.getTranslation()
.x();
if(posX < limit || posX > limit + 4.0f) {
translateX = -translateX;
}
MeshRenderer::getInstance("topTriangle")
->getTransform()
.translate(translateX, 0.0f, 0.0f);
MeshRenderer::getInstance("bottomTriangle")
->getTransform()
.translate(-translateX, 0.0f, 0.0f);
// And lets rotate the triangles in two directions at once
MeshRenderer::getInstance("topTriangle")
->getTransform()
.rotate(0.75f, 0.2f, 0.0f, 0.4f);
MeshRenderer::getInstance("bottomTriangle")
->getTransform()
.rotate(0.75f, 0.0f, 0.2f, 0.4f);
// And make the bottom triangle green, instead of RGB
// Rotate center cube in several directions simultaneously
// + Not subject to gimbal lock since we are using quaternions :)
MeshRenderer::getInstance("centerCube")
->getTransform()
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
}

View File

@ -1,75 +0,0 @@
/*##############################################################################
## 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>
/**
* Example scene using QtkWidget to render 3D models and simple geometry within
* QtOpenGLWidgets. This scene also shows some examples of using GLSL shaders to
* apply the basic lighting techniques leading up to Phong.
*
* The Qtk::Scene base class provides containers for N pointers to MeshRenderer
* and Model objects. We can create and insert as many as we like within this
* child class implementation. This child class does not need to manually draw
* objects inserted into these containers. The child class would only need to
* update uniform or other data that may change per-frame.
* See scene.h and `init()` for more information.
*
* To modify the scene objects should be initialized within the `init()` public
* method. Any required movement or updates should be applied within `draw()` or
* `update()`.
*
* To create your own Scene from scratch see Qtk::Scene.
*/
class QtkScene : public Qtk::SceneInterface {
public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
QtkScene(Qtk::Scene * scene);
~QtkScene();
/***************************************************************************
* Inherited Public Overrides
**************************************************************************/
/**
* Initialize objects within the scene
*/
void init() override;
/**
* Called when OpenGL repaints the widget.
*/
void draw() override;
/**
* Called when the Qt `frameSwapped` signal is caught.
* See definition of `QtkWidget::initializeGL()`
*/
void update() override;
private:
/***************************************************************************
* Private Members
**************************************************************************/
// Additional example objects created within this example.
// + The base class Scene manages objects stored within mMeshes or mModels
Qtk::MeshRenderer * mTestPhong {};
Qtk::MeshRenderer * mTestSpecular {};
Qtk::MeshRenderer * mTestDiffuse {};
Qtk::MeshRenderer * mTestAmbient {};
};
#endif // QTK_EXAMPLE_SCENE_H

View File

@ -1,357 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## 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 <QMimeData>
#include <QVBoxLayout>
#include <qtk/input.h>
#include <qtk/scene.h>
#include <qtk/shape.h>
#include <QVBoxLayout>
#include <qtk/input.h>
#include <qtk/scene.h>
#include <qtk/shape.h>
#include "debugconsole.h"
#include "qtkmainwindow.h"
#include "qtkwidget.h"
using namespace Qtk;
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
QtkWidget::QtkWidget(QWidget * parent) : QtkWidget(parent, "QtkWidget") {}
QtkWidget::QtkWidget(QWidget * parent, const QString & name) :
QtkWidget(parent, name, Q_NULLPTR) {}
QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR),
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR) {
setAcceptDrops(true);
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);
}
QtkWidget::~QtkWidget() {
makeCurrent();
teardownGL();
}
/*******************************************************************************
* Public Methods
******************************************************************************/
QAction * QtkWidget::getActionToggleConsole() {
auto action = new QAction(mScene->getSceneName() + " debug console");
action->setCheckable(true);
action->setChecked(mConsoleActive);
action->setStatusTip("Add a debug console for this QtkWidget.");
connect(action, &QAction::triggered, this, &QtkWidget::toggleConsole);
return action;
}
void QtkWidget::initializeGL() {
initializeOpenGLFunctions();
// Connect the frameSwapped signal to call the update() function
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
toggleConsole();
// Initialize OpenGL debug context
mDebugLogger = new QOpenGLDebugLogger(this);
if(mDebugLogger->initialize()) {
qDebug() << "GL_DEBUG Debug Logger" << mDebugLogger << "\n";
connect(
mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this,
SLOT(messageLogged(QOpenGLDebugMessage)));
mDebugLogger->startLogging();
}
printContextInformation();
// Initialize opengl settings
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 QtkWidget::resizeGL(int width, int height) {
Scene::getProjectionMatrix().setToIdentity();
Scene::getProjectionMatrix().perspective(
45.0f, float(width) / float(height), 0.1f, 1000.0f);
}
void QtkWidget::paintGL() {
// Clear buffers and draw the scene if it is valid.
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
if(mScene != Q_NULLPTR) {
mScene->draw();
}
}
void QtkWidget::setScene(Scene * scene) {
if(mScene != Q_NULLPTR) {
delete mScene;
connect(
scene, &Scene::sceneUpdated, MainWindow::getMainWindow(),
&MainWindow::refreshScene);
}
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, mConsole);
mConsole->setHidden(false);
mConsoleActive = true;
}
}
/*******************************************************************************
* Protected Methods
******************************************************************************/
void QtkWidget::dragEnterEvent(QDragEnterEvent * event) {
if(event->mimeData()->hasFormat("text/plain")) {
event->acceptProposedAction();
}
}
void QtkWidget::dropEvent(QDropEvent * event) {
mConsole->sendLog(event->mimeData()->text());
auto urls = event->mimeData()->urls();
if(!urls.isEmpty()) {
if(urls.size() > 1) {
qDebug() << "Cannot accept drop of multiple files.";
event->ignore();
return;
}
// TODO: Support other object types.
auto url = urls.front();
if(url.fileName().endsWith(".obj")) {
mScene->loadModel(url);
event->acceptProposedAction();
} else {
qDebug() << "Unsupported file type: " + url.fileName() + "\n";
event->ignore();
}
}
}
void QtkWidget::keyPressEvent(QKeyEvent * event) {
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();
if(mScene != Q_NULLPTR) {
mScene->update();
}
QWidget::update();
}
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
QString error;
DebugContext context;
// Format based on severity
switch(msg.severity()) {
case QOpenGLDebugMessage::NotificationSeverity:
error += "--";
context = Status;
break;
case QOpenGLDebugMessage::HighSeverity:
error += "!!";
context = Fatal;
break;
case QOpenGLDebugMessage::MediumSeverity:
error += "!~";
context = Error;
break;
case QOpenGLDebugMessage::LowSeverity:
error += "~~";
context = Warn;
break;
}
error += " (";
// Format based on source
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error += #c; \
break
switch(msg.source()) {
CASE(APISource);
CASE(WindowSystemSource);
CASE(ShaderCompilerSource);
CASE(ThirdPartySource);
CASE(ApplicationSource);
CASE(OtherSource);
CASE(InvalidSource);
}
#undef CASE
error += " : ";
// Format based on type
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error += #c; \
break
switch(msg.type()) {
CASE(InvalidType);
CASE(ErrorType);
CASE(DeprecatedBehaviorType);
CASE(UndefinedBehaviorType);
CASE(PortabilityType);
CASE(PerformanceType);
CASE(OtherType);
CASE(MarkerType);
CASE(GroupPushType);
CASE(GroupPopType);
}
#undef CASE
error += ")\n" + msg.message() + "\n";
qDebug() << qPrintable(error);
sendLog("(OpenGL) " + error.replace("\n", "\n(OpenGL) "), context);
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void QtkWidget::teardownGL() { /* Nothing to teardown yet... */
}
void QtkWidget::updateCameraInput() {
Input::update();
// Camera Transformation
if(Input::buttonPressed(Qt::LeftButton)
|| 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() {
QString glType;
QString glVersion;
QString glProfile;
QString glVendor;
QString glRenderer;
// Get Version Information
glType = (context()->isOpenGLES()) ? "OpenGL ES" : "OpenGL";
glVersion = reinterpret_cast<const char *>(glGetString(GL_VERSION));
glVendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
glRenderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
// Get Profile Information
#define CASE(c) \
case QSurfaceFormat::c: \
glProfile = #c; \
break
switch(format().profile()) {
CASE(NoProfile);
CASE(CoreProfile);
CASE(CompatibilityProfile);
}
#undef CASE
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,214 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## 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 ##
##############################################################################*/
#ifndef QTK_QTKWIDGET_H
#define QTK_QTKWIDGET_H
#include <iostream>
#include <QDockWidget>
#include <QMatrix4x4>
#include <QOpenGLDebugLogger>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <QPlainTextEdit>
#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 QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT;
public:
/*************************************************************************
* Contructors / Destructors
************************************************************************/
/**
* Qt Designer will call this ctor when creating this widget as a child.
*
* @param parent Pointer to a parent widget for this QtkWidget or nullptr.
*/
explicit QtkWidget(QWidget * parent = nullptr);
/**
* Default construct a QtkWidget.
*
* @param parent Pointer to a parent widget or nullptr if no parent.
* @param name An objectName for the new QtkWidget.
*/
explicit QtkWidget(QWidget * parent, const QString & name);
/**
* Construct a custom QtkWidget.
*
* @param parent Pointer to a parent widget or nullptr if no parent.
* @param name An objectName for the new QtkWidget.
* @param scene Pointer to a custom class inheriting from Qtk::Scene.
*/
QtkWidget(QWidget * parent, const QString & name, Qtk::Scene * scene);
~QtkWidget();
/*************************************************************************
* Public 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.
*/
void initializeGL() override;
/**
* Called when the application window is resized.
*
* @param width The new width of the window.
* @param height The new height of the window.
*/
void resizeGL(int width, int height) override;
/**
* Called when OpenGL repaints the widget.
*/
void paintGL() override;
/*************************************************************************
* 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
************************************************************************/
/**
* @param scene The new scene to view.
*/
void setScene(Qtk::Scene * scene);
public slots:
/**
* Toggle visibility of the DebugConsole associated with this QtkWidget.
*/
void toggleConsole();
signals:
/**
* Log a message to the DebugConsole associated with this widget.
* @param message The message to log.
* @param context The context of the log message.
*/
void sendLog(const QString & message, DebugContext context = Status);
// TODO: Use this signal in treeview and toolbox to update object
// properties
void objectFocusChanged(const QString objectName);
protected:
/*************************************************************************
* Protected Methods
************************************************************************/
void dragEnterEvent(QDragEnterEvent * event) override;
void dropEvent(QDropEvent * event) override;
/**
* @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()
* https://doc.qt.io/qt-6/qopengldebuglogger.html#signals
*
* @param msg The message logged.
*/
void messageLogged(const QOpenGLDebugMessage & msg);
private:
/*************************************************************************
* Private Methods
************************************************************************/
/**
* Deconstruct any resources we have allocated for this widget.
*/
void teardownGL();
/**
* Callback function to update input for camera controls
*/
static void updateCameraInput();
/**
* Prints OpenGL context information at start of debug session.
*/
void printContextInformation();
/*************************************************************************
* Private Members
************************************************************************/
QOpenGLDebugLogger * mDebugLogger;
Qtk::Scene * mScene;
Qtk::DebugConsole * mConsole;
bool mConsoleActive = false;
};
} // namespace Qtk
#endif // QTK_QTKWIDGET_H

View File

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

View File

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

View File

@ -1,56 +0,0 @@
/*##############################################################################
## 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>
#include <QDoubleSpinBox>
#include <QGroupBox>
#include "qtk/scene.h"
namespace Ui {
class ToolBox;
}
namespace Qtk {
class ToolBox : public QDockWidget {
Q_OBJECT
public:
/*************************************************************************
* Contructors / Destructors
*************************************************************************/
explicit ToolBox(QWidget * parent = nullptr);
~ToolBox();
void removePages();
void createPageProperties(const Object * object);
void createPageShader(const Object * object);
void updateFocus(const QString & name);
private:
/*************************************************************************
* Private Members
************************************************************************/
Ui::ToolBox * ui;
};
} // namespace Qtk
#endif // TOOLBOX_H

View File

@ -1,83 +0,0 @@
<?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="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>86</width>
<height>167</height>
</size>
</property>
<property name="windowTitle">
<string>Object Details</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QToolBox" name="toolBox">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_properties">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>382</width>
<height>201</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="label">
<string>Properties</string>
</attribute>
</widget>
<widget class="QWidget" name="page_shaders">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>382</width>
<height>201</height>
</rect>
</property>
<attribute name="label">
<string>Shaders</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,72 +0,0 @@
/*##############################################################################
## 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);
Transform3D * objectTransform;
// If the object is a mesh or model, focus the camera on it.
if(object == Q_NULLPTR) {
qDebug() << "Attempt to get non-existing object with name '" << name
<< "'\n";
} else if(object->getType() == Object::QTK_MESH) {
objectTransform = &dynamic_cast<MeshRenderer *>(object)->getTransform();
} else if(object->getType() == Object::QTK_MODEL) {
objectTransform = &dynamic_cast<Model *>(object)->getTransform();
}
auto focusScale = objectTransform->getScale();
float width = focusScale.x() / 2.0f;
float height = focusScale.y() / 2.0f;
QVector3D pos = objectTransform->getTranslation();
// pos.setX(pos.x() + width);
pos.setY(pos.y() + height);
transform.setTranslation(pos);
transform.translate(0.0f, 0.0f, 3.0f);
// Emit signal from qtk widget for new object focus. Triggers GUI updates.
emit MainWindow::getMainWindow()->getQtkWidget()->objectFocusChanged(name);
}

View File

@ -1,73 +0,0 @@
/*##############################################################################
## 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

View File

@ -1,44 +0,0 @@
<?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>

View File

@ -1,98 +0,0 @@
/*##############################################################################
## 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";
}

View File

@ -1,124 +0,0 @@
/*##############################################################################
## 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

@ -1,43 +0,0 @@
/*##############################################################################
## 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

@ -1,50 +0,0 @@
/*##############################################################################
## 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,56 +1,59 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2022 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);
/*******************************************************************************
* Public Methods
* Accessors
******************************************************************************/
const QMatrix4x4 & Camera3D::toMatrix() {
// Produces worldToView matrix
const QMatrix4x4 & Camera3D::toMatrix()
{
mWorld.setToIdentity();
// Qt6 renamed QMatrix4x4::conjugate() to conjugated()
mWorld.rotate(mTransform.getRotation().conjugated());
mWorld.translate(-mTransform.getTranslation());
mWorld.rotate(mTransform.rotation().conjugated());
mWorld.translate(-mTransform.translation());
return mWorld;
}
/*******************************************************************************
* Qt Streams
******************************************************************************/
QDataStream & operator<<(QDataStream & out, Camera3D & transform) {
out << transform.getTransform();
QDataStream & operator<<(QDataStream & out, Camera3D & transform)
{
out << transform.transform();
return out;
}
QDataStream & operator>>(QDataStream & in, Camera3D & transform) {
in >> transform.getTransform();
QDataStream & operator>>(QDataStream & in, Camera3D & transform)
{
in >> transform.transform();
return in;
}
QDebug operator<<(QDebug dbg, const Camera3D & transform) {
QDebug operator<<(QDebug dbg, const Camera3D & transform)
{
dbg << "Camera3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
dbg << "Position: <" << transform.translation().x() << ", "
<< transform.translation().y() << ", "
<< transform.translation().z() << ">\n";
dbg << "Rotation: <" << transform.rotation().x() << ", "
<< transform.rotation().y() << ", "
<< transform.rotation().z() << " | "
<< transform.rotation().scalar() << ">\n}";
return dbg;
}

62
src/camera3d.h Normal file
View File

@ -0,0 +1,62 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 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 ##
##############################################################################*/
#ifndef QTK_CAMERA3D_H
#define QTK_CAMERA3D_H
#include <QDebug>
#include <transform3D.h>
class Camera3D {
public:
// Constants
static const QVector3D LocalForward;
static const QVector3D LocalUp;
static const QVector3D LocalRight;
// Accessors
inline Transform3D & transform() { return mTransform;}
inline const QVector3D & translation() const
{ return mTransform.translation();}
inline const QQuaternion & rotation() const
{ return mTransform.rotation();}
const QMatrix4x4 & toMatrix();
// Queries
inline QVector3D forward() const
{ return mTransform.rotation().rotatedVector(LocalForward);}
inline QVector3D right() const
{ return mTransform.rotation().rotatedVector(LocalRight);}
inline QVector3D up() const
{ return mTransform.rotation().rotatedVector(LocalUp);}
private:
Transform3D mTransform;
QMatrix4x4 mWorld;
#ifndef QT_NO_DATASTREAM
friend QDataStream & operator<<(QDataStream & out, Camera3D & transform);
friend QDataStream & operator>>(QDataStream & in, Camera3D & transform);
#endif
};
Q_DECLARE_TYPEINFO(Camera3D, Q_MOVABLE_TYPE);
// Qt Streams
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Camera3D & transform);
QDataStream & operator>>(QDataStream & in, Camera3D & transform);
#endif
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Camera3D & transform);
#endif
#endif // QTK_CAMERA3D_H

View File

@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## 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 ##
@ -11,34 +11,25 @@
#include <QCursor>
#include "input.h"
#include <input.h>
using namespace Qtk;
/*******************************************************************************
* Static Helper Structs
******************************************************************************/
template <typename T>
struct InputInstance : std::pair<T, Input::InputState>
{
typedef std::pair<T, Input::InputState> base_class;
/**
* 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;
inline InputInstance(T value)
: base_class(value, Input::InputInvalid) {}
// Disable clang-tidy from marking this ctor explicit
// NOLINTNEXTLINE
inline InputInstance(T value) : base_class(value, Input::InputInvalid) {}
inline InputInstance(T value, Input::InputState state)
: base_class(value, state) {}
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;
}
inline bool operator==(const InputInstance & rhs) const
{ return this->first == rhs.first;}
};
// Key, button instance typedefs
@ -57,50 +48,26 @@ static QPoint sg_mouseCurrPosition;
static QPoint sg_mousePrevPosition;
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) {
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) {
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) {
static inline void UpdateStates(TPair & instance)
{
switch (instance.second)
{
case Input::InputRegistered:
instance.second = Input::InputTriggered;
break;
@ -115,18 +82,20 @@ template <typename TPair> static inline void UpdateStates(TPair & instance) {
}
}
/**
* 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) {
template <typename TPair>
static inline bool CheckReleased(const TPair & instance)
{
return instance.second == Input::InputReleased;
}
template <typename Container>
static inline void Update(Container & container)
{
typedef typename Container::iterator Iter;
typedef typename Container::value_type TPair;
// Remove old data
auto remove =
Iter remove =
std::remove_if(container.begin(), container.end(), &CheckReleased<TPair>);
container.erase(remove, container.end());
@ -134,11 +103,35 @@ template <typename Container> static inline void Update(Container & container) {
std::for_each(container.begin(), container.end(), &UpdateStates<TPair>);
}
/*******************************************************************************
* Static Public Methods
* Input Implementation
******************************************************************************/
void Input::update() {
Input::InputState Input::keyState(Qt::Key k)
{
KeyContainer::iterator it = FindKey(k);
return (it != sg_keyInstances.end()) ? it->second : InputInvalid;
}
Input::InputState Input::buttonState(Qt::MouseButton k)
{
ButtonContainer::iterator 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;
sg_mouseCurrPosition = QCursor::pos();
@ -149,53 +142,44 @@ void Input::update() {
Update(sg_keyInstances);
}
void Input::registerKeyPress(int k) {
auto it = FindKey((Qt::Key)k);
if(it == sg_keyInstances.end()) {
void Input::registerKeyPress(int k)
{
KeyContainer::iterator it = FindKey((Qt::Key)k);
if (it == sg_keyInstances.end())
{
sg_keyInstances.push_back(KeyInstance((Qt::Key)k, InputRegistered));
}
}
void Input::registerKeyRelease(int k) {
auto it = FindKey((Qt::Key)k);
if(it != sg_keyInstances.end()) {
void Input::registerKeyRelease(int k)
{
KeyContainer::iterator it = FindKey((Qt::Key)k);
if (it != sg_keyInstances.end())
{
it->second = InputUnregistered;
}
}
void Input::registerMousePress(Qt::MouseButton btn) {
auto it = FindButton(btn);
if(it == sg_buttonInstances.end()) {
void Input::registerMousePress(Qt::MouseButton btn)
{
ButtonContainer::iterator it = FindButton(btn);
if (it == sg_buttonInstances.end())
{
sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered));
}
}
void Input::registerMouseRelease(Qt::MouseButton btn) {
auto it = FindButton(btn);
if(it != sg_buttonInstances.end()) {
void Input::registerMouseRelease(Qt::MouseButton btn)
{
ButtonContainer::iterator it = FindButton(btn);
if (it != sg_buttonInstances.end())
{
it->second = InputUnregistered;
}
}
void Input::reset() {
void Input::reset()
{
sg_keyInstances.clear();
sg_buttonInstances.clear();
}
Input::InputState Input::keyState(Qt::Key k) {
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;
}

63
src/input.h Normal file
View File

@ -0,0 +1,63 @@
/*##############################################################################
## 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>
class Input {
friend class MainWidget;
public:
// Possible key states
enum InputState
{
InputInvalid,
InputRegistered,
InputUnregistered,
InputTriggered,
InputPressed,
InputReleased
};
// 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:
// 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();
};
#endif // QTOPENGL_INPUT_H

370
src/mainwidget.cpp Normal file
View File

@ -0,0 +1,370 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main window for Qt6 OpenGL widget application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <QKeyEvent>
#include <input.h>
#include <mesh.h>
#include <object.h>
#include <scene.h>
#include <mainwidget.h>
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
MainWidget::MainWidget() : mDebugLogger(Q_NULLPTR)
{
initializeWidget();
setFocusPolicy(Qt::ClickFocus);
}
// Constructor for using this widget in QtDesigner
MainWidget::MainWidget(QWidget *parent) : QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR)
{
initializeWidget();
setFocusPolicy(Qt::ClickFocus);
}
MainWidget::MainWidget(const QSurfaceFormat &format)
: mDebugLogger(Q_NULLPTR)
{
setFormat(format);
setFocusPolicy(Qt::ClickFocus);
}
MainWidget::~MainWidget()
{
makeCurrent();
teardownGL();
}
/*******************************************************************************
* Private Member Functions
******************************************************************************/
void MainWidget::teardownGL()
{
// Nothing to teardown yet...
}
void MainWidget::initObjects()
{
mScene = new Scene;
// Drawing a primitive object using Qt and OpenGL
// The Object class only stores basic QOpenGL* members and shape data
// + Within mainwidget, mObject serves as a basic QOpenGL example
mObject = new Object("testObject");
mObject->setVertices(Cube(QTK_DRAW_ELEMENTS).vertices());
mObject->setIndices(Cube(QTK_DRAW_ELEMENTS).indices());
mObject->mProgram.create();
mObject->mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
":/solid-ambient.vert");
mObject->mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
":/solid-ambient.frag");
mObject->mProgram.link();
mObject->mProgram.bind();
mObject->mVAO.create();
mObject->mVAO.bind();
mObject->mVBO.create();
mObject->mVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
mObject->mVBO.bind();
mObject->mVBO.allocate(mObject->vertices().data(),
mObject->vertices().size()
* sizeof(mObject->vertices()[0]));
mObject->mProgram.enableAttributeArray(0);
mObject->mProgram.setAttributeBuffer(0, GL_FLOAT, 0,
3, sizeof(mObject->vertices()[0]));
mObject->mProgram.setUniformValue("uColor", QVector3D(1.0f, 0.0f, 0.0f));
mObject->mProgram.setUniformValue("uLightColor", WHITE);
mObject->mProgram.setUniformValue("uAmbientStrength", 0.75f);
mObject->mVBO.release();
mObject->mVAO.release();
mObject->mProgram.release();
mObject->mTransform.setTranslation(13.0f, 0.0f, -2.0f);
}
/*******************************************************************************
* Inherited Virtual Member Functions
******************************************************************************/
void MainWidget::paintGL()
{
// Clear buffers
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// Draw the scene first, since it handles drawing our skybox
mScene->draw();
// Draw any additional objects within mainwidget manually
mObject->mProgram.bind();
mObject->mVAO.bind();
mObject->mProgram.setUniformValue("uModel", mObject->mTransform.toMatrix());
mObject->mProgram.setUniformValue("uView", Scene::Camera().toMatrix());
mObject->mProgram.setUniformValue("uProjection", Scene::Projection());
glDrawElements(GL_TRIANGLES, mObject->indices().size(),
GL_UNSIGNED_INT, mObject->indices().data());
mObject->mVAO.release();
mObject->mProgram.release();
}
void MainWidget::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";
connect(mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)),
this, SLOT(messageLogged(QOpenGLDebugMessage)));
mDebugLogger->startLogging();
}
#endif // QTK_DEBUG
printContextInformation();
// Initialize opengl settings
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);
// Initialize default objects within the scene
initObjects();
}
void MainWidget::resizeGL(int width, int height)
{
Scene::Projection().setToIdentity();
Scene::Projection().perspective(45.0f,
float(width) / float(height),
0.1f, 1000.0f);
}
/*******************************************************************************
* Protected Slots
******************************************************************************/
void MainWidget::update()
{
updateCameraInput();
mScene->update();
QWidget::update();
}
void MainWidget::messageLogged(const QOpenGLDebugMessage &msg)
{
QString error;
// Format based on severity
switch (msg.severity())
{
case QOpenGLDebugMessage::NotificationSeverity:
error += "--";
break;
case QOpenGLDebugMessage::HighSeverity:
error += "!!";
break;
case QOpenGLDebugMessage::MediumSeverity:
error += "!~";
break;
case QOpenGLDebugMessage::LowSeverity:
error += "~~";
break;
}
error += " (";
// Format based on source
#define CASE(c) case QOpenGLDebugMessage::c: error += #c; break
switch (msg.source())
{
CASE(APISource);
CASE(WindowSystemSource);
CASE(ShaderCompilerSource);
CASE(ThirdPartySource);
CASE(ApplicationSource);
CASE(OtherSource);
CASE(InvalidSource);
}
#undef CASE
error += " : ";
// Format based on type
#define CASE(c) case QOpenGLDebugMessage::c: error += #c; break
switch (msg.type())
{
CASE(InvalidType);
CASE(ErrorType);
CASE(DeprecatedBehaviorType);
CASE(UndefinedBehaviorType);
CASE(PortabilityType);
CASE(PerformanceType);
CASE(OtherType);
CASE(MarkerType);
CASE(GroupPushType);
CASE(GroupPopType);
}
#undef CASE
error += ")";
qDebug() << qPrintable(error) << "\n" << qPrintable(msg.message()) << "\n";
}
/*******************************************************************************
* Protected Helpers
******************************************************************************/
void MainWidget::keyPressEvent(QKeyEvent *event)
{
if (event->isAutoRepeat()) {
// Do not repeat input while a key is held down
event->ignore();
} else {
Input::registerKeyPress(event->key());
}
}
void MainWidget::keyReleaseEvent(QKeyEvent *event)
{
if (event->isAutoRepeat()) {
event->ignore();
} else {
Input::registerKeyRelease(event->key());
}
}
void MainWidget::mousePressEvent(QMouseEvent *event)
{
Input::registerMousePress(event->button());
}
void MainWidget::mouseReleaseEvent(QMouseEvent *event)
{
Input::registerMouseRelease(event->button());
}
/*******************************************************************************
* Private Helpers
******************************************************************************/
void MainWidget::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);
}
void MainWidget::printContextInformation()
{
QString glType;
QString glVersion;
QString glProfile;
QString glVendor;
QString glRenderer;
// Get Version Information
glType = (context()->isOpenGLES()) ? "OpenGL ES" : "OpenGL";
glVersion = reinterpret_cast<const char *>(glGetString(GL_VERSION));
glVendor =
reinterpret_cast<const char *>(glGetString(GL_VENDOR));
glRenderer =
reinterpret_cast<const char *>(glGetString(GL_RENDERER));
// Get Profile Information
#define CASE(c) case QSurfaceFormat::c: glProfile = #c; break
switch (format().profile()) {
CASE(NoProfile);
CASE(CoreProfile);
CASE(CompatibilityProfile);
}
#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 MainWidget::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::Camera().transform().rotate(-rotSpeed * Input::mouseDelta().x(),
Camera3D::LocalUp);
Scene::Camera().transform().rotate(-rotSpeed * Input::mouseDelta().y(),
Scene::Camera().right());
// Handle translations
QVector3D translation;
if (Input::keyPressed(Qt::Key_W)) {
translation += Scene::Camera().forward();
}
if (Input::keyPressed(Qt::Key_S)) {
translation -= Scene::Camera().forward();
}
if (Input::keyPressed(Qt::Key_A)) {
translation -= Scene::Camera().right();
}
if (Input::keyPressed(Qt::Key_D)) {
translation += Scene::Camera().right();
}
if (Input::keyPressed(Qt::Key_Q)) {
translation -= Scene::Camera().up() / 2.0f;
}
if (Input::keyPressed(Qt::Key_E)) {
translation += Scene::Camera().up() / 2.0f;
}
Scene::Camera().transform().translate(transSpeed * translation);
}
}

71
src/mainwidget.h Normal file
View File

@ -0,0 +1,71 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main window for Qt6 OpenGL widget application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_MAINWIDGET_H
#define QTK_MAINWIDGET_H
#include <iostream>
#include <QMatrix4x4>
#include <QOpenGLDebugLogger>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#define QTK_DEBUG
class MeshRenderer;
class Model;
class Object;
class Scene;
class Skybox;
class Texture;
class MainWidget : public QOpenGLWidget,
protected QOpenGLFunctions {
Q_OBJECT;
public:
// Constructors
MainWidget();
explicit MainWidget(QWidget *parent);
explicit MainWidget(const QSurfaceFormat &format);
~MainWidget() override;
private:
void teardownGL();
void initObjects();
public:
// Inherited virtual Members
void paintGL() override;
void initializeGL() override;
void resizeGL(int width, int height) override;
protected slots:
void update();
void messageLogged(const QOpenGLDebugMessage &msg);
// Protected Helpers
protected:
void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
// Private helpers
void initializeWidget();
void printContextInformation();
void updateCameraInput();
Scene * mScene;
Object * mObject;
QOpenGLDebugLogger * mDebugLogger;
};
#endif // QTK_MAINWIDGET_H

15
src/mainwindow.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowIcon(QIcon("../resources/icon.png"));
}
MainWindow::~MainWindow()
{
delete ui;
}

24
src/mainwindow.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "main-widget_export.h"
namespace Ui {
class MainWindow;
}
class MAIN_WIDGET_EXPORT MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

104
src/mainwindow.ui Normal file
View File

@ -0,0 +1,104 @@
<?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="MainWidget" name="openGLWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>775</width>
<height>550</height>
</rect>
</property>
</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>MainWidget</class>
<extends>QOpenGLWidget</extends>
<header>mainwidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,31 +1,25 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2022 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 "shape.h"
#include <mesh.h>
using namespace Qtk;
Cube::Cube(DrawMode mode) {
Cube::Cube(DrawMode mode)
{
mDrawMode = mode;
switch(mode) {
// The order of the following assignment values helps to visualize.
// clang-format off
// Cube data for use with glDrawArrays
case QTK_DRAW_ARRAYS:
mIndices = {/* No indices needed for glDrawArrays */};
mIndices = { /* No indices needed for glDrawArrays */ };
mNormals = {FACE_FRONT, FACE_BACK, FACE_TOP,
FACE_BOTTOM, FACE_LEFT, FACE_RIGHT};
mNormals =
{FACE_FRONT, FACE_BACK, FACE_TOP, FACE_BOTTOM, FACE_LEFT, FACE_RIGHT};
// We're drawing triangles to construct the geometry of a cube.
// Each triangle is made up of three points.
// The entire cube has 12 triangles to make 6 square faces of the cube.
mVertices = {
// Face 1 (Front)
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL,
@ -47,9 +41,6 @@ Cube::Cube(DrawMode mode) {
VERTEX_BBR, VERTEX_BTR, VERTEX_FTR
};
// Setting colors for each vertex that we defined above.
// These are defaults and can be overriden by the caller with setColor().
// The colors below are interpolated from vertex to vertex.
mColors = {
// Face 1 (Front)
RED, GREEN, BLUE,
@ -71,8 +62,6 @@ Cube::Cube(DrawMode mode) {
YELLOW, MAGENTA, RED
};
// Define texture coordinates for the cube.
// This defines the orientation of the texture when applied the object.
mTexCoords = {
// Face 1 (Front)
UV_TOP, UV_ORIGIN, UV_RIGHT,
@ -96,20 +85,22 @@ Cube::Cube(DrawMode mode) {
break;
// Cube data for use with glDrawElements
case QTK_DRAW_ELEMENTS:
mNormals = {
/* For normals and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */};
mTexCoords = {
/* For UVs and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */};
mNormals =
{/* For normals and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */};
mTexCoords =
{ /* For UVs and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */ };
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mVertices = {// 0 1 2 3
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR,
// 4 5 6 7
VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR};
mVertices = {
// 0 1 2 3
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR,
// 4 5 6 7
VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR
};
mIndices = {
// Face 1 (Front)
0, 1, 2, 2, 3, 0,
@ -124,7 +115,6 @@ Cube::Cube(DrawMode mode) {
// Face 6 (Right)
0, 3, 7, 7, 4, 0
};
break;
// Cube shape data for using normals and UVs with glDrawElements
@ -161,20 +151,20 @@ Cube::Cube(DrawMode mode) {
8, 9, 10, 10, 11, 8,
// Face 4 (Bottom)
12, 13, 14, 14, 15, 12,
// Face 5 (Left)
16, 17, 18, 18, 19, 16,
// Face 6 (Right)
20, 21, 22, 22, 23, 20
};
mNormals = {
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD,
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK,
VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP,
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN,
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT,
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT,
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK,
VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP,
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN,
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT,
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT,
};
mTexCoords = {
@ -199,20 +189,18 @@ Cube::Cube(DrawMode mode) {
};
break;
// clang-format on
}
}
Triangle::Triangle(DrawMode mode) {
Triangle::Triangle(DrawMode mode)
{
mDrawMode = mode;
const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f);
switch(mode) {
// clang-format off
case QTK_DRAW_ARRAYS:
mIndices = {/* No indices needed for glDrawArrays */};
mIndices = { /* No indices needed for glDrawArrays */ };
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK };
mVertices = {
// Bottom face (Base of the pyramid)
@ -230,11 +218,11 @@ Triangle::Triangle(DrawMode mode) {
};
// Find normals for each triangle of the mesh
for(int i = 0; i < mVertices.size(); i += 3) {
for (int i = 0; i < mVertices.size(); i += 3) {
QVector3D vertexNormal =
QVector3D::normal(mVertices[i], mVertices[i + 1], mVertices[i + 2]);
QVector3D::normal(mVertices[i], mVertices[i+1], mVertices[i+2]);
// Three points share this normal
for(int j = 0; j < 3; j++) {
for (int j = 0; j < 3; j++) {
mNormals.push_back(vertexNormal);
}
}
@ -258,31 +246,31 @@ Triangle::Triangle(DrawMode mode) {
// Triangle shape data for using glDrawElements
case QTK_DRAW_ELEMENTS:
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK };
mVertices = {VERTEX_FBL, VERTEX_FBR, VERTEX_BBL, VERTEX_BBR, triangleTop};
mIndices = {
// Bottom face (Base of the pyramid)
2, 3, 1, // Use customVertexes[2], then 3, 1...
1, 0, 2, // Use customVertexes[1], then 0, 2
2, 3, 1, // Use customVertexes[2], then 3, 1...
1, 0, 2, // Use customVertexes[1], then 0, 2
0, 1, 4, // Front face
3, 2, 4, // Back face
2, 0, 4, // Left face
1, 3, 4, // Right face
0, 1, 4, // Front face
3, 2, 4, // Back face
2, 0, 4, // Left face
1, 3, 4, // Right face
};
mNormals = {
/* Use QTK_DRAW_ELEMENTS_NORMALS for normals with glDrawElements */};
mNormals =
{/* Use QTK_DRAW_ELEMENTS_NORMALS for normals with glDrawElements */};
mTexCoords = {/* No UVs for triangle with glDrawElements */};
mTexCoords = { /* No UVs for triangle with glDrawElements */ };
break;
// Triangle shape data for using normals and UVs with glDrawElements
case QTK_DRAW_ELEMENTS_NORMALS:
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK };
mVertices = {
// Bottom face
@ -307,22 +295,23 @@ Triangle::Triangle(DrawMode mode) {
mIndices = {
// Bottom face (Base of the pyramid)
0, 1, 2, // Use customVertexes[2], then 3, 1...
3, 4, 5, // Use customVertexes[1], then 0, 2
0, 1, 2, // Use customVertexes[2], then 3, 1...
3, 4, 5, // Use customVertexes[1], then 0, 2
6, 7, 8, // Front face
9, 10, 11, // Back face
12, 13, 14, // Left face
15, 16, 17, // Right face
6, 7, 8, // Front face
9, 10, 11, // Back face
12, 13, 14, // Left face
15, 16, 17, // Right face
};
// Find normals for each triangle of the mesh
for(int i = 0; i < mVertices.size(); i += 3) {
QVector3D vertexNormal = QVector3D::normal(
mVertices[mIndices[i]], mVertices[mIndices[i + 1]],
mVertices[mIndices[i + 2]]);
for (int i = 0; i < mVertices.size(); i += 3) {
QVector3D vertexNormal =
QVector3D::normal(mVertices[mIndices[i]],
mVertices[mIndices[i+1]],
mVertices[mIndices[i+2]]);
// Three points share this normal
for(int j = 0; j < 3; j++) {
for (int j = 0; j < 3; j++) {
mNormals.push_back(vertexNormal);
}
}
@ -342,6 +331,6 @@ Triangle::Triangle(DrawMode mode) {
UV_ORIGIN, UV_RIGHT, UV_CORNER,
};
break;
// clang-format on
}
}

132
src/mesh.h Normal file
View File

@ -0,0 +1,132 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 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
#include <QOpenGLWidget>
#include <QVector2D>
#include <QVector3D>
#include <transform3D.h>
class MeshRenderer;
class Object;
// Define vertices for drawing a cube using two faces (8 vertex points)
// Front Vertices
#define VERTEX_FTR QVector3D( 0.5f, 0.5f, 0.5f) // 1
#define VERTEX_FTL QVector3D(-0.5f, 0.5f, 0.5f) // 2
#define VERTEX_FBL QVector3D(-0.5f, -0.5f, 0.5f) // 3
#define VERTEX_FBR QVector3D( 0.5f, -0.5f, 0.5f) // 4
// Back Vertices
#define VERTEX_BTR QVector3D( 0.5f, 0.5f, -0.5f) // 5
#define VERTEX_BTL QVector3D(-0.5f, 0.5f, -0.5f) // 6
#define VERTEX_BBL QVector3D(-0.5f, -0.5f, -0.5f) // 7
#define VERTEX_BBR QVector3D( 0.5f, -0.5f, -0.5f) // 8
// Direction vectors
#define VECTOR_UP QVector3D(0.0f, 1.0f, 0.0f)
#define VECTOR_DOWN QVector3D(0.0f, -1.0f, 0.0f)
#define VECTOR_LEFT QVector3D(-1.0f, 0.0f, 0.0f)
#define VECTOR_RIGHT QVector3D(1.0f, 0.0f, 0.0f)
#define VECTOR_FORWARD QVector3D(0.0f, 0.0f, 1.0f)
#define VECTOR_BACK QVector3D(0.0f, 0.0f, -1.0f)
// Identity and zero vectors
#define VECTOR_ONE QVector3D(1.0f, 1.0f, 1.0f)
#define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f)
// A series of direction vectors to represent cube face normal
#define FACE_TOP VECTOR_UP, VECTOR_UP, VECTOR_UP, \
VECTOR_UP, VECTOR_UP, VECTOR_UP
#define FACE_BOTTOM VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, \
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN
#define FACE_LEFT VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, \
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT
#define FACE_RIGHT VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT
#define FACE_FRONT VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD
#define FACE_BACK VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, \
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK
// Colors using QVector3Ds as RGB values
#define WHITE VECTOR_ONE
#define BLACK VECTOR_ZERO
#define RED QVector3D(1.0f, 0.0f, 0.0f)
#define GREEN QVector3D(0.0f, 1.0f, 0.0f)
#define BLUE QVector3D(0.0f, 0.0f, 1.0f)
#define YELLOW QVector3D(1.0f, 1.0f, 0.0f)
#define CYAN QVector3D(0.0f, 1.0f, 1.0f)
#define MAGENTA QVector3D(1.0f, 0.0f, 1.0f)
#define UV_ORIGIN QVector2D(0.0f, 0.0f)
#define UV_TOP QVector2D(1.0f, 0.0f)
#define UV_RIGHT QVector2D(0.0f, 1.0f)
#define UV_CORNER QVector2D(1.0f, 1.0f)
typedef std::vector<QVector3D> Vertices;
typedef std::vector<QVector3D> Colors;
typedef std::vector<GLuint> Indices;
typedef std::vector<QVector2D> TexCoords;
typedef std::vector<QVector3D> Normals;
enum DrawMode { QTK_DRAW_ARRAYS, QTK_DRAW_ELEMENTS, QTK_DRAW_ELEMENTS_NORMALS };
struct ShapeBase {
ShapeBase(DrawMode mode=QTK_DRAW_ARRAYS, Vertices v={},Indices i={}, Colors c={},
TexCoords t={}, Normals n={})
: mVertices(v), mColors(c), mIndices(i), mTexCoords(t), mNormals(n)
{}
inline const Vertices & vertices() const { return mVertices;}
inline const Indices & indices() const { return mIndices;}
inline const Colors & colors() const { return mColors;}
inline const TexCoords & texCoords() const { return mTexCoords;}
inline const Normals & normals() const { return mNormals;}
protected:
DrawMode mDrawMode;
Vertices mVertices;
Colors mColors;
Indices mIndices;
TexCoords mTexCoords;
Normals mNormals;
};
struct Shape : public ShapeBase {
friend MeshRenderer;
friend Object;
Shape () {}
Shape(const ShapeBase & rhs) : ShapeBase(rhs) {}
virtual inline void setVertices(const Vertices & value) {mVertices = value;}
virtual inline void setIndices(const Indices & value) {mIndices = value;}
virtual inline void setColors(const Colors & value) {mColors = value;}
virtual inline void setTexCoords(const TexCoords & value) {mTexCoords = value;}
virtual inline void setNormals(const Normals & value) {mNormals = value;}
virtual inline void setShape(const Shape & value) { *this = value;}
};
// Primitives inherit from ShapeBase, does not allow setting of shape values
class Mesh {
};
struct Cube : public ShapeBase {
Cube(DrawMode mode=QTK_DRAW_ARRAYS);
};
struct Triangle : public ShapeBase {
Triangle(DrawMode mode=QTK_DRAW_ARRAYS);
};
#endif // QTK_MESH_H

172
src/meshrenderer.cpp Normal file
View File

@ -0,0 +1,172 @@
/*##############################################################################
## 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 ##
##############################################################################*/
#include <QImageReader>
#include <scene.h>
#include <texture.h>
#include <meshrenderer.h>
// Static QHash that holds all MeshRenderer instances using their mName as keys
MeshRenderer::MeshManager MeshRenderer::sInstances;
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape)
: Object(name, shape), mVertexShader(":/multi-color.vert"),
mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES),
mHasTexture(false)
{
mShape = Shape(shape);
init();
sInstances.insert(name, this);
}
MeshRenderer::~MeshRenderer()
{
if (mHasTexture) {
mTexture->destroy();
}
sInstances.remove(mName);
}
// Static member function to retrieve instances of MeshRenderers
MeshRenderer * MeshRenderer::getInstance(const QString & name)
{ return sInstances[name];}
/*******************************************************************************
* Public Member Functions
******************************************************************************/
void MeshRenderer::init()
{
if (mVAO.isCreated()) mVAO.destroy();
if (mProgram.isLinked()) mProgram.removeAllShaders();
if (mVBO.isCreated()) mVBO.destroy();
mVAO.create();
mVAO.bind();
mProgram.create();
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
mVertexShader.c_str());
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
mFragmentShader.c_str());
mProgram.link();
mProgram.bind();
mVBO.create();
mVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
mVBO.bind();
// Combine position and color data into one vector, allowing us to use one VBO
Vertices combined;
combined.reserve(vertices().size() + colors().size());
combined.insert(combined.end(), vertices().begin(), vertices().end());
combined.insert(combined.end(), colors().begin(), colors().end());
mVBO.allocate(combined.data(),
combined.size() * sizeof(combined[0]));
// Enable position attribute
mProgram.enableAttributeArray(0);
mProgram.setAttributeBuffer(0, GL_FLOAT, 0,
3, sizeof(QVector3D));
// Enable color attribute, setting offset to total size of vertices()
mProgram.enableAttributeArray(1);
mProgram.setAttributeBuffer(1, GL_FLOAT,
vertices().size() * sizeof(vertices()[0]),
3, sizeof(QVector3D));
mVBO.release();
mProgram.release();
mVAO.release();
}
void MeshRenderer::draw()
{
mProgram.bind();
mVAO.bind();
if(mHasTexture) {
mTexture->bind();
}
// TODO: Automate uniforms some other way
setUniformMVP();
if (mShape.mDrawMode == QTK_DRAW_ARRAYS) {
glDrawArrays(mDrawType, 0, vertices().size());
}
else if (mShape.mDrawMode == QTK_DRAW_ELEMENTS
|| mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) {
glDrawElements(mDrawType, mShape.mIndices.size(),
GL_UNSIGNED_INT, mShape.mIndices.data());
}
if(mHasTexture) {
mTexture->release();
}
mVAO.release();
mProgram.release();
}
void MeshRenderer::setShaders(const std::string & vert, const std::string & frag)
{
mVertexShader = vert;
mFragmentShader = frag;
}
void MeshRenderer::setUniformMVP(const char * model, const char * view,
const char * projection)
{
mProgram.setUniformValue(projection, Scene::Projection());
mProgram.setUniformValue(view, Scene::View());
mProgram.setUniformValue(model, mTransform.toMatrix());
}
void MeshRenderer::setColor(const QVector3D & color)
{
if (mShape.mColors.empty()) {
for (const auto & vertex : mShape.vertices()) {
mShape.mColors.push_back(color);
}
}
else {
for (int i = 0; i < mShape.colors().size(); i++) {
mShape.mColors[i] = color;
}
}
}
void MeshRenderer::setTexture(const char * path)
{
mTexture = new QOpenGLTexture(*Texture::initImage(path));
mHasTexture = true;
}
void MeshRenderer::setTexture(QOpenGLTexture * texture)
{
mTexture = texture;
mHasTexture = true;
}
/*******************************************************************************
* Inherited Virtual Member Functions
******************************************************************************/
void MeshRenderer::setShape(const Shape & value)
{
Object::setShape(value);
init();
}

76
src/meshrenderer.h Normal file
View File

@ -0,0 +1,76 @@
/*##############################################################################
## 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>
class MeshRenderer : public Object {
public:
// Delegate constructors
MeshRenderer(const char * name, Vertices vertices, Indices indices,
DrawMode mode=QTK_DRAW_ARRAYS)
: MeshRenderer(name, ShapeBase(mode, vertices, indices))
{}
MeshRenderer(const char * name)
: MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS))
{}
// Constructor
MeshRenderer(const char * name, const ShapeBase &shape);
~MeshRenderer();
// Retrieve a mesh by name stored within a static QHash
static MeshRenderer * getInstance(const QString & name);
void init();
void draw();
// 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)
{ mProgram.setUniformValue(location, value);}
template <typename T>
inline void setUniform(const char * location, T value)
{ 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");
// Sets the texture to the image at the given path
// + Sets mHasTexture to enable texture binding in draw()
void setTexture(const char * path);
void setTexture(QOpenGLTexture * texture);
// 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);
// Static QHash of all mesh objects within the scene
typedef QHash<QString, MeshRenderer *> MeshManager;
private:
static MeshManager sInstances;
int mDrawType;
bool mHasTexture;
std::string mVertexShader, mFragmentShader;
};
#endif // QTK_MESHRENDERER_H

401
src/model.cpp Normal file
View File

@ -0,0 +1,401 @@
/*##############################################################################
## 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 ##
##############################################################################*/
#include <QFileInfo>
#include <scene.h>
#include <texture.h>
#include <model.h>
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)
{
// 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::View());
shader.setUniformValue("uProjection", Scene::Projection());
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
******************************************************************************/
void Model::draw()
{
for (GLuint i = 0; i < mMeshes.size(); i++) {
mMeshes[i].mTransform = mTransform;
mMeshes[i].draw();
}
}
void Model::draw(QOpenGLShaderProgram & shader)
{
for (GLuint i = 0; i < mMeshes.size(); i++) {
mMeshes[i].mTransform = mTransform;
mMeshes[i].draw(shader);
}
}
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
{
bool modified = false;
std::string fullPath = mDirectory + '/' + fileName;
for (auto & texture : mTexturesLoaded) {
if (texture.mPath == fileName) {
texture.mTexture->destroy();
texture.mTexture->create();
texture.mTexture->setData(
*Texture::initImage(fullPath.c_str(), flipX, flipY));
modified = true;
}
}
if (!modified) {
qDebug() << "Attempt to flip texture that doesn't exist: "
<< fullPath.c_str() << "\n";
}
}
/*******************************************************************************
* Model 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();
std::string temp = info.absoluteFilePath().toStdString();
// 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(temp, 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 = path.substr(0, path.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
processNode(scene->mRootNode, scene);
// 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();
// Object finished loading, insert it into ModelManager
mManager.insert(mName, this);
}
void Model::processNode(aiNode * node, const aiScene * scene)
{
// Process each mesh that is available for this node
for (GLuint i = 0; i < node->mNumMeshes; i++) {
aiMesh * mesh = scene->mMeshes[node->mMeshes[i]];
mMeshes.push_back(processMesh(mesh, scene));
}
// Process each child node for this mesh using recursion
for (GLuint i = 0; i < node->mNumChildren; i++) {
processNode(node->mChildren[i], scene);
}
}
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
{
ModelMesh::Vertices vertices;
ModelMesh::Indices indices;
ModelMesh::Textures textures;
// For each vertex in the aiMesh
for (GLuint i = 0; i < mesh->mNumVertices; i++) {
// Create a local vertex object for positions, normals, and texture coords
ModelVertex vertex;
// Reuse this vector to initialize positions and normals
QVector3D vector3D;
// Initialize vertex position
vector3D.setX(mesh->mVertices[i].x);
vector3D.setY(mesh->mVertices[i].y);
vector3D.setZ(mesh->mVertices[i].z);
// Set the position of our local vertex to the local vector object
vertex.mPosition = vector3D;
if (mesh->HasNormals()) {
// Initialize vertex normal
vector3D.setX(mesh->mNormals[i].x);
vector3D.setY(mesh->mNormals[i].y);
vector3D.setZ(mesh->mNormals[i].z);
// Set the normals of our local vertex to the local vector object
vertex.mNormal = vector3D;
}
// Initialize texture coordinates, if any are available
if (mesh->mTextureCoords[0]) {
QVector2D vector2D;
// Texture coordinates
vector2D.setX(mesh->mTextureCoords[0][i].x);
vector2D.setY(mesh->mTextureCoords[0][i].y);
vertex.mTextureCoord = vector2D;
// Tangents
vector3D.setX(mesh->mTangents[i].x);
vector3D.setY(mesh->mTangents[i].y);
vector3D.setZ(mesh->mTangents[i].z);
vertex.mTangent = vector3D;
// Bitangents
vector3D.setX(mesh->mBitangents[i].x);
vector3D.setY(mesh->mBitangents[i].y);
vector3D.setZ(mesh->mBitangents[i].z);
vertex.mBitangent = vector3D;
}
else {
vertex.mTextureCoord = {0.0f, 0.0f};
}
// Add the initialized vertex to our container of vertices
vertices.push_back(vertex);
}
// For each face on the mesh, process its indices
for (GLuint i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (GLuint j = 0; j < face.mNumIndices; j++) {
// Add the index to out container of indices
indices.push_back(face.mIndices[j]);
}
}
// Process material
if (mesh->mMaterialIndex >= 0) {
// 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");
// Insert all diffuse textures found into our textures container
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// Get all specular textures from the material
ModelMesh::Textures specularMaps =
loadMaterialTextures(material, aiTextureType_SPECULAR,
"texture_specular");
// Insert all specular textures found into our textures container
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
// Get all normal textures from the material
ModelMesh::Textures normalMaps =
loadMaterialTextures(material, aiTextureType_HEIGHT,
"texture_normal");
// Insert all normal maps found into our textures container
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
}
return ModelMesh(vertices, indices, textures,
mVertexShader, mFragmentShader);
}
ModelMesh::Textures Model::loadMaterialTextures(
aiMaterial * mat, aiTextureType type, const std::string & typeName)
{
ModelMesh::Textures textures;
for (GLuint i = 0; i < mat->GetTextureCount(type); i++) {
// Call GetTexture to get the name of the texture file to load
aiString fileName;
mat->GetTexture(type, i, &fileName);
// Check if we have already loaded this texture
bool skip = false;
for (GLuint j = 0; j < mTexturesLoaded.size(); j++) {
// If the path to the texture already exists in m_texturesLoaded, skip it
if (std::strcmp(mTexturesLoaded[j].mPath.data(), fileName.C_Str()) == 0) {
textures.push_back(mTexturesLoaded[j]);
// If we have loaded the texture, do not load it again
skip = true;
break;
}
}
// If the texture has not yet been loaded
if (!skip) {
ModelTexture texture;
texture.mTexture = Texture::initTexture2D(
std::string(mDirectory + '/' + fileName.C_Str()).c_str(),
false, false);
texture.mID = texture.mTexture->textureId();
texture.mType = typeName;
texture.mPath = fileName.C_Str();
// Add the texture to the textures container
textures.push_back(texture);
// Add the texture to the loaded textures to avoid loading it twice
mTexturesLoaded.push_back(texture);
}
}
// Return the resulting textures
return textures;
}
void Model::sortModels()
{
auto cameraPos = Scene::Camera().transform();
auto cameraDistance = [&cameraPos](const ModelMesh &a, const ModelMesh &b)
{
// Sort by the first vertex position, since all transforms will be the same
return (cameraPos.translation().distanceToPoint(a.mVertices[0].mPosition))
< (cameraPos.translation().distanceToPoint(b.mVertices[0].mPosition));
};
std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance);
}

139
src/model.h Normal file
View File

@ -0,0 +1,139 @@
/*##############################################################################
## 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/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
// QTK
#include <transform3D.h>
struct ModelVertex {
QVector3D mPosition;
QVector3D mNormal;
QVector3D mTangent;
QVector3D mBitangent;
QVector2D mTextureCoord;
};
struct ModelTexture {
GLuint mID;
QOpenGLTexture * mTexture;
std::string mType;
std::string mPath;
};
class Model;
class ModelMesh {
public:
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() {}
private:
void initMesh(const char * vert, const char * frag);
// ModelMesh Private Members
QOpenGLBuffer * mVBO, * mEBO;
QOpenGLVertexArrayObject * mVAO;
QOpenGLShaderProgram * mProgram;
public:
inline void draw() { draw(*mProgram);}
void draw(QOpenGLShaderProgram & shader);
// ModelMesh Public Members
Vertices mVertices;
Indices mIndices;
Textures mTextures;
Transform3D mTransform;
};
class Model : public QObject {
Q_OBJECT
public:
inline Model(const char * name, const char * path,
const char * vertexShader=":/model-basic.vert",
const char * fragmentShader=":/model-basic.frag")
: mName(name), mVertexShader(vertexShader),
mFragmentShader(fragmentShader)
{ loadModel(path);}
inline ~Model() { mManager.remove(mName);}
void draw();
void draw(QOpenGLShaderProgram & shader);
void flipTexture(const std::string & fileName,
bool flipX=false, bool flipY=true);
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();
}
}
Transform3D mTransform;
static Model * getInstance(const char * name);
typedef QHash<QString, Model *> ModelManager;
private:
static ModelManager mManager;
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();
// Model Private Members
ModelMesh::Textures mTexturesLoaded;
std::vector<ModelMesh> mMeshes;
std::string mDirectory;
const char * mVertexShader, * mFragmentShader, * mName;
};
#endif // QTK_MODEL_H

View File

@ -1,11 +1,9 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2022 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"
using namespace Qtk;
#include <object.h>

61
src/object.h Normal file
View File

@ -0,0 +1,61 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Object class for storing object data ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_OBJECT_H
#define QTK_OBJECT_H
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <mesh.h>
class Object : public QObject {
Q_OBJECT
public:
friend MeshRenderer;
// Initialize an object with no shape data assigned
Object(const char * name)
: mName(name), mVBO(QOpenGLBuffer::VertexBuffer)
{ }
// Initialize an object with shape data assigned
Object(const char * name, const ShapeBase & shape)
: mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape)
{ }
~Object() {}
inline const Vertices & vertices() { return mShape.mVertices;}
inline const Indices & indices() { return mShape.mIndices;}
inline const Colors & colors() { return mShape.mColors;}
inline const TexCoords & texCoords() { return mShape.mTexCoords;}
inline const Normals & normals() { return mShape.mNormals;}
inline QOpenGLTexture & texture() const { return *mTexture;}
virtual inline void setVertices(const Vertices & value) { mShape.mVertices = value;}
virtual inline void setIndices(const Indices & value) { mShape.mIndices = value;}
virtual inline void setColors(const Colors & value) { mShape.mColors = value;}
virtual inline void setTexCoords(const TexCoords & value) { mShape.mTexCoords = value;}
virtual inline void setNormals(const Normals & value) { mShape.mNormals = value;}
virtual inline void setShape(const Shape & value) { mShape = value;}
QOpenGLBuffer mVBO, mNBO;
QOpenGLVertexArrayObject mVAO;
QOpenGLShaderProgram mProgram;
Transform3D mTransform;
Shape mShape;
const char * mName;
private:
QOpenGLTexture * mTexture;
};
#endif // QTK_OBJECT_H

View File

@ -1,79 +0,0 @@
################################################################################
## 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_SUBMODULES OR NOT QTK_ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk_library PUBLIC assimp)
elseif(QTK_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,112 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## 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 ##
##############################################################################*/
#ifndef QTK_CAMERA3D_H
#define QTK_CAMERA3D_H
#include <QDebug>
#include "qtkapi.h"
#include "transform3D.h"
namespace Qtk {
class QTKAPI Camera3D {
public:
/*************************************************************************
* Static Public Constants
************************************************************************/
static const QVector3D LocalForward;
static const QVector3D LocalUp;
static const QVector3D LocalRight;
/*************************************************************************
* 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();
}
/**
* @return QVector3D for the forward vector of the camera.
*/
[[nodiscard]] inline QVector3D getForward() const {
return mTransform.getRotation().rotatedVector(LocalForward);
}
/**
* @return QVector3D for the right vector of the camera.
*/
[[nodiscard]] inline QVector3D getRight() const {
return mTransform.getRotation().rotatedVector(LocalRight);
}
/**
* @return QVector3D for the up vector of the camera.
*/
[[nodiscard]] inline QVector3D getUp() const {
return mTransform.getRotation().rotatedVector(LocalUp);
}
/*************************************************************************
* Public Methods
************************************************************************/
/**
* @return World To View matrix for this camera.
*/
const QMatrix4x4 & toMatrix();
private:
/*************************************************************************
* Private Methods
************************************************************************/
#ifndef QT_NO_DATASTREAM
friend QDataStream & operator<<(QDataStream & out, Camera3D & transform);
friend QDataStream & operator>>(QDataStream & in, Camera3D & transform);
#endif
/*************************************************************************
* Private Members
************************************************************************/
Transform3D mTransform;
QMatrix4x4 mWorld;
};
// Qt Streams
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Camera3D & transform);
QDataStream & operator>>(QDataStream & in, Camera3D & transform);
#endif
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Camera3D & transform);
#endif
} // namespace Qtk
Q_DECLARE_TYPEINFO(Qtk::Camera3D, Q_MOVABLE_TYPE);
#endif // QTK_CAMERA3D_H

View File

@ -1,155 +0,0 @@
/*##############################################################################
## 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,222 +0,0 @@
/*##############################################################################
## 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 ##
##############################################################################*/
#include <QImageReader>
#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, 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.c_str());
}
/*******************************************************************************
* Public Methods
******************************************************************************/
void MeshRenderer::init() {
if(mVAO.isCreated()) {
mVAO.destroy();
}
if(mProgram.isLinked()) {
mProgram.removeAllShaders();
}
if(mVBO.isCreated()) {
mVBO.destroy();
}
mVAO.create();
mVAO.bind();
mProgram.create();
mProgram.addShaderFromSourceFile(
QOpenGLShader::Vertex, mVertexShader.c_str());
mProgram.addShaderFromSourceFile(
QOpenGLShader::Fragment, mFragmentShader.c_str());
mProgram.link();
mProgram.bind();
mVBO.create();
mVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
mVBO.bind();
// Combine position and color data into one vector, allowing us to use one VBO
Vertices combined;
combined.reserve(getVertices().size() + getColors().size());
combined.insert(combined.end(), getVertices().begin(), getVertices().end());
combined.insert(combined.end(), getColors().begin(), getColors().end());
mVBO.allocate(combined.data(), combined.size() * sizeof(combined[0]));
// Enable position attribute
mProgram.enableAttributeArray(0);
mProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D));
// Enable color attribute, setting offset to total size of vertices()
mProgram.enableAttributeArray(1);
mProgram.setAttributeBuffer(
1, GL_FLOAT, getVertices().size() * sizeof(getVertices()[0]), 3,
sizeof(QVector3D));
mVBO.release();
mProgram.release();
mVAO.release();
}
void MeshRenderer::draw() {
bindShaders();
mVAO.bind();
if(mTexture.hasTexture()) {
mTexture.getOpenGLTexture().bind();
}
// TODO: Automate uniforms some other way
setUniformMVP();
if(mShape.mDrawMode == QTK_DRAW_ARRAYS) {
glDrawArrays(mDrawType, 0, getVertices().size());
} else if(
mShape.mDrawMode == QTK_DRAW_ELEMENTS
|| mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) {
glDrawElements(
mDrawType, mShape.mIndices.size(), GL_UNSIGNED_INT,
mShape.mIndices.data());
}
if(mTexture.hasTexture()) {
mTexture.getOpenGLTexture().release();
}
mVAO.release();
releaseShaders();
}
void MeshRenderer::enableAttributeArray(int location) {
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.enableAttributeArray(location);
mVAO.release();
}
void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims) {
mVAO.bind();
mNBO.destroy();
mNBO.create();
mNBO.bind();
mNBO.allocate(t.data(), t.size() * sizeof(t[0]));
enableAttributeArray(1);
if(dims == 2) {
setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D));
} else if(dims == 3) {
setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D));
}
mNBO.release();
mVAO.release();
}
void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims) {
// TODO: Store state to track if buffer objects are bound
mVAO.bind();
mNBO.destroy();
mNBO.create();
mNBO.bind();
mNBO.allocate(n.data(), n.size() * sizeof(n[0]));
enableAttributeArray(1);
if(dims == 2) {
setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D));
} else if(dims == 3) {
setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D));
}
mNBO.release();
mVAO.release();
}
void MeshRenderer::setShaders(
const std::string & vert, const std::string & frag) {
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];
}

View File

@ -1,234 +0,0 @@
/*##############################################################################
## 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; }
inline std::string getVertexShader() const override {
return mVertexShader;
}
inline std::string getFragmentShader() const override {
return mFragmentShader;
}
private:
/*************************************************************************
* Private Members
************************************************************************/
static MeshManager sInstances;
int mDrawType {};
std::string mVertexShader {}, mFragmentShader {};
};
} // namespace Qtk
#endif // QTK_MESHRENDERER_H

View File

@ -1,259 +0,0 @@
/*##############################################################################
## 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 ##
##############################################################################*/
#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;
/*******************************************************************************
* Public Member Functions
******************************************************************************/
void Model::draw() {
for(auto & mesh : mMeshes) {
mesh.mTransform = mTransform;
mesh.draw();
}
}
void Model::draw(QOpenGLShaderProgram & shader) {
for(auto & mesh : mMeshes) {
mesh.mTransform = mTransform;
mesh.draw(shader);
}
}
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
bool modified = false;
std::string fullPath = mDirectory + '/' + fileName;
for(auto & texture : mTexturesLoaded) {
if(texture.mPath == fileName) {
texture.mTexture->destroy();
texture.mTexture->create();
texture.mTexture->setData(
OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY));
modified = true;
}
}
if(!modified) {
qDebug() << "Attempt to flip texture that doesn't exist: "
<< fullPath.c_str() << "\n";
}
}
// Static function to access ModelManager for getting Models by name
Model * Qtk::Model::getInstance(const char * name) {
return mManager[name];
}
/*******************************************************************************
* Private Member Functions
******************************************************************************/
void Model::loadModel(const std::string & path) {
Assimp::Importer import;
// 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(
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;
}
// Pass the pointers to the root node and the scene to recursive function
// + Base case breaks when no nodes left to process on model
processNode(scene->mRootNode, scene);
// 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
sortModelMeshes();
// Object finished loading, insert it into ModelManager
mManager.insert(getName().c_str(), this);
}
void Model::processNode(aiNode * node, const aiScene * scene) {
// Process each mesh that is available for this node
for(GLuint i = 0; i < node->mNumMeshes; i++) {
aiMesh * mesh = scene->mMeshes[node->mMeshes[i]];
mMeshes.push_back(processMesh(mesh, scene));
}
// Process each child node for this mesh using recursion
for(GLuint i = 0; i < node->mNumChildren; i++) {
processNode(node->mChildren[i], scene);
}
}
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
ModelMesh::Vertices vertices;
ModelMesh::Indices indices;
ModelMesh::Textures textures;
// For each vertex in the aiMesh
for(GLuint i = 0; i < mesh->mNumVertices; i++) {
// Create a local vertex object for positions, normals, and texture coords
ModelVertex vertex;
// Reuse this vector to initialize positions and normals
QVector3D vector3D;
// Initialize vertex position
vector3D.setX(mesh->mVertices[i].x);
vector3D.setY(mesh->mVertices[i].y);
vector3D.setZ(mesh->mVertices[i].z);
// Set the position of our local vertex to the local vector object
vertex.mPosition = vector3D;
if(mesh->HasNormals()) {
// Initialize vertex normal
vector3D.setX(mesh->mNormals[i].x);
vector3D.setY(mesh->mNormals[i].y);
vector3D.setZ(mesh->mNormals[i].z);
// Set the normals of our local vertex to the local vector object
vertex.mNormal = vector3D;
}
// Initialize texture coordinates, if any are available
if(mesh->mTextureCoords[0]) {
QVector2D vector2D;
// Texture coordinates
vector2D.setX(mesh->mTextureCoords[0][i].x);
vector2D.setY(mesh->mTextureCoords[0][i].y);
vertex.mTextureCoord = vector2D;
// Tangents
vector3D.setX(mesh->mTangents[i].x);
vector3D.setY(mesh->mTangents[i].y);
vector3D.setZ(mesh->mTangents[i].z);
vertex.mTangent = vector3D;
// Bitangents
vector3D.setX(mesh->mBitangents[i].x);
vector3D.setY(mesh->mBitangents[i].y);
vector3D.setZ(mesh->mBitangents[i].z);
vertex.mBitangent = vector3D;
} else {
vertex.mTextureCoord = {0.0f, 0.0f};
}
// Add the initialized vertex to our container of vertices
vertices.push_back(vertex);
}
// For each face on the mesh, process its indices
for(GLuint i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for(GLuint j = 0; j < face.mNumIndices; j++) {
// Add the index to out container of indices
indices.push_back(face.mIndices[j]);
}
}
// Process material
if(mesh->mMaterialIndex >= 0) {
// 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");
// Insert all diffuse textures found into our textures container
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// Get all specular textures from the material
ModelMesh::Textures specularMaps = loadMaterialTextures(
material, aiTextureType_SPECULAR, "texture_specular");
// Insert all specular textures found into our textures container
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
// Get all normal textures from the material
ModelMesh::Textures normalMaps =
loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
// Insert all normal maps found into our textures container
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
}
return {
vertices, indices, textures, mVertexShader.c_str(),
mFragmentShader.c_str()};
}
ModelMesh::Textures Model::loadMaterialTextures(
aiMaterial * mat, aiTextureType type, const std::string & typeName) {
ModelMesh::Textures textures;
for(GLuint i = 0; i < mat->GetTextureCount(type); i++) {
// Call GetTexture to get the name of the texture file to load
aiString fileName;
mat->GetTexture(type, i, &fileName);
// Check if we have already loaded this texture
bool skip = false;
for(auto & j : mTexturesLoaded) {
// If the path to the texture already exists in m_texturesLoaded, skip it
if(std::strcmp(j.mPath.data(), fileName.C_Str()) == 0) {
textures.push_back(j);
// If we have loaded the texture, do not load it again
skip = true;
break;
}
}
// If the texture has not yet been loaded
if(!skip) {
ModelTexture texture;
texture.mTexture = OpenGLTextureFactory::initTexture(
std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false,
false);
texture.mID = texture.mTexture->textureId();
texture.mType = typeName;
texture.mPath = fileName.C_Str();
// Add the texture to the textures container
textures.push_back(texture);
// Add the texture to the loaded textures to avoid loading it twice
mTexturesLoaded.push_back(textures.back());
}
}
// Return the resulting textures
return textures;
}
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
return (cameraPos.getTranslation().distanceToPoint(
a.mVertices[0].mPosition))
< (cameraPos.getTranslation().distanceToPoint(
b.mVertices[0].mPosition));
};
std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance);
}

View File

@ -1,215 +0,0 @@
/*##############################################################################
## 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 <QFileInfo>
#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; }
inline std::string getVertexShader() const override {
return mVertexShader;
}
inline std::string getFragmentShader() const override {
return mFragmentShader;
}
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

View File

@ -1,132 +0,0 @@
/*##############################################################################
## 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);
}
// Always reset active texture to GL_TEXTURE0 before we draw.
// This is important for models with no textures.
glActiveTexture(GL_TEXTURE0);
// 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();
}
/*******************************************************************************
* 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();
}

View File

@ -1,154 +0,0 @@
/*##############################################################################
## 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 {
ModelTexture() = default;
/**
* Construct a ModelTexture.
*
* @param id Texture ID for this texture.
* @param type Type of texture in string format.
* @param path Path to the texture on disk.
*/
ModelTexture(const std::string & type, const std::string & path) :
mType(type), mPath(path) {
mTexture = OpenGLTextureFactory::initTexture(path.c_str());
mID = mTexture->textureId();
}
/** Texture ID for for this texture. */
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,216 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## 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 ##
##############################################################################*/
#ifndef QTK_OBJECT_H
#define QTK_OBJECT_H
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#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.
*/
class QTKAPI Object : public QObject {
Q_OBJECT
public:
/*************************************************************************
* Typedefs
************************************************************************/
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, 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, Type type) :
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape),
mBound(false), mType(type) {
initResources();
setObjectName(name);
}
~Object() override = default;
/*************************************************************************
* Accessors
************************************************************************/
[[nodiscard]] inline const Colors & getColors() const {
return mShape.mColors;
}
[[nodiscard]] inline const Indices & getIndexData() const {
return mShape.mIndices;
}
[[nodiscard]] inline const Normals & getNormals() const {
return mShape.mNormals;
}
[[nodiscard]] inline const Shape & getShape() const { return mShape; }
[[nodiscard]] inline const TexCoords & getTexCoords() const {
return mShape.mTexCoords;
}
[[nodiscard]] inline const Texture & getTexture() const {
return mTexture;
}
[[nodiscard]] inline const Vertices & getVertices() const {
return mShape.mVertices;
}
[[nodiscard]] inline std::string getName() const { return mName; }
[[nodiscard]] inline const Type & getType() const { return mType; }
[[nodiscard]] inline virtual const Transform3D & getTransform() const {
return mTransform;
}
[[nodiscard]] inline virtual std::string getVertexShader() const {
return "Base Object has no vertex shader.";
}
virtual inline std::string getFragmentShader() const {
return "Base Object has no fragment shader.";
}
/*************************************************************************
* Setters
************************************************************************/
virtual inline void setName(const std::string & name) { mName = name; }
virtual inline void setColors(const Colors & value) {
mShape.mColors = value;
}
virtual inline void setIndices(const Indices & value) {
mShape.mIndices = value;
}
virtual inline void setNormals(const Normals & value) {
mShape.mNormals = value;
}
virtual inline void setShape(const Shape & value) { mShape = value; }
virtual inline void setTexCoords(const TexCoords & value) {
mShape.mTexCoords = value;
}
virtual inline void setTexture(
const char * path, bool flipX = false, bool flipY = false) {
mTexture.setTexture(path, flipX, flipY);
}
virtual inline void setCubeMap(const char * path) {
mTexture.setCubeMap(path);
}
virtual inline void setTexture(const Texture & t) {
mTexture.setTexture(t.getPath());
}
virtual inline void setVertices(const Vertices & value) {
mShape.mVertices = value;
}
inline void setScaleX(double x) {
mTransform.setScale(
x, mTransform.getScale().y(), mTransform.getScale().z());
}
inline void setScaleY(double y) {
mTransform.setScale(
mTransform.getScale().x(), y, mTransform.getScale().z());
}
inline void setScaleZ(double z) {
mTransform.setScale(
mTransform.getScale().x(), mTransform.getScale().y(), z);
}
inline void setTranslationX(double x) {
mTransform.setTranslation(
x, mTransform.getTranslation().y(),
mTransform.getTranslation().z());
}
inline void setTranslationY(double y) {
mTransform.setTranslation(
mTransform.getTranslation().x(), y,
mTransform.getTranslation().z());
}
inline void setTranslationZ(double z) {
mTransform.setTranslation(
mTransform.getTranslation().x(), mTransform.getTranslation().y(),
z);
}
/*************************************************************************
* Public Methods
************************************************************************/
virtual inline void bindShaders() {
mBound = true;
mProgram.bind();
}
virtual inline void releaseShaders() {
mBound = false;
mProgram.release();
}
private:
/*************************************************************************
* Private Members
************************************************************************/
QOpenGLShaderProgram mProgram;
QOpenGLBuffer mVBO, mNBO;
QOpenGLVertexArrayObject mVAO;
Transform3D mTransform;
Shape mShape;
Texture mTexture;
std::string mName;
bool mBound;
Type mType = QTK_OBJECT;
};
} // namespace Qtk
#endif // QTK_OBJECT_H

View File

@ -1,62 +0,0 @@
/*##############################################################################
## 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

Some files were not shown because too many files have changed in this diff Show More