Refactor texture handling

This commit is contained in:
Shaun Reed 2022-11-24 22:26:53 +00:00
parent 0f372f71f4
commit 443c09da7c
36 changed files with 2187 additions and 1754 deletions

76
.clang-format Normal file
View File

@ -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
...

146
.clang-tidy Normal file
View File

@ -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'

View File

@ -1,4 +1,4 @@
name: Build Test name: All Builds
on: on:
push: push:
@ -32,7 +32,7 @@ jobs:
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
uses: crazy-max/ghaction-chocolatey@v2.0.0 uses: crazy-max/ghaction-chocolatey@v2.0.0
with: with:
args: install pkgconfiglite args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1
- name: Build Qtk - name: Build Qtk
shell: bash shell: bash

67
.github/workflows/linting.yml vendored Normal file
View File

@ -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 }}

View File

@ -18,6 +18,7 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_compile_options(/wd4131 /wd4127) add_compile_options(/wd4131 /wd4127)

View File

@ -1,4 +1,6 @@
# Qtk # 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. Practice project for learning about using OpenGL in Qt widget applications.
Model loader using [Assimp](https://assimp.org/) within a Qt widget application. 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. 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. 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 - Once Qt6 is installed, to build and run `qtk` on Ubuntu -
```bash ```bash
sudo apt update -y && sudo apt install freeglut3-dev libassimp-dev cmake build-essential git sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git
git clone https://gitlab.com/shaunrd0/qtk 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 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 ./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 ./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. If you are building on **Windows / Mac** and bringing your own installation of Assimp, consider setting the `-DASSIMP_NEW_INTERFACE` build flag.
```bash ```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 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 ### Controls
You can fly around the scene if you hold the right mouse button and use WASD. You can fly around the scene if you hold the right mouse button and use WASD.

View File

@ -8,10 +8,10 @@
#include <abstractscene.h> #include <abstractscene.h>
#include <camera3d.h> #include <camera3d.h>
#include <examplescene.h>
#include <meshrenderer.h> #include <meshrenderer.h>
#include <model.h> #include <model.h>
#include <resourcemanager.h> #include <resourcemanager.h>
#include <examplescene.h>
#include <texture.h> #include <texture.h>
using namespace Qtk; using namespace Qtk;
@ -20,138 +20,87 @@ using namespace Qtk;
* Constructors, Destructors * Constructors, Destructors
******************************************************************************/ ******************************************************************************/
ExampleScene::ExampleScene() ExampleScene::ExampleScene() {
{
Camera().transform().setTranslation(0.0f, 0.0f, 20.0f); Camera().transform().setTranslation(0.0f, 0.0f, 20.0f);
Camera().transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); Camera().transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
} }
ExampleScene::~ExampleScene() ExampleScene::~ExampleScene() {
{
delete mTestPhong; delete mTestPhong;
delete mTestSpecular; delete mTestSpecular;
delete mTestDiffuse; delete mTestDiffuse;
delete mTestAmbient; delete mTestAmbient;
for (auto & mesh : mMeshes) delete mesh; for(auto & mesh : mMeshes) {
for (auto & model : mModels) delete model; delete mesh;
}
for(auto & model : mModels) {
delete model;
}
delete mSkybox; delete mSkybox;
} }
/******************************************************************************* /*******************************************************************************
* Public Member Functions * Public Member Functions
******************************************************************************/ ******************************************************************************/
void ExampleScene::init() void ExampleScene::init() {
{ auto * sb = new Qtk::Skybox("Skybox");
Qtk::Skybox * sb = new Qtk::Skybox("Skybox");
setSkybox(sb); setSkybox(sb);
// Initialize Phong example cube // Initialize Phong example cube
mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube()); mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube());
mTestPhong->mTransform.setTranslation(3.0f, 0.0f, -2.0f); mTestPhong->mTransform.setTranslation(3.0f, 0.0f, -2.0f);
mTestPhong->setShaders(":/solid-phong.vert", ":/solid-phong.frag"); 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("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestPhong->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestPhong->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestPhong->setUniform("uAmbientStrength", 0.2f); mTestPhong->setUniform("uAmbientStrength", 0.2f);
mTestPhong->setUniform("uSpecularStrength", 0.50f); mTestPhong->setUniform("uSpecularStrength", 0.50f);
mTestPhong->setUniform("uSpecularShine", 256); mTestPhong->setUniform("uSpecularShine", 256);
mTestPhong->reallocateNormals(mTestPhong->getNormals());
mTestPhong->mVAO.bind(); // mTestPhong->releaseShaders();
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();
// Initialize Ambient example cube // Initialize Ambient example cube
mTestAmbient = new Qtk::MeshRenderer("ambient", Cube()); mTestAmbient = new Qtk::MeshRenderer("ambient", Cube());
mTestAmbient->mTransform.setTranslation(7.0f, 0.0f, -2.0f); mTestAmbient->mTransform.setTranslation(7.0f, 0.0f, -2.0f);
mTestAmbient->setShaders(":/solid-ambient.vert", ":/solid-ambient.frag"); mTestAmbient->setShaders(":/solid-ambient.vert", ":/solid-ambient.frag");
mTestAmbient->init(); mTestAmbient->init();
mTestAmbient->mProgram.bind();
mTestAmbient->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestAmbient->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestAmbient->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestAmbient->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestAmbient->setUniform("uAmbientStrength", 0.2f); mTestAmbient->setUniform("uAmbientStrength", 0.2f);
mTestAmbient->reallocateNormals(mTestAmbient->getNormals());
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();
// Initialize Diffuse example cube // Initialize Diffuse example cube
mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube()); mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube());
mTestDiffuse->mTransform.setTranslation(9.0f, 0.0f, -2.0f); mTestDiffuse->mTransform.setTranslation(9.0f, 0.0f, -2.0f);
mTestDiffuse->setShaders(":/solid-diffuse.vert", ":/solid-diffuse.frag"); mTestDiffuse->setShaders(":/solid-diffuse.vert", ":/solid-diffuse.frag");
mTestDiffuse->init(); mTestDiffuse->init();
mTestDiffuse->mProgram.bind();
mTestDiffuse->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestDiffuse->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestDiffuse->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestDiffuse->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestDiffuse->setUniform("uAmbientStrength", 0.2f); mTestDiffuse->setUniform("uAmbientStrength", 0.2f);
mTestDiffuse->reallocateNormals(mTestDiffuse->getNormals());
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();
// Initialize Specular example cube // Initialize Specular example cube
mTestSpecular = new Qtk::MeshRenderer("specular", Cube()); mTestSpecular = new Qtk::MeshRenderer("specular", Cube());
mTestSpecular->mTransform.setTranslation(11.0f, 0.0f, -2.0f); mTestSpecular->mTransform.setTranslation(11.0f, 0.0f, -2.0f);
mTestSpecular->setShaders(":/solid-specular.vert", ":/solid-specular.frag"); mTestSpecular->setShaders(":/solid-specular.vert", ":/solid-specular.frag");
mTestSpecular->init(); mTestSpecular->init();
mTestSpecular->mProgram.bind();
mTestSpecular->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestSpecular->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestSpecular->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestSpecular->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestSpecular->setUniform("uAmbientStrength", 0.2f); mTestSpecular->setUniform("uAmbientStrength", 0.2f);
mTestSpecular->setUniform("uSpecularStrength", 0.50f); mTestSpecular->setUniform("uSpecularStrength", 0.50f);
mTestSpecular->setUniform("uSpecularShine", 256); 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 // 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 // Sometimes model textures need flipped in certain directions
mModels.back()->flipTexture("diffuse.jpg", false, true); mModels.back()->flipTexture("diffuse.jpg", false, true);
mModels.back()->mTransform.setTranslation(0.0f, 0.0f, -10.0f); 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.setTranslation(-3.0f, -1.0f, -10.0f);
mModels.back()->mTransform.scale(0.15f); 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.setTranslation(2.0f, -1.0f, -5.0f);
mModels.back()->mTransform.scale(0.15f); 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, 1.0f, 0.0f, 0.0f);
mModels.back()->mTransform.rotate(90.0f, 0.0f, 1.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); mModels.back()->mTransform.setTranslation(-1.5f, 0.5f, -2.0f);
// //
// Building example mesh objects // Building example mesh objects
// Render an alien with specular // Render an alien with specular
// Test alien Model with phong lighting and specular mapping // Test alien Model with phong lighting and specular mapping
mMeshes.push_back( mMeshes.push_back(new Qtk::MeshRenderer(
new Qtk::MeshRenderer("alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS))); "alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(4.0f, 1.5f, 10.0f); mMeshes.back()->mTransform.setTranslation(4.0f, 1.5f, 10.0f);
mMeshes.back()->mTransform.scale(0.25f); mMeshes.back()->mTransform.scale(0.25f);
// This function changes values we have allocated in a buffer, so init() after // This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN); mMeshes.back()->setColor(GREEN);
mMeshes.back()->init();
mModels.push_back( mModels.push_back(new Qtk::Model(
new Qtk::Model("alienTest", ":/models/alien-hominid/alien.obj", "alienTest", ":/models/alien-hominid/alien.obj", ":/model-specular.vert",
":/model-specular.vert", ":/model-specular.frag") ":/model-specular.frag"));
);
mModels.back()->mTransform.setTranslation(3.0f, -1.0f, 10.0f); mModels.back()->mTransform.setTranslation(3.0f, -1.0f, 10.0f);
mModels.back()->mTransform.scale(0.15f); mModels.back()->mTransform.scale(0.15f);
mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f)); 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.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.specular", 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 // Test spartan Model with phong lighting, specular and normal mapping
mMeshes.push_back( 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.setTranslation(1.0f, 1.5f, 10.0f);
mMeshes.back()->mTransform.scale(0.25f); mMeshes.back()->mTransform.scale(0.25f);
// This function changes values we have allocated in a buffer, so init() after // This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN); mMeshes.back()->setColor(GREEN);
mMeshes.back()->init();
mModels.push_back( mModels.push_back(new Qtk::Model(
new Qtk::Model("spartanTest", ":/models/spartan/spartan.obj", "spartanTest", ":/models/spartan/spartan.obj", ":/model-normals.vert",
":/model-normals.vert", ":/model-normals.frag") ":/model-normals.frag"));
);
mModels.back()->mTransform.setTranslation(0.0f, -1.0f, 10.0f); mModels.back()->mTransform.setTranslation(0.0f, -1.0f, 10.0f);
mModels.back()->mTransform.scale(2.0f); mModels.back()->mTransform.scale(2.0f);
mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.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.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.specular", 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 // Test basic cube with phong.vert and phong.frag shaders
mMeshes.push_back( mMeshes.push_back(
new Qtk::MeshRenderer("testLight", Triangle(QTK_DRAW_ELEMENTS))); new Qtk::MeshRenderer("testLight", Triangle(QTK_DRAW_ELEMENTS)));
@ -247,33 +190,15 @@ void ExampleScene::init()
mMeshes.back()->setDrawType(GL_LINE_LOOP); mMeshes.back()->setDrawType(GL_LINE_LOOP);
// This function changes values we have allocated in a buffer, so init() after // This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN); mMeshes.back()->setColor(GREEN);
mMeshes.back()->init();
mMeshes.push_back( mMeshes.push_back(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS)));
new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 10.0f); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 10.0f);
mMeshes.back()->setShaders(":/phong.vert", ":/phong.frag"); mMeshes.back()->setShaders(":/phong.vert", ":/phong.frag");
mMeshes.back()->setColor(QVector3D(0.0f, 0.25f, 0.0f)); mMeshes.back()->setColor(QVector3D(0.0f, 0.25f, 0.0f));
mMeshes.back()->init(); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
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()->setUniform("uMaterial.ambient", QVector3D(0.0f, 0.3f, 0.0f)); 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.diffuse", QVector3D(0.0f, 0.2f, 0.0f));
mMeshes.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f)); mMeshes.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
mMeshes.back()->setUniform("uMaterial.ambientStrength", 1.0f); mMeshes.back()->setUniform("uMaterial.ambientStrength", 1.0f);
mMeshes.back()->setUniform("uMaterial.diffuseStrength", 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("uLight.specular", QVector3D(0.62f, 0.55f, 0.37f));
mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); 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 // Create simple shapes using MeshRenderer class and data in mesh.h
@ -314,7 +238,6 @@ void ExampleScene::init()
mMeshes.back()->setDrawType(GL_LINE_LOOP); mMeshes.back()->setDrawType(GL_LINE_LOOP);
// This function changes values we have allocated in a buffer, so init() after // This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN); mMeshes.back()->setColor(GREEN);
mMeshes.back()->init();
// //
// Testing for normals, texture coordinates // Testing for normals, texture coordinates
@ -324,99 +247,50 @@ void ExampleScene::init()
new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 4.0f); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 4.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->init(); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
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();
// RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS // RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS
mMeshes.push_back( mMeshes.push_back(new Qtk::MeshRenderer(
new Qtk::MeshRenderer("rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); "rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 2.0f); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 2.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->init(); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mMeshes.back()->mVAO.bind();
mMeshes.back()->mNBO.create();
mMeshes.back()->mNBO.bind();
mMeshes.back()->mProgram.bind();
mMeshes.back()->mNBO.allocate(mMeshes.back()->normals().data(), Texture crateTexture;
mMeshes.back()->normals().size() crateTexture.setTexture(":/crate.png");
* sizeof(mMeshes.back()->normals()[0])); Cube cube;
mMeshes.back()->mProgram.enableAttributeArray(1); auto * m = new MeshRenderer("Test Crate", Cube(QTK_DRAW_ARRAYS));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, m->mTransform.setTranslation(0, 0, 13);
3, sizeof(QVector3D)); m->setShaders(":/texture2d.vert", ":/texture2d.frag");
m->setTexture(crateTexture);
mMeshes.back()->mProgram.release(); m->setUniform("uTexture", 0);
mMeshes.back()->mNBO.release(); m->reallocateTexCoords(cube.getTexCoords());
mMeshes.back()->mVAO.release(); mMeshes.push_back(m);
// Texturing a cube using texture coordinates and glDrawArrays // 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 // + UVs required duplicating element position data from QTK_DRAW_ELEMENTS
// + This is because the same position must use different UV coordinates // + This is because the same position must use different UV coordinates
mMeshes.push_back( mMeshes.push_back(
new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(-3.0f, 0.0f, -2.0f); mMeshes.back()->mTransform.setTranslation(-3.0f, 0.0f, -2.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->init(); mMeshes.back()->setTexture(crateTexture);
mMeshes.back()->mProgram.bind();
mMeshes.back()->setTexture(Texture::initTexture2D(":/crate.png"));
mMeshes.back()->setUniform("uTexture", 0); mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->texture().bind(); mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords());
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();
// Test drawing a cube with texture coordinates using glDrawElements // Test drawing a cube with texture coordinates using glDrawElements
mMeshes.push_back( mMeshes.push_back(new Qtk::MeshRenderer(
new Qtk::MeshRenderer("uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); "uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->mTransform.setTranslation(-1.7f, 0.0f, -2.0f); mMeshes.back()->mTransform.setTranslation(-1.7f, 0.0f, -2.0f);
mMeshes.back()->setTexture(":/crate.png");
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->init(); mMeshes.back()->bindShaders();
mMeshes.back()->mVAO.bind(); mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->mNBO.create(); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mMeshes.back()->mNBO.bind(); mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords(), 3);
mMeshes.back()->mProgram.bind(); mMeshes.back()->releaseShaders();
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()->mTransform.rotate(45.0f, 0.0f, 1.0f, 0.0f); mMeshes.back()->mTransform.rotate(45.0f, 0.0f, 1.0f, 0.0f);
// Texturing a cube using a cube map // Texturing a cube using a cube map
@ -425,26 +299,11 @@ void ExampleScene::init()
new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS))); new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(-3.0f, 1.0f, -2.0f); mMeshes.back()->mTransform.setTranslation(-3.0f, 1.0f, -2.0f);
mMeshes.back()->mTransform.setRotation(45.0f, 0.0f, 1.0f, 0.0f); mMeshes.back()->mTransform.setRotation(45.0f, 0.0f, 1.0f, 0.0f);
mMeshes.back()->setShaders(":/texture-cubemap.vert", ":/texture-cubemap.frag"); mMeshes.back()->setShaders(
mMeshes.back()->init(); ":/texture-cubemap.vert", ":/texture-cubemap.frag");
mMeshes.back()->mProgram.bind(); mMeshes.back()->setCubeMap(":/crate.png");
mMeshes.back()->setTexture(Texture::initCubeMap(":/crate.png"));
mMeshes.back()->setUniform("uTexture", 0); mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords());
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();
// Create a cube with custom shaders // Create a cube with custom shaders
// + Apply RGB normals shader and spin the cube for a neat effect // + 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))); new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 2.0f, -2.0f); mMeshes.back()->mTransform.setTranslation(5.0f, 2.0f, -2.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->init(); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
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();
// RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS // RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS
mMeshes.push_back( mMeshes.push_back(new Qtk::MeshRenderer(
new Qtk::MeshRenderer("rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS))); "rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 2.0f); mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 2.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->init(); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
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();
// RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS // RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS
mMeshes.push_back( mMeshes.push_back(new Qtk::MeshRenderer(
new Qtk::MeshRenderer("rgbTriangleElementsTest", "rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 4.0f); mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 4.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->init(); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
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();
// Test drawing triangle with glDrawArrays with texture coordinates // Test drawing triangle with glDrawArrays with texture coordinates
mMeshes.push_back( mMeshes.push_back(
new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS))); new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(-3.0f, 2.0f, -2.0f); mMeshes.back()->mTransform.setTranslation(-3.0f, 2.0f, -2.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); 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()->setUniform("uTexture", 0);
mMeshes.back()->texture().bind(); mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords());
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();
// Test drawing triangle with glDrawElements with texture coordinates // Test drawing triangle with glDrawElements with texture coordinates
mMeshes.push_back( mMeshes.push_back(new Qtk::MeshRenderer(
new Qtk::MeshRenderer("testTriangleElementsUV", "testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->mTransform.setTranslation(-2.5f, 0.0f, -1.0f); mMeshes.back()->mTransform.setTranslation(-2.5f, 0.0f, -1.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->init(); mMeshes.back()->setTexture(":/crate.png");
mMeshes.back()->mProgram.bind();
mMeshes.back()->setTexture(Texture::initTexture2D(":/crate.png"));
mMeshes.back()->setUniform("uTexture", 0); mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->texture().bind(); mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords());
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();
// //
// Lighting cube examples // Lighting cube examples
// Example of a cube with no lighting applied // Example of a cube with no lighting applied
mMeshes.push_back( mMeshes.push_back(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS)));
new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, -2.0f); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, -2.0f);
mMeshes.back()->setShaders(":/solid-perspective.vert", mMeshes.back()->setShaders(
":/solid-perspective.frag"); ":/solid-perspective.vert", ":/solid-perspective.frag");
mMeshes.back()->init();
mMeshes.back()->mProgram.bind();
mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mMeshes.back()->mProgram.release();
// Create objects that represent light sources for lighting examples // Create objects that represent light sources for lighting examples
mMeshes.push_back( mMeshes.push_back(
@ -600,103 +373,101 @@ void ExampleScene::init()
mMeshes.back()->mTransform.scale(0.25f); mMeshes.back()->mTransform.scale(0.25f);
} }
void ExampleScene::draw() void ExampleScene::draw() {
{
Scene::draw(); Scene::draw();
mTestPhong->mProgram.bind(); for(const auto & model : mModels) {
mTestPhong->setUniform("uModelInverseTransposed", model->draw();
}
for(const auto & mesh : mMeshes) {
mesh->draw();
}
mTestPhong->bindShaders();
mTestPhong->setUniform(
"uModelInverseTransposed",
mTestPhong->mTransform.toMatrix().normalMatrix()); mTestPhong->mTransform.toMatrix().normalMatrix());
mTestPhong->setUniform( mTestPhong->setUniform(
"uLightPosition", "uLightPosition",
MeshRenderer::getInstance("phongLight")->mTransform.translation()); MeshRenderer::getInstance("phongLight")->mTransform.getTranslation());
mTestPhong->setUniform("uCameraPosition", mTestPhong->setUniform(
ExampleScene::Camera().transform().translation()); "uCameraPosition", ExampleScene::Camera().transform().getTranslation());
mTestPhong->mProgram.release(); mTestPhong->releaseShaders();
mTestPhong->draw(); mTestPhong->draw();
mTestAmbient->mProgram.bind(); mTestAmbient->bindShaders();
mTestAmbient->setUniform("uCameraPosition", mTestAmbient->setUniform(
ExampleScene::Camera().transform().translation()); "uCameraPosition", ExampleScene::Camera().transform().getTranslation());
mTestAmbient->mProgram.release(); mTestAmbient->releaseShaders();
mTestAmbient->draw(); mTestAmbient->draw();
mTestDiffuse->mProgram.bind(); mTestDiffuse->bindShaders();
mTestDiffuse->setUniform("uModelInverseTransposed", mTestDiffuse->setUniform(
"uModelInverseTransposed",
mTestDiffuse->mTransform.toMatrix().normalMatrix()); mTestDiffuse->mTransform.toMatrix().normalMatrix());
mTestDiffuse->setUniform( mTestDiffuse->setUniform(
"uLightPosition", "uLightPosition",
MeshRenderer::getInstance("diffuseLight")->mTransform.translation()); MeshRenderer::getInstance("diffuseLight")->mTransform.getTranslation());
mTestDiffuse->setUniform("uCameraPosition", ExampleScene::Camera().transform().translation()); mTestDiffuse->setUniform(
mTestDiffuse->mProgram.release(); "uCameraPosition", ExampleScene::Camera().transform().getTranslation());
mTestDiffuse->releaseShaders();
mTestDiffuse->draw(); mTestDiffuse->draw();
mTestSpecular->mProgram.bind(); mTestSpecular->bindShaders();
mTestSpecular->setUniform( mTestSpecular->setUniform(
"uModelInverseTransposed", "uModelInverseTransposed",
mTestSpecular->mTransform.toMatrix().normalMatrix()); mTestSpecular->mTransform.toMatrix().normalMatrix());
mTestSpecular->setUniform( mTestSpecular->setUniform(
"uLightPosition", "uLightPosition",
MeshRenderer::getInstance("specularLight")->mTransform.translation()); MeshRenderer::getInstance("specularLight")->mTransform.getTranslation());
mTestSpecular->setUniform("uCameraPosition", ExampleScene::Camera().transform().translation()); mTestSpecular->setUniform(
mTestSpecular->mProgram.release(); "uCameraPosition", ExampleScene::Camera().transform().getTranslation());
mTestSpecular->releaseShaders();
mTestSpecular->draw(); mTestSpecular->draw();
} }
void ExampleScene::update() void ExampleScene::update() {
{ auto position =
auto position = MeshRenderer::getInstance("alienTestLight")->mTransform.translation(); MeshRenderer::getInstance("alienTestLight")->mTransform.getTranslation();
Model::getInstance("alienTest")->setUniform( auto alien = Model::getInstance("alienTest");
"uLight.position", position); alien->setUniform("uLight.position", position);
Model::getInstance("alienTest")->setUniform( alien->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().translation()); "uCameraPosition", ExampleScene::Camera().transform().getTranslation());
auto posMatrix = Model::getInstance("alienTest")->mTransform.toMatrix(); auto posMatrix = alien->mTransform.toMatrix();
Model::getInstance("alienTest")->setUniform( alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
"uMVP.normalMatrix", posMatrix.normalMatrix()); alien->setUniform("uMVP.model", posMatrix);
Model::getInstance("alienTest")->setUniform( alien->setUniform("uMVP.view", ExampleScene::Camera().toMatrix());
"uMVP.model", posMatrix); alien->setUniform("uMVP.projection", ExampleScene::Projection());
Model::getInstance("alienTest")->setUniform( alien->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f);
"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);
position = MeshRenderer::getInstance("spartanTestLight")->mTransform.translation(); position = MeshRenderer::getInstance("spartanTestLight")
Model::getInstance("spartanTest")->setUniform( ->mTransform.getTranslation();
"uLight.position", position); auto spartan = Model::getInstance("spartanTest");
Model::getInstance("spartanTest")->setUniform( spartan->setUniform("uLight.position", position);
"uCameraPosition", ExampleScene::Camera().transform().translation()); spartan->setUniform(
posMatrix = Model::getInstance("spartanTest")->mTransform.toMatrix(); "uCameraPosition", ExampleScene::Camera().transform().getTranslation());
Model::getInstance("spartanTest")->setUniform( posMatrix = spartan->mTransform.toMatrix();
"uMVP.normalMatrix", posMatrix.normalMatrix()); spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
Model::getInstance("spartanTest")->setUniform( spartan->setUniform("uMVP.model", posMatrix);
"uMVP.model", posMatrix); spartan->setUniform("uMVP.view", ExampleScene::Camera().toMatrix());
Model::getInstance("spartanTest")->setUniform( spartan->setUniform("uMVP.projection", ExampleScene::Projection());
"uMVP.view", ExampleScene::Camera().toMatrix()); spartan->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f);
Model::getInstance("spartanTest")->setUniform(
"uMVP.projection", ExampleScene::Projection());
Model::getInstance("spartanTest")->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto phong = MeshRenderer::getInstance("testPhong");
phong->mTransform.rotate(0.75f, 1.0f, 0.5f, 0.0f);
MeshRenderer::getInstance("testPhong")->mTransform.rotate( phong->bindShaders();
0.75f, 1.0f, 0.5f, 0.0f); position =
MeshRenderer::getInstance("testPhong")->mProgram.bind(); MeshRenderer::getInstance("testLight")->mTransform.getTranslation();
position = MeshRenderer::getInstance("testLight")->mTransform.translation(); phong->setUniform("uLight.position", position);
MeshRenderer::getInstance("testPhong")->setUniform( phong->setUniform(
"uLight.position", position); "uCameraPosition", ExampleScene::Camera().transform().getTranslation());
MeshRenderer::getInstance("testPhong")->setUniform( posMatrix = phong->mTransform.toMatrix();
"uCameraPosition", ExampleScene::Camera().transform().translation()); phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
posMatrix = MeshRenderer::getInstance("testPhong")->mTransform.toMatrix(); phong->setUniform("uMVP.model", posMatrix);
MeshRenderer::getInstance("testPhong")->setUniform( phong->setUniform("uMVP.view", ExampleScene::Camera().toMatrix());
"uMVP.normalMatrix", posMatrix.normalMatrix()); phong->setUniform("uMVP.projection", ExampleScene::Projection());
MeshRenderer::getInstance("testPhong")->setUniform( phong->releaseShaders();
"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();
// Rotate lighting example cubes // Rotate lighting example cubes
mTestPhong->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); mTestPhong->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f);
@ -709,36 +480,36 @@ void ExampleScene::update()
// Examples of various translations and rotations // Examples of various translations and rotations
// Rotate in multiple directions simultaneously // Rotate in multiple directions simultaneously
MeshRenderer::getInstance("rgbNormalsCube")->mTransform.rotate( MeshRenderer::getInstance("rgbNormalsCube")
0.75f, 0.2f, 0.4f, 0.6f); ->mTransform.rotate(0.75f, 0.2f, 0.4f, 0.6f);
// Pitch forward and roll sideways // Pitch forward and roll sideways
MeshRenderer::getInstance("leftTriangle")->mTransform.rotate( MeshRenderer::getInstance("leftTriangle")
0.75f, 1.0f, 0.0f, 0.0f); ->mTransform.rotate(0.75f, 1.0f, 0.0f, 0.0f);
MeshRenderer::getInstance("rightTriangle")->mTransform.rotate( MeshRenderer::getInstance("rightTriangle")
0.75f, 0.0f, 0.0f, 1.0f); ->mTransform.rotate(0.75f, 0.0f, 0.0f, 1.0f);
// Move between two positions over time // Move between two positions over time
static float translateX = 0.025f; 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 = float posX =
MeshRenderer::getInstance("topTriangle")->mTransform.translation().x(); MeshRenderer::getInstance("topTriangle")->mTransform.getTranslation().x();
if(posX < limit || posX > limit + 4.0f) { if(posX < limit || posX > limit + 4.0f) {
translateX = -translateX; translateX = -translateX;
} }
MeshRenderer::getInstance("topTriangle")->mTransform.translate( MeshRenderer::getInstance("topTriangle")
translateX, 0.0f, 0.0f); ->mTransform.translate(translateX, 0.0f, 0.0f);
MeshRenderer::getInstance("bottomTriangle")->mTransform.translate( MeshRenderer::getInstance("bottomTriangle")
-translateX, 0.0f, 0.0f); ->mTransform.translate(-translateX, 0.0f, 0.0f);
// And lets rotate the triangles in two directions at once // And lets rotate the triangles in two directions at once
MeshRenderer::getInstance("topTriangle")->mTransform.rotate( MeshRenderer::getInstance("topTriangle")
0.75f, 0.2f, 0.0f, 0.4f); ->mTransform.rotate(0.75f, 0.2f, 0.0f, 0.4f);
MeshRenderer::getInstance("bottomTriangle")->mTransform.rotate( MeshRenderer::getInstance("bottomTriangle")
0.75f, 0.0f, 0.2f, 0.4f); ->mTransform.rotate(0.75f, 0.0f, 0.2f, 0.4f);
// And make the bottom triangle green, instead of RGB // And make the bottom triangle green, instead of RGB
// Rotate center cube in several directions simultaneously // Rotate center cube in several directions simultaneously
// + Not subject to gimbal lock since we are using quaternions :) // + Not subject to gimbal lock since we are using quaternions :)
MeshRenderer::getInstance("centerCube")->mTransform.rotate( MeshRenderer::getInstance("centerCube")
0.75f, 0.2f, 0.4f, 0.6f); ->mTransform.rotate(0.75f, 0.2f, 0.4f, 0.6f);
} }

View File

@ -15,22 +15,20 @@
#include <QMatrix4x4> #include <QMatrix4x4>
class ExampleScene : public Qtk::Scene { class ExampleScene : public Qtk::Scene {
public: public:
ExampleScene(); ExampleScene();
~ExampleScene(); ~ExampleScene();
virtual void init(); void init() override;
virtual void draw() override; void draw() override;
virtual void update(); void update() override;
private: private:
Qtk::MeshRenderer * mTestPhong {};
Qtk::MeshRenderer * mTestPhong; Qtk::MeshRenderer * mTestSpecular {};
Qtk::MeshRenderer * mTestSpecular; Qtk::MeshRenderer * mTestDiffuse {};
Qtk::MeshRenderer * mTestDiffuse; Qtk::MeshRenderer * mTestAmbient {};
Qtk::MeshRenderer * mTestAmbient;
}; };
#endif // QTK_EXAMPLE_SCENE_H #endif // QTK_EXAMPLE_SCENE_H

View File

@ -9,12 +9,11 @@
#include <QApplication> #include <QApplication>
#include <QLabel> #include <QLabel>
#include <qtkwidget.h>
#include <mainwindow.h> #include <mainwindow.h>
#include <qtkwidget.h>
#include <QSurfaceFormat>
int main(int argc, char * argv[]) {
int main(int argc, char *argv[])
{
QApplication a(argc, argv); QApplication a(argc, argv);
// Set OpenGL Version information // Set OpenGL Version information
@ -35,6 +34,5 @@ int main(int argc, char *argv[])
MainWindow w; MainWindow w;
w.show(); w.show();
return a.exec(); return QApplication::exec();
} }

View File

@ -3,9 +3,7 @@
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget * parent) : MainWindow::MainWindow(QWidget * parent) :
QMainWindow(parent), QMainWindow(parent), ui(new Ui::MainWindow) {
ui(new Ui::MainWindow)
{
ui->setupUi(this); ui->setupUi(this);
// For use in design mode using Qt Creator // For use in design mode using Qt Creator
// + We can use the `ui` member to access nested widgets by name // + We can use the `ui` member to access nested widgets by name
@ -22,7 +20,6 @@ MainWindow::MainWindow(QWidget *parent) :
setWindowIcon(QIcon("../resources/icon.png")); setWindowIcon(QIcon("../resources/icon.png"));
} }
MainWindow::~MainWindow() MainWindow::~MainWindow() {
{
delete ui; delete ui;
} }

View File

@ -5,25 +5,25 @@
#include <QMainWindow> #include <QMainWindow>
#include "qtk-widget_export.h"
#include <examplescene.h> #include <examplescene.h>
#include "qtk-widget_export.h"
namespace Ui { namespace Ui {
class MainWindow; class MainWindow;
} }
class QTK_WIDGET_EXPORT MainWindow : public QMainWindow class QTK_WIDGET_EXPORT MainWindow
{
: public QMainWindow {
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(QWidget * parent = nullptr); explicit MainWindow(QWidget * parent = nullptr);
~MainWindow(); ~MainWindow() override;
private: private:
Ui::MainWindow *ui; Ui::MainWindow * ui {};
std::unordered_map<std::string, Qtk::Scene*> mScenes; std::unordered_map<std::string, Qtk::Scene *> mScenes {};
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@ -15,15 +15,15 @@
typedef class ResourceManager { typedef class ResourceManager {
public: public:
/** /**
* Takes a path using qrc format and constructs full system path to qtk assets * Takes a path using qrc format and constructs full system path to qtk
* Qrc format prefix ':/' is trimmed from the path for the caller * 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 * 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' * @param path Path relative to qtk/resources/; ie)
* An asset at location qtk/resources/path/to/asset.obj * ':/models/backpack/backpack.obj' An asset at location
* Should be given in qrc format: ':/path/to/asset.obj' * qtk/resources/path/to/asset.obj Should be given in qrc format:
* ':/path/to/asset.obj'
* @return Absoulte system path to a qtk asset * @return Absoulte system path to a qtk asset
*/ */
static std::string getPath(const std::string & path) { static std::string getPath(const std::string & path) {

View File

@ -16,33 +16,39 @@ using namespace Qtk;
Camera3D Scene::mCamera; Camera3D Scene::mCamera;
QMatrix4x4 Scene::mProjection; QMatrix4x4 Scene::mProjection;
/******************************************************************************* /*******************************************************************************
* Constructors, Destructors * Constructors, Destructors
******************************************************************************/ ******************************************************************************/
Scene::Scene() Scene::Scene() {
{
mCamera.transform().setTranslation(0.0f, 0.0f, 20.0f); mCamera.transform().setTranslation(0.0f, 0.0f, 20.0f);
mCamera.transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); mCamera.transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
} }
Scene::~Scene() Scene::~Scene() {
{ for(auto & mesh : mMeshes) {
for (auto & mesh : mMeshes) delete mesh; delete mesh;
for (auto & model : mModels) delete model; }
if (mSkybox != Q_NULLPTR) delete mSkybox; for(auto & model : mModels) {
delete model;
} }
void Scene::privDraw() delete mSkybox;
{ }
void Scene::privDraw() {
if(!mInit) { if(!mInit) {
initializeOpenGLFunctions(); initializeOpenGLFunctions();
init(); init();
mInit = true; mInit = true;
} }
if (mSkybox != Q_NULLPTR) mSkybox->draw(); if(mSkybox != Q_NULLPTR) {
for (auto & model : mModels) model->draw(); mSkybox->draw();
for (const auto & mesh : mMeshes) mesh->draw(); }
for(auto & model : mModels) {
model->draw();
}
for(const auto & mesh : mMeshes) {
mesh->draw();
}
} }

View File

@ -16,7 +16,6 @@
#include <QMatrix4x4> #include <QMatrix4x4>
namespace Qtk { namespace Qtk {
class Scene : protected QOpenGLFunctions { class Scene : protected QOpenGLFunctions {
friend class MainWidget; friend class MainWidget;
@ -26,17 +25,19 @@ namespace Qtk {
~Scene(); ~Scene();
virtual void init() = 0; virtual void init() = 0;
virtual void draw() { privDraw(); }; virtual void draw() { privDraw(); };
virtual void update() = 0; virtual void update() = 0;
static Camera3D & Camera() { return mCamera; } static Camera3D & Camera() { return mCamera; }
static QMatrix4x4 View() { return mCamera.toMatrix(); } static QMatrix4x4 View() { return mCamera.toMatrix(); }
static QMatrix4x4 & Projection() { return mProjection; } static QMatrix4x4 & Projection() { return mProjection; }
inline Skybox * getSkybox() { return mSkybox; } inline Skybox * getSkybox() { return mSkybox; }
inline void setSkybox(Skybox * skybox) {
mSkybox = skybox; inline void setSkybox(Skybox * skybox) { mSkybox = skybox; }
}
private: private:
static Camera3D mCamera; static Camera3D mCamera;
@ -46,10 +47,10 @@ namespace Qtk {
void privDraw(); void privDraw();
protected: protected:
Skybox * mSkybox; Skybox * mSkybox {};
std::vector<MeshRenderer *> mMeshes; std::vector<MeshRenderer *> mMeshes {};
std::vector<Model *> mModels; std::vector<Model *> mModels {};
}; };
} } // namespace Qtk
#endif // QTK_SCENE_H #endif // QTK_SCENE_H

View File

@ -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::LocalUp(0.0f, 1.0f, 0.0f);
const QVector3D Camera3D::LocalRight(1.0f, 0.0f, 0.0f); const QVector3D Camera3D::LocalRight(1.0f, 0.0f, 0.0f);
/******************************************************************************* /*******************************************************************************
* Accessors * Accessors
******************************************************************************/ ******************************************************************************/
// Produces worldToView matrix // Produces worldToView matrix
const QMatrix4x4 & Camera3D::toMatrix() const QMatrix4x4 & Camera3D::toMatrix() {
{
mWorld.setToIdentity(); mWorld.setToIdentity();
// Qt6 renamed QMatrix4x4::conjugate() to conjugated() // Qt6 renamed QMatrix4x4::conjugate() to conjugated()
mWorld.rotate(mTransform.rotation().conjugated()); mWorld.rotate(mTransform.getRotation().conjugated());
mWorld.translate(-mTransform.translation()); mWorld.translate(-mTransform.getTranslation());
return mWorld; return mWorld;
} }
/******************************************************************************* /*******************************************************************************
* Qt Streams * Qt Streams
******************************************************************************/ ******************************************************************************/
QDataStream & operator<<(QDataStream & out, Camera3D & transform) QDataStream & operator<<(QDataStream & out, Camera3D & transform) {
{
out << transform.transform(); out << transform.transform();
return out; return out;
} }
QDataStream & operator>>(QDataStream & in, Camera3D & transform) QDataStream & operator>>(QDataStream & in, Camera3D & transform) {
{
in >> transform.transform(); in >> transform.transform();
return in; return in;
} }
QDebug operator<<(QDebug dbg, const Camera3D & transform) QDebug operator<<(QDebug dbg, const Camera3D & transform) {
{
dbg << "Camera3D\n{\n"; dbg << "Camera3D\n{\n";
dbg << "Position: <" << transform.translation().x() << ", " dbg << "Position: <" << transform.translation().x() << ", "
<< transform.translation().y() << ", " << transform.translation().y() << ", " << transform.translation().z()
<< transform.translation().z() << ">\n"; << ">\n";
dbg << "Rotation: <" << transform.rotation().x() << ", " dbg << "Rotation: <" << transform.rotation().x() << ", "
<< transform.rotation().y() << ", " << transform.rotation().y() << ", " << transform.rotation().z() << " | "
<< transform.rotation().z() << " | "
<< transform.rotation().scalar() << ">\n}"; << transform.rotation().scalar() << ">\n}";
return dbg; return dbg;
} }

View File

@ -11,8 +11,8 @@
#include <QDebug> #include <QDebug>
#include <transform3D.h>
#include <qtkapi.h> #include <qtkapi.h>
#include <transform3D.h>
namespace Qtk { namespace Qtk {
class QTKAPI Camera3D { class QTKAPI Camera3D {
@ -24,19 +24,29 @@ namespace Qtk {
// Accessors // Accessors
inline Transform3D & transform() { return mTransform; } inline Transform3D & transform() { return mTransform; }
inline const QVector3D & translation() const
{ return mTransform.translation();} [[nodiscard]] inline const QVector3D & translation() const {
inline const QQuaternion & rotation() const return mTransform.getTranslation();
{ return mTransform.rotation();} }
[[nodiscard]] inline const QQuaternion & rotation() const {
return mTransform.getRotation();
}
const QMatrix4x4 & toMatrix(); const QMatrix4x4 & toMatrix();
// Queries // Queries
inline QVector3D forward() const [[nodiscard]] inline QVector3D forward() const {
{ return mTransform.rotation().rotatedVector(LocalForward);} return mTransform.getRotation().rotatedVector(LocalForward);
inline QVector3D right() const }
{ return mTransform.rotation().rotatedVector(LocalRight);}
inline QVector3D up() const [[nodiscard]] inline QVector3D right() const {
{ return mTransform.rotation().rotatedVector(LocalUp);} return mTransform.getRotation().rotatedVector(LocalRight);
}
[[nodiscard]] inline QVector3D up() const {
return mTransform.getRotation().rotatedVector(LocalUp);
}
private: private:
Transform3D mTransform; Transform3D mTransform;
@ -57,7 +67,7 @@ namespace Qtk {
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Camera3D & transform); QDebug operator<<(QDebug dbg, const Camera3D & transform);
#endif #endif
} } // namespace Qtk
Q_DECLARE_TYPEINFO(Qtk::Camera3D, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(Qtk::Camera3D, Q_MOVABLE_TYPE);

View File

@ -18,19 +18,19 @@ using namespace Qtk;
/******************************************************************************* /*******************************************************************************
* Static Helper Structs * Static Helper Structs
******************************************************************************/ ******************************************************************************/
template <typename T> template <typename T> struct InputInstance : std::pair<T, Input::InputState> {
struct InputInstance : std::pair<T, Input::InputState>
{
typedef std::pair<T, Input::InputState> base_class; typedef std::pair<T, Input::InputState> base_class;
inline InputInstance(T value) // Disable clang-tidy from marking this ctor explicit
: base_class(value, Input::InputInvalid) {} // NOLINTNEXTLINE
inline InputInstance(T value) : base_class(value, Input::InputInvalid) {}
inline InputInstance(T value, Input::InputState state) inline InputInstance(T value, Input::InputState state) :
: base_class(value, state) {} base_class(value, state) {}
inline bool operator==(const InputInstance & rhs) const inline bool operator==(const InputInstance & rhs) const {
{ return this->first == rhs.first;} return this->first == rhs.first;
}
}; };
// Key, button instance typedefs // Key, button instance typedefs
@ -49,26 +49,20 @@ static QPoint sg_mouseCurrPosition;
static QPoint sg_mousePrevPosition; static QPoint sg_mousePrevPosition;
static QPoint sg_mouseDelta; static QPoint sg_mouseDelta;
/******************************************************************************* /*******************************************************************************
* Static Inline Helper Functions * Static Inline Helper Functions
******************************************************************************/ ******************************************************************************/
static inline KeyContainer::iterator FindKey(Qt::Key value) static inline KeyContainer::iterator FindKey(Qt::Key value) {
{
return std::find(sg_keyInstances.begin(), sg_keyInstances.end(), value); return std::find(sg_keyInstances.begin(), sg_keyInstances.end(), value);
} }
static inline ButtonContainer::iterator FindButton(Qt::MouseButton value) static inline ButtonContainer::iterator FindButton(Qt::MouseButton value) {
{
return std::find(sg_buttonInstances.begin(), sg_buttonInstances.end(), value); return std::find(sg_buttonInstances.begin(), sg_buttonInstances.end(), value);
} }
template <typename TPair> template <typename TPair> static inline void UpdateStates(TPair & instance) {
static inline void UpdateStates(TPair & instance) switch(instance.second) {
{
switch (instance.second)
{
case Input::InputRegistered: case Input::InputRegistered:
instance.second = Input::InputTriggered; instance.second = Input::InputTriggered;
break; break;
@ -84,19 +78,16 @@ static inline void UpdateStates(TPair & instance)
} }
template <typename TPair> template <typename TPair>
static inline bool CheckReleased(const TPair & instance) static inline bool CheckReleased(const TPair & instance) {
{
return instance.second == Input::InputReleased; return instance.second == Input::InputReleased;
} }
template <typename Container> template <typename Container> static inline void Update(Container & container) {
static inline void Update(Container & container)
{
typedef typename Container::iterator Iter; typedef typename Container::iterator Iter;
typedef typename Container::value_type TPair; typedef typename Container::value_type TPair;
// Remove old data // Remove old data
Iter remove = auto remove =
std::remove_if(container.begin(), container.end(), &CheckReleased<TPair>); std::remove_if(container.begin(), container.end(), &CheckReleased<TPair>);
container.erase(remove, container.end()); container.erase(remove, container.end());
@ -104,35 +95,29 @@ static inline void Update(Container & container)
std::for_each(container.begin(), container.end(), &UpdateStates<TPair>); std::for_each(container.begin(), container.end(), &UpdateStates<TPair>);
} }
/******************************************************************************* /*******************************************************************************
* Input Implementation * Input Implementation
******************************************************************************/ ******************************************************************************/
Input::InputState Input::keyState(Qt::Key k) Input::InputState Input::keyState(Qt::Key k) {
{ auto it = FindKey(k);
KeyContainer::iterator it = FindKey(k);
return (it != sg_keyInstances.end()) ? it->second : InputInvalid; return (it != sg_keyInstances.end()) ? it->second : InputInvalid;
} }
Input::InputState Input::buttonState(Qt::MouseButton k) Input::InputState Input::buttonState(Qt::MouseButton k) {
{ auto it = FindButton(k);
ButtonContainer::iterator it = FindButton(k);
return (it != sg_buttonInstances.end()) ? it->second : InputInvalid; return (it != sg_buttonInstances.end()) ? it->second : InputInvalid;
} }
QPoint Input::mousePosition() QPoint Input::mousePosition() {
{
return QCursor::pos(); return QCursor::pos();
} }
QPoint Input::mouseDelta() QPoint Input::mouseDelta() {
{
return sg_mouseDelta; return sg_mouseDelta;
} }
void Input::update() void Input::update() {
{
// Update Mouse Delta // Update Mouse Delta
sg_mousePrevPosition = sg_mouseCurrPosition; sg_mousePrevPosition = sg_mouseCurrPosition;
sg_mouseCurrPosition = QCursor::pos(); sg_mouseCurrPosition = QCursor::pos();
@ -143,44 +128,35 @@ void Input::update()
Update(sg_keyInstances); Update(sg_keyInstances);
} }
void Input::registerKeyPress(int k) void Input::registerKeyPress(int k) {
{ auto it = FindKey((Qt::Key)k);
KeyContainer::iterator it = FindKey((Qt::Key)k); if(it == sg_keyInstances.end()) {
if (it == sg_keyInstances.end())
{
sg_keyInstances.push_back(KeyInstance((Qt::Key)k, InputRegistered)); sg_keyInstances.push_back(KeyInstance((Qt::Key)k, InputRegistered));
} }
} }
void Input::registerKeyRelease(int k) void Input::registerKeyRelease(int k) {
{ auto it = FindKey((Qt::Key)k);
KeyContainer::iterator it = FindKey((Qt::Key)k); if(it != sg_keyInstances.end()) {
if (it != sg_keyInstances.end())
{
it->second = InputUnregistered; it->second = InputUnregistered;
} }
} }
void Input::registerMousePress(Qt::MouseButton btn) void Input::registerMousePress(Qt::MouseButton btn) {
{ auto it = FindButton(btn);
ButtonContainer::iterator it = FindButton(btn); if(it == sg_buttonInstances.end()) {
if (it == sg_buttonInstances.end())
{
sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered)); sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered));
} }
} }
void Input::registerMouseRelease(Qt::MouseButton btn) void Input::registerMouseRelease(Qt::MouseButton btn) {
{ auto it = FindButton(btn);
ButtonContainer::iterator it = FindButton(btn); if(it != sg_buttonInstances.end()) {
if (it != sg_buttonInstances.end())
{
it->second = InputUnregistered; it->second = InputUnregistered;
} }
} }
void Input::reset() void Input::reset() {
{
sg_keyInstances.clear(); sg_keyInstances.clear();
sg_buttonInstances.clear(); sg_buttonInstances.clear();
} }

View File

@ -18,10 +18,10 @@
namespace Qtk { namespace Qtk {
class QTKAPI Input { class QTKAPI Input {
friend class Qtk::QtkWidget; friend class Qtk::QtkWidget;
public: public:
// Possible key states // Possible key states
enum InputState enum InputState {
{
InputInvalid, InputInvalid,
InputRegistered, InputRegistered,
InputUnregistered, InputUnregistered,
@ -31,18 +31,29 @@ namespace Qtk {
}; };
// State checking // State checking
inline static bool keyTriggered(Qt::Key key) inline static bool keyTriggered(Qt::Key key) {
{ return keyState(key) == InputTriggered;} return keyState(key) == InputTriggered;
inline static bool keyPressed(Qt::Key key) }
{ return keyState(key) == InputPressed;}
inline static bool keyReleased(Qt::Key key) inline static bool keyPressed(Qt::Key key) {
{ return keyState(key) == InputReleased;} return keyState(key) == InputPressed;
inline static bool buttonTriggered(Qt::MouseButton button) }
{ return buttonState(button) == InputTriggered;}
inline static bool buttonPressed(Qt::MouseButton button) inline static bool keyReleased(Qt::Key key) {
{ return buttonState(button) == InputPressed;} return keyState(key) == InputReleased;
inline static bool buttonReleased(Qt::MouseButton button) }
{ return buttonState(button) == 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 // Implementation
static InputState keyState(Qt::Key key); static InputState keyState(Qt::Key key);
@ -60,6 +71,6 @@ namespace Qtk {
static void registerMouseRelease(Qt::MouseButton button); static void registerMouseRelease(Qt::MouseButton button);
static void reset(); static void reset();
}; };
} } // namespace Qtk
#endif // QTOPENGL_INPUT_H #endif // QTOPENGL_INPUT_H

75
src/mainwidget.h Normal file
View File

@ -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 <iostream>
#include <QMatrix4x4>
#include <QOpenGLDebugLogger>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#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

View File

@ -10,100 +10,78 @@
using namespace Qtk; using namespace Qtk;
Cube::Cube(DrawMode mode) Cube::Cube(DrawMode mode) {
{
mDrawMode = mode; mDrawMode = mode;
switch(mode) { switch(mode) {
// Cube data for use with glDrawArrays // Cube data for use with glDrawArrays
case QTK_DRAW_ARRAYS: case QTK_DRAW_ARRAYS:
mIndices = {/* No indices needed for glDrawArrays */}; mIndices = {/* No indices needed for glDrawArrays */};
mNormals = mNormals = {FACE_FRONT, FACE_BACK, FACE_TOP,
{FACE_FRONT, FACE_BACK, FACE_TOP, FACE_BOTTOM, FACE_LEFT, FACE_RIGHT}; FACE_BOTTOM, FACE_LEFT, FACE_RIGHT};
mVertices = { mVertices = {// Face 1 (Front)
// Face 1 (Front) VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBL, VERTEX_FBR,
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FTR,
VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
// Face 2 (Back) // Face 2 (Back)
VERTEX_BBR, VERTEX_BTL, VERTEX_BTR, VERTEX_BBR, VERTEX_BTL, VERTEX_BTR, VERTEX_BTL, VERTEX_BBR,
VERTEX_BTL, VERTEX_BBR, VERTEX_BBL, VERTEX_BBL,
// Face 3 (Top) // Face 3 (Top)
VERTEX_FTR, VERTEX_BTR, VERTEX_BTL, VERTEX_FTR, VERTEX_BTR, VERTEX_BTL, VERTEX_BTL, VERTEX_FTL,
VERTEX_BTL, VERTEX_FTL, VERTEX_FTR, VERTEX_FTR,
// Face 4 (Bottom) // Face 4 (Bottom)
VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, VERTEX_BBL, VERTEX_BBR,
VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, VERTEX_FBR,
// Face 5 (Left) // Face 5 (Left)
VERTEX_FBL, VERTEX_FTL, VERTEX_BTL, VERTEX_FBL, VERTEX_FTL, VERTEX_BTL, VERTEX_FBL, VERTEX_BTL,
VERTEX_FBL, VERTEX_BTL, VERTEX_BBL, VERTEX_BBL,
// Face 6 (Right) // Face 6 (Right)
VERTEX_FTR, VERTEX_FBR, VERTEX_BBR, VERTEX_FTR, VERTEX_FBR, VERTEX_BBR, VERTEX_BBR, VERTEX_BTR,
VERTEX_BBR, VERTEX_BTR, VERTEX_FTR VERTEX_FTR};
};
mColors = { mColors = {// Face 1 (Front)
// Face 1 (Front) RED, GREEN, BLUE, BLUE, WHITE, RED,
RED, GREEN, BLUE,
BLUE, WHITE, RED,
// Face 2 (Back) // Face 2 (Back)
YELLOW, CYAN, MAGENTA, YELLOW, CYAN, MAGENTA, CYAN, YELLOW, BLACK,
CYAN, YELLOW, BLACK,
// Face 3 (Top) // Face 3 (Top)
RED, MAGENTA, CYAN, RED, MAGENTA, CYAN, CYAN, GREEN, RED,
CYAN, GREEN, RED,
// Face 4 (Bottom) // Face 4 (Bottom)
WHITE, BLUE, BLACK, WHITE, BLUE, BLACK, BLACK, YELLOW, WHITE,
BLACK, YELLOW, WHITE,
// Face 5 (Left) // Face 5 (Left)
BLUE, GREEN, CYAN, BLUE, GREEN, CYAN, BLUE, CYAN, BLACK,
BLUE, CYAN, BLACK,
// Face 6 (Right) // Face 6 (Right)
RED, WHITE, YELLOW, RED, WHITE, YELLOW, YELLOW, MAGENTA, RED};
YELLOW, MAGENTA, RED
};
mTexCoords = { mTexCoords = {// Face 1 (Front)
// Face 1 (Front) UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, UV_TOP,
UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_RIGHT, UV_CORNER, UV_TOP,
// Face 2 (Back) // Face 2 (Back)
UV_TOP, UV_RIGHT, UV_CORNER, UV_TOP, UV_RIGHT, UV_CORNER, UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_RIGHT, UV_TOP, UV_ORIGIN,
// Face 3 (Top) // Face 3 (Top)
UV_CORNER, UV_TOP, UV_ORIGIN, UV_CORNER, UV_TOP, UV_ORIGIN, UV_ORIGIN, UV_RIGHT,
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_CORNER,
// Face 4 (Bottom) // Face 4 (Bottom)
UV_TOP, UV_ORIGIN, UV_RIGHT, UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, UV_TOP,
UV_RIGHT, UV_CORNER, UV_TOP,
// Face 5 (Left) // Face 5 (Left)
UV_TOP, UV_CORNER, UV_RIGHT, UV_TOP, UV_CORNER, UV_RIGHT, UV_TOP, UV_RIGHT, UV_ORIGIN,
UV_TOP, UV_RIGHT, UV_ORIGIN,
// Face 6 (Right) // Face 6 (Right)
UV_TOP, UV_CORNER, UV_RIGHT, UV_TOP, UV_CORNER, UV_RIGHT, UV_RIGHT, UV_ORIGIN, UV_TOP};
UV_RIGHT, UV_ORIGIN, UV_TOP
};
break; break;
// Cube data for use with glDrawElements // Cube data for use with glDrawElements
case QTK_DRAW_ELEMENTS: case QTK_DRAW_ELEMENTS:
mNormals = mNormals = {
{/* For normals and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */}; /* For normals and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */};
mTexCoords = mTexCoords = {
{ /* For UVs and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */ }; /* For UVs and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */};
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK}; mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mVertices = { mVertices = {// 0 1 2 3
// 0 1 2 3
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR,
// 4 5 6 7 // 4 5 6 7
VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR};
}; mIndices = {// Face 1 (Front)
mIndices = {
// Face 1 (Front)
0, 1, 2, 2, 3, 0, 0, 1, 2, 2, 3, 0,
// Face 2 (Back) // Face 2 (Back)
7, 5, 4, 5, 7, 6, 7, 5, 4, 5, 7, 6,
@ -114,16 +92,14 @@ Cube::Cube(DrawMode mode)
// Face 5 (Left) // Face 5 (Left)
2, 1, 5, 2, 5, 6, 2, 1, 5, 2, 5, 6,
// Face 6 (Right) // Face 6 (Right)
0, 3, 7, 7, 4, 0 0, 3, 7, 7, 4, 0};
};
break; break;
// Cube shape data for using normals and UVs with glDrawElements // Cube shape data for using normals and UVs with glDrawElements
case QTK_DRAW_ELEMENTS_NORMALS: case QTK_DRAW_ELEMENTS_NORMALS:
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK}; mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mVertices = { mVertices = {// Face 1 (Front)
// Face 1 (Front)
// 0 1 2 3 // 0 1 2 3
VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
// Face 2 (Back) // Face 2 (Back)
@ -140,11 +116,9 @@ Cube::Cube(DrawMode mode)
VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL, VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL,
// Face 6 (Right) // Face 6 (Right)
// 20 21 22 23 // 20 21 22 23
VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR};
};
mIndices = { mIndices = {// Face 1 (Front)
// Face 1 (Front)
0, 1, 2, 2, 3, 0, 0, 1, 2, 2, 3, 0,
// Face 2 (Back) // Face 2 (Back)
4, 5, 6, 6, 7, 4, 4, 5, 6, 6, 7, 4,
@ -156,8 +130,7 @@ Cube::Cube(DrawMode mode)
// Face 5 (Left) // Face 5 (Left)
16, 17, 18, 18, 19, 16, 16, 17, 18, 18, 19, 16,
// Face 6 (Right) // Face 6 (Right)
20, 21, 22, 22, 23, 20 20, 21, 22, 22, 23, 20};
};
mNormals = { mNormals = {
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD,
@ -170,31 +143,54 @@ Cube::Cube(DrawMode mode)
mTexCoords = { mTexCoords = {
// Face 1 (Front) // Face 1 (Front)
UV_TOP, UV_RIGHT, UV_CORNER, UV_TOP,
UV_RIGHT, UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 2 (Back) // Face 2 (Back)
UV_TOP, UV_RIGHT, UV_CORNER, UV_TOP,
UV_RIGHT, UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 3 (Top) // Face 3 (Top)
UV_TOP, UV_RIGHT, UV_CORNER, UV_TOP,
UV_RIGHT, UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 4 (Bottom) // Face 4 (Bottom)
UV_TOP, UV_RIGHT, UV_CORNER, UV_TOP,
UV_RIGHT, UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 5 (Left) // Face 5 (Left)
UV_TOP, UV_RIGHT, UV_CORNER, UV_TOP,
UV_RIGHT, UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 6 (Right) // Face 6 (Right)
UV_TOP, UV_RIGHT, UV_CORNER, UV_TOP,
UV_RIGHT, UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
}; };
break; break;
} }
} }
Triangle::Triangle(DrawMode mode) Triangle::Triangle(DrawMode mode) {
{
mDrawMode = mode; mDrawMode = mode;
const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f); const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f);
switch(mode) { switch(mode) {
@ -205,17 +201,29 @@ Triangle::Triangle(DrawMode mode)
mVertices = { mVertices = {
// Bottom face (Base of the pyramid) // Bottom face (Base of the pyramid)
VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, VERTEX_BBL,
VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, VERTEX_BBR,
VERTEX_FBR,
VERTEX_FBR,
VERTEX_FBL,
VERTEX_BBL,
// Front face // Front face
VERTEX_FBL, VERTEX_FBR, triangleTop, VERTEX_FBL,
VERTEX_FBR,
triangleTop,
// Back face // Back face
VERTEX_BBR, VERTEX_BBL, triangleTop, VERTEX_BBR,
VERTEX_BBL,
triangleTop,
// Left face // Left face
VERTEX_BBL, VERTEX_FBL, triangleTop, VERTEX_BBL,
VERTEX_FBL,
triangleTop,
// Right face // Right face
VERTEX_FBR, VERTEX_BBR, triangleTop, VERTEX_FBR,
VERTEX_BBR,
triangleTop,
}; };
// Find normals for each triangle of the mesh // Find normals for each triangle of the mesh
@ -230,17 +238,29 @@ Triangle::Triangle(DrawMode mode)
mTexCoords = { mTexCoords = {
// Bottom face (Base of the pyramid) // Bottom face (Base of the pyramid)
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_ORIGIN,
UV_CORNER, UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_CORNER,
UV_CORNER,
UV_TOP,
UV_ORIGIN,
// Front face // Front face
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Back face // Back face
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Left face // Left face
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Right face // Right face
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
}; };
break; break;
@ -262,8 +282,8 @@ Triangle::Triangle(DrawMode mode)
1, 3, 4, // Right face 1, 3, 4, // Right face
}; };
mNormals = mNormals = {
{/* Use QTK_DRAW_ELEMENTS_NORMALS for normals with glDrawElements */}; /* Use QTK_DRAW_ELEMENTS_NORMALS for normals with glDrawElements */};
mTexCoords = {/* No UVs for triangle with glDrawElements */}; mTexCoords = {/* No UVs for triangle with glDrawElements */};
@ -276,22 +296,34 @@ Triangle::Triangle(DrawMode mode)
mVertices = { mVertices = {
// Bottom face // Bottom face
// 0 1 2 // 0 1 2
VERTEX_FBL, VERTEX_FBR, VERTEX_BBL, VERTEX_FBL,
VERTEX_FBR,
VERTEX_BBL,
// 3 4 5 // 3 4 5
VERTEX_BBR, VERTEX_FBR, VERTEX_BBL, VERTEX_BBR,
VERTEX_FBR,
VERTEX_BBL,
// Front face // Front face
// 6 7 8 // 6 7 8
VERTEX_FBL, VERTEX_FBR, triangleTop, VERTEX_FBL,
VERTEX_FBR,
triangleTop,
// Back face // Back face
// 9 10 11 // 9 10 11
VERTEX_BBR, VERTEX_BBL, triangleTop, VERTEX_BBR,
VERTEX_BBL,
triangleTop,
// Left face // Left face
// 12 13 14 // 12 13 14
VERTEX_BBL, VERTEX_FBL, triangleTop, VERTEX_BBL,
VERTEX_FBL,
triangleTop,
// Right face // Right face
// 15 16 17 // 15 16 17
VERTEX_FBR, VERTEX_BBR, triangleTop, VERTEX_FBR,
VERTEX_BBR,
triangleTop,
}; };
mIndices = { mIndices = {
@ -307,9 +339,8 @@ Triangle::Triangle(DrawMode mode)
// Find normals for each triangle of the mesh // 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 vertexNormal = QVector3D::normal(
QVector3D::normal(mVertices[mIndices[i]], mVertices[mIndices[i]], mVertices[mIndices[i + 1]],
mVertices[mIndices[i+1]],
mVertices[mIndices[i + 2]]); mVertices[mIndices[i + 2]]);
// Three points share this normal // Three points share this normal
for(int j = 0; j < 3; j++) { for(int j = 0; j < 3; j++) {
@ -319,19 +350,30 @@ Triangle::Triangle(DrawMode mode)
mTexCoords = { mTexCoords = {
// Bottom face // Bottom face
UV_ORIGIN, UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_CORNER, UV_RIGHT, UV_TOP, UV_RIGHT,
UV_TOP,
UV_CORNER,
UV_RIGHT,
UV_TOP,
// Front face // Front face
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Back face // Back face
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Left face // Left face
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Right face // Right face
UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
}; };
break; break;
} }
} }

View File

@ -11,12 +11,14 @@
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <QVector2D> #include <QVector2D>
#include <QVector3D> #include <QVector3D>
#include <utility>
#include <qtkapi.h> #include <qtkapi.h>
#include <transform3D.h> #include <transform3D.h>
namespace Qtk { namespace Qtk {
class MeshRenderer; class MeshRenderer;
class Object; class Object;
// Define vertices for drawing a cube using two faces (8 vertex points) // Define vertices for drawing a cube using two faces (8 vertex points)
@ -45,18 +47,20 @@ namespace Qtk {
#define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f) #define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f)
// A series of direction vectors to represent cube face normal // A series of direction vectors to represent cube face normal
#define FACE_TOP VECTOR_UP, VECTOR_UP, VECTOR_UP, \ #define FACE_TOP \
VECTOR_UP, VECTOR_UP, VECTOR_UP VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP
#define FACE_BOTTOM VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, \ #define FACE_BOTTOM \
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN
#define FACE_LEFT VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, \ #define FACE_LEFT \
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT
#define FACE_RIGHT VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \ #define FACE_RIGHT \
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \
#define FACE_FRONT VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \ VECTOR_RIGHT
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD #define FACE_FRONT \
#define FACE_BACK VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, \ VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK 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 // Colors using QVector3Ds as RGB values
#define WHITE VECTOR_ONE #define WHITE VECTOR_ONE
@ -73,62 +77,99 @@ namespace Qtk {
#define UV_RIGHT QVector2D(0.0f, 1.0f) #define UV_RIGHT QVector2D(0.0f, 1.0f)
#define UV_CORNER QVector2D(1.0f, 1.0f) #define UV_CORNER QVector2D(1.0f, 1.0f)
// TODO: Vertices.getData(); Vertices.getStride();
typedef std::vector<QVector3D> Vertices; typedef std::vector<QVector3D> Vertices;
typedef std::vector<QVector3D> Colors; typedef std::vector<QVector3D> Colors;
typedef std::vector<GLuint> Indices; typedef std::vector<GLuint> Indices;
typedef std::vector<QVector2D> TexCoords; typedef std::vector<QVector2D> TexCoords;
typedef std::vector<QVector3D> Normals; typedef std::vector<QVector3D> 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 { struct QTKAPI ShapeBase {
ShapeBase(DrawMode mode=QTK_DRAW_ARRAYS, Vertices v={},Indices i={}, Colors c={}, explicit ShapeBase(
TexCoords t={}, Normals n={}) DrawMode mode = QTK_DRAW_ARRAYS, Vertices v = {}, Indices i = {},
: mVertices(v), mColors(c), mIndices(i), mTexCoords(t), mNormals(n) 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;} [[nodiscard]] inline const Vertices & getVertices() const {
inline const Indices & indices() const { return mIndices;} return mVertices;
inline const Colors & colors() const { return mColors;} }
inline const TexCoords & texCoords() const { return mTexCoords;}
inline const Normals & normals() const { return mNormals;} [[nodiscard]] inline const Indices & getIndexData() const {
return mIndices;
}
[[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: protected:
DrawMode mDrawMode; DrawMode mDrawMode;
Vertices mVertices; Vertices mVertices {};
Colors mColors; Colors mColors {};
Indices mIndices; Indices mIndices {};
TexCoords mTexCoords; TexCoords mTexCoords {};
Normals mNormals; Normals mNormals {};
}; };
struct Shape : public ShapeBase { struct Shape : public ShapeBase {
friend MeshRenderer; friend MeshRenderer;
friend Object; friend Object;
Shape () {}
Shape(const ShapeBase & rhs) : ShapeBase(rhs) {}
virtual inline void setVertices(const Vertices & value) {mVertices = value;} Shape() = default;
virtual inline void setIndices(const Indices & value) {mIndices = value;}
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 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 setTexCoords(const TexCoords & value) {
mTexCoords = value;
}
virtual inline void setNormals(const Normals & value) {
mNormals = value;
}
virtual inline void setShape(const Shape & value) { *this = value; } virtual inline void setShape(const Shape & value) { *this = value; }
}; };
// Primitives inherit from ShapeBase, does not allow setting of shape values // Primitives inherit from ShapeBase, does not allow setting of shape values
class QTKAPI Mesh { class QTKAPI Mesh {};
};
struct QTKAPI Cube : public ShapeBase { struct QTKAPI Cube : public ShapeBase {
Cube(DrawMode mode=QTK_DRAW_ARRAYS); explicit Cube(DrawMode mode = QTK_DRAW_ARRAYS);
}; };
struct QTKAPI Triangle : public ShapeBase { 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

View File

@ -17,48 +17,46 @@ using namespace Qtk;
// Static QHash that holds all MeshRenderer instances using their mName as keys // Static QHash that holds all MeshRenderer instances using their mName as keys
Qtk::MeshRenderer::MeshManager Qtk::MeshRenderer::sInstances; Qtk::MeshRenderer::MeshManager Qtk::MeshRenderer::sInstances;
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
: Object(name, shape), mVertexShader(":/multi-color.vert"), Object(name, shape), mVertexShader(":/multi-color.vert"),
mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES), mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES) {
mHasTexture(false)
{
mShape = Shape(shape); mShape = Shape(shape);
init(); init();
sInstances.insert(name, this); sInstances.insert(name, this);
} }
MeshRenderer::~MeshRenderer() MeshRenderer::~MeshRenderer() {
{
if (mHasTexture) {
mTexture->destroy();
}
sInstances.remove(mName); sInstances.remove(mName);
} }
// Static member function to retrieve instances of MeshRenderers // Static member function to retrieve instances of MeshRenderers
MeshRenderer * MeshRenderer::getInstance(const QString & name) MeshRenderer * MeshRenderer::getInstance(const QString & name) {
{ return sInstances[name];} return sInstances[name];
}
/******************************************************************************* /*******************************************************************************
* Public Member Functions * Public Member Functions
******************************************************************************/ ******************************************************************************/
void MeshRenderer::init() void MeshRenderer::init() {
{ if(mVAO.isCreated()) {
if (mVAO.isCreated()) mVAO.destroy(); mVAO.destroy();
if (mProgram.isLinked()) mProgram.removeAllShaders(); }
if (mVBO.isCreated()) mVBO.destroy(); if(mProgram.isLinked()) {
mProgram.removeAllShaders();
}
if(mVBO.isCreated()) {
mVBO.destroy();
}
mVAO.create(); mVAO.create();
mVAO.bind(); mVAO.bind();
mProgram.create(); mProgram.create();
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, mProgram.addShaderFromSourceFile(
mVertexShader.c_str()); QOpenGLShader::Vertex, mVertexShader.c_str());
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, mProgram.addShaderFromSourceFile(
mFragmentShader.c_str()); QOpenGLShader::Fragment, mFragmentShader.c_str());
mProgram.link(); mProgram.link();
mProgram.bind(); mProgram.bind();
@ -68,22 +66,20 @@ void MeshRenderer::init()
// Combine position and color data into one vector, allowing us to use one VBO // Combine position and color data into one vector, allowing us to use one VBO
Vertices combined; Vertices combined;
combined.reserve(vertices().size() + colors().size()); combined.reserve(getVertices().size() + getColors().size());
combined.insert(combined.end(), vertices().begin(), vertices().end()); combined.insert(combined.end(), getVertices().begin(), getVertices().end());
combined.insert(combined.end(), colors().begin(), colors().end()); combined.insert(combined.end(), getColors().begin(), getColors().end());
mVBO.allocate(combined.data(), mVBO.allocate(combined.data(), combined.size() * sizeof(combined[0]));
combined.size() * sizeof(combined[0]));
// Enable position attribute // Enable position attribute
mProgram.enableAttributeArray(0); mProgram.enableAttributeArray(0);
mProgram.setAttributeBuffer(0, GL_FLOAT, 0, mProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D));
3, sizeof(QVector3D));
// Enable color attribute, setting offset to total size of vertices() // Enable color attribute, setting offset to total size of vertices()
mProgram.enableAttributeArray(1); mProgram.enableAttributeArray(1);
mProgram.setAttributeBuffer(1, GL_FLOAT, mProgram.setAttributeBuffer(
vertices().size() * sizeof(vertices()[0]), 1, GL_FLOAT, getVertices().size() * sizeof(getVertices()[0]), 3,
3, sizeof(QVector3D)); sizeof(QVector3D));
mVBO.release(); mVBO.release();
@ -91,82 +87,100 @@ void MeshRenderer::init()
mVAO.release(); mVAO.release();
} }
void MeshRenderer::draw() void MeshRenderer::draw() {
{ bindShaders();
mProgram.bind();
mVAO.bind(); mVAO.bind();
if(mHasTexture) { if(mTexture.hasTexture()) {
mTexture->bind(); mTexture.getOpenGLTexture().bind();
} }
// TODO: Automate uniforms some other way // TODO: Automate uniforms some other way
setUniformMVP(); setUniformMVP();
if(mShape.mDrawMode == QTK_DRAW_ARRAYS) { if(mShape.mDrawMode == QTK_DRAW_ARRAYS) {
glDrawArrays(mDrawType, 0, vertices().size()); glDrawArrays(mDrawType, 0, getVertices().size());
} } else if(
else if (mShape.mDrawMode == QTK_DRAW_ELEMENTS mShape.mDrawMode == QTK_DRAW_ELEMENTS
|| mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) { || mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) {
glDrawElements(mDrawType, mShape.mIndices.size(), glDrawElements(
GL_UNSIGNED_INT, mShape.mIndices.data()); mDrawType, mShape.mIndices.size(), GL_UNSIGNED_INT,
mShape.mIndices.data());
} }
if(mHasTexture) { if(mTexture.hasTexture()) {
mTexture->release(); mTexture.getOpenGLTexture().release();
} }
mVAO.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; mVertexShader = vert;
mFragmentShader = frag; mFragmentShader = frag;
init();
} }
void MeshRenderer::setUniformMVP(const char * model, const char * view, void MeshRenderer::setUniformMVP(
const char * projection) const char * model, const char * view, const char * projection) {
{ ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(projection, Scene::Projection()); mProgram.setUniformValue(projection, Scene::Projection());
mProgram.setUniformValue(view, Scene::View()); mProgram.setUniformValue(view, Scene::View());
mProgram.setUniformValue(model, mTransform.toMatrix()); mProgram.setUniformValue(model, mTransform.toMatrix());
} }
void MeshRenderer::setColor(const QVector3D & color) void MeshRenderer::setColor(const QVector3D & color) {
{
if(mShape.mColors.empty()) { if(mShape.mColors.empty()) {
for (const auto & vertex : mShape.vertices()) { for(const auto & vertex : mShape.getVertices()) {
mShape.mColors.push_back(color); mShape.mColors.push_back(color);
} }
} } else {
else { for(int i = 0; i < mShape.getColors().size(); i++) {
for (int i = 0; i < mShape.colors().size(); i++) {
mShape.mColors[i] = color; mShape.mColors[i] = color;
} }
} }
} }
void MeshRenderer::setTexture(const char * path) void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims) {
{ mVAO.bind();
mTexture = new QOpenGLTexture(*Texture::initImage(path)); mNBO.destroy();
mHasTexture = true; 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) void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims) {
{ // TODO: Store state to track if buffer objects are bound
mTexture = texture; mVAO.bind();
mHasTexture = true; 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 * Inherited Virtual Member Functions
******************************************************************************/ ******************************************************************************/
void MeshRenderer::setShape(const Shape & value) void MeshRenderer::setShape(const Shape & value) {
{
Object::setShape(value); Object::setShape(value);
init(); init();
} }

View File

@ -12,21 +12,47 @@
#include <object.h> #include <object.h>
#include <qtkapi.h> #include <qtkapi.h>
#include <utility>
namespace Qtk { namespace Qtk {
class QTKAPI ShaderBindScope {
public:
explicit ShaderBindScope(
QOpenGLShaderProgram * program, bool was_locked) :
mWasBound(was_locked) {
mProgram = program;
if(!mWasBound) {
mProgram->bind();
}
}
~ShaderBindScope() {
if(!mWasBound) {
mProgram->release();
}
}
private:
QOpenGLShaderProgram * mProgram;
bool mWasBound;
};
class QTKAPI MeshRenderer : public Object { class QTKAPI MeshRenderer : public Object {
public: public:
// Delegate constructors // Delegate constructors
MeshRenderer(const char * name, Vertices vertices, Indices indices, MeshRenderer(
DrawMode mode=QTK_DRAW_ARRAYS) const char * name, Vertices vertices, Indices indices,
: MeshRenderer(name, ShapeBase(mode, vertices, indices)) DrawMode mode = QTK_DRAW_ARRAYS) :
{} MeshRenderer(
MeshRenderer(const char * name) name, ShapeBase(mode, std::move(vertices), std::move(indices))) {}
: MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS))
{} explicit MeshRenderer(const char * name) :
MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS)) {}
// Constructor // Constructor
MeshRenderer(const char * name, const ShapeBase & shape); MeshRenderer(const char * name, const ShapeBase & shape);
~MeshRenderer(); ~MeshRenderer() override;
// Retrieve a mesh by name stored within a static QHash // Retrieve a mesh by name stored within a static QHash
static MeshRenderer * getInstance(const QString & name); static MeshRenderer * getInstance(const QString & name);
@ -38,43 +64,67 @@ namespace Qtk {
void setDrawType(int drawType) { mDrawType = drawType; } void setDrawType(int drawType) { mDrawType = drawType; }
// Shader settings // Shader settings
inline void setShaderVertex(const std::string & vert) { mVertexShader = vert;} inline void setShaderVertex(const std::string & vert) {
inline void setShaderFragment(const std::string & frag) mVertexShader = vert;
{ mFragmentShader = frag;} }
inline void setShaderFragment(const std::string & frag) {
mFragmentShader = frag;
}
void setShaders(const std::string & vert, const std::string & frag); void setShaders(const std::string & vert, const std::string & frag);
template <typename T> template <typename T> inline void setUniform(int location, T value) {
inline void setUniform(int location, T value) ShaderBindScope lock(&mProgram, mBound);
{ mProgram.setUniformValue(location, value);} mProgram.setUniformValue(location, value);
}
template <typename T> template <typename T>
inline void setUniform(const char * location, T value) inline void setUniform(const char * location, T value) {
{ mProgram.setUniformValue(location, value);} ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(location, value);
}
// Set MVP matrix using this Object's transform // Set MVP matrix using this Object's transform
// + View and projection provided by MainWidget static members // + View and projection provided by MainWidget static members
void setUniformMVP(const char * model="uModel", const char * view="uView", void setUniformMVP(
const char * model = "uModel", const char * view = "uView",
const char * projection = "uProjection"); 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 // These functions modify data stored in a VBO
// + After calling them, the VBO will need to be reallocated // + After calling them, the VBO will need to be reallocated
void setShape(const Shape & value) override; void setShape(const Shape & value) override;
void setColor(const QVector3D & color); 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 // Static QHash of all mesh objects within the scene
typedef QHash<QString, MeshRenderer *> MeshManager; typedef QHash<QString, MeshRenderer *> MeshManager;
private: private:
static MeshManager sInstances; static MeshManager sInstances;
int mDrawType; int mDrawType {};
bool mHasTexture; std::string mVertexShader {}, mFragmentShader {};
std::string mVertexShader, mFragmentShader;
}; };
} } // namespace Qtk
#endif // QTK_MESHRENDERER_H #endif // QTK_MESHRENDERER_H

View File

@ -19,18 +19,15 @@ using namespace Qtk;
Model::ModelManager Model::mManager; Model::ModelManager Model::mManager;
// Static function to access ModelManager for getting Models by name // 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]; return mManager[name];
} }
/******************************************************************************* /*******************************************************************************
* ModelMesh Private Member Functions * ModelMesh Private Member Functions
******************************************************************************/ ******************************************************************************/
void ModelMesh::initMesh(const char * vert, const char * frag) void ModelMesh::initMesh(const char * vert, const char * frag) {
{
initializeOpenGLFunctions(); initializeOpenGLFunctions();
// Create VAO, VBO, EBO // Create VAO, VBO, EBO
@ -44,14 +41,12 @@ void ModelMesh::initMesh(const char * vert, const char * frag)
mVBO->setUsagePattern(QOpenGLBuffer::StaticDraw); mVBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
mVBO->bind(); mVBO->bind();
mVBO->allocate(mVertices.data(), mVBO->allocate(mVertices.data(), mVertices.size() * sizeof(mVertices[0]));
mVertices.size() * sizeof(mVertices[0]));
// Allocate EBO // Allocate EBO
mEBO->setUsagePattern(QOpenGLBuffer::StaticDraw); mEBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
mEBO->bind(); mEBO->bind();
mEBO->allocate(mIndices.data(), mEBO->allocate(mIndices.data(), mIndices.size() * sizeof(mIndices[0]));
mIndices.size() * sizeof(mIndices[0]));
mEBO->release(); mEBO->release();
// Load and link shaders // Load and link shaders
@ -62,46 +57,40 @@ void ModelMesh::initMesh(const char * vert, const char * frag)
// Positions // Positions
mProgram->enableAttributeArray(0); mProgram->enableAttributeArray(0);
mProgram->setAttributeBuffer(0, GL_FLOAT, mProgram->setAttributeBuffer(
offsetof(ModelVertex, mPosition), 3, 0, GL_FLOAT, offsetof(ModelVertex, mPosition), 3, sizeof(ModelVertex));
sizeof(ModelVertex));
// Normals // Normals
mProgram->enableAttributeArray(1); mProgram->enableAttributeArray(1);
mProgram->setAttributeBuffer(1, GL_FLOAT, mProgram->setAttributeBuffer(
offsetof(ModelVertex, mNormal), 3, 1, GL_FLOAT, offsetof(ModelVertex, mNormal), 3, sizeof(ModelVertex));
sizeof(ModelVertex));
// Texture Coordinates // Texture Coordinates
mProgram->enableAttributeArray(2); mProgram->enableAttributeArray(2);
mProgram->setAttributeBuffer(2, GL_FLOAT, mProgram->setAttributeBuffer(
offsetof(ModelVertex, mTextureCoord), 2, 2, GL_FLOAT, offsetof(ModelVertex, mTextureCoord), 2,
sizeof(ModelVertex)); sizeof(ModelVertex));
// Vertex tangents // Vertex tangents
mProgram->enableAttributeArray(3); mProgram->enableAttributeArray(3);
mProgram->setAttributeBuffer(3, GL_FLOAT, mProgram->setAttributeBuffer(
offsetof(ModelVertex, mTangent), 3, 3, GL_FLOAT, offsetof(ModelVertex, mTangent), 3, sizeof(ModelVertex));
sizeof(ModelVertex));
// Vertex bitangents // Vertex bitangents
mProgram->enableAttributeArray(4); mProgram->enableAttributeArray(4);
mProgram->setAttributeBuffer(4, GL_FLOAT, mProgram->setAttributeBuffer(
offsetof(ModelVertex, mBitangent), 3, 4, GL_FLOAT, offsetof(ModelVertex, mBitangent), 3, sizeof(ModelVertex));
sizeof(ModelVertex));
mProgram->release(); mProgram->release();
mVBO->release(); mVBO->release();
mVAO->release(); mVAO->release();
} }
/******************************************************************************* /*******************************************************************************
* ModelMesh Public Member Functions * ModelMesh Public Member Functions
******************************************************************************/ ******************************************************************************/
void ModelMesh::draw(QOpenGLShaderProgram & shader) void ModelMesh::draw(QOpenGLShaderProgram & shader) {
{
mVAO->bind(); mVAO->bind();
// Bind shader // Bind shader
shader.bind(); shader.bind();
@ -124,17 +113,23 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader)
// Specular: material.texture_specular1, material.texture_specular2, ... // Specular: material.texture_specular1, material.texture_specular2, ...
std::string number; std::string number;
std::string name = mTextures[i].mType; std::string name = mTextures[i].mType;
if (name == "texture_diffuse") number = std::to_string(diffuseCount++); if(name == "texture_diffuse") {
if (name == "texture_specular") number = std::to_string(specularCount++); number = std::to_string(diffuseCount++);
if (name == "texture_normal") number = std::to_string(normalCount++); }
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 // Set the uniform to track this texture ID using our naming convention
shader.setUniformValue((name + number).c_str(), i); shader.setUniformValue((name + number).c_str(), i);
} }
// Draw the mesh // Draw the mesh
glDrawElements(GL_TRIANGLES, mIndices.size(), glDrawElements(
GL_UNSIGNED_INT, mIndices.data()); GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
// Release shader, textures // Release shader, textures
for(const auto & texture : mTextures) { for(const auto & texture : mTextures) {
@ -145,29 +140,25 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader)
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
/******************************************************************************* /*******************************************************************************
* Model Public Member Functions * Model Public Member Functions
******************************************************************************/ ******************************************************************************/
void Model::draw() void Model::draw() {
{ for(auto & mMeshe : mMeshes) {
for (GLuint i = 0; i < mMeshes.size(); i++) { mMeshe.mTransform = mTransform;
mMeshes[i].mTransform = mTransform; mMeshe.draw();
mMeshes[i].draw();
} }
} }
void Model::draw(QOpenGLShaderProgram & shader) void Model::draw(QOpenGLShaderProgram & shader) {
{ for(auto & mMeshe : mMeshes) {
for (GLuint i = 0; i < mMeshes.size(); i++) { mMeshe.mTransform = mTransform;
mMeshes[i].mTransform = mTransform; mMeshe.draw(shader);
mMeshes[i].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; bool modified = false;
std::string fullPath = mDirectory + '/' + fileName; std::string fullPath = mDirectory + '/' + fileName;
for(auto & texture : mTexturesLoaded) { for(auto & texture : mTexturesLoaded) {
@ -175,7 +166,7 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
texture.mTexture->destroy(); texture.mTexture->destroy();
texture.mTexture->create(); texture.mTexture->create();
texture.mTexture->setData( texture.mTexture->setData(
*Texture::initImage(fullPath.c_str(), flipX, flipY)); *OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY));
modified = true; modified = true;
} }
} }
@ -183,10 +174,8 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
qDebug() << "Attempt to flip texture that doesn't exist: " qDebug() << "Attempt to flip texture that doesn't exist: "
<< fullPath.c_str() << "\n"; << fullPath.c_str() << "\n";
} }
} }
/******************************************************************************* /*******************************************************************************
* Model Private Member Functions * Model Private Member Functions
******************************************************************************/ ******************************************************************************/
@ -206,8 +195,7 @@ 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 * @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; Assimp::Importer import;
// JIC a relative path was used, get the absolute file path // JIC a relative path was used, get the absolute file path
@ -219,15 +207,10 @@ void Model::loadModel(const std::string & path)
// Import the model, converting non-triangular geometry to triangles // Import the model, converting non-triangular geometry to triangles
// + And flipping texture UVs, etc.. // + And flipping texture UVs, etc..
// Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html // Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
const aiScene * scene = const aiScene * scene = import.ReadFile(
import.ReadFile(mDirectory, aiProcess_Triangulate mDirectory, aiProcess_Triangulate | aiProcess_FlipUVs
| aiProcess_FlipUVs | aiProcess_GenSmoothNormals | aiProcess_CalcTangentSpace
| aiProcess_GenSmoothNormals | aiProcess_OptimizeMeshes | aiProcess_SplitLargeMeshes);
| aiProcess_CalcTangentSpace
| aiProcess_OptimizeMeshes
| aiProcess_SplitLargeMeshes
);
// If there were errors, print and return // 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) {
@ -250,8 +233,7 @@ void Model::loadModel(const std::string & path)
mManager.insert(mName, this); 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 // 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]]; aiMesh * mesh = scene->mMeshes[node->mMeshes[i]];
@ -264,8 +246,7 @@ void Model::processNode(aiNode * node, const aiScene * scene)
} }
} }
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
{
ModelMesh::Vertices vertices; ModelMesh::Vertices vertices;
ModelMesh::Indices indices; ModelMesh::Indices indices;
ModelMesh::Textures textures; ModelMesh::Textures textures;
@ -313,8 +294,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
vector3D.setY(mesh->mBitangents[i].y); vector3D.setY(mesh->mBitangents[i].y);
vector3D.setZ(mesh->mBitangents[i].z); vector3D.setZ(mesh->mBitangents[i].z);
vertex.mBitangent = vector3D; vertex.mBitangent = vector3D;
} } else {
else {
vertex.mTextureCoord = {0.0f, 0.0f}; vertex.mTextureCoord = {0.0f, 0.0f};
} }
@ -337,48 +317,42 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
aiMaterial * material = scene->mMaterials[mesh->mMaterialIndex]; aiMaterial * material = scene->mMaterials[mesh->mMaterialIndex];
// Get all diffuse textures from the material // Get all diffuse textures from the material
ModelMesh::Textures diffuseMaps = ModelMesh::Textures diffuseMaps = loadMaterialTextures(
loadMaterialTextures(material, aiTextureType_DIFFUSE, material, aiTextureType_DIFFUSE, "texture_diffuse");
"texture_diffuse");
// Insert all diffuse textures found into our textures container // Insert all diffuse textures found into our textures container
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// Get all specular textures from the material // Get all specular textures from the material
ModelMesh::Textures specularMaps = ModelMesh::Textures specularMaps = loadMaterialTextures(
loadMaterialTextures(material, aiTextureType_SPECULAR, material, aiTextureType_SPECULAR, "texture_specular");
"texture_specular");
// Insert all specular textures found into our textures container // Insert all specular textures found into our textures container
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
// Get all normal textures from the material // Get all normal textures from the material
ModelMesh::Textures normalMaps = ModelMesh::Textures normalMaps =
loadMaterialTextures(material, aiTextureType_HEIGHT, loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
"texture_normal");
// Insert all normal maps found into our textures container // Insert all normal maps found into our textures container
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
} }
return ModelMesh(vertices, indices, textures, return {vertices, indices, textures, mVertexShader, mFragmentShader};
mVertexShader, mFragmentShader);
} }
ModelMesh::Textures Model::loadMaterialTextures( ModelMesh::Textures Model::loadMaterialTextures(
aiMaterial * mat, aiTextureType type, const std::string & typeName) aiMaterial * mat, aiTextureType type, const std::string & typeName) {
{
ModelMesh::Textures textures; 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 // Call GetTexture to get the name of the texture file to load
aiString fileName; aiString fileName;
mat->GetTexture(type, i, &fileName); mat->GetTexture(type, i, &fileName);
// Check if we have already loaded this texture // Check if we have already loaded this texture
bool skip = false; 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 the path to the texture already exists in m_texturesLoaded, skip it
if (std::strcmp(mTexturesLoaded[j].mPath.data(), fileName.C_Str()) == 0) { if(std::strcmp(j.mPath.data(), fileName.C_Str()) == 0) {
textures.push_back(mTexturesLoaded[j]); textures.push_back(j);
// If we have loaded the texture, do not load it again // If we have loaded the texture, do not load it again
skip = true; skip = true;
break; break;
@ -388,9 +362,9 @@ ModelMesh::Textures Model::loadMaterialTextures(
// If the texture has not yet been loaded // If the texture has not yet been loaded
if(!skip) { if(!skip) {
ModelTexture texture; ModelTexture texture;
texture.mTexture = Texture::initTexture2D( texture.mTexture = OpenGLTextureFactory::initTexture2D(
std::string(mDirectory + '/' + fileName.C_Str()).c_str(), std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false,
false, false); false);
texture.mID = texture.mTexture->textureId(); texture.mID = texture.mTexture->textureId();
texture.mType = typeName; texture.mType = typeName;
texture.mPath = fileName.C_Str(); 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 // Add the texture to the loaded textures to avoid loading it twice
mTexturesLoaded.push_back(texture); mTexturesLoaded.push_back(texture);
} }
} }
// Return the resulting textures // Return the resulting textures
return textures; return textures;
} }
void Model::sortModels() void Model::sortModels() {
{
auto cameraPos = Scene::Camera().transform(); auto cameraPos = Scene::Camera().transform();
auto cameraDistance = [&cameraPos](const ModelMesh &a, const ModelMesh &b) auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) {
{ // Sort by the first vertex position in the model
// Sort by the first vertex position, since all transforms will be the same return (cameraPos.getTranslation().distanceToPoint(
return (cameraPos.translation().distanceToPoint(a.mVertices[0].mPosition)) a.mVertices[0].mPosition))
< (cameraPos.translation().distanceToPoint(b.mVertices[0].mPosition)); < (cameraPos.getTranslation().distanceToPoint(
b.mVertices[0].mPosition));
}; };
std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance); std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance);
} }

