Compare commits

...

7 Commits

Author SHA1 Message Date
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
52 changed files with 1215 additions and 802 deletions

View File

@ -1,6 +1,6 @@
---
BasedOnStyle: Google
AlignAfterOpenBracket: AlwaysBreak
AlignAfterOpenBracket: Align
AlignArrayOfStructures: Left
AlignConsecutiveAssignments: None
AlignConsecutiveDeclarations: None
@ -23,6 +23,7 @@ EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
BinPackArguments: true
BinPackParameters: true
BreakBeforeBraces: Linux
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
@ -58,7 +59,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'
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'

2
.gitignore vendored
View File

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

View File

@ -7,24 +7,40 @@ Qtk is a Qt OpenGL graphics library created primarily for my own learning
purposes. The library wraps some QOpenGL functionality in convenience classes
that allow rendering geometry in 2D and 3D using custom GLSL shader programs.
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.
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.
- [x] Runtime loading of `.obj` or similar 3D models.
- [x] 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.
![](resources/screenshot.png)
Spartan with no normals -
![](resources/spartan-specular.png)
Spartan with normals -
![](resources/spartan-normals.png)
Object names can be double-clicked in the tree view panel for quick camera
navigation. 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. See the `View` menu to enable debug
console widgets for open scenes or reopen previously closed panels.
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.
For examples of using the Qtk API, see the `example-app` project in the root of
this repository.
@ -177,55 +193,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 +219,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().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,11 +56,20 @@ 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()
@ -69,27 +78,20 @@ void ExampleScene::update() {
->getTransform()
.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.75f, 0.2f, 0.0f, 0.4f);
MeshRenderer::getInstance("bottomTriangle")
->getTransform()
.rotate(0.75f, 0.0f, 0.2f, 0.4f);
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()

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

