From 443c09da7cf2c52ef39fb28d498500c25afd1631 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Thu, 24 Nov 2022 22:26:53 +0000 Subject: [PATCH] Refactor texture handling --- .clang-format | 76 +++ .clang-tidy | 146 +++++ .../{build-test.yml => all-builds.yml} | 4 +- .github/workflows/linting.yml | 67 ++ CMakeLists.txt | 1 + README.md | 66 +- app/examplescene.cpp | 579 ++++++------------ app/examplescene.h | 26 +- app/main.cpp | 14 +- app/mainwindow.cpp | 17 +- app/mainwindow.h | 24 +- app/mainwindow.ui | 220 +++---- app/resourcemanager.h | 34 +- src/abstractscene.cpp | 36 +- src/abstractscene.h | 53 +- src/camera3d.cpp | 25 +- src/camera3d.h | 64 +- src/input.cpp | 98 ++- src/input.h | 91 +-- src/mainwidget.h | 75 +++ src/mesh.cpp | 434 +++++++------ src/mesh.h | 187 +++--- src/meshrenderer.cpp | 156 ++--- src/meshrenderer.h | 170 +++-- src/model.cpp | 192 +++--- src/model.h | 179 +++--- src/object.h | 120 +++- src/qtkapi.h | 14 +- src/qtkwidget.cpp | 153 ++--- src/qtkwidget.h | 81 +-- src/skybox.cpp | 52 +- src/skybox.h | 47 +- src/texture.cpp | 72 +-- src/texture.h | 118 +++- src/transform3D.cpp | 78 +-- src/transform3D.h | 172 +++--- 36 files changed, 2187 insertions(+), 1754 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy rename .github/workflows/{build-test.yml => all-builds.yml} (94%) create mode 100644 .github/workflows/linting.yml create mode 100644 src/mainwidget.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..aad4bd6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,76 @@ + +--- +# clang-format off +BasedOnStyle: Google +# clang-format on +AlignAfterOpenBracket: AlwaysBreak +AlignArrayOfStructures: Left +AlignConsecutiveAssignments: None +AlignConsecutiveDeclarations: None +AlignConsecutiveMacros: Consecutive +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: MultiLine +InsertBraces: true +IndentAccessModifiers: true +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: true +ColumnLimit: 80 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +QualifierAlignment: Left +ReferenceAlignment: Middle +DerivePointerAlignment: false +SpaceAroundPointerQualifiers: Both +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCBlockIndentWidth: 2 +PointerAlignment: Middle +ReflowComments: true +SortIncludes: CaseSensitive +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeParens: Never +SpaceBeforeRangeBasedForLoopColon: true +SpacesBeforeTrailingComments: 2 +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +TabWidth: 2 +UseTab: Never +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..b42ea9a --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,146 @@ +# Generated from CLion Inspection settings +--- +HeaderFilterRegex: "*.h" +UseColor: true +Checks: '-*, +bugprone-argument-comment, +bugprone-assert-side-effect, +bugprone-bad-signal-to-kill-thread, +bugprone-branch-clone, +bugprone-copy-constructor-init, +bugprone-dangling-handle, +bugprone-dynamic-static-initializers, +bugprone-fold-init-type, +bugprone-forward-declaration-namespace, +bugprone-forwarding-reference-overload, +bugprone-inaccurate-erase, +bugprone-incorrect-roundings, +bugprone-integer-division, +bugprone-lambda-function-name, +bugprone-macro-parentheses, +bugprone-macro-repeated-side-effects, +bugprone-misplaced-operator-in-strlen-in-alloc, +bugprone-misplaced-pointer-arithmetic-in-alloc, +bugprone-misplaced-widening-cast, +bugprone-move-forwarding-reference, +bugprone-multiple-statement-macro, +bugprone-no-escape, +bugprone-not-null-terminated-result, +bugprone-parent-virtual-call, +bugprone-posix-return, +bugprone-reserved-identifier, +bugprone-sizeof-container, +bugprone-sizeof-expression, +bugprone-spuriously-wake-up-functions, +bugprone-string-constructor, +bugprone-string-integer-assignment, +bugprone-string-literal-with-embedded-nul, +bugprone-suspicious-enum-usage, +bugprone-suspicious-include, +bugprone-suspicious-memory-comparison, +bugprone-suspicious-memset-usage, +bugprone-suspicious-missing-comma, +bugprone-suspicious-semicolon, +bugprone-suspicious-string-compare, +bugprone-swapped-arguments, +bugprone-terminating-continue, +bugprone-throw-keyword-missing, +bugprone-too-small-loop-variable, +bugprone-undefined-memory-manipulation, +bugprone-undelegated-constructor, +bugprone-unhandled-self-assignment, +bugprone-unused-raii, +bugprone-unused-return-value, +bugprone-use-after-move, +bugprone-virtual-near-miss, +cert-dcl21-cpp, +cert-dcl58-cpp, +cert-err34-c, +cert-err52-cpp, +cert-err60-cpp, +cert-flp30-c, +cert-msc50-cpp, +cert-msc51-cpp, +cert-str34-c, +cppcoreguidelines-interfaces-global-init, +cppcoreguidelines-pro-type-member-init, +cppcoreguidelines-pro-type-static-cast-downcast, +cppcoreguidelines-slicing, +google-explicit-constructor, +google-runtime-operator, +hicpp-exception-baseclass, +hicpp-multiway-paths-covered, +misc-misplaced-const, +misc-new-delete-overloads, +misc-non-copyable-objects, +misc-throw-by-value-catch-by-reference, +misc-unconventional-assign-operator, +misc-uniqueptr-reset-release, +modernize-avoid-bind, +modernize-concat-nested-namespaces, +modernize-deprecated-headers, +modernize-deprecated-ios-base-aliases, +modernize-loop-convert, +modernize-make-shared, +modernize-make-unique, +modernize-pass-by-value, +modernize-raw-string-literal, +modernize-redundant-void-arg, +modernize-replace-auto-ptr, +modernize-replace-disallow-copy-and-assign-macro, +modernize-replace-random-shuffle, +modernize-return-braced-init-list, +modernize-shrink-to-fit, +modernize-unary-static-assert, +modernize-use-auto, +modernize-use-bool-literals, +modernize-use-emplace, +modernize-use-equals-default, +modernize-use-equals-delete, +modernize-use-nodiscard, +modernize-use-noexcept, +modernize-use-nullptr, +modernize-use-override, +modernize-use-transparent-functors, +modernize-use-uncaught-exceptions, +mpi-buffer-deref, +mpi-type-mismatch, +openmp-use-default-none, +performance-faster-string-find, +performance-for-range-copy, +performance-implicit-conversion-in-loop, +performance-inefficient-algorithm, +performance-inefficient-string-concatenation, +performance-inefficient-vector-operation, +performance-move-const-arg, +performance-move-constructor-init, +performance-no-automatic-move, +performance-noexcept-move-constructor, +performance-trivially-destructible, +performance-type-promotion-in-math-fn, +performance-unnecessary-copy-initialization, +performance-unnecessary-value-param, +portability-simd-intrinsics, +readability-avoid-const-params-in-decls, +readability-const-return-type, +readability-container-size-empty, +readability-delete-null-pointer, +readability-deleted-default, +readability-inconsistent-declaration-parameter-name, +readability-make-member-function-const, +readability-misleading-indentation, +readability-misplaced-array-index, +readability-non-const-parameter, +readability-redundant-control-flow, +readability-redundant-declaration, +readability-redundant-function-ptr-dereference, +readability-braces-around-statements, +readability-redundant-smartptr-get, +readability-redundant-string-cstr, +readability-redundant-string-init, +readability-simplify-subscript-expr, +readability-static-accessed-through-instance, +readability-static-definition-in-anonymous-namespace, +readability-string-compare, +readability-uniqueptr-delete-release, +readability-use-anyofallof' \ No newline at end of file diff --git a/.github/workflows/build-test.yml b/.github/workflows/all-builds.yml similarity index 94% rename from .github/workflows/build-test.yml rename to .github/workflows/all-builds.yml index 0157c72..86c5e7a 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/all-builds.yml @@ -1,4 +1,4 @@ -name: Build Test +name: All Builds on: push: @@ -32,7 +32,7 @@ jobs: if: matrix.os == 'windows-latest' uses: crazy-max/ghaction-chocolatey@v2.0.0 with: - args: install pkgconfiglite + args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1 - name: Build Qtk shell: bash diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 0000000..0f445a4 --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,67 @@ +name: Linting + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + Tidy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install Qt + uses: jurplel/install-qt-action@v2 + with: + version: '6.3.1' + + - name: Install Assimp Ubuntu + run: sudo apt install libassimp-dev + + - name: Build Qtk + run: | + cmake -B build -DQTK_UPDATE_SUBMODULES=OFF && cmake --build build + + - uses: cpp-linter/cpp-linter-action@v2 + id: linter + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + # Use clang-tools 14 + version: '14' + # Don't use clang-format with this action + # + Set to `file` to use .clang-format (Qtk formats with clang 15) + style: '' + # Use .clang-tidy file + tidy-checks: '' + # Check the entire repo for source files to tidy + files-changed-only: false + # Ignore qtk build and external assimp directories + ignore: '.github|build|extern/assimp/assimp' + # Point to compile_commands.json produced by build + database: 'build' + # Use thread comments as feedback + thread-comments: true + # Show file annotations on GH + file-annotations: true + + - name: Fail CI if checks don't pass + if: steps.linter.outputs.checks-failed != 0 + run: exit 1 + + Format: + runs-on: ubuntu-latest + strategy: + matrix: + path: + - 'src' + - 'app' + steps: + - uses: actions/checkout@v3 + + - name: clang-format Check + uses: jidicula/clang-format-action@v4.9.0 + with: + clang-format-version: '15' + check-path: ${{ matrix.path }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ad36dc..bbfebb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") add_compile_options(/wd4131 /wd4127) diff --git a/README.md b/README.md index 4a3688b..4d200ca 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # Qtk +[![All Builds](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml) +[![Linting](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml) Practice project for learning about using OpenGL in Qt widget applications. Model loader using [Assimp](https://assimp.org/) within a Qt widget application. @@ -20,9 +22,11 @@ This project has been ported to Qt6, which is not yet available in Ubuntu apt re To run this project, you will *need* to install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for your system. Be sure to take note of the Qt6 installation directory, as we will need it to correctly set our `CMAKE_PREFIX_PATH` in the next steps. +#### Linux + Once Qt6 is installed, to build and run `qtk` on Ubuntu - ```bash -sudo apt update -y && sudo apt install freeglut3-dev libassimp-dev cmake build-essential git +sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git git clone https://gitlab.com/shaunrd0/qtk cmake -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64 -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nproc --ignore=2) --target qtk-main ./qtk/build/qtk-main @@ -42,12 +46,72 @@ cmake -DQTK_UPDATE_SUBMODULES=OFF -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64 -S q ./qtk/build/qtk-main ``` +#### Windows / MacOS + If you are building on **Windows / Mac** and bringing your own installation of Assimp, consider setting the `-DASSIMP_NEW_INTERFACE` build flag. ```bash cmake -DASSIMP_NEW_INTERFACE=ON -DQTK_UPDATE_SUBMODULES=OFF -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64;/path/to/assimp/ -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nproc --ignore=2) --target qtk-main ``` +#### Development + +This project uses version `15.0.5` of `clang-format`. +Before merging any branch we should run `clang-tidy` followed by `clang-format`. + +```bash +git clone git@github.com:llvm/llvm-project.git -b llvmorg-15.0.5 +cd llvm-project +cmake -B build -DLLVM_ENABLE_PROJECTS=clang -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" llvm +cmake --build build -j $(nproc --ignore=2) +sudo cmake --build build -j $(nproc --ignore=2) --target install +``` + +If this version is any earlier than `15.0.0`, running `clang-format` will fail because this project uses configuration options made available since `15.0.0`. + +```bash +clang-format --version +clang-format version 15.0.5 (git@github.com:llvm/llvm-project.git 154e88af7ec97d9b9f389e55d45bf07108a9a097) +``` + +CLion has integration for IDE code reformatting actions with `clang-format`. +If you're using CLion, the `.clang-format` configuration will be picked up by CLion automatically. + +`clang-tidy` can be run with the following commands. + +```bash +# Move to the root of the repo +cd qtk +# Build +cmake -B build && cmake --build build +clang-tidy -p build/ --fix --config-file=.clang-tidy src/*.cpp src/*.h app/*.cpp app/*.h +``` + +Last we need to run `clang-format`, this can be done with the command directly. +This will reformat all the code in the repository. + +```bash +clang-format -i --style=file:.clang-format src/*.cpp src/*.h app/*.cpp app/*.h +``` + +`clang-format` can be run with git integration (or CLion if you prefer). +Git will only reformat the lines you modified, which can be useful. + +```bash +# If we want to format the last N commits +# git clang-format HEAD~N +# 3 commits +git clang-format HEAD~3 +changed files: + app/examplescene.h + app/mainwindow.h + src/abstractscene.cpp + src/skybox.h + src/texture.cpp + src/texture.h + src/transform3D.h +``` + ### Controls You can fly around the scene if you hold the right mouse button and use WASD. diff --git a/app/examplescene.cpp b/app/examplescene.cpp index 46ffa5f..9aa433f 100644 --- a/app/examplescene.cpp +++ b/app/examplescene.cpp @@ -8,10 +8,10 @@ #include #include +#include #include #include #include -#include #include using namespace Qtk; @@ -20,138 +20,87 @@ using namespace Qtk; * Constructors, Destructors ******************************************************************************/ -ExampleScene::ExampleScene() -{ +ExampleScene::ExampleScene() { Camera().transform().setTranslation(0.0f, 0.0f, 20.0f); Camera().transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); } -ExampleScene::~ExampleScene() -{ +ExampleScene::~ExampleScene() { delete mTestPhong; delete mTestSpecular; delete mTestDiffuse; delete mTestAmbient; - for (auto & mesh : mMeshes) delete mesh; - for (auto & model : mModels) delete model; + for(auto & mesh : mMeshes) { + delete mesh; + } + for(auto & model : mModels) { + delete model; + } delete mSkybox; } - /******************************************************************************* * Public Member Functions ******************************************************************************/ -void ExampleScene::init() -{ - Qtk::Skybox * sb = new Qtk::Skybox("Skybox"); +void ExampleScene::init() { + auto * sb = new Qtk::Skybox("Skybox"); setSkybox(sb); // Initialize Phong example cube mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube()); mTestPhong->mTransform.setTranslation(3.0f, 0.0f, -2.0f); mTestPhong->setShaders(":/solid-phong.vert", ":/solid-phong.frag"); - mTestPhong->init(); - mTestPhong->mProgram.bind(); + + // You no longer need to manually bind shader program. + // + But, you can still bind it if you want to. + // mTestPhong->bindShaders(); mTestPhong->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestPhong->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestPhong->setUniform("uAmbientStrength", 0.2f); mTestPhong->setUniform("uSpecularStrength", 0.50f); mTestPhong->setUniform("uSpecularShine", 256); - - mTestPhong->mVAO.bind(); - mTestPhong->mNBO.create(); - mTestPhong->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw); - mTestPhong->mNBO.bind(); - mTestPhong->mNBO.allocate(mTestPhong->normals().data(), - mTestPhong->normals().size() - * sizeof(mTestPhong->normals()[0])); - mTestPhong->mProgram.enableAttributeArray(1); - mTestPhong->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - mTestPhong->mNBO.release(); - mTestPhong->mVAO.release(); - mTestPhong->mProgram.release(); - + mTestPhong->reallocateNormals(mTestPhong->getNormals()); + // mTestPhong->releaseShaders(); // Initialize Ambient example cube mTestAmbient = new Qtk::MeshRenderer("ambient", Cube()); mTestAmbient->mTransform.setTranslation(7.0f, 0.0f, -2.0f); mTestAmbient->setShaders(":/solid-ambient.vert", ":/solid-ambient.frag"); mTestAmbient->init(); - mTestAmbient->mProgram.bind(); mTestAmbient->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestAmbient->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestAmbient->setUniform("uAmbientStrength", 0.2f); - - mTestAmbient->mVAO.bind(); - mTestAmbient->mNBO.create(); - mTestAmbient->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw); - mTestAmbient->mNBO.bind(); - mTestAmbient->mNBO.allocate(mTestAmbient->normals().data(), - mTestAmbient->normals().size() - * sizeof(mTestAmbient->normals()[0])); - mTestAmbient->mProgram.enableAttributeArray(1); - mTestAmbient->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - mTestAmbient->mNBO.release(); - mTestAmbient->mVAO.release(); - mTestAmbient->mProgram.release(); + mTestAmbient->reallocateNormals(mTestAmbient->getNormals()); // Initialize Diffuse example cube mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube()); mTestDiffuse->mTransform.setTranslation(9.0f, 0.0f, -2.0f); mTestDiffuse->setShaders(":/solid-diffuse.vert", ":/solid-diffuse.frag"); mTestDiffuse->init(); - mTestDiffuse->mProgram.bind(); mTestDiffuse->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestDiffuse->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestDiffuse->setUniform("uAmbientStrength", 0.2f); - - mTestDiffuse->mVAO.bind(); - mTestDiffuse->mNBO.create(); - mTestDiffuse->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw); - mTestDiffuse->mNBO.bind(); - mTestDiffuse->mNBO.allocate(mTestDiffuse->normals().data(), - mTestDiffuse->normals().size() - * sizeof(mTestDiffuse->normals()[0])); - mTestDiffuse->mProgram.enableAttributeArray(1); - mTestDiffuse->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - mTestDiffuse->mNBO.release(); - mTestDiffuse->mVAO.release(); - mTestDiffuse->mProgram.release(); + mTestDiffuse->reallocateNormals(mTestDiffuse->getNormals()); // Initialize Specular example cube mTestSpecular = new Qtk::MeshRenderer("specular", Cube()); mTestSpecular->mTransform.setTranslation(11.0f, 0.0f, -2.0f); mTestSpecular->setShaders(":/solid-specular.vert", ":/solid-specular.frag"); mTestSpecular->init(); - mTestSpecular->mProgram.bind(); mTestSpecular->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestSpecular->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestSpecular->setUniform("uAmbientStrength", 0.2f); mTestSpecular->setUniform("uSpecularStrength", 0.50f); mTestSpecular->setUniform("uSpecularShine", 256); + mTestSpecular->reallocateNormals(mTestSpecular->getNormals()); - mTestSpecular->mVAO.bind(); - mTestSpecular->mNBO.create(); - mTestSpecular->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw); - mTestSpecular->mNBO.bind(); - mTestSpecular->mNBO.allocate(mTestSpecular->normals().data(), - mTestSpecular->normals().size() - * sizeof(mTestSpecular->normals()[0])); - mTestSpecular->mProgram.enableAttributeArray(1); - mTestSpecular->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - mTestSpecular->mNBO.release(); - mTestSpecular->mVAO.release(); - mTestSpecular->mProgram.release(); // // Model loading - mModels.push_back(new Qtk::Model("backpack", ":/models/backpack/backpack.obj")); + mModels.push_back( + new Qtk::Model("backpack", ":/models/backpack/backpack.obj")); // Sometimes model textures need flipped in certain directions mModels.back()->flipTexture("diffuse.jpg", false, true); mModels.back()->mTransform.setTranslation(0.0f, 0.0f, -10.0f); @@ -166,7 +115,8 @@ void ExampleScene::init() mModels.back()->mTransform.setTranslation(-3.0f, -1.0f, -10.0f); mModels.back()->mTransform.scale(0.15f); - mModels.push_back(new Qtk::Model("alien", ":/models/alien-hominid/alien.obj")); + mModels.push_back( + new Qtk::Model("alien", ":/models/alien-hominid/alien.obj")); mModels.back()->mTransform.setTranslation(2.0f, -1.0f, -5.0f); mModels.back()->mTransform.scale(0.15f); @@ -175,27 +125,25 @@ void ExampleScene::init() mModels.back()->mTransform.rotate(-90.0f, 1.0f, 0.0f, 0.0f); mModels.back()->mTransform.rotate(90.0f, 0.0f, 1.0f, 0.0f); - mModels.push_back(new Qtk::Model("masterChief", ":/models/spartan/spartan.obj")); + mModels.push_back( + new Qtk::Model("masterChief", ":/models/spartan/spartan.obj")); mModels.back()->mTransform.setTranslation(-1.5f, 0.5f, -2.0f); - // // Building example mesh objects // Render an alien with specular // Test alien Model with phong lighting and specular mapping - mMeshes.push_back( - new Qtk::MeshRenderer("alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS))); + mMeshes.push_back(new Qtk::MeshRenderer( + "alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS))); mMeshes.back()->mTransform.setTranslation(4.0f, 1.5f, 10.0f); mMeshes.back()->mTransform.scale(0.25f); // This function changes values we have allocated in a buffer, so init() after mMeshes.back()->setColor(GREEN); - mMeshes.back()->init(); - mModels.push_back( - new Qtk::Model("alienTest", ":/models/alien-hominid/alien.obj", - ":/model-specular.vert", ":/model-specular.frag") - ); + mModels.push_back(new Qtk::Model( + "alienTest", ":/models/alien-hominid/alien.obj", ":/model-specular.vert", + ":/model-specular.frag")); mModels.back()->mTransform.setTranslation(3.0f, -1.0f, 10.0f); mModels.back()->mTransform.scale(0.15f); mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f)); @@ -210,21 +158,17 @@ void ExampleScene::init() mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f)); - // Test spartan Model with phong lighting, specular and normal mapping mMeshes.push_back( - new Qtk::MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS)) - ); + new Qtk::MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS))); mMeshes.back()->mTransform.setTranslation(1.0f, 1.5f, 10.0f); mMeshes.back()->mTransform.scale(0.25f); // This function changes values we have allocated in a buffer, so init() after mMeshes.back()->setColor(GREEN); - mMeshes.back()->init(); - mModels.push_back( - new Qtk::Model("spartanTest", ":/models/spartan/spartan.obj", - ":/model-normals.vert", ":/model-normals.frag") - ); + mModels.push_back(new Qtk::Model( + "spartanTest", ":/models/spartan/spartan.obj", ":/model-normals.vert", + ":/model-normals.frag")); mModels.back()->mTransform.setTranslation(0.0f, -1.0f, 10.0f); mModels.back()->mTransform.scale(2.0f); mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f)); @@ -238,7 +182,6 @@ void ExampleScene::init() mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f)); mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f)); - // Test basic cube with phong.vert and phong.frag shaders mMeshes.push_back( new Qtk::MeshRenderer("testLight", Triangle(QTK_DRAW_ELEMENTS))); @@ -247,33 +190,15 @@ void ExampleScene::init() mMeshes.back()->setDrawType(GL_LINE_LOOP); // This function changes values we have allocated in a buffer, so init() after mMeshes.back()->setColor(GREEN); - mMeshes.back()->init(); - mMeshes.push_back( - new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS))); + mMeshes.push_back(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 10.0f); mMeshes.back()->setShaders(":/phong.vert", ":/phong.frag"); mMeshes.back()->setColor(QVector3D(0.0f, 0.25f, 0.0f)); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - - mMeshes.back()->mNBO.allocate(mMeshes.back()->normals().data(), - mMeshes.back()->normals().size() - * sizeof(mMeshes.back()->normals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->setUniform("uMaterial.ambient", QVector3D(0.0f, 0.3f, 0.0f)); mMeshes.back()->setUniform("uMaterial.diffuse", QVector3D(0.0f, 0.2f, 0.0f)); - mMeshes.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f)); mMeshes.back()->setUniform("uMaterial.ambientStrength", 1.0f); mMeshes.back()->setUniform("uMaterial.diffuseStrength", 1.0f); @@ -284,7 +209,6 @@ void ExampleScene::init() mMeshes.back()->setUniform("uLight.specular", QVector3D(0.62f, 0.55f, 0.37f)); mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); - mMeshes.back()->mProgram.release(); // // Create simple shapes using MeshRenderer class and data in mesh.h @@ -314,7 +238,6 @@ void ExampleScene::init() mMeshes.back()->setDrawType(GL_LINE_LOOP); // This function changes values we have allocated in a buffer, so init() after mMeshes.back()->setColor(GREEN); - mMeshes.back()->init(); // // Testing for normals, texture coordinates @@ -324,99 +247,50 @@ void ExampleScene::init() new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 4.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mNBO.allocate(mMeshes.back()->normals().data(), - mMeshes.back()->normals().size() - * sizeof(mMeshes.back()->normals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - - mMeshes.back()->mProgram.release(); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); // RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS - mMeshes.push_back( - new Qtk::MeshRenderer("rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); + mMeshes.push_back(new Qtk::MeshRenderer( + "rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 2.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mProgram.bind(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); - mMeshes.back()->mNBO.allocate(mMeshes.back()->normals().data(), - mMeshes.back()->normals().size() - * sizeof(mMeshes.back()->normals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - - mMeshes.back()->mProgram.release(); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + Texture crateTexture; + crateTexture.setTexture(":/crate.png"); + Cube cube; + auto * m = new MeshRenderer("Test Crate", Cube(QTK_DRAW_ARRAYS)); + m->mTransform.setTranslation(0, 0, 13); + m->setShaders(":/texture2d.vert", ":/texture2d.frag"); + m->setTexture(crateTexture); + m->setUniform("uTexture", 0); + m->reallocateTexCoords(cube.getTexCoords()); + mMeshes.push_back(m); // Texturing a cube using texture coordinates and glDrawArrays - // + Texturing with UVs using glDrawElements requires QTK_DRAW_ELEMENTS_NORMALS + // + Texturing with UVs using glDrawElements requires + // QTK_DRAW_ELEMENTS_NORMALS // + UVs required duplicating element position data from QTK_DRAW_ELEMENTS // + This is because the same position must use different UV coordinates mMeshes.push_back( new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(-3.0f, 0.0f, -2.0f); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->setTexture(Texture::initTexture2D(":/crate.png")); + mMeshes.back()->setTexture(crateTexture); mMeshes.back()->setUniform("uTexture", 0); - mMeshes.back()->texture().bind(); - - mMeshes.back()->texture().release(); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.destroy(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate(mMeshes.back()->mShape.texCoords().data(), - mMeshes.back()->mShape.texCoords().size() - * sizeof(mMeshes.back()->mShape.texCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 2, sizeof(QVector2D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); // Test drawing a cube with texture coordinates using glDrawElements - mMeshes.push_back( - new Qtk::MeshRenderer("uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); + mMeshes.push_back(new Qtk::MeshRenderer( + "uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); mMeshes.back()->mTransform.setTranslation(-1.7f, 0.0f, -2.0f); + mMeshes.back()->setTexture(":/crate.png"); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->init(); - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mNBO.allocate(mMeshes.back()->texCoords().data(), - mMeshes.back()->texCoords().size() - * sizeof(mMeshes.back()->texCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - mMeshes.back()->setTexture(Texture::initTexture2D(":/crate.png")); - - mMeshes.back()->mProgram.setUniformValue("uTexture", 0); - mMeshes.back()->mProgram.release(); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + mMeshes.back()->bindShaders(); + mMeshes.back()->setUniform("uTexture", 0); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords(), 3); + mMeshes.back()->releaseShaders(); mMeshes.back()->mTransform.rotate(45.0f, 0.0f, 1.0f, 0.0f); // Texturing a cube using a cube map @@ -425,26 +299,11 @@ void ExampleScene::init() new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS))); mMeshes.back()->mTransform.setTranslation(-3.0f, 1.0f, -2.0f); mMeshes.back()->mTransform.setRotation(45.0f, 0.0f, 1.0f, 0.0f); - mMeshes.back()->setShaders(":/texture-cubemap.vert", ":/texture-cubemap.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->setTexture(Texture::initCubeMap(":/crate.png")); + mMeshes.back()->setShaders( + ":/texture-cubemap.vert", ":/texture-cubemap.frag"); + mMeshes.back()->setCubeMap(":/crate.png"); mMeshes.back()->setUniform("uTexture", 0); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.destroy(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate(mMeshes.back()->mShape.texCoords().data(), - mMeshes.back()->mShape.texCoords().size() - * sizeof(mMeshes.back()->mShape.texCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 2, sizeof(QVector2D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); // Create a cube with custom shaders // + Apply RGB normals shader and spin the cube for a neat effect @@ -452,136 +311,50 @@ void ExampleScene::init() new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(5.0f, 2.0f, -2.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mNBO.allocate(mMeshes.back()->normals().data(), - mMeshes.back()->normals().size() - * sizeof(mMeshes.back()->normals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - - mMeshes.back()->mProgram.release(); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); // RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS - mMeshes.push_back( - new Qtk::MeshRenderer("rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS))); + mMeshes.push_back(new Qtk::MeshRenderer( + "rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 2.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate(mMeshes.back()->normals().data(), - mMeshes.back()->normals().size() - * sizeof(mMeshes.back()->normals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); // RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS - mMeshes.push_back( - new Qtk::MeshRenderer("rgbTriangleElementsTest", - Triangle(QTK_DRAW_ELEMENTS_NORMALS))); + mMeshes.push_back(new Qtk::MeshRenderer( + "rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS))); mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 4.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate(mMeshes.back()->normals().data(), - mMeshes.back()->normals().size() - * sizeof(mMeshes.back()->normals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 3, sizeof(QVector3D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); // Test drawing triangle with glDrawArrays with texture coordinates mMeshes.push_back( new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(-3.0f, 2.0f, -2.0f); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - mMeshes.back()->setTexture(Texture::initTexture2D(":/crate.png")); + mMeshes.back()->setTexture(":/crate.png"); mMeshes.back()->setUniform("uTexture", 0); - mMeshes.back()->texture().bind(); - - mMeshes.back()->texture().release(); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.destroy(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate(mMeshes.back()->mShape.texCoords().data(), - mMeshes.back()->mShape.texCoords().size() - * sizeof(mMeshes.back()->mShape.texCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 2, sizeof(QVector2D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); // Test drawing triangle with glDrawElements with texture coordinates - mMeshes.push_back( - new Qtk::MeshRenderer("testTriangleElementsUV", - Triangle(QTK_DRAW_ELEMENTS_NORMALS))); + mMeshes.push_back(new Qtk::MeshRenderer( + "testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS))); mMeshes.back()->mTransform.setTranslation(-2.5f, 0.0f, -1.0f); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->setTexture(Texture::initTexture2D(":/crate.png")); + mMeshes.back()->setTexture(":/crate.png"); mMeshes.back()->setUniform("uTexture", 0); - mMeshes.back()->texture().bind(); - - mMeshes.back()->texture().release(); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.destroy(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate(mMeshes.back()->mShape.texCoords().data(), - mMeshes.back()->mShape.texCoords().size() - * sizeof(mMeshes.back()->mShape.texCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, - 2, sizeof(QVector2D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); // // Lighting cube examples // Example of a cube with no lighting applied - mMeshes.push_back( - new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS))); + mMeshes.push_back(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS))); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, -2.0f); - mMeshes.back()->setShaders(":/solid-perspective.vert", - ":/solid-perspective.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); + mMeshes.back()->setShaders( + ":/solid-perspective.vert", ":/solid-perspective.frag"); mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); - mMeshes.back()->mProgram.release(); // Create objects that represent light sources for lighting examples mMeshes.push_back( @@ -600,103 +373,101 @@ void ExampleScene::init() mMeshes.back()->mTransform.scale(0.25f); } -void ExampleScene::draw() -{ +void ExampleScene::draw() { Scene::draw(); - mTestPhong->mProgram.bind(); - mTestPhong->setUniform("uModelInverseTransposed", - mTestPhong->mTransform.toMatrix().normalMatrix()); + for(const auto & model : mModels) { + model->draw(); + } + + for(const auto & mesh : mMeshes) { + mesh->draw(); + } + + mTestPhong->bindShaders(); + mTestPhong->setUniform( + "uModelInverseTransposed", + mTestPhong->mTransform.toMatrix().normalMatrix()); mTestPhong->setUniform( "uLightPosition", - MeshRenderer::getInstance("phongLight")->mTransform.translation()); - mTestPhong->setUniform("uCameraPosition", - ExampleScene::Camera().transform().translation()); - mTestPhong->mProgram.release(); + MeshRenderer::getInstance("phongLight")->mTransform.getTranslation()); + mTestPhong->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + mTestPhong->releaseShaders(); mTestPhong->draw(); - mTestAmbient->mProgram.bind(); - mTestAmbient->setUniform("uCameraPosition", - ExampleScene::Camera().transform().translation()); - mTestAmbient->mProgram.release(); + mTestAmbient->bindShaders(); + mTestAmbient->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + mTestAmbient->releaseShaders(); mTestAmbient->draw(); - mTestDiffuse->mProgram.bind(); - mTestDiffuse->setUniform("uModelInverseTransposed", - mTestDiffuse->mTransform.toMatrix().normalMatrix()); + mTestDiffuse->bindShaders(); + mTestDiffuse->setUniform( + "uModelInverseTransposed", + mTestDiffuse->mTransform.toMatrix().normalMatrix()); mTestDiffuse->setUniform( "uLightPosition", - MeshRenderer::getInstance("diffuseLight")->mTransform.translation()); - mTestDiffuse->setUniform("uCameraPosition", ExampleScene::Camera().transform().translation()); - mTestDiffuse->mProgram.release(); + MeshRenderer::getInstance("diffuseLight")->mTransform.getTranslation()); + mTestDiffuse->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + mTestDiffuse->releaseShaders(); mTestDiffuse->draw(); - mTestSpecular->mProgram.bind(); + mTestSpecular->bindShaders(); mTestSpecular->setUniform( "uModelInverseTransposed", mTestSpecular->mTransform.toMatrix().normalMatrix()); mTestSpecular->setUniform( "uLightPosition", - MeshRenderer::getInstance("specularLight")->mTransform.translation()); - mTestSpecular->setUniform("uCameraPosition", ExampleScene::Camera().transform().translation()); - mTestSpecular->mProgram.release(); + MeshRenderer::getInstance("specularLight")->mTransform.getTranslation()); + mTestSpecular->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + mTestSpecular->releaseShaders(); mTestSpecular->draw(); } -void ExampleScene::update() -{ - auto position = MeshRenderer::getInstance("alienTestLight")->mTransform.translation(); - Model::getInstance("alienTest")->setUniform( - "uLight.position", position); - Model::getInstance("alienTest")->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().translation()); - auto posMatrix = Model::getInstance("alienTest")->mTransform.toMatrix(); - Model::getInstance("alienTest")->setUniform( - "uMVP.normalMatrix", posMatrix.normalMatrix()); - Model::getInstance("alienTest")->setUniform( - "uMVP.model", posMatrix); - Model::getInstance("alienTest")->setUniform( - "uMVP.view", ExampleScene::Camera().toMatrix()); - Model::getInstance("alienTest")->setUniform( - "uMVP.projection", ExampleScene::Projection()); - Model::getInstance("alienTest")->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); +void ExampleScene::update() { + auto position = + MeshRenderer::getInstance("alienTestLight")->mTransform.getTranslation(); + auto alien = Model::getInstance("alienTest"); + alien->setUniform("uLight.position", position); + alien->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + auto posMatrix = alien->mTransform.toMatrix(); + alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); + alien->setUniform("uMVP.model", posMatrix); + alien->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); + alien->setUniform("uMVP.projection", ExampleScene::Projection()); + alien->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); - position = MeshRenderer::getInstance("spartanTestLight")->mTransform.translation(); - Model::getInstance("spartanTest")->setUniform( - "uLight.position", position); - Model::getInstance("spartanTest")->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().translation()); - posMatrix = Model::getInstance("spartanTest")->mTransform.toMatrix(); - Model::getInstance("spartanTest")->setUniform( - "uMVP.normalMatrix", posMatrix.normalMatrix()); - Model::getInstance("spartanTest")->setUniform( - "uMVP.model", posMatrix); - Model::getInstance("spartanTest")->setUniform( - "uMVP.view", ExampleScene::Camera().toMatrix()); - Model::getInstance("spartanTest")->setUniform( - "uMVP.projection", ExampleScene::Projection()); - Model::getInstance("spartanTest")->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); + position = MeshRenderer::getInstance("spartanTestLight") + ->mTransform.getTranslation(); + auto spartan = Model::getInstance("spartanTest"); + spartan->setUniform("uLight.position", position); + spartan->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + posMatrix = spartan->mTransform.toMatrix(); + spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); + spartan->setUniform("uMVP.model", posMatrix); + spartan->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); + spartan->setUniform("uMVP.projection", ExampleScene::Projection()); + spartan->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); - - - MeshRenderer::getInstance("testPhong")->mTransform.rotate( - 0.75f, 1.0f, 0.5f, 0.0f); - MeshRenderer::getInstance("testPhong")->mProgram.bind(); - position = MeshRenderer::getInstance("testLight")->mTransform.translation(); - MeshRenderer::getInstance("testPhong")->setUniform( - "uLight.position", position); - MeshRenderer::getInstance("testPhong")->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().translation()); - posMatrix = MeshRenderer::getInstance("testPhong")->mTransform.toMatrix(); - MeshRenderer::getInstance("testPhong")->setUniform( - "uMVP.normalMatrix", posMatrix.normalMatrix()); - MeshRenderer::getInstance("testPhong")->setUniform( - "uMVP.model", posMatrix); - MeshRenderer::getInstance("testPhong")->setUniform( - "uMVP.view", ExampleScene::Camera().toMatrix()); - MeshRenderer::getInstance("testPhong")->setUniform( - "uMVP.projection", ExampleScene::Projection()); - MeshRenderer::getInstance("testPhong")->mProgram.release(); + auto phong = MeshRenderer::getInstance("testPhong"); + phong->mTransform.rotate(0.75f, 1.0f, 0.5f, 0.0f); + phong->bindShaders(); + position = + MeshRenderer::getInstance("testLight")->mTransform.getTranslation(); + phong->setUniform("uLight.position", position); + phong->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + posMatrix = phong->mTransform.toMatrix(); + phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); + phong->setUniform("uMVP.model", posMatrix); + phong->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); + phong->setUniform("uMVP.projection", ExampleScene::Projection()); + phong->releaseShaders(); // Rotate lighting example cubes mTestPhong->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); @@ -709,36 +480,36 @@ void ExampleScene::update() // Examples of various translations and rotations // Rotate in multiple directions simultaneously - MeshRenderer::getInstance("rgbNormalsCube")->mTransform.rotate( - 0.75f, 0.2f, 0.4f, 0.6f); + MeshRenderer::getInstance("rgbNormalsCube") + ->mTransform.rotate(0.75f, 0.2f, 0.4f, 0.6f); // Pitch forward and roll sideways - MeshRenderer::getInstance("leftTriangle")->mTransform.rotate( - 0.75f, 1.0f, 0.0f, 0.0f); - MeshRenderer::getInstance("rightTriangle")->mTransform.rotate( - 0.75f, 0.0f, 0.0f, 1.0f); + MeshRenderer::getInstance("leftTriangle") + ->mTransform.rotate(0.75f, 1.0f, 0.0f, 0.0f); + MeshRenderer::getInstance("rightTriangle") + ->mTransform.rotate(0.75f, 0.0f, 0.0f, 1.0f); // Move between two positions over time static float translateX = 0.025f; - float limit = -9.0f; // Origin position.x - 2.0f + float limit = -9.0f; // Origin position.x - 2.0f float posX = - MeshRenderer::getInstance("topTriangle")->mTransform.translation().x(); - if (posX < limit || posX > limit + 4.0f) { + MeshRenderer::getInstance("topTriangle")->mTransform.getTranslation().x(); + if(posX < limit || posX > limit + 4.0f) { translateX = -translateX; } - MeshRenderer::getInstance("topTriangle")->mTransform.translate( - translateX, 0.0f, 0.0f); - MeshRenderer::getInstance("bottomTriangle")->mTransform.translate( - -translateX, 0.0f, 0.0f); + MeshRenderer::getInstance("topTriangle") + ->mTransform.translate(translateX, 0.0f, 0.0f); + MeshRenderer::getInstance("bottomTriangle") + ->mTransform.translate(-translateX, 0.0f, 0.0f); // And lets rotate the triangles in two directions at once - MeshRenderer::getInstance("topTriangle")->mTransform.rotate( - 0.75f, 0.2f, 0.0f, 0.4f); - MeshRenderer::getInstance("bottomTriangle")->mTransform.rotate( - 0.75f, 0.0f, 0.2f, 0.4f); + MeshRenderer::getInstance("topTriangle") + ->mTransform.rotate(0.75f, 0.2f, 0.0f, 0.4f); + MeshRenderer::getInstance("bottomTriangle") + ->mTransform.rotate(0.75f, 0.0f, 0.2f, 0.4f); // And make the bottom triangle green, instead of RGB // Rotate center cube in several directions simultaneously // + Not subject to gimbal lock since we are using quaternions :) - MeshRenderer::getInstance("centerCube")->mTransform.rotate( - 0.75f, 0.2f, 0.4f, 0.6f); + MeshRenderer::getInstance("centerCube") + ->mTransform.rotate(0.75f, 0.2f, 0.4f, 0.6f); } diff --git a/app/examplescene.h b/app/examplescene.h index ea30dba..117cbcf 100644 --- a/app/examplescene.h +++ b/app/examplescene.h @@ -15,22 +15,20 @@ #include - class ExampleScene : public Qtk::Scene { -public: - ExampleScene(); - ~ExampleScene(); + public: + ExampleScene(); + ~ExampleScene(); - virtual void init(); - virtual void draw() override; - virtual void update(); + void init() override; + void draw() override; + void update() override; -private: - - Qtk::MeshRenderer * mTestPhong; - Qtk::MeshRenderer * mTestSpecular; - Qtk::MeshRenderer * mTestDiffuse; - Qtk::MeshRenderer * mTestAmbient; + private: + Qtk::MeshRenderer * mTestPhong {}; + Qtk::MeshRenderer * mTestSpecular {}; + Qtk::MeshRenderer * mTestDiffuse {}; + Qtk::MeshRenderer * mTestAmbient {}; }; -#endif // QTK_EXAMPLE_SCENE_H +#endif // QTK_EXAMPLE_SCENE_H diff --git a/app/main.cpp b/app/main.cpp index 55312bf..1adf4d2 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -9,12 +9,11 @@ #include #include -#include #include +#include +#include - -int main(int argc, char *argv[]) -{ +int main(int argc, char * argv[]) { QApplication a(argc, argv); // Set OpenGL Version information @@ -22,19 +21,18 @@ int main(int argc, char *argv[]) QSurfaceFormat format; format.setRenderableType(QSurfaceFormat::OpenGL); format.setProfile(QSurfaceFormat::CoreProfile); - format.setVersion(4,5); + format.setVersion(4, 5); // Set the number of samples used for glEnable(GL_MULTISAMPLING) format.setSamples(4); // Set the size of the depth bufer for glEnable(GL_DEPTH_TEST) format.setDepthBufferSize(16); #ifdef QTK_DEBUG format.setOption(QSurfaceFormat::DebugContext); -#endif // QTK_DEBUG +#endif // QTK_DEBUG // Create window for Qt application using custom mainwindow.h MainWindow w; w.show(); - return a.exec(); + return QApplication::exec(); } - diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index ea20f46..2369f4d 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -2,18 +2,16 @@ #include #include "ui_mainwindow.h" -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) -{ +MainWindow::MainWindow(QWidget * parent) : + QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); // For use in design mode using Qt Creator // + We can use the `ui` member to access nested widgets by name - for (const auto widget : ui->qWidget->children()) { - auto qtkWidget = dynamic_cast(widget); - if (qtkWidget != nullptr) { + for(const auto widget : ui->qWidget->children()) { + auto qtkWidget = dynamic_cast(widget); + if(qtkWidget != nullptr) { std::string key = qtkWidget->objectName().toStdString(); - if (mScenes[key] == nullptr) { + if(mScenes[key] == nullptr) { mScenes[qtkWidget->objectName().toStdString()] = new ExampleScene(); } qtkWidget->setScene(mScenes[qtkWidget->objectName().toStdString()]); @@ -22,7 +20,6 @@ MainWindow::MainWindow(QWidget *parent) : setWindowIcon(QIcon("../resources/icon.png")); } -MainWindow::~MainWindow() -{ +MainWindow::~MainWindow() { delete ui; } diff --git a/app/mainwindow.h b/app/mainwindow.h index 0d4b340..c389295 100644 --- a/app/mainwindow.h +++ b/app/mainwindow.h @@ -5,25 +5,25 @@ #include -#include "qtk-widget_export.h" #include - +#include "qtk-widget_export.h" namespace Ui { class MainWindow; } -class QTK_WIDGET_EXPORT MainWindow : public QMainWindow -{ - Q_OBJECT +class QTK_WIDGET_EXPORT MainWindow -public: - explicit MainWindow(QWidget *parent = nullptr); - ~MainWindow(); + : public QMainWindow { + Q_OBJECT -private: - Ui::MainWindow *ui; - std::unordered_map mScenes; + public: + explicit MainWindow(QWidget * parent = nullptr); + ~MainWindow() override; + + private: + Ui::MainWindow * ui {}; + std::unordered_map mScenes {}; }; -#endif // MAINWINDOW_H +#endif // MAINWINDOW_H diff --git a/app/mainwindow.ui b/app/mainwindow.ui index 3e6b31c..ded93d0 100644 --- a/app/mainwindow.ui +++ b/app/mainwindow.ui @@ -1,114 +1,114 @@ - MainWindow - - - - 0 - 0 - 800 - 600 - - - - Qtk - MainWindow - - - - - - 0 - 0 - 801 - 561 - - - - - - 10 - 10 - 781 - 541 - - + MainWindow + + + + 0 + 0 + 800 + 600 + + + + Qtk - MainWindow + + + + + + 0 + 0 + 801 + 561 + + + + + + 10 + 10 + 781 + 541 + + + + + + + + + 0 + 0 + 800 + 22 + + + + + File + + + + + + + + + View + + + + + + + + + + Save + + + + + Save + + + + + Open... + + + + + Save + + + + + Save as... + + + + + Exit + + + + + true + + + Show Console + + - - - - - - 0 - 0 - 800 - 22 - - - - - File - - - - - - - - - View - - - - - - - - - - Save - - - - - Save - - - - - Open... - - - - - Save - - - - - Save as... - - - - - Exit - - - - - true - - - Show Console - - - - - - Qtk::QtkWidget - QOpenGLWidget -
qtkwidget.h
-
-
- - + + + Qtk::QtkWidget + QOpenGLWidget +
qtkwidget.h
+
+
+ +
diff --git a/app/resourcemanager.h b/app/resourcemanager.h index a3b9ecb..47dcf82 100644 --- a/app/resourcemanager.h +++ b/app/resourcemanager.h @@ -14,22 +14,22 @@ #define QTK_RESOURCEMANAGER_H typedef class ResourceManager { -public: - - /** - * Takes a path using qrc format and constructs full system path to qtk assets - * Qrc format prefix ':/' is trimmed from the path for the caller - * Assets used with RM may (or may not) appear in qtk/resources.qrc - * - * @param path Path relative to qtk/resources/; ie) ':/models/backpack/backpack.obj' - * An asset at location qtk/resources/path/to/asset.obj - * Should be given in qrc format: ':/path/to/asset.obj' - * @return Absoulte system path to a qtk asset - */ - static std::string getPath(const std::string & path) { - // Only construct qtk resource path if in qrc format; else return it as-is - return path[0] == ':' ? QTK_RESOURCES + path.substr(1) : path; - } + public: + /** + * Takes a path using qrc format and constructs full system path to qtk + * assets Qrc format prefix ':/' is trimmed from the path for the caller + * Assets used with RM may (or may not) appear in qtk/resources.qrc + * + * @param path Path relative to qtk/resources/; ie) + * ':/models/backpack/backpack.obj' An asset at location + * qtk/resources/path/to/asset.obj Should be given in qrc format: + * ':/path/to/asset.obj' + * @return Absoulte system path to a qtk asset + */ + static std::string getPath(const std::string & path) { + // Only construct qtk resource path if in qrc format; else return it as-is + return path[0] == ':' ? QTK_RESOURCES + path.substr(1) : path; + } } RM; -#endif //QTK_RESOURCEMANAGER_H +#endif // QTK_RESOURCEMANAGER_H diff --git a/src/abstractscene.cpp b/src/abstractscene.cpp index 4d1dcdd..6e6fcdc 100644 --- a/src/abstractscene.cpp +++ b/src/abstractscene.cpp @@ -16,33 +16,39 @@ using namespace Qtk; Camera3D Scene::mCamera; QMatrix4x4 Scene::mProjection; - /******************************************************************************* * Constructors, Destructors ******************************************************************************/ -Scene::Scene() -{ +Scene::Scene() { mCamera.transform().setTranslation(0.0f, 0.0f, 20.0f); mCamera.transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); } -Scene::~Scene() -{ - for (auto & mesh : mMeshes) delete mesh; - for (auto & model : mModels) delete model; - if (mSkybox != Q_NULLPTR) delete mSkybox; +Scene::~Scene() { + for(auto & mesh : mMeshes) { + delete mesh; + } + for(auto & model : mModels) { + delete model; + } + + delete mSkybox; } -void Scene::privDraw() -{ - if (!mInit) { +void Scene::privDraw() { + if(!mInit) { initializeOpenGLFunctions(); init(); mInit = true; } - if (mSkybox != Q_NULLPTR) mSkybox->draw(); - for (auto & model : mModels) model->draw(); - for (const auto & mesh : mMeshes) mesh->draw(); + if(mSkybox != Q_NULLPTR) { + mSkybox->draw(); + } + for(auto & model : mModels) { + model->draw(); + } + for(const auto & mesh : mMeshes) { + mesh->draw(); + } } - diff --git a/src/abstractscene.h b/src/abstractscene.h index 1fd596d..8e27178 100644 --- a/src/abstractscene.h +++ b/src/abstractscene.h @@ -16,40 +16,41 @@ #include - namespace Qtk { class Scene : protected QOpenGLFunctions { - friend class MainWidget; + friend class MainWidget; - public: - Scene(); - ~Scene(); + public: + Scene(); + ~Scene(); - virtual void init() = 0; - virtual void draw() { privDraw(); }; - virtual void update() = 0; + virtual void init() = 0; - static Camera3D & Camera() { return mCamera;} - static QMatrix4x4 View() { return mCamera.toMatrix();} - static QMatrix4x4 & Projection() { return mProjection;} + virtual void draw() { privDraw(); }; + virtual void update() = 0; - inline Skybox * getSkybox() {return mSkybox;} - inline void setSkybox(Skybox * skybox) { - mSkybox = skybox; - } + static Camera3D & Camera() { return mCamera; } - private: - static Camera3D mCamera; - static QMatrix4x4 mProjection; - bool mInit = false; + static QMatrix4x4 View() { return mCamera.toMatrix(); } - void privDraw(); + static QMatrix4x4 & Projection() { return mProjection; } - protected: - Skybox * mSkybox; - std::vector mMeshes; - std::vector mModels; + inline Skybox * getSkybox() { return mSkybox; } + + inline void setSkybox(Skybox * skybox) { mSkybox = skybox; } + + private: + static Camera3D mCamera; + static QMatrix4x4 mProjection; + bool mInit = false; + + void privDraw(); + + protected: + Skybox * mSkybox {}; + std::vector mMeshes {}; + std::vector mModels {}; }; -} +} // namespace Qtk -#endif // QTK_SCENE_H +#endif // QTK_SCENE_H diff --git a/src/camera3d.cpp b/src/camera3d.cpp index 968c837..a40d8ad 100644 --- a/src/camera3d.cpp +++ b/src/camera3d.cpp @@ -14,47 +14,40 @@ const QVector3D Camera3D::LocalForward(0.0f, 0.0f, -1.0f); const QVector3D Camera3D::LocalUp(0.0f, 1.0f, 0.0f); const QVector3D Camera3D::LocalRight(1.0f, 0.0f, 0.0f); - /******************************************************************************* * Accessors ******************************************************************************/ // Produces worldToView matrix -const QMatrix4x4 & Camera3D::toMatrix() -{ +const QMatrix4x4 & Camera3D::toMatrix() { mWorld.setToIdentity(); // Qt6 renamed QMatrix4x4::conjugate() to conjugated() - mWorld.rotate(mTransform.rotation().conjugated()); - mWorld.translate(-mTransform.translation()); + mWorld.rotate(mTransform.getRotation().conjugated()); + mWorld.translate(-mTransform.getTranslation()); return mWorld; } - /******************************************************************************* * Qt Streams ******************************************************************************/ -QDataStream & operator<<(QDataStream & out, Camera3D & transform) -{ +QDataStream & operator<<(QDataStream & out, Camera3D & transform) { out << transform.transform(); return out; } -QDataStream & operator>>(QDataStream & in, Camera3D & transform) -{ +QDataStream & operator>>(QDataStream & in, Camera3D & transform) { in >> transform.transform(); return in; } -QDebug operator<<(QDebug dbg, const Camera3D & transform) -{ +QDebug operator<<(QDebug dbg, const Camera3D & transform) { dbg << "Camera3D\n{\n"; dbg << "Position: <" << transform.translation().x() << ", " - << transform.translation().y() << ", " - << transform.translation().z() << ">\n"; + << transform.translation().y() << ", " << transform.translation().z() + << ">\n"; dbg << "Rotation: <" << transform.rotation().x() << ", " - << transform.rotation().y() << ", " - << transform.rotation().z() << " | " + << transform.rotation().y() << ", " << transform.rotation().z() << " | " << transform.rotation().scalar() << ">\n}"; return dbg; } diff --git a/src/camera3d.h b/src/camera3d.h index a26d769..01e14ab 100644 --- a/src/camera3d.h +++ b/src/camera3d.h @@ -11,40 +11,50 @@ #include -#include #include +#include namespace Qtk { class QTKAPI Camera3D { - public: - // Constants - static const QVector3D LocalForward; - static const QVector3D LocalUp; - static const QVector3D LocalRight; + public: + // Constants + static const QVector3D LocalForward; + static const QVector3D LocalUp; + static const QVector3D LocalRight; - // Accessors - inline Transform3D & transform() { return mTransform;} - inline const QVector3D & translation() const - { return mTransform.translation();} - inline const QQuaternion & rotation() const - { return mTransform.rotation();} - const QMatrix4x4 & toMatrix(); + // Accessors + inline Transform3D & transform() { return mTransform; } - // Queries - inline QVector3D forward() const - { return mTransform.rotation().rotatedVector(LocalForward);} - inline QVector3D right() const - { return mTransform.rotation().rotatedVector(LocalRight);} - inline QVector3D up() const - { return mTransform.rotation().rotatedVector(LocalUp);} + [[nodiscard]] inline const QVector3D & translation() const { + return mTransform.getTranslation(); + } - private: - Transform3D mTransform; - QMatrix4x4 mWorld; + [[nodiscard]] inline const QQuaternion & rotation() const { + return mTransform.getRotation(); + } + + const QMatrix4x4 & toMatrix(); + + // Queries + [[nodiscard]] inline QVector3D forward() const { + return mTransform.getRotation().rotatedVector(LocalForward); + } + + [[nodiscard]] inline QVector3D right() const { + return mTransform.getRotation().rotatedVector(LocalRight); + } + + [[nodiscard]] inline QVector3D up() const { + return mTransform.getRotation().rotatedVector(LocalUp); + } + + private: + Transform3D mTransform; + QMatrix4x4 mWorld; #ifndef QT_NO_DATASTREAM - friend QDataStream & operator<<(QDataStream & out, Camera3D & transform); - friend QDataStream & operator>>(QDataStream & in, Camera3D & transform); + friend QDataStream & operator<<(QDataStream & out, Camera3D & transform); + friend QDataStream & operator>>(QDataStream & in, Camera3D & transform); #endif }; @@ -57,8 +67,8 @@ namespace Qtk { #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const Camera3D & transform); #endif -} +} // namespace Qtk Q_DECLARE_TYPEINFO(Qtk::Camera3D, Q_MOVABLE_TYPE); -#endif // QTK_CAMERA3D_H +#endif // QTK_CAMERA3D_H diff --git a/src/input.cpp b/src/input.cpp index cca776d..a568ac3 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -18,19 +18,19 @@ using namespace Qtk; /******************************************************************************* * Static Helper Structs ******************************************************************************/ -template -struct InputInstance : std::pair -{ - typedef std::pair base_class; +template struct InputInstance : std::pair { + typedef std::pair base_class; - inline InputInstance(T value) - : base_class(value, Input::InputInvalid) {} + // Disable clang-tidy from marking this ctor explicit + // NOLINTNEXTLINE + inline InputInstance(T value) : base_class(value, Input::InputInvalid) {} - inline InputInstance(T value, Input::InputState state) - : base_class(value, state) {} + inline InputInstance(T value, Input::InputState state) : + base_class(value, state) {} - inline bool operator==(const InputInstance & rhs) const - { return this->first == rhs.first;} + inline bool operator==(const InputInstance & rhs) const { + return this->first == rhs.first; + } }; // Key, button instance typedefs @@ -49,26 +49,20 @@ static QPoint sg_mouseCurrPosition; static QPoint sg_mousePrevPosition; static QPoint sg_mouseDelta; - /******************************************************************************* * Static Inline Helper Functions ******************************************************************************/ -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); } -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); } -template -static inline void UpdateStates(TPair & instance) -{ - switch (instance.second) - { +template static inline void UpdateStates(TPair & instance) { + switch(instance.second) { case Input::InputRegistered: instance.second = Input::InputTriggered; break; @@ -84,19 +78,16 @@ static inline void UpdateStates(TPair & instance) } template -static inline bool CheckReleased(const TPair & instance) -{ +static inline bool CheckReleased(const TPair & instance) { return instance.second == Input::InputReleased; } -template -static inline void Update(Container & container) -{ +template static inline void Update(Container & container) { typedef typename Container::iterator Iter; typedef typename Container::value_type TPair; // Remove old data - Iter remove = + auto remove = std::remove_if(container.begin(), container.end(), &CheckReleased); container.erase(remove, container.end()); @@ -104,35 +95,29 @@ static inline void Update(Container & container) std::for_each(container.begin(), container.end(), &UpdateStates); } - /******************************************************************************* * Input Implementation ******************************************************************************/ -Input::InputState Input::keyState(Qt::Key k) -{ - KeyContainer::iterator it = FindKey(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) -{ - ButtonContainer::iterator it = FindButton(k); +Input::InputState Input::buttonState(Qt::MouseButton k) { + auto it = FindButton(k); 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; } -void Input::update() -{ +void Input::update() { // Update Mouse Delta sg_mousePrevPosition = sg_mouseCurrPosition; sg_mouseCurrPosition = QCursor::pos(); @@ -143,44 +128,35 @@ void Input::update() Update(sg_keyInstances); } -void Input::registerKeyPress(int k) -{ - KeyContainer::iterator it = FindKey((Qt::Key)k); - if (it == sg_keyInstances.end()) - { +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)); } } -void Input::registerKeyRelease(int k) -{ - KeyContainer::iterator it = FindKey((Qt::Key)k); - if (it != sg_keyInstances.end()) - { +void Input::registerKeyRelease(int k) { + auto it = FindKey((Qt::Key)k); + if(it != sg_keyInstances.end()) { it->second = InputUnregistered; } } -void Input::registerMousePress(Qt::MouseButton btn) -{ - ButtonContainer::iterator it = FindButton(btn); - if (it == sg_buttonInstances.end()) - { +void Input::registerMousePress(Qt::MouseButton btn) { + auto it = FindButton(btn); + if(it == sg_buttonInstances.end()) { sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered)); } } -void Input::registerMouseRelease(Qt::MouseButton btn) -{ - ButtonContainer::iterator it = FindButton(btn); - if (it != sg_buttonInstances.end()) - { +void Input::registerMouseRelease(Qt::MouseButton btn) { + auto it = FindButton(btn); + if(it != sg_buttonInstances.end()) { it->second = InputUnregistered; } } -void Input::reset() -{ +void Input::reset() { sg_keyInstances.clear(); sg_buttonInstances.clear(); } diff --git a/src/input.h b/src/input.h index ba68804..5863105 100644 --- a/src/input.h +++ b/src/input.h @@ -17,49 +17,60 @@ namespace Qtk { class QTKAPI Input { - friend class Qtk::QtkWidget; - public: - // Possible key states - enum InputState - { - InputInvalid, - InputRegistered, - InputUnregistered, - InputTriggered, - InputPressed, - InputReleased - }; + friend class Qtk::QtkWidget; - // State checking - inline static bool keyTriggered(Qt::Key key) - { return keyState(key) == InputTriggered;} - inline static bool keyPressed(Qt::Key key) - { return keyState(key) == InputPressed;} - inline static bool keyReleased(Qt::Key key) - { return keyState(key) == InputReleased;} - inline static bool buttonTriggered(Qt::MouseButton button) - { return buttonState(button) == InputTriggered;} - inline static bool buttonPressed(Qt::MouseButton button) - { return buttonState(button) == InputPressed;} - inline static bool buttonReleased(Qt::MouseButton button) - { return buttonState(button) == InputReleased;} + public: + // Possible key states + enum InputState { + InputInvalid, + InputRegistered, + InputUnregistered, + InputTriggered, + InputPressed, + InputReleased + }; - // Implementation - static InputState keyState(Qt::Key key); - static InputState buttonState(Qt::MouseButton button); + // State checking + inline static bool keyTriggered(Qt::Key key) { + return keyState(key) == InputTriggered; + } - static QPoint mousePosition(); - static QPoint mouseDelta(); + inline static bool keyPressed(Qt::Key key) { + return keyState(key) == InputPressed; + } - private: - // State updating - static void update(); - static void registerKeyPress(int key); - static void registerKeyRelease(int key); - static void registerMousePress(Qt::MouseButton button); - static void registerMouseRelease(Qt::MouseButton button); - static void reset(); + inline static bool keyReleased(Qt::Key key) { + return keyState(key) == InputReleased; + } + + inline static bool buttonTriggered(Qt::MouseButton button) { + return buttonState(button) == InputTriggered; + } + + inline static bool buttonPressed(Qt::MouseButton button) { + return buttonState(button) == InputPressed; + } + + inline static bool buttonReleased(Qt::MouseButton button) { + return buttonState(button) == InputReleased; + } + + // Implementation + static InputState keyState(Qt::Key key); + static InputState buttonState(Qt::MouseButton button); + + static QPoint mousePosition(); + static QPoint mouseDelta(); + + private: + // State updating + static void update(); + static void registerKeyPress(int key); + static void registerKeyRelease(int key); + static void registerMousePress(Qt::MouseButton button); + static void registerMouseRelease(Qt::MouseButton button); + static void reset(); }; -} +} // namespace Qtk -#endif // QTOPENGL_INPUT_H +#endif // QTOPENGL_INPUT_H diff --git a/src/mainwidget.h b/src/mainwidget.h new file mode 100644 index 0000000..589b852 --- /dev/null +++ b/src/mainwidget.h @@ -0,0 +1,75 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## +## About: Main window for Qt6 OpenGL widget application ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +##############################################################################*/ +#ifndef QTK_MAINWIDGET_H +#define QTK_MAINWIDGET_H + +#include + +#include +#include +#include +#include + +#define QTK_DEBUG + +class MeshRenderer; + +class Model; + +class Object; + +class Scene; + +class Skybox; + +class OpenGLTextureFactory; + +class MainWidget : public QOpenGLWidget, protected QOpenGLFunctions { + Q_OBJECT; + + public: + // Constructors + MainWidget(); + explicit MainWidget(QWidget * parent); + explicit MainWidget(const QSurfaceFormat & format); + ~MainWidget() override; + + private: + void teardownGL(); + void initObjects(); + + public: + // Inherited virtual Members + void paintGL() override; + void initializeGL() override; + void resizeGL(int width, int height) override; + + protected slots: + void update(); + void messageLogged(const QOpenGLDebugMessage & msg); + + // Protected Helpers + protected: + void keyPressEvent(QKeyEvent * event) override; + void keyReleaseEvent(QKeyEvent * event) override; + void mousePressEvent(QMouseEvent * event) override; + void mouseReleaseEvent(QMouseEvent * event) override; + + private: + // Private helpers + void initializeWidget(); + void printContextInformation(); + void updateCameraInput(); + + Scene * mScene; + Object * mObject; + + QOpenGLDebugLogger * mDebugLogger; +}; + +#endif // QTK_MAINWIDGET_H diff --git a/src/mesh.cpp b/src/mesh.cpp index 3f765f6..2b1266e 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -10,328 +10,370 @@ using namespace Qtk; -Cube::Cube(DrawMode mode) -{ +Cube::Cube(DrawMode mode) { mDrawMode = mode; switch(mode) { // Cube data for use with glDrawArrays case QTK_DRAW_ARRAYS: - mIndices = { /* No indices needed for glDrawArrays */ }; + mIndices = {/* No indices needed for glDrawArrays */}; - mNormals = - {FACE_FRONT, FACE_BACK, FACE_TOP, FACE_BOTTOM, FACE_LEFT, FACE_RIGHT}; + mNormals = {FACE_FRONT, FACE_BACK, FACE_TOP, + FACE_BOTTOM, FACE_LEFT, FACE_RIGHT}; - mVertices = { - // Face 1 (Front) - VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, - VERTEX_FBL, VERTEX_FBR, VERTEX_FTR, - // Face 2 (Back) - VERTEX_BBR, VERTEX_BTL, VERTEX_BTR, - VERTEX_BTL, VERTEX_BBR, VERTEX_BBL, - // Face 3 (Top) - VERTEX_FTR, VERTEX_BTR, VERTEX_BTL, - VERTEX_BTL, VERTEX_FTL, VERTEX_FTR, - // Face 4 (Bottom) - VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, - VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, - // Face 5 (Left) - VERTEX_FBL, VERTEX_FTL, VERTEX_BTL, - VERTEX_FBL, VERTEX_BTL, VERTEX_BBL, - // Face 6 (Right) - VERTEX_FTR, VERTEX_FBR, VERTEX_BBR, - VERTEX_BBR, VERTEX_BTR, VERTEX_FTR - }; + mVertices = {// Face 1 (Front) + VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBL, VERTEX_FBR, + VERTEX_FTR, + // Face 2 (Back) + VERTEX_BBR, VERTEX_BTL, VERTEX_BTR, VERTEX_BTL, VERTEX_BBR, + VERTEX_BBL, + // Face 3 (Top) + VERTEX_FTR, VERTEX_BTR, VERTEX_BTL, VERTEX_BTL, VERTEX_FTL, + VERTEX_FTR, + // Face 4 (Bottom) + VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, VERTEX_BBL, VERTEX_BBR, + VERTEX_FBR, + // Face 5 (Left) + VERTEX_FBL, VERTEX_FTL, VERTEX_BTL, VERTEX_FBL, VERTEX_BTL, + VERTEX_BBL, + // Face 6 (Right) + VERTEX_FTR, VERTEX_FBR, VERTEX_BBR, VERTEX_BBR, VERTEX_BTR, + VERTEX_FTR}; - mColors = { - // Face 1 (Front) - RED, GREEN, BLUE, - BLUE, WHITE, RED, - // Face 2 (Back) - YELLOW, CYAN, MAGENTA, - CYAN, YELLOW, BLACK, - // Face 3 (Top) - RED, MAGENTA, CYAN, - CYAN, GREEN, RED, - // Face 4 (Bottom) - WHITE, BLUE, BLACK, - BLACK, YELLOW, WHITE, - // Face 5 (Left) - BLUE, GREEN, CYAN, - BLUE, CYAN, BLACK, - // Face 6 (Right) - RED, WHITE, YELLOW, - YELLOW, MAGENTA, RED - }; + mColors = {// Face 1 (Front) + RED, GREEN, BLUE, BLUE, WHITE, RED, + // Face 2 (Back) + YELLOW, CYAN, MAGENTA, CYAN, YELLOW, BLACK, + // Face 3 (Top) + RED, MAGENTA, CYAN, CYAN, GREEN, RED, + // Face 4 (Bottom) + WHITE, BLUE, BLACK, BLACK, YELLOW, WHITE, + // Face 5 (Left) + BLUE, GREEN, CYAN, BLUE, CYAN, BLACK, + // Face 6 (Right) + RED, WHITE, YELLOW, YELLOW, MAGENTA, RED}; - mTexCoords = { - // Face 1 (Front) - UV_TOP, UV_ORIGIN, UV_RIGHT, - UV_RIGHT, UV_CORNER, UV_TOP, - // Face 2 (Back) - UV_TOP, UV_RIGHT, UV_CORNER, - UV_RIGHT, UV_TOP, UV_ORIGIN, - // Face 3 (Top) - UV_CORNER, UV_TOP, UV_ORIGIN, - UV_ORIGIN, UV_RIGHT, UV_CORNER, - // Face 4 (Bottom) - UV_TOP, UV_ORIGIN, UV_RIGHT, - UV_RIGHT, UV_CORNER, UV_TOP, - // Face 5 (Left) - UV_TOP, UV_CORNER, UV_RIGHT, - UV_TOP, UV_RIGHT, UV_ORIGIN, - // Face 6 (Right) - UV_TOP, UV_CORNER, UV_RIGHT, - UV_RIGHT, UV_ORIGIN, UV_TOP - }; + mTexCoords = {// Face 1 (Front) + UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, UV_TOP, + // Face 2 (Back) + UV_TOP, UV_RIGHT, UV_CORNER, UV_RIGHT, UV_TOP, UV_ORIGIN, + // Face 3 (Top) + UV_CORNER, UV_TOP, UV_ORIGIN, UV_ORIGIN, UV_RIGHT, + UV_CORNER, + // Face 4 (Bottom) + UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, UV_TOP, + // Face 5 (Left) + UV_TOP, UV_CORNER, UV_RIGHT, UV_TOP, UV_RIGHT, UV_ORIGIN, + // Face 6 (Right) + UV_TOP, UV_CORNER, UV_RIGHT, UV_RIGHT, UV_ORIGIN, UV_TOP}; break; - // Cube data for use with glDrawElements case QTK_DRAW_ELEMENTS: - mNormals = - {/* For normals and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */}; - mTexCoords = - { /* For UVs and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */ }; + mNormals = { + /* For normals and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */}; + mTexCoords = { + /* For UVs and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */}; mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK}; - mVertices = { - // 0 1 2 3 - VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, - // 4 5 6 7 - VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR - }; - mIndices = { - // Face 1 (Front) - 0, 1, 2, 2, 3, 0, - // Face 2 (Back) - 7, 5, 4, 5, 7, 6, - // Face 3 (Top) - 0, 4, 5, 5, 1, 0, - // Face 4 (Bottom) - 3, 2, 6, 6, 7, 3, - // Face 5 (Left) - 2, 1, 5, 2, 5, 6, - // Face 6 (Right) - 0, 3, 7, 7, 4, 0 - }; + mVertices = {// 0 1 2 3 + VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, + // 4 5 6 7 + VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR}; + mIndices = {// Face 1 (Front) + 0, 1, 2, 2, 3, 0, + // Face 2 (Back) + 7, 5, 4, 5, 7, 6, + // Face 3 (Top) + 0, 4, 5, 5, 1, 0, + // Face 4 (Bottom) + 3, 2, 6, 6, 7, 3, + // Face 5 (Left) + 2, 1, 5, 2, 5, 6, + // Face 6 (Right) + 0, 3, 7, 7, 4, 0}; break; // Cube shape data for using normals and UVs with glDrawElements case QTK_DRAW_ELEMENTS_NORMALS: mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK}; - mVertices = { - // Face 1 (Front) - // 0 1 2 3 - VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR, - // Face 2 (Back) - // 4 5 6 7 - VERTEX_BTL, VERTEX_BBL, VERTEX_BBR, VERTEX_BTR, - // Face 3 (Top) - // 8 9 10 11 - VERTEX_FTL, VERTEX_BTL, VERTEX_BTR, VERTEX_FTR, - // Face 4 (Bottom) - // 12 13 14 15 - VERTEX_FBL, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, - // Face 5 (Left) - // 16 17 18 19 - VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL, - // Face 6 (Right) - // 20 21 22 23 - VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR - }; + mVertices = {// Face 1 (Front) + // 0 1 2 3 + VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR, + // Face 2 (Back) + // 4 5 6 7 + VERTEX_BTL, VERTEX_BBL, VERTEX_BBR, VERTEX_BTR, + // Face 3 (Top) + // 8 9 10 11 + VERTEX_FTL, VERTEX_BTL, VERTEX_BTR, VERTEX_FTR, + // Face 4 (Bottom) + // 12 13 14 15 + VERTEX_FBL, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, + // Face 5 (Left) + // 16 17 18 19 + VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL, + // Face 6 (Right) + // 20 21 22 23 + VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR}; - mIndices = { - // Face 1 (Front) - 0, 1, 2, 2, 3, 0, - // Face 2 (Back) - 4, 5, 6, 6, 7, 4, - // Face 3 (Top) - 8, 9, 10, 10, 11, 8, - // Face 4 (Bottom) - 12, 13, 14, 14, 15, 12, + mIndices = {// Face 1 (Front) + 0, 1, 2, 2, 3, 0, + // Face 2 (Back) + 4, 5, 6, 6, 7, 4, + // Face 3 (Top) + 8, 9, 10, 10, 11, 8, + // Face 4 (Bottom) + 12, 13, 14, 14, 15, 12, - // Face 5 (Left) - 16, 17, 18, 18, 19, 16, - // Face 6 (Right) - 20, 21, 22, 22, 23, 20 - }; + // Face 5 (Left) + 16, 17, 18, 18, 19, 16, + // Face 6 (Right) + 20, 21, 22, 22, 23, 20}; mNormals = { VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, - VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, - VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, - VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, - VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, - VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, + VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, + VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, + VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, + VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, + VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, }; mTexCoords = { // Face 1 (Front) - UV_TOP, UV_RIGHT, UV_CORNER, - UV_RIGHT, UV_TOP, UV_ORIGIN, + UV_TOP, + UV_RIGHT, + UV_CORNER, + UV_RIGHT, + UV_TOP, + UV_ORIGIN, // Face 2 (Back) - UV_TOP, UV_RIGHT, UV_CORNER, - UV_RIGHT, UV_TOP, UV_ORIGIN, + UV_TOP, + UV_RIGHT, + UV_CORNER, + UV_RIGHT, + UV_TOP, + UV_ORIGIN, // Face 3 (Top) - UV_TOP, UV_RIGHT, UV_CORNER, - UV_RIGHT, UV_TOP, UV_ORIGIN, + UV_TOP, + UV_RIGHT, + UV_CORNER, + UV_RIGHT, + UV_TOP, + UV_ORIGIN, // Face 4 (Bottom) - UV_TOP, UV_RIGHT, UV_CORNER, - UV_RIGHT, UV_TOP, UV_ORIGIN, + UV_TOP, + UV_RIGHT, + UV_CORNER, + UV_RIGHT, + UV_TOP, + UV_ORIGIN, // Face 5 (Left) - UV_TOP, UV_RIGHT, UV_CORNER, - UV_RIGHT, UV_TOP, UV_ORIGIN, + UV_TOP, + UV_RIGHT, + UV_CORNER, + UV_RIGHT, + UV_TOP, + UV_ORIGIN, // Face 6 (Right) - UV_TOP, UV_RIGHT, UV_CORNER, - UV_RIGHT, UV_TOP, UV_ORIGIN, + UV_TOP, + UV_RIGHT, + UV_CORNER, + UV_RIGHT, + UV_TOP, + UV_ORIGIN, }; break; } } -Triangle::Triangle(DrawMode mode) -{ +Triangle::Triangle(DrawMode mode) { mDrawMode = mode; const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f); switch(mode) { case QTK_DRAW_ARRAYS: - mIndices = { /* No indices needed for glDrawArrays */ }; + mIndices = {/* No indices needed for glDrawArrays */}; - mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK }; + mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK}; mVertices = { // Bottom face (Base of the pyramid) - VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, - VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, + VERTEX_BBL, + VERTEX_BBR, + VERTEX_FBR, + VERTEX_FBR, + VERTEX_FBL, + VERTEX_BBL, // Front face - VERTEX_FBL, VERTEX_FBR, triangleTop, + VERTEX_FBL, + VERTEX_FBR, + triangleTop, // Back face - VERTEX_BBR, VERTEX_BBL, triangleTop, + VERTEX_BBR, + VERTEX_BBL, + triangleTop, // Left face - VERTEX_BBL, VERTEX_FBL, triangleTop, + VERTEX_BBL, + VERTEX_FBL, + triangleTop, // Right face - VERTEX_FBR, VERTEX_BBR, triangleTop, + VERTEX_FBR, + VERTEX_BBR, + triangleTop, }; // Find normals for each triangle of the mesh - for (int i = 0; i < mVertices.size(); i += 3) { + for(int i = 0; i < mVertices.size(); i += 3) { QVector3D vertexNormal = - QVector3D::normal(mVertices[i], mVertices[i+1], mVertices[i+2]); + QVector3D::normal(mVertices[i], mVertices[i + 1], mVertices[i + 2]); // Three points share this normal - for (int j = 0; j < 3; j++) { + for(int j = 0; j < 3; j++) { mNormals.push_back(vertexNormal); } } mTexCoords = { // Bottom face (Base of the pyramid) - UV_ORIGIN, UV_RIGHT, UV_CORNER, - UV_CORNER, UV_TOP, UV_ORIGIN, + UV_ORIGIN, + UV_RIGHT, + UV_CORNER, + UV_CORNER, + UV_TOP, + UV_ORIGIN, // Front face - UV_ORIGIN, UV_RIGHT, UV_CORNER, + UV_ORIGIN, + UV_RIGHT, + UV_CORNER, // Back face - UV_ORIGIN, UV_RIGHT, UV_CORNER, + UV_ORIGIN, + UV_RIGHT, + UV_CORNER, // Left face - UV_ORIGIN, UV_RIGHT, UV_CORNER, + UV_ORIGIN, + UV_RIGHT, + UV_CORNER, // Right face - UV_ORIGIN, UV_RIGHT, UV_CORNER, + UV_ORIGIN, + UV_RIGHT, + UV_CORNER, }; break; // Triangle shape data for using glDrawElements case QTK_DRAW_ELEMENTS: - mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK }; + mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK}; mVertices = {VERTEX_FBL, VERTEX_FBR, VERTEX_BBL, VERTEX_BBR, triangleTop}; mIndices = { // Bottom face (Base of the pyramid) - 2, 3, 1, // Use customVertexes[2], then 3, 1... - 1, 0, 2, // Use customVertexes[1], then 0, 2 + 2, 3, 1, // Use customVertexes[2], then 3, 1... + 1, 0, 2, // Use customVertexes[1], then 0, 2 - 0, 1, 4, // Front face - 3, 2, 4, // Back face - 2, 0, 4, // Left face - 1, 3, 4, // Right face + 0, 1, 4, // Front face + 3, 2, 4, // Back face + 2, 0, 4, // Left face + 1, 3, 4, // Right face }; - mNormals = - {/* Use QTK_DRAW_ELEMENTS_NORMALS for normals with glDrawElements */}; + mNormals = { + /* Use QTK_DRAW_ELEMENTS_NORMALS for normals with glDrawElements */}; - mTexCoords = { /* No UVs for triangle with glDrawElements */ }; + mTexCoords = {/* No UVs for triangle with glDrawElements */}; break; // Triangle shape data for using normals and UVs with glDrawElements case QTK_DRAW_ELEMENTS_NORMALS: - mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK }; + mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK}; mVertices = { // Bottom face // 0 1 2 - VERTEX_FBL, VERTEX_FBR, VERTEX_BBL, + VERTEX_FBL, + VERTEX_FBR, + VERTEX_BBL, // 3 4 5 - VERTEX_BBR, VERTEX_FBR, VERTEX_BBL, + VERTEX_BBR, + VERTEX_FBR, + VERTEX_BBL, // Front face // 6 7 8 - VERTEX_FBL, VERTEX_FBR, triangleTop, + VERTEX_FBL, + VERTEX_FBR, + triangleTop, // Back face // 9 10 11 - VERTEX_BBR, VERTEX_BBL, triangleTop, + VERTEX_BBR, + VERTEX_BBL, + triangleTop, // Left face // 12 13 14 - VERTEX_BBL, VERTEX_FBL, triangleTop, + VERTEX_BBL, + VERTEX_FBL, + triangleTop, // Right face // 15 16 17 - VERTEX_FBR, VERTEX_BBR, triangleTop, + VERTEX_FBR, + VERTEX_BBR, + triangleTop, }; mIndices = { // Bottom face (Base of the pyramid) - 0, 1, 2, // Use customVertexes[2], then 3, 1... - 3, 4, 5, // Use customVertexes[1], then 0, 2 + 0, 1, 2, // Use customVertexes[2], then 3, 1... + 3, 4, 5, // Use customVertexes[1], then 0, 2 - 6, 7, 8, // Front face - 9, 10, 11, // Back face - 12, 13, 14, // Left face - 15, 16, 17, // Right face + 6, 7, 8, // Front face + 9, 10, 11, // Back face + 12, 13, 14, // Left face + 15, 16, 17, // Right face }; // Find normals for each triangle of the mesh - for (int i = 0; i < mVertices.size(); i += 3) { - QVector3D vertexNormal = - QVector3D::normal(mVertices[mIndices[i]], - mVertices[mIndices[i+1]], - mVertices[mIndices[i+2]]); + for(int i = 0; i < mVertices.size(); i += 3) { + QVector3D vertexNormal = QVector3D::normal( + mVertices[mIndices[i]], mVertices[mIndices[i + 1]], + mVertices[mIndices[i + 2]]); // Three points share this normal - for (int j = 0; j < 3; j++) { + for(int j = 0; j < 3; j++) { mNormals.push_back(vertexNormal); } } mTexCoords = { // Bottom face - UV_ORIGIN, UV_RIGHT, UV_TOP, - UV_CORNER, UV_RIGHT, UV_TOP, + UV_ORIGIN, + UV_RIGHT, + UV_TOP, + UV_CORNER, + UV_RIGHT, + UV_TOP, // Front face - UV_ORIGIN, UV_RIGHT, UV_CORNER, + UV_ORIGIN, + UV_RIGHT, + UV_CORNER, // Back face - UV_ORIGIN, UV_RIGHT, UV_CORNER, + UV_ORIGIN, + UV_RIGHT, + UV_CORNER, // Left face - UV_ORIGIN, UV_RIGHT, UV_CORNER, + UV_ORIGIN, + UV_RIGHT, + UV_CORNER, // Right face - UV_ORIGIN, UV_RIGHT, UV_CORNER, + UV_ORIGIN, + UV_RIGHT, + UV_CORNER, }; break; } - } diff --git a/src/mesh.h b/src/mesh.h index 41cd579..a411fef 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -11,28 +11,30 @@ #include #include #include +#include #include #include namespace Qtk { class MeshRenderer; + class Object; - // Define vertices for drawing a cube using two faces (8 vertex points) - // Front Vertices -#define VERTEX_FTR QVector3D( 0.5f, 0.5f, 0.5f) // 1 -#define VERTEX_FTL QVector3D(-0.5f, 0.5f, 0.5f) // 2 -#define VERTEX_FBL QVector3D(-0.5f, -0.5f, 0.5f) // 3 -#define VERTEX_FBR QVector3D( 0.5f, -0.5f, 0.5f) // 4 +// Define vertices for drawing a cube using two faces (8 vertex points) +// Front Vertices +#define VERTEX_FTR QVector3D(0.5f, 0.5f, 0.5f) // 1 +#define VERTEX_FTL QVector3D(-0.5f, 0.5f, 0.5f) // 2 +#define VERTEX_FBL QVector3D(-0.5f, -0.5f, 0.5f) // 3 +#define VERTEX_FBR QVector3D(0.5f, -0.5f, 0.5f) // 4 - // Back Vertices -#define VERTEX_BTR QVector3D( 0.5f, 0.5f, -0.5f) // 5 -#define VERTEX_BTL QVector3D(-0.5f, 0.5f, -0.5f) // 6 -#define VERTEX_BBL QVector3D(-0.5f, -0.5f, -0.5f) // 7 -#define VERTEX_BBR QVector3D( 0.5f, -0.5f, -0.5f) // 8 +// Back Vertices +#define VERTEX_BTR QVector3D(0.5f, 0.5f, -0.5f) // 5 +#define VERTEX_BTL QVector3D(-0.5f, 0.5f, -0.5f) // 6 +#define VERTEX_BBL QVector3D(-0.5f, -0.5f, -0.5f) // 7 +#define VERTEX_BBR QVector3D(0.5f, -0.5f, -0.5f) // 8 - // Direction vectors +// Direction vectors #define VECTOR_UP QVector3D(0.0f, 1.0f, 0.0f) #define VECTOR_DOWN QVector3D(0.0f, -1.0f, 0.0f) #define VECTOR_LEFT QVector3D(-1.0f, 0.0f, 0.0f) @@ -40,95 +42,134 @@ namespace Qtk { #define VECTOR_FORWARD QVector3D(0.0f, 0.0f, 1.0f) #define VECTOR_BACK QVector3D(0.0f, 0.0f, -1.0f) - // Identity and zero vectors -#define VECTOR_ONE QVector3D(1.0f, 1.0f, 1.0f) -#define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f) +// Identity and zero vectors +#define VECTOR_ONE QVector3D(1.0f, 1.0f, 1.0f) +#define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f) - // A series of direction vectors to represent cube face normal -#define FACE_TOP VECTOR_UP, VECTOR_UP, VECTOR_UP, \ - VECTOR_UP, VECTOR_UP, VECTOR_UP -#define FACE_BOTTOM VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, \ - VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN -#define FACE_LEFT VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, \ - VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT -#define FACE_RIGHT VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \ - VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT -#define FACE_FRONT VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \ - VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD -#define FACE_BACK VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, \ - VECTOR_BACK, VECTOR_BACK, VECTOR_BACK +// A series of direction vectors to represent cube face normal +#define FACE_TOP \ + VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP +#define FACE_BOTTOM \ + VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN +#define FACE_LEFT \ + VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT +#define FACE_RIGHT \ + VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \ + VECTOR_RIGHT +#define FACE_FRONT \ + VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \ + VECTOR_FORWARD, VECTOR_FORWARD +#define FACE_BACK \ + VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK - // Colors using QVector3Ds as RGB values -#define WHITE VECTOR_ONE -#define BLACK VECTOR_ZERO -#define RED QVector3D(1.0f, 0.0f, 0.0f) -#define GREEN QVector3D(0.0f, 1.0f, 0.0f) -#define BLUE QVector3D(0.0f, 0.0f, 1.0f) +// Colors using QVector3Ds as RGB values +#define WHITE VECTOR_ONE +#define BLACK VECTOR_ZERO +#define RED QVector3D(1.0f, 0.0f, 0.0f) +#define GREEN QVector3D(0.0f, 1.0f, 0.0f) +#define BLUE QVector3D(0.0f, 0.0f, 1.0f) #define YELLOW QVector3D(1.0f, 1.0f, 0.0f) #define CYAN QVector3D(0.0f, 1.0f, 1.0f) #define MAGENTA QVector3D(1.0f, 0.0f, 1.0f) -#define UV_ORIGIN QVector2D(0.0f, 0.0f) -#define UV_TOP QVector2D(1.0f, 0.0f) -#define UV_RIGHT QVector2D(0.0f, 1.0f) -#define UV_CORNER QVector2D(1.0f, 1.0f) +#define UV_ORIGIN QVector2D(0.0f, 0.0f) +#define UV_TOP QVector2D(1.0f, 0.0f) +#define UV_RIGHT QVector2D(0.0f, 1.0f) +#define UV_CORNER QVector2D(1.0f, 1.0f) + // TODO: Vertices.getData(); Vertices.getStride(); typedef std::vector Vertices; typedef std::vector Colors; typedef std::vector Indices; typedef std::vector TexCoords; typedef std::vector Normals; - enum DrawMode { QTK_DRAW_ARRAYS, QTK_DRAW_ELEMENTS, QTK_DRAW_ELEMENTS_NORMALS }; + enum DrawMode { + QTK_DRAW_ARRAYS, + QTK_DRAW_ELEMENTS, + QTK_DRAW_ELEMENTS_NORMALS + }; struct QTKAPI ShapeBase { - ShapeBase(DrawMode mode=QTK_DRAW_ARRAYS, Vertices v={},Indices i={}, Colors c={}, - TexCoords t={}, Normals n={}) - : mVertices(v), mColors(c), mIndices(i), mTexCoords(t), mNormals(n) - {} + 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)) {} - inline const Vertices & vertices() const { return mVertices;} - inline const Indices & indices() const { return mIndices;} - inline const Colors & colors() const { return mColors;} - inline const TexCoords & texCoords() const { return mTexCoords;} - inline const Normals & normals() const { return mNormals;} + [[nodiscard]] inline const Vertices & getVertices() const { + return mVertices; + } - protected: - DrawMode mDrawMode; + [[nodiscard]] inline const Indices & getIndexData() const { + return mIndices; + } - Vertices mVertices; - Colors mColors; - Indices mIndices; - TexCoords mTexCoords; - Normals mNormals; + [[nodiscard]] inline const Colors & getColors() const { return mColors; } + + [[nodiscard]] inline const TexCoords & getTexCoords() const { + return mTexCoords; + } + + [[nodiscard]] inline const Normals & getNormals() const { + return mNormals; + } + + [[nodiscard]] inline size_t getTexCoordsStride() const { + return mTexCoords.size() * sizeof(mTexCoords[0]); + } + + protected: + DrawMode mDrawMode; + + Vertices mVertices {}; + Colors mColors {}; + Indices mIndices {}; + TexCoords mTexCoords {}; + Normals mNormals {}; }; struct Shape : public ShapeBase { - friend MeshRenderer; - friend Object; - Shape () {} - Shape(const ShapeBase & rhs) : ShapeBase(rhs) {} + friend MeshRenderer; + friend Object; - virtual inline void setVertices(const Vertices & value) {mVertices = value;} - virtual inline void setIndices(const Indices & value) {mIndices = value;} - virtual inline void setColors(const Colors & value) {mColors = value;} - virtual inline void setTexCoords(const TexCoords & value) {mTexCoords = value;} - virtual inline void setNormals(const Normals & value) {mNormals = value;} - virtual inline void setShape(const Shape & value) { *this = value;} + Shape() = default; + + explicit Shape(const ShapeBase & rhs) : ShapeBase(rhs) {} + + virtual inline void setVertices(const Vertices & value) { + mVertices = value; + } + + virtual inline void setIndices(const Indices & value) { + mIndices = value; + } + + virtual inline void setColors(const Colors & value) { mColors = value; } + + virtual inline void setTexCoords(const TexCoords & value) { + mTexCoords = value; + } + + virtual inline void setNormals(const Normals & value) { + mNormals = value; + } + + virtual inline void setShape(const Shape & value) { *this = value; } }; -// Primitives inherit from ShapeBase, does not allow setting of shape values - class QTKAPI Mesh { - - }; + // Primitives inherit from ShapeBase, does not allow setting of shape values + class QTKAPI Mesh {}; struct QTKAPI Cube : public ShapeBase { - Cube(DrawMode mode=QTK_DRAW_ARRAYS); + explicit Cube(DrawMode mode = QTK_DRAW_ARRAYS); }; struct QTKAPI Triangle : public ShapeBase { - Triangle(DrawMode mode=QTK_DRAW_ARRAYS); + explicit Triangle(DrawMode mode = QTK_DRAW_ARRAYS); }; -} +} // namespace Qtk -#endif // QTK_MESH_H +#endif // QTK_MESH_H diff --git a/src/meshrenderer.cpp b/src/meshrenderer.cpp index 0d7a114..1d8dc21 100644 --- a/src/meshrenderer.cpp +++ b/src/meshrenderer.cpp @@ -17,48 +17,46 @@ using namespace Qtk; // Static QHash that holds all MeshRenderer instances using their mName as keys Qtk::MeshRenderer::MeshManager Qtk::MeshRenderer::sInstances; -MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) - : Object(name, shape), mVertexShader(":/multi-color.vert"), - mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES), - mHasTexture(false) -{ +MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) : + Object(name, shape), mVertexShader(":/multi-color.vert"), + mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES) { mShape = Shape(shape); init(); sInstances.insert(name, this); } -MeshRenderer::~MeshRenderer() -{ - if (mHasTexture) { - mTexture->destroy(); - } +MeshRenderer::~MeshRenderer() { sInstances.remove(mName); } - // Static member function to retrieve instances of MeshRenderers -MeshRenderer * MeshRenderer::getInstance(const QString & name) -{ return sInstances[name];} - +MeshRenderer * MeshRenderer::getInstance(const QString & name) { + return sInstances[name]; +} /******************************************************************************* * Public Member Functions ******************************************************************************/ -void MeshRenderer::init() -{ - if (mVAO.isCreated()) mVAO.destroy(); - if (mProgram.isLinked()) mProgram.removeAllShaders(); - if (mVBO.isCreated()) mVBO.destroy(); +void MeshRenderer::init() { + if(mVAO.isCreated()) { + mVAO.destroy(); + } + if(mProgram.isLinked()) { + mProgram.removeAllShaders(); + } + if(mVBO.isCreated()) { + mVBO.destroy(); + } mVAO.create(); mVAO.bind(); mProgram.create(); - mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, - mVertexShader.c_str()); - mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, - mFragmentShader.c_str()); + mProgram.addShaderFromSourceFile( + QOpenGLShader::Vertex, mVertexShader.c_str()); + mProgram.addShaderFromSourceFile( + QOpenGLShader::Fragment, mFragmentShader.c_str()); mProgram.link(); mProgram.bind(); @@ -68,22 +66,20 @@ void MeshRenderer::init() // Combine position and color data into one vector, allowing us to use one VBO Vertices combined; - combined.reserve(vertices().size() + colors().size()); - combined.insert(combined.end(), vertices().begin(), vertices().end()); - combined.insert(combined.end(), colors().begin(), colors().end()); + combined.reserve(getVertices().size() + getColors().size()); + combined.insert(combined.end(), getVertices().begin(), getVertices().end()); + combined.insert(combined.end(), getColors().begin(), getColors().end()); - mVBO.allocate(combined.data(), - combined.size() * sizeof(combined[0])); + mVBO.allocate(combined.data(), combined.size() * sizeof(combined[0])); // Enable position attribute mProgram.enableAttributeArray(0); - mProgram.setAttributeBuffer(0, GL_FLOAT, 0, - 3, sizeof(QVector3D)); + mProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D)); // Enable color attribute, setting offset to total size of vertices() mProgram.enableAttributeArray(1); - mProgram.setAttributeBuffer(1, GL_FLOAT, - vertices().size() * sizeof(vertices()[0]), - 3, sizeof(QVector3D)); + mProgram.setAttributeBuffer( + 1, GL_FLOAT, getVertices().size() * sizeof(getVertices()[0]), 3, + sizeof(QVector3D)); mVBO.release(); @@ -91,82 +87,100 @@ void MeshRenderer::init() mVAO.release(); } -void MeshRenderer::draw() -{ - mProgram.bind(); +void MeshRenderer::draw() { + bindShaders(); mVAO.bind(); - if(mHasTexture) { - mTexture->bind(); + if(mTexture.hasTexture()) { + mTexture.getOpenGLTexture().bind(); } // TODO: Automate uniforms some other way setUniformMVP(); - if (mShape.mDrawMode == QTK_DRAW_ARRAYS) { - glDrawArrays(mDrawType, 0, vertices().size()); - } - else if (mShape.mDrawMode == QTK_DRAW_ELEMENTS - || mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) { - glDrawElements(mDrawType, mShape.mIndices.size(), - GL_UNSIGNED_INT, mShape.mIndices.data()); + if(mShape.mDrawMode == QTK_DRAW_ARRAYS) { + glDrawArrays(mDrawType, 0, getVertices().size()); + } else if( + mShape.mDrawMode == QTK_DRAW_ELEMENTS + || mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) { + glDrawElements( + mDrawType, mShape.mIndices.size(), GL_UNSIGNED_INT, + mShape.mIndices.data()); } - if(mHasTexture) { - mTexture->release(); + if(mTexture.hasTexture()) { + mTexture.getOpenGLTexture().release(); } mVAO.release(); - mProgram.release(); + releaseShaders(); } -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::Projection()); mProgram.setUniformValue(view, Scene::View()); mProgram.setUniformValue(model, mTransform.toMatrix()); } -void MeshRenderer::setColor(const QVector3D & color) -{ - if (mShape.mColors.empty()) { - for (const auto & vertex : mShape.vertices()) { +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.colors().size(); i++) { + } else { + for(int i = 0; i < mShape.getColors().size(); i++) { mShape.mColors[i] = color; } } } -void MeshRenderer::setTexture(const char * path) -{ - mTexture = new QOpenGLTexture(*Texture::initImage(path)); - mHasTexture = true; +void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims) { + mVAO.bind(); + mNBO.destroy(); + mNBO.create(); + mNBO.bind(); + mNBO.allocate(t.data(), t.size() * sizeof(t[0])); + enableAttributeArray(1); + if(dims == 2) { + setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D)); + } else if(dims == 3) { + setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D)); + } + mNBO.release(); + mVAO.release(); } -void MeshRenderer::setTexture(QOpenGLTexture * texture) -{ - mTexture = texture; - mHasTexture = true; +void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims) { + // TODO: Store state to track if buffer objects are bound + mVAO.bind(); + mNBO.destroy(); + mNBO.create(); + mNBO.bind(); + mNBO.allocate(n.data(), n.size() * sizeof(n[0])); + enableAttributeArray(1); + if(dims == 2) { + setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D)); + } else if(dims == 3) { + setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D)); + } + mNBO.release(); + mVAO.release(); } - /******************************************************************************* * Inherited Virtual Member Functions ******************************************************************************/ -void MeshRenderer::setShape(const Shape & value) -{ +void MeshRenderer::setShape(const Shape & value) { Object::setShape(value); init(); } diff --git a/src/meshrenderer.h b/src/meshrenderer.h index edde65b..f33a2d6 100644 --- a/src/meshrenderer.h +++ b/src/meshrenderer.h @@ -12,69 +12,119 @@ #include #include +#include namespace Qtk { - class QTKAPI MeshRenderer : public Object { - public: - // Delegate constructors - MeshRenderer(const char * name, Vertices vertices, Indices indices, - DrawMode mode=QTK_DRAW_ARRAYS) - : MeshRenderer(name, ShapeBase(mode, vertices, indices)) - {} - MeshRenderer(const char * name) - : MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS)) - {} - // Constructor - MeshRenderer(const char * name, const ShapeBase &shape); - ~MeshRenderer(); + class QTKAPI ShaderBindScope { + public: + explicit ShaderBindScope( + QOpenGLShaderProgram * program, bool was_locked) : + mWasBound(was_locked) { + mProgram = program; + if(!mWasBound) { + mProgram->bind(); + } + } - // Retrieve a mesh by name stored within a static QHash - static MeshRenderer * getInstance(const QString & name); + ~ShaderBindScope() { + if(!mWasBound) { + mProgram->release(); + } + } - void init(); - void draw(); - - // Draw types like GL_TRIANGLES, GL_POINTS, GL_LINES, etc - void setDrawType(int drawType) { mDrawType = drawType;} - - // Shader settings - inline void setShaderVertex(const std::string & vert) { mVertexShader = vert;} - inline void setShaderFragment(const std::string & frag) - { mFragmentShader = frag;} - void setShaders(const std::string & vert, const std::string & frag); - - template - inline void setUniform(int location, T value) - { mProgram.setUniformValue(location, value);} - - template - inline void setUniform(const char * location, T value) - { mProgram.setUniformValue(location, value);} - - // Set MVP matrix using this Object's transform - // + View and projection provided by MainWidget static members - void setUniformMVP(const char * model="uModel", const char * view="uView", - const char * projection="uProjection"); - - // Sets the texture to the image at the given path - // + Sets mHasTexture to enable texture binding in draw() - void setTexture(const char * path); - void setTexture(QOpenGLTexture * texture); - - // These functions modify data stored in a VBO - // + After calling them, the VBO will need to be reallocated - void setShape(const Shape & value) override; - void setColor(const QVector3D & color); - - // Static QHash of all mesh objects within the scene - typedef QHash MeshManager; - private: - static MeshManager sInstances; - - int mDrawType; - bool mHasTexture; - std::string mVertexShader, mFragmentShader; + private: + QOpenGLShaderProgram * mProgram; + bool mWasBound; }; -} -#endif // QTK_MESHRENDERER_H + + class QTKAPI MeshRenderer : public Object { + public: + // Delegate constructors + MeshRenderer( + const char * name, Vertices vertices, Indices indices, + DrawMode mode = QTK_DRAW_ARRAYS) : + MeshRenderer( + name, ShapeBase(mode, std::move(vertices), std::move(indices))) {} + + explicit MeshRenderer(const char * name) : + MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS)) {} + + // Constructor + MeshRenderer(const char * name, const ShapeBase & shape); + ~MeshRenderer() override; + + // Retrieve a mesh by name stored within a static QHash + static MeshRenderer * getInstance(const QString & name); + + void init(); + void draw(); + + // Draw types like GL_TRIANGLES, GL_POINTS, GL_LINES, etc + void setDrawType(int drawType) { mDrawType = drawType; } + + // Shader settings + inline void setShaderVertex(const std::string & vert) { + mVertexShader = vert; + } + + inline void setShaderFragment(const std::string & frag) { + mFragmentShader = frag; + } + + void setShaders(const std::string & vert, const std::string & frag); + + template inline void setUniform(int location, T value) { + ShaderBindScope lock(&mProgram, mBound); + mProgram.setUniformValue(location, value); + } + + template + inline void setUniform(const char * location, T value) { + ShaderBindScope lock(&mProgram, mBound); + mProgram.setUniformValue(location, value); + } + + // Set MVP matrix using this Object's transform + // + View and projection provided by MainWidget static members + void setUniformMVP( + const char * model = "uModel", const char * view = "uView", + const char * projection = "uProjection"); + + // These functions modify data stored in a VBO + // + After calling them, the VBO will need to be reallocated + void setShape(const Shape & value) override; + void setColor(const QVector3D & color); + + void setAttributeBuffer( + int location, GLenum type, int offset, int tupleSize, + int stride = 0) { + ShaderBindScope lock(&mProgram, mBound); + mVAO.bind(); + mProgram.setAttributeBuffer(location, type, offset, tupleSize, stride); + mVAO.release(); + } + + inline void enableAttributeArray(int location) { + ShaderBindScope lock(&mProgram, mBound); + mVAO.bind(); + mProgram.enableAttributeArray(location); + mVAO.release(); + } + + void reallocateTexCoords(const TexCoords & t, unsigned dims = 2); + + void reallocateNormals(const Normals & n, unsigned dims = 3); + + // Static QHash of all mesh objects within the scene + typedef QHash MeshManager; + + private: + static MeshManager sInstances; + + int mDrawType {}; + std::string mVertexShader {}, mFragmentShader {}; + }; +} // namespace Qtk + +#endif // QTK_MESHRENDERER_H diff --git a/src/model.cpp b/src/model.cpp index 1f1128a..373e9c8 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -19,18 +19,15 @@ using namespace Qtk; Model::ModelManager Model::mManager; // Static function to access ModelManager for getting Models by name -Model * Model::getInstance(const char * name) -{ +Model * Model::getInstance(const char * name) { return mManager[name]; } - /******************************************************************************* * ModelMesh 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 @@ -44,14 +41,12 @@ void ModelMesh::initMesh(const char * vert, const char * frag) mVBO->setUsagePattern(QOpenGLBuffer::StaticDraw); mVBO->bind(); - mVBO->allocate(mVertices.data(), - mVertices.size() * sizeof(mVertices[0])); + mVBO->allocate(mVertices.data(), mVertices.size() * sizeof(mVertices[0])); // Allocate EBO mEBO->setUsagePattern(QOpenGLBuffer::StaticDraw); mEBO->bind(); - mEBO->allocate(mIndices.data(), - mIndices.size() * sizeof(mIndices[0])); + mEBO->allocate(mIndices.data(), mIndices.size() * sizeof(mIndices[0])); mEBO->release(); // Load and link shaders @@ -62,46 +57,40 @@ 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(); mVAO->release(); } - /******************************************************************************* * ModelMesh Public Member Functions ******************************************************************************/ -void ModelMesh::draw(QOpenGLShaderProgram & shader) -{ +void ModelMesh::draw(QOpenGLShaderProgram & shader) { mVAO->bind(); // Bind shader shader.bind(); @@ -114,7 +103,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(); @@ -124,20 +113,26 @@ 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") number = std::to_string(diffuseCount++); - if (name == "texture_specular") number = std::to_string(specularCount++); - if (name == "texture_normal") number = std::to_string(normalCount++); + if(name == "texture_diffuse") { + number = std::to_string(diffuseCount++); + } + if(name == "texture_specular") { + number = std::to_string(specularCount++); + } + if(name == "texture_normal") { + number = std::to_string(normalCount++); + } // Set the uniform to track this texture ID using our naming convention shader.setUniformValue((name + number).c_str(), i); } // Draw the mesh - glDrawElements(GL_TRIANGLES, mIndices.size(), - GL_UNSIGNED_INT, mIndices.data()); + 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(); @@ -145,48 +140,42 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) glActiveTexture(GL_TEXTURE0); } - /******************************************************************************* * Model Public Member Functions ******************************************************************************/ -void Model::draw() -{ - for (GLuint i = 0; i < mMeshes.size(); i++) { - mMeshes[i].mTransform = mTransform; - mMeshes[i].draw(); +void Model::draw() { + for(auto & mMeshe : mMeshes) { + mMeshe.mTransform = mTransform; + mMeshe.draw(); } } -void Model::draw(QOpenGLShaderProgram & shader) -{ - for (GLuint i = 0; i < mMeshes.size(); i++) { - mMeshes[i].mTransform = mTransform; - mMeshes[i].draw(shader); +void Model::draw(QOpenGLShaderProgram & shader) { + for(auto & mMeshe : mMeshes) { + mMeshe.mTransform = mTransform; + mMeshe.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( - *Texture::initImage(fullPath.c_str(), flipX, flipY)); + *OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY)); modified = true; } } - if (!modified) { + if(!modified) { qDebug() << "Attempt to flip texture that doesn't exist: " << fullPath.c_str() << "\n"; } - } - /******************************************************************************* * Model Private Member Functions ******************************************************************************/ @@ -206,31 +195,25 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) * * @param path Absolute path to a model .obj or other format accepted by assimp */ -void Model::loadModel(const std::string & path) -{ +void Model::loadModel(const std::string & path) { Assimp::Importer import; // JIC a relative path was used, get the absolute file path QFileInfo info(path.c_str()); info.makeAbsolute(); mDirectory = path[0] == ':' ? RM::getPath(path) - : info.absoluteFilePath().toStdString(); + : info.absoluteFilePath().toStdString(); // Import the model, converting non-triangular geometry to triangles // + And flipping texture UVs, etc.. // Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html - const aiScene * scene = - import.ReadFile(mDirectory, aiProcess_Triangulate - | aiProcess_FlipUVs - | aiProcess_GenSmoothNormals - | aiProcess_CalcTangentSpace - | aiProcess_OptimizeMeshes - | aiProcess_SplitLargeMeshes - ); - + const aiScene * scene = import.ReadFile( + mDirectory, aiProcess_Triangulate | aiProcess_FlipUVs + | aiProcess_GenSmoothNormals | aiProcess_CalcTangentSpace + | aiProcess_OptimizeMeshes | aiProcess_SplitLargeMeshes); // If there were errors, print and return - if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { + if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { qDebug() << "Error::ASSIMP::" << import.GetErrorString() << "\n"; return; } @@ -250,28 +233,26 @@ void Model::loadModel(const std::string & path) mManager.insert(mName, 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; @@ -285,7 +266,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); @@ -295,7 +276,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); @@ -313,8 +294,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) vector3D.setY(mesh->mBitangents[i].y); vector3D.setZ(mesh->mBitangents[i].z); vertex.mBitangent = vector3D; - } - else { + } else { vertex.mTextureCoord = {0.0f, 0.0f}; } @@ -323,62 +303,56 @@ 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 - ModelMesh::Textures diffuseMaps = - loadMaterialTextures(material, aiTextureType_DIFFUSE, - "texture_diffuse"); + ModelMesh::Textures diffuseMaps = loadMaterialTextures( + material, aiTextureType_DIFFUSE, "texture_diffuse"); // Insert all diffuse textures found into our textures container textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); // Get all specular textures from the material - ModelMesh::Textures specularMaps = - loadMaterialTextures(material, aiTextureType_SPECULAR, - "texture_specular"); + ModelMesh::Textures specularMaps = loadMaterialTextures( + material, aiTextureType_SPECULAR, "texture_specular"); // Insert all specular textures found into our textures container textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); // Get all normal textures from the material ModelMesh::Textures normalMaps = - loadMaterialTextures(material, aiTextureType_HEIGHT, - "texture_normal"); + loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal"); // Insert all normal maps found into our textures container textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); } - return ModelMesh(vertices, indices, textures, - mVertexShader, mFragmentShader); + return {vertices, indices, textures, mVertexShader, mFragmentShader}; } ModelMesh::Textures Model::loadMaterialTextures( - aiMaterial * mat, aiTextureType type, const std::string & typeName) -{ + 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 (GLuint j = 0; j < mTexturesLoaded.size(); j++) { + for(auto & j : mTexturesLoaded) { // If the path to the texture already exists in m_texturesLoaded, skip it - if (std::strcmp(mTexturesLoaded[j].mPath.data(), fileName.C_Str()) == 0) { - textures.push_back(mTexturesLoaded[j]); + if(std::strcmp(j.mPath.data(), fileName.C_Str()) == 0) { + textures.push_back(j); // If we have loaded the texture, do not load it again skip = true; break; @@ -386,11 +360,11 @@ ModelMesh::Textures Model::loadMaterialTextures( } // If the texture has not yet been loaded - if (!skip) { + if(!skip) { ModelTexture texture; - texture.mTexture = Texture::initTexture2D( - std::string(mDirectory + '/' + fileName.C_Str()).c_str(), - false, false); + texture.mTexture = OpenGLTextureFactory::initTexture2D( + std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false, + false); texture.mID = texture.mTexture->textureId(); texture.mType = typeName; texture.mPath = fileName.C_Str(); @@ -399,22 +373,20 @@ ModelMesh::Textures Model::loadMaterialTextures( // Add the texture to the loaded textures to avoid loading it twice mTexturesLoaded.push_back(texture); } - } // Return the resulting textures return textures; } -void Model::sortModels() -{ +void Model::sortModels() { auto cameraPos = Scene::Camera().transform(); - auto cameraDistance = [&cameraPos](const ModelMesh &a, const ModelMesh &b) - { - // Sort by the first vertex position, since all transforms will be the same - return (cameraPos.translation().distanceToPoint(a.mVertices[0].mPosition)) - < (cameraPos.translation().distanceToPoint(b.mVertices[0].mPosition)); + auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) { + // Sort by the first vertex position in the model + return (cameraPos.getTranslation().distanceToPoint( + a.mVertices[0].mPosition)) + < (cameraPos.getTranslation().distanceToPoint( + b.mVertices[0].mPosition)); }; std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance); } - diff --git a/src/model.h b/src/model.h index 20b5e96..432add0 100644 --- a/src/model.h +++ b/src/model.h @@ -18,124 +18,129 @@ #include // Assimp -#include #include #include +#include // QTK +#include #include #include namespace Qtk { struct QTKAPI ModelVertex { - QVector3D mPosition; - QVector3D mNormal; - QVector3D mTangent; - QVector3D mBitangent; - QVector2D mTextureCoord; + QVector3D mPosition; + QVector3D mNormal; + QVector3D mTangent; + QVector3D mBitangent; + QVector2D mTextureCoord; }; struct QTKAPI ModelTexture { - GLuint mID; - QOpenGLTexture * mTexture; - std::string mType; - std::string mPath; + GLuint mID {}; + QOpenGLTexture * mTexture {}; + std::string mType {}; + std::string mPath {}; }; class Model; class QTKAPI ModelMesh : protected QOpenGLFunctions { - public: - friend Model; - typedef std::vector Vertices; - typedef std::vector Indices; - typedef std::vector Textures; + public: + friend Model; + typedef std::vector Vertices; + typedef std::vector Indices; + typedef std::vector Textures; - // Constructors, Destructors - ModelMesh(Vertices vertices, Indices indices, Textures textures, - const char * vertexShader=":/model-basic.vert", - const char * fragmentShader=":/model-basic.frag") - : mProgram(new QOpenGLShaderProgram), - mVAO(new QOpenGLVertexArrayObject), - mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)), - mEBO(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer)), - mVertices(std::move(vertices)), - mIndices(std::move(indices)), - mTextures(std::move(textures)) - { initMesh(vertexShader, fragmentShader);} - ~ModelMesh() {} + // Constructors, Destructors + ModelMesh( + Vertices vertices, Indices indices, Textures textures, + const char * vertexShader = ":/model-basic.vert", + const char * fragmentShader = ":/model-basic.frag") : + mProgram(new QOpenGLShaderProgram), + mVAO(new QOpenGLVertexArrayObject), + mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)), + mEBO(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer)), + mVertices(std::move(vertices)), mIndices(std::move(indices)), + mTextures(std::move(textures)) { + initMesh(vertexShader, fragmentShader); + } - private: - void initMesh(const char * vert, const char * frag); + ~ModelMesh() = default; - // ModelMesh Private Members - QOpenGLBuffer * mVBO, * mEBO; - QOpenGLVertexArrayObject * mVAO; - QOpenGLShaderProgram * mProgram; + private: + void initMesh(const char * vert, const char * frag); - public: - inline void draw() { draw(*mProgram);} - void draw(QOpenGLShaderProgram & shader); + // ModelMesh Private Members + QOpenGLBuffer *mVBO, *mEBO; + QOpenGLVertexArrayObject * mVAO; + QOpenGLShaderProgram * mProgram; - // ModelMesh Public Members - Vertices mVertices; - Indices mIndices; - Textures mTextures; - Transform3D mTransform; + public: + inline void draw() { draw(*mProgram); } + + void draw(QOpenGLShaderProgram & shader); + + // ModelMesh Public Members + Vertices mVertices {}; + Indices mIndices {}; + Textures mTextures {}; + Transform3D mTransform; }; - class QTKAPI Model : public QObject { - Q_OBJECT + Q_OBJECT - public: - inline Model(const char * name, const char * path, - const char * vertexShader=":/model-basic.vert", - const char * fragmentShader=":/model-basic.frag") - : mName(name), mVertexShader(vertexShader), - mFragmentShader(fragmentShader) - { loadModel(path);} - inline ~Model() { mManager.remove(mName);} - - void draw(); - void draw(QOpenGLShaderProgram & shader); - - void flipTexture(const std::string & fileName, - bool flipX=false, bool flipY=true); - - template - void setUniform(const char * location, T value) - { - for (auto & mesh : mMeshes) { - mesh.mProgram->bind(); - - mesh.mProgram->setUniformValue(location, value); - - mesh.mProgram->release(); + public: + inline Model( + const char * name, const char * path, + const char * vertexShader = ":/model-basic.vert", + const char * fragmentShader = ":/model-basic.frag") : + mName(name), + mVertexShader(vertexShader), mFragmentShader(fragmentShader) { + loadModel(path); } - } - Transform3D mTransform; + inline ~Model() override { mManager.remove(mName); } - static Model * getInstance(const char * name); + void draw(); + void draw(QOpenGLShaderProgram & shader); - typedef QHash ModelManager; - private: - static ModelManager mManager; - void loadModel(const std::string & path); - void processNode(aiNode * node, const aiScene * scene); - ModelMesh processMesh(aiMesh * mesh, const aiScene * scene); - ModelMesh::Textures loadMaterialTextures(aiMaterial * mat, aiTextureType type, - const std::string & typeName); - void sortModels(); + void flipTexture( + const std::string & fileName, bool flipX = false, bool flipY = true); - // Model Private Members + template void setUniform(const char * location, T value) { + for(auto & mesh : mMeshes) { + mesh.mProgram->bind(); - ModelMesh::Textures mTexturesLoaded; - std::vector mMeshes; - std::string mDirectory; - const char * mVertexShader, * mFragmentShader, * mName; + mesh.mProgram->setUniformValue(location, value); + + mesh.mProgram->release(); + } + } + + Transform3D mTransform; + + static Model * getInstance(const char * name); + + typedef QHash ModelManager; + + private: + static ModelManager mManager; + void loadModel(const std::string & path); + void processNode(aiNode * node, const aiScene * scene); + ModelMesh processMesh(aiMesh * mesh, const aiScene * scene); + ModelMesh::Textures loadMaterialTextures( + aiMaterial * mat, aiTextureType type, const std::string & typeName); + void sortModels(); + + // Model Private Members + + ModelMesh::Textures mTexturesLoaded {}; + std::vector mMeshes {}; + std::string mDirectory {}; + const char *mVertexShader, *mFragmentShader, *mName; }; -} +} // namespace Qtk -#endif // QTK_MODEL_H +#endif // QTK_MODEL_H diff --git a/src/object.h b/src/object.h index 03169fb..90098f0 100644 --- a/src/object.h +++ b/src/object.h @@ -15,49 +15,101 @@ #include #include +#include namespace Qtk { class QTKAPI Object : public QObject { - Q_OBJECT + Q_OBJECT - public: - friend MeshRenderer; - // Initialize an object with no shape data assigned - Object(const char * name) - : mName(name), mVBO(QOpenGLBuffer::VertexBuffer) - { } - // Initialize an object with shape data assigned - Object(const char * name, const ShapeBase & shape) - : mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape) - { } + public: + friend MeshRenderer; - ~Object() {} + // Initialize an object with no shape data assigned + explicit Object(const char * name) : + mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false) {} - inline const Vertices & vertices() { return mShape.mVertices;} - inline const Indices & indices() { return mShape.mIndices;} - inline const Colors & colors() { return mShape.mColors;} - inline const TexCoords & texCoords() { return mShape.mTexCoords;} - inline const Normals & normals() { return mShape.mNormals;} - inline QOpenGLTexture & texture() const { return *mTexture;} + // Initialize an object with shape data assigned + Object(const char * name, const ShapeBase & shape) : + mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape), + mBound(false) {} - virtual inline void setVertices(const Vertices & value) { mShape.mVertices = value;} - virtual inline void setIndices(const Indices & value) { mShape.mIndices = value;} - virtual inline void setColors(const Colors & value) { mShape.mColors = value;} - virtual inline void setTexCoords(const TexCoords & value) { mShape.mTexCoords = value;} - virtual inline void setNormals(const Normals & value) { mShape.mNormals = value;} - virtual inline void setShape(const Shape & value) { mShape = value;} + ~Object() override = default; - QOpenGLBuffer mVBO, mNBO; - QOpenGLVertexArrayObject mVAO; - QOpenGLShaderProgram mProgram; + inline const Colors & getColors() { return mShape.mColors; } - Transform3D mTransform; - Shape mShape; + inline const Indices & getIndexData() { return mShape.mIndices; } - const char * mName; - private: - QOpenGLTexture * mTexture; + inline const Normals & getNormals() { return mShape.mNormals; } + + [[nodiscard]] inline const Shape & getShape() const { return mShape; } + + inline const TexCoords & getTexCoords() { return mShape.mTexCoords; } + + inline Texture & getTexture() { return mTexture; } + + inline const Vertices & getVertices() { return mShape.mVertices; } + + virtual inline void setColors(const Colors & value) { + mShape.mColors = value; + } + + virtual inline void setIndices(const Indices & value) { + mShape.mIndices = value; + } + + virtual inline void setNormals(const Normals & value) { + mShape.mNormals = value; + } + + virtual inline void setShape(const Shape & value) { mShape = value; } + + virtual inline void setTexCoords(const TexCoords & value) { + mShape.mTexCoords = value; + } + + virtual inline void setTexture( + const char * path, bool flipX = false, bool flipY = false) { + mTexture.setTexture(path, flipX, flipY); + } + + virtual inline void setCubeMap(const char * path) { + mTexture.setCubeMap(path); + } + + virtual inline void setTexture(const Texture & t) { + mTexture.setTexture(t.getPath()); + } + + virtual inline void setVertices(const Vertices & value) { + mShape.mVertices = value; + } + + virtual inline void bindShaders() { + mBound = true; + mProgram.bind(); + } + + virtual inline void releaseShaders() { + mBound = false; + mProgram.release(); + } + + QOpenGLBuffer mVBO, mNBO; + QOpenGLVertexArrayObject mVAO; + + Transform3D mTransform; + Shape mShape; + Texture mTexture; + const char * mName; + bool mBound; + + private: + virtual inline void setTexture(QOpenGLTexture * value) { + mTexture.setTexture(value); + } + + QOpenGLShaderProgram mProgram; }; -} +} // namespace Qtk -#endif // QTK_OBJECT_H +#endif // QTK_OBJECT_H diff --git a/src/qtkapi.h b/src/qtkapi.h index a34cdae..088a1fd 100644 --- a/src/qtkapi.h +++ b/src/qtkapi.h @@ -11,13 +11,13 @@ #include #ifdef QTK_SHARED -# if defined(QTK_EXPORT) -# define QTKAPI Q_DECL_EXPORT -# else -# define QTKAPI Q_DECL_IMPORT -# endif +#if defined(QTK_EXPORT) +#define QTKAPI Q_DECL_EXPORT #else -# define QTKAPI +#define QTKAPI Q_DECL_IMPORT +#endif +#else +#define QTKAPI #endif -#endif //QTK_QTKAPI_H +#endif // QTK_QTKAPI_H diff --git a/src/qtkwidget.cpp b/src/qtkwidget.cpp index ede2b86..34cd9a3 100644 --- a/src/qtkwidget.cpp +++ b/src/qtkwidget.cpp @@ -8,10 +8,10 @@ #include -#include -#include -#include #include +#include +#include +#include using namespace Qtk; @@ -19,57 +19,50 @@ using namespace Qtk; * Constructors, Destructors ******************************************************************************/ -QtkWidget::QtkWidget() : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) -{ +QtkWidget::QtkWidget() : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) { initializeWidget(); } // Constructor for using this widget in QtDesigner -QtkWidget::QtkWidget(QWidget *parent) : QOpenGLWidget(parent), - mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) -{ +QtkWidget::QtkWidget(QWidget * parent) : + QOpenGLWidget(parent), mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) { initializeWidget(); } -QtkWidget::QtkWidget(const QSurfaceFormat &format) - : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) -{ +QtkWidget::QtkWidget(const QSurfaceFormat & format) : + mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) { setFormat(format); setFocusPolicy(Qt::ClickFocus); } -QtkWidget::~QtkWidget() -{ +QtkWidget::~QtkWidget() { makeCurrent(); teardownGL(); } - /******************************************************************************* * Private Member Functions ******************************************************************************/ -void QtkWidget::teardownGL() -{ +void QtkWidget::teardownGL() { // Nothing to teardown yet... } - /******************************************************************************* * Inherited Virtual Member Functions ******************************************************************************/ -void QtkWidget::paintGL() -{ +void QtkWidget::paintGL() { // Clear buffers glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // Draw the scene first, since it handles drawing our skybox - if (mScene != Q_NULLPTR) mScene->draw(); + if(mScene != Q_NULLPTR) { + mScene->draw(); + } } -void QtkWidget::initializeGL() -{ +void QtkWidget::initializeGL() { initializeOpenGLFunctions(); // Connect the frameSwapped signal to call the update() function connect(this, SIGNAL(frameSwapped()), this, SLOT(update())); @@ -77,17 +70,18 @@ void QtkWidget::initializeGL() // Initialize OpenGL debug context #ifdef QTK_DEBUG 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(); } -#endif // QTK_DEBUG +#endif // QTK_DEBUG printContextInformation(); -// Initialize opengl settings + // Initialize opengl settings glEnable(GL_MULTISAMPLE); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); @@ -98,35 +92,31 @@ 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::Projection().setToIdentity(); - Scene::Projection().perspective(45.0f, - float(width) / float(height), - 0.1f, 1000.0f); + Scene::Projection().perspective( + 45.0f, float(width) / float(height), 0.1f, 1000.0f); } - /******************************************************************************* * Protected Slots ******************************************************************************/ -void QtkWidget::update() -{ +void QtkWidget::update() { updateCameraInput(); - if (mScene != Q_NULLPTR) mScene->update(); + if(mScene != Q_NULLPTR) { + mScene->update(); + } QWidget::update(); } -void QtkWidget::messageLogged(const QOpenGLDebugMessage &msg) -{ +void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) { QString error; // Format based on severity - switch (msg.severity()) - { + switch(msg.severity()) { case QOpenGLDebugMessage::NotificationSeverity: error += "--"; break; @@ -144,9 +134,11 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage &msg) error += " ("; // Format based on source -#define CASE(c) case QOpenGLDebugMessage::c: error += #c; break - switch (msg.source()) - { +#define CASE(c) \ + case QOpenGLDebugMessage::c: \ + error += #c; \ + break + switch(msg.source()) { CASE(APISource); CASE(WindowSystemSource); CASE(ShaderCompilerSource); @@ -160,9 +152,11 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage &msg) error += " : "; // Format based on type -#define CASE(c) case QOpenGLDebugMessage::c: error += #c; break - switch (msg.type()) - { +#define CASE(c) \ + case QOpenGLDebugMessage::c: \ + error += #c; \ + break + switch(msg.type()) { CASE(InvalidType); CASE(ErrorType); CASE(DeprecatedBehaviorType); @@ -180,14 +174,12 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage &msg) qDebug() << qPrintable(error) << "\n" << qPrintable(msg.message()) << "\n"; } - /******************************************************************************* * Protected Helpers ******************************************************************************/ -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 { @@ -195,32 +187,27 @@ 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()); } - /******************************************************************************* * Private Helpers ******************************************************************************/ -void QtkWidget::initializeWidget() -{ +void QtkWidget::initializeWidget() { QSurfaceFormat format; format.setRenderableType(QSurfaceFormat::OpenGL); format.setProfile(QSurfaceFormat::CoreProfile); @@ -237,8 +224,7 @@ void QtkWidget::initializeWidget() setFocusPolicy(Qt::ClickFocus); } -void QtkWidget::printContextInformation() -{ +void QtkWidget::printContextInformation() { QString glType; QString glVersion; QString glProfile; @@ -246,65 +232,62 @@ void QtkWidget::printContextInformation() QString glVendor; QString glRenderer; - // Get Version Information glType = (context()->isOpenGLES()) ? "OpenGL ES" : "OpenGL"; glVersion = reinterpret_cast(glGetString(GL_VERSION)); - glVendor = - reinterpret_cast(glGetString(GL_VENDOR)); - glRenderer = - reinterpret_cast(glGetString(GL_RENDERER)); + glVendor = reinterpret_cast(glGetString(GL_VENDOR)); + glRenderer = reinterpret_cast(glGetString(GL_RENDERER)); // Get Profile Information -#define CASE(c) case QSurfaceFormat::c: glProfile = #c; break - switch (format().profile()) { +#define CASE(c) \ + case QSurfaceFormat::c: \ + glProfile = #c; \ + break + switch(format().profile()) { CASE(NoProfile); CASE(CoreProfile); CASE(CompatibilityProfile); } #undef CASE -// qPrintable() will print our QString w/o quotes around it. + // qPrintable() will print our QString w/o quotes around it. qDebug() << qPrintable(glType) << qPrintable(glVersion) << "(" << qPrintable(glProfile) << ")" << "\nOpenGL Vendor: " << qPrintable(glVendor) << "\nRendering Device: " << qPrintable(glRenderer) << "\n"; - - } -void QtkWidget::updateCameraInput() -{ +void QtkWidget::updateCameraInput() { Input::update(); // Camera Transformation - if (Input::buttonPressed(Qt::RightButton)) { + if(Input::buttonPressed(Qt::RightButton)) { static const float transSpeed = 0.1f; static const float rotSpeed = 0.5f; // Handle rotations - Scene::Camera().transform().rotate(-rotSpeed * Input::mouseDelta().x(), - Camera3D::LocalUp); - Scene::Camera().transform().rotate(-rotSpeed * Input::mouseDelta().y(), - Scene::Camera().right()); + Scene::Camera().transform().rotate( + -rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp); + Scene::Camera().transform().rotate( + -rotSpeed * Input::mouseDelta().y(), Scene::Camera().right()); // Handle translations QVector3D translation; - if (Input::keyPressed(Qt::Key_W)) { + if(Input::keyPressed(Qt::Key_W)) { translation += Scene::Camera().forward(); } - if (Input::keyPressed(Qt::Key_S)) { + if(Input::keyPressed(Qt::Key_S)) { translation -= Scene::Camera().forward(); } - if (Input::keyPressed(Qt::Key_A)) { + if(Input::keyPressed(Qt::Key_A)) { translation -= Scene::Camera().right(); } - if (Input::keyPressed(Qt::Key_D)) { + if(Input::keyPressed(Qt::Key_D)) { translation += Scene::Camera().right(); } - if (Input::keyPressed(Qt::Key_Q)) { + if(Input::keyPressed(Qt::Key_Q)) { translation -= Scene::Camera().up() / 2.0f; } - if (Input::keyPressed(Qt::Key_E)) { + if(Input::keyPressed(Qt::Key_E)) { translation += Scene::Camera().up() / 2.0f; } Scene::Camera().transform().translate(transSpeed * translation); diff --git a/src/qtkwidget.h b/src/qtkwidget.h index cdd0cba..f3647d4 100644 --- a/src/qtkwidget.h +++ b/src/qtkwidget.h @@ -15,60 +15,61 @@ #include #include -#include #include +#include namespace Qtk { - class QTKAPI QtkWidget : public QOpenGLWidget, - protected QOpenGLFunctions { - Q_OBJECT; + class QTKAPI QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions { + Q_OBJECT; - public: - // Constructors - QtkWidget(); - explicit QtkWidget(QWidget *parent); - explicit QtkWidget(const QSurfaceFormat &format); - ~QtkWidget() override; + public: + // Constructors + QtkWidget(); + explicit QtkWidget(QWidget * parent); + explicit QtkWidget(const QSurfaceFormat & format); + ~QtkWidget() override; - private: - void teardownGL(); + private: + void teardownGL(); - public: - // Inherited virtual Members - void paintGL() override; - void initializeGL() override; - void resizeGL(int width, int height) override; + public: + // Inherited virtual Members + void paintGL() override; + void initializeGL() override; + void resizeGL(int width, int height) override; - inline Qtk::Scene * getScene() {return mScene;} - inline void setScene(Qtk::Scene * scene) { - if (mScene != Q_NULLPTR) delete mScene; - mScene = scene; - } + inline Qtk::Scene * getScene() { return mScene; } - protected slots: - void update(); + inline void setScene(Qtk::Scene * scene) { + delete mScene; + + mScene = scene; + } + + protected slots: + void update(); #ifdef QTK_DEBUG - void messageLogged(const QOpenGLDebugMessage &msg); + static void messageLogged(const QOpenGLDebugMessage & msg); #endif - // Protected Helpers - protected: - void keyPressEvent(QKeyEvent *event); - void keyReleaseEvent(QKeyEvent *event); - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); + // Protected Helpers + protected: + void keyPressEvent(QKeyEvent * event) override; + void keyReleaseEvent(QKeyEvent * event) override; + void mousePressEvent(QMouseEvent * event) override; + void mouseReleaseEvent(QMouseEvent * event) override; - private: - // Private helpers - void initializeWidget(); - void updateCameraInput(); + private: + // Private helpers + void initializeWidget(); + static void updateCameraInput(); - Qtk::Scene * mScene; + Qtk::Scene * mScene; #ifdef QTK_DEBUG - void printContextInformation(); - QOpenGLDebugLogger * mDebugLogger; + void printContextInformation(); + QOpenGLDebugLogger * mDebugLogger; #endif }; -} +} // namespace Qtk -#endif // QTK_QTKWIDGET_H +#endif // QTK_QTKWIDGET_H diff --git a/src/skybox.cpp b/src/skybox.cpp index c0febd7..7088c35 100644 --- a/src/skybox.cpp +++ b/src/skybox.cpp @@ -12,50 +12,50 @@ using namespace Qtk; -Skybox::Skybox(std::string right, std::string top, std::string front, - std::string left, std::string bottom, std::string back, - const std::string & name) - : mVBO(QOpenGLBuffer::VertexBuffer), - mVertices(Cube(QTK_DRAW_ELEMENTS).vertices()), - mIndices(Cube(QTK_DRAW_ELEMENTS).indices()) -{ +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()) { init(); - mCubeMap = Texture::initCubeMap( + 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())); + QImage(front.c_str()), QImage(left.c_str()), QImage(bottom.c_str()), + QImage(back.c_str())); } -Skybox::Skybox(std::string name) - : Skybox(":/right.png", ":/top.png", ":/front.png", - ":/left.png", ":/bottom.png", ":/back.png", - name) -{} - -Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) - : mCubeMap(cubeMap) { init();} +Skybox::Skybox(const std::string & name) : + Skybox( + ":/right.png", ":/top.png", ":/front.png", ":/left.png", ":/bottom.png", + ":/back.png", name) {} +Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) : + mTexture(cubeMap) { + init(); +} /******************************************************************************* * Public Member Functions ******************************************************************************/ -void Skybox::draw() -{ +void Skybox::draw() { glDepthFunc(GL_LEQUAL); glDepthMask(GL_FALSE); mVAO.bind(); mProgram.bind(); - mCubeMap->bind(); + mTexture.getOpenGLTexture().bind(); mProgram.setUniformValue("uProjectionMatrix", Scene::Projection()); mProgram.setUniformValue("uViewMatrix", Scene::Camera().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()); - mCubeMap->release(); + mTexture.getOpenGLTexture().bind(); mProgram.release(); mVAO.release(); @@ -64,13 +64,11 @@ void Skybox::draw() glActiveTexture(GL_TEXTURE0); } - /******************************************************************************* * Private Member Functions ******************************************************************************/ -void Skybox::init() -{ +void Skybox::init() { initializeOpenGLFunctions(); // Set up shader program diff --git a/src/skybox.h b/src/skybox.h index 654daef..edbace4 100644 --- a/src/skybox.h +++ b/src/skybox.h @@ -18,34 +18,37 @@ #include #include #include - +#include namespace Qtk { class QTKAPI Skybox : protected QOpenGLFunctions { - public: - // Delegate this constructor to use default skybox images - // + This allows creating a skybox with no arguments ( auto s = new Skybox; ) - explicit Skybox(std::string name="Skybox"); - explicit Skybox(QOpenGLTexture * cubeMap, const std::string & name="Skybox"); - // Constructor, Destructor - Skybox(std::string right, std::string top, std::string front, - std::string left, std::string bottom, std::string back, - const std::string & name="Skybox"); - ~Skybox() {} + public: + // Delegate this constructor to use default skybox images + explicit Skybox(const std::string & name = "Skybox"); + explicit Skybox( + QOpenGLTexture * cubeMap, const std::string & name = "Skybox"); + // Constructor, Destructor + 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"); - void draw(); + ~Skybox() = default; - private: - void init(); + void draw(); - Vertices mVertices; - Indices mIndices; + private: + void init(); - QOpenGLShaderProgram mProgram; - QOpenGLVertexArrayObject mVAO; - QOpenGLBuffer mVBO; - QOpenGLTexture * mCubeMap; + Vertices mVertices {}; + Indices mIndices {}; + + QOpenGLShaderProgram mProgram; + QOpenGLVertexArrayObject mVAO; + QOpenGLBuffer mVBO; + Texture mTexture; }; -} +} // namespace Qtk -#endif // QTK_SKYBOX_H +#endif // QTK_SKYBOX_H diff --git a/src/texture.cpp b/src/texture.cpp index 5b3795f..72e4b06 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -8,17 +8,18 @@ #include #include +#include #include using namespace Qtk; -QImage * Texture::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(512); auto loadedImage = new QImage(QImage(image).mirrored(flipX, flipY)); - if (loadedImage->isNull()) { + if(loadedImage->isNull()) { qDebug() << "Error loading image: " << image << "\n"; qDebug() << QImageReader::supportedImageFormats(); return Q_NULLPTR; @@ -27,46 +28,39 @@ QImage * Texture::initImage(const char * image, bool flipX, bool flipY) return loadedImage; } -QOpenGLTexture * Texture::initTexture2D(const char * texture, - bool flipX, bool flipY) -{ +QOpenGLTexture * OpenGLTextureFactory::initTexture2D( + 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); delete image; return newTexture; } -QOpenGLTexture * Texture::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 * Texture::initCubeMap( - const char * right, const char * top, - const char * front, const char * left, - const char * bottom, const char * back) -{ - return initCubeMap(QImage(right), QImage(top), - QImage(front), QImage(left), - QImage(bottom), QImage(back)); +QOpenGLTexture * OpenGLTextureFactory::initCubeMap( + const char * right, const char * top, const char * front, const char * left, + const char * bottom, const char * back) { + return initCubeMap( + QImage(right), QImage(top), QImage(front), QImage(left), QImage(bottom), + QImage(back)); } -QOpenGLTexture * Texture::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) { auto texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap); - std::vector faceTextures = { - right, top, front, - left, bottom, back - }; + std::vector faceTextures = {std::move(right), std::move(top), + std::move(front), std::move(left), + std::move(bottom), std::move(back)}; // Initialize skybox cubemap texture texture->create(); texture->bind(); @@ -74,27 +68,27 @@ QOpenGLTexture * Texture::initCubeMap( std::vector faces = { QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::CubeMapPositiveY, QOpenGLTexture::CubeMapPositiveZ, QOpenGLTexture::CubeMapNegativeX, - QOpenGLTexture::CubeMapNegativeY, QOpenGLTexture::CubeMapNegativeZ - }; + 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 = 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++; } diff --git a/src/texture.h b/src/texture.h index f2eb5aa..acf3464 100644 --- a/src/texture.h +++ b/src/texture.h @@ -10,36 +10,108 @@ #define QTOPENGL_TEXTURE_H #include +#include #include namespace Qtk { - class QTKAPI Texture { - public: - ~Texture() {} + class QTKAPI OpenGLTextureFactory { + public: + ~OpenGLTextureFactory() = default; - // QImage - static QImage * initImage(const char * image, - bool flipX=false, bool flipY=false); + // QImage + static QImage * initImage( + const char * image, bool flipX = false, bool flipY = false); - // 2D Texture - static QOpenGLTexture * initTexture2D(const char * texture, - bool flipX=false, bool flipY=false); + // 2D Texture + static QOpenGLTexture * initTexture2D( + const char * texture, bool flipX = false, bool flipY = false); - // Cube maps - static QOpenGLTexture * initCubeMap(QImage right, QImage top, - QImage front, QImage left, - QImage bottom, QImage back); - // Overloads for cube map initialization - static QOpenGLTexture * initCubeMap(const char * tile); - static QOpenGLTexture * initCubeMap(const char * right, const char * top, - const char * front, const char * left, - const char * bottom, const char * back); + // Cube maps + static QOpenGLTexture * initCubeMap( + const QImage & right, const QImage & top, const QImage & front, + const QImage & left, const QImage & bottom, const QImage & back); + // Overloads for cube map initialization + static QOpenGLTexture * initCubeMap(const char * tile); + static QOpenGLTexture * initCubeMap( + const char * right, const char * top, const char * front, + const char * left, const char * bottom, const char * back); - private: - // Private ctor to prevent creating instances of this class - Texture() {} + private: + // Private ctor to prevent creating instances of this class + OpenGLTextureFactory() = default; }; -} -#endif // QTOPENGL_TEXTURE_H + // TODO: Struct for (re)storing texture state + class Texture { + public: + Texture() = default; + Texture(const Texture & value) { + mOpenGLTexture = OpenGLTextureFactory::initTexture2D(value.mPath); + mPath = value.mPath; + } + explicit Texture( + const char * path, bool flipX = false, bool flipY = false) : + mOpenGLTexture( + OpenGLTextureFactory::initTexture2D(path, flipX, flipY)), + mPath(path) {} + explicit Texture(QOpenGLTexture * texture) : mOpenGLTexture(texture) {} + + ~Texture() { mOpenGLTexture->destroy(); } + + [[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const { + return *mOpenGLTexture; + } + + [[nodiscard]] inline std::string getPath() const { return mPath; } + + void setTexture( + const std::string & path, bool flipX = false, bool flipY = false) { + mOpenGLTexture = + OpenGLTextureFactory::initTexture2D(path.data(), flipX, flipY); + mPath = path.data(); + } + + void setTexture( + const char * path, bool flipX = false, bool flipY = false) { + mOpenGLTexture = + OpenGLTextureFactory::initTexture2D(path, flipX, flipY); + mPath = path; + } + + // TODO: This is unsafe because we don't have a path. Encapsulate it. + inline void setTexture(QOpenGLTexture * texture) { + mOpenGLTexture = texture; + } + + virtual inline void setCubeMap(const char * path) { + mOpenGLTexture = OpenGLTextureFactory::initCubeMap(path); + mPath = path; + } + + 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 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); + } + + [[nodiscard]] inline bool hasTexture() const { + return mOpenGLTexture != Q_NULLPTR; + } + + private: + QOpenGLTexture * mOpenGLTexture = Q_NULLPTR; + const char * mPath {}; + }; + +} // namespace Qtk + +#endif // QTOPENGL_TEXTURE_H diff --git a/src/transform3D.cpp b/src/transform3D.cpp index 9fd3db9..7d4d041 100644 --- a/src/transform3D.cpp +++ b/src/transform3D.cpp @@ -19,64 +19,53 @@ const QVector3D Transform3D::LocalRight(1.0f, 0.0f, 0.0f); * Transformations ******************************************************************************/ -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::rotate(const QQuaternion & dr) -{ +void Transform3D::rotate(const QQuaternion & dr) { m_dirty = true; mRotation = dr * mRotation; } -void Transform3D::grow(const QVector3D & ds) -{ +void Transform3D::grow(const QVector3D & ds) { m_dirty = true; mScale += ds; } - /******************************************************************************* * Setters ******************************************************************************/ -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; } - /******************************************************************************* * Accessors ******************************************************************************/ // Produces modelToWorld matrix using current set of transformations // Transformation * rotation * scale = modelToWorld -const QMatrix4x4 & Transform3D::toMatrix() -{ - if (m_dirty) - { +const QMatrix4x4 & Transform3D::toMatrix() { + if(m_dirty) { m_dirty = false; mWorld.setToIdentity(); mWorld.translate(mTranslation); @@ -86,66 +75,63 @@ const QMatrix4x4 & Transform3D::toMatrix() return mWorld; } - /******************************************************************************* * Queries ******************************************************************************/ -QVector3D Transform3D::forward() const -{ +QVector3D Transform3D::getForward() const { return mRotation.rotatedVector(LocalForward); } -QVector3D Transform3D::up() const -{ +QVector3D Transform3D::getUp() const { return mRotation.rotatedVector(LocalUp); } -QVector3D Transform3D::right() const -{ +QVector3D Transform3D::getRight() const { return mRotation.rotatedVector(LocalRight); + while(true) { + int xx; + }; } - /******************************************************************************* * QT Streams ******************************************************************************/ 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.translation().x() << ", " - << transform.translation().y() << ", " - << transform.translation().z() << ">\n"; - dbg << "Scale: <" << transform.scale().x() << ", " - << transform.scale().y() << ", " - << transform.scale().z() << ">\n"; - dbg << "Rotation: <" << transform.rotation().x() << ", " - << transform.rotation().y() << ", " - << transform.rotation().z() << " | " << - transform.rotation().scalar() << ">\n}"; + dbg << "Position: <" << transform.getTranslation().x() << ", " + << transform.getTranslation().y() << ", " + << transform.getTranslation().z() << ">\n"; + dbg << "Scale: <" << transform.getScale().x() << ", " + << transform.getScale().y() << ", " << transform.getScale().z() + << ">\n"; + dbg << "Rotation: <" << transform.getRotation().x() << ", " + << transform.getRotation().y() << ", " << transform.getRotation().z() + << " | " << transform.getRotation().scalar() << ">\n}"; return dbg; -} + } + #endif #ifndef QT_NO_DATASTREAM - QDataStream & operator<<(QDataStream & out, const Transform3D & transform) - { + 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; transform.m_dirty = true; return in; } + #endif -} +} // namespace Qtk diff --git a/src/transform3D.h b/src/transform3D.h index 14123b7..974aabd 100644 --- a/src/transform3D.h +++ b/src/transform3D.h @@ -15,111 +15,141 @@ #include #ifndef QT_NO_DEBUG_STREAM + #include + #endif #include namespace Qtk { - class QTKAPI Transform3D - { - public: - // Constructors - inline Transform3D() : m_dirty(true), - mScale(1.0f, 1.0f, 1.0f), - mTranslation(0.0f, 0.0f, 0.0f) { } + class QTKAPI Transform3D { + public: + // Constructors + inline Transform3D() : + m_dirty(true), mScale(1.0f, 1.0f, 1.0f), + mTranslation(0.0f, 0.0f, 0.0f) {} - // - // Transformations + // + // Transformations - void translate(const QVector3D & dt); - inline void translate(float dx, float dy, float dz) - { translate(QVector3D(dx, dy, dz));} + void translate(const QVector3D & dt); - // Scale object with multiplication - void scale(const QVector3D & ds); - inline void scale(float dx, float dy, float dz) - { scale(QVector3D(dx, dy, dz));} - inline void scale(float factor) - { scale(QVector3D(factor, factor, factor));} + inline void translate(float dx, float dy, float dz) { + translate(QVector3D(dx, dy, dz)); + } - // Multiplying by a rotation - void rotate(const QQuaternion & dr); - inline void rotate(float angle, const QVector3D & axis) - { rotate(QQuaternion::fromAxisAndAngle(axis, angle));} - inline void rotate(float angle, float ax, float ay, float az) - { rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));} + // Scale object with multiplication + void scale(const QVector3D & ds); - // Scale object by addition - void grow(const QVector3D & ds); - inline void grow(float dx, float dy, float dz) - { grow(QVector3D(dx, dy, dz));} - inline void grow(float factor) - { grow(QVector3D(factor, factor, factor));} + inline void scale(float dx, float dy, float dz) { + scale(QVector3D(dx, dy, dz)); + } - // - // Setters + inline void scale(float factor) { + scale(QVector3D(factor, factor, factor)); + } - // Set object position - void setTranslation(const QVector3D & t); - inline void setTranslation(float x, float y, float z) - { setTranslation(QVector3D(x, y, z));} + // Multiplying by a rotation + void rotate(const QQuaternion & dr); - // Set object scale - void setScale(const QVector3D & s); - inline void setScale(float x, float y, float z) - { setScale(QVector3D(x, y, z));} - inline void setScale(float k) - { setScale(QVector3D(k, k, k));} + inline void rotate(float angle, const QVector3D & axis) { + rotate(QQuaternion::fromAxisAndAngle(axis, angle)); + } - // Set object rotation - void setRotation(const QQuaternion & r); - inline void setRotation(float angle, const QVector3D & axis) - { setRotation(QQuaternion::fromAxisAndAngle(axis, angle));} - inline void setRotation(float angle, float ax, float ay, float az) - { setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));} + inline void rotate(float angle, float ax, float ay, float az) { + rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle)); + } - // - // Accessors + // Scale object by addition + void grow(const QVector3D & ds); - inline const QVector3D & translation() const { return mTranslation;} - inline const QVector3D & scale() const { return mScale; } - inline const QQuaternion & rotation() const { return mRotation; } - const QMatrix4x4 & toMatrix(); + inline void grow(float dx, float dy, float dz) { + grow(QVector3D(dx, dy, dz)); + } - QVector3D forward() const; - QVector3D up() const; - QVector3D right() const; + inline void grow(float factor) { + grow(QVector3D(factor, factor, factor)); + } - static const QVector3D LocalForward, LocalUp, LocalRight; + // + // Setters - private: - QVector3D mTranslation; - QQuaternion mRotation; - QVector3D mScale; - QMatrix4x4 mWorld; + // Set object position + void setTranslation(const QVector3D & t); - bool m_dirty; + inline void setTranslation(float x, float y, float z) { + setTranslation(QVector3D(x, y, z)); + } + // Set object scale + void setScale(const QVector3D & s); + + inline void setScale(float x, float y, float z) { + setScale(QVector3D(x, y, z)); + } + + inline void setScale(float k) { setScale(QVector3D(k, k, k)); } + + // Set object rotation + void setRotation(const QQuaternion & r); + + inline void setRotation(float angle, const QVector3D & axis) { + setRotation(QQuaternion::fromAxisAndAngle(axis, angle)); + } + + inline void setRotation(float angle, float ax, float ay, float az) { + setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle)); + } + + // + // Accessors + + [[nodiscard]] inline const QVector3D & getTranslation() const { + return mTranslation; + } + + [[nodiscard]] inline const QVector3D & getScale() const { return mScale; } + + [[nodiscard]] inline const QQuaternion & getRotation() const { + return mRotation; + } + + const QMatrix4x4 & toMatrix(); + + [[nodiscard]] QVector3D getForward() const; + [[nodiscard]] QVector3D getUp() const; + [[nodiscard]] QVector3D getRight() const; + + static const QVector3D LocalForward, LocalUp, LocalRight; + + private: + QVector3D mTranslation; + QQuaternion mRotation; + QVector3D mScale; + QMatrix4x4 mWorld; + + 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 }; - // Qt Streams #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const Transform3D & transform); #endif #ifndef QT_NO_DATASTREAM - QDataStream &operator<<(QDataStream & out, const Transform3D & transform); - QDataStream &operator>>(QDataStream & in, Transform3D & transform); + QDataStream & operator<<(QDataStream & out, const Transform3D & transform); + QDataStream & operator>>(QDataStream & in, Transform3D & transform); #endif -} +} // namespace Qtk Q_DECLARE_TYPEINFO(Qtk::Transform3D, Q_MOVABLE_TYPE); -#endif // QTK_TRANSFORM3D_H +#endif // QTK_TRANSFORM3D_H