Compare commits

12 Commits

Author SHA1 Message Date
8dc5d82c8d README screenshots. 2025-03-09 11:43:38 -04:00
78639cf1c2 Reposition models in scene. 2025-03-09 11:11:09 -04:00
941f2d228c Clean up separation between plugins and app. 2025-03-08 14:09:56 -05:00
16baf6cdaf CI 2025-03-08 12:57:12 -05:00
c86a7744b3 Set clang-format argument options. 2025-03-08 11:52:39 -05:00
7fac6bafb4 Clang format.
Some checks failed
All Builds / Qtk (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), ubuntu-latest) (push) Failing after 23s
All Builds / Qtk-Library (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), ubuntu-latest) (push) Failing after 22s
All Builds / Qtk-Plugins (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), ubuntu-latest) (push) Failing after 20s
All Builds / Qtk-Assimp-Targets (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/, ubuntu-latest) (push) Failing after 18s
Linting / Tidy (push) Failing after 24s
Linting / Format (app) (push) Failing after 23s
Linting / Format (src) (push) Failing after 23s
All Builds / Qtk (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), macos-latest) (push) Has been cancelled
All Builds / Qtk (-DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG, , windows-latest) (push) Has been cancelled
All Builds / Qtk-Library (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), macos-latest) (push) Has been cancelled
All Builds / Qtk-Library (-DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG, , windows-latest) (push) Has been cancelled
All Builds / Qtk-Plugins (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/ $CONFIG, -j $(nproc), macos-latest) (push) Has been cancelled
All Builds / Qtk-Plugins (-DCMAKE_PREFIX_PATH=D:/a/qtk/qtk/Qt/$QT_VERSION/mingw81_64/ $CONFIG, , windows-latest) (push) Has been cancelled
All Builds / Qtk-Assimp-Targets (-DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/$QT_VERSION/gcc_64/, macos-latest) (push) Has been cancelled
2025-03-08 11:47:19 -05:00
1bed9545c9 Clean up qtk. 2025-03-08 11:40:00 -05:00
32641acd8d Clean up example code.
+ Fix spartan path.
+ Fix triangle draw mode.
2025-03-08 10:46:53 -05:00
68bfff7bcd Modify clang-tidy checks and options.
+ Disable static accessed though instance.
+ Do not mark destructors with override.
2025-03-08 09:38:04 -05:00
92e5937cc7 Add default options for enabled clang-tidy checks. 2025-03-08 09:35:12 -05:00
1e1c328a5a clang-tidy example-app 2025-03-08 09:27:39 -05:00
dcbeb26738 README 2025-03-08 09:02:55 -05:00
80 changed files with 1632 additions and 1281 deletions

View File

@@ -1,6 +1,6 @@
---
BasedOnStyle: Google
AlignAfterOpenBracket: AlwaysBreak
AlignAfterOpenBracket: Align
AlignArrayOfStructures: Left
AlignConsecutiveAssignments: None
AlignConsecutiveDeclarations: None
@@ -9,7 +9,6 @@ AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
@@ -21,8 +20,11 @@ InsertBraces: true
IndentAccessModifiers: true
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
BinPackArguments: true
BinPackParameters: true
BinPackArguments: false
BinPackParameters: false
AllowAllParametersOfDeclarationOnNextLine: true
ExperimentalAutoDetectBinPacking: false
BreakBeforeBraces: Linux
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
@@ -58,7 +60,7 @@ SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeParens: Never
SpaceBeforeParens: ControlStatementsExceptControlMacros
SpaceBeforeRangeBasedForLoopColon: true
SpacesBeforeTrailingComments: 2
SpaceInEmptyParentheses: false

View File

@@ -139,8 +139,149 @@ 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'
CheckOptions:
bugprone-argument-comment.CommentBoolLiterals: '0'
bugprone-argument-comment.CommentCharacterLiterals: '0'
bugprone-argument-comment.CommentFloatLiterals: '0'
bugprone-argument-comment.CommentIntegerLiterals: '0'
bugprone-argument-comment.CommentNullPtrs: '0'
bugprone-argument-comment.CommentStringLiterals: '0'
bugprone-argument-comment.CommentUserDefinedLiterals: '0'
bugprone-argument-comment.IgnoreSingleArgument: '0'
bugprone-argument-comment.StrictMode: '0'
bugprone-assert-side-effect.AssertMacros: assert,NSAssert,NSCAssert
bugprone-assert-side-effect.CheckFunctionCalls: 'false'
bugprone-assert-side-effect.IgnoredFunctions: __builtin_expect
bugprone-dangling-handle.HandleClasses: 'std::basic_string_view;std::experimental::basic_string_view'
bugprone-dynamic-static-initializers.HeaderFileExtensions: ';h;hh;hpp;hxx'
bugprone-lambda-function-name.IgnoreMacros: 'false'
bugprone-misplaced-widening-cast.CheckImplicitCasts: 'false'
bugprone-not-null-terminated-result.WantToUseSafeFunctions: 'true'
bugprone-reserved-identifier.AggressiveDependentMemberLookup: 'false'
bugprone-reserved-identifier.AllowedIdentifiers: ''
bugprone-reserved-identifier.Invert: 'false'
bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant: 'true'
bugprone-sizeof-expression.WarnOnSizeOfConstant: 'true'
bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression: 'false'
bugprone-sizeof-expression.WarnOnSizeOfPointerToAggregate: 'true'
bugprone-sizeof-expression.WarnOnSizeOfThis: 'true'
bugprone-string-constructor.LargeLengthThreshold: '8388608'
bugprone-string-constructor.StringNames: '::std::basic_string;::std::basic_string_view'
bugprone-string-constructor.WarnOnLargeLength: 'true'
bugprone-suspicious-enum-usage.StrictMode: 'false'
bugprone-suspicious-include.HeaderFileExtensions: ';h;hh;hpp;hxx'
bugprone-suspicious-include.ImplementationFileExtensions: 'c;cc;cpp;cxx'
bugprone-suspicious-missing-comma.MaxConcatenatedTokens: '5'
bugprone-suspicious-missing-comma.RatioThreshold: '0.200000'
bugprone-suspicious-missing-comma.SizeThreshold: '5'
bugprone-suspicious-string-compare.StringCompareLikeFunctions: ''
bugprone-suspicious-string-compare.WarnOnImplicitComparison: 'true'
bugprone-suspicious-string-compare.WarnOnLogicalNotComparison: 'false'
bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit: '16'
bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField: 'true'
bugprone-unused-return-value.AllowCastToVoid: 'false'
bugprone-unused-return-value.CheckedFunctions: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty;::std::back_inserter;::std::distance;::std::find;::std::find_if;::std::inserter;::std::lower_bound;::std::make_pair;::std::map::count;::std::map::find;::std::map::lower_bound;::std::multimap::equal_range;::std::multimap::upper_bound;::std::set::count;::std::set::find;::std::setfill;::std::setprecision;::std::setw;::std::upper_bound;::std::vector::at;::bsearch;::ferror;::feof;::isalnum;::isalpha;::isblank;::iscntrl;::isdigit;::isgraph;::islower;::isprint;::ispunct;::isspace;::isupper;::iswalnum;::iswprint;::iswspace;::isxdigit;::memchr;::memcmp;::strcmp;::strcoll;::strncmp;::strpbrk;::strrchr;::strspn;::strstr;::wcscmp;::access;::bind;::connect;::difftime;::dlsym;::fnmatch;::getaddrinfo;::getopt;::htonl;::htons;::iconv_open;::inet_addr;::isascii;::isatty;::mmap;::newlocale;::openat;::pathconf;::pthread_equal;::pthread_getspecific;::pthread_mutex_trylock;::readdir;::readlink;::recvmsg;::regexec;::scandir;::semget;::setjmp;::shm_open;::shmget;::sigismember;::strcasecmp;::strsignal;::ttyname'
bugprone-unused-return-value.CheckedReturnTypes: '::std::error_code;::std::error_condition;::std::errc;::std::expected;::boost::system::error_code'
cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU'
cert-err33-c.AllowCastToVoid: 'true'
cert-err33-c.CheckedFunctions: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;'
cert-msc51-cpp.DisallowedSeedTypes: 'time_t,std::time_t'
cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false'
cert-str34-c.CharTypdefsToIgnore: ''
cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false'
cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true'
cppcoreguidelines-pro-type-member-init.IgnoreArrays: 'false'
cppcoreguidelines-pro-type-member-init.UseAssignment: 'false'
cppcoreguidelines-pro-type-static-cast-downcast.StrictMode: 'true'
google-readability-braces-around-statements.ShortStatementLines: '1'
google-readability-function-size.StatementThreshold: '800'
google-readability-namespace-comments.ShortNamespaceLines: '10'
google-readability-namespace-comments.SpacesBeforeComments: '2'
hicpp-multiway-paths-covered.WarnOnMissingElse: 'false'
llvm-else-after-return.WarnOnConditionVariables: 'false'
llvm-else-after-return.WarnOnUnfixable: 'false'
llvm-qualified-auto.AddConstToQualified: 'false'
misc-throw-by-value-catch-by-reference.CheckThrowTemporaries: 'true'
misc-throw-by-value-catch-by-reference.WarnOnLargeObjects: 'false'
misc-uniqueptr-reset-release.IncludeStyle: llvm
modernize-avoid-bind.PermissiveParameterList: 'false'
modernize-deprecated-headers.CheckHeaderFile: 'false'
modernize-loop-convert.IncludeStyle: llvm
modernize-loop-convert.MakeReverseRangeFunction: ''
modernize-loop-convert.MakeReverseRangeHeader: ''
modernize-loop-convert.MaxCopySize: '16'
modernize-loop-convert.MinConfidence: reasonable
modernize-loop-convert.NamingStyle: CamelCase
modernize-loop-convert.UseCxx20ReverseRanges: 'true'
modernize-make-shared.IgnoreDefaultInitialization: 'true'
modernize-make-shared.IgnoreMacros: 'true'
modernize-make-shared.IncludeStyle: llvm
modernize-make-shared.MakeSmartPtrFunction: 'std::make_shared'
modernize-make-shared.MakeSmartPtrFunctionHeader: '<memory>'
modernize-make-unique.IgnoreDefaultInitialization: 'true'
modernize-make-unique.IgnoreMacros: 'true'
modernize-make-unique.IncludeStyle: llvm
modernize-make-unique.MakeSmartPtrFunction: 'std::make_unique'
modernize-make-unique.MakeSmartPtrFunctionHeader: '<memory>'
modernize-pass-by-value.IncludeStyle: llvm
modernize-pass-by-value.ValuesOnly: 'false'
modernize-raw-string-literal.DelimiterStem: lit
modernize-raw-string-literal.ReplaceShorterLiterals: 'false'
modernize-replace-auto-ptr.IncludeStyle: llvm
modernize-replace-disallow-copy-and-assign-macro.MacroName: DISALLOW_COPY_AND_ASSIGN
modernize-replace-random-shuffle.IncludeStyle: llvm
modernize-use-auto.MinTypeNameLength: '5'
modernize-use-auto.RemoveStars: 'false'
modernize-use-bool-literals.IgnoreMacros: 'true'
modernize-use-emplace.ContainersWithPush: '::std::stack;::std::queue;::std::priority_queue'
modernize-use-emplace.ContainersWithPushBack: '::std::vector;::std::list;::std::deque'
modernize-use-emplace.ContainersWithPushFront: '::std::forward_list;::std::list;::std::deque'
modernize-use-emplace.EmplacyFunctions: 'vector::emplace_back;vector::emplace;deque::emplace;deque::emplace_front;deque::emplace_back;forward_list::emplace_after;forward_list::emplace_front;list::emplace;list::emplace_back;list::emplace_front;set::emplace;set::emplace_hint;map::emplace;map::emplace_hint;multiset::emplace;multiset::emplace_hint;multimap::emplace;multimap::emplace_hint;unordered_set::emplace;unordered_set::emplace_hint;unordered_map::emplace;unordered_map::emplace_hint;unordered_multiset::emplace;unordered_multiset::emplace_hint;unordered_multimap::emplace;unordered_multimap::emplace_hint;stack::emplace;queue::emplace;priority_queue::emplace'
modernize-use-emplace.IgnoreImplicitConstructors: 'false'
modernize-use-emplace.SmartPointers: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr'
modernize-use-emplace.TupleMakeFunctions: '::std::make_pair;::std::make_tuple'
modernize-use-emplace.TupleTypes: '::std::pair;::std::tuple'
modernize-use-equals-default.IgnoreMacros: 'true'
modernize-use-equals-delete.IgnoreMacros: 'true'
modernize-use-nodiscard.ReplacementString: '[[nodiscard]]'
modernize-use-noexcept.ReplacementString: ''
modernize-use-noexcept.UseNoexceptFalse: 'true'
modernize-use-nullptr.IgnoredTypes: 'std::_CmpUnspecifiedParam::;^std::__cmp_cat::__unspec'
modernize-use-nullptr.NullMacros: 'NULL'
modernize-use-override.AllowOverrideAndFinal: 'false'
modernize-use-override.FinalSpelling: final
modernize-use-override.IgnoreDestructors: 'true'
modernize-use-override.IgnoreTemplateInstantiations: 'false'
modernize-use-override.OverrideSpelling: override
modernize-use-transparent-functors.SafeMode: 'false'
performance-faster-string-find.StringLikeClasses: '::std::basic_string;::std::basic_string_view'
performance-for-range-copy.AllowedTypes: ''
performance-for-range-copy.WarnOnAllAutoCopies: 'false'
performance-inefficient-string-concatenation.StrictMode: 'false'
performance-inefficient-vector-operation.EnableProto: 'false'
performance-inefficient-vector-operation.VectorLikeClasses: '::std::vector'
performance-move-const-arg.CheckMoveToConstRef: 'true'
performance-move-const-arg.CheckTriviallyCopyableMove: 'true'
performance-no-automatic-move.AllowedTypes: ''
performance-type-promotion-in-math-fn.IncludeStyle: llvm
performance-unnecessary-copy-initialization.AllowedTypes: ''
performance-unnecessary-copy-initialization.ExcludedContainerTypes: ''
performance-unnecessary-value-param.AllowedTypes: ''
performance-unnecessary-value-param.IncludeStyle: llvm
portability-simd-intrinsics.Std: ''
portability-simd-intrinsics.Suggest: 'false'
readability-avoid-const-params-in-decls.IgnoreMacros: 'true'
readability-braces-around-statements.ShortStatementLines: '0'
readability-const-return-type.IgnoreMacros: 'true'
readability-container-size-empty.ExcludedComparisonTypes: '::std::array'
readability-inconsistent-declaration-parameter-name.IgnoreMacros: 'true'
readability-inconsistent-declaration-parameter-name.Strict: 'false'
readability-redundant-declaration.IgnoreMacros: 'true'
readability-redundant-smartptr-get.IgnoreMacros: 'true'
readability-redundant-string-init.StringNames: '::std::basic_string_view;::std::basic_string'
readability-simplify-subscript-expr.Types: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array;::std::span'
readability-uniqueptr-delete-release.PreferResetCall: 'false'

View File