View File

@ -18,11 +18,12 @@
#include <QOpenGLVertexArrayObject> #include <QOpenGLVertexArrayObject>
// Assimp // Assimp
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h> #include <assimp/postprocess.h>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/Importer.hpp>
// QTK // QTK
#include <object.h>
#include <qtkapi.h> #include <qtkapi.h>
#include <transform3D.h> #include <transform3D.h>
@ -36,10 +37,10 @@ namespace Qtk {
}; };
struct QTKAPI ModelTexture { struct QTKAPI ModelTexture {
GLuint mID; GLuint mID {};
QOpenGLTexture * mTexture; QOpenGLTexture * mTexture {};
std::string mType; std::string mType {};
std::string mPath; std::string mPath {};
}; };
class Model; class Model;
@ -52,18 +53,20 @@ namespace Qtk {
typedef std::vector<ModelTexture> Textures; typedef std::vector<ModelTexture> Textures;
// Constructors, Destructors // Constructors, Destructors
ModelMesh(Vertices vertices, Indices indices, Textures textures, ModelMesh(
Vertices vertices, Indices indices, Textures textures,
const char * vertexShader = ":/model-basic.vert", const char * vertexShader = ":/model-basic.vert",
const char * fragmentShader=":/model-basic.frag") const char * fragmentShader = ":/model-basic.frag") :
: mProgram(new QOpenGLShaderProgram), mProgram(new QOpenGLShaderProgram),
mVAO(new QOpenGLVertexArrayObject), mVAO(new QOpenGLVertexArrayObject),
mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)), mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)),
mEBO(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer)), mEBO(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer)),
mVertices(std::move(vertices)), mVertices(std::move(vertices)), mIndices(std::move(indices)),
mIndices(std::move(indices)), mTextures(std::move(textures)) {
mTextures(std::move(textures)) initMesh(vertexShader, fragmentShader);
{ initMesh(vertexShader, fragmentShader);} }
~ModelMesh() {}
~ModelMesh() = default;
private: private:
void initMesh(const char * vert, const char * frag); void initMesh(const char * vert, const char * frag);
@ -75,37 +78,38 @@ namespace Qtk {
public: public:
inline void draw() { draw(*mProgram); } inline void draw() { draw(*mProgram); }
void draw(QOpenGLShaderProgram & shader); void draw(QOpenGLShaderProgram & shader);
// ModelMesh Public Members // ModelMesh Public Members
Vertices mVertices; Vertices mVertices {};
Indices mIndices; Indices mIndices {};
Textures mTextures; Textures mTextures {};
Transform3D mTransform; Transform3D mTransform;
}; };
class QTKAPI Model : public QObject { class QTKAPI Model : public QObject {
Q_OBJECT Q_OBJECT
public: public:
inline Model(const char * name, const char * path, inline Model(
const char * name, const char * path,
const char * vertexShader = ":/model-basic.vert", const char * vertexShader = ":/model-basic.vert",
const char * fragmentShader=":/model-basic.frag") const char * fragmentShader = ":/model-basic.frag") :
: mName(name), mVertexShader(vertexShader), mName(name),
mFragmentShader(fragmentShader) mVertexShader(vertexShader), mFragmentShader(fragmentShader) {
{ loadModel(path);} loadModel(path);
inline ~Model() { mManager.remove(mName);} }
inline ~Model() override { mManager.remove(mName); }
void draw(); void draw();
void draw(QOpenGLShaderProgram & shader); void draw(QOpenGLShaderProgram & shader);
void flipTexture(const std::string & fileName, void flipTexture(
bool flipX=false, bool flipY=true); const std::string & fileName, bool flipX = false, bool flipY = true);
template <typename T> template <typename T> void setUniform(const char * location, T value) {
void setUniform(const char * location, T value)
{
for(auto & mesh : mMeshes) { for(auto & mesh : mMeshes) {
mesh.mProgram->bind(); mesh.mProgram->bind();
@ -120,22 +124,23 @@ namespace Qtk {
static Model * getInstance(const char * name); static Model * getInstance(const char * name);
typedef QHash<QString, Model *> ModelManager; typedef QHash<QString, Model *> ModelManager;
private: private:
static ModelManager mManager; static ModelManager mManager;
void loadModel(const std::string & path); void loadModel(const std::string & path);
void processNode(aiNode * node, const aiScene * scene); void processNode(aiNode * node, const aiScene * scene);
ModelMesh processMesh(aiMesh * mesh, const aiScene * scene); ModelMesh processMesh(aiMesh * mesh, const aiScene * scene);
ModelMesh::Textures loadMaterialTextures(aiMaterial * mat, aiTextureType type, ModelMesh::Textures loadMaterialTextures(
const std::string & typeName); aiMaterial * mat, aiTextureType type, const std::string & typeName);
void sortModels(); void sortModels();
// Model Private Members // Model Private Members
ModelMesh::Textures mTexturesLoaded; ModelMesh::Textures mTexturesLoaded {};
std::vector<ModelMesh> mMeshes; std::vector<ModelMesh> mMeshes {};
std::string mDirectory; std::string mDirectory {};
const char *mVertexShader, *mFragmentShader, *mName; const char *mVertexShader, *mFragmentShader, *mName;
}; };
} } // namespace Qtk
#endif // QTK_MODEL_H #endif // QTK_MODEL_H

View File

@ -15,6 +15,7 @@
#include <mesh.h> #include <mesh.h>
#include <qtkapi.h> #include <qtkapi.h>
#include <texture.h>
namespace Qtk { namespace Qtk {
class QTKAPI Object : public QObject { class QTKAPI Object : public QObject {
@ -22,42 +23,93 @@ namespace Qtk {
public: public:
friend MeshRenderer; friend MeshRenderer;
// Initialize an object with no shape data assigned // Initialize an object with no shape data assigned
Object(const char * name) explicit Object(const char * name) :
: mName(name), mVBO(QOpenGLBuffer::VertexBuffer) mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false) {}
{ }
// Initialize an object with shape data assigned // Initialize an object with shape data assigned
Object(const char * name, const ShapeBase & shape) Object(const char * name, const ShapeBase & shape) :
: mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape) mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape),
{ } mBound(false) {}
~Object() {} ~Object() override = default;
inline const Vertices & vertices() { return mShape.mVertices;} inline const Colors & getColors() { return mShape.mColors; }
inline const Indices & indices() { return mShape.mIndices;}
inline const Colors & colors() { return mShape.mColors;} inline const Indices & getIndexData() { return mShape.mIndices; }
inline const TexCoords & texCoords() { return mShape.mTexCoords;}
inline const Normals & normals() { return mShape.mNormals;} inline const Normals & getNormals() { return mShape.mNormals; }
inline QOpenGLTexture & texture() const { return *mTexture;}
[[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 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; } 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; QOpenGLBuffer mVBO, mNBO;
QOpenGLVertexArrayObject mVAO; QOpenGLVertexArrayObject mVAO;
QOpenGLShaderProgram mProgram;
Transform3D mTransform; Transform3D mTransform;
Shape mShape; Shape mShape;
Texture mTexture;
const char * mName; const char * mName;
bool mBound;
private: private:
QOpenGLTexture * mTexture; virtual inline void setTexture(QOpenGLTexture * value) {
}; mTexture.setTexture(value);
} }
QOpenGLShaderProgram mProgram;
};
} // namespace Qtk
#endif // QTK_OBJECT_H #endif // QTK_OBJECT_H

View File

@ -8,10 +8,10 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <input.h>
#include <qtkwidget.h>
#include <mesh.h>
#include <abstractscene.h> #include <abstractscene.h>
#include <input.h>
#include <mesh.h>
#include <qtkwidget.h>
using namespace Qtk; using namespace Qtk;
@ -19,57 +19,50 @@ using namespace Qtk;
* Constructors, Destructors * Constructors, Destructors
******************************************************************************/ ******************************************************************************/
QtkWidget::QtkWidget() : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) QtkWidget::QtkWidget() : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
{
initializeWidget(); initializeWidget();
} }
// Constructor for using this widget in QtDesigner // Constructor for using this widget in QtDesigner
QtkWidget::QtkWidget(QWidget *parent) : QOpenGLWidget(parent), QtkWidget::QtkWidget(QWidget * parent) :
mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) QOpenGLWidget(parent), mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
{
initializeWidget(); initializeWidget();
} }
QtkWidget::QtkWidget(const QSurfaceFormat &format) QtkWidget::QtkWidget(const QSurfaceFormat & format) :
: mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
{
setFormat(format); setFormat(format);
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
} }
QtkWidget::~QtkWidget() QtkWidget::~QtkWidget() {
{
makeCurrent(); makeCurrent();
teardownGL(); teardownGL();
} }
/******************************************************************************* /*******************************************************************************
* Private Member Functions * Private Member Functions
******************************************************************************/ ******************************************************************************/
void QtkWidget::teardownGL() void QtkWidget::teardownGL() {
{
// Nothing to teardown yet... // Nothing to teardown yet...
} }
/******************************************************************************* /*******************************************************************************
* Inherited Virtual Member Functions * Inherited Virtual Member Functions
******************************************************************************/ ******************************************************************************/
void QtkWidget::paintGL() void QtkWidget::paintGL() {
{
// Clear buffers // Clear buffers
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// Draw the scene first, since it handles drawing our skybox // 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(); initializeOpenGLFunctions();
// Connect the frameSwapped signal to call the update() function // Connect the frameSwapped signal to call the update() function
connect(this, SIGNAL(frameSwapped()), this, SLOT(update())); connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
@ -79,8 +72,9 @@ void QtkWidget::initializeGL()
mDebugLogger = new QOpenGLDebugLogger(this); mDebugLogger = new QOpenGLDebugLogger(this);
if(mDebugLogger->initialize()) { if(mDebugLogger->initialize()) {
qDebug() << "GL_DEBUG Debug Logger" << mDebugLogger << "\n"; qDebug() << "GL_DEBUG Debug Logger" << mDebugLogger << "\n";
connect(mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), connect(
this, SLOT(messageLogged(QOpenGLDebugMessage))); mDebugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this,
SLOT(messageLogged(QOpenGLDebugMessage)));
mDebugLogger->startLogging(); mDebugLogger->startLogging();
} }
#endif // QTK_DEBUG #endif // QTK_DEBUG
@ -98,35 +92,31 @@ void QtkWidget::initializeGL()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 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().setToIdentity();
Scene::Projection().perspective(45.0f, Scene::Projection().perspective(
float(width) / float(height), 45.0f, float(width) / float(height), 0.1f, 1000.0f);
0.1f, 1000.0f);
} }
/******************************************************************************* /*******************************************************************************
* Protected Slots * Protected Slots
******************************************************************************/ ******************************************************************************/
void QtkWidget::update() void QtkWidget::update() {
{
updateCameraInput(); updateCameraInput();
if (mScene != Q_NULLPTR) mScene->update(); if(mScene != Q_NULLPTR) {
mScene->update();
}
QWidget::update(); QWidget::update();
} }
void QtkWidget::messageLogged(const QOpenGLDebugMessage &msg) void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
{
QString error; QString error;
// Format based on severity // Format based on severity
switch (msg.severity()) switch(msg.severity()) {
{
case QOpenGLDebugMessage::NotificationSeverity: case QOpenGLDebugMessage::NotificationSeverity:
error += "--"; error += "--";
break; break;
@ -144,9 +134,11 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage &msg)
error += " ("; error += " (";
// Format based on source // Format based on source
#define CASE(c) case QOpenGLDebugMessage::c: error += #c; break #define CASE(c) \
switch (msg.source()) case QOpenGLDebugMessage::c: \
{ error += #c; \
break
switch(msg.source()) {
CASE(APISource); CASE(APISource);
CASE(WindowSystemSource); CASE(WindowSystemSource);
CASE(ShaderCompilerSource); CASE(ShaderCompilerSource);
@ -160,9 +152,11 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage &msg)
error += " : "; error += " : ";
// Format based on type // Format based on type
#define CASE(c) case QOpenGLDebugMessage::c: error += #c; break #define CASE(c) \
switch (msg.type()) case QOpenGLDebugMessage::c: \
{ error += #c; \
break
switch(msg.type()) {
CASE(InvalidType); CASE(InvalidType);
CASE(ErrorType); CASE(ErrorType);
CASE(DeprecatedBehaviorType); CASE(DeprecatedBehaviorType);
@ -180,13 +174,11 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage &msg)
qDebug() << qPrintable(error) << "\n" << qPrintable(msg.message()) << "\n"; qDebug() << qPrintable(error) << "\n" << qPrintable(msg.message()) << "\n";
} }
/******************************************************************************* /*******************************************************************************
* Protected Helpers * Protected Helpers
******************************************************************************/ ******************************************************************************/
void QtkWidget::keyPressEvent(QKeyEvent *event) void QtkWidget::keyPressEvent(QKeyEvent * event) {
{
if(event->isAutoRepeat()) { if(event->isAutoRepeat()) {
// Do not repeat input while a key is held down // Do not repeat input while a key is held down
event->ignore(); event->ignore();
@ -195,8 +187,7 @@ void QtkWidget::keyPressEvent(QKeyEvent *event)
} }
} }
void QtkWidget::keyReleaseEvent(QKeyEvent *event) void QtkWidget::keyReleaseEvent(QKeyEvent * event) {
{
if(event->isAutoRepeat()) { if(event->isAutoRepeat()) {
event->ignore(); event->ignore();
} else { } else {
@ -204,23 +195,19 @@ void QtkWidget::keyReleaseEvent(QKeyEvent *event)
} }
} }
void QtkWidget::mousePressEvent(QMouseEvent *event) void QtkWidget::mousePressEvent(QMouseEvent * event) {
{
Input::registerMousePress(event->button()); Input::registerMousePress(event->button());
} }
void QtkWidget::mouseReleaseEvent(QMouseEvent *event) void QtkWidget::mouseReleaseEvent(QMouseEvent * event) {
{
Input::registerMouseRelease(event->button()); Input::registerMouseRelease(event->button());
} }
/******************************************************************************* /*******************************************************************************
* Private Helpers * Private Helpers
******************************************************************************/ ******************************************************************************/
void QtkWidget::initializeWidget() void QtkWidget::initializeWidget() {
{
QSurfaceFormat format; QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL); format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile); format.setProfile(QSurfaceFormat::CoreProfile);
@ -237,8 +224,7 @@ void QtkWidget::initializeWidget()
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
} }
void QtkWidget::printContextInformation() void QtkWidget::printContextInformation() {
{
QString glType; QString glType;
QString glVersion; QString glVersion;
QString glProfile; QString glProfile;
@ -246,17 +232,17 @@ void QtkWidget::printContextInformation()
QString glVendor; QString glVendor;
QString glRenderer; QString glRenderer;
// Get Version Information // Get Version Information
glType = (context()->isOpenGLES()) ? "OpenGL ES" : "OpenGL"; glType = (context()->isOpenGLES()) ? "OpenGL ES" : "OpenGL";
glVersion = reinterpret_cast<const char *>(glGetString(GL_VERSION)); glVersion = reinterpret_cast<const char *>(glGetString(GL_VERSION));
glVendor = glVendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
reinterpret_cast<const char *>(glGetString(GL_VENDOR)); glRenderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
glRenderer =
reinterpret_cast<const char *>(glGetString(GL_RENDERER));
// Get Profile Information // Get Profile Information
#define CASE(c) case QSurfaceFormat::c: glProfile = #c; break #define CASE(c) \
case QSurfaceFormat::c: \
glProfile = #c; \
break
switch(format().profile()) { switch(format().profile()) {
CASE(NoProfile); CASE(NoProfile);
CASE(CoreProfile); CASE(CoreProfile);
@ -269,12 +255,9 @@ void QtkWidget::printContextInformation()
<< qPrintable(glProfile) << ")" << qPrintable(glProfile) << ")"
<< "\nOpenGL Vendor: " << qPrintable(glVendor) << "\nOpenGL Vendor: " << qPrintable(glVendor)
<< "\nRendering Device: " << qPrintable(glRenderer) << "\n"; << "\nRendering Device: " << qPrintable(glRenderer) << "\n";
} }
void QtkWidget::updateCameraInput() void QtkWidget::updateCameraInput() {
{
Input::update(); Input::update();
// Camera Transformation // Camera Transformation
if(Input::buttonPressed(Qt::RightButton)) { if(Input::buttonPressed(Qt::RightButton)) {
@ -282,10 +265,10 @@ void QtkWidget::updateCameraInput()
static const float rotSpeed = 0.5f; static const float rotSpeed = 0.5f;
// Handle rotations // Handle rotations
Scene::Camera().transform().rotate(-rotSpeed * Input::mouseDelta().x(), Scene::Camera().transform().rotate(
Camera3D::LocalUp); -rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp);
Scene::Camera().transform().rotate(-rotSpeed * Input::mouseDelta().y(), Scene::Camera().transform().rotate(
Scene::Camera().right()); -rotSpeed * Input::mouseDelta().y(), Scene::Camera().right());
// Handle translations // Handle translations
QVector3D translation; QVector3D translation;

View File

@ -15,12 +15,11 @@
#include <QOpenGLFunctions> #include <QOpenGLFunctions>
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <qtkapi.h>
#include <abstractscene.h> #include <abstractscene.h>
#include <qtkapi.h>
namespace Qtk { namespace Qtk {
class QTKAPI QtkWidget : public QOpenGLWidget, class QTKAPI QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
protected QOpenGLFunctions {
Q_OBJECT; Q_OBJECT;
public: public:
@ -40,28 +39,30 @@ namespace Qtk {
void resizeGL(int width, int height) override; void resizeGL(int width, int height) override;
inline Qtk::Scene * getScene() { return mScene; } inline Qtk::Scene * getScene() { return mScene; }
inline void setScene(Qtk::Scene * scene) { inline void setScene(Qtk::Scene * scene) {
if (mScene != Q_NULLPTR) delete mScene; delete mScene;
mScene = scene; mScene = scene;
} }
protected slots: protected slots:
void update(); void update();
#ifdef QTK_DEBUG #ifdef QTK_DEBUG
void messageLogged(const QOpenGLDebugMessage &msg); static void messageLogged(const QOpenGLDebugMessage & msg);
#endif #endif
// Protected Helpers // Protected Helpers
protected: protected:
void keyPressEvent(QKeyEvent *event); void keyPressEvent(QKeyEvent * event) override;
void keyReleaseEvent(QKeyEvent *event); void keyReleaseEvent(QKeyEvent * event) override;
void mousePressEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent * event) override;
void mouseReleaseEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent * event) override;
private: private:
// Private helpers // Private helpers
void initializeWidget(); void initializeWidget();
void updateCameraInput(); static void updateCameraInput();
Qtk::Scene * mScene; Qtk::Scene * mScene;
#ifdef QTK_DEBUG #ifdef QTK_DEBUG
@ -69,6 +70,6 @@ namespace Qtk {
QOpenGLDebugLogger * mDebugLogger; QOpenGLDebugLogger * mDebugLogger;
#endif #endif
}; };
} } // namespace Qtk
#endif // QTK_QTKWIDGET_H #endif // QTK_QTKWIDGET_H

View File

@ -12,50 +12,50 @@
using namespace Qtk; using namespace Qtk;
Skybox::Skybox(std::string right, std::string top, std::string front, Skybox::Skybox(
std::string left, std::string bottom, std::string back, const std::string & right, const std::string & top,
const std::string & name) const std::string & front, const std::string & left,
: mVBO(QOpenGLBuffer::VertexBuffer), const std::string & bottom, const std::string & back,
mVertices(Cube(QTK_DRAW_ELEMENTS).vertices()), const std::string & name) :
mIndices(Cube(QTK_DRAW_ELEMENTS).indices()) mVBO(QOpenGLBuffer::VertexBuffer),
{ mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData()) {
init(); init();
mCubeMap = Texture::initCubeMap( mTexture.setCubeMap(
QImage(right.c_str()).mirrored(), QImage(top.c_str()), QImage(right.c_str()).mirrored(), QImage(top.c_str()),
QImage(front.c_str()), QImage(left.c_str()), QImage(front.c_str()), QImage(left.c_str()), QImage(bottom.c_str()),
QImage(bottom.c_str()), QImage(back.c_str())); QImage(back.c_str()));
} }
Skybox::Skybox(std::string name) Skybox::Skybox(const std::string & name) :
: Skybox(":/right.png", ":/top.png", ":/front.png", Skybox(
":/left.png", ":/bottom.png", ":/back.png", ":/right.png", ":/top.png", ":/front.png", ":/left.png", ":/bottom.png",
name) ":/back.png", name) {}
{}
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name)
: mCubeMap(cubeMap) { init();}
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) :
mTexture(cubeMap) {
init();
}
/******************************************************************************* /*******************************************************************************
* Public Member Functions * Public Member Functions
******************************************************************************/ ******************************************************************************/
void Skybox::draw() void Skybox::draw() {
{
glDepthFunc(GL_LEQUAL); glDepthFunc(GL_LEQUAL);
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
mVAO.bind(); mVAO.bind();
mProgram.bind(); mProgram.bind();
mCubeMap->bind(); mTexture.getOpenGLTexture().bind();
mProgram.setUniformValue("uProjectionMatrix", Scene::Projection()); mProgram.setUniformValue("uProjectionMatrix", Scene::Projection());
mProgram.setUniformValue("uViewMatrix", Scene::Camera().toMatrix()); mProgram.setUniformValue("uViewMatrix", Scene::Camera().toMatrix());
mProgram.setUniformValue("uTexture", 0); mProgram.setUniformValue("uTexture", 0);
glDrawElements(GL_TRIANGLES, mIndices.size(), glDrawElements(
GL_UNSIGNED_INT, mIndices.data()); GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
mCubeMap->release(); mTexture.getOpenGLTexture().bind();
mProgram.release(); mProgram.release();
mVAO.release(); mVAO.release();
@ -64,13 +64,11 @@ void Skybox::draw()
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
/******************************************************************************* /*******************************************************************************
* Private Member Functions * Private Member Functions
******************************************************************************/ ******************************************************************************/
void Skybox::init() void Skybox::init() {
{
initializeOpenGLFunctions(); initializeOpenGLFunctions();
// Set up shader program // Set up shader program

View File

@ -18,34 +18,37 @@
#include <camera3d.h> #include <camera3d.h>
#include <mesh.h> #include <mesh.h>
#include <qtkapi.h> #include <qtkapi.h>
#include <texture.h>
namespace Qtk { namespace Qtk {
class QTKAPI Skybox : protected QOpenGLFunctions { class QTKAPI Skybox : protected QOpenGLFunctions {
public: public:
// Delegate this constructor to use default skybox images // Delegate this constructor to use default skybox images
// + This allows creating a skybox with no arguments ( auto s = new Skybox; ) explicit Skybox(const std::string & name = "Skybox");
explicit Skybox(std::string name="Skybox"); explicit Skybox(
explicit Skybox(QOpenGLTexture * cubeMap, const std::string & name="Skybox"); QOpenGLTexture * cubeMap, const std::string & name = "Skybox");
// Constructor, Destructor // Constructor, Destructor
Skybox(std::string right, std::string top, std::string front, Skybox(
std::string left, std::string bottom, std::string back, 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"); const std::string & name = "Skybox");
~Skybox() {}
~Skybox() = default;
void draw(); void draw();
private: private:
void init(); void init();
Vertices mVertices; Vertices mVertices {};
Indices mIndices; Indices mIndices {};
QOpenGLShaderProgram mProgram; QOpenGLShaderProgram mProgram;
QOpenGLVertexArrayObject mVAO; QOpenGLVertexArrayObject mVAO;
QOpenGLBuffer mVBO; QOpenGLBuffer mVBO;
QOpenGLTexture * mCubeMap; Texture mTexture;
}; };
} } // namespace Qtk
#endif // QTK_SKYBOX_H #endif // QTK_SKYBOX_H

View File

@ -8,13 +8,14 @@
#include <QDebug> #include <QDebug>
#include <QImageReader> #include <QImageReader>
#include <utility>
#include <texture.h> #include <texture.h>
using namespace Qtk; 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 // Qt6 limits loaded images to 256MB by default
QImageReader::setAllocationLimit(512); QImageReader::setAllocationLimit(512);
auto loadedImage = new QImage(QImage(image).mirrored(flipX, flipY)); auto loadedImage = new QImage(QImage(image).mirrored(flipX, flipY));
@ -27,46 +28,39 @@ QImage * Texture::initImage(const char * image, bool flipX, bool flipY)
return loadedImage; return loadedImage;
} }
QOpenGLTexture * Texture::initTexture2D(const char * texture, QOpenGLTexture * OpenGLTextureFactory::initTexture2D(
bool flipX, bool flipY) const char * texture, bool flipX, bool flipY) {
{
QImage * image = initImage(texture, flipX, flipY); QImage * image = initImage(texture, flipX, flipY);
auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
newTexture->setData(*image); newTexture->setData(*image);
newTexture->setWrapMode(QOpenGLTexture::Repeat); newTexture->setWrapMode(QOpenGLTexture::Repeat);
newTexture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, newTexture->setMinMagFilters(
QOpenGLTexture::Linear); QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
delete image; delete image;
return newTexture; return newTexture;
} }
QOpenGLTexture * Texture::initCubeMap(const char * tile) QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * tile) {
{ return initCubeMap(
return initCubeMap(QImage(tile), QImage(tile), QImage(tile), QImage(tile), QImage(tile), QImage(tile), QImage(tile),
QImage(tile), QImage(tile), QImage(tile));
QImage(tile), QImage(tile));
} }
QOpenGLTexture * Texture::initCubeMap( QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
const char * right, const char * top, const char * right, const char * top, const char * front, const char * left,
const char * front, const char * left, const char * bottom, const char * back) {
const char * bottom, const char * back) return initCubeMap(
{ QImage(right), QImage(top), QImage(front), QImage(left), QImage(bottom),
return initCubeMap(QImage(right), QImage(top), QImage(back));
QImage(front), QImage(left),
QImage(bottom), QImage(back));
} }
QOpenGLTexture * Texture::initCubeMap( QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
QImage right, QImage top, const QImage & right, const QImage & top, const QImage & front,
QImage front, QImage left, const QImage & left, const QImage & bottom, const QImage & back) {
QImage bottom, QImage back)
{
auto texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap); auto texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
std::vector<QImage> faceTextures = { std::vector<QImage> faceTextures = {std::move(right), std::move(top),
right, top, front, std::move(front), std::move(left),
left, bottom, back std::move(bottom), std::move(back)};
};
// Initialize skybox cubemap texture // Initialize skybox cubemap texture
texture->create(); texture->create();
texture->bind(); texture->bind();
@ -74,8 +68,7 @@ QOpenGLTexture * Texture::initCubeMap(
std::vector<QOpenGLTexture::CubeMapFace> faces = { std::vector<QOpenGLTexture::CubeMapFace> faces = {
QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::CubeMapPositiveY, QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::CubeMapPositiveY,
QOpenGLTexture::CubeMapPositiveZ, QOpenGLTexture::CubeMapNegativeX, QOpenGLTexture::CubeMapPositiveZ, QOpenGLTexture::CubeMapNegativeX,
QOpenGLTexture::CubeMapNegativeY, QOpenGLTexture::CubeMapNegativeZ QOpenGLTexture::CubeMapNegativeY, QOpenGLTexture::CubeMapNegativeZ};
};
int i = 0; int i = 0;
for(const auto & face : faces) { for(const auto & face : faces) {
QImage faceImage(faceTextures[i]); QImage faceImage(faceTextures[i]);
@ -87,13 +80,14 @@ QOpenGLTexture * Texture::initCubeMap(
// On the first iteration, set format and allocate texture storage // 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 // 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->setFormat(QOpenGLTexture::RGBA8_UNorm);
texture->allocateStorage(); texture->allocateStorage();
} }
texture->setData(0, 0, face, texture->setData(
QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, 0, 0, face, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
faceImage.constBits()); faceImage.constBits());
i++; i++;
} }

View File

@ -10,36 +10,108 @@
#define QTOPENGL_TEXTURE_H #define QTOPENGL_TEXTURE_H
#include <QOpenGLTexture> #include <QOpenGLTexture>
#include <utility>
#include <qtkapi.h> #include <qtkapi.h>
namespace Qtk { namespace Qtk {
class QTKAPI Texture { class QTKAPI OpenGLTextureFactory {
public: public:
~Texture() {} ~OpenGLTextureFactory() = default;
// QImage // QImage
static QImage * initImage(const char * image, static QImage * initImage(
bool flipX=false, bool flipY=false); const char * image, bool flipX = false, bool flipY = false);
// 2D Texture // 2D Texture
static QOpenGLTexture * initTexture2D(const char * texture, static QOpenGLTexture * initTexture2D(
bool flipX=false, bool flipY=false); const char * texture, bool flipX = false, bool flipY = false);
// Cube maps // Cube maps
static QOpenGLTexture * initCubeMap(QImage right, QImage top, static QOpenGLTexture * initCubeMap(
QImage front, QImage left, const QImage & right, const QImage & top, const QImage & front,
QImage bottom, QImage back); const QImage & left, const QImage & bottom, const QImage & back);
// Overloads for cube map initialization // Overloads for cube map initialization
static QOpenGLTexture * initCubeMap(const char * tile); static QOpenGLTexture * initCubeMap(const char * tile);
static QOpenGLTexture * initCubeMap(const char * right, const char * top, static QOpenGLTexture * initCubeMap(
const char * front, const char * left, const char * right, const char * top, const char * front,
const char * bottom, const char * back); const char * left, const char * bottom, const char * back);
private: private:
// Private ctor to prevent creating instances of this class // Private ctor to prevent creating instances of this class
Texture() {} OpenGLTextureFactory() = default;
}; };
// 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 #endif // QTOPENGL_TEXTURE_H

View File

@ -19,64 +19,53 @@ const QVector3D Transform3D::LocalRight(1.0f, 0.0f, 0.0f);
* Transformations * Transformations
******************************************************************************/ ******************************************************************************/
void Transform3D::translate(const QVector3D & dt) void Transform3D::translate(const QVector3D & dt) {
{
m_dirty = true; m_dirty = true;
mTranslation += dt; mTranslation += dt;
} }
void Transform3D::scale(const QVector3D & ds) void Transform3D::scale(const QVector3D & ds) {
{
m_dirty = true; m_dirty = true;
mScale *= ds; mScale *= ds;
} }
void Transform3D::rotate(const QQuaternion & dr) void Transform3D::rotate(const QQuaternion & dr) {
{
m_dirty = true; m_dirty = true;
mRotation = dr * mRotation; mRotation = dr * mRotation;
} }
void Transform3D::grow(const QVector3D & ds) void Transform3D::grow(const QVector3D & ds) {
{
m_dirty = true; m_dirty = true;
mScale += ds; mScale += ds;
} }
/******************************************************************************* /*******************************************************************************
* Setters * Setters
******************************************************************************/ ******************************************************************************/
void Transform3D::setTranslation(const QVector3D & t) void Transform3D::setTranslation(const QVector3D & t) {
{
m_dirty = true; m_dirty = true;
mTranslation = t; mTranslation = t;
} }
void Transform3D::setScale(const QVector3D & s) void Transform3D::setScale(const QVector3D & s) {
{
m_dirty = true; m_dirty = true;
mScale = s; mScale = s;
} }
void Transform3D::setRotation(const QQuaternion & r) void Transform3D::setRotation(const QQuaternion & r) {
{
m_dirty = true; m_dirty = true;
mRotation = r; mRotation = r;
} }
/******************************************************************************* /*******************************************************************************
* Accessors * Accessors
******************************************************************************/ ******************************************************************************/
// Produces modelToWorld matrix using current set of transformations // Produces modelToWorld matrix using current set of transformations
// Transformation * rotation * scale = modelToWorld // Transformation * rotation * scale = modelToWorld
const QMatrix4x4 & Transform3D::toMatrix() const QMatrix4x4 & Transform3D::toMatrix() {
{ if(m_dirty) {
if (m_dirty)
{
m_dirty = false; m_dirty = false;
mWorld.setToIdentity(); mWorld.setToIdentity();
mWorld.translate(mTranslation); mWorld.translate(mTranslation);
@ -86,66 +75,63 @@ const QMatrix4x4 & Transform3D::toMatrix()
return mWorld; return mWorld;
} }
/******************************************************************************* /*******************************************************************************
* Queries * Queries
******************************************************************************/ ******************************************************************************/
QVector3D Transform3D::forward() const QVector3D Transform3D::getForward() const {
{
return mRotation.rotatedVector(LocalForward); return mRotation.rotatedVector(LocalForward);
} }
QVector3D Transform3D::up() const QVector3D Transform3D::getUp() const {
{
return mRotation.rotatedVector(LocalUp); return mRotation.rotatedVector(LocalUp);
} }
QVector3D Transform3D::right() const QVector3D Transform3D::getRight() const {
{
return mRotation.rotatedVector(LocalRight); return mRotation.rotatedVector(LocalRight);
while(true) {
int xx;
};
} }
/******************************************************************************* /*******************************************************************************
* QT Streams * QT Streams
******************************************************************************/ ******************************************************************************/
namespace Qtk { namespace Qtk {
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform)
{ QDebug operator<<(QDebug dbg, const Transform3D & transform) {
dbg << "Transform3D\n{\n"; dbg << "Transform3D\n{\n";
dbg << "Position: <" << transform.translation().x() << ", " dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.translation().y() << ", " << transform.getTranslation().y() << ", "
<< transform.translation().z() << ">\n"; << transform.getTranslation().z() << ">\n";
dbg << "Scale: <" << transform.scale().x() << ", " dbg << "Scale: <" << transform.getScale().x() << ", "
<< transform.scale().y() << ", " << transform.getScale().y() << ", " << transform.getScale().z()
<< transform.scale().z() << ">\n"; << ">\n";
dbg << "Rotation: <" << transform.rotation().x() << ", " dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.rotation().y() << ", " << transform.getRotation().y() << ", " << transform.getRotation().z()
<< transform.rotation().z() << " | " << << " | " << transform.getRotation().scalar() << ">\n}";
transform.rotation().scalar() << ">\n}";
return dbg; return dbg;
} }
#endif #endif
#ifndef QT_NO_DATASTREAM #ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Transform3D & transform) QDataStream & operator<<(QDataStream & out, const Transform3D & transform) {
{
out << transform.mTranslation; out << transform.mTranslation;
out << transform.mScale; out << transform.mScale;
out << transform.mRotation; out << transform.mRotation;
return out; return out;
} }
QDataStream & operator>>(QDataStream & in, Transform3D & transform) QDataStream & operator>>(QDataStream & in, Transform3D & transform) {
{
in >> transform.mTranslation; in >> transform.mTranslation;
in >> transform.mScale; in >> transform.mScale;
in >> transform.mRotation; in >> transform.mRotation;
transform.m_dirty = true; transform.m_dirty = true;
return in; return in;
} }
#endif #endif
} } // namespace Qtk

View File

@ -15,81 +15,111 @@
#include <QVector3D> #include <QVector3D>
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
#include <QDebug> #include <QDebug>
#endif #endif
#include <qtkapi.h> #include <qtkapi.h>
namespace Qtk { namespace Qtk {
class QTKAPI Transform3D class QTKAPI Transform3D {
{
public: public:
// Constructors // Constructors
inline Transform3D() : m_dirty(true), inline Transform3D() :
mScale(1.0f, 1.0f, 1.0f), m_dirty(true), mScale(1.0f, 1.0f, 1.0f),
mTranslation(0.0f, 0.0f, 0.0f) {} mTranslation(0.0f, 0.0f, 0.0f) {}
// //
// Transformations // Transformations
void translate(const QVector3D & dt); void translate(const QVector3D & dt);
inline void translate(float dx, float dy, float dz)
{ translate(QVector3D(dx, dy, dz));} inline void translate(float dx, float dy, float dz) {
translate(QVector3D(dx, dy, dz));
}
// Scale object with multiplication // Scale object with multiplication
void scale(const QVector3D & ds); void scale(const QVector3D & ds);
inline void scale(float dx, float dy, float dz)
{ scale(QVector3D(dx, dy, dz));} inline void scale(float dx, float dy, float dz) {
inline void scale(float factor) scale(QVector3D(dx, dy, dz));
{ scale(QVector3D(factor, factor, factor));} }
inline void scale(float factor) {
scale(QVector3D(factor, factor, factor));
}
// Multiplying by a rotation // Multiplying by a rotation
void rotate(const QQuaternion & dr); void rotate(const QQuaternion & dr);
inline void rotate(float angle, const QVector3D & axis)
{ rotate(QQuaternion::fromAxisAndAngle(axis, angle));} inline void rotate(float angle, const QVector3D & axis) {
inline void rotate(float angle, float ax, float ay, float az) rotate(QQuaternion::fromAxisAndAngle(axis, angle));
{ rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));} }
inline void rotate(float angle, float ax, float ay, float az) {
rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
}
// Scale object by addition // Scale object by addition
void grow(const QVector3D & ds); void grow(const QVector3D & ds);
inline void grow(float dx, float dy, float dz)
{ grow(QVector3D(dx, dy, dz));} inline void grow(float dx, float dy, float dz) {
inline void grow(float factor) grow(QVector3D(dx, dy, dz));
{ grow(QVector3D(factor, factor, factor));} }
inline void grow(float factor) {
grow(QVector3D(factor, factor, factor));
}
// //
// Setters // Setters
// Set object position // Set object position
void setTranslation(const QVector3D & t); void setTranslation(const QVector3D & t);
inline void setTranslation(float x, float y, float z)
{ setTranslation(QVector3D(x, y, z));} inline void setTranslation(float x, float y, float z) {
setTranslation(QVector3D(x, y, z));
}
// Set object scale // Set object scale
void setScale(const QVector3D & s); void setScale(const QVector3D & s);
inline void setScale(float x, float y, float z)
{ setScale(QVector3D(x, y, z));} inline void setScale(float x, float y, float z) {
inline void setScale(float k) setScale(QVector3D(x, y, z));
{ setScale(QVector3D(k, k, k));} }
inline void setScale(float k) { setScale(QVector3D(k, k, k)); }
// Set object rotation // Set object rotation
void setRotation(const QQuaternion & r); void setRotation(const QQuaternion & r);
inline void setRotation(float angle, const QVector3D & axis)
{ setRotation(QQuaternion::fromAxisAndAngle(axis, angle));} inline void setRotation(float angle, const QVector3D & axis) {
inline void setRotation(float angle, float ax, float ay, float az) setRotation(QQuaternion::fromAxisAndAngle(axis, angle));
{ setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));} }
inline void setRotation(float angle, float ax, float ay, float az) {
setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
}
// //
// Accessors // Accessors
inline const QVector3D & translation() const { return mTranslation;} [[nodiscard]] inline const QVector3D & getTranslation() const {
inline const QVector3D & scale() const { return mScale; } return mTranslation;
inline const QQuaternion & rotation() const { return mRotation; } }
[[nodiscard]] inline const QVector3D & getScale() const { return mScale; }
[[nodiscard]] inline const QQuaternion & getRotation() const {
return mRotation;
}
const QMatrix4x4 & toMatrix(); const QMatrix4x4 & toMatrix();
QVector3D forward() const; [[nodiscard]] QVector3D getForward() const;
QVector3D up() const; [[nodiscard]] QVector3D getUp() const;
QVector3D right() const; [[nodiscard]] QVector3D getRight() const;
static const QVector3D LocalForward, LocalUp, LocalRight; static const QVector3D LocalForward, LocalUp, LocalRight;
@ -101,14 +131,14 @@ namespace Qtk {
bool m_dirty; bool m_dirty;
#ifndef QT_NO_DATASTREAM #ifndef QT_NO_DATASTREAM
friend QDataStream &operator<<(QDataStream & out, const Transform3D & transform); friend QDataStream & operator<<(
friend QDataStream &operator>>(QDataStream & in, Transform3D & transform); QDataStream & out, const Transform3D & transform);
friend QDataStream & operator>>(
QDataStream & in, Transform3D & transform);
#endif #endif
}; };
// Qt Streams // Qt Streams
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform); QDebug operator<<(QDebug dbg, const Transform3D & transform);
@ -118,7 +148,7 @@ namespace Qtk {
QDataStream & operator<<(QDataStream & out, const Transform3D & transform); QDataStream & operator<<(QDataStream & out, const Transform3D & transform);
QDataStream & operator>>(QDataStream & in, Transform3D & transform); QDataStream & operator>>(QDataStream & in, Transform3D & transform);
#endif #endif
} } // namespace Qtk
Q_DECLARE_TYPEINFO(Qtk::Transform3D, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(Qtk::Transform3D, Q_MOVABLE_TYPE);