@ -15,10 +15,13 @@
using namespace Qtk;
DebugConsole::DebugConsole(QWidget * owner, const QString & key) :
DebugConsole(owner, key, key + "Debugger") {}
DebugConsole(owner, key, key + "Debugger")
{
}
DebugConsole::DebugConsole(
QWidget * owner, const QString & key, const QString & name) {
DebugConsole::DebugConsole(QWidget * owner, const QString & key,
const QString & name)
{
ui_ = new Ui::DebugConsole;
ui_->setupUi(this);
setObjectName(name);
@ -27,7 +30,7 @@ DebugConsole::DebugConsole(
setWindowTitle(name + " Debug Console");
auto qtkWidget = dynamic_cast<QtkWidget *>(owner);
if(qtkWidget) {
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,7 +64,8 @@ namespace Qtk {
* @param context The DebugContext to use for the message.
* Default value is Status.
*/
inline void sendLog(QString message, DebugContext context = Status) {
inline void sendLog(QString message, DebugContext context = Status)
{
mConsole->setTextColor(logColor(context));
mConsole->append(logPrefix(message, context));
}
@ -72,7 +76,8 @@ namespace Qtk {
*
* @param name Base name for the DebugConsole window.
*/
inline void setTitle(QString name) {
inline void setTitle(const QString & name)
{
setWindowTitle(name + " Debug Console");
}
@ -81,8 +86,9 @@ namespace Qtk {
* @param context Log context severity level.
* @return QColor corresponding with the message context.
*/
[[nodiscard]] QColor logColor(const DebugContext & context) const {
switch(context) {
[[nodiscard]] QColor logColor(const DebugContext & context) const
{
switch (context) {
case Status:
return Qt::GlobalColor::darkGray;
case Debug:
@ -105,10 +111,11 @@ namespace Qtk {
* @param context The log context severity level.
* @return The log message prefixed with the DebugContext level.
*/
[[nodiscard]] QString logPrefix(
QString & message, const DebugContext & context) {
[[nodiscard]] QString logPrefix(QString & message,
const DebugContext & context)
{
QString prefix;
switch(context) {
switch (context) {
case Status:
prefix = "[Status]: ";
break;

View File

@ -11,18 +11,14 @@
#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);
auto window = MainWindow::getMainWindow();
// Qtk currently uses the decorator pattern to save / load scenes.
// This is a temporary solution and will be improved in the future.
auto emptyScene = new Qtk::SceneEmpty;
window->getQtkWidget()->setScene(new QtkScene(emptyScene));
window->show();
return QApplication::exec();
}

View File

@ -16,7 +16,8 @@ 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 +27,19 @@ 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) {
// Qtk currently uses the decorator pattern to save / load scenes.
// This is a temporary solution and will be improved in the future.
qtkWidget->setScene(new QtkScene);
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 +59,8 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
setWindowIcon(Qtk::getIcon());
}
MainWindow::~MainWindow() {
MainWindow::~MainWindow()
{
delete ui_;
}
@ -66,28 +68,34 @@ MainWindow::~MainWindow() {
* Public Methods
******************************************************************************/
MainWindow * MainWindow::getMainWindow() {
if(mainWindow_ == Q_NULLPTR) {
MainWindow * MainWindow::getMainWindow()
{
if (mainWindow_ == Q_NULLPTR) {
mainWindow_ = new MainWindow;
}
return mainWindow_;
}
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,7 +17,8 @@
#include "debugconsole.h"
#include "qtkwidget.h"
namespace Ui {
namespace Ui
{
class MainWindow;
}
@ -25,7 +26,8 @@ namespace Ui {
* 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:

View File

@ -14,13 +14,15 @@ using namespace Qtk;
* Constructors, Destructors
******************************************************************************/
QtkScene::QtkScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) {
QtkScene::QtkScene()
{
setSceneName("Qtk Scene");
getCamera().getTransform().setTranslation(0.0f, 0.0f, 20.0f);
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
QtkScene::~QtkScene() {
QtkScene::~QtkScene()
{
delete mTestPhong;
delete mTestSpecular;
delete mTestDiffuse;
@ -31,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"));
@ -116,8 +119,8 @@ void QtkScene::init() {
// NOTE: You no longer need to manually bind shader program to set uniforms.
// + You can still bind it if you want to for performance reasons.
// + Qtk will only bind / release if the shader program is not already bound.
mTestPhong->setShaders(
":/shaders/solid-phong.vert", ":/shaders/solid-phong.frag");
mTestPhong->setShaders(":/shaders/solid-phong.vert",
":/shaders/solid-phong.frag");
// For example this would technically not be efficient, because each one of
// these calls will bind, set, release. We could instead bind, set N uniforms,
@ -143,16 +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->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));
@ -163,8 +166,8 @@ void QtkScene::init() {
/* Initialize Diffuse example cube */
mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube());
mTestDiffuse->getTransform().setTranslation(9.0f, 0.0f, -2.0f);
mTestDiffuse->setShaders(
":/shaders/solid-diffuse.vert", ":/shaders/solid-diffuse.frag");
mTestDiffuse->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);
@ -179,8 +182,8 @@ void QtkScene::init() {
/* Initialize Specular example cube */
mTestSpecular = new Qtk::MeshRenderer("specular", Cube());
mTestSpecular->getTransform().setTranslation(11.0f, 0.0f, -2.0f);
mTestSpecular->setShaders(
":/shaders/solid-specular.vert", ":/shaders/solid-specular.frag");
mTestSpecular->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);
@ -246,8 +249,8 @@ 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
@ -290,8 +293,8 @@ void QtkScene::init() {
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS
mesh = addObject(new Qtk::MeshRenderer(
"rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh = addObject(new Qtk::MeshRenderer("rgbNormalsCubeElementsTest",
Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(5.0f, 0.0f, 2.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
@ -321,8 +324,8 @@ void QtkScene::init() {
mesh->reallocateTexCoords(mesh->getTexCoords());
// Test drawing a cube with texture coordinates using glDrawElements
mesh = addObject(new Qtk::MeshRenderer(
"uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh = addObject(new Qtk::MeshRenderer("uvCubeElementsTest",
Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(-1.7f, 0.0f, -2.0f);
mesh->setTexture(":/textures/crate.png");
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
@ -339,8 +342,8 @@ void QtkScene::init() {
addObject(new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS)));
mesh->getTransform().setTranslation(-3.0f, 1.0f, -2.0f);
mesh->getTransform().setRotation(45.0f, 0.0f, 1.0f, 0.0f);
mesh->setShaders(
":/shaders/texture-cubemap.vert", ":/shaders/texture-cubemap.frag");
mesh->setShaders(":/shaders/texture-cubemap.vert",
":/shaders/texture-cubemap.frag");
mesh->setCubeMap(":/textures/crate.png");
mesh->setUniform("uTexture", 0);
mesh->reallocateTexCoords(mesh->getTexCoords());
@ -354,15 +357,15 @@ void QtkScene::init() {
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS
mesh = addObject(new Qtk::MeshRenderer(
"rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS)));
mesh = addObject(new Qtk::MeshRenderer("rgbTriangleArraysTest",
Triangle(QTK_DRAW_ARRAYS)));
mesh->getTransform().setTranslation(7.0f, 0.0f, 2.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
// RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS
mesh = addObject(new Qtk::MeshRenderer(
"rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh = addObject(new Qtk::MeshRenderer("rgbTriangleElementsTest",
Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(7.0f, 0.0f, 4.0f);
mesh->setShaders(":/shaders/rgb-normals.vert", ":/shaders/rgb-normals.frag");
mesh->reallocateNormals(mesh->getNormals());
@ -378,8 +381,8 @@ void QtkScene::init() {
mesh->reallocateTexCoords(mesh->getTexCoords());
// Test drawing triangle with glDrawElements with texture coordinates
mesh = addObject(new Qtk::MeshRenderer(
"testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh = addObject(new Qtk::MeshRenderer("testTriangleElementsUV",
Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mesh->getTransform().setTranslation(-2.5f, 0.0f, -1.0f);
mesh->setShaders(":/shaders/texture2d.vert", ":/shaders/texture2d.frag");
mesh->setTexture(":/textures/crate.png");
@ -387,20 +390,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();
@ -414,10 +417,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();
@ -427,17 +430,18 @@ 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.75f, 0.0f, 1.0f, 0.0f);
@ -449,8 +453,8 @@ void QtkScene::update() {
.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);
@ -463,8 +467,8 @@ void QtkScene::update() {
.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);
@ -478,8 +482,8 @@ void QtkScene::update() {
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,8 +493,8 @@ void QtkScene::update() {
// Rotate lighting example cubes
mTestPhong->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
MeshRenderer::getInstance("noLight")->getTransform().rotate(
0.75f, 0.5f, 0.3f, 0.2f);
MeshRenderer::getInstance("noLight")->getTransform().rotate(0.75f, 0.5f, 0.3f,
0.2f);
mTestAmbient->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
mTestDiffuse->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
mTestSpecular->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
@ -517,7 +521,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")

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

@ -32,11 +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(Q_NULLPTR),
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR) {
mConsole(new DebugConsole(this, name)), mScene(Q_NULLPTR)
{
setAcceptDrops(true);
setScene(scene);
setObjectName(name);
@ -54,7 +57,8 @@ QtkWidget::QtkWidget(QWidget * parent, const QString & name, Scene * scene) :
setFocusPolicy(Qt::ClickFocus);
}
QtkWidget::~QtkWidget() {
QtkWidget::~QtkWidget()
{
makeCurrent();
teardownGL();
}
@ -63,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);
@ -72,7 +77,8 @@ 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()));
@ -80,11 +86,10 @@ void QtkWidget::initializeGL() {
toggleConsole();
// Initialize OpenGL debug context
mDebugLogger = new QOpenGLDebugLogger(this);
if(mDebugLogger->initialize()) {
if (mDebugLogger->initialize()) {
qDebug() << "GL_DEBUG Debug Logger" << mDebugLogger << "\n";
connect(
mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this,
SLOT(messageLogged(QOpenGLDebugMessage)));
connect(mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this,
SLOT(messageLogged(QOpenGLDebugMessage)));
mDebugLogger->startLogging();
}
@ -101,38 +106,41 @@ 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);
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 {
@ -147,17 +155,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;
@ -165,7 +175,7 @@ void QtkWidget::dropEvent(QDropEvent * event) {
// TODO: Support other object types.
auto url = urls.front();
if(url.fileName().endsWith(".obj")) {
if (url.fileName().endsWith(".obj")) {
mScene->loadModel(url);
event->acceptProposedAction();
} else {
@ -175,8 +185,9 @@ void QtkWidget::dropEvent(QDropEvent * event) {
}
}
void QtkWidget::keyPressEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
void QtkWidget::keyPressEvent(QKeyEvent * event)
{
if (event->isAutoRepeat()) {
// Do not repeat input while a key is held down
event->ignore();
} else {
@ -184,38 +195,43 @@ void QtkWidget::keyPressEvent(QKeyEvent * event) {
}
}
void QtkWidget::keyReleaseEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
void QtkWidget::keyReleaseEvent(QKeyEvent * event)
{
if (event->isAutoRepeat()) {
event->ignore();
} else {
Input::registerKeyRelease(event->key());
}
}
void QtkWidget::mousePressEvent(QMouseEvent * event) {
void QtkWidget::mousePressEvent(QMouseEvent * event)
{
Input::registerMousePress(event->button());
}
void QtkWidget::mouseReleaseEvent(QMouseEvent * event) {
void QtkWidget::mouseReleaseEvent(QMouseEvent * event)
{
Input::registerMouseRelease(event->button());
}
void QtkWidget::update() {
void QtkWidget::update()
{
updateCameraInput();
if(mScene != Q_NULLPTR) {
if (mScene != Q_NULLPTR) {
mScene->update();
}
QWidget::update();
}
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg)
{
QString error;
DebugContext context;
// Format based on severity
switch(msg.severity()) {
switch (msg.severity()) {
case QOpenGLDebugMessage::NotificationSeverity:
error += "--";
context = Status;
@ -241,7 +257,7 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
case QOpenGLDebugMessage::c: \
error += #c; \
break
switch(msg.source()) {
switch (msg.source()) {
CASE(APISource);
CASE(WindowSystemSource);
CASE(ShaderCompilerSource);
@ -259,7 +275,7 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
case QOpenGLDebugMessage::c: \
error += #c; \
break
switch(msg.type()) {
switch (msg.type()) {
CASE(InvalidType);
CASE(ErrorType);
CASE(DeprecatedBehaviorType);
@ -282,14 +298,16 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
* Private Methods
******************************************************************************/
void QtkWidget::teardownGL() { /* Nothing to teardown yet... */
void QtkWidget::teardownGL()
{ /* Nothing to teardown yet... */
}
void QtkWidget::updateCameraInput() {
void QtkWidget::updateCameraInput()
{
Input::update();
// Camera Transformation
if(Input::buttonPressed(Qt::LeftButton)
|| Input::buttonPressed(Qt::RightButton)) {
if (Input::buttonPressed(Qt::LeftButton)
|| Input::buttonPressed(Qt::RightButton)) {
static const float transSpeed = 0.1f;
static const float rotSpeed = 0.5f;
@ -301,29 +319,30 @@ void QtkWidget::updateCameraInput() {
// Handle translations
QVector3D translation;
if(Input::keyPressed(Qt::Key_W)) {
if (Input::keyPressed(Qt::Key_W)) {
translation += Scene::getCamera().getForward();
}
if(Input::keyPressed(Qt::Key_S)) {
if (Input::keyPressed(Qt::Key_S)) {
translation -= Scene::getCamera().getForward();
}
if(Input::keyPressed(Qt::Key_A)) {
if (Input::keyPressed(Qt::Key_A)) {
translation -= Scene::getCamera().getRight();
}
if(Input::keyPressed(Qt::Key_D)) {
if (Input::keyPressed(Qt::Key_D)) {
translation += Scene::getCamera().getRight();
}
if(Input::keyPressed(Qt::Key_Q)) {
if (Input::keyPressed(Qt::Key_Q)) {
translation -= Scene::getCamera().getUp() / 2.0f;
}
if(Input::keyPressed(Qt::Key_E)) {
if (Input::keyPressed(Qt::Key_E)) {
translation += Scene::getCamera().getUp() / 2.0f;
}
Scene::getCamera().getTransform().translate(transSpeed * translation);
}
}
void QtkWidget::printContextInformation() {
void QtkWidget::printContextInformation()
{
QString glType;
QString glVersion;
QString glProfile;
@ -342,7 +361,7 @@ void QtkWidget::printContextInformation() {
case QSurfaceFormat::c: \
glProfile = #c; \
break
switch(format().profile()) {
switch (format().profile()) {
CASE(NoProfile);
CASE(CoreProfile);
CASE(CompatibilityProfile);

View File

@ -20,7 +20,8 @@
#include <qtk/qtkapi.h>
#include <qtk/scene.h>
namespace Qtk {
namespace Qtk
{
class DebugConsole;
/**
@ -29,7 +30,8 @@ namespace Qtk {
* This object has a Scene attached which manages the objects to render.
* Client input is passed through this widget to control the camera view.
*/
class QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
class QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT;
public:
@ -103,7 +105,8 @@ namespace Qtk {
/**
* @return Pointer to the QOpenGLDebugLogger attached to this widget.
*/
inline QOpenGLDebugLogger * getOpenGLDebugLogger() {
inline QOpenGLDebugLogger * getOpenGLDebugLogger()
{
return mDebugLogger;
}
@ -133,7 +136,7 @@ namespace Qtk {
// TODO: Use this signal in treeview and toolbox to update object
// properties
void objectFocusChanged(const QString objectName);
void objectFocusChanged(QString objectName);
protected:
/*************************************************************************

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,22 @@ 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,22 +96,23 @@ void ToolBox::createPageProperties(const Object * object) {
spinBox->setFixedWidth(minWidth);
rowLayout->addWidget(spinBox);
if(i == 0) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleX);
} else if(i == 1) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleY);
} else if(i == 2) {
connect(
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleZ);
if (i == 0) {
connect(spinBox, &QDoubleSpinBox::valueChanged, object,
&Object::setScaleX);
} else if (i == 1) {
connect(spinBox, &QDoubleSpinBox::valueChanged, object,
&Object::setScaleY);
} else if (i == 2) {
connect(spinBox, &QDoubleSpinBox::valueChanged, object,
&Object::setScaleZ);
}
}
layout->addRow(rowLayout);
widget->setLayout(layout);
}
void ToolBox::createPageShader(const Object * object) {
void ToolBox::createPageShader(const Object * object)
{
// Shaders page.
auto widget = new QWidget;
ui->toolBox->addItem(widget, "Shaders");
@ -123,7 +125,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 +140,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,15 @@
******************************************************************************/
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,31 +32,33 @@ 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) {
for (const auto & object : objects) {
auto item =
new QTreeWidgetItem(QStringList(QString(object->getName().c_str())));
ui->treeWidget->insertTopLevelItem(0, item);
}
}
void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) {
void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column)
{
QString name = item->text(column);
auto scene = MainWindow::getMainWindow()->getQtkWidget()->getScene();
auto & transform = scene->getCamera().getTransform();
auto & transform = Qtk::Scene::getCamera().getTransform();
auto object = scene->getObject(name);
Transform3D * objectTransform;
// If the object is a mesh or model, focus the camera on it.
if(object == Q_NULLPTR) {
if (object == Q_NULLPTR) {
qDebug() << "Attempt to get non-existing object with name '" << name
<< "'\n";
} else if(object->getType() == Object::QTK_MESH) {
} else if (object->getType() == Object::QTK_MESH) {
objectTransform = &dynamic_cast<MeshRenderer *>(object)->getTransform();
} else if(object->getType() == Object::QTK_MODEL) {
} else if (object->getType() == Object::QTK_MODEL) {
objectTransform = &dynamic_cast<Model *>(object)->getTransform();
}
auto focusScale = objectTransform->getScale();
@ -68,5 +71,5 @@ void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) {
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,13 @@
* 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 +33,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,8 @@ 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,7 +19,8 @@
******************************************************************************/
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",
@ -38,6 +39,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

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

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
@ -37,35 +39,40 @@ namespace Qtk {
/**
* @return Current translation of the camera as a QVector3D.
*/
[[nodiscard]] inline const QVector3D & getTranslation() const {
[[nodiscard]] inline const QVector3D & getTranslation() const
{
return mTransform.getTranslation();
}
/**
* @return Current rotation of this camera as a QQuaternion.
*/
[[nodiscard]] inline const QQuaternion & getRotation() const {
[[nodiscard]] inline const QQuaternion & getRotation() const
{
return mTransform.getRotation();
}
/**
* @return QVector3D for the forward vector of the camera.
*/
[[nodiscard]] inline QVector3D getForward() const {
[[nodiscard]] inline QVector3D getForward() const
{
return mTransform.getRotation().rotatedVector(LocalForward);
}
/**
* @return QVector3D for the right vector of the camera.
*/
[[nodiscard]] inline QVector3D getRight() const {
[[nodiscard]] inline QVector3D getRight() const
{
return mTransform.getRotation().rotatedVector(LocalRight);
}
/**
* @return QVector3D for the up vector of the camera.
*/
[[nodiscard]] inline QVector3D getUp() const {
[[nodiscard]] inline QVector3D getUp() const
{
return mTransform.getRotation().rotatedVector(LocalUp);
}

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,28 @@ 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 +50,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 +66,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 +90,9 @@ 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 +100,27 @@ 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 +128,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 +162,46 @@ 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;
}
}
@ -197,8 +209,9 @@ void MeshRenderer::setColor(const QVector3D & color) {
init();
}
void MeshRenderer::setAttributeBuffer(
int location, GLenum type, int offset, int tupleSize, int stride) {
void MeshRenderer::setAttributeBuffer(int location, GLenum type, int offset,
int tupleSize, int stride)
{
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.setAttributeBuffer(location, type, offset, tupleSize, stride);
@ -210,8 +223,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,8 @@ 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 +113,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 +137,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 +149,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 +164,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.
@ -192,8 +197,8 @@ namespace Qtk {
* @param stride Stride between groups of elements in the buffer.
* For example (x, y) data stride is `2 * sizeof(type)`
*/
void setAttributeBuffer(
int location, GLenum type, int offset, int tupleSize, int stride = 0);
void setAttributeBuffer(int location, GLenum type, int offset,
int tupleSize, int stride = 0);
/*************************************************************************
* Accessors
@ -211,11 +216,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.
@ -81,7 +86,8 @@ void Model::loadModel(const std::string & path) {
| 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,26 @@ 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,7 +236,7 @@ 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,
@ -246,7 +255,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,12 @@ 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 +87,8 @@ 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 +102,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 +129,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 +187,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++);
}
@ -57,11 +58,11 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
glActiveTexture(GL_TEXTURE0);
// Draw the mesh
glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
glDrawElements(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
@ -102,29 +104,29 @@ void ModelMesh::initMesh(const char * vert, const char * frag) {
// Positions
mProgram->enableAttributeArray(0);
mProgram->setAttributeBuffer(
0, GL_FLOAT, offsetof(ModelVertex, mPosition), 3, sizeof(ModelVertex));
mProgram->setAttributeBuffer(0, GL_FLOAT, offsetof(ModelVertex, mPosition), 3,
sizeof(ModelVertex));
// Normals
mProgram->enableAttributeArray(1);
mProgram->setAttributeBuffer(
1, GL_FLOAT, offsetof(ModelVertex, mNormal), 3, sizeof(ModelVertex));
mProgram->setAttributeBuffer(1, GL_FLOAT, offsetof(ModelVertex, mNormal), 3,
sizeof(ModelVertex));
// Texture Coordinates
mProgram->enableAttributeArray(2);
mProgram->setAttributeBuffer(
2, GL_FLOAT, offsetof(ModelVertex, mTextureCoord), 2,
sizeof(ModelVertex));
mProgram->setAttributeBuffer(2, GL_FLOAT,
offsetof(ModelVertex, mTextureCoord), 2,
sizeof(ModelVertex));
// Vertex tangents
mProgram->enableAttributeArray(3);
mProgram->setAttributeBuffer(
3, GL_FLOAT, offsetof(ModelVertex, mTangent), 3, sizeof(ModelVertex));
mProgram->setAttributeBuffer(3, GL_FLOAT, offsetof(ModelVertex, mTangent), 3,
sizeof(ModelVertex));
// Vertex bitangents
mProgram->enableAttributeArray(4);
mProgram->setAttributeBuffer(
4, GL_FLOAT, offsetof(ModelVertex, mBitangent), 3, sizeof(ModelVertex));
mProgram->setAttributeBuffer(4, GL_FLOAT, offsetof(ModelVertex, mBitangent),
3, sizeof(ModelVertex));
mProgram->release();
mVBO->release();

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,16 @@ 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,18 @@ 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 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 +127,97 @@ 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) {
mTransform.setScale(
x, mTransform.getScale().y(), mTransform.getScale().z());
inline void setScaleX(double x)
{
mTransform.setScale(x, mTransform.getScale().y(),
mTransform.getScale().z());
}
inline void setScaleY(double y) {
mTransform.setScale(
mTransform.getScale().x(), y, mTransform.getScale().z());
inline void setScaleY(double y)
{
mTransform.setScale(mTransform.getScale().x(), y,
mTransform.getScale().z());
}
inline void setScaleZ(double z) {
mTransform.setScale(
mTransform.getScale().x(), mTransform.getScale().y(), z);
inline void 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

@ -27,11 +27,13 @@
* 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.
*/
@ -43,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;
@ -54,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

@ -18,16 +18,18 @@ QMatrix4x4 Scene::mProjection;
* Constructors / Destructors
******************************************************************************/
Scene::Scene() : mSceneName("Default Scene") {
Scene::Scene() : mSceneName("Default Scene")
{
mCamera.getTransform().setTranslation(0.0f, 0.0f, 20.0f);
mCamera.getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
Scene::~Scene() {
for(auto & mesh : mMeshes) {
Scene::~Scene()
{
for (auto & mesh : mMeshes) {
delete mesh;
}
for(auto & model : mModels) {
for (auto & model : mModels) {
delete model;
}
delete mSkybox;
@ -37,83 +39,92 @@ 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())) {
void Scene::initSceneObjectName(Object * object)
{
if (!mObjectCount.count(object->getName())) {
mObjectCount[object->getName()] = 1;
} else {
mObjectCount[object->getName()]++;
}
auto count = mObjectCount[object->getName()];
if(count > 1) {
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,14 @@ 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 +131,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 +152,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 +211,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 +232,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,38 @@ 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);
@ -57,8 +59,8 @@ void Skybox::draw() {
mProgram.setUniformValue("uProjectionMatrix", Scene::getProjectionMatrix());
mProgram.setUniformValue("uViewMatrix", Scene::getCamera().toMatrix());
mProgram.setUniformValue("uTexture", 0);
glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
glDrawElements(GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT,
mIndices.data());
mTexture.getOpenGLTexture().bind();
mProgram.release();
@ -73,15 +75,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,10 @@ 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,49 @@
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));
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) {
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
@ -65,26 +68,25 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
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());
texture->setSize(faceImage.width(), faceImage.height(),
faceImage.depth());
texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
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,8 @@ 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 +92,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.
@ -128,12 +133,14 @@ 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 +158,8 @@ namespace Qtk {
* TODO: Struct for (re)storing texture state
* A struct to store flipX, flipY and other initial state needed to copy
*/
class Texture {
class Texture
{
public:
/*************************************************************************
* Typedefs
@ -169,7 +177,8 @@ namespace Qtk {
*
* @param value Texture to copy.
*/
Texture(const Texture & value) {
Texture(const Texture & value)
{
mOpenGLTexture = OpenGLTextureFactory::initTexture(value.mPath);
mPath = value.mPath;
}
@ -179,10 +188,12 @@ 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 +211,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 +223,8 @@ namespace Qtk {
/**
* @return QOpenGLTexture associated with this Texture.
*/
[[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const {
[[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const
{
return *mOpenGLTexture;
}
@ -231,8 +244,9 @@ 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 +255,9 @@ 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 +267,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,11 +283,12 @@ 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) {
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(
right, top, front, left, bottom, 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,11 +301,12 @@ 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) {
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(
right, top, front, left, bottom, 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);
}
private:
@ -299,7 +317,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,44 +19,52 @@ const QVector3D Transform3D::LocalRight(1.0f, 0.0f, 0.0f);
* Public Methods
******************************************************************************/
void Transform3D::translate(const QVector3D & dt) {
void Transform3D::translate(const QVector3D & dt)
{
m_dirty = true;
mTranslation += dt;
}
void Transform3D::scale(const QVector3D & ds) {
void Transform3D::scale(const QVector3D & ds)
{
m_dirty = true;
mScale *= ds;
}
void Transform3D::grow(const QVector3D & ds) {
void Transform3D::grow(const QVector3D & ds)
{
m_dirty = true;
mScale += ds;
}
void Transform3D::rotate(const QQuaternion & dr) {
void Transform3D::rotate(const QQuaternion & dr)
{
m_dirty = true;
mRotation = dr * mRotation;
}
void Transform3D::setTranslation(const QVector3D & t) {
void Transform3D::setTranslation(const QVector3D & t)
{
m_dirty = true;
mTranslation = t;
}
void Transform3D::setScale(const QVector3D & s) {
void Transform3D::setScale(const QVector3D & s)
{
m_dirty = true;
mScale = s;
}
void Transform3D::setRotation(const QQuaternion & r) {
void Transform3D::setRotation(const QQuaternion & r)
{
m_dirty = true;
mRotation = r;
}
const QMatrix4x4 & Transform3D::toMatrix() {
if(m_dirty) {
const QMatrix4x4 & Transform3D::toMatrix()
{
if (m_dirty) {
m_dirty = false;
mWorld.setToIdentity();
mWorld.translate(mTranslation);
@ -66,15 +74,18 @@ const QMatrix4x4 & Transform3D::toMatrix() {
return mWorld;
}
QVector3D Transform3D::getForward() const {
QVector3D Transform3D::getForward() const
{
return mRotation.rotatedVector(LocalForward);
}
QVector3D Transform3D::getUp() const {
QVector3D Transform3D::getUp() const
{
return mRotation.rotatedVector(LocalUp);
}
QVector3D Transform3D::getRight() const {
QVector3D Transform3D::getRight() const
{
return mRotation.rotatedVector(LocalRight);
}
@ -82,10 +93,12 @@ QVector3D Transform3D::getRight() const {
* Private Methods
******************************************************************************/
namespace Qtk {
namespace Qtk
{
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform) {
QDebug operator<<(QDebug dbg, const Transform3D & transform)
{
dbg << "Transform3D\n{\n";
dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
@ -102,14 +115,16 @@ namespace Qtk {
#endif
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Transform3D & transform) {
QDataStream & operator<<(QDataStream & out, const Transform3D & transform)
{
out << transform.mTranslation;
out << transform.mScale;
out << transform.mRotation;
return out;
}
QDataStream & operator>>(QDataStream & in, Transform3D & transform) {
QDataStream & operator>>(QDataStream & in, Transform3D & transform)
{
in >> transform.mTranslation;
in >> transform.mScale;
in >> transform.mRotation;

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,7 +118,8 @@ namespace Qtk {
* @param angle Angle to rotate.
* @param axis Axis to rotate apply the rotation on.
*/
inline void rotate(float angle, const QVector3D & axis) {
inline void rotate(float angle, const QVector3D & axis)
{
rotate(QQuaternion::fromAxisAndAngle(axis, angle));
}
@ -121,7 +131,8 @@ namespace Qtk {
* @param ay Y axis to apply the rotation on.
* @param az Z axis to apply the rotation on.
*/
inline void rotate(float angle, float ax, float ay, float az) {
inline void rotate(float angle, float ax, float ay, float az)
{
rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
}
@ -139,7 +150,8 @@ namespace Qtk {
* @param y Y position to set transform.
* @param z Z position to set transform.
*/
inline void setTranslation(float x, float y, float z) {
inline void setTranslation(float x, float y, float z)
{
setTranslation(QVector3D(x, y, z));
}
@ -153,7 +165,8 @@ namespace Qtk {
* @param y Y axis scale to set for this transform.
* @param z Z axis scale to set for this transform.
*/
inline void setScale(float x, float y, float z) {
inline void setScale(float x, float y, float z)
{
setScale(QVector3D(x, y, z));
}
@ -171,7 +184,8 @@ namespace Qtk {
* @param angle Angle to set for rotation.
* @param axis Axis to set rotation for.
*/
inline void setRotation(float angle, const QVector3D & axis) {
inline void setRotation(float angle, const QVector3D & axis)
{
setRotation(QQuaternion::fromAxisAndAngle(axis, angle));
}
@ -183,7 +197,8 @@ namespace Qtk {
* @param ay Y axis to set angle for.
* @param az Z axis to set angle for.
*/
inline void setRotation(float angle, float ax, float ay, float az) {
inline void setRotation(float angle, float ax, float ay, float az)
{
setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
}
@ -194,7 +209,8 @@ namespace Qtk {
/**
* @return Translation for this transform.
*/
[[nodiscard]] inline const QVector3D & getTranslation() const {
[[nodiscard]] inline const QVector3D & getTranslation() const
{
return mTranslation;
}
@ -206,7 +222,8 @@ namespace Qtk {
/**
* @return Rotation for this transform.
*/
[[nodiscard]] inline const QQuaternion & getRotation() const {
[[nodiscard]] inline const QQuaternion & getRotation() const
{
return mRotation;
}
@ -250,10 +267,10 @@ namespace Qtk {
bool m_dirty;
#ifndef QT_NO_DATASTREAM
friend QDataStream & operator<<(
QDataStream & out, const Transform3D & transform);
friend QDataStream & operator>>(
QDataStream & in, Transform3D & transform);
friend QDataStream & operator<<(QDataStream & out,
const Transform3D & transform);
friend QDataStream & operator>>(QDataStream & in,
Transform3D & transform);
#endif
};