@@ -10,6 +10,7 @@ env:
jobs:
Qtk:
name: Qtk Applications
env:
CONFIG: -DQTK_SUBMODULES=ON -DQTK_DEBUG=OFF -DQTK_CCACHE=OFF -DQTK_GUI=ON -DQTK_PLUGINS=OFF -DQTK_EXAMPLE=ON
strategy:
@@ -29,10 +30,10 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Qt
uses: jurplel/install-qt-action@v2
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
@@ -82,7 +83,7 @@ jobs:
- name: Upload package artifacts (DEB)
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: qtk-gui-${{ matrix.os }}
path: |
@@ -97,7 +98,7 @@ jobs:
- name: Upload package artifacts (WIN)
if: matrix.os == 'windows-latest'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: qtk-gui-${{ matrix.os }}
path: |
@@ -112,14 +113,14 @@ jobs:
- name: Upload package artifacts (OSX)
if: matrix.os == 'macos-latest'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: qtk-gui-${{ matrix.os }}
path: |
build/packages/*.tar.gz
- name: Upload Qtk install directory
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: qtk-gui-${{ matrix.os }}-install
path: install/*
@@ -132,7 +133,7 @@ jobs:
# cmake --build build/ --target package_source
#
# - name: Upload package artifacts
# uses: actions/upload-artifact@v3
# uses: actions/upload-artifact@v4
# with:
# name: qtk-${{ matrix.os }}-packages
# path: |
@@ -140,6 +141,7 @@ jobs:
# !build/packages/_CPack_Packages/*
Qtk-Library:
name: Qtk library
env:
CONFIG: -DQTK_SUBMODULES=ON -DQTK_DEBUG=OFF -DQTK_CCACHE=OFF -DQTK_GUI=OFF -DQTK_PLUGINS=OFF -DQTK_EXAMPLE=OFF
strategy:
@@ -159,10 +161,10 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Qt
uses: jurplel/install-qt-action@v2
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
@@ -201,7 +203,7 @@ jobs:
- name: Upload package artifacts (DEB)
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: libqtk-${{ matrix.os }}
path: |
@@ -216,7 +218,7 @@ jobs:
- name: Upload package artifacts (WIN)
if: matrix.os == 'windows-latest'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: qtk-${{ matrix.os }}
path: |
@@ -231,20 +233,21 @@ jobs:
- name: Upload package artifacts (OSX)
if: matrix.os == 'macos-latest'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: qtk-${{ matrix.os }}
path: |
build/packages/*.tar.gz
- name: Upload libqtk install
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: always()
with:
name: libqtk-${{ matrix.os }}-install
path: install/*
Qtk-Plugins:
name: Qtk Qt Designer Plugins
env:
CONFIG: -DQTK_SUBMODULES=ON -DQTK_DEBUG=OFF -DQTK_CCACHE=OFF -DQTK_GUI=OFF -DQTK_PLUGINS=ON -DQTK_EXAMPLE=OFF
strategy:
@@ -264,10 +267,10 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Qt
uses: jurplel/install-qt-action@v2
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
@@ -292,6 +295,7 @@ jobs:
run: cmake --install build/ --config Release --component qtk_plugins
Qtk-Assimp-Targets:
name: Qtk Assimp Platform Targets
strategy:
fail-fast: false
matrix:
@@ -304,10 +308,10 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Qt
uses: jurplel/install-qt-action@v2
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}

View File

@@ -9,10 +9,10 @@ jobs:
Tidy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Qt
uses: jurplel/install-qt-action@v2
uses: jurplel/install-qt-action@v4
with:
version: '6.5.0'
@@ -22,7 +22,7 @@ jobs:
- 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)
cmake --build build --target qtk_gui qtk_example -- -j $(nproc)
- uses: cpp-linter/cpp-linter-action@v2
id: linter
@@ -30,7 +30,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Use clang-tools 14
version: '14'
version: '18'
# Don't use clang-format with this action
# + Set to `file` to use .clang-format (Qtk formats with clang 15)
style: ''
@@ -57,12 +57,12 @@ jobs:
matrix:
path:
- 'src'
- 'app'
- 'example-app'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: clang-format Check
uses: jidicula/clang-format-action@v4.9.0
with:
clang-format-version: '15'
clang-format-version: '18'
check-path: ${{ matrix.path }}

2
.gitignore vendored
View File

@@ -5,7 +5,7 @@
**/.vscode/**
# CMake build files
**/cmake-build-debug/**
**/cmake-build-*/**
**/build/**
install

111
README.md
View File

@@ -3,28 +3,58 @@
[![All Builds](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml)
[![Linting](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml)
Qtk is a Qt OpenGL graphics library created primarily for my own learning
purposes. The library wraps some QOpenGL functionality in convenience classes
Qtk is a Qt OpenGL graphics library that wraps some QOpenGL functionality in convenience classes
that allow rendering geometry in 2D and 3D using custom GLSL shader programs.
The long-term goal for this project is to create a tool that I can use to
practice shader coding or graphics programming techniques. In doing this I hope
to also learn more about the Qt UI framework, and the CMake build system.
The Qtk desktop application provides a model loader using [Assimp](https://assimp.org/) within a Qt widget application.
You can fly around the scene using WASD while holding down the right mouse button.
Object names can be double-clicked in the tree view panel for quick camera navigation.
Properties of the object, like shader code and translation / scale, can be viewed and modified in the side panel.
![](resources/screenshots/screen.png)
All side panels and toolbars are dockable widgets that can be popped out
and reorganized as needed. Panels can also 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.
![](resources/screenshots/screen-1.png)
The small triangles floating near 3D models represent the light source being used for the shader.
These appear on models using phong, specular, and diffuse lighting techniques.
The default scene contains basic examples like texture mapping to make a crate from basic cube geometry
![](resources/screenshots/screen-texture.png)
Examples of Ambient, Diffuse, and Specular GLSL shaders.
| Ambient | Diffuse | Specular |
|-------------------------------------------------------|-------------------------------------------------------|--------------------------------------------------------|
| <img src="resources/screenshots/screen-ambient.png"/> | <img src="resources/screenshots/screen-diffuse.png"/> | <img src="resources/screenshots/screen-specular.png"/> |
And more advanced techniques like Phong lighting (ambient + diffuse + specular) and normal mapping.
![](resources/screenshots/screen-phong.png)
| Normal Mapping Disabled | Normal Mapping Enabled |
|------------------------------------------------------|--------------------------------------------------------|
| <img src="resources/screenshots/spartan-phong.png"/> | <img src="resources/screenshots/spartan-normals.png"/> |
See the `View` toolbar menu to enable debug console widgets for open scenes or reopen previously closed panels.
Key features that are planned:
* Runtime loading of `.obj` or similar 3D models.
* Drag-and-drop interaction for adding objects to the scene.
* Runtime reloading of modified GLSL shaders attached to objects within scenes.
* Multiple views of a scene at one time.
* Camera control modes such as panning, orbiting, or following objects.
* Save / load for scene data. The current inheritance model is temporary.
* Basic text editor for quickly modifying shaders attached to objects.
* Shader / object properties panel to modify related settings.
* Reduce size of application resources and git references.
The Qtk desktop application provides a model loader
using [Assimp](https://assimp.org/) within a Qt widget application.
- [x] Runtime loading of `.obj` or similar 3D models.
- [x] Drag-and-drop interaction for adding objects to the scene.
- [x] Shader / object properties panel to modify related settings.
- [ ] 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.
- [ ] Reduce size of application resources and git references.
For examples of using the Qtk API, see the `example-app` project in the root of
this repository.
@@ -177,55 +207,24 @@ cmake --install build-all/ --component qtk_example --prefix=install
See the README in the [example-app/](example-app) subdirectory for instructions
on standalone builds.
### Controls
You can fly around the scene if you hold the right mouse button and use WASD.
If you see a small triangle floating by a model it represents the light source
that is being used for the shader rendering the model. These appear on models
using phong, specular, and diffuse lighting techniques.
Object names can be double-clicked in the tree view panel for quick camera
navigation. All panels and toolbars are dockable widgets that can be popped out
and reorganized as needed. Panels can be stacked to create a docked widget with
tabs. The central widget that provides the camera view into the scene cannot be
detached from the main window in this way. See the `View` menu to enable debug
console widgets for open scenes or reopen previously closed panels.
![](resources/screenshot.png)
Spartan with no normals -
![](resources/spartan-specular.png)
Spartan with normals -
![](resources/spartan-normals.png)
#### Development
This project uses version `15.0.5` of `clang-format`.
Before merging any branch we should run `clang-tidy` followed by `clang-format`.
This project is using `clang-format` version `>=15.0.5`.
On Ubuntu 24.04, clang-format 18 is available to install in apt repositories.
```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
sudo apt install clang-format
```
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`.
If `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)
Ubuntu clang-format version 18.1.3 (1ubuntu1)
```
CLion has integration for IDE code reformatting actions with `clang-format`.
If you're using CLion, the `.clang-format` configuration will be picked up by
CLion automatically.
If you're using CLion, the `.clang-format` configuration will be picked up by CLion automatically.
`clang-tidy` can be run with the following commands.
@@ -234,14 +233,14 @@ CLion automatically.
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
clang-tidy -p build/ --fix --config-file=.clang-tidy src/**/*.cpp src/**/*.h example-app/*.cpp example-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 -i --style=file:.clang-format src/**/*.cpp src/**/*.h example-app/*.cpp example-app/*.h
```
`clang-format` can be run with git integration (or CLion if you prefer).

View File

@@ -11,26 +11,26 @@
using namespace Qtk;
ExampleScene::ExampleScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) {
ExampleScene::ExampleScene()
{
setSceneName("Example Scene");
getCamera().setTranslation({-8.0f, 0.0f, 10.0f});
getCamera().setRotation(0.0f, 1.0f, 0.0f, -5.0f);
getCamera().getTransform().setTranslation(-8.0f, 0.0f, 10.0f);
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
ExampleScene::~ExampleScene() {}
ExampleScene::~ExampleScene() = default;
void ExampleScene::init() {
auto skybox = new Qtk::Skybox("Skybox");
setSkybox(skybox);
void ExampleScene::init()
{
setSkybox(new Qtk::Skybox("Skybox"));
std::string spartanPath = QTK_EXAMPLE_SOURCE_DIR;
spartanPath += "/resources/models/spartan/spartan.obj";
auto spartan = new Model("spartan", spartanPath.c_str());
addObject(spartan);
spartanPath += "/../resources/models/spartan/spartan.obj";
auto spartan = addObject(new Model("spartan", spartanPath.c_str()));
spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f);
auto mesh = addObject(
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ARRAYS)));
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(-5.0f, 0.0f, -2.0f);
// QTK_DRAW_ARRAYS is the default for generic shapes in qtk/shape.h
@@ -56,42 +56,44 @@ void ExampleScene::init() {
mesh->setColor(GREEN);
}
void ExampleScene::draw() {
void ExampleScene::draw()
{
// The base class method _must_ be called first, before additional logic.
Scene::draw();
// No additional custom draw logic for this example.
// QtkScene in Qtk desktop application is an example using custom draw logic.
}
void ExampleScene::update() {
void ExampleScene::update()
{
auto top_triangle = MeshRenderer::getInstance("topTriangle");
auto bottom_triangle = MeshRenderer::getInstance("bottomTriangle");
// Pitch forward and roll sideways
MeshRenderer::getInstance("leftTriangle")
->getTransform()
.rotate(1.0f, 0.0f, 0.0f, 0.75f);
.rotate(0.75f, 1.0f, 0.0f, 0.0f);
MeshRenderer::getInstance("rightTriangle")
->getTransform()
.rotate(0.0f, 0.0f, 1.0f, 0.75f);
.rotate(0.75f, 0.0f, 0.0f, 1.0f);
// Make the top and bottom triangles slide left-to-right.
static float translateX = 0.025f;
float limit = -9.0f; // Origin position.x - 2.0f
float posX = MeshRenderer::getInstance("topTriangle")
->getTransform()
.getTranslation()
.x();
if(posX < limit || posX > limit + 4.0f) {
float posX = top_triangle->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.2f, 0.0f, 0.4f, 0.75f);
MeshRenderer::getInstance("bottomTriangle")
->getTransform()
.rotate(0.0f, 0.2f, 0.4f, 0.75f);
top_triangle->getTransform().translate(translateX, 0.0f, 0.0f);
bottom_triangle->getTransform().translate(-translateX, 0.0f, 0.0f);
// Apply some rotation to the triangles as they move left-to-right.
top_triangle->getTransform().rotate(0.75f, 0.2f, 0.0f, 0.4f);
bottom_triangle->getTransform().rotate(0.75f, 0.0f, 0.2f, 0.4f);
MeshRenderer::getInstance("centerCube")
->getTransform()
.rotate(0.2f, 0.4f, 0.6f, 0.75f);
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
}

View File

@@ -11,16 +11,30 @@
#include <qtk/scene.h>
class ExampleScene : public Qtk::SceneInterface {
class ExampleScene : public Qtk::Scene
{
public:
ExampleScene(Qtk::Scene * scene);
explicit ExampleScene();
~ExampleScene();
/**
* Override the initialization logic for the scene.
* This method should up the scene's objects, skybox, etc.
*/
void init() override;
/**
* Optionally override the draw method for the scene.
*
* This is just here for example, it should be omitted entirely if we don't
* want to provide a custom implementation for the ExampleScene.
*/
void draw() override;
/**
* Update objects in the scene for translation or rotation.
*/
void update() override;
};

View File

