Compare commits
15 Commits
c745da30c6
...
master
Author | SHA1 | Date |
---|---|---|
Transporter | d0c8316f79 | |
Shaun Reed | ad59d9149e | |
Shaun Reed | e889785b65 | |
Shaun Reed | a04ebae42a | |
Shaun Reed | 443c09da7c | |
Shaun Reed | 0f372f71f4 | |
Shaun Reed | ad4d4636a4 | |
Shaun Reed | 86b3f78368 | |
Shaun Reed | 44d9092306 | |
Shaun Reed | f3406ee749 | |
Shaun Reed | 249a2b4446 | |
Shaun Reed | 432bf9919c | |
Shaun Reed | 52521dc331 | |
Shaun Reed | 5452520324 | |
Shaun Reed | c15d064dce |
|
@ -0,0 +1,73 @@
|
||||||
|
---
|
||||||
|
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
|
||||||
|
...
|
|
@ -0,0 +1,146 @@
|
||||||
|
# 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'
|
|
@ -0,0 +1,332 @@
|
||||||
|
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
|
|
@ -1,26 +0,0 @@
|
||||||
name: Build Test
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Ubuntu:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Install Qt
|
|
||||||
uses: jurplel/install-qt-action@v2
|
|
||||||
with:
|
|
||||||
version: '6.2.3'
|
|
||||||
|
|
||||||
- name: Install Assimp
|
|
||||||
run: |
|
|
||||||
sudo apt install libassimp-dev -y
|
|
||||||
|
|
||||||
- name: Build Qtk
|
|
||||||
run: |
|
|
||||||
mkdir build && cd build
|
|
||||||
cmake .. -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.2.3/gcc_64/ && cmake --build .
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
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 }}
|
|
@ -1,9 +1,13 @@
|
||||||
# CLion
|
# CLion
|
||||||
**/.idea/**
|
**/.idea/**
|
||||||
|
|
||||||
|
# VS Code
|
||||||
|
**/.vscode/**
|
||||||
|
|
||||||
# CMake build files
|
# CMake build files
|
||||||
**/cmake-build-debug/**
|
**/cmake-build-debug/**
|
||||||
**/build/**
|
**/build/**
|
||||||
|
install
|
||||||
|
|
||||||
# C++ objects and libs
|
# C++ objects and libs
|
||||||
*.slo
|
*.slo
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "extern/assimp/assimp"]
|
||||||
|
path = extern/assimp/assimp
|
||||||
|
url = https://github.com/assimp/assimp.git
|
228
CMakeLists.txt
|
@ -1,80 +1,200 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
|
||||||
## ##
|
|
||||||
## Project for working with OpenGL and Qt6 widgets ##
|
## Project for working with OpenGL and Qt6 widgets ##
|
||||||
|
## ##
|
||||||
|
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
||||||
|
## All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
################################################################################
|
################################################################################
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.23)
|
||||||
|
|
||||||
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_INCLUDE_CURRENT_DIR ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
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)
|
||||||
|
|
||||||
# For CLion builds, point CMAKE_PREFIX_PATH to Qt6 install directory
|
if(WIN32)
|
||||||
list(APPEND CMAKE_PREFIX_PATH $ENV{HOME}/Code/Clones/Qt6.2/6.2.3/gcc_64/)
|
set(CMAKE_COMPILE_WARNING_AS_ERROR OFF)
|
||||||
find_package(Qt6 COMPONENTS OpenGLWidgets REQUIRED)
|
add_compile_options(/wd4131 /wd4127)
|
||||||
|
endif()
|
||||||
|
add_compile_options(-fPIC)
|
||||||
|
|
||||||
# Add our Qt resources.qrc file to our application
|
################################################################################
|
||||||
set(SOURCES app/main.cpp)
|
# Project
|
||||||
qt6_add_resources(SOURCES resources.qrc)
|
################################################################################
|
||||||
|
project(
|
||||||
add_executable(
|
#[[NAME]] Qtk
|
||||||
qtk # Executable name
|
VERSION 0.2
|
||||||
${SOURCES} # Executable source code
|
DESCRIPTION "Qt OpenGL library and desktop application."
|
||||||
|
LANGUAGES CXX C
|
||||||
)
|
)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# External Libraries
|
# Includes
|
||||||
################################################################################
|
################################################################################
|
||||||
|
include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake")
|
||||||
# https://github.com/assimp/assimp/commit/6ac8279977c3a54118551e549d77329497116f66
|
include(CMakePackageConfigHelpers)
|
||||||
find_package(assimp REQUIRED)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Custom Libraries
|
# Options
|
||||||
################################################################################
|
################################################################################
|
||||||
|
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)
|
||||||
|
|
||||||
# Mainwidget
|
if (QTK_CCACHE)
|
||||||
include(GenerateExportHeader)
|
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
|
||||||
add_library(main-widget SHARED
|
endif()
|
||||||
src/mainwidget.cpp src/mainwidget.h
|
|
||||||
src/mainwindow.cpp src/mainwindow.h src/mainwindow.ui
|
# Install Qtk for use within Qt Creator projects only, instead of system-wide.
|
||||||
src/input.cpp src/input.h
|
option(QTK_PREFIX_QTCREATOR "Install Qtk to Qt Creator. Untested." OFF)
|
||||||
src/mesh.cpp src/mesh.h
|
|
||||||
src/texture.cpp src/texture.h
|
# Option for bringing your own assimp installation; Otherwise not needed
|
||||||
src/object.cpp src/object.h
|
# + If assimp is available system-wide we can just set QTK_SUBMODULES OFF
|
||||||
src/meshrenderer.cpp src/meshrenderer.h
|
option(
|
||||||
src/camera3d.cpp src/camera3d.h
|
QTK_ASSIMP_NEW_INTERFACE
|
||||||
src/skybox.cpp src/skybox.h
|
"Use the assimp::assimp interface (WIN / OSX)"
|
||||||
src/transform3D.cpp src/transform3D.h
|
OFF
|
||||||
src/model.cpp src/model.h
|
|
||||||
src/scene.cpp src/scene.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(main-widget PUBLIC src/)
|
if(QTK_DEBUG OR CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$")
|
||||||
target_link_libraries(main-widget PRIVATE assimp)
|
set(QTK_DEBUG ON)
|
||||||
target_link_libraries(main-widget PUBLIC Qt${QT_VERSION_MAJOR}::OpenGLWidgets)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
else()
|
||||||
|
set(QTK_DEBUG OFF)
|
||||||
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
|
endif()
|
||||||
|
|
||||||
################################################################################
|
# This should be set to your Qt6 installation directory.
|
||||||
# Final Application
|
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 ()
|
||||||
|
|
||||||
# Link qtk executable to main main-widget library
|
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
|
||||||
target_link_libraries(qtk PUBLIC main-widget)
|
set(QTK_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns)
|
||||||
|
|
||||||
set_target_properties(qtk PROPERTIES
|
# Point CMAKE_PREFIX_PATH to Qt6 install directory
|
||||||
WIN32_EXECUTABLE TRUE
|
# If Qtk is built within Qt Creator this is not required.
|
||||||
MACOSX_BUNDLE TRUE
|
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."
|
||||||
)
|
)
|
||||||
|
|
||||||
generate_export_header(main-widget)
|
# 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()
|
||||||
|
|
348
README.md
|
@ -1,39 +1,196 @@
|
||||||
# Qtk
|
# Qtk
|
||||||
|
|
||||||
Practice project for learning about using OpenGL in Qt widget applications.
|
[![All Builds](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml)
|
||||||
Model loader using [Assimp](https://assimp.org/) within a Qt widget application.
|
[![Linting](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml)
|
||||||
|
|
||||||
You can import your own models within `mainwdget.cpp`, inside the
|
Qtk is a Qt OpenGL graphics library created primarily for my own learning
|
||||||
`MainWidget::initObjects()` function. I've commented throughout the code there
|
purposes. The library wraps some QOpenGL functionality in convenience classes
|
||||||
to explain which model or example I'm modifying. Rotations and translations
|
that allow rendering geometry in 2D and 3D using custom GLSL shader programs.
|
||||||
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`
|
|
||||||
|
|
||||||
Can be built with cmake manually or using
|
The long-term goal for this project is to create a tool that I can use to
|
||||||
[Qt Creator](https://github.com/qt-creator/qt-creator).
|
practice shader coding or graphics programming techniques. In doing this I hope
|
||||||
For the build to be successful, I've found through testing on VMs that the system requires around 6GB of RAM.
|
to also learn more about the Qt UI framework, and the CMake build system.
|
||||||
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)
|
|
||||||
|
|
||||||
This project has been ported to Qt6, which is not yet available in Ubuntu apt repositories.
|
Key features that are planned:
|
||||||
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.
|
* 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.
|
||||||
|
|
||||||
Once Qt6 is installed, to build and run `qtk` on Ubuntu -
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt update -y && sudo apt install freeglut3-dev libassimp-dev cmake build-essential git
|
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://gitlab.com/shaunrd0/qtk && cd qtk
|
git clone https://github.com/shaunrd0/qtk
|
||||||
mkdir build && cd build
|
cd qtk
|
||||||
cmake .. -DCMAKE_PREFIX_PATH=$HOME/Qt6/6.2.3/gcc_64 && cmake --build .
|
# Configure the build with all components enabled
|
||||||
./qtk
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 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.
|
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
|
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
|
that is being used for the shader rendering the model. These appear on models
|
||||||
using phong, specular, and diffuse lighting techniques.
|
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)
|
![](resources/screenshot.png)
|
||||||
|
|
||||||
Spartan with no normals -
|
Spartan with no normals -
|
||||||
|
@ -44,20 +201,147 @@ Spartan with normals -
|
||||||
|
|
||||||
![](resources/spartan-normals.png)
|
![](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
|
## Model Artists
|
||||||
|
|
||||||
|
"Alien Hominid" (https://skfb.ly/onStx) by Nwilly_art is licensed under Creative
|
||||||
|
Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
"Alien Hominid" (https://skfb.ly/onStx) by Nwilly_art is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
"Scythe World Of Warcraft" (https://skfb.ly/6UooG) by Warcraft-3D-Models is
|
||||||
|
licensed under Creative Commons
|
||||||
|
Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
"Scythe World Of Warcraft" (https://skfb.ly/6UooG) by Warcraft-3D-Models is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
"Spartan Armour MKV - Halo Reach" (https://skfb.ly/6QVvM) by McCarthy3D is
|
||||||
|
licensed under Creative Commons
|
||||||
|
Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
"Spartan Armour MKV - Halo Reach" (https://skfb.ly/6QVvM) by McCarthy3D is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
"Survival Guitar Backpack (Low Poly)" (https://skfb.ly/6RnCB) by Berk Gedik is
|
||||||
|
licensed under Creative Commons
|
||||||
|
Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
Model by Berk Gedik,
|
||||||
|
from: https://sketchfab.com/3d-models/survival-guitar-backpack-low-poly-799f8c4511f84fab8c3f12887f7e6b36
|
||||||
|
Modified (learnopengl.com) material assignment (Joey de Vries) for easier load
|
||||||
|
in OpenGL model loading chapter, and renamed albedo to diffuse and metallic to
|
||||||
|
specular to match non-PBR lighting setup.
|
||||||
|
|
||||||
"Survival Guitar Backpack (Low Poly)" (https://skfb.ly/6RnCB) by Berk Gedik is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
"Terror-bird (NHMW-Geo 2012/0007/0001)" (https://skfb.ly/onAWy) by Natural
|
||||||
Model by Berk Gedik, from: https://sketchfab.com/3d-models/survival-guitar-backpack-low-poly-799f8c4511f84fab8c3f12887f7e6b36
|
History Museum Vienna is licensed under Creative Commons
|
||||||
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.
|
Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/).
|
||||||
|
|
||||||
"Terror-bird (NHMW-Geo 2012/0007/0001)" (https://skfb.ly/onAWy) by Natural History Museum Vienna is licensed under Creative Commons Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/).
|
|
||||||
|
|
||||||
"Golden Lion Sitting OBJ Low Poly FREE" (https://skfb.ly/onZAH) by LordSamueliSolo is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
|
||||||
|
|
||||||
|
"Golden Lion Sitting OBJ Low Poly FREE" (https://skfb.ly/onZAH) by
|
||||||
|
LordSamueliSolo is licensed under Creative Commons
|
||||||
|
Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
40
app/main.cpp
|
@ -1,40 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## 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();
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
################################################################################
|
||||||
|
## 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()
|
|
@ -0,0 +1,13 @@
|
||||||
|
@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)
|
|
@ -0,0 +1,96 @@
|
||||||
|
################################################################################
|
||||||
|
## 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)
|
|
@ -0,0 +1,73 @@
|
||||||
|
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
|
||||||
|
```
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Example Qtk scene ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#ifndef QTK_EXAMPLE_SCENE_H
|
||||||
|
#define QTK_EXAMPLE_SCENE_H
|
||||||
|
|
||||||
|
#include <qtk/scene.h>
|
||||||
|
|
||||||
|
class ExampleScene : public Qtk::SceneInterface {
|
||||||
|
public:
|
||||||
|
ExampleScene(Qtk::Scene * scene);
|
||||||
|
|
||||||
|
~ExampleScene();
|
||||||
|
|
||||||
|
void init() override;
|
||||||
|
|
||||||
|
void draw() override;
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTK_EXAMPLE_SCENE_H
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Example Qtk widget ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <qtk/scene.h>
|
||||||
|
|
||||||
|
#include "examplewidget.h"
|
||||||
|
|
||||||
|
ExampleWidget::ExampleWidget(QWidget * parent) :
|
||||||
|
QOpenGLWidget(parent), mScene(new ExampleScene(new Qtk::SceneEmpty)) {
|
||||||
|
// NOTE: The decorator pattern is used to save / load scenes in Qtk currently.
|
||||||
|
// The initializer above sets mScene to the concrete decorator ExampleScene.
|
||||||
|
// Qtk::SceneEmpty provides an empty scene as the concrete component.
|
||||||
|
// ExampleScene is defined in client source, deriving Qtk::SceneInterface.
|
||||||
|
|
||||||
|
QSurfaceFormat format;
|
||||||
|
format.setRenderableType(QSurfaceFormat::OpenGL);
|
||||||
|
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
|
format.setVersion(4, 6);
|
||||||
|
format.setSamples(4);
|
||||||
|
format.setDepthBufferSize(16);
|
||||||
|
setFormat(format);
|
||||||
|
setFocusPolicy(Qt::ClickFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleWidget::initializeGL() {
|
||||||
|
initializeOpenGLFunctions();
|
||||||
|
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
|
||||||
|
glEnable(GL_MULTISAMPLE);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
glDepthRange(0.1f, 1.0f);
|
||||||
|
glClearDepth(1.0f);
|
||||||
|
glClearColor(0.0f, 0.25f, 0.0f, 0.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleWidget::resizeGL(int width, int height) {
|
||||||
|
Qtk::Scene::getProjectionMatrix().setToIdentity();
|
||||||
|
Qtk::Scene::getProjectionMatrix().perspective(
|
||||||
|
45.0f, float(width) / float(height), 0.1f, 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleWidget::paintGL() {
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||||
|
mScene->draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleWidget::update() {
|
||||||
|
mScene->update();
|
||||||
|
QWidget::update();
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Example Qtk widget ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#ifndef QTKCLIENT_EXAMPLEWIDGET_H
|
||||||
|
#define QTKCLIENT_EXAMPLEWIDGET_H
|
||||||
|
|
||||||
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLWidget>
|
||||||
|
|
||||||
|
#include "examplescene.h"
|
||||||
|
|
||||||
|
class ExampleWidget : public QOpenGLWidget, protected QOpenGLFunctions {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ExampleWidget(QWidget * parent = nullptr);
|
||||||
|
|
||||||
|
~ExampleWidget() = default;
|
||||||
|
|
||||||
|
void initializeGL() override;
|
||||||
|
|
||||||
|
void resizeGL(int width, int height) override;
|
||||||
|
|
||||||
|
void paintGL() override;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Qtk::Scene * mScene;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTKCLIENT_EXAMPLEWIDGET_H
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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();
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#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
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 5d5496f1ad895297cede723b3c96b513263f82ed
|
|
@ -1,81 +0,0 @@
|
||||||
<RCC>
|
|
||||||
<qresource prefix="/">
|
|
||||||
<!--3DModel test shader-->
|
|
||||||
<file alias="model.frag">resources/shaders/fragment/model.frag</file>
|
|
||||||
<file alias="model.vert">resources/shaders/vertex/model.vert</file>
|
|
||||||
<!--Phong test shader-->
|
|
||||||
<file alias="phong.frag">resources/shaders/fragment/phong.frag</file>
|
|
||||||
<file alias="phong.vert">resources/shaders/vertex/phong.vert</file>
|
|
||||||
|
|
||||||
|
|
||||||
<!--Simple Solid Shader-->
|
|
||||||
<file alias="solid.frag">resources/shaders/fragment/solid.frag</file>
|
|
||||||
<file alias="solid.vert">resources/shaders/vertex/solid.vert</file>
|
|
||||||
|
|
||||||
<!--Solid Color Shader-->
|
|
||||||
<file alias="solid-perspective.frag">resources/shaders/fragment/solid-perspective.frag</file>
|
|
||||||
<file alias="solid-perspective.vert">resources/shaders/vertex/solid-perspective.vert</file>
|
|
||||||
|
|
||||||
<!--Multi-color Shader-->
|
|
||||||
<file alias="multi-color.frag">resources/shaders/fragment/multi-color.frag</file>
|
|
||||||
<file alias="multi-color.vert">resources/shaders/vertex/multi-color.vert</file>
|
|
||||||
|
|
||||||
<!--RGB Normals Shader-->
|
|
||||||
<file alias="rgb-normals.frag">resources/shaders/fragment/rgb-normals.frag</file>
|
|
||||||
<file alias="rgb-normals.vert">resources/shaders/vertex/rgb-normals.vert</file>
|
|
||||||
|
|
||||||
<!--CubeMap Texture Shader-->
|
|
||||||
<file alias="texture-cubemap.frag">resources/shaders/fragment/texture-cubemap.frag</file>
|
|
||||||
<file alias="texture-cubemap.vert">resources/shaders/vertex/texture-cubemap.vert</file>
|
|
||||||
|
|
||||||
<!--2D Texture Shader-->
|
|
||||||
<file alias="texture2d.frag">resources/shaders/fragment/texture2d.frag</file>
|
|
||||||
<file alias="texture2d.vert">resources/shaders/vertex/texture2d.vert</file>
|
|
||||||
|
|
||||||
|
|
||||||
<!--Ambient Shader-->
|
|
||||||
<file alias="solid-ambient.frag">resources/shaders/fragment/solid-ambient.frag</file>
|
|
||||||
<file alias="solid-ambient.vert">resources/shaders/vertex/solid-ambient.vert</file>
|
|
||||||
<!--Diffuse Shader-->
|
|
||||||
<file alias="solid-diffuse.frag">resources/shaders/fragment/solid-diffuse.frag</file>
|
|
||||||
<file alias="solid-diffuse.vert">resources/shaders/vertex/solid-diffuse.vert</file>
|
|
||||||
<!--Specular Shader-->
|
|
||||||
<file alias="solid-specular.frag">resources/shaders/fragment/solid-specular.frag</file>
|
|
||||||
<file alias="solid-specular.vert">resources/shaders/vertex/solid-specular.vert</file>
|
|
||||||
<!--Basic Phong Shader-->
|
|
||||||
<file alias="solid-phong.frag">resources/shaders/fragment/solid-phong.frag</file>
|
|
||||||
<file alias="solid-phong.vert">resources/shaders/vertex/solid-phong.vert</file>
|
|
||||||
|
|
||||||
|
|
||||||
<!--3DModel Basic Shader-->
|
|
||||||
<file alias="model-basic.frag">resources/shaders/fragment/model-basic.frag</file>
|
|
||||||
<file alias="model-basic.vert">resources/shaders/vertex/model-basic.vert</file>
|
|
||||||
|
|
||||||
<!--3DModel shader with specular mapping-->
|
|
||||||
<file alias="model-specular.frag">resources/shaders/fragment/model-specular.frag</file>
|
|
||||||
<file alias="model-specular.vert">resources/shaders/vertex/model-specular.vert</file>
|
|
||||||
|
|
||||||
<!--3DModel shader with normal mapping-->
|
|
||||||
<file alias="model-normals.frag">resources/shaders/fragment/model-normals.frag</file>
|
|
||||||
<file alias="model-normals.vert">resources/shaders/vertex/model-normals.vert</file>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Skybox Shaders-->
|
|
||||||
<file alias="skybox.frag">resources/skybox/skybox.frag</file>
|
|
||||||
<file alias="skybox.vert">resources/skybox/skybox.vert</file>
|
|
||||||
|
|
||||||
|
|
||||||
<!--Texture Images-->
|
|
||||||
<file alias="crate.png">resources/images/crate.png</file>
|
|
||||||
<file alias="stone.png">resources/images/stone.png</file>
|
|
||||||
<file alias="wood.png">resources/images/wood.png</file>
|
|
||||||
<!-- Skybox Images-->
|
|
||||||
<file alias="back.png">resources/skybox/back.png</file>
|
|
||||||
<file alias="bottom.png">resources/skybox/bottom.png</file>
|
|
||||||
<file alias="front.png">resources/skybox/front.png</file>
|
|
||||||
<file alias="left.png">resources/skybox/left.png</file>
|
|
||||||
<file alias="right.png">resources/skybox/right.png</file>
|
|
||||||
<file alias="top.png">resources/skybox/top.png</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
Fonticons, Inc. (https://fontawesome.com)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Font Awesome Free License
|
||||||
|
|
||||||
|
Font Awesome Free is free, open source, and GPL friendly. You can use it for
|
||||||
|
commercial projects, open source projects, or really almost whatever you want.
|
||||||
|
Full Font Awesome Free license: https://fontawesome.com/license/free.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
|
||||||
|
|
||||||
|
The Font Awesome Free download is licensed under a Creative Commons
|
||||||
|
Attribution 4.0 International License and applies to all icons packaged
|
||||||
|
as SVG and JS file types.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Fonts: SIL OFL 1.1 License
|
||||||
|
|
||||||
|
In the Font Awesome Free download, the SIL OFL license applies to all icons
|
||||||
|
packaged as web and desktop font files.
|
||||||
|
|
||||||
|
Copyright (c) 2022 Fonticons, Inc. (https://fontawesome.com)
|
||||||
|
with Reserved Font Name: "Font Awesome".
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
SIL OPEN FONT LICENSE
|
||||||
|
Version 1.1 - 26 February 2007
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting — in part or in whole — any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Code: MIT License (https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
In the Font Awesome Free download, the MIT license applies to all non-font and
|
||||||
|
non-icon files.
|
||||||
|
|
||||||
|
Copyright 2022 Fonticons, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
and to permit persons to whom the Software is furnished to do so, subject to the
|
||||||
|
following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Attribution
|
||||||
|
|
||||||
|
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
|
||||||
|
Awesome Free files already contain embedded comments with sufficient
|
||||||
|
attribution, so you shouldn't need to do anything additional when using these
|
||||||
|
files normally.
|
||||||
|
|
||||||
|
We've kept attribution comments terse, so we ask that you do not actively work
|
||||||
|
to remove them from files, especially code. They're a great way for folks to
|
||||||
|
learn about Font Awesome.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Brand Icons
|
||||||
|
|
||||||
|
All brand icons are trademarks of their respective owners. The use of these
|
||||||
|
trademarks does not indicate endorsement of the trademark holder by Font
|
||||||
|
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
||||||
|
to represent the company, product, or service to which they refer.**
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M439.55 236.05L244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
After Width: | Height: | Size: 749 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M224 256c-35.2 0-64 28.8-64 64c0 35.2 28.8 64 64 64c35.2 0 64-28.8 64-64C288 284.8 259.2 256 224 256zM433.1 129.1l-83.9-83.9C341.1 37.06 328.8 32 316.1 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h320c35.35 0 64-28.65 64-64V163.9C448 151.2 442.9 138.9 433.1 129.1zM128 80h144V160H128V80zM400 416c0 8.836-7.164 16-16 16H64c-8.836 0-16-7.164-16-16V96c0-8.838 7.164-16 16-16h16v104c0 13.25 10.75 24 24 24h192C309.3 208 320 197.3 320 184V83.88l78.25 78.25C399.4 163.2 400 164.8 400 166.3V416z"/></svg>
|
After Width: | Height: | Size: 789 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M572.6 270.3l-96 192C471.2 473.2 460.1 480 447.1 480H64c-35.35 0-64-28.66-64-64V96c0-35.34 28.65-64 64-64h117.5c16.97 0 33.25 6.742 45.26 18.75L275.9 96H416c35.35 0 64 28.66 64 64v32h-48V160c0-8.824-7.178-16-16-16H256L192.8 84.69C189.8 81.66 185.8 80 181.5 80H64C55.18 80 48 87.18 48 96v288l71.16-142.3C124.6 230.8 135.7 224 147.8 224h396.2C567.7 224 583.2 249 572.6 270.3z"/></svg>
|
After Width: | Height: | Size: 664 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M160 400C160 408.8 152.8 416 144 416C135.2 416 128 408.8 128 400V192C128 183.2 135.2 176 144 176C152.8 176 160 183.2 160 192V400zM240 400C240 408.8 232.8 416 224 416C215.2 416 208 408.8 208 400V192C208 183.2 215.2 176 224 176C232.8 176 240 183.2 240 192V400zM320 400C320 408.8 312.8 416 304 416C295.2 416 288 408.8 288 400V192C288 183.2 295.2 176 304 176C312.8 176 320 183.2 320 192V400zM317.5 24.94L354.2 80H424C437.3 80 448 90.75 448 104C448 117.3 437.3 128 424 128H416V432C416 476.2 380.2 512 336 512H112C67.82 512 32 476.2 32 432V128H24C10.75 128 0 117.3 0 104C0 90.75 10.75 80 24 80H93.82L130.5 24.94C140.9 9.357 158.4 0 177.1 0H270.9C289.6 0 307.1 9.358 317.5 24.94H317.5zM151.5 80H296.5L277.5 51.56C276 49.34 273.5 48 270.9 48H177.1C174.5 48 171.1 49.34 170.5 51.56L151.5 80zM80 432C80 449.7 94.33 464 112 464H336C353.7 464 368 449.7 368 432V128H80V432z"/></svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M234.5 5.7c13.9-5 29.1-5 43.1 0l192 68.6C495 83.4 512 107.5 512 134.6V377.4c0 27-17 51.2-42.5 60.3l-192 68.6c-13.9 5-29.1 5-43.1 0l-192-68.6C17 428.6 0 404.5 0 377.4V134.6c0-27 17-51.2 42.5-60.3l192-68.6zM256 66L82.3 128 256 190l173.7-62L256 66zm32 368.6l160-57.1v-188L288 246.6v188z"/></svg>
|
After Width: | Height: | Size: 574 B |
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install icnsutils
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
convert icon.png -resize 32x32 kilroy_32.png
|
||||||
|
convert icon.png -resize 16x16 kilroy_16.png
|
||||||
|
convert icon.png -resize 48x48 kilroy_48.png
|
||||||
|
convert icon.png -resize 128x128 kilroy_128.png
|
||||||
|
convert icon.png -resize 256x256 kilroy_256.png
|
||||||
|
png2icns png2icns kilroy.icns kilroy_*.png
|
||||||
|
```
|
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 512 B |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 869 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 3.8 MiB |
|
@ -1,5 +1,5 @@
|
||||||
# Created by RapidCompact v4.3.2 | www.rapidcompact.com
|
# Created by RapidCompact v4.3.2 | www.rapidcompact.com
|
||||||
mtllib rapid.mtl
|
mtllib bird.mtl
|
||||||
o mesh_0
|
o mesh_0
|
||||||
v 290.631131412626928068 748.484123578408002686 -224.342122210487644907
|
v 290.631131412626928068 748.484123578408002686 -224.342122210487644907
|
||||||
v 282.356783853739386814 751.945844816198814442 -222.136356657249308455
|
v 282.356783853739386814 751.945844816198814442 -222.136356657249308455
|
||||||
|
|
Before Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 254 KiB |
|
@ -1,16 +0,0 @@
|
||||||
# Blender MTL File: 'None'
|
|
||||||
# Material Count: 1
|
|
||||||
|
|
||||||
newmtl Lion
|
|
||||||
Ns 500.000001
|
|
||||||
Ka 1.000000 1.000000 1.000000
|
|
||||||
Kd 0.800000 0.800000 0.800000
|
|
||||||
Ks 0.800000 0.800000 0.800000
|
|
||||||
Ke 0.000000 0.000000 0.000000
|
|
||||||
Ni 1.450000
|
|
||||||
d 1.000000
|
|
||||||
illum 2
|
|
||||||
map_Bump normal.jpg
|
|
||||||
map_Kd diffuse.jpg
|
|
||||||
map_Ns roughness.jpg
|
|
||||||
refl specular.jpg
|
|
Before Width: | Height: | Size: 484 KiB |
Before Width: | Height: | Size: 562 KiB |
Before Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 263 KiB |
|
@ -0,0 +1,131 @@
|
||||||
|
<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>
|
Before Width: | Height: | Size: 406 KiB After Width: | Height: | Size: 316 KiB |
|
@ -0,0 +1,157 @@
|
||||||
|
################################################################################
|
||||||
|
## 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)
|
|
@ -0,0 +1,71 @@
|
||||||
|
################################################################################
|
||||||
|
## Project for working with OpenGL and Qt6 widgets ##
|
||||||
|
## ##
|
||||||
|
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
||||||
|
## All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Qtk Widget Library
|
||||||
|
################################################################################
|
||||||
|
# Create a library of widgets used to build Qtk GUI
|
||||||
|
set(
|
||||||
|
QTK_PLUGIN_LIBRARY_SOURCES
|
||||||
|
qtkwidget.cpp
|
||||||
|
debugconsole.cpp debugconsole.ui
|
||||||
|
toolbox.cpp toolbox.ui
|
||||||
|
treeview.cpp treeview.ui
|
||||||
|
qtkmainwindow.cpp qtkmainwindow.h qtkmainwindow.ui
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
QTK_PLUGIN_LIBRARY_HEADERS
|
||||||
|
qtkwidget.h
|
||||||
|
debugconsole.h
|
||||||
|
toolbox.h
|
||||||
|
treeview.h
|
||||||
|
)
|
||||||
|
qt_add_library(qtk_plugin_library STATIC EXCLUDE_FROM_ALL)
|
||||||
|
target_sources(
|
||||||
|
qtk_plugin_library PRIVATE
|
||||||
|
"${QTK_PLUGIN_LIBRARY_SOURCES}"
|
||||||
|
"${QTK_PLUGIN_LIBRARY_HEADERS}"
|
||||||
|
)
|
||||||
|
target_link_libraries(qtk_plugin_library PUBLIC Qt6::UiPlugin qtk_library)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Qtk Widget 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}
|
||||||
|
)
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Debug console for qtk views ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
|
#include "debugconsole.h"
|
||||||
|
#include "ui_debugconsole.h"
|
||||||
|
|
||||||
|
using namespace Qtk;
|
||||||
|
|
||||||
|
DebugConsole::DebugConsole(QWidget * owner, const QString & key) :
|
||||||
|
DebugConsole(owner, key, key + "Debugger") {}
|
||||||
|
|
||||||
|
DebugConsole::DebugConsole(
|
||||||
|
QWidget * owner, const QString & key, const QString & name) {
|
||||||
|
ui_ = new Ui::DebugConsole;
|
||||||
|
ui_->setupUi(this);
|
||||||
|
setObjectName(name);
|
||||||
|
mConsole = ui_->textEdit;
|
||||||
|
setWidget(mConsole);
|
||||||
|
setWindowTitle(name + " Debug Console");
|
||||||
|
|
||||||
|
auto qtkWidget = dynamic_cast<QtkWidget *>(owner);
|
||||||
|
if(qtkWidget) {
|
||||||
|
connect(qtkWidget, &QtkWidget::sendLog, this, &DebugConsole::sendLog);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Debug console for qtk views ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#ifndef QTK_DEBUGCONSOLE_H
|
||||||
|
#define QTK_DEBUGCONSOLE_H
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDockWidget>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "qtkwidget.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class DebugConsole;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Qtk {
|
||||||
|
class DebugConsole : public QDockWidget {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Construct a new DebugConsole.
|
||||||
|
* Assigns a default name to the console using `key + "Debugger"`
|
||||||
|
*
|
||||||
|
* @param owner Parent widget for this console or nullptr if no parent.
|
||||||
|
* If this parameter inherits from QMainWindow we will add this dock
|
||||||
|
* widget to the window.
|
||||||
|
* @param key The objectName associated with the attached QtkWidget.
|
||||||
|
*/
|
||||||
|
DebugConsole(QWidget * owner, const QString & key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new DebugConsole.
|
||||||
|
*
|
||||||
|
* @param owner Parent widget for this console or nullptr if no parent.
|
||||||
|
* If this parameter inherits from QMainWindow we will add this dock
|
||||||
|
* widget to the window.
|
||||||
|
* @param key The objectName associated with the attached QtkWidget.
|
||||||
|
* @param name The objectName to associate with this DebugConsole.
|
||||||
|
*/
|
||||||
|
DebugConsole(QWidget * owner, const QString & key, const QString & name);
|
||||||
|
|
||||||
|
~DebugConsole() = default;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/*************************************************************************
|
||||||
|
* Public Qt slots
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a message to the DebugConsole text view.
|
||||||
|
*
|
||||||
|
* @param message The message to log.
|
||||||
|
* @param context The DebugContext to use for the message.
|
||||||
|
* Default value is Status.
|
||||||
|
*/
|
||||||
|
inline void sendLog(QString message, DebugContext context = Status) {
|
||||||
|
mConsole->setTextColor(logColor(context));
|
||||||
|
mConsole->append(logPrefix(message, context));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the window title for the DebugConsole. This will appear in the
|
||||||
|
* widget title bar and within any context menu actions.
|
||||||
|
*
|
||||||
|
* @param name Base name for the DebugConsole window.
|
||||||
|
*/
|
||||||
|
inline void setTitle(QString name) {
|
||||||
|
setWindowTitle(name + " Debug Console");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @param context Log context severity level.
|
||||||
|
* @return QColor corresponding with the message context.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QColor logColor(const DebugContext & context) const {
|
||||||
|
switch(context) {
|
||||||
|
case Status:
|
||||||
|
return Qt::GlobalColor::darkGray;
|
||||||
|
case Debug:
|
||||||
|
return Qt::GlobalColor::white;
|
||||||
|
case Warn:
|
||||||
|
return Qt::GlobalColor::yellow;
|
||||||
|
case Error:
|
||||||
|
return Qt::GlobalColor::red;
|
||||||
|
case Fatal:
|
||||||
|
return Qt::GlobalColor::magenta;
|
||||||
|
default:
|
||||||
|
return Qt::GlobalColor::darkYellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefixes a log message to add context level.
|
||||||
|
*
|
||||||
|
* @param message The message to prefix.
|
||||||
|
* @param context The log context severity level.
|
||||||
|
* @return The log message prefixed with the DebugContext level.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QString logPrefix(
|
||||||
|
QString & message, const DebugContext & context) {
|
||||||
|
QString prefix;
|
||||||
|
switch(context) {
|
||||||
|
case Status:
|
||||||
|
prefix = "[Status]: ";
|
||||||
|
break;
|
||||||
|
case Debug:
|
||||||
|
prefix = "[Debug]: ";
|
||||||
|
break;
|
||||||
|
case Warn:
|
||||||
|
prefix = "[Warn]: ";
|
||||||
|
break;
|
||||||
|
case Error:
|
||||||
|
prefix = "[Error]: ";
|
||||||
|
break;
|
||||||
|
case Fatal:
|
||||||
|
prefix = "[Fatal]: ";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
prefix = "[No Context]: ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
message = prefix + message.replace("\n", "\t\n" + prefix);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ui::DebugConsole * ui_;
|
||||||
|
QTextEdit * mConsole;
|
||||||
|
};
|
||||||
|
} // namespace Qtk
|
||||||
|
|
||||||
|
#endif // QTK_DEBUGCONSOLE_H
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>DebugConsole</class>
|
||||||
|
<widget class="QDockWidget" name="DebugConsole">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Debug Console</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="dockWidgetContents">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTextEdit" name="textEdit">
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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();
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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());
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: MainWindow for Qtk application ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
|
#include "debugconsole.h"
|
||||||
|
#include "qtkwidget.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MainWindow class to provide an example of using a QtkWidget within a Qt
|
||||||
|
* window application.
|
||||||
|
*/
|
||||||
|
class MainWindow : public QMainWindow {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
/***************************************************************************
|
||||||
|
* Contructors / Destructors
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This ctor also initializes the Scene for each QtkWidget in the window.
|
||||||
|
* To load a different scene this would need to be updated.
|
||||||
|
*
|
||||||
|
* @param parent The parent for this QMainWindow
|
||||||
|
*/
|
||||||
|
explicit MainWindow(QWidget * parent = nullptr);
|
||||||
|
|
||||||
|
~MainWindow() override;
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Public Methods
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows widgets to retrieve an instance of this root QMainWindow.
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
static MainWindow * getMainWindow();
|
||||||
|
|
||||||
|
Qtk::QtkWidget * getQtkWidget(int64_t index = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessor for retrieving a QtkWidget by it's objectName.
|
||||||
|
* This function will not construct a new QtkWidget if none is found.
|
||||||
|
*
|
||||||
|
* @param name The objectName associated with the QtkWidget.
|
||||||
|
* @return Pointer to an active QtkWidget or Q_NULLPTR is not found.
|
||||||
|
*/
|
||||||
|
Qtk::QtkWidget * getQtkWidget(const QString & name);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/**
|
||||||
|
* Trigger a refresh for widgets related to a scene that has been updated.
|
||||||
|
* @param sceneName The name of the scene that has been modified.
|
||||||
|
*/
|
||||||
|
void refreshScene(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
|
|
@ -0,0 +1,348 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,543 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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);
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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
|
|
@ -0,0 +1,357 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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);
|
||||||
|
}
|
|
@ -0,0 +1,214 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef QTK_RESOURCES_H_IN_H
|
||||||
|
#define QTK_RESOURCES_H_IN_H
|
||||||
|
|
||||||
|
// Not currently in use, but will be in the future.
|
||||||
|
|
||||||
|
#endif // QTK_RESOURCES_H_IN_H
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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);
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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);
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: TreeView plugin for scene hierarchy ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TREEVIEW_H
|
||||||
|
#define TREEVIEW_H
|
||||||
|
|
||||||
|
#include <QDesignerCustomWidgetInterface>
|
||||||
|
#include <QDesignerExportWidget>
|
||||||
|
#include <QDockWidget>
|
||||||
|
|
||||||
|
#include <qtk/scene.h>
|
||||||
|
#include <QTreeWidgetItem>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class TreeView;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Qtk {
|
||||||
|
class TreeView : public QDockWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*************************************************************************
|
||||||
|
* Constructors / Destructors
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
explicit TreeView(QWidget * parent = nullptr);
|
||||||
|
|
||||||
|
~TreeView();
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Public Methods
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the QTreeWidget with all objects within the scene.
|
||||||
|
* @param scene The scene to load objects from.
|
||||||
|
*/
|
||||||
|
void updateView(const Scene * scene);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/**
|
||||||
|
* Focus the camera on an item when it is double clicked.
|
||||||
|
* Triggered by QTreeWidget::itemDoubleClicked signal.
|
||||||
|
*
|
||||||
|
* @param item The item that was double clicked
|
||||||
|
* @param column The column of the item that was double clicked.
|
||||||
|
* This param is currently not used but required for this signal.
|
||||||
|
*/
|
||||||
|
void itemFocus(QTreeWidgetItem * item, int column);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*************************************************************************
|
||||||
|
* Private Members
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
Ui::TreeView * ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the scene last loaded by this TreeWidget.
|
||||||
|
* Used to load object data from a target scene.
|
||||||
|
*/
|
||||||
|
QString mSceneName;
|
||||||
|
};
|
||||||
|
} // namespace Qtk
|
||||||
|
|
||||||
|
#endif // TREEVIEW_H
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>TreeView</class>
|
||||||
|
<widget class="QDockWidget" name="TreeView">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Scene Tree View</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="dockWidgetContents">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeWidget" name="treeWidget">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="indentation">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="sortingEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="headerVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">1</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Generic Qt Designer widget plugin ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QtPlugin>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <qtk/qtkapi.h>
|
||||||
|
|
||||||
|
#include "widgetplugin.h"
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Constructors, Destructors
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
WidgetPlugin::WidgetPlugin(
|
||||||
|
QString group, QString class_name, QString include,
|
||||||
|
WidgetPlugin::Factory factory) :
|
||||||
|
m_group(std::move(group)),
|
||||||
|
m_className(std::move(class_name)), m_includeFile(std::move(include)),
|
||||||
|
m_factory(std::move(factory)), m_objectName(class_name) {}
|
||||||
|
|
||||||
|
WidgetPlugin::WidgetPlugin(QObject * parent) : QObject(parent) {}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Public Methods
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
QString WidgetPlugin::group() const {
|
||||||
|
return m_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WidgetPlugin::name() const {
|
||||||
|
return m_className;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WidgetPlugin::includeFile() const {
|
||||||
|
return m_includeFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget * WidgetPlugin::createWidget(QWidget * parent) {
|
||||||
|
return m_factory(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WidgetPlugin::toolTip() const {
|
||||||
|
return QStringLiteral("A custom widget tool tip.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WidgetPlugin::whatsThis() const {
|
||||||
|
return QStringLiteral("Custom widget what's this?");
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon WidgetPlugin::icon() const {
|
||||||
|
return Qtk::getIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidgetPlugin::isContainer() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidgetPlugin::isInitialized() const {
|
||||||
|
return m_initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WidgetPlugin::initialize(QDesignerFormEditorInterface *) {
|
||||||
|
if(m_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
}
|
||||||
|
QString WidgetPlugin::domXml() const {
|
||||||
|
return
|
||||||
|
"<ui language=\"c++\">\n"
|
||||||
|
" <widget class=\"" + m_className + "\" name=\"" + m_objectName + "\">\n"
|
||||||
|
" <property name=\"geometry\">\n"
|
||||||
|
" <rect>\n"
|
||||||
|
" <x>0</x>\n"
|
||||||
|
" <y>0</y>\n"
|
||||||
|
" <width>100</width>\n"
|
||||||
|
" <height>100</height>\n"
|
||||||
|
" </rect>\n"
|
||||||
|
" </property>\n"
|
||||||
|
" <property name=\"toolTip\" >\n"
|
||||||
|
" <string>" + toolTip() + "</string>\n"
|
||||||
|
" </property>\n"
|
||||||
|
" <property name=\"whatsThis\" >\n"
|
||||||
|
" <string>" + whatsThis() + "</string>\n"
|
||||||
|
" </property>\n"
|
||||||
|
" </widget>\n"
|
||||||
|
"</ui>\n";
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Generic Qt Designer widget plugin ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QTK_WIDGETPLUGIN_H
|
||||||
|
#define QTK_WIDGETPLUGIN_H
|
||||||
|
|
||||||
|
#include <QDesignerCustomWidgetInterface>
|
||||||
|
#include <QDesignerExportWidget>
|
||||||
|
|
||||||
|
class QDESIGNER_WIDGET_EXPORT WidgetPlugin :
|
||||||
|
public QObject,
|
||||||
|
public QDesignerCustomWidgetInterface {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_INTERFACES(QDesignerCustomWidgetInterface)
|
||||||
|
|
||||||
|
using Factory = std::function<QWidget *(QWidget *)>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/***************************************************************************
|
||||||
|
* Contructors / Destructors
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
WidgetPlugin(
|
||||||
|
QString group, QString class_name, QString include, Factory factory);
|
||||||
|
|
||||||
|
explicit WidgetPlugin(QObject * parent = nullptr);
|
||||||
|
|
||||||
|
~WidgetPlugin() = default;
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Public Methods
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The name of the group to which this widget belongs.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QString group() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must return the _class name_ of the widget.
|
||||||
|
*
|
||||||
|
* @return The class name for the associated widget.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QString name() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this path changes for a custom widget, it must be removed and added
|
||||||
|
* back in Qt Designer for the XML surrounding this value to be regenerated.
|
||||||
|
*
|
||||||
|
* See the `<customwidget>` XML in any `.ui` file using a custom widget.
|
||||||
|
*
|
||||||
|
* @return Path to the include file for UIC to use when generating code.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QString includeFile() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param parent Parent widget to the new instance of this widget.
|
||||||
|
* @return A new instance of this custom widget.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QWidget * createWidget(QWidget * parent) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Short description used in Qt Designer tool tips.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QString toolTip() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Widget description used in `What's this?` within Qt Creator.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QString whatsThis() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Icon used to represent the widget in Qt Designer's GUI.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QIcon icon() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not this widget should act as a container for other widgets.
|
||||||
|
*
|
||||||
|
* @return True if this custom widget is meant to be a container.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool isContainer() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if this widget has been initialized.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool isInitialized() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an instance of this custom widget.
|
||||||
|
* @param core
|
||||||
|
*/
|
||||||
|
void initialize(QDesignerFormEditorInterface * core) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default XML for an instance of this custom widget within a `.ui` file.
|
||||||
|
*
|
||||||
|
* Any property available for the widget in Qt Designer can be set using XML
|
||||||
|
* properties, as seen here with `toolTip` and `whatsThis`.
|
||||||
|
*
|
||||||
|
* @return XML inserted for each instance of this widget.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QString domXml() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/***************************************************************************
|
||||||
|
* Private Members
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
bool m_initialized = false;
|
||||||
|
QString m_group;
|
||||||
|
QString m_className;
|
||||||
|
QString m_objectName;
|
||||||
|
QString m_includeFile;
|
||||||
|
Factory m_factory;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTK_WIDGETPLUGIN_H
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Collection of widget plugins for Qt Designer ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "widgetplugincollection.h"
|
||||||
|
#include "debugconsole.h"
|
||||||
|
#include "qtkwidget.h"
|
||||||
|
#include "toolbox.h"
|
||||||
|
#include "treeview.h"
|
||||||
|
#include "widgetplugin.h"
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Constructors, Destructors
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
WidgetPluginCollection::WidgetPluginCollection(QObject * parent) :
|
||||||
|
QObject(parent), m_collectionName("Qtk Widget Collection") {
|
||||||
|
m_collection = {
|
||||||
|
new WidgetPlugin(
|
||||||
|
m_collectionName, "Qtk::QtkWidget", "qtkwidget.h",
|
||||||
|
[](QWidget * parent) { return new Qtk::QtkWidget(parent); }),
|
||||||
|
new WidgetPlugin(
|
||||||
|
m_collectionName, "Qtk::TreeView", "treeview.h",
|
||||||
|
[](QWidget * parent) { return new Qtk::TreeView(parent); }),
|
||||||
|
new WidgetPlugin(
|
||||||
|
m_collectionName, "Qtk::ToolBox", "toolbox.h",
|
||||||
|
[](QWidget * parent) { return new Qtk::ToolBox(parent); }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Public Methods
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
QList<QDesignerCustomWidgetInterface *> WidgetPluginCollection::customWidgets()
|
||||||
|
const {
|
||||||
|
return m_collection;
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Collection of widget plugins for Qt Designer ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QTK_WIDGETPLUGINCOLLECTION_H
|
||||||
|
#define QTK_WIDGETPLUGINCOLLECTION_H
|
||||||
|
|
||||||
|
#include <QDesignerCustomWidgetCollectionInterface>
|
||||||
|
|
||||||
|
class WidgetPluginCollection :
|
||||||
|
public QObject,
|
||||||
|
public QDesignerCustomWidgetCollectionInterface {
|
||||||
|
Q_OBJECT
|
||||||
|
// Since we're exporting a collection, this is the only plugin metadata
|
||||||
|
// needed. We don't need this for-each widget in the collection.
|
||||||
|
Q_PLUGIN_METADATA(IID "com.Klips.WidgetPluginCollection")
|
||||||
|
// Tell Qt Object system that we're implementing an interface.
|
||||||
|
Q_INTERFACES(QDesignerCustomWidgetCollectionInterface)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/***************************************************************************
|
||||||
|
* Contructors / Destructors
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
explicit WidgetPluginCollection(QObject * parent = nullptr);
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Public Methods
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return QList of all custom widgets pointers.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QList<QDesignerCustomWidgetInterface *> customWidgets() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/***************************************************************************
|
||||||
|
* Private Members
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
QList<QDesignerCustomWidgetInterface *> m_collection;
|
||||||
|
QString m_collectionName;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTK_WIDGETPLUGINCOLLECTION_H
|
|
@ -1,62 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## 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
|
|
63
src/input.h
|
@ -1,63 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## Author: Shaun Reed ##
|
|
||||||
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
|
||||||
## About: Input class from tutorials followed at trentreed.net ##
|
|
||||||
## ##
|
|
||||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
|
||||||
##############################################################################*/
|
|
||||||
|
|
||||||
#ifndef QTOPENGL_INPUT_H
|
|
||||||
#define QTOPENGL_INPUT_H
|
|
||||||
|
|
||||||
#include <QPoint>
|
|
||||||
#include <Qt>
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,369 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor for using this widget in QtDesigner
|
|
||||||
MainWidget::MainWidget(QWidget *parent) : QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR)
|
|
||||||
{
|
|
||||||
initializeWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
setFocusPolicy(Qt::ClickFocus);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## 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
|
|
|
@ -1,15 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
#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
|
|
|
@ -1,104 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>MainWindow</class>
|
|
||||||
<widget class="QMainWindow" name="MainWindow">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>800</width>
|
|
||||||
<height>600</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Qtk - MainWindow</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="centralwidget">
|
|
||||||
<widget class="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>
|
|
132
src/mesh.h
|
@ -1,132 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## 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
|
|
|
@ -1,172 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## Author: Shaun Reed ##
|
|
||||||
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
|
||||||
## About: MeshRenderer class for quick object creation and drawing ##
|
|
||||||
## ##
|
|
||||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
|
||||||
##############################################################################*/
|
|
||||||
|
|
||||||
#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();
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## Author: Shaun Reed ##
|
|
||||||
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
|
||||||
## About: MeshRenderer class for quick object creation and drawing ##
|
|
||||||
## ##
|
|
||||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
|
||||||
##############################################################################*/
|
|
||||||
#ifndef QTK_MESHRENDERER_H
|
|
||||||
#define QTK_MESHRENDERER_H
|
|
||||||
|
|
||||||
#include <mesh.h>
|
|
||||||
#include <object.h>
|
|
||||||
|
|
||||||
|
|
||||||
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
|
@ -1,401 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## Author: Shaun Reed ##
|
|
||||||
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
|
||||||
## About: Model classes for importing with Assimp ##
|
|
||||||
## From following tutorials on learnopengl.com ##
|
|
||||||
## ##
|
|
||||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
|
||||||
##############################################################################*/
|
|
||||||
|
|
||||||
#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
|
@ -1,139 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## Author: Shaun Reed ##
|
|
||||||
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
|
||||||
## About: Model classes for importing with Assimp ##
|
|
||||||
## From following tutorials on learnopengl.com ##
|
|
||||||
## ##
|
|
||||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
|
||||||
##############################################################################*/
|
|
||||||
#ifndef QTK_MODEL_H
|
|
||||||
#define QTK_MODEL_H
|
|
||||||
|
|
||||||
// QT
|
|
||||||
#include <QObject>
|
|
||||||
#include <QOpenGLBuffer>
|
|
||||||
#include <QOpenGLFunctions>
|
|
||||||
#include <QOpenGLShaderProgram>
|
|
||||||
#include <QOpenGLTexture>
|
|
||||||
#include <QOpenGLVertexArrayObject>
|
|
||||||
|
|
||||||
// Assimp
|
|
||||||
#include <assimp/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
|
|
61
src/object.h
|
@ -1,61 +0,0 @@
|
||||||
/*##############################################################################
|
|
||||||
## 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
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
################################################################################
|
||||||
|
## Project for working with OpenGL and Qt6 widgets ##
|
||||||
|
## ##
|
||||||
|
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
||||||
|
## All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Qtk Library
|
||||||
|
################################################################################
|
||||||
|
set(
|
||||||
|
QTK_LIBRARY_PUBLIC_HEADERS
|
||||||
|
camera3d.h
|
||||||
|
input.h
|
||||||
|
meshrenderer.h
|
||||||
|
model.h
|
||||||
|
modelmesh.h
|
||||||
|
object.h
|
||||||
|
qtkapi.h
|
||||||
|
qtkiostream.h
|
||||||
|
qtkiosystem.h
|
||||||
|
scene.h
|
||||||
|
shape.h
|
||||||
|
skybox.h
|
||||||
|
texture.h
|
||||||
|
transform3D.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
QTK_LIBRARY_SOURCES
|
||||||
|
camera3d.cpp
|
||||||
|
input.cpp
|
||||||
|
meshrenderer.cpp
|
||||||
|
model.cpp
|
||||||
|
modelmesh.cpp
|
||||||
|
object.cpp
|
||||||
|
qtkiostream.cpp
|
||||||
|
qtkiosystem.cpp
|
||||||
|
scene.cpp
|
||||||
|
shape.cpp
|
||||||
|
skybox.cpp
|
||||||
|
texture.cpp
|
||||||
|
transform3D.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
qt6_add_big_resources(QTK_LIBRARY_SOURCES "${QTK_RESOURCES}/resources.qrc")
|
||||||
|
qt_add_library(qtk_library STATIC EXCLUDE_FROM_ALL)
|
||||||
|
target_sources(qtk_library PRIVATE ${QTK_LIBRARY_SOURCES})
|
||||||
|
target_sources(
|
||||||
|
qtk_library PUBLIC
|
||||||
|
FILE_SET HEADERS
|
||||||
|
BASE_DIRS $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
|
||||||
|
BASE_DIRS $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
|
||||||
|
FILES ${QTK_LIBRARY_PUBLIC_HEADERS}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(QTK_DEBUG)
|
||||||
|
target_compile_definitions(qtk_library PUBLIC QTK_DEBUG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(
|
||||||
|
qtk_library PROPERTIES
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
qtk_library PUBLIC
|
||||||
|
Qt6::Core Qt6::OpenGLWidgets Qt6::Widgets
|
||||||
|
)
|
||||||
|
|
||||||
|
if(QTK_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()
|
|
@ -1,59 +1,56 @@
|
||||||
/*##############################################################################
|
/*##############################################################################
|
||||||
## Author: Shaun Reed ##
|
## Author: Shaun Reed ##
|
||||||
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
## About: Fly camera class from tutorials followed at trentreed.net ##
|
## About: Fly camera class from tutorials followed at trentreed.net ##
|
||||||
## ##
|
## ##
|
||||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
## 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::LocalForward(0.0f, 0.0f, -1.0f);
|
||||||
const QVector3D Camera3D::LocalUp(0.0f, 1.0f, 0.0f);
|
const QVector3D Camera3D::LocalUp(0.0f, 1.0f, 0.0f);
|
||||||
const QVector3D Camera3D::LocalRight(1.0f, 0.0f, 0.0f);
|
const QVector3D Camera3D::LocalRight(1.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Accessors
|
* Public Methods
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
// Produces worldToView matrix
|
const QMatrix4x4 & Camera3D::toMatrix() {
|
||||||
const QMatrix4x4 & Camera3D::toMatrix()
|
|
||||||
{
|
|
||||||
mWorld.setToIdentity();
|
mWorld.setToIdentity();
|
||||||
// Qt6 renamed QMatrix4x4::conjugate() to conjugated()
|
// Qt6 renamed QMatrix4x4::conjugate() to conjugated()
|
||||||
mWorld.rotate(mTransform.rotation().conjugated());
|
mWorld.rotate(mTransform.getRotation().conjugated());
|
||||||
mWorld.translate(-mTransform.translation());
|
mWorld.translate(-mTransform.getTranslation());
|
||||||
return mWorld;
|
return mWorld;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Qt Streams
|
* Qt Streams
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
QDataStream & operator<<(QDataStream & out, Camera3D & transform)
|
QDataStream & operator<<(QDataStream & out, Camera3D & transform) {
|
||||||
{
|
out << transform.getTransform();
|
||||||
out << transform.transform();
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDataStream & operator>>(QDataStream & in, Camera3D & transform)
|
QDataStream & operator>>(QDataStream & in, Camera3D & transform) {
|
||||||
{
|
in >> transform.getTransform();
|
||||||
in >> transform.transform();
|
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDebug operator<<(QDebug dbg, const Camera3D & transform)
|
QDebug operator<<(QDebug dbg, const Camera3D & transform) {
|
||||||
{
|
|
||||||
dbg << "Camera3D\n{\n";
|
dbg << "Camera3D\n{\n";
|
||||||
dbg << "Position: <" << transform.translation().x() << ", "
|
dbg << "Position: <" << transform.getTranslation().x() << ", "
|
||||||
<< transform.translation().y() << ", "
|
<< transform.getTranslation().y() << ", "
|
||||||
<< transform.translation().z() << ">\n";
|
<< transform.getTranslation().z() << ">\n";
|
||||||
dbg << "Rotation: <" << transform.rotation().x() << ", "
|
dbg << "Rotation: <" << transform.getRotation().x() << ", "
|
||||||
<< transform.rotation().y() << ", "
|
<< transform.getRotation().y() << ", " << transform.getRotation().z()
|
||||||
<< transform.rotation().z() << " | "
|
<< " | " << transform.getRotation().scalar() << ">\n}";
|
||||||
<< transform.rotation().scalar() << ">\n}";
|
|
||||||
return dbg;
|
return dbg;
|
||||||
}
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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
|
|
@ -1,6 +1,6 @@
|
||||||
/*##############################################################################
|
/*##############################################################################
|
||||||
## Author: Shaun Reed ##
|
## Author: Shaun Reed ##
|
||||||
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
## About: Input class from tutorials followed at trentreed.net ##
|
## About: Input class from tutorials followed at trentreed.net ##
|
||||||
## ##
|
## ##
|
||||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
@ -11,25 +11,34 @@
|
||||||
|
|
||||||
#include <QCursor>
|
#include <QCursor>
|
||||||
|
|
||||||
#include <input.h>
|
#include "input.h"
|
||||||
|
|
||||||
|
using namespace Qtk;
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Static Helper Structs
|
* Static Helper Structs
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
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) {}
|
* 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, Input::InputState state)
|
// Disable clang-tidy from marking this ctor explicit
|
||||||
: base_class(value, state) {}
|
// NOLINTNEXTLINE
|
||||||
|
inline InputInstance(T value) : base_class(value, Input::InputInvalid) {}
|
||||||
|
|
||||||
inline bool operator==(const InputInstance & rhs) const
|
inline InputInstance(T value, Input::InputState state) :
|
||||||
{ return this->first == rhs.first;}
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Key, button instance typedefs
|
// Key, button instance typedefs
|
||||||
|
@ -48,26 +57,50 @@ static QPoint sg_mouseCurrPosition;
|
||||||
static QPoint sg_mousePrevPosition;
|
static QPoint sg_mousePrevPosition;
|
||||||
static QPoint sg_mouseDelta;
|
static QPoint sg_mouseDelta;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Static Inline Helper Functions
|
* Static Inline Helper Functions
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
static inline KeyContainer::iterator FindKey(Qt::Key value)
|
/**
|
||||||
{
|
* Search for the InputInstance of a key.
|
||||||
|
*
|
||||||
|
* @param value The key to search for.
|
||||||
|
* @return Iterator to the found element or the end iterator if not found.
|
||||||
|
*/
|
||||||
|
static inline KeyContainer::iterator FindKey(Qt::Key value) {
|
||||||
return std::find(sg_keyInstances.begin(), sg_keyInstances.end(), value);
|
return std::find(sg_keyInstances.begin(), sg_keyInstances.end(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline ButtonContainer::iterator FindButton(Qt::MouseButton value)
|
/**
|
||||||
{
|
* Search for the InputInstance of a mouse button.
|
||||||
|
*
|
||||||
|
* @param value The mouse button to search for.
|
||||||
|
* @return Iterator to the found element or the end iterator if not found.
|
||||||
|
*/
|
||||||
|
static inline ButtonContainer::iterator FindButton(Qt::MouseButton value) {
|
||||||
return std::find(sg_buttonInstances.begin(), sg_buttonInstances.end(), value);
|
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>
|
template <typename TPair>
|
||||||
static inline void UpdateStates(TPair & instance)
|
static inline bool CheckReleased(const TPair & instance) {
|
||||||
{
|
return instance.second == Input::InputReleased;
|
||||||
switch (instance.second)
|
}
|
||||||
{
|
|
||||||
|
/**
|
||||||
|
* Updates an InputInstance and applies transitions if needed.
|
||||||
|
*
|
||||||
|
* @tparam TPair KeyInstance or ButtonInstance.
|
||||||
|
* @param instance The InputInstance to update.
|
||||||
|
*/
|
||||||
|
template <typename TPair> static inline void UpdateStates(TPair & instance) {
|
||||||
|
switch(instance.second) {
|
||||||
case Input::InputRegistered:
|
case Input::InputRegistered:
|
||||||
instance.second = Input::InputTriggered;
|
instance.second = Input::InputTriggered;
|
||||||
break;
|
break;
|
||||||
|
@ -82,20 +115,18 @@ static inline void UpdateStates(TPair & instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TPair>
|
/**
|
||||||
static inline bool CheckReleased(const TPair & instance)
|
* Updates InputInstance containers to track input state.
|
||||||
{
|
*
|
||||||
return instance.second == Input::InputReleased;
|
* @tparam Container The type of container, KeyContainer or ButtonContainer.
|
||||||
}
|
* @param container The InputInstance container to update.
|
||||||
|
*/
|
||||||
template <typename Container>
|
template <typename Container> static inline void Update(Container & container) {
|
||||||
static inline void Update(Container & container)
|
|
||||||
{
|
|
||||||
typedef typename Container::iterator Iter;
|
typedef typename Container::iterator Iter;
|
||||||
typedef typename Container::value_type TPair;
|
typedef typename Container::value_type TPair;
|
||||||
|
|
||||||
// Remove old data
|
// Remove old data
|
||||||
Iter remove =
|
auto remove =
|
||||||
std::remove_if(container.begin(), container.end(), &CheckReleased<TPair>);
|
std::remove_if(container.begin(), container.end(), &CheckReleased<TPair>);
|
||||||
container.erase(remove, container.end());
|
container.erase(remove, container.end());
|
||||||
|
|
||||||
|
@ -103,35 +134,11 @@ static inline void Update(Container & container)
|
||||||
std::for_each(container.begin(), container.end(), &UpdateStates<TPair>);
|
std::for_each(container.begin(), container.end(), &UpdateStates<TPair>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Input Implementation
|
* Static Public Methods
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
Input::InputState Input::keyState(Qt::Key k)
|
void Input::update() {
|
||||||
{
|
|
||||||
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
|
// Update Mouse Delta
|
||||||
sg_mousePrevPosition = sg_mouseCurrPosition;
|
sg_mousePrevPosition = sg_mouseCurrPosition;
|
||||||
sg_mouseCurrPosition = QCursor::pos();
|
sg_mouseCurrPosition = QCursor::pos();
|
||||||
|
@ -142,44 +149,53 @@ void Input::update()
|
||||||
Update(sg_keyInstances);
|
Update(sg_keyInstances);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::registerKeyPress(int k)
|
void Input::registerKeyPress(int k) {
|
||||||
{
|
auto it = FindKey((Qt::Key)k);
|
||||||
KeyContainer::iterator it = FindKey((Qt::Key)k);
|
if(it == sg_keyInstances.end()) {
|
||||||
if (it == sg_keyInstances.end())
|
|
||||||
{
|
|
||||||
sg_keyInstances.push_back(KeyInstance((Qt::Key)k, InputRegistered));
|
sg_keyInstances.push_back(KeyInstance((Qt::Key)k, InputRegistered));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::registerKeyRelease(int k)
|
void Input::registerKeyRelease(int k) {
|
||||||
{
|
auto it = FindKey((Qt::Key)k);
|
||||||
KeyContainer::iterator it = FindKey((Qt::Key)k);
|
if(it != sg_keyInstances.end()) {
|
||||||
if (it != sg_keyInstances.end())
|
|
||||||
{
|
|
||||||
it->second = InputUnregistered;
|
it->second = InputUnregistered;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::registerMousePress(Qt::MouseButton btn)
|
void Input::registerMousePress(Qt::MouseButton btn) {
|
||||||
{
|
auto it = FindButton(btn);
|
||||||
ButtonContainer::iterator it = FindButton(btn);
|
if(it == sg_buttonInstances.end()) {
|
||||||
if (it == sg_buttonInstances.end())
|
|
||||||
{
|
|
||||||
sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered));
|
sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::registerMouseRelease(Qt::MouseButton btn)
|
void Input::registerMouseRelease(Qt::MouseButton btn) {
|
||||||
{
|
auto it = FindButton(btn);
|
||||||
ButtonContainer::iterator it = FindButton(btn);
|
if(it != sg_buttonInstances.end()) {
|
||||||
if (it != sg_buttonInstances.end())
|
|
||||||
{
|
|
||||||
it->second = InputUnregistered;
|
it->second = InputUnregistered;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::reset()
|
void Input::reset() {
|
||||||
{
|
|
||||||
sg_keyInstances.clear();
|
sg_keyInstances.clear();
|
||||||
sg_buttonInstances.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;
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Input class from tutorials followed at trentreed.net ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#ifndef QTOPENGL_INPUT_H
|
||||||
|
#define QTOPENGL_INPUT_H
|
||||||
|
|
||||||
|
#include <QPoint>
|
||||||
|
#include <Qt>
|
||||||
|
|
||||||
|
#include "qtkapi.h"
|
||||||
|
|
||||||
|
namespace Qtk {
|
||||||
|
class QTKAPI Input {
|
||||||
|
public:
|
||||||
|
/*************************************************************************
|
||||||
|
* Typedefs
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible key states. See UpdateStates for state transitions.
|
||||||
|
*
|
||||||
|
* When a key is pressed we enter states Registered->Triggered->Pressed.
|
||||||
|
* InputTriggered state will be met only once if a key is pressed or held.
|
||||||
|
* While a key is held down the state is InputPressed.
|
||||||
|
*
|
||||||
|
* When a key is released we enter InputUnregistered->InputReleased
|
||||||
|
* When an active InputInstance for a key has this state it is removed.
|
||||||
|
*/
|
||||||
|
enum InputState {
|
||||||
|
InputInvalid,
|
||||||
|
InputRegistered, // Initial state. Transitions to InputTriggered
|
||||||
|
InputUnregistered, // Transition to InputReleased
|
||||||
|
InputTriggered, // Transition to InputPressed
|
||||||
|
InputPressed, // State of a key while it is held down.
|
||||||
|
InputReleased // Released keys are removed from state containers.
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Public Methods
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
//
|
||||||
|
// State updating.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update state for all mouse button and key instances.
|
||||||
|
*/
|
||||||
|
static void update();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to set InputRegistered state.
|
||||||
|
*/
|
||||||
|
static void registerKeyPress(int key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to set InputReleased state.
|
||||||
|
*/
|
||||||
|
static void registerKeyRelease(int key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param button Mouse button to set InputRegistered state.
|
||||||
|
*/
|
||||||
|
static void registerMousePress(Qt::MouseButton button);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param button Mouse button to set InputReleased state.
|
||||||
|
*/
|
||||||
|
static void registerMouseRelease(Qt::MouseButton button);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset input state for all key and mouse buttons.
|
||||||
|
*/
|
||||||
|
static void reset();
|
||||||
|
|
||||||
|
//
|
||||||
|
// State Checking.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to check state.
|
||||||
|
* @return True if the key is in InputTriggered state.
|
||||||
|
*/
|
||||||
|
inline static bool keyTriggered(Qt::Key key) {
|
||||||
|
return keyState(key) == InputTriggered;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to check state.
|
||||||
|
* @return True if the key is in InputPressed state.
|
||||||
|
*/
|
||||||
|
inline static bool keyPressed(Qt::Key key) {
|
||||||
|
return keyState(key) == InputPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to check state.
|
||||||
|
* @return True if the key is in InputReleased state.
|
||||||
|
*/
|
||||||
|
inline static bool keyReleased(Qt::Key key) {
|
||||||
|
return keyState(key) == InputReleased;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param button Mouse button to check state.
|
||||||
|
* @return True if the key is in InputTriggered state.
|
||||||
|
*/
|
||||||
|
inline static bool buttonTriggered(Qt::MouseButton button) {
|
||||||
|
return buttonState(button) == InputTriggered;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param button Mouse button to check state.
|
||||||
|
* @return True if the key is in InputPressed state.
|
||||||
|
*/
|
||||||
|
inline static bool buttonPressed(Qt::MouseButton button) {
|
||||||
|
return buttonState(button) == InputPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param button Mouse button to check state.
|
||||||
|
* @return True if the key is in InputReleased state.
|
||||||
|
*/
|
||||||
|
inline static bool buttonReleased(Qt::MouseButton button) {
|
||||||
|
return buttonState(button) == InputReleased;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key The key to check InputState.
|
||||||
|
* @return The current InputState for the given key.
|
||||||
|
*/
|
||||||
|
static InputState keyState(Qt::Key key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param button The mouse button to check InputState.
|
||||||
|
* @return The current InputState for the mouse button.
|
||||||
|
*/
|
||||||
|
static InputState buttonState(Qt::MouseButton button);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return QPoint representing the mouse position within the widget.
|
||||||
|
*/
|
||||||
|
static QPoint mousePosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Delta movement of mouse from previous to current position.
|
||||||
|
*/
|
||||||
|
static QPoint mouseDelta();
|
||||||
|
};
|
||||||
|
} // namespace Qtk
|
||||||
|
|
||||||
|
#endif // QTOPENGL_INPUT_H
|
|
@ -0,0 +1,222 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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];
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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
|
|
@ -0,0 +1,259 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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);
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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();
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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
|
|
@ -1,9 +1,11 @@
|
||||||
/*##############################################################################
|
/*##############################################################################
|
||||||
## Author: Shaun Reed ##
|
## Author: Shaun Reed ##
|
||||||
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
## About: Object class for storing object data ##
|
## About: Object class for storing object data ##
|
||||||
## ##
|
## ##
|
||||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
##############################################################################*/
|
##############################################################################*/
|
||||||
|
|
||||||
#include <object.h>
|
#include "object.h"
|
||||||
|
|
||||||
|
using namespace Qtk;
|
|
@ -0,0 +1,216 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## 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
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Main window for Qt6 OpenGL widget application ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
#ifndef QTK_QTKAPI_H
|
||||||
|
#define QTK_QTKAPI_H
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QtCore/QtGlobal>
|
||||||
|
|
||||||
|
#ifdef QTK_SHARED
|
||||||
|
#if defined(QTK_EXPORT)
|
||||||
|
#define QTKAPI Q_DECL_EXPORT
|
||||||
|
#else
|
||||||
|
#define QTKAPI Q_DECL_IMPORT
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define QTKAPI
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize Qt resources required by the Qtk library.
|
||||||
|
* This cannot be defined within any namespace, but can be called by ctors.
|
||||||
|
* See object.h for example.
|
||||||
|
*/
|
||||||
|
inline void initResources() {
|
||||||
|
Q_INIT_RESOURCE(resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Qtk {
|
||||||
|
/**
|
||||||
|
* Flag to set context for debug messages.
|
||||||
|
*/
|
||||||
|
enum DebugContext { Status, Debug, Warn, Error, Fatal };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find top level parent for a widget.
|
||||||
|
*
|
||||||
|
* @param widget Widget to start the search from.
|
||||||
|
* @return Top level parent widget or Q_NULLPTR if no parent
|
||||||
|
*/
|
||||||
|
static QWidget * topLevelParent(QWidget * widget) {
|
||||||
|
QString name = widget->objectName();
|
||||||
|
while(widget->parentWidget() != Q_NULLPTR) {
|
||||||
|
widget = widget->parentWidget();
|
||||||
|
}
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Default icon to use for Qtk desktop application.
|
||||||
|
*/
|
||||||
|
static QIcon getIcon() {
|
||||||
|
return QIcon(":/icons/icon.png");
|
||||||
|
}
|
||||||
|
} // namespace Qtk
|
||||||
|
|
||||||
|
#endif // QTK_QTKAPI_H
|