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:
push:
@ -32,7 +32,7 @@ jobs:
if: matrix.os == 'windows-latest'
uses: crazy-max/ghaction-chocolatey@v2.0.0
with:
args: install pkgconfiglite
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1
- name: Build Qtk
shell: bash

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_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_compile_options(/wd4131 /wd4127)

View File

@ -1,4 +1,6 @@
# Qtk
[![All Builds](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml)
[![Linting](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml)
Practice project for learning about using OpenGL in Qt widget applications.
Model loader using [Assimp](https://assimp.org/) within a Qt widget application.
@ -20,9 +22,11 @@ This project has been ported to Qt6, which is not yet available in Ubuntu apt re
To run this project, you will *need* to install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for your system.
Be sure to take note of the Qt6 installation directory, as we will need it to correctly set our `CMAKE_PREFIX_PATH` in the next steps.
#### Linux
Once Qt6 is installed, to build and run `qtk` on Ubuntu -
```bash
sudo apt update -y && sudo apt install freeglut3-dev libassimp-dev cmake build-essential git
sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git
git clone https://gitlab.com/shaunrd0/qtk
cmake -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64 -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nproc --ignore=2) --target qtk-main
./qtk/build/qtk-main
@ -42,12 +46,72 @@ cmake -DQTK_UPDATE_SUBMODULES=OFF -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64 -S q
./qtk/build/qtk-main
```
#### Windows / MacOS
If you are building on **Windows / Mac** and bringing your own installation of Assimp, consider setting the `-DASSIMP_NEW_INTERFACE` build flag.
```bash
cmake -DASSIMP_NEW_INTERFACE=ON -DQTK_UPDATE_SUBMODULES=OFF -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64;/path/to/assimp/ -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nproc --ignore=2) --target qtk-main
```
#### Development
This project uses version `15.0.5` of `clang-format`.
Before merging any branch we should run `clang-tidy` followed by `clang-format`.
```bash
git clone git@github.com:llvm/llvm-project.git -b llvmorg-15.0.5
cd llvm-project
cmake -B build -DLLVM_ENABLE_PROJECTS=clang -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" llvm
cmake --build build -j $(nproc --ignore=2)
sudo cmake --build build -j $(nproc --ignore=2) --target install
```
If this version is any earlier than `15.0.0`, running `clang-format` will fail because this project uses configuration options made available since `15.0.0`.
```bash
clang-format --version
clang-format version 15.0.5 (git@github.com:llvm/llvm-project.git 154e88af7ec97d9b9f389e55d45bf07108a9a097)
```
CLion has integration for IDE code reformatting actions with `clang-format`.
If you're using CLion, the `.clang-format` configuration will be picked up by CLion automatically.
`clang-tidy` can be run with the following commands.
```bash
# Move to the root of the repo
cd qtk
# Build
cmake -B build && cmake --build build
clang-tidy -p build/ --fix --config-file=.clang-tidy src/*.cpp src/*.h app/*.cpp app/*.h
```
Last we need to run `clang-format`, this can be done with the command directly.
This will reformat all the code in the repository.
```bash
clang-format -i --style=file:.clang-format src/*.cpp src/*.h app/*.cpp app/*.h
```
`clang-format` can be run with git integration (or CLion if you prefer).
Git will only reformat the lines you modified, which can be useful.
```bash
# If we want to format the last N commits
# git clang-format HEAD~N
# 3 commits
git clang-format HEAD~3
changed files:
app/examplescene.h
app/mainwindow.h
src/abstractscene.cpp
src/skybox.h
src/texture.cpp
src/texture.h
src/transform3D.h
```
### Controls
You can fly around the scene if you hold the right mouse button and use WASD.

View File

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

View File

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

View File

@ -9,12 +9,11 @@
#include <QApplication>
#include <QLabel>
#include <qtkwidget.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);
// Set OpenGL Version information
@ -22,19 +21,18 @@ int main(int argc, char *argv[])
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(4,5);
format.setVersion(4, 5);
// Set the number of samples used for glEnable(GL_MULTISAMPLING)
format.setSamples(4);
// Set the size of the depth bufer for glEnable(GL_DEPTH_TEST)
format.setDepthBufferSize(16);
#ifdef QTK_DEBUG
format.setOption(QSurfaceFormat::DebugContext);
#endif // QTK_DEBUG
#endif // QTK_DEBUG
// Create window for Qt application using custom mainwindow.h
MainWindow w;
w.show();
return a.exec();
return QApplication::exec();
}

View File

@ -2,18 +2,16 @@
#include <qtkwidget.h>
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
MainWindow::MainWindow(QWidget * parent) :
QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
// For use in design mode using Qt Creator
// + We can use the `ui` member to access nested widgets by name
for (const auto widget : ui->qWidget->children()) {
auto qtkWidget = dynamic_cast<Qtk::QtkWidget*>(widget);
if (qtkWidget != nullptr) {
for(const auto widget : ui->qWidget->children()) {
auto qtkWidget = dynamic_cast<Qtk::QtkWidget *>(widget);
if(qtkWidget != nullptr) {
std::string key = qtkWidget->objectName().toStdString();
if (mScenes[key] == nullptr) {
if(mScenes[key] == nullptr) {
mScenes[qtkWidget->objectName().toStdString()] = new ExampleScene();
}
qtkWidget->setScene(mScenes[qtkWidget->objectName().toStdString()]);
@ -22,7 +20,6 @@ MainWindow::MainWindow(QWidget *parent) :
setWindowIcon(QIcon("../resources/icon.png"));
}
MainWindow::~MainWindow()
{
MainWindow::~MainWindow() {
delete ui;
}

View File

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

View File

@ -1,114 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Qtk - MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="qWidget" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>801</width>
<height>561</height>
</rect>
</property>
<widget class="Qtk::QtkWidget" name="openGLWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>781</width>
<height>541</height>
</rect>
</property>
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Qtk - MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="qWidget" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>801</width>
<height>561</height>
</rect>
</property>
<widget class="Qtk::QtkWidget" name="openGLWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>781</width>
<height>541</height>
</rect>
</property>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuTest">
<property name="title">
<string>File</string>
</property>
<addaction name="actionOpen"/>
<addaction name="actionSave_2"/>
<addaction name="actionSave_as"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
<addaction name="actionShow_Console"/>
</widget>
<addaction name="menuTest"/>
<addaction name="menuView"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionOtherTest">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionSave">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionOpen">
<property name="text">
<string>Open...</string>
</property>
</action>
<action name="actionSave_2">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionSave_as">
<property name="text">
<string>Save as...</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionShow_Console">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show Console</string>
</property>
</action>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuTest">
<property name="title">
<string>File</string>
</property>
<addaction name="actionOpen"/>
<addaction name="actionSave_2"/>
<addaction name="actionSave_as"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
<addaction name="actionShow_Console"/>
</widget>
<addaction name="menuTest"/>
<addaction name="menuView"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionOtherTest">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionSave">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionOpen">
<property name="text">
<string>Open...</string>
</property>
</action>
<action name="actionSave_2">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionSave_as">
<property name="text">
<string>Save as...</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionShow_Console">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show Console</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>Qtk::QtkWidget</class>
<extends>QOpenGLWidget</extends>
<header>qtkwidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<customwidgets>
<customwidget>
<class>Qtk::QtkWidget</class>
<extends>QOpenGLWidget</extends>
<header>qtkwidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -14,22 +14,22 @@
#define QTK_RESOURCEMANAGER_H
typedef class ResourceManager {
public:
/**
* Takes a path using qrc format and constructs full system path to qtk assets
* Qrc format prefix ':/' is trimmed from the path for the caller
* Assets used with RM may (or may not) appear in qtk/resources.qrc
*
* @param path Path relative to qtk/resources/; ie) ':/models/backpack/backpack.obj'
* An asset at location qtk/resources/path/to/asset.obj
* Should be given in qrc format: ':/path/to/asset.obj'
* @return Absoulte system path to a qtk asset
*/
static std::string getPath(const std::string & path) {
// Only construct qtk resource path if in qrc format; else return it as-is
return path[0] == ':' ? QTK_RESOURCES + path.substr(1) : path;
}
public:
/**
* Takes a path using qrc format and constructs full system path to qtk
* assets Qrc format prefix ':/' is trimmed from the path for the caller
* Assets used with RM may (or may not) appear in qtk/resources.qrc
*
* @param path Path relative to qtk/resources/; ie)
* ':/models/backpack/backpack.obj' An asset at location
* qtk/resources/path/to/asset.obj Should be given in qrc format:
* ':/path/to/asset.obj'
* @return Absoulte system path to a qtk asset
*/
static std::string getPath(const std::string & path) {
// Only construct qtk resource path if in qrc format; else return it as-is
return path[0] == ':' ? QTK_RESOURCES + path.substr(1) : path;
}
} RM;
#endif //QTK_RESOURCEMANAGER_H
#endif // QTK_RESOURCEMANAGER_H

View File

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

View File

@ -16,40 +16,41 @@
#include <QMatrix4x4>
namespace Qtk {
class Scene : protected QOpenGLFunctions {
friend class MainWidget;
friend class MainWidget;
public:
Scene();
~Scene();
public:
Scene();
~Scene();
virtual void init() = 0;
virtual void draw() { privDraw(); };
virtual void update() = 0;
virtual void init() = 0;
static Camera3D & Camera() { return mCamera;}
static QMatrix4x4 View() { return mCamera.toMatrix();}
static QMatrix4x4 & Projection() { return mProjection;}
virtual void draw() { privDraw(); };
virtual void update() = 0;
inline Skybox * getSkybox() {return mSkybox;}
inline void setSkybox(Skybox * skybox) {
mSkybox = skybox;
}
static Camera3D & Camera() { return mCamera; }
private:
static Camera3D mCamera;
static QMatrix4x4 mProjection;
bool mInit = false;
static QMatrix4x4 View() { return mCamera.toMatrix(); }
void privDraw();
static QMatrix4x4 & Projection() { return mProjection; }
protected:
Skybox * mSkybox;
std::vector<MeshRenderer *> mMeshes;
std::vector<Model *> mModels;
inline Skybox * getSkybox() { return mSkybox; }
inline void setSkybox(Skybox * skybox) { mSkybox = skybox; }
private:
static Camera3D mCamera;
static QMatrix4x4 mProjection;
bool mInit = false;
void privDraw();
protected:
Skybox * mSkybox {};
std::vector<MeshRenderer *> mMeshes {};
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::LocalRight(1.0f, 0.0f, 0.0f);
/*******************************************************************************
* Accessors
******************************************************************************/
// Produces worldToView matrix
const QMatrix4x4 & Camera3D::toMatrix()
{
const QMatrix4x4 & Camera3D::toMatrix() {
mWorld.setToIdentity();
// Qt6 renamed QMatrix4x4::conjugate() to conjugated()
mWorld.rotate(mTransform.rotation().conjugated());
mWorld.translate(-mTransform.translation());
mWorld.rotate(mTransform.getRotation().conjugated());
mWorld.translate(-mTransform.getTranslation());
return mWorld;
}
/*******************************************************************************
* Qt Streams
******************************************************************************/
QDataStream & operator<<(QDataStream & out, Camera3D & transform)
{
QDataStream & operator<<(QDataStream & out, Camera3D & transform) {
out << transform.transform();
return out;
}
QDataStream & operator>>(QDataStream & in, Camera3D & transform)
{
QDataStream & operator>>(QDataStream & in, Camera3D & transform) {
in >> transform.transform();
return in;
}
QDebug operator<<(QDebug dbg, const Camera3D & transform)
{
QDebug operator<<(QDebug dbg, const Camera3D & transform) {
dbg << "Camera3D\n{\n";
dbg << "Position: <" << transform.translation().x() << ", "
<< transform.translation().y() << ", "
<< transform.translation().z() << ">\n";
<< transform.translation().y() << ", " << transform.translation().z()
<< ">\n";
dbg << "Rotation: <" << transform.rotation().x() << ", "
<< transform.rotation().y() << ", "
<< transform.rotation().z() << " | "
<< transform.rotation().y() << ", " << transform.rotation().z() << " | "
<< transform.rotation().scalar() << ">\n}";
return dbg;
}

View File

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

View File

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

View File

@ -17,49 +17,60 @@
namespace Qtk {
class QTKAPI Input {
friend class Qtk::QtkWidget;
public:
// Possible key states
enum InputState
{
InputInvalid,
InputRegistered,
InputUnregistered,
InputTriggered,
InputPressed,
InputReleased
};
friend class Qtk::QtkWidget;
// State checking
inline static bool keyTriggered(Qt::Key key)
{ return keyState(key) == InputTriggered;}
inline static bool keyPressed(Qt::Key key)
{ return keyState(key) == InputPressed;}
inline static bool keyReleased(Qt::Key key)
{ return keyState(key) == InputReleased;}
inline static bool buttonTriggered(Qt::MouseButton button)
{ return buttonState(button) == InputTriggered;}
inline static bool buttonPressed(Qt::MouseButton button)
{ return buttonState(button) == InputPressed;}
inline static bool buttonReleased(Qt::MouseButton button)
{ return buttonState(button) == InputReleased;}
public:
// Possible key states
enum InputState {
InputInvalid,
InputRegistered,
InputUnregistered,
InputTriggered,
InputPressed,
InputReleased
};
// Implementation
static InputState keyState(Qt::Key key);
static InputState buttonState(Qt::MouseButton button);
// State checking
inline static bool keyTriggered(Qt::Key key) {
return keyState(key) == InputTriggered;
}
static QPoint mousePosition();
static QPoint mouseDelta();
inline static bool keyPressed(Qt::Key key) {
return keyState(key) == InputPressed;
}
private:
// State updating
static void update();
static void registerKeyPress(int key);
static void registerKeyRelease(int key);
static void registerMousePress(Qt::MouseButton button);
static void registerMouseRelease(Qt::MouseButton button);
static void reset();
inline static bool keyReleased(Qt::Key key) {
return keyState(key) == InputReleased;
}
inline static bool buttonTriggered(Qt::MouseButton button) {
return buttonState(button) == InputTriggered;
}
inline static bool buttonPressed(Qt::MouseButton button) {
return buttonState(button) == InputPressed;
}
inline static bool buttonReleased(Qt::MouseButton button) {
return buttonState(button) == InputReleased;
}
// Implementation
static InputState keyState(Qt::Key key);
static InputState buttonState(Qt::MouseButton button);
static QPoint mousePosition();
static QPoint mouseDelta();
private:
// State updating
static void update();
static void registerKeyPress(int key);
static void registerKeyRelease(int key);
static void registerMousePress(Qt::MouseButton button);
static void registerMouseRelease(Qt::MouseButton button);
static void reset();
};
}
} // namespace Qtk
#endif // QTOPENGL_INPUT_H
#endif // QTOPENGL_INPUT_H

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,328 +10,370 @@
using namespace Qtk;
Cube::Cube(DrawMode mode)
{
Cube::Cube(DrawMode mode) {
mDrawMode = mode;
switch(mode) {
// Cube data for use with glDrawArrays
case QTK_DRAW_ARRAYS:
mIndices = { /* No indices needed for glDrawArrays */ };
mIndices = {/* No indices needed for glDrawArrays */};
mNormals =
{FACE_FRONT, FACE_BACK, FACE_TOP, FACE_BOTTOM, FACE_LEFT, FACE_RIGHT};
mNormals = {FACE_FRONT, FACE_BACK, FACE_TOP,
FACE_BOTTOM, FACE_LEFT, FACE_RIGHT};
mVertices = {
// Face 1 (Front)
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL,
VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
// Face 2 (Back)
VERTEX_BBR, VERTEX_BTL, VERTEX_BTR,
VERTEX_BTL, VERTEX_BBR, VERTEX_BBL,
// Face 3 (Top)
VERTEX_FTR, VERTEX_BTR, VERTEX_BTL,
VERTEX_BTL, VERTEX_FTL, VERTEX_FTR,
// Face 4 (Bottom)
VERTEX_FBR, VERTEX_FBL, VERTEX_BBL,
VERTEX_BBL, VERTEX_BBR, VERTEX_FBR,
// Face 5 (Left)
VERTEX_FBL, VERTEX_FTL, VERTEX_BTL,
VERTEX_FBL, VERTEX_BTL, VERTEX_BBL,
// Face 6 (Right)
VERTEX_FTR, VERTEX_FBR, VERTEX_BBR,
VERTEX_BBR, VERTEX_BTR, VERTEX_FTR
};
mVertices = {// Face 1 (Front)
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBL, VERTEX_FBR,
VERTEX_FTR,
// Face 2 (Back)
VERTEX_BBR, VERTEX_BTL, VERTEX_BTR, VERTEX_BTL, VERTEX_BBR,
VERTEX_BBL,
// Face 3 (Top)
VERTEX_FTR, VERTEX_BTR, VERTEX_BTL, VERTEX_BTL, VERTEX_FTL,
VERTEX_FTR,
// Face 4 (Bottom)
VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, VERTEX_BBL, VERTEX_BBR,
VERTEX_FBR,
// Face 5 (Left)
VERTEX_FBL, VERTEX_FTL, VERTEX_BTL, VERTEX_FBL, VERTEX_BTL,
VERTEX_BBL,
// Face 6 (Right)
VERTEX_FTR, VERTEX_FBR, VERTEX_BBR, VERTEX_BBR, VERTEX_BTR,
VERTEX_FTR};
mColors = {
// Face 1 (Front)
RED, GREEN, BLUE,
BLUE, WHITE, RED,
// Face 2 (Back)
YELLOW, CYAN, MAGENTA,
CYAN, YELLOW, BLACK,
// Face 3 (Top)
RED, MAGENTA, CYAN,
CYAN, GREEN, RED,
// Face 4 (Bottom)
WHITE, BLUE, BLACK,
BLACK, YELLOW, WHITE,
// Face 5 (Left)
BLUE, GREEN, CYAN,
BLUE, CYAN, BLACK,
// Face 6 (Right)
RED, WHITE, YELLOW,
YELLOW, MAGENTA, RED
};
mColors = {// Face 1 (Front)
RED, GREEN, BLUE, BLUE, WHITE, RED,
// Face 2 (Back)
YELLOW, CYAN, MAGENTA, CYAN, YELLOW, BLACK,
// Face 3 (Top)
RED, MAGENTA, CYAN, CYAN, GREEN, RED,
// Face 4 (Bottom)
WHITE, BLUE, BLACK, BLACK, YELLOW, WHITE,
// Face 5 (Left)
BLUE, GREEN, CYAN, BLUE, CYAN, BLACK,
// Face 6 (Right)
RED, WHITE, YELLOW, YELLOW, MAGENTA, RED};
mTexCoords = {
// Face 1 (Front)
UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_RIGHT, UV_CORNER, UV_TOP,
// Face 2 (Back)
UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_TOP, UV_ORIGIN,
// Face 3 (Top)
UV_CORNER, UV_TOP, UV_ORIGIN,
UV_ORIGIN, UV_RIGHT, UV_CORNER,
// Face 4 (Bottom)
UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_RIGHT, UV_CORNER, UV_TOP,
// Face 5 (Left)
UV_TOP, UV_CORNER, UV_RIGHT,
UV_TOP, UV_RIGHT, UV_ORIGIN,
// Face 6 (Right)
UV_TOP, UV_CORNER, UV_RIGHT,
UV_RIGHT, UV_ORIGIN, UV_TOP
};
mTexCoords = {// Face 1 (Front)
UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, UV_TOP,
// Face 2 (Back)
UV_TOP, UV_RIGHT, UV_CORNER, UV_RIGHT, UV_TOP, UV_ORIGIN,
// Face 3 (Top)
UV_CORNER, UV_TOP, UV_ORIGIN, UV_ORIGIN, UV_RIGHT,
UV_CORNER,
// Face 4 (Bottom)
UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, UV_TOP,
// Face 5 (Left)
UV_TOP, UV_CORNER, UV_RIGHT, UV_TOP, UV_RIGHT, UV_ORIGIN,
// Face 6 (Right)
UV_TOP, UV_CORNER, UV_RIGHT, UV_RIGHT, UV_ORIGIN, UV_TOP};
break;
// Cube data for use with glDrawElements
case QTK_DRAW_ELEMENTS:
mNormals =
{/* For normals and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */};
mTexCoords =
{ /* For UVs and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */ };
mNormals = {
/* For normals and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */};
mTexCoords = {
/* For UVs and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */};
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mVertices = {
// 0 1 2 3
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR,
// 4 5 6 7
VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR
};
mIndices = {
// Face 1 (Front)
0, 1, 2, 2, 3, 0,
// Face 2 (Back)
7, 5, 4, 5, 7, 6,
// Face 3 (Top)
0, 4, 5, 5, 1, 0,
// Face 4 (Bottom)
3, 2, 6, 6, 7, 3,
// Face 5 (Left)
2, 1, 5, 2, 5, 6,
// Face 6 (Right)
0, 3, 7, 7, 4, 0
};
mVertices = {// 0 1 2 3
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR,
// 4 5 6 7
VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR};
mIndices = {// Face 1 (Front)
0, 1, 2, 2, 3, 0,
// Face 2 (Back)
7, 5, 4, 5, 7, 6,
// Face 3 (Top)
0, 4, 5, 5, 1, 0,
// Face 4 (Bottom)
3, 2, 6, 6, 7, 3,
// Face 5 (Left)
2, 1, 5, 2, 5, 6,
// Face 6 (Right)
0, 3, 7, 7, 4, 0};
break;
// Cube shape data for using normals and UVs with glDrawElements
case QTK_DRAW_ELEMENTS_NORMALS:
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mVertices = {
// Face 1 (Front)
// 0 1 2 3
VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
// Face 2 (Back)
// 4 5 6 7
VERTEX_BTL, VERTEX_BBL, VERTEX_BBR, VERTEX_BTR,
// Face 3 (Top)
// 8 9 10 11
VERTEX_FTL, VERTEX_BTL, VERTEX_BTR, VERTEX_FTR,
// Face 4 (Bottom)
// 12 13 14 15
VERTEX_FBL, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR,
// Face 5 (Left)
// 16 17 18 19
VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL,
// Face 6 (Right)
// 20 21 22 23
VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR
};
mVertices = {// Face 1 (Front)
// 0 1 2 3
VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
// Face 2 (Back)
// 4 5 6 7
VERTEX_BTL, VERTEX_BBL, VERTEX_BBR, VERTEX_BTR,
// Face 3 (Top)
// 8 9 10 11
VERTEX_FTL, VERTEX_BTL, VERTEX_BTR, VERTEX_FTR,
// Face 4 (Bottom)
// 12 13 14 15
VERTEX_FBL, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR,
// Face 5 (Left)
// 16 17 18 19
VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL,
// Face 6 (Right)
// 20 21 22 23
VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR};
mIndices = {
// Face 1 (Front)
0, 1, 2, 2, 3, 0,
// Face 2 (Back)
4, 5, 6, 6, 7, 4,
// Face 3 (Top)
8, 9, 10, 10, 11, 8,
// Face 4 (Bottom)
12, 13, 14, 14, 15, 12,
mIndices = {// Face 1 (Front)
0, 1, 2, 2, 3, 0,
// Face 2 (Back)
4, 5, 6, 6, 7, 4,
// Face 3 (Top)
8, 9, 10, 10, 11, 8,
// Face 4 (Bottom)
12, 13, 14, 14, 15, 12,
// Face 5 (Left)
16, 17, 18, 18, 19, 16,
// Face 6 (Right)
20, 21, 22, 22, 23, 20
};
// Face 5 (Left)
16, 17, 18, 18, 19, 16,
// Face 6 (Right)
20, 21, 22, 22, 23, 20};
mNormals = {
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD,
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK,
VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP,
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN,
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT,
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT,
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK,
VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP,
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN,
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT,
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT,
};
mTexCoords = {
// Face 1 (Front)
UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_TOP,
UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 2 (Back)
UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_TOP,
UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 3 (Top)
UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_TOP,
UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 4 (Bottom)
UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_TOP,
UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 5 (Left)
UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_TOP,
UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 6 (Right)
UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_TOP,
UV_RIGHT,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
};
break;
}
}
Triangle::Triangle(DrawMode mode)
{
Triangle::Triangle(DrawMode mode) {
mDrawMode = mode;
const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f);
switch(mode) {
case QTK_DRAW_ARRAYS:
mIndices = { /* No indices needed for glDrawArrays */ };
mIndices = {/* No indices needed for glDrawArrays */};
mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK };
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mVertices = {
// Bottom face (Base of the pyramid)
VERTEX_BBL, VERTEX_BBR, VERTEX_FBR,
VERTEX_FBR, VERTEX_FBL, VERTEX_BBL,
VERTEX_BBL,
VERTEX_BBR,
VERTEX_FBR,
VERTEX_FBR,
VERTEX_FBL,
VERTEX_BBL,
// Front face
VERTEX_FBL, VERTEX_FBR, triangleTop,
VERTEX_FBL,
VERTEX_FBR,
triangleTop,
// Back face
VERTEX_BBR, VERTEX_BBL, triangleTop,
VERTEX_BBR,
VERTEX_BBL,
triangleTop,
// Left face
VERTEX_BBL, VERTEX_FBL, triangleTop,
VERTEX_BBL,
VERTEX_FBL,
triangleTop,
// Right face
VERTEX_FBR, VERTEX_BBR, triangleTop,
VERTEX_FBR,
VERTEX_BBR,
triangleTop,
};
// Find normals for each triangle of the mesh
for (int i = 0; i < mVertices.size(); i += 3) {
for(int i = 0; i < mVertices.size(); i += 3) {
QVector3D vertexNormal =
QVector3D::normal(mVertices[i], mVertices[i+1], mVertices[i+2]);
QVector3D::normal(mVertices[i], mVertices[i + 1], mVertices[i + 2]);
// Three points share this normal
for (int j = 0; j < 3; j++) {
for(int j = 0; j < 3; j++) {
mNormals.push_back(vertexNormal);
}
}
mTexCoords = {
// Bottom face (Base of the pyramid)
UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_CORNER, UV_TOP, UV_ORIGIN,
UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
UV_CORNER,
UV_TOP,
UV_ORIGIN,
// Front face
UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Back face
UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Left face
UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Right face
UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
};
break;
// Triangle shape data for using glDrawElements
case QTK_DRAW_ELEMENTS:
mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK };
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mVertices = {VERTEX_FBL, VERTEX_FBR, VERTEX_BBL, VERTEX_BBR, triangleTop};
mIndices = {
// Bottom face (Base of the pyramid)
2, 3, 1, // Use customVertexes[2], then 3, 1...
1, 0, 2, // Use customVertexes[1], then 0, 2
2, 3, 1, // Use customVertexes[2], then 3, 1...
1, 0, 2, // Use customVertexes[1], then 0, 2
0, 1, 4, // Front face
3, 2, 4, // Back face
2, 0, 4, // Left face
1, 3, 4, // Right face
0, 1, 4, // Front face
3, 2, 4, // Back face
2, 0, 4, // Left face
1, 3, 4, // Right face
};
mNormals =
{/* Use QTK_DRAW_ELEMENTS_NORMALS for normals with glDrawElements */};
mNormals = {
/* Use QTK_DRAW_ELEMENTS_NORMALS for normals with glDrawElements */};
mTexCoords = { /* No UVs for triangle with glDrawElements */ };
mTexCoords = {/* No UVs for triangle with glDrawElements */};
break;
// Triangle shape data for using normals and UVs with glDrawElements
case QTK_DRAW_ELEMENTS_NORMALS:
mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK };
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mVertices = {
// Bottom face
// 0 1 2
VERTEX_FBL, VERTEX_FBR, VERTEX_BBL,
VERTEX_FBL,
VERTEX_FBR,
VERTEX_BBL,
// 3 4 5
VERTEX_BBR, VERTEX_FBR, VERTEX_BBL,
VERTEX_BBR,
VERTEX_FBR,
VERTEX_BBL,
// Front face
// 6 7 8
VERTEX_FBL, VERTEX_FBR, triangleTop,
VERTEX_FBL,
VERTEX_FBR,
triangleTop,
// Back face
// 9 10 11
VERTEX_BBR, VERTEX_BBL, triangleTop,
VERTEX_BBR,
VERTEX_BBL,
triangleTop,
// Left face
// 12 13 14
VERTEX_BBL, VERTEX_FBL, triangleTop,
VERTEX_BBL,
VERTEX_FBL,
triangleTop,
// Right face
// 15 16 17
VERTEX_FBR, VERTEX_BBR, triangleTop,
VERTEX_FBR,
VERTEX_BBR,
triangleTop,
};
mIndices = {
// Bottom face (Base of the pyramid)
0, 1, 2, // Use customVertexes[2], then 3, 1...
3, 4, 5, // Use customVertexes[1], then 0, 2
0, 1, 2, // Use customVertexes[2], then 3, 1...
3, 4, 5, // Use customVertexes[1], then 0, 2
6, 7, 8, // Front face
9, 10, 11, // Back face
12, 13, 14, // Left face
15, 16, 17, // Right face
6, 7, 8, // Front face
9, 10, 11, // Back face
12, 13, 14, // Left face
15, 16, 17, // Right face
};
// Find normals for each triangle of the mesh
for (int i = 0; i < mVertices.size(); i += 3) {
QVector3D vertexNormal =
QVector3D::normal(mVertices[mIndices[i]],
mVertices[mIndices[i+1]],
mVertices[mIndices[i+2]]);
for(int i = 0; i < mVertices.size(); i += 3) {
QVector3D vertexNormal = QVector3D::normal(
mVertices[mIndices[i]], mVertices[mIndices[i + 1]],
mVertices[mIndices[i + 2]]);
// Three points share this normal
for (int j = 0; j < 3; j++) {
for(int j = 0; j < 3; j++) {
mNormals.push_back(vertexNormal);
}
}
mTexCoords = {
// Bottom face
UV_ORIGIN, UV_RIGHT, UV_TOP,
UV_CORNER, UV_RIGHT, UV_TOP,
UV_ORIGIN,
UV_RIGHT,
UV_TOP,
UV_CORNER,
UV_RIGHT,
UV_TOP,
// Front face
UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Back face
UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Left face
UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
// Right face
UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_ORIGIN,
UV_RIGHT,
UV_CORNER,
};
break;
}
}

View File

@ -11,28 +11,30 @@
#include <QOpenGLWidget>
#include <QVector2D>
#include <QVector3D>
#include <utility>
#include <qtkapi.h>
#include <transform3D.h>
namespace Qtk {
class MeshRenderer;
class Object;
// Define vertices for drawing a cube using two faces (8 vertex points)
// Front Vertices
#define VERTEX_FTR QVector3D( 0.5f, 0.5f, 0.5f) // 1
#define VERTEX_FTL QVector3D(-0.5f, 0.5f, 0.5f) // 2
#define VERTEX_FBL QVector3D(-0.5f, -0.5f, 0.5f) // 3
#define VERTEX_FBR QVector3D( 0.5f, -0.5f, 0.5f) // 4
// Define vertices for drawing a cube using two faces (8 vertex points)
// Front Vertices
#define VERTEX_FTR QVector3D(0.5f, 0.5f, 0.5f) // 1
#define VERTEX_FTL QVector3D(-0.5f, 0.5f, 0.5f) // 2
#define VERTEX_FBL QVector3D(-0.5f, -0.5f, 0.5f) // 3
#define VERTEX_FBR QVector3D(0.5f, -0.5f, 0.5f) // 4
// Back Vertices
#define VERTEX_BTR QVector3D( 0.5f, 0.5f, -0.5f) // 5
#define VERTEX_BTL QVector3D(-0.5f, 0.5f, -0.5f) // 6
#define VERTEX_BBL QVector3D(-0.5f, -0.5f, -0.5f) // 7
#define VERTEX_BBR QVector3D( 0.5f, -0.5f, -0.5f) // 8
// Back Vertices
#define VERTEX_BTR QVector3D(0.5f, 0.5f, -0.5f) // 5
#define VERTEX_BTL QVector3D(-0.5f, 0.5f, -0.5f) // 6
#define VERTEX_BBL QVector3D(-0.5f, -0.5f, -0.5f) // 7
#define VERTEX_BBR QVector3D(0.5f, -0.5f, -0.5f) // 8
// Direction vectors
// Direction vectors
#define VECTOR_UP QVector3D(0.0f, 1.0f, 0.0f)
#define VECTOR_DOWN QVector3D(0.0f, -1.0f, 0.0f)
#define VECTOR_LEFT QVector3D(-1.0f, 0.0f, 0.0f)
@ -40,95 +42,134 @@ namespace Qtk {
#define VECTOR_FORWARD QVector3D(0.0f, 0.0f, 1.0f)
#define VECTOR_BACK QVector3D(0.0f, 0.0f, -1.0f)
// Identity and zero vectors
#define VECTOR_ONE QVector3D(1.0f, 1.0f, 1.0f)
#define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f)
// Identity and zero vectors
#define VECTOR_ONE QVector3D(1.0f, 1.0f, 1.0f)
#define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f)
// A series of direction vectors to represent cube face normal
#define FACE_TOP VECTOR_UP, VECTOR_UP, VECTOR_UP, \
VECTOR_UP, VECTOR_UP, VECTOR_UP
#define FACE_BOTTOM VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, \
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN
#define FACE_LEFT VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, \
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT
#define FACE_RIGHT VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT
#define FACE_FRONT VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD
#define FACE_BACK VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, \
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK
// A series of direction vectors to represent cube face normal
#define FACE_TOP \
VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP
#define FACE_BOTTOM \
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN
#define FACE_LEFT \
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT
#define FACE_RIGHT \
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \
VECTOR_RIGHT
#define FACE_FRONT \
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \
VECTOR_FORWARD, VECTOR_FORWARD
#define FACE_BACK \
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK
// Colors using QVector3Ds as RGB values
#define WHITE VECTOR_ONE
#define BLACK VECTOR_ZERO
#define RED QVector3D(1.0f, 0.0f, 0.0f)
#define GREEN QVector3D(0.0f, 1.0f, 0.0f)
#define BLUE QVector3D(0.0f, 0.0f, 1.0f)
// Colors using QVector3Ds as RGB values
#define WHITE VECTOR_ONE
#define BLACK VECTOR_ZERO
#define RED QVector3D(1.0f, 0.0f, 0.0f)
#define GREEN QVector3D(0.0f, 1.0f, 0.0f)
#define BLUE QVector3D(0.0f, 0.0f, 1.0f)
#define YELLOW QVector3D(1.0f, 1.0f, 0.0f)
#define CYAN QVector3D(0.0f, 1.0f, 1.0f)
#define MAGENTA QVector3D(1.0f, 0.0f, 1.0f)
#define UV_ORIGIN QVector2D(0.0f, 0.0f)
#define UV_TOP QVector2D(1.0f, 0.0f)
#define UV_RIGHT QVector2D(0.0f, 1.0f)
#define UV_CORNER QVector2D(1.0f, 1.0f)
#define UV_ORIGIN QVector2D(0.0f, 0.0f)
#define UV_TOP QVector2D(1.0f, 0.0f)
#define UV_RIGHT QVector2D(0.0f, 1.0f)
#define UV_CORNER QVector2D(1.0f, 1.0f)
// TODO: Vertices.getData(); Vertices.getStride();
typedef std::vector<QVector3D> Vertices;
typedef std::vector<QVector3D> Colors;
typedef std::vector<GLuint> Indices;
typedef std::vector<QVector2D> TexCoords;
typedef std::vector<QVector3D> Normals;
enum DrawMode { QTK_DRAW_ARRAYS, QTK_DRAW_ELEMENTS, QTK_DRAW_ELEMENTS_NORMALS };
enum DrawMode {
QTK_DRAW_ARRAYS,
QTK_DRAW_ELEMENTS,
QTK_DRAW_ELEMENTS_NORMALS
};
struct QTKAPI ShapeBase {
ShapeBase(DrawMode mode=QTK_DRAW_ARRAYS, Vertices v={},Indices i={}, Colors c={},
TexCoords t={}, Normals n={})
: mVertices(v), mColors(c), mIndices(i), mTexCoords(t), mNormals(n)
{}
explicit ShapeBase(
DrawMode mode = QTK_DRAW_ARRAYS, Vertices v = {}, Indices i = {},
Colors c = {}, TexCoords t = {}, Normals n = {}) :
mDrawMode(mode),
mVertices(std::move(v)), mColors(std::move(c)),
mIndices(std::move(i)), mTexCoords(std::move(t)),
mNormals(std::move(n)) {}
inline const Vertices & vertices() const { return mVertices;}
inline const Indices & indices() const { return mIndices;}
inline const Colors & colors() const { return mColors;}
inline const TexCoords & texCoords() const { return mTexCoords;}
inline const Normals & normals() const { return mNormals;}
[[nodiscard]] inline const Vertices & getVertices() const {
return mVertices;
}
protected:
DrawMode mDrawMode;
[[nodiscard]] inline const Indices & getIndexData() const {
return mIndices;
}
Vertices mVertices;
Colors mColors;
Indices mIndices;
TexCoords mTexCoords;
Normals mNormals;
[[nodiscard]] inline const Colors & getColors() const { return mColors; }
[[nodiscard]] inline const TexCoords & getTexCoords() const {
return mTexCoords;
}
[[nodiscard]] inline const Normals & getNormals() const {
return mNormals;
}
[[nodiscard]] inline size_t getTexCoordsStride() const {
return mTexCoords.size() * sizeof(mTexCoords[0]);
}
protected:
DrawMode mDrawMode;
Vertices mVertices {};
Colors mColors {};
Indices mIndices {};
TexCoords mTexCoords {};
Normals mNormals {};
};
struct Shape : public ShapeBase {
friend MeshRenderer;
friend Object;
Shape () {}
Shape(const ShapeBase & rhs) : ShapeBase(rhs) {}
friend MeshRenderer;
friend Object;
virtual inline void setVertices(const Vertices & value) {mVertices = value;}
virtual inline void setIndices(const Indices & value) {mIndices = value;}
virtual inline void setColors(const Colors & value) {mColors = value;}
virtual inline void setTexCoords(const TexCoords & value) {mTexCoords = value;}
virtual inline void setNormals(const Normals & value) {mNormals = value;}
virtual inline void setShape(const Shape & value) { *this = value;}
Shape() = default;
explicit Shape(const ShapeBase & rhs) : ShapeBase(rhs) {}
virtual inline void setVertices(const Vertices & value) {
mVertices = value;
}
virtual inline void setIndices(const Indices & value) {
mIndices = value;
}
virtual inline void setColors(const Colors & value) { mColors = value; }
virtual inline void setTexCoords(const TexCoords & value) {
mTexCoords = value;
}
virtual inline void setNormals(const Normals & value) {
mNormals = value;
}
virtual inline void setShape(const Shape & value) { *this = value; }
};
// Primitives inherit from ShapeBase, does not allow setting of shape values
class QTKAPI Mesh {
};
// Primitives inherit from ShapeBase, does not allow setting of shape values
class QTKAPI Mesh {};
struct QTKAPI Cube : public ShapeBase {
Cube(DrawMode mode=QTK_DRAW_ARRAYS);
explicit Cube(DrawMode mode = QTK_DRAW_ARRAYS);
};
struct QTKAPI Triangle : public ShapeBase {
Triangle(DrawMode mode=QTK_DRAW_ARRAYS);
explicit Triangle(DrawMode mode = QTK_DRAW_ARRAYS);
};
}
} // namespace Qtk
#endif // QTK_MESH_H
#endif // QTK_MESH_H

View File

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

View File

@ -12,69 +12,119 @@
#include <object.h>
#include <qtkapi.h>
#include <utility>
namespace Qtk {
class QTKAPI MeshRenderer : public Object {
public:
// Delegate constructors
MeshRenderer(const char * name, Vertices vertices, Indices indices,
DrawMode mode=QTK_DRAW_ARRAYS)
: MeshRenderer(name, ShapeBase(mode, vertices, indices))
{}
MeshRenderer(const char * name)
: MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS))
{}
// Constructor
MeshRenderer(const char * name, const ShapeBase &shape);
~MeshRenderer();
class QTKAPI ShaderBindScope {
public:
explicit ShaderBindScope(
QOpenGLShaderProgram * program, bool was_locked) :
mWasBound(was_locked) {
mProgram = program;
if(!mWasBound) {
mProgram->bind();
}
}
// Retrieve a mesh by name stored within a static QHash
static MeshRenderer * getInstance(const QString & name);
~ShaderBindScope() {
if(!mWasBound) {
mProgram->release();
}
}
void init();
void draw();
// Draw types like GL_TRIANGLES, GL_POINTS, GL_LINES, etc
void setDrawType(int drawType) { mDrawType = drawType;}
// Shader settings
inline void setShaderVertex(const std::string & vert) { mVertexShader = vert;}
inline void setShaderFragment(const std::string & frag)
{ mFragmentShader = frag;}
void setShaders(const std::string & vert, const std::string & frag);
template <typename T>
inline void setUniform(int location, T value)
{ mProgram.setUniformValue(location, value);}
template <typename T>
inline void setUniform(const char * location, T value)
{ mProgram.setUniformValue(location, value);}
// Set MVP matrix using this Object's transform
// + View and projection provided by MainWidget static members
void setUniformMVP(const char * model="uModel", const char * view="uView",
const char * projection="uProjection");
// Sets the texture to the image at the given path
// + Sets mHasTexture to enable texture binding in draw()
void setTexture(const char * path);
void setTexture(QOpenGLTexture * texture);
// These functions modify data stored in a VBO
// + After calling them, the VBO will need to be reallocated
void setShape(const Shape & value) override;
void setColor(const QVector3D & color);
// Static QHash of all mesh objects within the scene
typedef QHash<QString, MeshRenderer *> MeshManager;
private:
static MeshManager sInstances;
int mDrawType;
bool mHasTexture;
std::string mVertexShader, mFragmentShader;
private:
QOpenGLShaderProgram * mProgram;
bool mWasBound;
};
}
#endif // QTK_MESHRENDERER_H
class QTKAPI MeshRenderer : public Object {
public:
// Delegate constructors
MeshRenderer(
const char * name, Vertices vertices, Indices indices,
DrawMode mode = QTK_DRAW_ARRAYS) :
MeshRenderer(
name, ShapeBase(mode, std::move(vertices), std::move(indices))) {}
explicit MeshRenderer(const char * name) :
MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS)) {}
// Constructor
MeshRenderer(const char * name, const ShapeBase & shape);
~MeshRenderer() override;
// Retrieve a mesh by name stored within a static QHash
static MeshRenderer * getInstance(const QString & name);
void init();
void draw();
// Draw types like GL_TRIANGLES, GL_POINTS, GL_LINES, etc
void setDrawType(int drawType) { mDrawType = drawType; }
// Shader settings
inline void setShaderVertex(const std::string & vert) {
mVertexShader = vert;
}
inline void setShaderFragment(const std::string & frag) {
mFragmentShader = frag;
}
void setShaders(const std::string & vert, const std::string & frag);
template <typename T> inline void setUniform(int location, T value) {
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(location, value);
}
template <typename T>
inline void setUniform(const char * location, T value) {
ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(location, value);
}
// Set MVP matrix using this Object's transform
// + View and projection provided by MainWidget static members
void setUniformMVP(
const char * model = "uModel", const char * view = "uView",
const char * projection = "uProjection");
// These functions modify data stored in a VBO
// + After calling them, the VBO will need to be reallocated
void setShape(const Shape & value) override;
void setColor(const QVector3D & color);
void setAttributeBuffer(
int location, GLenum type, int offset, int tupleSize,
int stride = 0) {
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.setAttributeBuffer(location, type, offset, tupleSize, stride);
mVAO.release();
}
inline void enableAttributeArray(int location) {
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.enableAttributeArray(location);
mVAO.release();
}
void reallocateTexCoords(const TexCoords & t, unsigned dims = 2);
void reallocateNormals(const Normals & n, unsigned dims = 3);
// Static QHash of all mesh objects within the scene
typedef QHash<QString, MeshRenderer *> MeshManager;
private:
static MeshManager sInstances;
int mDrawType {};
std::string mVertexShader {}, mFragmentShader {};
};
} // namespace Qtk
#endif // QTK_MESHRENDERER_H

View File

@ -19,18 +19,15 @@ using namespace Qtk;
Model::ModelManager Model::mManager;
// Static function to access ModelManager for getting Models by name
Model * Model::getInstance(const char * name)
{
Model * Model::getInstance(const char * name) {
return mManager[name];
}
/*******************************************************************************
* ModelMesh Private Member Functions
******************************************************************************/
void ModelMesh::initMesh(const char * vert, const char * frag)
{
void ModelMesh::initMesh(const char * vert, const char * frag) {
initializeOpenGLFunctions();
// Create VAO, VBO, EBO
@ -44,14 +41,12 @@ void ModelMesh::initMesh(const char * vert, const char * frag)
mVBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
mVBO->bind();
mVBO->allocate(mVertices.data(),
mVertices.size() * sizeof(mVertices[0]));
mVBO->allocate(mVertices.data(), mVertices.size() * sizeof(mVertices[0]));
// Allocate EBO
mEBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
mEBO->bind();
mEBO->allocate(mIndices.data(),
mIndices.size() * sizeof(mIndices[0]));
mEBO->allocate(mIndices.data(), mIndices.size() * sizeof(mIndices[0]));
mEBO->release();
// Load and link shaders
@ -62,46 +57,40 @@ void ModelMesh::initMesh(const char * vert, const char * frag)
// Positions
mProgram->enableAttributeArray(0);
mProgram->setAttributeBuffer(0, GL_FLOAT,
offsetof(ModelVertex, mPosition), 3,
sizeof(ModelVertex));
mProgram->setAttributeBuffer(
0, GL_FLOAT, offsetof(ModelVertex, mPosition), 3, sizeof(ModelVertex));
// Normals
mProgram->enableAttributeArray(1);
mProgram->setAttributeBuffer(1, GL_FLOAT,
offsetof(ModelVertex, mNormal), 3,
sizeof(ModelVertex));
mProgram->setAttributeBuffer(
1, GL_FLOAT, offsetof(ModelVertex, mNormal), 3, sizeof(ModelVertex));
// Texture Coordinates
mProgram->enableAttributeArray(2);
mProgram->setAttributeBuffer(2, GL_FLOAT,
offsetof(ModelVertex, mTextureCoord), 2,
sizeof(ModelVertex));
mProgram->setAttributeBuffer(
2, GL_FLOAT, offsetof(ModelVertex, mTextureCoord), 2,
sizeof(ModelVertex));
// Vertex tangents
mProgram->enableAttributeArray(3);
mProgram->setAttributeBuffer(3, GL_FLOAT,
offsetof(ModelVertex, mTangent), 3,
sizeof(ModelVertex));
mProgram->setAttributeBuffer(
3, GL_FLOAT, offsetof(ModelVertex, mTangent), 3, sizeof(ModelVertex));
// Vertex bitangents
mProgram->enableAttributeArray(4);
mProgram->setAttributeBuffer(4, GL_FLOAT,
offsetof(ModelVertex, mBitangent), 3,
sizeof(ModelVertex));
mProgram->setAttributeBuffer(
4, GL_FLOAT, offsetof(ModelVertex, mBitangent), 3, sizeof(ModelVertex));
mProgram->release();
mVBO->release();
mVAO->release();
}
/*******************************************************************************
* ModelMesh Public Member Functions
******************************************************************************/
void ModelMesh::draw(QOpenGLShaderProgram & shader)
{
void ModelMesh::draw(QOpenGLShaderProgram & shader) {
mVAO->bind();
// Bind shader
shader.bind();
@ -114,7 +103,7 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader)
GLuint diffuseCount = 1;
GLuint specularCount = 1;
GLuint normalCount = 1;
for (GLuint i = 0; i < mTextures.size(); i++) {
for(GLuint i = 0; i < mTextures.size(); i++) {
// Activate the current texture index by adding offset to GL_TEXTURE0
glActiveTexture(GL_TEXTURE0 + i);
mTextures[i].mTexture->bind();
@ -124,20 +113,26 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader)
// Specular: material.texture_specular1, material.texture_specular2, ...
std::string number;
std::string name = mTextures[i].mType;
if (name == "texture_diffuse") number = std::to_string(diffuseCount++);
if (name == "texture_specular") number = std::to_string(specularCount++);
if (name == "texture_normal") number = std::to_string(normalCount++);
if(name == "texture_diffuse") {
number = std::to_string(diffuseCount++);
}
if(name == "texture_specular") {
number = std::to_string(specularCount++);
}
if(name == "texture_normal") {
number = std::to_string(normalCount++);
}
// Set the uniform to track this texture ID using our naming convention
shader.setUniformValue((name + number).c_str(), i);
}
// Draw the mesh
glDrawElements(GL_TRIANGLES, mIndices.size(),
GL_UNSIGNED_INT, mIndices.data());
glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
// Release shader, textures
for (const auto & texture : mTextures) {
for(const auto & texture : mTextures) {
texture.mTexture->release();
}
shader.release();
@ -145,48 +140,42 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader)
glActiveTexture(GL_TEXTURE0);
}
/*******************************************************************************
* Model Public Member Functions
******************************************************************************/
void Model::draw()
{
for (GLuint i = 0; i < mMeshes.size(); i++) {
mMeshes[i].mTransform = mTransform;
mMeshes[i].draw();
void Model::draw() {
for(auto & mMeshe : mMeshes) {
mMeshe.mTransform = mTransform;
mMeshe.draw();
}
}
void Model::draw(QOpenGLShaderProgram & shader)
{
for (GLuint i = 0; i < mMeshes.size(); i++) {
mMeshes[i].mTransform = mTransform;
mMeshes[i].draw(shader);
void Model::draw(QOpenGLShaderProgram & shader) {
for(auto & mMeshe : mMeshes) {
mMeshe.mTransform = mTransform;
mMeshe.draw(shader);
}
}
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
{
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
bool modified = false;
std::string fullPath = mDirectory + '/' + fileName;
for (auto & texture : mTexturesLoaded) {
if (texture.mPath == fileName) {
for(auto & texture : mTexturesLoaded) {
if(texture.mPath == fileName) {
texture.mTexture->destroy();
texture.mTexture->create();
texture.mTexture->setData(
*Texture::initImage(fullPath.c_str(), flipX, flipY));
*OpenGLTextureFactory::initImage(fullPath.c_str(), flipX, flipY));
modified = true;
}
}
if (!modified) {
if(!modified) {
qDebug() << "Attempt to flip texture that doesn't exist: "
<< fullPath.c_str() << "\n";
}
}
/*******************************************************************************
* Model Private Member Functions
******************************************************************************/
@ -206,31 +195,25 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
*
* @param path Absolute path to a model .obj or other format accepted by assimp
*/
void Model::loadModel(const std::string & path)
{
void Model::loadModel(const std::string & path) {
Assimp::Importer import;
// JIC a relative path was used, get the absolute file path
QFileInfo info(path.c_str());
info.makeAbsolute();
mDirectory = path[0] == ':' ? RM::getPath(path)
: info.absoluteFilePath().toStdString();
: info.absoluteFilePath().toStdString();
// Import the model, converting non-triangular geometry to triangles
// + And flipping texture UVs, etc..
// Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
const aiScene * scene =
import.ReadFile(mDirectory, aiProcess_Triangulate
| aiProcess_FlipUVs
| aiProcess_GenSmoothNormals
| aiProcess_CalcTangentSpace
| aiProcess_OptimizeMeshes
| aiProcess_SplitLargeMeshes
);
const aiScene * scene = import.ReadFile(
mDirectory, aiProcess_Triangulate | aiProcess_FlipUVs
| aiProcess_GenSmoothNormals | aiProcess_CalcTangentSpace
| aiProcess_OptimizeMeshes | aiProcess_SplitLargeMeshes);
// If there were errors, print and return
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
qDebug() << "Error::ASSIMP::" << import.GetErrorString() << "\n";
return;
}
@ -250,28 +233,26 @@ void Model::loadModel(const std::string & path)
mManager.insert(mName, this);
}
void Model::processNode(aiNode * node, const aiScene * scene)
{
void Model::processNode(aiNode * node, const aiScene * scene) {
// Process each mesh that is available for this node
for (GLuint i = 0; i < node->mNumMeshes; i++) {
for(GLuint i = 0; i < node->mNumMeshes; i++) {
aiMesh * mesh = scene->mMeshes[node->mMeshes[i]];
mMeshes.push_back(processMesh(mesh, scene));
}
// Process each child node for this mesh using recursion
for (GLuint i = 0; i < node->mNumChildren; i++) {
for(GLuint i = 0; i < node->mNumChildren; i++) {
processNode(node->mChildren[i], scene);
}
}
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
{
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
ModelMesh::Vertices vertices;
ModelMesh::Indices indices;
ModelMesh::Textures textures;
// For each vertex in the aiMesh
for (GLuint i = 0; i < mesh->mNumVertices; i++) {
for(GLuint i = 0; i < mesh->mNumVertices; i++) {
// Create a local vertex object for positions, normals, and texture coords
ModelVertex vertex;
@ -285,7 +266,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
// Set the position of our local vertex to the local vector object
vertex.mPosition = vector3D;
if (mesh->HasNormals()) {
if(mesh->HasNormals()) {
// Initialize vertex normal
vector3D.setX(mesh->mNormals[i].x);
vector3D.setY(mesh->mNormals[i].y);
@ -295,7 +276,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
}
// Initialize texture coordinates, if any are available
if (mesh->mTextureCoords[0]) {
if(mesh->mTextureCoords[0]) {
QVector2D vector2D;
// Texture coordinates
vector2D.setX(mesh->mTextureCoords[0][i].x);
@ -313,8 +294,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
vector3D.setY(mesh->mBitangents[i].y);
vector3D.setZ(mesh->mBitangents[i].z);
vertex.mBitangent = vector3D;
}
else {
} else {
vertex.mTextureCoord = {0.0f, 0.0f};
}
@ -323,62 +303,56 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
}
// For each face on the mesh, process its indices
for (GLuint i = 0; i < mesh->mNumFaces; i++) {
for(GLuint i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (GLuint j = 0; j < face.mNumIndices; j++) {
for(GLuint j = 0; j < face.mNumIndices; j++) {
// Add the index to out container of indices
indices.push_back(face.mIndices[j]);
}
}
// Process material
if (mesh->mMaterialIndex >= 0) {
if(mesh->mMaterialIndex >= 0) {
// Get the material attached to the model using Assimp
aiMaterial * material = scene->mMaterials[mesh->mMaterialIndex];
// Get all diffuse textures from the material
ModelMesh::Textures diffuseMaps =
loadMaterialTextures(material, aiTextureType_DIFFUSE,
"texture_diffuse");
ModelMesh::Textures diffuseMaps = loadMaterialTextures(
material, aiTextureType_DIFFUSE, "texture_diffuse");
// Insert all diffuse textures found into our textures container
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// Get all specular textures from the material
ModelMesh::Textures specularMaps =
loadMaterialTextures(material, aiTextureType_SPECULAR,
"texture_specular");
ModelMesh::Textures specularMaps = loadMaterialTextures(
material, aiTextureType_SPECULAR, "texture_specular");
// Insert all specular textures found into our textures container
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
// Get all normal textures from the material
ModelMesh::Textures normalMaps =
loadMaterialTextures(material, aiTextureType_HEIGHT,
"texture_normal");
loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
// Insert all normal maps found into our textures container
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
}
return ModelMesh(vertices, indices, textures,
mVertexShader, mFragmentShader);
return {vertices, indices, textures, mVertexShader, mFragmentShader};
}
ModelMesh::Textures Model::loadMaterialTextures(
aiMaterial * mat, aiTextureType type, const std::string & typeName)
{
aiMaterial * mat, aiTextureType type, const std::string & typeName) {
ModelMesh::Textures textures;
for (GLuint i = 0; i < mat->GetTextureCount(type); i++) {
for(GLuint i = 0; i < mat->GetTextureCount(type); i++) {
// Call GetTexture to get the name of the texture file to load
aiString fileName;
mat->GetTexture(type, i, &fileName);
// Check if we have already loaded this texture
bool skip = false;
for (GLuint j = 0; j < mTexturesLoaded.size(); j++) {
for(auto & j : mTexturesLoaded) {
// If the path to the texture already exists in m_texturesLoaded, skip it
if (std::strcmp(mTexturesLoaded[j].mPath.data(), fileName.C_Str()) == 0) {
textures.push_back(mTexturesLoaded[j]);
if(std::strcmp(j.mPath.data(), fileName.C_Str()) == 0) {
textures.push_back(j);
// If we have loaded the texture, do not load it again
skip = true;
break;
@ -386,11 +360,11 @@ ModelMesh::Textures Model::loadMaterialTextures(
}
// If the texture has not yet been loaded
if (!skip) {
if(!skip) {
ModelTexture texture;
texture.mTexture = Texture::initTexture2D(
std::string(mDirectory + '/' + fileName.C_Str()).c_str(),
false, false);
texture.mTexture = OpenGLTextureFactory::initTexture2D(
std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false,
false);
texture.mID = texture.mTexture->textureId();
texture.mType = typeName;
texture.mPath = fileName.C_Str();
@ -399,22 +373,20 @@ ModelMesh::Textures Model::loadMaterialTextures(
// Add the texture to the loaded textures to avoid loading it twice
mTexturesLoaded.push_back(texture);
}
}
// Return the resulting textures
return textures;
}
void Model::sortModels()
{
void Model::sortModels() {
auto cameraPos = Scene::Camera().transform();
auto cameraDistance = [&cameraPos](const ModelMesh &a, const ModelMesh &b)
{
// Sort by the first vertex position, since all transforms will be the same
return (cameraPos.translation().distanceToPoint(a.mVertices[0].mPosition))
< (cameraPos.translation().distanceToPoint(b.mVertices[0].mPosition));
auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) {
// Sort by the first vertex position in the model
return (cameraPos.getTranslation().distanceToPoint(
a.mVertices[0].mPosition))
< (cameraPos.getTranslation().distanceToPoint(
b.mVertices[0].mPosition));
};
std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance);
}

View File

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

View File

@ -15,49 +15,101 @@
#include <mesh.h>
#include <qtkapi.h>
#include <texture.h>
namespace Qtk {
class QTKAPI Object : public QObject {
Q_OBJECT
Q_OBJECT
public:
friend MeshRenderer;
// Initialize an object with no shape data assigned
Object(const char * name)
: mName(name), mVBO(QOpenGLBuffer::VertexBuffer)
{ }
// Initialize an object with shape data assigned
Object(const char * name, const ShapeBase & shape)
: mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape)
{ }
public:
friend MeshRenderer;
~Object() {}
// Initialize an object with no shape data assigned
explicit Object(const char * name) :
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false) {}
inline const Vertices & vertices() { return mShape.mVertices;}
inline const Indices & indices() { return mShape.mIndices;}
inline const Colors & colors() { return mShape.mColors;}
inline const TexCoords & texCoords() { return mShape.mTexCoords;}
inline const Normals & normals() { return mShape.mNormals;}
inline QOpenGLTexture & texture() const { return *mTexture;}
// Initialize an object with shape data assigned
Object(const char * name, const ShapeBase & shape) :
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape),
mBound(false) {}
virtual inline void setVertices(const Vertices & value) { mShape.mVertices = value;}
virtual inline void setIndices(const Indices & value) { mShape.mIndices = value;}
virtual inline void setColors(const Colors & value) { mShape.mColors = value;}
virtual inline void setTexCoords(const TexCoords & value) { mShape.mTexCoords = value;}
virtual inline void setNormals(const Normals & value) { mShape.mNormals = value;}
virtual inline void setShape(const Shape & value) { mShape = value;}
~Object() override = default;
QOpenGLBuffer mVBO, mNBO;
QOpenGLVertexArrayObject mVAO;
QOpenGLShaderProgram mProgram;
inline const Colors & getColors() { return mShape.mColors; }
Transform3D mTransform;
Shape mShape;
inline const Indices & getIndexData() { return mShape.mIndices; }
const char * mName;
private:
QOpenGLTexture * mTexture;
inline const Normals & getNormals() { return mShape.mNormals; }
[[nodiscard]] inline const Shape & getShape() const { return mShape; }
inline const TexCoords & getTexCoords() { return mShape.mTexCoords; }
inline Texture & getTexture() { return mTexture; }
inline const Vertices & getVertices() { return mShape.mVertices; }
virtual inline void setColors(const Colors & value) {
mShape.mColors = value;
}
virtual inline void setIndices(const Indices & value) {
mShape.mIndices = value;
}
virtual inline void setNormals(const Normals & value) {
mShape.mNormals = value;
}
virtual inline void setShape(const Shape & value) { mShape = value; }
virtual inline void setTexCoords(const TexCoords & value) {
mShape.mTexCoords = value;
}
virtual inline void setTexture(
const char * path, bool flipX = false, bool flipY = false) {
mTexture.setTexture(path, flipX, flipY);
}
virtual inline void setCubeMap(const char * path) {
mTexture.setCubeMap(path);
}
virtual inline void setTexture(const Texture & t) {
mTexture.setTexture(t.getPath());
}
virtual inline void setVertices(const Vertices & value) {
mShape.mVertices = value;
}
virtual inline void bindShaders() {
mBound = true;
mProgram.bind();
}
virtual inline void releaseShaders() {
mBound = false;
mProgram.release();
}
QOpenGLBuffer mVBO, mNBO;
QOpenGLVertexArrayObject mVAO;
Transform3D mTransform;
Shape mShape;
Texture mTexture;
const char * mName;
bool mBound;
private:
virtual inline void setTexture(QOpenGLTexture * value) {
mTexture.setTexture(value);
}
QOpenGLShaderProgram mProgram;
};
}
} // namespace Qtk
#endif // QTK_OBJECT_H
#endif // QTK_OBJECT_H

View File

@ -11,13 +11,13 @@
#include <QtCore/QtGlobal>
#ifdef QTK_SHARED
# if defined(QTK_EXPORT)
# define QTKAPI Q_DECL_EXPORT
# else
# define QTKAPI Q_DECL_IMPORT
# endif
#if defined(QTK_EXPORT)
#define QTKAPI Q_DECL_EXPORT
#else
# define QTKAPI
#define QTKAPI Q_DECL_IMPORT
#endif
#else
#define QTKAPI
#endif
#endif //QTK_QTKAPI_H
#endif // QTK_QTKAPI_H

View File

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

View File

@ -15,60 +15,61 @@
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <qtkapi.h>
#include <abstractscene.h>
#include <qtkapi.h>
namespace Qtk {
class QTKAPI QtkWidget : public QOpenGLWidget,
protected QOpenGLFunctions {
Q_OBJECT;
class QTKAPI QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT;
public:
// Constructors
QtkWidget();
explicit QtkWidget(QWidget *parent);
explicit QtkWidget(const QSurfaceFormat &format);
~QtkWidget() override;
public:
// Constructors
QtkWidget();
explicit QtkWidget(QWidget * parent);
explicit QtkWidget(const QSurfaceFormat & format);
~QtkWidget() override;
private:
void teardownGL();
private:
void teardownGL();
public:
// Inherited virtual Members
void paintGL() override;
void initializeGL() override;
void resizeGL(int width, int height) override;
public:
// Inherited virtual Members
void paintGL() override;
void initializeGL() override;
void resizeGL(int width, int height) override;
inline Qtk::Scene * getScene() {return mScene;}
inline void setScene(Qtk::Scene * scene) {
if (mScene != Q_NULLPTR) delete mScene;
mScene = scene;
}
inline Qtk::Scene * getScene() { return mScene; }
protected slots:
void update();
inline void setScene(Qtk::Scene * scene) {
delete mScene;
mScene = scene;
}
protected slots:
void update();
#ifdef QTK_DEBUG
void messageLogged(const QOpenGLDebugMessage &msg);
static void messageLogged(const QOpenGLDebugMessage & msg);
#endif
// Protected Helpers
protected:
void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
// Protected Helpers
protected:
void keyPressEvent(QKeyEvent * event) override;
void keyReleaseEvent(QKeyEvent * event) override;
void mousePressEvent(QMouseEvent * event) override;
void mouseReleaseEvent(QMouseEvent * event) override;
private:
// Private helpers
void initializeWidget();
void updateCameraInput();
private:
// Private helpers
void initializeWidget();
static void updateCameraInput();
Qtk::Scene * mScene;
Qtk::Scene * mScene;
#ifdef QTK_DEBUG
void printContextInformation();
QOpenGLDebugLogger * mDebugLogger;
void printContextInformation();
QOpenGLDebugLogger * mDebugLogger;
#endif
};
}
} // namespace Qtk
#endif // QTK_QTKWIDGET_H
#endif // QTK_QTKWIDGET_H

View File

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

View File

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

View File

@ -8,17 +8,18 @@
#include <QDebug>
#include <QImageReader>
#include <utility>
#include <texture.h>
using namespace Qtk;
QImage * Texture::initImage(const char * image, bool flipX, bool flipY)
{
QImage * OpenGLTextureFactory::initImage(
const char * image, bool flipX, bool flipY) {
// Qt6 limits loaded images to 256MB by default
QImageReader::setAllocationLimit(512);
auto loadedImage = new QImage(QImage(image).mirrored(flipX, flipY));
if (loadedImage->isNull()) {
if(loadedImage->isNull()) {
qDebug() << "Error loading image: " << image << "\n";
qDebug() << QImageReader::supportedImageFormats();
return Q_NULLPTR;
@ -27,46 +28,39 @@ QImage * Texture::initImage(const char * image, bool flipX, bool flipY)
return loadedImage;
}
QOpenGLTexture * Texture::initTexture2D(const char * texture,
bool flipX, bool flipY)
{
QOpenGLTexture * OpenGLTextureFactory::initTexture2D(
const char * texture, bool flipX, bool flipY) {
QImage * image = initImage(texture, flipX, flipY);
auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
newTexture->setData(*image);
newTexture->setWrapMode(QOpenGLTexture::Repeat);
newTexture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,
QOpenGLTexture::Linear);
newTexture->setMinMagFilters(
QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
delete image;
return newTexture;
}
QOpenGLTexture * Texture::initCubeMap(const char * tile)
{
return initCubeMap(QImage(tile), QImage(tile),
QImage(tile), QImage(tile),
QImage(tile), QImage(tile));
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * tile) {
return initCubeMap(
QImage(tile), QImage(tile), QImage(tile), QImage(tile), QImage(tile),
QImage(tile));
}
QOpenGLTexture * Texture::initCubeMap(
const char * right, const char * top,
const char * front, const char * left,
const char * bottom, const char * back)
{
return initCubeMap(QImage(right), QImage(top),
QImage(front), QImage(left),
QImage(bottom), QImage(back));
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
const char * right, const char * top, const char * front, const char * left,
const char * bottom, const char * back) {
return initCubeMap(
QImage(right), QImage(top), QImage(front), QImage(left), QImage(bottom),
QImage(back));
}
QOpenGLTexture * Texture::initCubeMap(
QImage right, QImage top,
QImage front, QImage left,
QImage bottom, QImage back)
{
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
const QImage & right, const QImage & top, const QImage & front,
const QImage & left, const QImage & bottom, const QImage & back) {
auto texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
std::vector<QImage> faceTextures = {
right, top, front,
left, bottom, back
};
std::vector<QImage> faceTextures = {std::move(right), std::move(top),
std::move(front), std::move(left),
std::move(bottom), std::move(back)};
// Initialize skybox cubemap texture
texture->create();
texture->bind();
@ -74,27 +68,27 @@ QOpenGLTexture * Texture::initCubeMap(
std::vector<QOpenGLTexture::CubeMapFace> faces = {
QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::CubeMapPositiveY,
QOpenGLTexture::CubeMapPositiveZ, QOpenGLTexture::CubeMapNegativeX,
QOpenGLTexture::CubeMapNegativeY, QOpenGLTexture::CubeMapNegativeZ
};
QOpenGLTexture::CubeMapNegativeY, QOpenGLTexture::CubeMapNegativeZ};
int i = 0;
for (const auto & face : faces) {
for(const auto & face : faces) {
QImage faceImage(faceTextures[i]);
if (faceImage.isNull()) {
if(faceImage.isNull()) {
qDebug() << "Error loading cube map image\n";
}
faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888);
// On the first iteration, set format and allocate texture storage
if (face == QOpenGLTexture::CubeMapPositiveX) {
if(face == QOpenGLTexture::CubeMapPositiveX) {
// This also needs to happen on the first iteration, anyways
texture->setSize(faceImage.width(), faceImage.height(), faceImage.depth());
texture->setSize(
faceImage.width(), faceImage.height(), faceImage.depth());
texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
texture->allocateStorage();
}
texture->setData(0, 0, face,
QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
faceImage.constBits());
texture->setData(
0, 0, face, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
faceImage.constBits());
i++;
}

View File

@ -10,36 +10,108 @@
#define QTOPENGL_TEXTURE_H
#include <QOpenGLTexture>
#include <utility>
#include <qtkapi.h>
namespace Qtk {
class QTKAPI Texture {
public:
~Texture() {}
class QTKAPI OpenGLTextureFactory {
public:
~OpenGLTextureFactory() = default;
// QImage
static QImage * initImage(const char * image,
bool flipX=false, bool flipY=false);
// QImage
static QImage * initImage(
const char * image, bool flipX = false, bool flipY = false);
// 2D Texture
static QOpenGLTexture * initTexture2D(const char * texture,
bool flipX=false, bool flipY=false);
// 2D Texture
static QOpenGLTexture * initTexture2D(
const char * texture, bool flipX = false, bool flipY = false);
// Cube maps
static QOpenGLTexture * initCubeMap(QImage right, QImage top,
QImage front, QImage left,
QImage bottom, QImage back);
// Overloads for cube map initialization
static QOpenGLTexture * initCubeMap(const char * tile);
static QOpenGLTexture * initCubeMap(const char * right, const char * top,
const char * front, const char * left,
const char * bottom, const char * back);
// Cube maps
static QOpenGLTexture * initCubeMap(
const QImage & right, const QImage & top, const QImage & front,
const QImage & left, const QImage & bottom, const QImage & back);
// Overloads for cube map initialization
static QOpenGLTexture * initCubeMap(const char * tile);
static QOpenGLTexture * initCubeMap(
const char * right, const char * top, const char * front,
const char * left, const char * bottom, const char * back);
private:
// Private ctor to prevent creating instances of this class
Texture() {}
private:
// Private ctor to prevent creating instances of this class
OpenGLTextureFactory() = default;
};
}
#endif // QTOPENGL_TEXTURE_H
// TODO: Struct for (re)storing texture state
class Texture {
public:
Texture() = default;
Texture(const Texture & value) {
mOpenGLTexture = OpenGLTextureFactory::initTexture2D(value.mPath);
mPath = value.mPath;
}
explicit Texture(
const char * path, bool flipX = false, bool flipY = false) :
mOpenGLTexture(
OpenGLTextureFactory::initTexture2D(path, flipX, flipY)),
mPath(path) {}
explicit Texture(QOpenGLTexture * texture) : mOpenGLTexture(texture) {}
~Texture() { mOpenGLTexture->destroy(); }
[[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const {
return *mOpenGLTexture;
}
[[nodiscard]] inline std::string getPath() const { return mPath; }
void setTexture(
const std::string & path, bool flipX = false, bool flipY = false) {
mOpenGLTexture =
OpenGLTextureFactory::initTexture2D(path.data(), flipX, flipY);
mPath = path.data();
}
void setTexture(
const char * path, bool flipX = false, bool flipY = false) {
mOpenGLTexture =
OpenGLTextureFactory::initTexture2D(path, flipX, flipY);
mPath = path;
}
// TODO: This is unsafe because we don't have a path. Encapsulate it.
inline void setTexture(QOpenGLTexture * texture) {
mOpenGLTexture = texture;
}
virtual inline void setCubeMap(const char * path) {
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(path);
mPath = path;
}
virtual inline void setCubeMap(
const char * right, const char * top, const char * front,
const char * left, const char * bottom, const char * back) {
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(
right, top, front, left, bottom, back);
}
virtual inline void setCubeMap(
const QImage & right, const QImage & top, const QImage & front,
const QImage & left, const QImage & bottom, const QImage & back) {
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(
right, top, front, left, bottom, back);
}
[[nodiscard]] inline bool hasTexture() const {
return mOpenGLTexture != Q_NULLPTR;
}
private:
QOpenGLTexture * mOpenGLTexture = Q_NULLPTR;
const char * mPath {};
};
} // namespace Qtk
#endif // QTOPENGL_TEXTURE_H

View File

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

View File

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