@@ -11,7 +11,8 @@
#include "examplewidget.h"
ExampleWidget::ExampleWidget(QWidget * parent) :
QOpenGLWidget(parent), mScene(new ExampleScene(new Qtk::SceneEmpty)) {
QOpenGLWidget(parent), mScene(new ExampleScene)
{
// 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.
@@ -27,7 +28,8 @@ ExampleWidget::ExampleWidget(QWidget * parent) :
setFocusPolicy(Qt::ClickFocus);
}
void ExampleWidget::initializeGL() {
void ExampleWidget::initializeGL()
{
initializeOpenGLFunctions();
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
glEnable(GL_MULTISAMPLE);
@@ -40,18 +42,21 @@ void ExampleWidget::initializeGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void ExampleWidget::resizeGL(int width, int height) {
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() {
void ExampleWidget::paintGL()
{
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
mScene->draw();
}
void ExampleWidget::update() {
void ExampleWidget::update()
{
mScene->update();
QWidget::update();
}

View File

@@ -14,7 +14,8 @@
#include "examplescene.h"
class ExampleWidget : public QOpenGLWidget, protected QOpenGLFunctions {
class ExampleWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT;
public:

View File

@@ -11,7 +11,8 @@
#include "examplewidget.h"
int main(int argc, char * argv[]) {
int main(int argc, char * argv[])
{
QApplication app(argc, argv);
auto window = new QMainWindow;

View File

@@ -46,8 +46,8 @@
<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-phong.frag">shaders/fragment/model-phong.frag</file>
<file alias="model-phong.vert">shaders/vertex/model-phong.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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

View File

Before

Width:  |  Height:  |  Size: 263 KiB

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 646 KiB

View File

@@ -47,13 +47,13 @@ target_link_libraries(qtk_plugins PUBLIC qtk_plugin_library)
################################################################################
# Final Qtk Application
################################################################################
# Source files for the main window and core application
set(
QTK_GUI_SOURCES
qtkscene.cpp qtkscene.h
# logger.cpp logger.h
main.cpp
)
qt_add_executable(qtk_gui ${QTK_GUI_SOURCES})
target_link_libraries(qtk_gui PRIVATE qtk_plugin_library)

View File

@@ -15,10 +15,14 @@
using namespace Qtk;
DebugConsole::DebugConsole(QWidget * owner, const QString & key) :
DebugConsole(owner, key, key + "Debugger") {}
DebugConsole(owner, key, key + "Debugger")
{
}
DebugConsole::DebugConsole(
QWidget * owner, const QString & key, const QString & name) {
DebugConsole::DebugConsole(QWidget * owner,
const QString & key,
const QString & name)
{
ui_ = new Ui::DebugConsole;
ui_->setupUi(this);
setObjectName(name);
@@ -27,8 +31,7 @@ DebugConsole::DebugConsole(
setWindowTitle(name + " Debug Console");
auto qtkWidget = dynamic_cast<QtkWidget *>(owner);
if(qtkWidget) {
connect(
qtkWidget->getLogger(), &Logger::sendLog, this, &DebugConsole::sendLog);
if (qtkWidget) {
connect(qtkWidget, &QtkWidget::sendLog, this, &DebugConsole::sendLog);
}
}

View File

@@ -16,12 +16,15 @@
#include "qtkwidget.h"
namespace Ui {
namespace Ui
{
class DebugConsole;
}
namespace Qtk {
class DebugConsole : public QDockWidget {
namespace Qtk
{
class DebugConsole : public QDockWidget
{
Q_OBJECT;
public:
@@ -61,8 +64,8 @@ namespace Qtk {
* @param context The DebugContext to use for the message.
* Default value is Status.
*/
inline void sendLog(QString message, Qtk::DebugContext context = Status) {
// qDebug() << "[LOGGER]: " << qPrintable(message) << "; LOGGER";
inline void sendLog(QString message, DebugContext context = Status)
{
mConsole->setTextColor(logColor(context));
mConsole->append(logPrefix(message, context));
}
@@ -73,7 +76,8 @@ namespace Qtk {
*
* @param name Base name for the DebugConsole window.
*/
inline void setTitle(QString name) {
inline void setTitle(const QString & name)
{
setWindowTitle(name + " Debug Console");
}
@@ -82,8 +86,9 @@ namespace Qtk {
* @param context Log context severity level.
* @return QColor corresponding with the message context.
*/
[[nodiscard]] QColor logColor(const DebugContext & context) const {
switch(context) {
[[nodiscard]] QColor logColor(const DebugContext & context) const
{
switch (context) {
case Status:
return Qt::GlobalColor::darkGray;
case Debug:
@@ -106,10 +111,11 @@ namespace Qtk {
* @param context The log context severity level.
* @return The log message prefixed with the DebugContext level.
*/
[[nodiscard]] QString logPrefix(
QString & message, const DebugContext & context) {
[[nodiscard]] QString logPrefix(QString & message,
const DebugContext & context)
{
QString prefix;
switch(context) {
switch (context) {
case Status:
prefix = "[Status]: ";
break;

View File

@@ -1,107 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Logger for Qtk ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include "logger.h"
#include "qtkwidget.h"
using namespace Qtk;
Logger::Logger(Qtk::QtkWidget * parent) : QObject(parent) {
// connect(
// this, SIGNAL(sendLog(QString, Qtk::DebugContext)), this,
// SLOT(log(QString, Qtk::DebugContext)));
if(parent != Q_NULLPTR) {
connect(
parent, SIGNAL(sendLog(QString, Qtk::DebugContext)), this,
SLOT(log(QString, Qtk::DebugContext)));
}
}
std::pair<QString, DebugContext> Logger::log(const QOpenGLDebugMessage & msg) {
QString error_msg;
DebugContext context;
// Format based on severity
switch(msg.severity()) {
case QOpenGLDebugMessage::NotificationSeverity:
error_msg += "--";
context = Status;
break;
case QOpenGLDebugMessage::HighSeverity:
error_msg += "!!";
context = Fatal;
break;
case QOpenGLDebugMessage::MediumSeverity:
error_msg += "!~";
context = Error;
break;
case QOpenGLDebugMessage::LowSeverity:
error_msg += "~~";
context = Warn;
break;
case QOpenGLDebugMessage::InvalidSeverity:
error_msg += "??";
context = Invalid;
break;
case QOpenGLDebugMessage::AnySeverity:
error_msg += "**";
context = Any;
break;
}
error_msg += " (";
// Format based on source
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error_msg += #c; \
break
switch(msg.source()) {
CASE(APISource);
CASE(WindowSystemSource);
CASE(ShaderCompilerSource);
CASE(ThirdPartySource);
CASE(ApplicationSource);
CASE(OtherSource);
CASE(InvalidSource);
CASE(AnySource);
}
#undef CASE
error_msg += " : ";
// Format based on type
#define CASE(c) \
case QOpenGLDebugMessage::c: \
error_msg += #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);
CASE(AnyType);
}
#undef CASE
error_msg += ")\n" + msg.message();
log(error_msg, context);
return {error_msg, context};
}
void Logger::log(const QString & message, DebugContext context) {
const QString log_msg = "[LOGGER]: " + message + "; LOGGER";
qDebug() << qPrintable(log_msg);
emit sendLog(log_msg, context);
}

View File

@@ -1,37 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2023 Shaun Reed, all rights reserved ##
## About: Logger for Qtk ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_LOGGER_H
#define QTK_LOGGER_H
#include <QOpenGLDebugMessage>
#include <QString>
#include "qtk/qtkapi.h"
namespace Qtk {
class QtkWidget;
class Logger : public QObject {
Q_OBJECT
public:
explicit Logger(Qtk::QtkWidget * parent = nullptr);
public slots:
std::pair<QString, DebugContext> log(
const QOpenGLDebugMessage & msg);
void log(
const QString & message,
Qtk::DebugContext context = Qtk::DebugContext::Status);
signals:
void sendLog(const QString & message, Qtk::DebugContext context);
};
} // namespace Qtk
#endif // QTK_LOGGER_H

View File

@@ -11,7 +11,8 @@
#include "qtkmainwindow.h"
#include "qtkscene.h"
int main(int argc, char * argv[]) {
int main(int argc, char * argv[])
{
Q_INIT_RESOURCE(resources);
QApplication a(argc, argv);
@@ -20,9 +21,12 @@ int main(int argc, char * argv[]) {
// Qtk currently uses the decorator pattern to save / load scenes.
// This is a temporary solution and will be improved in the future.
auto emptyScene = new Qtk::SceneEmpty;
window->getQtkWidget()->setScene(new QtkScene(emptyScene));
// NOTE: We set the scene here and not in QtkMainWindow to detach the scene
// from the QtkWidget plugin (qtk_plugin_library build target).
// Once we can save / load scenes, this call, and QtkScene, can be removed.
window->getQtkWidget()->setScene(new QtkScene);
window->show();
return QApplication::exec();
}

View File

@@ -7,16 +7,14 @@
##############################################################################*/
#include "qtkmainwindow.h"
#include "qtkscene.h"
#include "ui_qtkmainwindow.h"
MainWindow * MainWindow::mainWindow_ = Q_NULLPTR;
/*******************************************************************************
* Constructors / Destructors
******************************************************************************/
MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent)
{
ui_ = new Ui::MainWindow;
setObjectName("MainWindow");
// For use in design mode using Qt Creator
@@ -26,19 +24,23 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
// Initialize static container for all active QtkWidgets
auto qtkWidgets = findChildren<Qtk::QtkWidget *>();
for(auto & qtkWidget : qtkWidgets) {
qtkWidget->setScene(new Qtk::SceneEmpty);
for (auto & qtkWidget : qtkWidgets) {
// NOTE: Set a temporary scene for the widget to use for initialization.
// This should be replaced by loading a scene, or creating a new (unsaved)
// scene when Qtk is opened.
qtkWidget->setScene(new EmptyScene);
views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget);
// Add GUI 'view' toolbar option to show debug console.
ui_->menuView->addAction(qtkWidget->getActionToggleConsole());
// Refresh GUI widgets when scene or objects are updated.
connect(
qtkWidget->getScene(), &Qtk::Scene::sceneUpdated, this,
&MainWindow::refreshScene);
connect(
qtkWidget, &Qtk::QtkWidget::objectFocusChanged, ui_->qtk__ToolBox,
&Qtk::ToolBox::updateFocus);
connect(qtkWidget->getScene(),
&Qtk::Scene::sceneUpdated,
this,
&MainWindow::refreshScene);
connect(qtkWidget, &Qtk::QtkWidget::objectFocusChanged,
ui_->qtk__ToolBox,
&Qtk::ToolBox::updateFocus);
}
// TODO: Fix / use MainWindow in Qt Designer to add these dock widgets.
@@ -58,7 +60,8 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
setWindowIcon(Qtk::getIcon());
}
MainWindow::~MainWindow() {
MainWindow::~MainWindow()
{
delete ui_;
}
@@ -66,28 +69,32 @@ MainWindow::~MainWindow() {
* Public Methods
******************************************************************************/
MainWindow * MainWindow::getMainWindow() {
if(mainWindow_ == Q_NULLPTR) {
mainWindow_ = new MainWindow;
}
return mainWindow_;
MainWindow * MainWindow::getMainWindow()
{
static MainWindow window;
return &window;
}
Qtk::QtkWidget * MainWindow::getQtkWidget(int64_t index) {
if(views_.size() <= index) {
Qtk::QtkWidget * MainWindow::getQtkWidget(int64_t index)
{
if (views_.size() <= index) {
return Q_NULLPTR;
}
return views_.begin(index)->second;
auto it = views_.begin();
std::advance(it, index);
return it->second;
}
Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name) {
if(!views_.count(name)) {
Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name)
{
if (!views_.count(name)) {
return Q_NULLPTR;
}
return views_[name];
}
void MainWindow::refreshScene(const QString & sceneName) {
void MainWindow::refreshScene(const QString & sceneName)
{
// TODO: Select TreeView using sceneName
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene());
}

View File

@@ -17,15 +17,49 @@
#include "debugconsole.h"
#include "qtkwidget.h"
namespace Ui {
namespace Ui
{
class MainWindow;
}
/**
* An empty scene used for initializing all QtkWidgets within the MainWindow.
* This serves as a temporary placeholder for QtkScene (for example), which is
* defined in the separate qtk_gui target. The reason for this separation is to
* support the use of QtkWidgets (the qtk_plugins target) within the Qt Designer
* application without implementations provided in the Qtk Desktop Application.
*
* For the Qtk application, this should be replaced by loading the previous
* scene or creating a new _unsaved_ scene when the application is opened.
* Currently we have essentially hard-coded QtkScene to use as examples for
* testing the application. This means that the only way to create or modify a
* scene is to write code. Any modifications made in the application, such as
* moving or resizing objects, will not persist and cannot be saved.
*
* For users of Qtk Designer Plugins, this means that installing
* the `qtk_plugins` target to Qt Designer allows use all of the designer's
* features to build an interface and position or resize a QtkWidget as needed.
* The QtkWidget also appears as widget in the IDE's toolbars and can be added
* to any new application easily, once the plugins are installed.
*
* Once the application is designed, you can define a custom scene and use the
* Qtk API or Qt OpenGL funtions directly to render to it.
*
* Any application using a QtkWidget can set a custom scene in their main
* function. See the MainWindow::MainWindow constructor as an example.
*/
class EmptyScene : public Qtk::Scene {
void init() override {
setSceneName("Empty Scene");
}
};
/**
* MainWindow class to provide an example of using a QtkWidget within a Qt
* window application.
*/
class MainWindow : public QMainWindow {
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
@@ -80,7 +114,6 @@ class MainWindow : public QMainWindow {
MainWindow(const MainWindow &) {};
Ui::MainWindow * ui_ {};
static MainWindow * mainWindow_;
/**
* Maps a scene name to the QtkWidget viewing it.

View File

@@ -14,14 +14,15 @@ using namespace Qtk;
* Constructors, Destructors
******************************************************************************/
QtkScene::QtkScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) {
QtkScene::QtkScene()
{
setSceneName("Qtk Scene");
getCamera().setTranslation({0.0f, 0.0f, 20.0f});
getCamera().setRotation(
QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -5.0f));
getCamera().getTransform().setTranslation(0.0f, 0.0f, 20.0f);
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
QtkScene::~QtkScene() {
QtkScene::~QtkScene()
{
delete mTestPhong;
delete mTestSpecular;
delete mTestDiffuse;
@@ -32,7 +33,8 @@ QtkScene::~QtkScene() {
* Public Member Functions
******************************************************************************/
void QtkScene::init() {
void QtkScene::init()
{
// Add a skybox to the scene using default cube map images and settings.
setSkybox(new Qtk::Skybox("Skybox"));
@@ -90,7 +92,7 @@ void QtkScene::init() {
model->getTransform().setTranslation(2.0f, 2.0f, -10.0f);
// Sometimes the models are very large
model->getTransform().scale(0.0025f);
model->getTransform().rotate(0.0f, 1.0f, 0.0f, -110.0f);
model->getTransform().rotate(-110.0f, 0.0f, 1.0f, 0.0f);
model = addObject(
new Qtk::Model("alien", ":/models/models/alien-hominid/alien.obj"));
@@ -100,8 +102,8 @@ void QtkScene::init() {
model = addObject(
new Qtk::Model("My scythe", ":/models/models/scythe/scythe.obj"));
model->getTransform().setTranslation(-6.0f, 0.0f, -10.0f);
model->getTransform().rotate(1.0f, 0.0f, 0.0f, -90.0f);
model->getTransform().rotate(0.0f, 1.0f, 0.0f, 90.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"));
@@ -112,13 +114,13 @@ void QtkScene::init() {
// Simple cube lighting examples.
/* Phong lighting example on a basic cube. */
mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube());
mTestPhong = addObject(new Qtk::MeshRenderer("phongCube", Qtk::Cube()));
mTestPhong->getTransform().setTranslation(3.0f, 0.0f, -2.0f);
// NOTE: You no longer need to manually bind shader program to set uniforms.
// + You can still bind it if you want to for performance reasons.
// + Qtk will only bind / release if the shader program is not already bound.
mTestPhong->setShaders(
":/shaders/solid-phong.vert", ":/shaders/solid-phong.frag");
mTestPhong->setShaders(":/shaders/solid-phong.vert",
":/shaders/solid-phong.frag");
// For example this would technically not be efficient, because each one of
// these calls will bind, set, release. We could instead bind, set N uniforms,
@@ -144,16 +146,16 @@ void QtkScene::init() {
/* Example of a cube with no lighting applied */
mesh = addObject(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(5.0f, 0.0f, -2.0f);
mesh->setShaders(
":/shaders/solid-perspective.vert", ":/shaders/solid-perspective.frag");
mesh->setShaders(":/shaders/solid-perspective.vert",
":/shaders/solid-perspective.frag");
mesh->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
// No light source needed for this lighting technique
/* Initialize Ambient example cube */
mTestAmbient = new Qtk::MeshRenderer("ambient", Cube());
mTestAmbient = addObject(new Qtk::MeshRenderer("ambientCube", Cube()));
mTestAmbient->getTransform().setTranslation(7.0f, 0.0f, -2.0f);
mTestAmbient->setShaders(
":/shaders/solid-ambient.vert", ":/shaders/solid-ambient.frag");
mTestAmbient->setShaders(":/shaders/solid-ambient.vert",
":/shaders/solid-ambient.frag");
// Changing these uniform values will alter lighting effects.
mTestAmbient->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestAmbient->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
@@ -162,10 +164,10 @@ void QtkScene::init() {
// No light source needed for this lighting technique
/* Initialize Diffuse example cube */
mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube());
mTestDiffuse = addObject(new Qtk::MeshRenderer("diffuseCube", Cube()));
mTestDiffuse->getTransform().setTranslation(9.0f, 0.0f, -2.0f);
mTestDiffuse->setShaders(
":/shaders/solid-diffuse.vert", ":/shaders/solid-diffuse.frag");
mTestDiffuse->setShaders(":/shaders/solid-diffuse.vert",
":/shaders/solid-diffuse.frag");
mTestDiffuse->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestDiffuse->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestDiffuse->setUniform("uAmbientStrength", 0.2f);
@@ -178,10 +180,10 @@ void QtkScene::init() {
mesh->getTransform().scale(0.25f);
/* Initialize Specular example cube */
mTestSpecular = new Qtk::MeshRenderer("specular", Cube());
mTestSpecular = addObject(new Qtk::MeshRenderer("specularCube", Cube()));
mTestSpecular->getTransform().setTranslation(11.0f, 0.0f, -2.0f);
mTestSpecular->setShaders(
":/shaders/solid-specular.vert", ":/shaders/solid-specular.frag");
mTestSpecular->setShaders(":/shaders/solid-specular.vert",
":/shaders/solid-specular.frag");
mTestSpecular->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestSpecular->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestSpecular->setUniform("uAmbientStrength", 0.2f);
@@ -229,9 +231,10 @@ void QtkScene::init() {
// Building more complex objects for showing examples of lighting techniques
/* Test alien Model with phong lighting and specular mapping. */
model = addObject(new Qtk::Model(
"alienTest", ":/models/models/alien-hominid/alien.obj",
":/shaders/model-specular.vert", ":/shaders/model-specular.frag"));
model = addObject(new Qtk::Model("alienTest",
":/models/models/alien-hominid/alien.obj",
":/shaders/model-phong.vert",
":/shaders/model-phong.frag"));
model->getTransform().setTranslation(3.0f, -1.0f, 10.0f);
model->getTransform().scale(0.15f);
model->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
@@ -239,7 +242,7 @@ void QtkScene::init() {
model->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
model->setUniform("uMaterial.ambientStrength", 0.8f);
model->setUniform("uMaterial.diffuseStrength", 0.8f);
model->setUniform("uMaterial.specularStrength", 1.0f);
model->setUniform("uMaterial.specularStrength", 0.5f);
model->setUniform("uMaterial.shine", 32.0f);
model->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
@@ -247,17 +250,18 @@ void QtkScene::init() {
model->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
// Light source for alienTest object.
mesh = addObject(new Qtk::MeshRenderer(
"alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS)));
mesh = addObject(new Qtk::MeshRenderer("alienTestLight",
Triangle(Qtk::QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(4.0f, 1.5f, 10.0f);
mesh->getTransform().scale(0.25f);
// This function changes values we have allocated in a buffer, so init() after
mesh->setColor(GREEN);
/* Test spartan Model with phong lighting, specular and normal mapping. */
model = addObject(new Qtk::Model(
"spartanTest", ":/models/models/spartan/spartan.obj",
":/shaders/model-normals.vert", ":/shaders/model-normals.frag"));
model = addObject(new Qtk::Model("spartanTest",
":/models/models/spartan/spartan.obj",
":/shaders/model-phong.vert",
":/shaders/model-phong.frag"));
model->getTransform().setTranslation(0.0f, -1.0f, 10.0f);
model->getTransform().scale(2.0f);
model->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
@@ -291,8 +295,8 @@ void QtkScene::init() {
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS
mesh = addObject(new Qtk::MeshRenderer(
"rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh = addObject(new Qtk::MeshRenderer("rgbNormalsCubeElementsTest",
Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(5.0f, 0.0f, 2.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
@@ -322,8 +326,8 @@ void QtkScene::init() {
mesh->reallocateTexCoords(mesh->getTexCoords());
// Test drawing a cube with texture coordinates using glDrawElements
mesh = addObject(new Qtk::MeshRenderer(
"uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh = addObject(new Qtk::MeshRenderer("uvCubeElementsTest",
Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(-1.7f, 0.0f, -2.0f);
mesh->setTexture(":/textures/crate.png");
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
@@ -332,16 +336,16 @@ void QtkScene::init() {
mesh->reallocateNormals(mesh->getNormals());
mesh->reallocateTexCoords(mesh->getTexCoords(), 3);
mesh->releaseShaders();
mesh->getTransform().rotate(0.0f, 1.0f, 0.0f, 45.0f);
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(0.0f, 1.0f, 0.0f, 45.0f);
mesh->setShaders(
":/shaders/texture-cubemap.vert", ":/shaders/texture-cubemap.frag");
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());
@@ -355,15 +359,15 @@ void QtkScene::init() {
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS
mesh = addObject(new Qtk::MeshRenderer(
"rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS)));
mesh = addObject(new Qtk::MeshRenderer("rgbTriangleArraysTest",
Triangle(QTK_DRAW_ARRAYS)));
mesh->getTransform().setTranslation(7.0f, 0.0f, 2.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS
mesh = addObject(new Qtk::MeshRenderer(
"rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh = addObject(new Qtk::MeshRenderer("rgbTriangleElementsTest",
Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(7.0f, 0.0f, 4.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
@@ -379,8 +383,8 @@ void QtkScene::init() {
mesh->reallocateTexCoords(mesh->getTexCoords());
// Test drawing triangle with glDrawElements with texture coordinates
mesh = addObject(new Qtk::MeshRenderer(
"testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh = addObject(new Qtk::MeshRenderer("testTriangleElementsUV",
Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(-2.5f, 0.0f, -1.0f);
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
mesh->setTexture(":/textures/crate.png");
@@ -388,20 +392,20 @@ void QtkScene::init() {
mesh->reallocateTexCoords(mesh->getTexCoords());
}
void QtkScene::draw() {
void QtkScene::draw()
{
// WARNING: We must call the base class draw() function first.
// + This will handle rendering core scene components like the Skybox.
Scene::draw();
mTestPhong->bindShaders();
mTestPhong->setUniform(
"uModelInverseTransposed",
mTestPhong->getTransform().toMatrix().normalMatrix());
mTestPhong->setUniform("uModelInverseTransposed",
mTestPhong->getTransform().toMatrix().normalMatrix());
mTestPhong->setUniform(
"uLightPosition",
MeshRenderer::getInstance("phongLight")->getTransform().getTranslation());
mTestPhong->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestPhong->setUniform("uCameraPosition",
QtkScene::getCamera().getTransform().getTranslation());
mTestPhong->releaseShaders();
mTestPhong->draw();
@@ -415,10 +419,10 @@ void QtkScene::draw() {
mTestDiffuse->setUniform(
"uModelInverseTransposed",
mTestDiffuse->getTransform().toMatrix().normalMatrix());
mTestDiffuse->setUniform(
"uLightPosition", MeshRenderer::getInstance("diffuseLight")
->getTransform()
.getTranslation());
mTestDiffuse->setUniform("uLightPosition",
MeshRenderer::getInstance("diffuseLight")
->getTransform()
.getTranslation());
mTestDiffuse->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestDiffuse->releaseShaders();
@@ -428,59 +432,60 @@ void QtkScene::draw() {
mTestSpecular->setUniform(
"uModelInverseTransposed",
mTestSpecular->getTransform().toMatrix().normalMatrix());
mTestSpecular->setUniform(
"uLightPosition", MeshRenderer::getInstance("specularLight")
->getTransform()
.getTranslation());
mTestSpecular->setUniform("uLightPosition",
MeshRenderer::getInstance("specularLight")
->getTransform()
.getTranslation());
mTestSpecular->setUniform(
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
mTestSpecular->releaseShaders();
mTestSpecular->draw();
}
void QtkScene::update() {
void QtkScene::update()
{
auto mySpartan = Model::getInstance("My spartan");
mySpartan->getTransform().rotate(0.0f, 1.0f, 0.0f, 0.75f);
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto myCube = MeshRenderer::getInstance("My cube");
myCube->getTransform().rotate(-0.0f, 1.0f, 0.0f, 0.75f);
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());
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.0f, 1.0f, 0.0f, 0.75f);
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());
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.0f, 1.0f, 0.0f, 0.75f);
spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto phong = MeshRenderer::getInstance("testPhong");
phong->getTransform().rotate(1.0f, 0.5f, 0.0f, 0.75f);
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());
phong->setUniform("uCameraPosition",
QtkScene::getCamera().getTransform().getTranslation());
posMatrix = phong->getTransform().toMatrix();
phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
phong->setUniform("uMVP.model", posMatrix);
@@ -489,27 +494,27 @@ void QtkScene::update() {
phong->releaseShaders();
// Rotate lighting example cubes
mTestPhong->getTransform().rotate(0.5f, 0.3f, 0.2f, 0.75f);
mTestPhong->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
MeshRenderer::getInstance("noLight")->getTransform().rotate(
0.5f, 0.3f, 0.2f, 0.75f);
mTestAmbient->getTransform().rotate(0.5f, 0.3f, 0.2f, 0.75f);
mTestDiffuse->getTransform().rotate(0.5f, 0.3f, 0.2f, 0.75f);
mTestSpecular->getTransform().rotate(0.5f, 0.3f, 0.2f, 0.75f);
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.2f, 0.4f, 0.6f, 0.75f);
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
// Pitch forward and roll sideways
MeshRenderer::getInstance("leftTriangle")
->getTransform()
.rotate(1.0f, 0.0f, 0.0f, 0.75f);
.rotate(0.75f, 1.0f, 0.0f, 0.0f);
MeshRenderer::getInstance("rightTriangle")
->getTransform()
.rotate(0.0f, 0.0f, 1.0f, 0.75f);
.rotate(0.75f, 0.0f, 0.0f, 1.0f);
// Move between two positions over time
static float translateX = 0.025f;
@@ -518,7 +523,7 @@ void QtkScene::update() {
->getTransform()
.getTranslation()
.x();
if(posX < limit || posX > limit + 4.0f) {
if (posX < limit || posX > limit + 4.0f) {
translateX = -translateX;
}
MeshRenderer::getInstance("topTriangle")
@@ -530,15 +535,15 @@ void QtkScene::update() {
// And lets rotate the triangles in two directions at once
MeshRenderer::getInstance("topTriangle")
->getTransform()
.rotate(0.2f, 0.0f, 0.4f, 0.75f);
.rotate(0.75f, 0.2f, 0.0f, 0.4f);
MeshRenderer::getInstance("bottomTriangle")
->getTransform()
.rotate(0.0f, 0.2f, 0.4f, 0.75f);
.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.2f, 0.4f, 0.6f, 0.75f);
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
}

View File

@@ -29,13 +29,14 @@
*
* To create your own Scene from scratch see Qtk::Scene.
*/
class QtkScene : public Qtk::SceneInterface {
class QtkScene : public Qtk::Scene
{
public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
QtkScene(Qtk::Scene * scene);
QtkScene();
~QtkScene();

View File

@@ -10,12 +10,16 @@
#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 "qtk/qtkmessagelogger.h"
#include "qtkmainwindow.h"
#include "qtkwidget.h"
@@ -28,12 +32,14 @@ using namespace Qtk;
QtkWidget::QtkWidget(QWidget * parent) : QtkWidget(parent, "QtkWidget") {}
QtkWidget::QtkWidget(QWidget * parent, const QString & name) :
QtkWidget(parent, name, Q_NULLPTR) {}
QtkWidget(parent, name, Q_NULLPTR)
{
}
QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
QOpenGLWidget(parent), mDebugLogger(new QOpenGLDebugLogger(this)),
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR),
mLogger(new Qtk::Logger(this)) {
QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR),
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR)
{
setAcceptDrops(true);
setScene(scene);
setObjectName(name);
@@ -51,7 +57,8 @@ QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
setFocusPolicy(Qt::ClickFocus);
}
QtkWidget::~QtkWidget() {
QtkWidget::~QtkWidget()
{
makeCurrent();
teardownGL();
}
@@ -60,7 +67,8 @@ QtkWidget::~QtkWidget() {
* Public Methods
******************************************************************************/
QAction * QtkWidget::getActionToggleConsole() {
QAction * QtkWidget::getActionToggleConsole()
{
auto action = new QAction(mScene->getSceneName() + " debug console");
action->setCheckable(true);
action->setChecked(mConsoleActive);
@@ -69,27 +77,22 @@ QAction * QtkWidget::getActionToggleConsole() {
return action;
}
void QtkWidget::initializeGL() {
void QtkWidget::initializeGL()
{
initializeOpenGLFunctions();
// Connect the frameSwapped signal to call the update() function
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
toggleConsole();
// Initialize OpenGL debug context
if(mDebugLogger->initialize()) {
connect(
mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), mLogger,
SLOT(log(QOpenGLDebugMessage)));
// connect(
// Qtk::QtkMessageLogger::get(),
// &Qtk::QtkMessageLogger::messageLogged, mLogger,
// &Qtk::Logger::parseError);
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();
QString msg;
QTextStream stream(&msg);
stream << "Logging started on GL_DEBUG Debug Logger: " << mDebugLogger;
mDebugLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(
stream.string()->toStdString().c_str()));
}
printContextInformation();
@@ -105,38 +108,43 @@ void QtkWidget::initializeGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void QtkWidget::resizeGL(int width, int height) {
void QtkWidget::resizeGL(int width, int height)
{
Scene::getProjectionMatrix().setToIdentity();
Scene::getProjectionMatrix().perspective(
45.0f, float(width) / float(height), 0.1f, 1000.0f);
}
void QtkWidget::paintGL() {
void QtkWidget::paintGL()
{
// Clear buffers and draw the scene if it is valid.
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
if(mScene != Q_NULLPTR) {
if (mScene != Q_NULLPTR) {
mScene->draw();
}
}
void QtkWidget::setScene(Scene * scene) {
if(mScene != Q_NULLPTR) {
void QtkWidget::setScene(Scene * scene)
{
if (mScene != Q_NULLPTR) {
delete mScene;
connect(
scene, &Scene::sceneUpdated, MainWindow::getMainWindow(),
&MainWindow::refreshScene);
connect(scene,
&Scene::sceneUpdated,
MainWindow::getMainWindow(),
&MainWindow::refreshScene);
}
mScene = scene;
if(mScene != Q_NULLPTR) {
if (mScene != Q_NULLPTR) {
mConsole->setTitle(mScene->getSceneName());
} else {
mConsole->setTitle("Null Scene");
}
}
void QtkWidget::toggleConsole() {
if(mConsoleActive) {
void QtkWidget::toggleConsole()
{
if (mConsoleActive) {
mConsole->setHidden(true);
mConsoleActive = false;
} else {
@@ -151,17 +159,19 @@ void QtkWidget::toggleConsole() {
* Protected Methods
******************************************************************************/
void QtkWidget::dragEnterEvent(QDragEnterEvent * event) {
if(event->mimeData()->hasFormat("text/plain")) {
void QtkWidget::dragEnterEvent(QDragEnterEvent * event)
{
if (event->mimeData()->hasFormat("text/plain")) {
event->acceptProposedAction();
}
}
void QtkWidget::dropEvent(QDropEvent * event) {
void QtkWidget::dropEvent(QDropEvent * event)
{
mConsole->sendLog(event->mimeData()->text());
auto urls = event->mimeData()->urls();
if(!urls.isEmpty()) {
if(urls.size() > 1) {
if (!urls.isEmpty()) {
if (urls.size() > 1) {
qDebug() << "Cannot accept drop of multiple files.";
event->ignore();
return;
@@ -169,7 +179,7 @@ void QtkWidget::dropEvent(QDropEvent * event) {
// TODO: Support other object types.
auto url = urls.front();
if(url.fileName().endsWith(".obj")) {
if (url.fileName().endsWith(".obj")) {
mScene->loadModel(url);
event->acceptProposedAction();
} else {
@@ -179,8 +189,9 @@ void QtkWidget::dropEvent(QDropEvent * event) {
}
}
void QtkWidget::keyPressEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
void QtkWidget::keyPressEvent(QKeyEvent * event)
{
if (event->isAutoRepeat()) {
// Do not repeat input while a key is held down
event->ignore();
} else {
@@ -188,78 +199,154 @@ void QtkWidget::keyPressEvent(QKeyEvent * event) {
}
}
void QtkWidget::keyReleaseEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
void QtkWidget::keyReleaseEvent(QKeyEvent * event)
{
if (event->isAutoRepeat()) {
event->ignore();
} else {
Input::registerKeyRelease(event->key());
}
}
void QtkWidget::mousePressEvent(QMouseEvent * event) {
void QtkWidget::mousePressEvent(QMouseEvent * event)
{
Input::registerMousePress(event->button());
}
void QtkWidget::mouseReleaseEvent(QMouseEvent * event) {
void QtkWidget::mouseReleaseEvent(QMouseEvent * event)
{
Input::registerMouseRelease(event->button());
}
void QtkWidget::update() {
void QtkWidget::update()
{
updateCameraInput();
if(mScene != Q_NULLPTR) {
if (mScene != Q_NULLPTR) {
mScene->update();
}
QWidget::update();
}
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg)
{
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::teardownGL()
{ /* Nothing to teardown yet... */
}
void QtkWidget::updateCameraInput() {
void QtkWidget::updateCameraInput()
{
Input::update();
// Camera Transformation
if(Input::buttonPressed(Qt::LeftButton)
|| Input::buttonPressed(Qt::RightButton)) {
if (Input::buttonPressed(Qt::LeftButton)
|| Input::buttonPressed(Qt::RightButton)) {
static const float transSpeed = 0.1f;
static const float rotSpeed = 0.5f;
// Handle rotations
Scene::getCamera().rotate(QQuaternion::fromAxisAndAngle(
Camera3D::LocalUp, -rotSpeed * Input::mouseDelta().x()));
Scene::getCamera().rotate(QQuaternion::fromAxisAndAngle(
Scene::getCamera().getRight(), -rotSpeed * Input::mouseDelta().y()));
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)) {
if (Input::keyPressed(Qt::Key_W)) {
translation += Scene::getCamera().getForward();
}
if(Input::keyPressed(Qt::Key_S)) {
if (Input::keyPressed(Qt::Key_S)) {
translation -= Scene::getCamera().getForward();
}
if(Input::keyPressed(Qt::Key_A)) {
if (Input::keyPressed(Qt::Key_A)) {
translation -= Scene::getCamera().getRight();
}
if(Input::keyPressed(Qt::Key_D)) {
if (Input::keyPressed(Qt::Key_D)) {
translation += Scene::getCamera().getRight();
}
if(Input::keyPressed(Qt::Key_Q)) {
if (Input::keyPressed(Qt::Key_Q)) {
translation -= Scene::getCamera().getUp() / 2.0f;
}
if(Input::keyPressed(Qt::Key_E)) {
if (Input::keyPressed(Qt::Key_E)) {
translation += Scene::getCamera().getUp() / 2.0f;
}
Scene::getCamera().translate(transSpeed * translation);
Scene::getCamera().getTransform().translate(transSpeed * translation);
}
}
void QtkWidget::printContextInformation() {
void QtkWidget::printContextInformation()
{
QString glType;
QString glVersion;
QString glProfile;
@@ -278,7 +365,7 @@ void QtkWidget::printContextInformation() {
case QSurfaceFormat::c: \
glProfile = #c; \
break
switch(format().profile()) {
switch (format().profile()) {
CASE(NoProfile);
CASE(CoreProfile);
CASE(CompatibilityProfile);
@@ -288,5 +375,6 @@ void QtkWidget::printContextInformation() {
auto message = QString(glType) + glVersion + "(" + glProfile + ")"
+ "\nOpenGL Vendor: " + glVendor
+ "\nRendering Device: " + glRenderer;
emit sendLog("(OpenGL) " + message.replace("\n", "\n(OpenGL) "), Status);
qDebug() << qPrintable(message);
sendLog("(OpenGL) " + message.replace("\n", "\n(OpenGL) "), Status);
}

View File

@@ -19,9 +19,9 @@
#include <qtk/qtkapi.h>
#include <qtk/scene.h>
#include "logger.h"
namespace Qtk {
namespace Qtk
{
class DebugConsole;
/**
@@ -30,7 +30,8 @@ namespace Qtk {
* This object has a Scene attached which manages the objects to render.
* Client input is passed through this widget to control the camera view.
*/
class QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
class QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT;
public:
@@ -104,12 +105,11 @@ namespace Qtk {
/**
* @return Pointer to the QOpenGLDebugLogger attached to this widget.
*/
inline QOpenGLDebugLogger * getOpenGLDebugLogger() {
inline QOpenGLDebugLogger * getOpenGLDebugLogger()
{
return mDebugLogger;
}
inline Logger * getLogger() { return mLogger; }
/*************************************************************************
* Setters
************************************************************************/
@@ -132,11 +132,11 @@ namespace Qtk {
* @param message The message to log.
* @param context The context of the log message.
*/
void sendLog(const QString & message, Qtk::DebugContext context = Status);
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);
void objectFocusChanged(QString objectName);
protected:
/*************************************************************************
@@ -181,7 +181,7 @@ namespace Qtk {
*
* @param msg The message logged.
*/
// void messageLogged(const QOpenGLDebugMessage & msg);
void messageLogged(const QOpenGLDebugMessage & msg);
private:
/*************************************************************************
@@ -207,7 +207,6 @@ namespace Qtk {
* Private Members
************************************************************************/
Qtk::Logger * mLogger;
QOpenGLDebugLogger * mDebugLogger;
Qtk::Scene * mScene;
Qtk::DebugConsole * mConsole;

View File

@@ -16,34 +16,39 @@
using namespace Qtk;
ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), ui(new Ui::ToolBox) {
ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), ui(new Ui::ToolBox)
{
ui->setupUi(this);
setMinimumWidth(350);
}
void ToolBox::updateFocus(const QString & name) {
void ToolBox::updateFocus(const QString & name)
{
auto object =
MainWindow::getMainWindow()->getQtkWidget()->getScene()->getObject(name);
if(object != Q_NULLPTR) {
if (object != Q_NULLPTR) {
removePages();
createPageProperties(object);
createPageShader(object);
}
}
ToolBox::~ToolBox() {
ToolBox::~ToolBox()
{
delete ui;
}
void ToolBox::removePages() {
void ToolBox::removePages()
{
// Remove all existing pages.
for(size_t i = 0; i < ui->toolBox->count(); i++) {
for (size_t i = 0; i < ui->toolBox->count(); i++) {
delete ui->toolBox->widget(i);
ui->toolBox->removeItem(i);
}
}
void ToolBox::createPageProperties(const Object * object) {
void ToolBox::createPageProperties(const Object * object)
{
auto transform = object->getTransform();
auto type = object->getType();
auto * widget = new QWidget;
@@ -51,17 +56,16 @@ void ToolBox::createPageProperties(const Object * object) {
ui->toolBox->setCurrentWidget(widget);
auto * layout = new QFormLayout;
layout->addRow(
new QLabel(tr("Name:")), new QLabel(object->getName().c_str()));
layout->addRow(new QLabel(tr("Name:")),
new QLabel(object->getName().c_str()));
layout->addRow(
new QLabel(tr("Type:")),
new QLabel(type == Object::Type::QTK_MESH ? "Mesh" : "Model"));
layout->addRow(new QLabel(tr("Type:")),
new QLabel(type == Object::Type::QTK_MESH ? "Mesh" : "Model"));
auto rowLayout = new QHBoxLayout;
rowLayout->addWidget(new QLabel(tr("Translation:")));
int minWidth = 75;
for(size_t i = 0; i < 3; i++) {
for (size_t i = 0; i < 3; i++) {
auto spinBox = new QDoubleSpinBox;
spinBox->setMinimum(std::numeric_limits<double>::lowest());
spinBox->setSingleStep(0.1);
@@ -69,25 +73,28 @@ void ToolBox::createPageProperties(const Object * object) {
spinBox->setFixedWidth(minWidth);
rowLayout->addWidget(spinBox);
if(i == 0) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object,
&Object::setTranslationX);
} else if(i == 1) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object,
&Object::setTranslationY);
} else if(i == 2) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object,
&Object::setTranslationZ);
if (i == 0) {
connect(spinBox,
&QDoubleSpinBox::valueChanged,
object,
&Object::setTranslationX);
} else if (i == 1) {
connect(spinBox,
&QDoubleSpinBox::valueChanged,
object,
&Object::setTranslationY);
} else if (i == 2) {
connect(spinBox,
&QDoubleSpinBox::valueChanged,
object,
&Object::setTranslationZ);
}
}
layout->addRow(rowLayout);
rowLayout = new QHBoxLayout;
rowLayout->addWidget(new QLabel(tr("Scale:")));
for(size_t i = 0; i < 3; i++) {
for (size_t i = 0; i < 3; i++) {
auto spinBox = new QDoubleSpinBox;
spinBox->setMinimum(std::numeric_limits<double>::lowest());
spinBox->setSingleStep(0.1);
@@ -95,13 +102,13 @@ void ToolBox::createPageProperties(const Object * object) {
spinBox->setFixedWidth(minWidth);
rowLayout->addWidget(spinBox);
if(i == 0) {
if (i == 0) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleX);
} else if(i == 1) {
} else if (i == 1) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleY);
} else if(i == 2) {
} else if (i == 2) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleZ);
}
@@ -110,7 +117,8 @@ void ToolBox::createPageProperties(const Object * object) {
widget->setLayout(layout);
}
void ToolBox::createPageShader(const Object * object) {
void ToolBox::createPageShader(const Object * object)
{
// Shaders page.
auto widget = new QWidget;
ui->toolBox->addItem(widget, "Shaders");
@@ -123,7 +131,7 @@ void ToolBox::createPageShader(const Object * object) {
auto shaderView = new QTextEdit;
shaderView->setReadOnly(true);
auto vertexFile = QFile(object->getVertexShader().c_str());
if(vertexFile.exists()) {
if (vertexFile.exists()) {
vertexFile.open(QIODeviceBase::ReadOnly);
shaderView->setText(vertexFile.readAll());
vertexFile.close();
@@ -138,7 +146,7 @@ void ToolBox::createPageShader(const Object * object) {
shaderView = new QTextEdit;
shaderView->setReadOnly(true);
auto fragmentfile = QFile(object->getFragmentShader().c_str());
if(fragmentfile.exists()) {
if (fragmentfile.exists()) {
fragmentfile.open(QIODeviceBase::ReadOnly);
shaderView->setText(fragmentfile.readAll());
fragmentfile.close();

View File

@@ -18,12 +18,15 @@
#include "qtk/scene.h"
namespace Ui {
namespace Ui
{
class ToolBox;
}
namespace Qtk {
class ToolBox : public QDockWidget {
namespace Qtk
{
class ToolBox : public QDockWidget
{
Q_OBJECT
public:

View File

@@ -16,14 +16,17 @@
******************************************************************************/
Qtk::TreeView::TreeView(QWidget * parent) :
QDockWidget(parent), ui(new Ui::TreeView) {
QDockWidget(parent), ui(new Ui::TreeView)
{
ui->setupUi(this);
connect(
ui->treeWidget, &QTreeWidget::itemDoubleClicked, this,
&TreeView::itemFocus);
connect(ui->treeWidget,
&QTreeWidget::itemDoubleClicked,
this,
&TreeView::itemFocus);
}
Qtk::TreeView::~TreeView() {
Qtk::TreeView::~TreeView()
{
delete ui;
}
@@ -31,39 +34,41 @@ Qtk::TreeView::~TreeView() {
* Public Methods
******************************************************************************/
void Qtk::TreeView::updateView(const Qtk::Scene * scene) {
void Qtk::TreeView::updateView(const Qtk::Scene * scene)
{
ui->treeWidget->clear();
ui->treeWidget->setColumnCount(1);
mSceneName = scene->getSceneName();
auto objects = scene->getObjects();
for(const auto & object : objects) {
auto item =
new QTreeWidgetItem(QStringList(QString(object->getName().c_str())));
ui->treeWidget->insertTopLevelItem(0, item);
for (const auto & object : objects) {
QStringList list(QStringList(QString(object->getName().c_str())));
ui->treeWidget->insertTopLevelItem(0, new QTreeWidgetItem(list));
}
}
void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) {
QString name = item->text(column);
void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column)
{
const QString & name = item->text(column);
auto scene = MainWindow::getMainWindow()->getQtkWidget()->getScene();
auto object = scene->getObject(name);
Transform3D * objectTransform;
// If the object is a mesh or model, focus the camera on it.
if(object == Q_NULLPTR) {
if (object == Q_NULLPTR) {
qDebug() << "Attempt to get non-existing object with name '" << name
<< "'\n";
} else if(object->getType() == Object::QTK_MESH) {
objectTransform = &dynamic_cast<MeshRenderer *>(object)->getTransform();
} else if(object->getType() == Object::QTK_MODEL) {
objectTransform = &dynamic_cast<Model *>(object)->getTransform();
return;
}
auto focusScale = objectTransform->getScale();
const Transform3D& objectTransform = object->getTransform();
auto & camera_transform = Qtk::Scene::getCamera().getTransform();
auto focusScale = objectTransform.getScale();
float width = focusScale.x() / 2.0f;
float height = focusScale.y() / 2.0f;
QVector3D pos = objectTransform->getTranslation();
QVector3D pos = objectTransform.getTranslation();
// pos.setX(pos.x() + width);
pos.setY(pos.y() + height);
Qtk::Scene::getCamera().setTranslation(pos);
Qtk::Scene::getCamera().translate({0.0f, 0.0f, 3.0f});
camera_transform.setTranslation(pos);
camera_transform.translate(0.0f, 0.0f, 3.0f);
// Emit signal from qtk widget for new object focus. Triggers GUI updates.
emit MainWindow::getMainWindow()->getQtkWidget()->objectFocusChanged(name);
emit MainWindow::getMainWindow() -> getQtkWidget()->objectFocusChanged(name);
}

View File

@@ -17,12 +17,15 @@
#include <qtk/scene.h>
#include <QTreeWidgetItem>
namespace Ui {
namespace Ui
{
class TreeView;
}
namespace Qtk {
class TreeView : public QDockWidget {
namespace Qtk
{
class TreeView : public QDockWidget
{
Q_OBJECT
public:

View File

@@ -19,12 +19,15 @@
* Constructors, Destructors
******************************************************************************/
WidgetPlugin::WidgetPlugin(
QString group, QString class_name, QString include,
WidgetPlugin::Factory factory) :
m_group(std::move(group)),
m_className(std::move(class_name)), m_includeFile(std::move(include)),
m_factory(std::move(factory)), m_objectName(class_name) {}
WidgetPlugin::WidgetPlugin(QString group,
QString class_name,
QString include,
WidgetPlugin::Factory factory) :
m_group(std::move(group)), m_className(std::move(class_name)),
m_includeFile(std::move(include)), m_factory(std::move(factory)),
m_objectName(m_className)
{
}
WidgetPlugin::WidgetPlugin(QObject * parent) : QObject(parent) {}
@@ -32,50 +35,61 @@ WidgetPlugin::WidgetPlugin(QObject * parent) : QObject(parent) {}
* Public Methods
******************************************************************************/
QString WidgetPlugin::group() const {
QString WidgetPlugin::group() const
{
return m_group;
}
QString WidgetPlugin::name() const {
QString WidgetPlugin::name() const
{
return m_className;
}
QString WidgetPlugin::includeFile() const {
QString WidgetPlugin::includeFile() const
{
return m_includeFile;
}
QWidget * WidgetPlugin::createWidget(QWidget * parent) {
QWidget * WidgetPlugin::createWidget(QWidget * parent)
{
return m_factory(parent);
}
QString WidgetPlugin::toolTip() const {
QString WidgetPlugin::toolTip() const
{
return QStringLiteral("A custom widget tool tip.");
}
QString WidgetPlugin::whatsThis() const {
QString WidgetPlugin::whatsThis() const
{
return QStringLiteral("Custom widget what's this?");
}
QIcon WidgetPlugin::icon() const {
QIcon WidgetPlugin::icon() const
{
return Qtk::getIcon();
}
bool WidgetPlugin::isContainer() const {
bool WidgetPlugin::isContainer() const
{
return true;
}
bool WidgetPlugin::isInitialized() const {
bool WidgetPlugin::isInitialized() const
{
return m_initialized;
}
void WidgetPlugin::initialize(QDesignerFormEditorInterface *) {
if(m_initialized) {
void WidgetPlugin::initialize(QDesignerFormEditorInterface *)
{
if (m_initialized) {
return;
}
m_initialized = true;
}
QString WidgetPlugin::domXml() const {
QString WidgetPlugin::domXml() const
{
return
"<ui language=\"c++\">\n"
" <widget class=\"" + m_className + "\" name=\"" + m_objectName + "\">\n"

View File

@@ -15,7 +15,8 @@
class QDESIGNER_WIDGET_EXPORT WidgetPlugin :
public QObject,
public QDesignerCustomWidgetInterface {
public QDesignerCustomWidgetInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)
@@ -26,8 +27,10 @@ class QDESIGNER_WIDGET_EXPORT WidgetPlugin :
* Contructors / Destructors
**************************************************************************/
WidgetPlugin(
QString group, QString class_name, QString include, Factory factory);
WidgetPlugin(QString group,
QString class_name,
QString include,
Factory factory);
explicit WidgetPlugin(QObject * parent = nullptr);

View File

@@ -19,16 +19,23 @@
******************************************************************************/
WidgetPluginCollection::WidgetPluginCollection(QObject * parent) :
QObject(parent), m_collectionName("Qtk Widget Collection") {
QObject(parent), m_collectionName("Qtk Widget Collection")
{
m_collection = {
new WidgetPlugin(
m_collectionName, "Qtk::QtkWidget", "qtkwidget.h",
m_collectionName,
"Qtk::QtkWidget",
"qtkwidget.h",
[](QWidget * parent) { return new Qtk::QtkWidget(parent); }),
new WidgetPlugin(
m_collectionName, "Qtk::TreeView", "treeview.h",
m_collectionName,
"Qtk::TreeView",
"treeview.h",
[](QWidget * parent) { return new Qtk::TreeView(parent); }),
new WidgetPlugin(
m_collectionName, "Qtk::ToolBox", "toolbox.h",
m_collectionName,
"Qtk::ToolBox",
"toolbox.h",
[](QWidget * parent) { return new Qtk::ToolBox(parent); }),
};
}
@@ -38,6 +45,7 @@ WidgetPluginCollection::WidgetPluginCollection(QObject * parent) :
******************************************************************************/
QList<QDesignerCustomWidgetInterface *> WidgetPluginCollection::customWidgets()
const {
const
{
return m_collection;
}

View File

@@ -14,7 +14,8 @@
class WidgetPluginCollection :
public QObject,
public QDesignerCustomWidgetCollectionInterface {
public QDesignerCustomWidgetCollectionInterface
{
Q_OBJECT
// Since we're exporting a collection, this is the only plugin metadata
// needed. We don't need this for-each widget in the collection.
@@ -36,7 +37,8 @@ class WidgetPluginCollection :
/**
* @return QList of all custom widgets pointers.
*/
[[nodiscard]] QList<QDesignerCustomWidgetInterface *> customWidgets() const;
[[nodiscard]] QList<QDesignerCustomWidgetInterface *> customWidgets()
const override;
private:
/***************************************************************************

View File

@@ -24,8 +24,6 @@ set(
skybox.h
texture.h
transform3D.h
qtkmessagelogger.h
../app/logger.h
)
set(
@@ -43,8 +41,6 @@ set(
skybox.cpp
texture.cpp
transform3D.cpp
qtkmessagelogger.cpp
../app/logger.cpp
)
qt6_add_big_resources(QTK_LIBRARY_SOURCES "${QTK_RESOURCES}/resources.qrc")

View File

@@ -22,10 +22,39 @@ const QVector3D Camera3D::LocalRight(1.0f, 0.0f, 0.0f);
* Public Methods
******************************************************************************/
const QMatrix4x4 & Camera3D::toMatrix() {
const QMatrix4x4 & Camera3D::toMatrix()
{
mWorld.setToIdentity();
// Qt6 renamed QMatrix4x4::conjugate() to conjugated()
mWorld.rotate(mTransform.getRotation().conjugated());
mWorld.translate(-mTransform.getTranslation());
return mWorld;
}
/*******************************************************************************
* Qt Streams
******************************************************************************/
QDataStream & operator<<(QDataStream & out, Camera3D & transform)
{
out << transform.getTransform();
return out;
}
QDataStream & operator>>(QDataStream & in, Camera3D & transform)
{
in >> transform.getTransform();
return in;
}
QDebug operator<<(QDebug dbg, const Camera3D & transform)
{
dbg << "Camera3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
return dbg;
}

View File

@@ -14,8 +14,10 @@
#include "qtkapi.h"
#include "transform3D.h"
namespace Qtk {
class QTKAPI Camera3D {
namespace Qtk
{
class QTKAPI Camera3D
{
public:
/*************************************************************************
* Static Public Constants
@@ -32,42 +34,45 @@ namespace Qtk {
/**
* @return Transform3D associated with this camera.
*/
[[nodiscard]] inline const Transform3D & getTransform() const {
return mTransform;
}
inline Transform3D & getTransform() { return mTransform; }
/**
* @return Current translation of the camera as a QVector3D.
*/
[[nodiscard]] inline const QVector3D & getTranslation() const {
[[nodiscard]] inline const QVector3D & getTranslation() const
{
return mTransform.getTranslation();
}
/**
* @return Current rotation of this camera as a QQuaternion.
*/
[[nodiscard]] inline const QQuaternion & getRotation() const {
[[nodiscard]] inline const QQuaternion & getRotation() const
{
return mTransform.getRotation();
}
/**
* @return QVector3D for the forward vector of the camera.
*/
[[nodiscard]] inline QVector3D getForward() const {
[[nodiscard]] inline QVector3D getForward() const
{
return mTransform.getRotation().rotatedVector(LocalForward);
}
/**
* @return QVector3D for the right vector of the camera.
*/
[[nodiscard]] inline QVector3D getRight() const {
[[nodiscard]] inline QVector3D getRight() const
{
return mTransform.getRotation().rotatedVector(LocalRight);
}
/**
* @return QVector3D for the up vector of the camera.
*/
[[nodiscard]] inline QVector3D getUp() const {
[[nodiscard]] inline QVector3D getUp() const
{
return mTransform.getRotation().rotatedVector(LocalUp);
}
@@ -80,85 +85,33 @@ namespace Qtk {
*/
const QMatrix4x4 & toMatrix();
/**
* Set the translation for this camera.
* TODO: Replace these methods by inheriting from a base class.
*
* @param translation QVector3D for the new translation.
*/
inline void setTranslation(const QVector3D & translation) {
mTransform.setTranslation(translation);
}
/**
* Set the rotation for this camera.
*
* @param rotation QQuaternion for the new rotation.
*/
inline void setRotation(const QQuaternion & rotation) {
mTransform.setRotation(rotation);
}
/**
* Sets a rotation upon an axis represented by the 3D vector (x, y, z)
*
* @param ax X axis to set angle for.
* @param ay Y axis to set angle for.
* @param az Z axis to set angle for.
* @param angle Angle to set rotation.
*/
inline void setRotation(float ax, float ay, float az, float angle) {
mTransform.setRotation(ax, ay, az, angle);
}
/**
* Translate the camera by the given position.
*
* @param position QVector3D for the position to translate by.
*/
inline void translate(const QVector3D & position) {
mTransform.translate(position);
}
/**
* Rotate the camera by the given rotation.
*
* @param rotation QQaternion for the rotation to apply.
*/
inline void rotate(const QQuaternion & rotation) {
mTransform.rotate(rotation);
}
/**
* Sets a rotation upon an axis represented by the 3D vector (x, y, z)
*
* @param ax X axis to set angle for.
* @param ay Y axis to set angle for.
* @param az Z axis to set angle for.
* @param angle Angle to set rotation.
*/
inline void rotate(float ax, float ay, float az, float angle) {
mTransform.rotate(ax, ay, az, angle);
}
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
friend QDataStream & operator<<(
QDataStream & out, const Camera3D & camera);
friend QDataStream & operator>>(QDataStream & in, Camera3D & camera);
#endif
};
// 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);

View File

@@ -33,10 +33,13 @@ template <typename T> struct InputInstance : std::pair<T, Input::InputState> {
inline InputInstance(T value) : base_class(value, Input::InputInvalid) {}
inline InputInstance(T value, Input::InputState state) :
base_class(value, state) {}
base_class(value, state)
{
}
// Allows use of std::find to search for a key's InputInstance
inline bool operator==(const InputInstance & rhs) const {
inline bool operator==(const InputInstance & rhs) const
{
return this->first == rhs.first;
}
};
@@ -67,7 +70,8 @@ static QPoint sg_mouseDelta;
* @param value The key to search for.
* @return Iterator to the found element or the end iterator if not found.
*/
static inline KeyContainer::iterator FindKey(Qt::Key value) {
static inline KeyContainer::iterator FindKey(Qt::Key value)
{
return std::find(sg_keyInstances.begin(), sg_keyInstances.end(), value);
}
@@ -77,7 +81,8 @@ static inline KeyContainer::iterator FindKey(Qt::Key value) {
* @param value The mouse button to search for.
* @return Iterator to the found element or the end iterator if not found.
*/
static inline ButtonContainer::iterator FindButton(Qt::MouseButton value) {
static inline ButtonContainer::iterator FindButton(Qt::MouseButton value)
{
return std::find(sg_buttonInstances.begin(), sg_buttonInstances.end(), value);
}
@@ -89,7 +94,8 @@ static inline ButtonContainer::iterator FindButton(Qt::MouseButton value) {
* @return True if the InputInstance is in the released state.
*/
template <typename TPair>
static inline bool CheckReleased(const TPair & instance) {
static inline bool CheckReleased(const TPair & instance)
{
return instance.second == Input::InputReleased;
}
@@ -99,8 +105,9 @@ static inline bool CheckReleased(const TPair & instance) {
* @tparam TPair KeyInstance or ButtonInstance.
* @param instance The InputInstance to update.
*/
template <typename TPair> static inline void UpdateStates(TPair & instance) {
switch(instance.second) {
template <typename TPair> static inline void UpdateStates(TPair & instance)
{
switch (instance.second) {
case Input::InputRegistered:
instance.second = Input::InputTriggered;
break;
@@ -121,7 +128,8 @@ template <typename TPair> static inline void UpdateStates(TPair & instance) {
* @tparam Container The type of container, KeyContainer or ButtonContainer.
* @param container The InputInstance container to update.
*/
template <typename Container> static inline void Update(Container & container) {
template <typename Container> static inline void Update(Container & container)
{
typedef typename Container::iterator Iter;
typedef typename Container::value_type TPair;
@@ -138,7 +146,8 @@ template <typename Container> static inline void Update(Container & container) {
* Static Public Methods
******************************************************************************/
void Input::update() {
void Input::update()
{
// Update Mouse Delta
sg_mousePrevPosition = sg_mouseCurrPosition;
sg_mouseCurrPosition = QCursor::pos();
@@ -149,53 +158,62 @@ void Input::update() {
Update(sg_keyInstances);
}
void Input::registerKeyPress(int k) {
void Input::registerKeyPress(int k)
{
auto it = FindKey((Qt::Key)k);
if(it == sg_keyInstances.end()) {
sg_keyInstances.push_back(KeyInstance((Qt::Key)k, InputRegistered));
if (it == sg_keyInstances.end()) {
sg_keyInstances.emplace_back((Qt::Key)k, InputRegistered);
}
}
void Input::registerKeyRelease(int k) {
void Input::registerKeyRelease(int k)
{
auto it = FindKey((Qt::Key)k);
if(it != sg_keyInstances.end()) {
if (it != sg_keyInstances.end()) {
it->second = InputUnregistered;
}
}
void Input::registerMousePress(Qt::MouseButton btn) {
auto it = FindButton(btn);
if(it == sg_buttonInstances.end()) {
sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered));
void Input::registerMousePress(Qt::MouseButton button)
{
auto it = FindButton(button);
if (it == sg_buttonInstances.end()) {
sg_buttonInstances.emplace_back(button, InputRegistered);
}
}
void Input::registerMouseRelease(Qt::MouseButton btn) {
auto it = FindButton(btn);
if(it != sg_buttonInstances.end()) {
void Input::registerMouseRelease(Qt::MouseButton button)
{
auto it = FindButton(button);
if (it != sg_buttonInstances.end()) {
it->second = InputUnregistered;
}
}
void Input::reset() {
void Input::reset()
{
sg_keyInstances.clear();
sg_buttonInstances.clear();
}
Input::InputState Input::keyState(Qt::Key k) {
Input::InputState Input::keyState(Qt::Key k)
{
auto it = FindKey(k);
return (it != sg_keyInstances.end()) ? it->second : InputInvalid;
}
Input::InputState Input::buttonState(Qt::MouseButton k) {
auto it = FindButton(k);
Input::InputState Input::buttonState(Qt::MouseButton button)
{
auto it = FindButton(button);
return (it != sg_buttonInstances.end()) ? it->second : InputInvalid;
}
QPoint Input::mousePosition() {
QPoint Input::mousePosition()
{
return QCursor::pos();
}
QPoint Input::mouseDelta() {
QPoint Input::mouseDelta()
{
return sg_mouseDelta;
}

View File

@@ -14,8 +14,10 @@
#include "qtkapi.h"
namespace Qtk {
class QTKAPI Input {
namespace Qtk
{
class QTKAPI Input
{
public:
/*************************************************************************
* Typedefs
@@ -84,7 +86,8 @@ namespace Qtk {
* @param key Key to check state.
* @return True if the key is in InputTriggered state.
*/
inline static bool keyTriggered(Qt::Key key) {
inline static bool keyTriggered(Qt::Key key)
{
return keyState(key) == InputTriggered;
}
@@ -92,7 +95,8 @@ namespace Qtk {
* @param key Key to check state.
* @return True if the key is in InputPressed state.
*/
inline static bool keyPressed(Qt::Key key) {
inline static bool keyPressed(Qt::Key key)
{
return keyState(key) == InputPressed;
}
@@ -100,7 +104,8 @@ namespace Qtk {
* @param key Key to check state.
* @return True if the key is in InputReleased state.
*/
inline static bool keyReleased(Qt::Key key) {
inline static bool keyReleased(Qt::Key key)
{
return keyState(key) == InputReleased;
}
@@ -108,7 +113,8 @@ namespace Qtk {
* @param button Mouse button to check state.
* @return True if the key is in InputTriggered state.
*/
inline static bool buttonTriggered(Qt::MouseButton button) {
inline static bool buttonTriggered(Qt::MouseButton button)
{
return buttonState(button) == InputTriggered;
}
@@ -116,7 +122,8 @@ namespace Qtk {
* @param button Mouse button to check state.
* @return True if the key is in InputPressed state.
*/
inline static bool buttonPressed(Qt::MouseButton button) {
inline static bool buttonPressed(Qt::MouseButton button)
{
return buttonState(button) == InputPressed;
}
@@ -124,7 +131,8 @@ namespace Qtk {
* @param button Mouse button to check state.
* @return True if the key is in InputReleased state.
*/
inline static bool buttonReleased(Qt::MouseButton button) {
inline static bool buttonReleased(Qt::MouseButton button)
{
return buttonState(button) == InputReleased;
}

View File

@@ -21,23 +21,30 @@ Qtk::MeshRenderer::MeshManager Qtk::MeshRenderer::sInstances;
* Constructors / Destructors
******************************************************************************/
MeshRenderer::MeshRenderer(
const char * name, Vertices vertices, Indices indices, DrawMode mode) :
MeshRenderer(
name, ShapeBase(mode, std::move(vertices), std::move(indices))) {}
MeshRenderer::MeshRenderer(const char * name,
Vertices vertices,
Indices indices,
DrawMode mode) :
MeshRenderer(name, ShapeBase(mode, std::move(vertices), std::move(indices)))
{
}
MeshRenderer::MeshRenderer(const char * name) :
MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS)) {}
MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS))
{
}
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
Object(name, shape, QTK_MESH), mVertexShader(":/shaders/multi-color.vert"),
mFragmentShader(":/shaders/multi-color.frag"), mDrawType(GL_TRIANGLES) {
mFragmentShader(":/shaders/multi-color.frag"), mDrawType(GL_TRIANGLES)
{
mShape = Shape(shape);
init();
sInstances.insert(name, this);
}
MeshRenderer::~MeshRenderer() {
MeshRenderer::~MeshRenderer()
{
sInstances.remove(mName.c_str());
}
@@ -45,14 +52,15 @@ MeshRenderer::~MeshRenderer() {
* Public Methods
******************************************************************************/
void MeshRenderer::init() {
if(mVAO.isCreated()) {
void MeshRenderer::init()
{
if (mVAO.isCreated()) {
mVAO.destroy();
}
if(mProgram.isLinked()) {
if (mProgram.isLinked()) {
mProgram.removeAllShaders();
}
if(mVBO.isCreated()) {
if (mVBO.isCreated()) {
mVBO.destroy();
}
@@ -60,10 +68,10 @@ void MeshRenderer::init() {
mVAO.bind();
mProgram.create();
mProgram.addShaderFromSourceFile(
QOpenGLShader::Vertex, mVertexShader.c_str());
mProgram.addShaderFromSourceFile(
QOpenGLShader::Fragment, mFragmentShader.c_str());
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
mVertexShader.c_str());
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
mFragmentShader.c_str());
mProgram.link();
mProgram.bind();
@@ -84,9 +92,11 @@ void MeshRenderer::init() {
mProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D));
// Enable color attribute, setting offset to total size of vertices()
mProgram.enableAttributeArray(1);
mProgram.setAttributeBuffer(
1, GL_FLOAT, getVertices().size() * sizeof(getVertices()[0]), 3,
sizeof(QVector3D));
mProgram.setAttributeBuffer(1,
GL_FLOAT,
getVertices().size() * sizeof(getVertices()[0]),
3,
sizeof(QVector3D));
mVBO.release();
@@ -94,28 +104,29 @@ void MeshRenderer::init() {
mVAO.release();
}
void MeshRenderer::draw() {
void MeshRenderer::draw()
{
bindShaders();
mVAO.bind();
if(mTexture.hasTexture()) {
if (mTexture.hasTexture()) {
mTexture.getOpenGLTexture().bind();
}
// TODO: Automate uniforms some other way
setUniformMVP();
if(mShape.mDrawMode == QTK_DRAW_ARRAYS) {
if (mShape.mDrawMode == QTK_DRAW_ARRAYS) {
glDrawArrays(mDrawType, 0, getVertices().size());
} else if(
mShape.mDrawMode == QTK_DRAW_ELEMENTS
|| mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) {
glDrawElements(
mDrawType, mShape.mIndices.size(), GL_UNSIGNED_INT,
mShape.mIndices.data());
} else if (mShape.mDrawMode == QTK_DRAW_ELEMENTS
|| mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) {
glDrawElements(mDrawType,
mShape.mIndices.size(),
GL_UNSIGNED_INT,
mShape.mIndices.data());
}
if(mTexture.hasTexture()) {
if (mTexture.hasTexture()) {
mTexture.getOpenGLTexture().release();
}
@@ -123,30 +134,33 @@ void MeshRenderer::draw() {
releaseShaders();
}
void MeshRenderer::enableAttributeArray(int location) {
void MeshRenderer::enableAttributeArray(int location)
{
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.enableAttributeArray(location);
mVAO.release();
}
void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims) {
void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims)
{
mVAO.bind();
mNBO.destroy();
mNBO.create();
mNBO.bind();
mNBO.allocate(t.data(), t.size() * sizeof(t[0]));
enableAttributeArray(1);
if(dims == 2) {
if (dims == 2) {
setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D));
} else if(dims == 3) {
} else if (dims == 3) {
setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D));
}
mNBO.release();
mVAO.release();
}
void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims) {
void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims)
{
// TODO: Store state to track if buffer objects are bound
mVAO.bind();
mNBO.destroy();
@@ -154,42 +168,47 @@ void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims) {
mNBO.bind();
mNBO.allocate(n.data(), n.size() * sizeof(n[0]));
enableAttributeArray(1);
if(dims == 2) {
if (dims == 2) {
setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D));
} else if(dims == 3) {
} else if (dims == 3) {
setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D));
}
mNBO.release();
mVAO.release();
}
void MeshRenderer::setShaders(
const std::string & vert, const std::string & frag) {
void MeshRenderer::setShaders(const std::string & vert,
const std::string & frag)
{
mVertexShader = vert;
mFragmentShader = frag;
init();
}
void MeshRenderer::setUniformMVP(
const char * model, const char * view, const char * projection) {
void MeshRenderer::setUniformMVP(const char * model,
const char * view,
const char * projection)
{
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(projection, Scene::getProjectionMatrix());
mProgram.setUniformValue(view, Scene::getViewMatrix());
mProgram.setUniformValue(model, mTransform.toMatrix());
}
void MeshRenderer::setShape(const Shape & value) {
void MeshRenderer::setShape(const Shape & value)
{
Object::setShape(value);
init();
}
void MeshRenderer::setColor(const QVector3D & color) {
if(mShape.mColors.empty()) {
for(const auto & vertex : mShape.getVertices()) {
void MeshRenderer::setColor(const QVector3D & color)
{
if (mShape.mColors.empty()) {
for (const auto & vertex : mShape.getVertices()) {
mShape.mColors.push_back(color);
}
} else {
for(int i = 0; i < mShape.getColors().size(); i++) {
for (int i = 0; i < mShape.getColors().size(); i++) {
mShape.mColors[i] = color;
}
}
@@ -198,7 +217,8 @@ void MeshRenderer::setColor(const QVector3D & color) {
}
void MeshRenderer::setAttributeBuffer(
int location, GLenum type, int offset, int tupleSize, int stride) {
int location, GLenum type, int offset, int tupleSize, int stride)
{
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.setAttributeBuffer(location, type, offset, tupleSize, stride);
@@ -210,8 +230,9 @@ void MeshRenderer::setAttributeBuffer(
******************************************************************************/
// Static member function to retrieve instances of MeshRenderers
MeshRenderer * MeshRenderer::getInstance(const QString & name) {
if(!sInstances.contains(name)) {
MeshRenderer * MeshRenderer::getInstance(const QString & name)
{
if (!sInstances.contains(name)) {
#if QTK_DEBUG
qDebug() << "Attempt to access MeshRenderer instance that does not exist! ("
<< qPrintable(name) << ")\n";

View File

@@ -14,8 +14,10 @@
#include "qtkapi.h"
#include "shape.h"
namespace Qtk {
class QTKAPI MeshRenderer : public Object {
namespace Qtk
{
class QTKAPI MeshRenderer : public Object
{
public:
/*************************************************************************
* Typedefs
@@ -38,9 +40,10 @@ namespace Qtk {
* @param indices Indicess to use for initializes geometry shape.
* @param mode OpenGL draw mode. Supported modes are prefixed with QTK_*
*/
MeshRenderer(
const char * name, Vertices vertices, Indices indices,
DrawMode mode = QTK_DRAW_ARRAYS);
MeshRenderer(const char * name,
Vertices vertices,
Indices indices,
DrawMode mode = QTK_DRAW_ARRAYS);
/**
* Delegate constructor.
@@ -112,14 +115,16 @@ namespace Qtk {
/**
* @param vert Path to vertex shader to use for this MeshRenderer.
*/
inline void setShaderVertex(const std::string & vert) {
inline void setShaderVertex(const std::string & vert)
{
mVertexShader = vert;
}
/**
* @param frag Path to fragment shader to use for this MeshRenderer.
*/
inline void setShaderFragment(const std::string & frag) {
inline void setShaderFragment(const std::string & frag)
{
mFragmentShader = frag;
}
@@ -134,7 +139,8 @@ namespace Qtk {
* @param location Index location of the uniform value we are setting.
* @param value The value to use for the uniform.
*/
template <typename T> inline void setUniform(int location, T value) {
template <typename T> inline void setUniform(int location, T value)
{
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(location, value);
}
@@ -145,7 +151,8 @@ namespace Qtk {
* @param value The value to use for the uniform.
*/
template <typename T>
inline void setUniform(const char * location, T value) {
inline void setUniform(const char * location, T value)
{
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(location, value);
}
@@ -159,9 +166,9 @@ namespace Qtk {
* @param view Name of the uniform to store the View matrix.
* @param projection Name of the uniform to store the Projection matrix.
*/
void setUniformMVP(
const char * model = "uModel", const char * view = "uView",
const char * projection = "uProjection");
void setUniformMVP(const char * model = "uModel",
const char * view = "uView",
const char * projection = "uProjection");
/**
* Sets the shape of the MeshRenderer using the Object base class method.
@@ -211,11 +218,13 @@ namespace Qtk {
*/
inline Transform3D & getTransform() { return mTransform; }
inline std::string getVertexShader() const override {
[[nodiscard]] inline std::string getVertexShader() const override
{
return mVertexShader;
}
inline std::string getFragmentShader() const override {
[[nodiscard]] inline std::string getFragmentShader() const override
{
return mFragmentShader;
}

View File

@@ -21,25 +21,28 @@ Model::ModelManager Model::mManager;
* Public Member Functions
******************************************************************************/
void Model::draw() {
for(auto & mesh : mMeshes) {
void Model::draw()
{
for (auto & mesh : mMeshes) {
mesh.mTransform = mTransform;
mesh.draw();
}
}
void Model::draw(QOpenGLShaderProgram & shader) {
for(auto & mesh : mMeshes) {
void Model::draw(QOpenGLShaderProgram & shader)
{
for (auto & mesh : mMeshes) {
mesh.mTransform = mTransform;
mesh.draw(shader);
}
}
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
{
bool modified = false;
std::string fullPath = mDirectory + '/' + fileName;
for(auto & texture : mTexturesLoaded) {
if(texture.mPath == fileName) {
for (auto & texture : mTexturesLoaded) {
if (texture.mPath == fileName) {
texture.mTexture->destroy();
texture.mTexture->create();
texture.mTexture->setData(
@@ -47,14 +50,15 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
modified = true;
}
}
if(!modified) {
if (!modified) {
qDebug() << "Attempt to flip texture that doesn't exist: "
<< fullPath.c_str() << "\n";
}
}
// Static function to access ModelManager for getting Models by name
Model * Qtk::Model::getInstance(const char * name) {
Model * Qtk::Model::getInstance(const char * name)
{
return mManager[name];
}
@@ -62,10 +66,11 @@ Model * Qtk::Model::getInstance(const char * name) {
* Private Member Functions
******************************************************************************/
void Model::loadModel(const std::string & path) {
void Model::loadModel(const std::string & path)
{
Assimp::Importer import;
// If using a Qt Resource path, use QtkIOSystem for file handling.
if(path.front() == ':') {
if (path.front() == ':') {
import.SetIOHandler(new QtkIOSystem());
}
// Used as base path for loading model textures.
@@ -75,13 +80,14 @@ void Model::loadModel(const std::string & path) {
// + And flipping texture UVs, etc..
// Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
const aiScene * scene = import.ReadFile(
path.c_str(), aiProcess_Triangulate | aiProcess_FlipUVs
| aiProcess_GenSmoothNormals
| aiProcess_CalcTangentSpace | aiProcess_OptimizeMeshes
| aiProcess_SplitLargeMeshes);
path.c_str(),
aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals
| aiProcess_CalcTangentSpace | aiProcess_OptimizeMeshes
| aiProcess_SplitLargeMeshes);
// If there were errors, print and return
if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE
|| !scene->mRootNode) {
qDebug() << "Error::ASSIMP::" << import.GetErrorString() << "\n";
return;
}
@@ -99,26 +105,28 @@ void Model::loadModel(const std::string & path) {
mManager.insert(getName().c_str(), this);
}
void Model::processNode(aiNode * node, const aiScene * scene) {
void Model::processNode(aiNode * node, const aiScene * scene)
{
// Process each mesh that is available for this node
for(GLuint i = 0; i < node->mNumMeshes; i++) {
for (GLuint i = 0; i < node->mNumMeshes; i++) {
aiMesh * mesh = scene->mMeshes[node->mMeshes[i]];
mMeshes.push_back(processMesh(mesh, scene));
}
// Process each child node for this mesh using recursion
for(GLuint i = 0; i < node->mNumChildren; i++) {
for (GLuint i = 0; i < node->mNumChildren; i++) {
processNode(node->mChildren[i], scene);
}
}
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
{
ModelMesh::Vertices vertices;
ModelMesh::Indices indices;
ModelMesh::Textures textures;
// For each vertex in the aiMesh
for(GLuint i = 0; i < mesh->mNumVertices; i++) {
for (GLuint i = 0; i < mesh->mNumVertices; i++) {
// Create a local vertex object for positions, normals, and texture coords
ModelVertex vertex;
@@ -132,7 +140,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
// Set the position of our local vertex to the local vector object
vertex.mPosition = vector3D;
if(mesh->HasNormals()) {
if (mesh->HasNormals()) {
// Initialize vertex normal
vector3D.setX(mesh->mNormals[i].x);
vector3D.setY(mesh->mNormals[i].y);
@@ -142,7 +150,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
}
// Initialize texture coordinates, if any are available
if(mesh->mTextureCoords[0]) {
if (mesh->mTextureCoords[0]) {
QVector2D vector2D;
// Texture coordinates
vector2D.setX(mesh->mTextureCoords[0][i].x);
@@ -169,16 +177,16 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
}
// For each face on the mesh, process its indices
for(GLuint i = 0; i < mesh->mNumFaces; i++) {
for (GLuint i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for(GLuint j = 0; j < face.mNumIndices; j++) {
for (GLuint j = 0; j < face.mNumIndices; j++) {
// Add the index to out container of indices
indices.push_back(face.mIndices[j]);
}
}
// Process material
if(mesh->mMaterialIndex >= 0) {
if (mesh->mMaterialIndex >= 0) {
// Get the material attached to the model using Assimp
aiMaterial * material = scene->mMaterials[mesh->mMaterialIndex];
// Get all diffuse textures from the material
@@ -200,25 +208,29 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
}
return {
vertices, indices, textures, mVertexShader.c_str(),
mFragmentShader.c_str()};
return {vertices,
indices,
textures,
mVertexShader.c_str(),
mFragmentShader.c_str()};
}
ModelMesh::Textures Model::loadMaterialTextures(
aiMaterial * mat, aiTextureType type, const std::string & typeName) {
ModelMesh::Textures Model::loadMaterialTextures(aiMaterial * mat,
aiTextureType type,
const std::string & typeName)
{
ModelMesh::Textures textures;
for(GLuint i = 0; i < mat->GetTextureCount(type); i++) {
for (GLuint i = 0; i < mat->GetTextureCount(type); i++) {
// Call GetTexture to get the name of the texture file to load
aiString fileName;
mat->GetTexture(type, i, &fileName);
// Check if we have already loaded this texture
bool skip = false;
for(auto & j : mTexturesLoaded) {
for (auto & j : mTexturesLoaded) {
// If the path to the texture already exists in m_texturesLoaded, skip it
if(std::strcmp(j.mPath.data(), fileName.C_Str()) == 0) {
if (std::strcmp(j.mPath.data(), fileName.C_Str()) == 0) {
textures.push_back(j);
// If we have loaded the texture, do not load it again
skip = true;
@@ -227,10 +239,11 @@ ModelMesh::Textures Model::loadMaterialTextures(
}
// If the texture has not yet been loaded
if(!skip) {
if (!skip) {
ModelTexture texture;
texture.mTexture = OpenGLTextureFactory::initTexture(
std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false,
std::string(mDirectory + '/' + fileName.C_Str()).c_str(),
false,
false);
texture.mID = texture.mTexture->textureId();
texture.mType = typeName;
@@ -246,7 +259,8 @@ ModelMesh::Textures Model::loadMaterialTextures(
return textures;
}
void Model::sortModelMeshes() {
void Model::sortModelMeshes()
{
auto cameraPos = Scene::getCamera().getTransform();
auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) {
// Sort by the first vertex position in the model

View File

@@ -24,12 +24,14 @@
#include "modelmesh.h"
#include "qtkapi.h"
namespace Qtk {
namespace Qtk
{
/**
* Model object that has a ModelMesh.
* Top-level object that represents 3D models stored within a scene.
*/
class QTKAPI Model : public Object {
class QTKAPI Model : public Object
{
public:
/*************************************************************************
* Typedefs
@@ -51,13 +53,13 @@ namespace Qtk {
* @param vertexShader Optional path to custom vertex shader.
* @param fragmentShader Optional path to custom fragment shader.
*/
inline Model(
const char * name, const char * path,
const char * vertexShader = ":/shaders/model-basic.vert",
const char * fragmentShader = ":/shaders/model-basic.frag") :
Object(name, QTK_MODEL),
mModelPath(path), mVertexShader(vertexShader),
mFragmentShader(fragmentShader) {
inline Model(const char * name,
const char * path,
const char * vertexShader = ":/shaders/model-basic.vert",
const char * fragmentShader = ":/shaders/model-basic.frag") :
Object(name, QTK_MODEL), mModelPath(path),
mVertexShader(vertexShader), mFragmentShader(fragmentShader)
{
loadModel(mModelPath);
}
@@ -86,8 +88,9 @@ namespace Qtk {
* @param flipX Flip the texture along the X axis
* @param flipY Flip the texture along the Y axis
*/
void flipTexture(
const std::string & fileName, bool flipX = false, bool flipY = true);
void flipTexture(const std::string & fileName,
bool flipX = false,
bool flipY = true);
/*************************************************************************
* Setters
@@ -101,8 +104,9 @@ namespace Qtk {
* @param value The value to assign to the uniform
*/
template <typename T>
inline void setUniform(const char * location, T value) {
for(auto & mesh : mMeshes) {
inline void setUniform(const char * location, T value)
{
for (auto & mesh : mMeshes) {
mesh.mProgram->bind();
mesh.mProgram->setUniformValue(location, value);
mesh.mProgram->release();
@@ -127,11 +131,13 @@ namespace Qtk {
*/
inline Transform3D & getTransform() { return mTransform; }
inline std::string getVertexShader() const override {
[[nodiscard]] inline std::string getVertexShader() const override
{
return mVertexShader;
}
inline std::string getFragmentShader() const override {
[[nodiscard]] inline std::string getFragmentShader() const override
{
return mFragmentShader;
}
@@ -183,8 +189,9 @@ namespace Qtk {
* @param typeName Texture type name in string format.
* @return Collection of all textures for a single ModelMesh.
*/
ModelMesh::Textures loadMaterialTextures(
aiMaterial * mat, aiTextureType type, const std::string & typeName);
ModelMesh::Textures loadMaterialTextures(aiMaterial * mat,
aiTextureType type,
const std::string & typeName);
/**
* Sorts each mesh in the Model based on distance from the camera.

View File

@@ -15,7 +15,8 @@ using namespace Qtk;
* Public Member Functions
******************************************************************************/
void ModelMesh::draw(QOpenGLShaderProgram & shader) {
void ModelMesh::draw(QOpenGLShaderProgram & shader)
{
mVAO->bind();
// Bind shader
shader.bind();
@@ -28,7 +29,7 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
GLuint diffuseCount = 1;
GLuint specularCount = 1;
GLuint normalCount = 1;
for(GLuint i = 0; i < mTextures.size(); i++) {
for (GLuint i = 0; i < mTextures.size(); i++) {
// Activate the current texture index by adding offset to GL_TEXTURE0
glActiveTexture(GL_TEXTURE0 + i);
mTextures[i].mTexture->bind();
@@ -38,13 +39,13 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
// Specular: material.texture_specular1, material.texture_specular2, ...
std::string number;
std::string name = mTextures[i].mType;
if(name == "texture_diffuse") {
if (name == "texture_diffuse") {
number = std::to_string(diffuseCount++);
}
if(name == "texture_specular") {
if (name == "texture_specular") {
number = std::to_string(specularCount++);
}
if(name == "texture_normal") {
if (name == "texture_normal") {
number = std::to_string(normalCount++);
}
@@ -61,7 +62,7 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
// Release shader, textures
for(const auto & texture : mTextures) {
for (const auto & texture : mTextures) {
texture.mTexture->release();
}
shader.release();
@@ -72,7 +73,8 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
* Private Member Functions
******************************************************************************/
void ModelMesh::initMesh(const char * vert, const char * frag) {
void ModelMesh::initMesh(const char * vert, const char * frag)
{
initializeOpenGLFunctions();
// Create VAO, VBO, EBO
@@ -112,9 +114,11 @@ void ModelMesh::initMesh(const char * vert, const char * frag) {
// Texture Coordinates
mProgram->enableAttributeArray(2);
mProgram->setAttributeBuffer(
2, GL_FLOAT, offsetof(ModelVertex, mTextureCoord), 2,
sizeof(ModelVertex));
mProgram->setAttributeBuffer(2,
GL_FLOAT,
offsetof(ModelVertex, mTextureCoord),
2,
sizeof(ModelVertex));
// Vertex tangents
mProgram->enableAttributeArray(3);

View File

@@ -9,12 +9,15 @@
#ifndef QTK_MODELMESH_H
#define QTK_MODELMESH_H
#include <utility>
#include <QOpenGLFunctions>
#include "object.h"
#include "transform3D.h"
namespace Qtk {
namespace Qtk
{
/**
* 3D models will store this data for each vertex in geometry.
*/
@@ -39,8 +42,9 @@ namespace Qtk {
* @param type Type of texture in string format.
* @param path Path to the texture on disk.
*/
ModelTexture(const std::string & type, const std::string & path) :
mType(type), mPath(path) {
ModelTexture(std::string type, const std::string & path) :
mType(std::move(type)), mPath(path)
{
mTexture = OpenGLTextureFactory::initTexture(path.c_str());
mID = mTexture->textureId();
}
@@ -64,7 +68,8 @@ namespace Qtk {
* Mesh class specialized for storing 3D model data.
* Eventually this can be consolidated into a more generic class.
*/
class QTKAPI ModelMesh : protected QOpenGLFunctions {
class QTKAPI ModelMesh : protected QOpenGLFunctions
{
public:
/*************************************************************************
* Typedefs
@@ -89,16 +94,18 @@ namespace Qtk {
* @param vertexShader Path to vertex shader for this ModelMesh.
* @param fragmentShader Path to fragment shader for this ModelMesh.
*/
ModelMesh(
Vertices vertices, Indices indices, Textures textures,
const char * vertexShader = ":/model-basic.vert",
const char * fragmentShader = ":/model-basic.frag") :
ModelMesh(Vertices vertices,
Indices indices,
Textures textures,
const char * vertexShader = ":/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)) {
mTextures(std::move(textures))
{
initMesh(vertexShader, fragmentShader);
}

View File

@@ -17,14 +17,16 @@
#include "shape.h"
#include "texture.h"
namespace Qtk {
namespace Qtk
{
class Model;
/**
* Object base class for objects that can exist within a scene.
* An object could be a Cube, Skybox, 3D Model, or other standalone entities.
*/
class QTKAPI Object : public QObject {
class QTKAPI Object : public QObject
{
Q_OBJECT
public:
@@ -47,7 +49,8 @@ namespace Qtk {
// Initialize an object with no shape data assigned
explicit Object(const char * name, Type type) :
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false),
mType(type) {
mType(type)
{
initResources();
setObjectName(name);
}
@@ -55,7 +58,8 @@ namespace Qtk {
// 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) {
mBound(false), mType(type)
{
initResources();
setObjectName(name);
}
@@ -66,29 +70,35 @@ namespace Qtk {
* Accessors
************************************************************************/
[[nodiscard]] inline const Colors & getColors() const {
[[nodiscard]] inline const Colors & getColors() const
{
return mShape.mColors;
}
[[nodiscard]] inline const Indices & getIndexData() const {
[[nodiscard]] inline const Indices & getIndexData() const
{
return mShape.mIndices;
}
[[nodiscard]] inline const Normals & getNormals() const {
[[nodiscard]] inline const Normals & getNormals() const
{
return mShape.mNormals;
}
[[nodiscard]] inline const Shape & getShape() const { return mShape; }
[[nodiscard]] inline const TexCoords & getTexCoords() const {
[[nodiscard]] inline const TexCoords & getTexCoords() const
{
return mShape.mTexCoords;
}
[[nodiscard]] inline const Texture & getTexture() const {
[[nodiscard]] inline const Texture & getTexture() const
{
return mTexture;
}
[[nodiscard]] inline const Vertices & getVertices() const {
[[nodiscard]] inline const Vertices & getVertices() const
{
return mShape.mVertices;
}
@@ -96,15 +106,23 @@ namespace Qtk {
[[nodiscard]] inline const Type & getType() const { return mType; }
[[nodiscard]] inline virtual const Transform3D & getTransform() const {
[[nodiscard]] inline virtual const Transform3D & getTransform() const
{
return mTransform;
}
[[nodiscard]] inline virtual std::string getVertexShader() const {
[[nodiscard]] inline virtual Transform3D & getTransform()
{
return mTransform;
}
[[nodiscard]] inline virtual std::string getVertexShader() const
{
return "Base Object has no vertex shader.";
}
virtual inline std::string getFragmentShader() const {
[[nodiscard]] virtual inline std::string getFragmentShader() const
{
return "Base Object has no fragment shader.";
}
@@ -114,84 +132,101 @@ namespace Qtk {
virtual inline void setName(const std::string & name) { mName = name; }
virtual inline void setColors(const Colors & value) {
virtual inline void setColors(const Colors & value)
{
mShape.mColors = value;
}
virtual inline void setIndices(const Indices & value) {
virtual inline void setIndices(const Indices & value)
{
mShape.mIndices = value;
}
virtual inline void setNormals(const Normals & value) {
virtual inline void setNormals(const Normals & value)
{
mShape.mNormals = value;
}
virtual inline void setShape(const Shape & value) { mShape = value; }
virtual inline void setTexCoords(const TexCoords & value) {
virtual inline void setTexCoords(const TexCoords & value)
{
mShape.mTexCoords = value;
}
virtual inline void setTexture(
const char * path, bool flipX = false, bool flipY = false) {
virtual inline void setTexture(const char * path,
bool flipX = false,
bool flipY = false)
{
mTexture.setTexture(path, flipX, flipY);
}
virtual inline void setCubeMap(const char * path) {
virtual inline void setCubeMap(const char * path)
{
mTexture.setCubeMap(path);
}
virtual inline void setTexture(const Texture & t) {
virtual inline void setTexture(const Texture & t)
{
mTexture.setTexture(t.getPath());
}
virtual inline void setVertices(const Vertices & value) {
virtual inline void setVertices(const Vertices & value)
{
mShape.mVertices = value;
}
inline void setScaleX(double x) {
inline void setScaleX(double x)
{
mTransform.setScale(
x, mTransform.getScale().y(), mTransform.getScale().z());
}
inline void setScaleY(double y) {
inline void setScaleY(double y)
{
mTransform.setScale(
mTransform.getScale().x(), y, mTransform.getScale().z());
}
inline void setScaleZ(double z) {
inline void setScaleZ(double z)
{
mTransform.setScale(
mTransform.getScale().x(), mTransform.getScale().y(), z);
}
inline void setTranslationX(double x) {
mTransform.setTranslation(
x, mTransform.getTranslation().y(),
mTransform.getTranslation().z());
inline void setTranslationX(double x)
{
mTransform.setTranslation(x,
mTransform.getTranslation().y(),
mTransform.getTranslation().z());
}
inline void setTranslationY(double y) {
mTransform.setTranslation(
mTransform.getTranslation().x(), y,
mTransform.getTranslation().z());
inline void setTranslationY(double y)
{
mTransform.setTranslation(mTransform.getTranslation().x(),
y,
mTransform.getTranslation().z());
}
inline void setTranslationZ(double z) {
mTransform.setTranslation(
mTransform.getTranslation().x(), mTransform.getTranslation().y(),
z);
inline void setTranslationZ(double z)
{
mTransform.setTranslation(mTransform.getTranslation().x(),
mTransform.getTranslation().y(),
z);
}
/*************************************************************************
* Public Methods
************************************************************************/
virtual inline void bindShaders() {
virtual inline void bindShaders()
{
mBound = true;
mProgram.bind();
}
virtual inline void releaseShaders() {
virtual inline void releaseShaders()
{
mBound = false;
mProgram.release();
}

View File

@@ -22,22 +22,22 @@
#define QTKAPI
#endif
#include "qtk/qtkmessagelogger.h"
/**
* 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() {
inline void initResources()
{
Q_INIT_RESOURCE(resources);
}
namespace Qtk {
namespace Qtk
{
/**
* Flag to set context for debug messages.
*/
enum DebugContext { Status, Debug, Warn, Error, Fatal, Invalid, Any };
enum DebugContext { Status, Debug, Warn, Error, Fatal };
/**
* Find top level parent for a widget.
@@ -45,9 +45,10 @@ namespace Qtk {
* @param widget Widget to start the search from.
* @return Top level parent widget or Q_NULLPTR if no parent
*/
static QWidget * topLevelParent(QWidget * widget) {
static QWidget * topLevelParent(QWidget * widget)
{
QString name = widget->objectName();
while(widget->parentWidget() != Q_NULLPTR) {
while (widget->parentWidget() != Q_NULLPTR) {
widget = widget->parentWidget();
}
return widget;
@@ -56,7 +57,8 @@ namespace Qtk {
/**
* @return Default icon to use for Qtk desktop application.
*/
static QIcon getIcon() {
static QIcon getIcon()
{
return QIcon(":/icons/icon.png");
}
} // namespace Qtk

View File

@@ -14,23 +14,23 @@ using namespace Qtk;
* Constructors, Destructors
******************************************************************************/
QtkIOStream::QtkIOStream(const char * pFile, const char * pMode) :
mFile(pFile) {
QtkIOStream::QtkIOStream(const char * pFile, const char * pMode) : mFile(pFile)
{
QString mode(pMode);
bool open = false;
if(mode == "w" || mode == "wb") {
if (mode == "w" || mode == "wb") {
open = mFile.open(QIODeviceBase::WriteOnly);
} else if(mode == "r" || mode == "rb") {
} else if (mode == "r" || mode == "rb") {
open = mFile.open(QIODeviceBase::ReadOnly);
} else if(mode == "wt") {
} else if (mode == "wt") {
open = mFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text);
} else if(mode == "rt") {
} else if (mode == "rt") {
open = mFile.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text);
} else {
open = false;
qDebug() << "[Qtk::QtkIOStream] Invalid file open mode: " << mode << "\n";
}
if(!open) {
if (!open) {
qDebug() << "[Qtk::QtkIOStream] Could not open file: " << QString(pFile)
<< "\n";
}
@@ -40,9 +40,10 @@ QtkIOStream::QtkIOStream(const char * pFile, const char * pMode) :
* Public Member Functions
******************************************************************************/
size_t QtkIOStream::Read(void * pvBuffer, size_t pSize, size_t pCount) {
size_t QtkIOStream::Read(void * pvBuffer, size_t pSize, size_t pCount)
{
qint64 readSize = mFile.read((char *)pvBuffer, pSize * pCount);
if(readSize < 0) {
if (readSize < 0) {
qDebug() << "[Qtk::QtkIOStream] Failed to read (" << pSize
<< ") bytes from file at: " << mFile.filesystemFileName().c_str()
<< "\n";
@@ -51,9 +52,10 @@ size_t QtkIOStream::Read(void * pvBuffer, size_t pSize, size_t pCount) {
return readSize;
}
size_t QtkIOStream::Write(const void * pvBuffer, size_t pSize, size_t pCount) {
size_t QtkIOStream::Write(const void * pvBuffer, size_t pSize, size_t pCount)
{
qint64 writeSize = mFile.write((char *)pvBuffer, pSize * pCount);
if(writeSize < 0) {
if (writeSize < 0) {
qDebug() << "[Qtk::QtkIOStream] Failed to write buffer with size (" << pSize
<< ") to file at: " << mFile.filesystemFileName().c_str() << "\n";
return -1;
@@ -61,18 +63,22 @@ size_t QtkIOStream::Write(const void * pvBuffer, size_t pSize, size_t pCount) {
return writeSize;
}
aiReturn QtkIOStream::Seek(size_t pOffset, aiOrigin pOrigin) {
aiReturn QtkIOStream::Seek(size_t pOffset, aiOrigin pOrigin)
{
return mFile.seek(pOffset) ? aiReturn_SUCCESS : aiReturn_FAILURE;
}
size_t QtkIOStream::Tell() const {
size_t QtkIOStream::Tell() const
{
return mFile.pos();
}
size_t QtkIOStream::FileSize() const {
size_t QtkIOStream::FileSize() const
{
return mFile.size();
}
void QtkIOStream::Flush() {
void QtkIOStream::Flush()
{
mFile.flush();
}

View File

@@ -15,12 +15,14 @@
#ifndef QTK_QTKIOSTREAM_H
#define QTK_QTKIOSTREAM_H
namespace Qtk {
namespace Qtk
{
/**
* Custom Assimp IO stream to support QtkIOSystem file handling.
* Allows direct use of Qt Resource paths for loading models in Assimp.
*/
class QtkIOStream : public Assimp::IOStream {
class QtkIOStream : public Assimp::IOStream
{
friend class QtkIOSystem;
protected:

View File

@@ -15,22 +15,26 @@ using namespace Qtk;
* Public Member Functions
******************************************************************************/
bool QtkIOSystem::Exists(const char * pFile) const {
bool QtkIOSystem::Exists(const char * pFile) const
{
return QFileInfo::exists(pFile);
}
char QtkIOSystem::getOsSeparator() const {
char QtkIOSystem::getOsSeparator() const
{
return QDir::separator().toLatin1();
}
Assimp::IOStream * QtkIOSystem::Open(const char * pFile, const char * pMode) {
if(!Exists(pFile)) {
Assimp::IOStream * QtkIOSystem::Open(const char * pFile, const char * pMode)
{
if (!Exists(pFile)) {
qDebug() << "[Qtk::QtkIOSystem] failed to open file: " << pFile << "\n";
return nullptr;
}
return new QtkIOStream(pFile, pMode);
}
void QtkIOSystem::Close(Assimp::IOStream * pFile) {
void QtkIOSystem::Close(Assimp::IOStream * pFile)
{
delete pFile;
}

View File

@@ -15,11 +15,13 @@
#ifndef QTK_QTKIOSYSTEM_H
#define QTK_QTKIOSYSTEM_H
namespace Qtk {
namespace Qtk
{
/**
* Assimp IO system for loading models with assimp, using Qt Resource paths.
*/
class QtkIOSystem : public Assimp::IOSystem {
class QtkIOSystem : public Assimp::IOSystem
{
public:
QtkIOSystem() = default;
~QtkIOSystem() = default;
@@ -40,8 +42,8 @@ namespace Qtk {
* @param pMode Mode to open file. See `man fopen`.
* @return QtkIOStream for the opened file.
*/
Assimp::IOStream * Open(
const char * pFile, const char * pMode = "rb") override;
Assimp::IOStream * Open(const char * pFile,
const char * pMode = "rb") override;
/**
* @param pFile File to close.

View File

@@ -1,61 +0,0 @@
#include "qtkmessagelogger.h"
#include "camera3d.h"
#include "transform3D.h"
Qtk::QtkDebug Qtk::QtkDebug::operator<<(const Qtk::Transform3D & transform) {}
#ifndef QT_NO_DEBUG_STREAM
QDebug Qtk::operator<<(QDebug dbg, const Qtk::Transform3D & transform) {
dbg << "Transform3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Scale: <" << transform.getScale().x() << ", "
<< transform.getScale().y() << ", " << transform.getScale().z() << ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
return dbg;
}
QDebug Qtk::operator<<(QDebug dbg, const Qtk::Camera3D & transform) {
dbg << "Camera3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
return dbg;
}
#endif
#ifndef QT_NO_DATASTREAM
QDataStream & Qtk::operator<<(
QDataStream & out, const Qtk::Transform3D & transform) {
out << transform.mTranslation;
out << transform.mScale;
out << transform.mRotation;
return out;
}
QDataStream & Qtk::operator>>(QDataStream & in, Qtk::Transform3D & transform) {
in >> transform.mTranslation;
in >> transform.mScale;
in >> transform.mRotation;
transform.m_dirty = true;
return in;
}
QDataStream & Qtk::operator<<(QDataStream & out, const Qtk::Camera3D & camera) {
out << camera.getTransform();
return out;
}
QDataStream & Qtk::operator>>(QDataStream & in, Qtk::Camera3D & camera) {
in >> camera.mTransform;
return in;
}
#endif

View File

@@ -1,96 +0,0 @@
#ifndef QTK_QTKMESSAGELOGGER_H
#define QTK_QTKMESSAGELOGGER_H
#include "qtkapi.h"
#include <QDebug>
#include <QMessageLogger>
#include <Qt>
// #define qtkDebug Qtk::QtkMessageLogger::get().debug
// #define qtkInfo Qtk::QtkMessageLogger::get().info
// #define qtkWarning Qtk::QtkMessageLogger::get().warning
// #define qtkCritical Qtk::QtkMessageLogger::get().critical
// #define qtkFatal Qtk::QtkMessageLogger::get().fatal
#define qtkDebug \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.debug
#define qtkInfo \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.info
#define qtkWarning \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.warning
#define qtkCritical \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.critical
#define qtkFatal \
Qtk::QtkMessageLogger( \
QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC) \
.logger() \
.fatal
namespace Qtk {
class Transform3D;
class Camera3D;
class QTKAPI QtkDebug : public QDebug {
public:
QtkDebug operator<<(const Transform3D & transform);
};
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform);
QDebug operator<<(QDebug dbg, const Camera3D & camera);
#endif
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Transform3D & transform);
QDataStream & operator>>(QDataStream & in, Transform3D & transform);
QDataStream & operator<<(QDataStream & out, const Camera3D & camera);
QDataStream & operator>>(QDataStream & in, Camera3D & camera);
#endif
class QTKAPI QtkMessageLogger {
public:
QtkMessageLogger(const char * file, int line, const char * function) :
mQMessageLogger(file, line, function) {
qDebug();
}
// static QtkMessageLogger * get() {
// if(mQtkMessageLogger == Q_NULLPTR) {
// mQtkMessageLogger = new QtkMessageLogger();
// }
// return mQtkMessageLogger;
// }
// QtkDebug debug() const {}
[[nodiscard]] inline const QMessageLogger & logger() const {
return mQMessageLogger;
}
inline QtkDebug debug() const { return {}; };
static QtkMessageLogger * mQtkMessageLogger;
QMessageLogger mQMessageLogger;
};
} // namespace Qtk
#endif // QTK_QTKMESSAGELOGGER_H

View File

@@ -18,16 +18,18 @@ QMatrix4x4 Scene::mProjection;
* Constructors / Destructors
******************************************************************************/
Scene::Scene() : mSceneName("Default Scene") {
mCamera.setTranslation({0.0f, 0.0f, 20.0f});
mCamera.setRotation({-5.0f, 0.0f, 1.0f, 0.0f});
Scene::Scene() : mSceneName("Default Scene")
{
mCamera.getTransform().setTranslation(0.0f, 0.0f, 20.0f);
mCamera.getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
Scene::~Scene() {
for(auto & mesh : mMeshes) {
Scene::~Scene()
{
for (auto & mesh : mMeshes) {
delete mesh;
}
for(auto & model : mModels) {
for (auto & model : mModels) {
delete model;
}
delete mSkybox;
@@ -37,83 +39,90 @@ Scene::~Scene() {
* Public Methods
******************************************************************************/
template <> MeshRenderer * Scene::addObject(MeshRenderer * object) {
template <> MeshRenderer * Scene::addObject(MeshRenderer * object)
{
initSceneObjectName(object);
mMeshes.push_back(object);
sceneUpdated(mSceneName);
return object;
}
template <> Model * Scene::addObject(Model * object) {
template <> Model * Scene::addObject(Model * object)
{
initSceneObjectName(object);
mModels.push_back(object);
sceneUpdated(mSceneName);
return object;
}
void Scene::draw() {
if(!mInit) {
void Scene::draw()
{
if (!mInit) {
initializeOpenGLFunctions();
init();
mInit = true;
}
while(!mModelLoadQueue.empty()) {
// Check if there were new models added that still need to be loaded.
// This is for objects added at runtime via click-and-drag events, etc.
while (!mModelLoadQueue.empty()) {
auto modelSpec = mModelLoadQueue.front();
// Load the model and add it to the scene.
addObject(new Model(modelSpec.first.c_str(), modelSpec.second.c_str()));
mModelLoadQueue.pop();
}
if(mPause) {
if (mPause) {
return;
}
if(mSkybox != Q_NULLPTR) {
if (mSkybox != Q_NULLPTR) {
mSkybox->draw();
}
for(const auto & model : mModels) {
for (const auto & model : mModels) {
model->draw();
}
for(const auto & mesh : mMeshes) {
for (const auto & mesh : mMeshes) {
mesh->draw();
}
}
std::vector<Object *> Scene::getObjects() const {
std::vector<Object *> Scene::getObjects() const
{
// All scene objects must inherit from Qtk::Object.
std::vector<Object *> objects(mMeshes.begin(), mMeshes.end());
for(const auto & model : mModels) {
for (const auto & model : mModels) {
objects.push_back(model);
if(objects.back() == nullptr) {
if (objects.back() == nullptr) {
return {};
}
}
return objects;
}
Object * Scene::getObject(const QString & name) const {
for(const auto & object : getObjects()) {
if(object->getName() == name.toStdString()) {
Object * Scene::getObject(const QString & name) const
{
for (const auto & object : getObjects()) {
if (object->getName() == name.toStdString()) {
return object;
}
}
return Q_NULLPTR;
}
void Scene::setSkybox(Skybox * skybox) {
void Scene::setSkybox(Skybox * skybox)
{
delete mSkybox;
mSkybox = skybox;
}
void Scene::initSceneObjectName(Object * object) {
if(!mObjectCount.count(object->getName())) {
mObjectCount[object->getName()] = 1;
} else {
mObjectCount[object->getName()]++;
}
void Scene::initSceneObjectName(Object * object)
{
mObjectCount[object->getName()] = mObjectCount.count(object->getName()) + 1;
// If the object exists make it's name unique.
auto count = mObjectCount[object->getName()];
if(count > 1) {
if (count > 1) {
object->setName(object->getName() + " (" + std::to_string(count) + ")");
}
}

View File

@@ -21,7 +21,8 @@
#include "model.h"
#include "skybox.h"
namespace Qtk {
namespace Qtk
{
/**
* An abstract Scene class to inherit from when building new scenes.
*
@@ -43,7 +44,8 @@ namespace Qtk {
* If the child scene adds any objects which are not managed (drawn) by this
* base class, the child scene class must also override the `draw()` method.
*/
class Scene : public QObject, protected QOpenGLFunctions {
class Scene : public QObject, protected QOpenGLFunctions
{
Q_OBJECT
public:
@@ -53,7 +55,7 @@ namespace Qtk {
Scene();
virtual ~Scene();
~Scene() override;
/*************************************************************************
* Public Methods
@@ -73,18 +75,23 @@ namespace Qtk {
/**
* Function called to update the QOpenGLWidget. Does not trigger a redraw.
*
* This method can translate or rotate objects to simulate movement.
* Calling this several times will still result in only one repaint.
*
* It's very possible a client will not want to move objects in the scene
* using this method. This is intentially not pure virtual.
*/
virtual void update() {}
void loadModel(const QUrl & url) {
void loadModel(const QUrl & url)
{
auto fileName = url.fileName().replace(".obj", "").toStdString();
auto filePath = url.toLocalFile().toStdString();
loadModel(fileName, filePath);
}
void loadModel(const std::string & name, const std::string & path) {
void loadModel(const std::string & name, const std::string & path)
{
// Add the dropped model to the load queue.
// This is consumed during rendering of the scene if not empty.
mModelLoadQueue.emplace(name, path);
@@ -111,7 +118,8 @@ namespace Qtk {
/**
* @return The number of objects within the scene with the given name.
*/
[[nodiscard]] uint64_t getObjectCount(const QString & name) {
[[nodiscard]] uint64_t getObjectCount(const QString & name)
{
return mObjectCount.count(name.toStdString())
? mObjectCount[name.toStdString()]
: 0;
@@ -125,14 +133,16 @@ namespace Qtk {
/**
* @return View matrix for the camera attached to this scene.
*/
[[nodiscard]] inline static QMatrix4x4 getViewMatrix() {
[[nodiscard]] inline static QMatrix4x4 getViewMatrix()
{
return mCamera.toMatrix();
}
/**
* @return Projection matrix for the current view into the scene.
*/
[[nodiscard]] inline static QMatrix4x4 & getProjectionMatrix() {
[[nodiscard]] inline static QMatrix4x4 & getProjectionMatrix()
{
return mProjection;
}
@@ -150,15 +160,16 @@ namespace Qtk {
/**
* @return All MeshRenderers within the scene.
*/
[[nodiscard]] inline const std::vector<MeshRenderer *> & getMeshes()
const {
[[nodiscard]] inline const std::vector<MeshRenderer *> & getMeshes() const
{
return mMeshes;
}
/**
* @return All Models within the scene.
*/
[[nodiscard]] inline const std::vector<Model *> & getModels() const {
[[nodiscard]] inline const std::vector<Model *> & getModels() const
{
return mModels;
}
@@ -239,30 +250,6 @@ namespace Qtk {
/* Track count of objects with same initial name. */
std::unordered_map<std::string, uint64_t> mObjectCount;
};
class SceneEmpty : public Scene {
public:
void init() override { setSceneName("Empty Scene"); }
void draw() override { Scene::draw(); }
void update() override { Scene::update(); }
};
class SceneInterface : public Scene {
public:
explicit SceneInterface(Scene * scene) : mScene(scene) {}
void init() override { mScene->init(); }
void draw() override { mScene->draw(); }
void update() override { mScene->update(); }
protected:
Scene * mScene;
};
} // namespace Qtk
#endif // QTK_SCENE_H

View File

@@ -10,9 +10,10 @@
using namespace Qtk;
Cube::Cube(DrawMode mode) {
Cube::Cube(DrawMode mode)
{
mDrawMode = mode;
switch(mode) {
switch (mode) {
// The order of the following assignment values helps to visualize.
// clang-format off
@@ -203,10 +204,11 @@ Cube::Cube(DrawMode mode) {
}
}
Triangle::Triangle(DrawMode mode) {
Triangle::Triangle(DrawMode mode)
{
mDrawMode = mode;
const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f);
switch(mode) {
switch (mode) {
// clang-format off
case QTK_DRAW_ARRAYS:

View File

@@ -73,7 +73,8 @@
#define UV_RIGHT QVector2D(0.0f, 1.0f)
#define UV_CORNER QVector2D(1.0f, 1.0f)
namespace Qtk {
namespace Qtk
{
class MeshRenderer;
class Object;
@@ -114,13 +115,17 @@ namespace Qtk {
* @param t Texture coordinates for this shape.
* @param n Normals for this shape.
*/
explicit ShapeBase(
DrawMode mode = QTK_DRAW_ARRAYS, Vertices v = {}, Indices i = {},
Colors c = {}, TexCoords t = {}, Normals n = {}) :
mDrawMode(mode),
mVertices(std::move(v)), mColors(std::move(c)),
explicit ShapeBase(DrawMode mode = QTK_DRAW_ARRAYS,
Vertices v = {},
Indices i = {},
Colors c = {},
TexCoords t = {},
Normals n = {}) :
mDrawMode(mode), mVertices(std::move(v)), mColors(std::move(c)),
mIndices(std::move(i)), mTexCoords(std::move(t)),
mNormals(std::move(n)) {}
mNormals(std::move(n))
{
}
/*************************************************************************
* Accessors
@@ -129,14 +134,16 @@ namespace Qtk {
/**
* @return Vertex data for this shape.
*/
[[nodiscard]] inline const Vertices & getVertices() const {
[[nodiscard]] inline const Vertices & getVertices() const
{
return mVertices;
}
/**
* @return Index data for this shape.
*/
[[nodiscard]] inline const Indices & getIndexData() const {
[[nodiscard]] inline const Indices & getIndexData() const
{
return mIndices;
}
@@ -148,21 +155,24 @@ namespace Qtk {
/**
* @return Texture coordinates for this shape.
*/
[[nodiscard]] inline const TexCoords & getTexCoords() const {
[[nodiscard]] inline const TexCoords & getTexCoords() const
{
return mTexCoords;
}
/**
* @return Normals for this shape.
*/
[[nodiscard]] inline const Normals & getNormals() const {
[[nodiscard]] inline const Normals & getNormals() const
{
return mNormals;
}
/**
* @return Stride for texture coordinates on this shape.
*/
[[nodiscard]] inline size_t getTexCoordsStride() const {
[[nodiscard]] inline size_t getTexCoordsStride() const
{
return mTexCoords.size() * sizeof(mTexCoords[0]);
}
@@ -204,14 +214,16 @@ namespace Qtk {
/**
* @param value Vertex data to use for this shape.
*/
virtual inline void setVertices(const Vertices & value) {
virtual inline void setVertices(const Vertices & value)
{
mVertices = value;
}
/**
* @param value Index data to use for this shape.
*/
virtual inline void setIndices(const Indices & value) {
virtual inline void setIndices(const Indices & value)
{
mIndices = value;
}
@@ -223,14 +235,16 @@ namespace Qtk {
/**
* @param value Texture coordinates to use for this shape.
*/
virtual inline void setTexCoords(const TexCoords & value) {
virtual inline void setTexCoords(const TexCoords & value)
{
mTexCoords = value;
}
/**
* @param value Normals to use for this shape.
*/
virtual inline void setNormals(const Normals & value) {
virtual inline void setNormals(const Normals & value)
{
mNormals = value;
}

View File

@@ -17,36 +17,48 @@ using namespace Qtk;
******************************************************************************/
Skybox::Skybox(const std::string & name) :
Skybox(
":/textures/skybox/right.png", ":/textures/skybox/top.png",
":/textures/skybox/front.png", ":/textures/skybox/left.png",
":/textures/skybox/bottom.png", ":/textures/skybox/back.png", name) {}
Skybox(":/textures/skybox/right.png",
":/textures/skybox/top.png",
":/textures/skybox/front.png",
":/textures/skybox/left.png",
":/textures/skybox/bottom.png",
":/textures/skybox/back.png",
name)
{
}
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) {
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name)
{
mTexture.setTexture(cubeMap);
init();
}
Skybox::Skybox(
const std::string & right, const std::string & top,
const std::string & front, const std::string & left,
const std::string & bottom, const std::string & back,
const std::string & name) :
Skybox::Skybox(const std::string & right,
const std::string & top,
const std::string & front,
const std::string & left,
const std::string & bottom,
const std::string & back,
const std::string & name) :
mVBO(QOpenGLBuffer::VertexBuffer),
mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData()) {
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData())
{
init();
mTexture.setCubeMap(
QImage(right.c_str()).mirrored(), QImage(top.c_str()),
QImage(front.c_str()), QImage(left.c_str()), QImage(bottom.c_str()),
QImage(back.c_str()));
mTexture.setCubeMap(QImage(right.c_str()).mirrored(),
QImage(top.c_str()),
QImage(front.c_str()),
QImage(left.c_str()),
QImage(bottom.c_str()),
QImage(back.c_str()));
}
/*******************************************************************************
* Public Member Functions
******************************************************************************/
void Skybox::draw() {
void Skybox::draw()
{
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_FALSE);
@@ -73,15 +85,16 @@ void Skybox::draw() {
* Private Member Functions
******************************************************************************/
void Skybox::init() {
void Skybox::init()
{
initializeOpenGLFunctions();
// Set up shader program
mProgram.create();
mProgram.addShaderFromSourceFile(
QOpenGLShader::Vertex, ":/shaders/skybox.vert");
mProgram.addShaderFromSourceFile(
QOpenGLShader::Fragment, ":/shaders/skybox.frag");
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
":/shaders/skybox.vert");
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
":/shaders/skybox.frag");
mProgram.link();
mProgram.bind();

View File

@@ -20,13 +20,15 @@
#include "shape.h"
#include "texture.h"
namespace Qtk {
namespace Qtk
{
/**
* Skybox object for rendering a skybox within a Scene.
* A skybox is typically implemented using a cube map texture centered around
* the camera and projected outwards in all directions.
*/
class QTKAPI Skybox : protected QOpenGLFunctions {
class QTKAPI Skybox : protected QOpenGLFunctions
{
public:
/*************************************************************************
* Constructors / Destructors
@@ -48,8 +50,8 @@ namespace Qtk {
* @param cubeMap QOpenGLTexture to use for the new Skybox.
* @param name The objectName to use for the Skybox.
*/
explicit Skybox(
QOpenGLTexture * cubeMap, const std::string & name = "Skybox");
explicit Skybox(QOpenGLTexture * cubeMap,
const std::string & name = "Skybox");
/**
* Construct a Skybox.
@@ -62,11 +64,13 @@ namespace Qtk {
* @param back Image to use for the back side of the Skybox.
* @param name The objectName to use for this Skybox.
*/
Skybox(
const std::string & right, const std::string & top,
const std::string & front, const std::string & left,
const std::string & bottom, const std::string & back,
const std::string & name = "Skybox");
Skybox(const std::string & right,
const std::string & top,
const std::string & front,
const std::string & left,
const std::string & bottom,
const std::string & back,
const std::string & name = "Skybox");
~Skybox() = default;

View File

@@ -14,46 +14,65 @@
using namespace Qtk;
QImage OpenGLTextureFactory::initImage(
const char * image, bool flipX, bool flipY) {
QImage OpenGLTextureFactory::initImage(const char * image,
bool flipX,
bool flipY)
{
// Qt6 limits loaded images to 256MB by default
QImageReader::setAllocationLimit(1024);
auto loadedImage = QImage(image).mirrored(flipX, flipY);
if(loadedImage.isNull()) {
if (loadedImage.isNull()) {
return defaultTexture();
}
return loadedImage;
}
QOpenGLTexture * OpenGLTextureFactory::initTexture(
const char * texture, bool flipX, bool flipY) {
QOpenGLTexture * OpenGLTextureFactory::initTexture(const char * texture,
bool flipX,
bool flipY)
{
QImage image = initImage(texture, flipX, flipY);
auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
newTexture->setData(image);
newTexture->setWrapMode(QOpenGLTexture::Repeat);
newTexture->setMinMagFilters(
QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
newTexture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,
QOpenGLTexture::Linear);
return newTexture;
}
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * tile) {
return initCubeMap(
QImage(tile), QImage(tile), QImage(tile), QImage(tile), QImage(tile),
QImage(tile));
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * tile)
{
return initCubeMap(QImage(tile),
QImage(tile),
QImage(tile),
QImage(tile),
QImage(tile),
QImage(tile));
}
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
const char * right, const char * top, const char * front, const char * left,
const char * bottom, const char * back) {
return initCubeMap(
QImage(right), QImage(top), QImage(front), QImage(left), QImage(bottom),
QImage(back));
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * right,
const char * top,
const char * front,
const char * left,
const char * bottom,
const char * back)
{
return initCubeMap(QImage(right),
QImage(top),
QImage(front),
QImage(left),
QImage(bottom),
QImage(back));
}
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
const QImage & right, const QImage & top, const QImage & front,
const QImage & left, const QImage & bottom, const QImage & back) {
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const QImage & right,
const QImage & top,
const QImage & front,
const QImage & left,
const QImage & bottom,
const QImage & back)
{
auto texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
std::vector<QImage> faceTextures = {right, top, front, left, bottom, back};
// Initialize skybox cubemap texture
@@ -61,20 +80,23 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
texture->bind();
// For each cube map face
std::vector<QOpenGLTexture::CubeMapFace> faces = {
QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::CubeMapPositiveY,
QOpenGLTexture::CubeMapPositiveZ, QOpenGLTexture::CubeMapNegativeX,
QOpenGLTexture::CubeMapNegativeY, QOpenGLTexture::CubeMapNegativeZ};
QOpenGLTexture::CubeMapPositiveX,
QOpenGLTexture::CubeMapPositiveY,
QOpenGLTexture::CubeMapPositiveZ,
QOpenGLTexture::CubeMapNegativeX,
QOpenGLTexture::CubeMapNegativeY,
QOpenGLTexture::CubeMapNegativeZ};
int i = 0;
for(const auto & face : faces) {
for (const auto & face : faces) {
QImage faceImage(faceTextures[i]);
if(faceImage.isNull()) {
if (faceImage.isNull()) {
qDebug() << "Error loading cube map image\n";
faceImage = defaultTexture();
}
faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888);
// On the first iteration, set format and allocate texture storage
if(face == QOpenGLTexture::CubeMapPositiveX) {
if (face == QOpenGLTexture::CubeMapPositiveX) {
// This also needs to happen on the first iteration, anyways
texture->setSize(
faceImage.width(), faceImage.height(), faceImage.depth());
@@ -82,9 +104,12 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
texture->allocateStorage();
}
texture->setData(
0, 0, face, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
faceImage.constBits());
texture->setData(0,
0,
face,
QOpenGLTexture::RGBA,
QOpenGLTexture::UInt8,
faceImage.constBits());
i++;
}

View File

@@ -16,30 +16,33 @@
#include "qtkapi.h"
namespace Qtk {
namespace Qtk
{
/**
* Binds shader programs until the end of scope.
* Does nothing if the shader program was already bound.
*
* See MeshRenderer::setUniform() for example.
*/
class QTKAPI ShaderBindScope {
class QTKAPI ShaderBindScope
{
public:
/*************************************************************************
* Constructors / Destructors
************************************************************************/
explicit ShaderBindScope(
QOpenGLShaderProgram * program, bool was_locked) :
mWasBound(was_locked) {
explicit ShaderBindScope(QOpenGLShaderProgram * program,
bool was_locked) : mWasBound(was_locked)
{
mProgram = program;
if(!mWasBound) {
if (!mWasBound) {
mProgram->bind();
}
}
~ShaderBindScope() {
if(!mWasBound) {
~ShaderBindScope()
{
if (!mWasBound) {
mProgram->release();
}
}
@@ -56,7 +59,8 @@ namespace Qtk {
/**
* Factories for initializing various OpenGL textures
*/
class QTKAPI OpenGLTextureFactory {
class QTKAPI OpenGLTextureFactory
{
public:
/*************************************************************************
* Constructors / Destructors
@@ -76,8 +80,9 @@ namespace Qtk {
* @param flipY If true the image will be flipped on Y axis.
* @return QImage object.
*/
static QImage initImage(
const char * image, bool flipX = false, bool flipY = false);
static QImage initImage(const char * image,
bool flipX = false,
bool flipY = false);
/**
* QOpenGLTexture factory
@@ -88,8 +93,9 @@ namespace Qtk {
* @param flipY If true the image will be flipped on Y axis.
* @return Pointer to an initialized QOpenGLTexture object.
*/
static QOpenGLTexture * initTexture(
const char * texture, bool flipX = false, bool flipY = false);
static QOpenGLTexture * initTexture(const char * texture,
bool flipX = false,
bool flipY = false);
/**
* Cube map factory for initializing all sides of a CubeMap.
@@ -103,9 +109,12 @@ namespace Qtk {
* @param back Path to image for the back side of the CubeMap.
* @return Pointer to an initialized QOpenGLTexture object.
*/
static QOpenGLTexture * initCubeMap(
const QImage & right, const QImage & top, const QImage & front,
const QImage & left, const QImage & bottom, const QImage & back);
static QOpenGLTexture * initCubeMap(const QImage & right,
const QImage & top,
const QImage & front,
const QImage & left,
const QImage & bottom,
const QImage & back);
/**
* CubeMap factory for tiling the same image on all sides.
@@ -128,12 +137,16 @@ namespace Qtk {
* @param back Path to image for the back side of the CubeMap.
* @return Pointer to an initialized QOpenGLTexture object.
*/
static QOpenGLTexture * initCubeMap(
const char * right, const char * top, const char * front,
const char * left, const char * bottom, const char * back);
static QOpenGLTexture * initCubeMap(const char * right,
const char * top,
const char * front,
const char * left,
const char * bottom,
const char * back);
/// The texture used in place of a missing texture.
static QImage defaultTexture() {
static QImage defaultTexture()
{
// Use plaster for default texture if image fails to load.
// This prevents segfaults when loading a texture that doesn't exist.
// TODO: Replace with a '?' texture to indicate missing texture.
@@ -151,7 +164,8 @@ namespace Qtk {
* TODO: Struct for (re)storing texture state
* A struct to store flipX, flipY and other initial state needed to copy
*/
class Texture {
class Texture
{
public:
/*************************************************************************
* Typedefs
@@ -169,7 +183,8 @@ namespace Qtk {
*
* @param value Texture to copy.
*/
Texture(const Texture & value) {
Texture(const Texture & value)
{
mOpenGLTexture = OpenGLTextureFactory::initTexture(value.mPath);
mPath = value.mPath;
}
@@ -179,10 +194,13 @@ namespace Qtk {
* @param flipX True if texture is to be flipped on the X axis.
* @param flipY True if texture is to be flipped on the Y axis.
*/
explicit Texture(
const char * path, bool flipX = false, bool flipY = false) :
explicit Texture(const char * path,
bool flipX = false,
bool flipY = false) :
mOpenGLTexture(OpenGLTextureFactory::initTexture(path, flipX, flipY)),
mPath(path) {}
mPath(path)
{
}
/**
* Construct a Texture using an existing QOpenGLTexture.
@@ -200,7 +218,8 @@ namespace Qtk {
/**
* @return True if the OpenGL texture has been initialized.
*/
[[nodiscard]] inline bool hasTexture() const {
[[nodiscard]] inline bool hasTexture() const
{
return mOpenGLTexture != Q_NULLPTR;
}
@@ -211,7 +230,8 @@ namespace Qtk {
/**
* @return QOpenGLTexture associated with this Texture.
*/
[[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const {
[[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const
{
return *mOpenGLTexture;
}
@@ -231,8 +251,10 @@ namespace Qtk {
* @param flipX True if texture is to be flipped on the X axis.
* @param flipY True if texture is to be flipped on the Y axis.
*/
inline void setTexture(
const std::string & path, bool flipX = false, bool flipY = false) {
inline void setTexture(const std::string & path,
bool flipX = false,
bool flipY = false)
{
setTexture(path.c_str(), flipX, flipY);
}
@@ -241,8 +263,10 @@ namespace Qtk {
* @param flipX True if texture is to be flipped on the X axis.
* @param flipY True if texture is to be flipped on the Y axis.
*/
inline void setTexture(
const char * path, bool flipX = false, bool flipY = false) {
inline void setTexture(const char * path,
bool flipX = false,
bool flipY = false)
{
mOpenGLTexture = OpenGLTextureFactory::initTexture(path, flipX, flipY);
mPath = path;
}
@@ -252,7 +276,8 @@ namespace Qtk {
*
* @param path Path to texture to use for all sides of the cube map.
*/
virtual inline void setCubeMap(const char * path) {
virtual inline void setCubeMap(const char * path)
{
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(path);
mPath = path;
}
@@ -267,9 +292,13 @@ namespace Qtk {
* @param bottom Path to texture to use for bottom cube map side.
* @param back Path to texture to use for back cube map side.
*/
virtual inline void setCubeMap(
const char * right, const char * top, const char * front,
const char * left, const char * bottom, const char * back) {
virtual inline void setCubeMap(const char * right,
const char * top,
const char * front,
const char * left,
const char * bottom,
const char * back)
{
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(
right, top, front, left, bottom, back);
}
@@ -284,9 +313,13 @@ namespace Qtk {
* @param bottom Path to texture to use for bottom cube map side.
* @param back Path to texture to use for back cube map side.
*/
virtual inline void setCubeMap(
const QImage & right, const QImage & top, const QImage & front,
const QImage & left, const QImage & bottom, const QImage & back) {
virtual inline void setCubeMap(const QImage & right,
const QImage & top,
const QImage & front,
const QImage & left,
const QImage & bottom,
const QImage & back)
{
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(
right, top, front, left, bottom, back);
}
@@ -299,7 +332,8 @@ namespace Qtk {
/**
* @param texture QOpenGLTexture to use for this Texture.
*/
inline void setTexture(QOpenGLTexture * texture) {
inline void setTexture(QOpenGLTexture * texture)
{
mOpenGLTexture = texture;
}

View File

@@ -19,45 +19,52 @@ const QVector3D Transform3D::LocalRight(1.0f, 0.0f, 0.0f);
* Public Methods
******************************************************************************/
void Transform3D::translate(const QVector3D & dt) {
void Transform3D::translate(const QVector3D & dt)
{
m_dirty = true;
mTranslation += dt;
}
void Transform3D::scale(const QVector3D & ds) {
void Transform3D::scale(const QVector3D & ds)
{
m_dirty = true;
mScale *= ds;
}
void Transform3D::grow(const QVector3D & ds) {
void Transform3D::grow(const QVector3D & ds)
{
m_dirty = true;
mScale += ds;
}
void Transform3D::rotate(const QQuaternion & dr) {
void Transform3D::rotate(const QQuaternion & dr)
{
m_dirty = true;
mRotation = dr * mRotation;
}
void Transform3D::setTranslation(const QVector3D & t) {
void Transform3D::setTranslation(const QVector3D & t)
{
m_dirty = true;
qtkDebug() << "Setting translation to " << t;
mTranslation = t;
}
void Transform3D::setScale(const QVector3D & s) {
void Transform3D::setScale(const QVector3D & s)
{
m_dirty = true;
mScale = s;
}
void Transform3D::setRotation(const QQuaternion & r) {
void Transform3D::setRotation(const QQuaternion & r)
{
m_dirty = true;
mRotation = r;
}
const QMatrix4x4 & Transform3D::toMatrix() {
if(m_dirty) {
const QMatrix4x4 & Transform3D::toMatrix()
{
if (m_dirty) {
m_dirty = false;
mWorld.setToIdentity();
mWorld.translate(mTranslation);
@@ -67,14 +74,63 @@ const QMatrix4x4 & Transform3D::toMatrix() {
return mWorld;
}
QVector3D Transform3D::getForward() const {
QVector3D Transform3D::getForward() const
{
return mRotation.rotatedVector(LocalForward);
}
QVector3D Transform3D::getUp() const {
QVector3D Transform3D::getUp() const
{
return mRotation.rotatedVector(LocalUp);
}
QVector3D Transform3D::getRight() const {
QVector3D Transform3D::getRight() const
{
return mRotation.rotatedVector(LocalRight);
}
/*******************************************************************************
* Private Methods
******************************************************************************/
namespace Qtk
{
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform)
{
dbg << "Transform3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Scale: <" << transform.getScale().x() << ", "
<< transform.getScale().y() << ", " << transform.getScale().z()
<< ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
return dbg;
}
#endif
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Transform3D & transform)
{
out << transform.mTranslation;
out << transform.mScale;
out << transform.mRotation;
return out;
}
QDataStream & operator>>(QDataStream & in, Transform3D & transform)
{
in >> transform.mTranslation;
in >> transform.mScale;
in >> transform.mRotation;
transform.m_dirty = true;
return in;
}
#endif
} // namespace Qtk

View File

@@ -20,11 +20,13 @@
#include "qtkapi.h"
namespace Qtk {
namespace Qtk
{
/**
* Transform3D class to represent and modify object position in 3D space.
*/
class QTKAPI Transform3D {
class QTKAPI Transform3D
{
public:
/*************************************************************************
* Constructors, Destructors
@@ -32,7 +34,9 @@ namespace Qtk {
inline Transform3D() :
m_dirty(true), mScale(1.0f, 1.0f, 1.0f),
mTranslation(0.0f, 0.0f, 0.0f) {}
mTranslation(0.0f, 0.0f, 0.0f)
{
}
/*************************************************************************
* Public Methods
@@ -48,7 +52,8 @@ namespace Qtk {
* @param dy Y translation from last to current position.
* @param dz Z translation from last to current position.
*/
inline void translate(float dx, float dy, float dz) {
inline void translate(float dx, float dy, float dz)
{
translate(QVector3D(dx, dy, dz));
}
@@ -66,7 +71,8 @@ namespace Qtk {
* @param dy Amount to scale on the Y axis.
* @param dz Amount to scale on the Z axis.
*/
inline void scale(float dx, float dy, float dz) {
inline void scale(float dx, float dy, float dz)
{
scale(QVector3D(dx, dy, dz));
}
@@ -75,7 +81,8 @@ namespace Qtk {
*
* @param factor Scalar to apply to all axis of the object.
*/
inline void scale(float factor) {
inline void scale(float factor)
{
scale(QVector3D(factor, factor, factor));
}
@@ -89,14 +96,16 @@ namespace Qtk {
* @param dy Amount to grow Y axis.
* @param dz Amount to grow Z axis.
*/
inline void grow(float dx, float dy, float dz) {
inline void grow(float dx, float dy, float dz)
{
grow(QVector3D(dx, dy, dz));
}
/**
* @param factor Amount to grow all axis equally.
*/
inline void grow(float factor) {
inline void grow(float factor)
{
grow(QVector3D(factor, factor, factor));
}
@@ -109,19 +118,21 @@ namespace Qtk {
* @param angle Angle to rotate.
* @param axis Axis to rotate apply the rotation on.
*/
inline void rotate(float angle, const QVector3D & axis) {
inline void rotate(float angle, const QVector3D & axis)
{
rotate(QQuaternion::fromAxisAndAngle(axis, angle));
}
/**
* Apply rotation upon an axis represented by the 3D vector (x, y, z)
*
* @param angle Angle to rotate.
* @param ax X axis to apply the rotation on.
* @param ay Y axis to apply the rotation on.
* @param az Z axis to apply the rotation on.
* @param angle Angle to rotate.
*/
inline void rotate(float ax, float ay, float az, float angle) {
inline void rotate(float angle, float ax, float ay, float az)
{
rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
}
@@ -139,7 +150,8 @@ namespace Qtk {
* @param y Y position to set transform.
* @param z Z position to set transform.
*/
inline void setTranslation(float x, float y, float z) {
inline void setTranslation(float x, float y, float z)
{
setTranslation(QVector3D(x, y, z));
}
@@ -153,7 +165,8 @@ namespace Qtk {
* @param y Y axis scale to set for this transform.
* @param z Z axis scale to set for this transform.
*/
inline void setScale(float x, float y, float z) {
inline void setScale(float x, float y, float z)
{
setScale(QVector3D(x, y, z));
}
@@ -171,19 +184,21 @@ namespace Qtk {
* @param angle Angle to set for rotation.
* @param axis Axis to set rotation for.
*/
inline void setRotation(float angle, const QVector3D & axis) {
inline void setRotation(float angle, const QVector3D & axis)
{
setRotation(QQuaternion::fromAxisAndAngle(axis, angle));
}
/**
* Sets a rotation upon an axis represented by the 3D vector (x, y, z)
*
* @param angle Angle to set rotation.
* @param ax X axis to set angle for.
* @param ay Y axis to set angle for.
* @param az Z axis to set angle for.
* @param angle Angle to set rotation.
*/
inline void setRotation(float ax, float ay, float az, float angle) {
inline void setRotation(float angle, float ax, float ay, float az)
{
setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
}
@@ -194,7 +209,8 @@ namespace Qtk {
/**
* @return Translation for this transform.
*/
[[nodiscard]] inline const QVector3D & getTranslation() const {
[[nodiscard]] inline const QVector3D & getTranslation() const
{
return mTranslation;
}
@@ -206,7 +222,8 @@ namespace Qtk {
/**
* @return Rotation for this transform.
*/
[[nodiscard]] inline const QQuaternion & getRotation() const {
[[nodiscard]] inline const QQuaternion & getRotation() const
{
return mRotation;
}
@@ -249,17 +266,22 @@ namespace Qtk {
bool m_dirty;
/*************************************************************************
* Qt Streams
************************************************************************/
#ifndef QT_NO_DATASTREAM
friend QDataStream & operator<<(
QDataStream & out, const Transform3D & transform);
friend QDataStream & operator>>(
QDataStream & in, Transform3D & transform);
friend QDataStream & operator<<(QDataStream & out,
const Transform3D & transform);
friend QDataStream & operator>>(QDataStream & in,
Transform3D & transform);
#endif
};
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform);
#endif
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Transform3D & transform);
QDataStream & operator>>(QDataStream & in, Transform3D & transform);
#endif
} // namespace Qtk
Q_DECLARE_TYPEINFO(Qtk::Transform3D, Q_MOVABLE_TYPE);

1
tools/build-qt.sh Normal file
View File

@@ -0,0 +1 @@
#!/usr/bin/env bash

7
tools/format.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
cmake -B build && cmake --build build -- -j $(nproc --ignore=1)
clang-tidy -p build/ --fix --config-file=.clang-tidy \
src/**/*.cpp src/**/*.h example-app/*.cpp example-app/*.h
clang-format -i --style=file:.clang-format \
src/**/*.cpp src/**/*.h example-app/*.cpp example-app/*.h