Compare commits

..

3 Commits

Author SHA1 Message Date
Shaun Reed f78e41dc76 Work on texture class
+ Set up TextureManager class to handle static texture map
2022-08-06 15:21:20 -04:00
Shaun Reed 26200d39a2 Work on Texture class 2022-08-06 15:21:20 -04:00
Shaun Reed 132491b28d Refactor 2022-08-06 15:21:18 -04:00
47 changed files with 1781 additions and 2575 deletions

View File

@ -1,76 +0,0 @@
---
# 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
...

View File

@ -1,148 +0,0 @@
# Generated from CLion Inspection settings
---
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-narrowing-conversions,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-slicing,
google-default-arguments,
google-explicit-constructor,
google-runtime-operator,
hicpp-exception-baseclass,
hicpp-multiway-paths-covered,
misc-misplaced-const,
misc-new-delete-overloads,
misc-no-recursion,
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-convert-member-functions-to-static,
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

@ -7,49 +7,15 @@ on:
jobs:
Build-Qtk:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH="/home/runner/work/qtk/Qt/6.3.1/gcc_64/"
- os: windows-latest
cmake: -DCMAKE_PREFIX_PATH="D:/a/qtk/qtk/Qt/6.3.1/mingw81_64/"
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH="/home/runner/work/qtk/Qt/6.3.1/gcc_64/"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.3.1'
- name: Chocolatey Action
if: matrix.os == 'windows-latest'
uses: crazy-max/ghaction-chocolatey@v2.0.0
with:
args: install pkgconfiglite
- name: Build Qtk
shell: bash
run: |
cmake -S . -B build/ ${{ matrix.cmake }} && cmake --build build/ \
--target qtk-main
Build-Qtk-Assimp-Targets:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
include:
- os: ubuntu-latest
cmake: -DCMAKE_PREFIX_PATH="/home/runner/work/qtk/Qt/6.3.1/gcc_64/" -DQTK_UPDATE_SUBMODULES=OFF
CMAKE_PARAMS: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.3.1/gcc_64/
- os: macos-latest
cmake: -DCMAKE_PREFIX_PATH="/home/runner/work/qtk/Qt/6.3.1/gcc_64/" -DASSIMP_NEW_INTERFACE=ON -DQTK_UPDATE_SUBMODULES=OFF
CMAKE_PARAMS: -DCMAKE_PREFIX_PATH=/home/runner/work/qtk/Qt/6.3.1/gcc_64/ -DASSIMP_NEW_INTERFACE=on
runs-on: ${{ matrix.os }}
steps:
@ -60,20 +26,20 @@ jobs:
with:
version: '6.3.1'
- name: Install Assimp Linux
if: matrix.os == 'ubuntu-latest'
shell: bash
run: |
sudo apt install libassimp-dev -y
- name: Install Assimp MacOS
if: matrix.os == 'macos-latest'
shell: bash
run: |
brew install assimp
- name: Install Assimp Ubuntu
if: matrix.os == 'ubuntu-latest'
shell: bash
run: |
sudo apt install libassimp-dev
- name: Build Qtk
shell: bash
run: |
cmake -S . -B build/ ${{ matrix.cmake }} && cmake --build build/ \
--target qtk-main
mkdir build && cd build
cmake .. ${{ matrix.CMAKE_PARAMS }} && cmake --build .

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "extern/assimp/assimp"]
path = extern/assimp/assimp
url = https://github.com/assimp/assimp.git

View File

@ -3,7 +3,7 @@
## ##
## Project for working with OpenGL and Qt6 widgets ##
################################################################################
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.5)
project(
#[[NAME]] Qtk
@ -16,213 +16,72 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_compile_options(/wd4131 /wd4127)
# For CLion builds, point CMAKE_PREFIX_PATH to Qt6 install directory
list(APPEND CMAKE_PREFIX_PATH $ENV{HOME}/Code/Clones/Qt/6.3.1/gcc_64/)
find_package(Qt6 COMPONENTS OpenGLWidgets)
if (NOT Qt6_FOUND)
message(SEND_ERROR "Unable to find Qt6 at CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}")
message(FATAL_ERROR "Specify path to Qt6 with `cmake -DCMAKE_PREFIX_PATH=/path/to/Qt/6.x.x/gcc_64 -S /path/to/qtk -B /path/to/qtk/build && cmake --build /path/to/qtk/build -j $(nprocs)`")
endif()
message(STATUS "[Qtk] Compiling with ${CMAKE_CXX_COMPILER_ID}")
# Qtk build options
option(QTK_DEBUG "Enable debugger" ON)
message(STATUS "[Qtk] Compiling with QTK_DEBUG=${QTK_DEBUG}")
option(QTK_UPDATE_SUBMODULES "Update external project (assimp) submodule" ON)
message(
STATUS
"[Qtk] Compiling with QTK_UPDATE_SUBMODULES=${QTK_UPDATE_SUBMODULES}"
)
# Qt options
set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/6.3.1/gcc_64/" CACHE PATH "Path to Qt6")
# Options for bringing your own assimp installation; Otherwise not needed
# + If assimp is available system-wide we can just set QTK_UPDATE_SUBMODULES OFF
option(ASSIMP_NEW_INTERFACE "Use the assimp::assimp interface (WIN / OSX)" OFF)
message(
STATUS
"[Qtk] Compiling with ASSIMP_NEW_INTERFACE=${ASSIMP_NEW_INTERFACE}"
)
################################################################################
# External Libraries
################################################################################
# For CLion builds, point CMAKE_PREFIX_PATH to Qt6 install directory
# + QtCreator will handle this for you if that is used instead
list(APPEND CMAKE_PREFIX_PATH "${QT_DIR}")
# Find Qt
find_package(Qt6 COMPONENTS OpenGLWidgets)
if (NOT Qt6_FOUND)
message(
SEND_ERROR "[Qtk] Error: Unable to find Qt6 at CMAKE_PREFIX_PATH: "
"${CMAKE_PREFIX_PATH}"
)
message(
FATAL_ERROR
"[Qtk] Error: Specify path to Qt6 with `cmake "
"-DCMAKE_PREFIX_PATH=/path/to/Qt/6.x.x/gcc_64 -S /path/to/qtk -B "
"/path/to/qtk/build && cmake --build /path/to/qtk/build -j $(nprocs)`"
)
endif()
if (QTK_UPDATE_SUBMODULES)
message(STATUS "[Qtk] Updating submodules...")
include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake")
submodule_update("${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/")
else()
find_package(assimp REQUIRED)
endif()
################################################################################
# Qtk
################################################################################
set(
PUBLIC_HEADERS
src/qtkwidget.h
src/abstractscene.h
src/camera3d.h
src/mesh.h
src/meshrenderer.h
src/model.h
src/object.h
src/skybox.h
src/texture.h
src/transform3D.h
)
set(
SOURCE_FILES
src/qtkwidget.cpp
src/abstractscene.cpp
src/camera3d.cpp
src/input.cpp
src/input.h
src/mesh.cpp
src/meshrenderer.cpp
src/model.cpp
src/object.cpp
src/qtkapi.h
src/skybox.cpp
src/texture.cpp
src/transform3D.cpp
)
# https://github.com/assimp/assimp/commit/6ac8279977c3a54118551e549d77329497116f66
find_package(assimp REQUIRED)
option(ASSIMP_NEW_INTERFACE "Use assimp::assimp as target instead of assimp" OFF)
include(GenerateExportHeader)
add_library(qtk-widget STATIC ${PUBLIC_HEADERS} ${SOURCE_FILES})
target_include_directories(qtk-widget PRIVATE src/ app/)
set_target_properties(qtk-widget PROPERTIES
PUBLIC_HEADER "${PUBLIC_HEADERS}"
VERSION ${PROJECT_VERSION}
)
target_link_libraries(qtk-widget PUBLIC Qt6::OpenGLWidgets)
target_link_libraries(qtk-widget PUBLIC Qt6::Widgets)
if (QTK_UPDATE_SUBMODULES OR NOT ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk-widget PUBLIC assimp)
elseif(ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk-widget PUBLIC assimp::assimp)
endif()
if(QTK_DEBUG)
message(STATUS "[Qtk] Building with QTK_DEBUG=${QTK_DEBUG}")
target_compile_definitions(qtk-widget PUBLIC QTK_DEBUG)
endif()
if(WIN32)
find_package(OpenGL REQUIRED)
target_link_libraries(qtk-widget PUBLIC OpenGL::GL)
endif()
# Install files
install(TARGETS qtk-widget
# Associate qtk-widget target with qtk-export
EXPORT qtk-export
# <prefix>/bin on DLL systems and <prefix>/lib on non-dll systems
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static
RUNTIME DESTINATION bin
PUBLIC_HEADER DESTINATION include
)
# Install export
# qtkTargets.cmake will only be installed when one of the CONFIGURATIONS is installed
# + The generated import will only reference that qtk configuration
install(EXPORT qtk-export
FILE qtkTargets.cmake
CONFIGURATIONS Debug|Release
DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake
)
################################################################################
# Final Application
################################################################################
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/src/qtkresources.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/src/qtkresources.h"
@ONLY
)
# Add our Qt resources.qrc file to our application
set(QTK_APP_SOURCES app/main.cpp
app/examplescene.cpp app/examplescene.h
app/mainwindow.cpp app/mainwindow.h app/mainwindow.ui
app/resourcemanager.h
src/qtkresources.h.in
set(SOURCES app/main.cpp)
qt6_add_big_resources(SOURCES resources.qrc)
qt_add_executable(qtk ${SOURCES})
set(SOURCES
src/mainwidget.cpp src/mainwidget.h
src/mainwindow.cpp src/mainwindow.h src/mainwindow.ui
src/input.cpp src/input.h
src/mesh.cpp src/mesh.h
src/texture.cpp src/texture.h
src/object.cpp src/object.h
src/meshrenderer.cpp src/meshrenderer.h
src/camera3d.cpp src/camera3d.h
src/skybox.cpp src/skybox.h
src/transform3D.cpp src/transform3D.h
src/model.cpp src/model.h
src/scene.cpp src/scene.h
src/resourcemanager.cpp src/resourcemanager.h
)
qt6_add_big_resources(QTK_APP_SOURCES resources.qrc)
qt_add_library(main-widget STATIC ${SOURCES})
qt_add_executable(qtk-main ${QTK_APP_SOURCES})
target_include_directories(qtk-main PRIVATE src/ app/)
target_include_directories(main-widget PUBLIC src/)
if(ASSIMP_NEW_INTERFACE)
target_link_libraries(main-widget PRIVATE assimp::assimp)
else()
target_link_libraries(main-widget PRIVATE assimp)
endif()
target_link_libraries(main-widget PUBLIC Qt6::OpenGLWidgets)
if(WIN32)
find_package(OpenGL REQUIRED)
target_link_libraries(main-widget PUBLIC OpenGL::GL)
endif()
# Link qtk-main executable to main qtk-widget library
target_link_libraries(qtk-main PUBLIC qtk-widget)
target_link_libraries(qtk PUBLIC main-widget)
set_target_properties(qtk-main PROPERTIES
# Link qtk executable to main main-widget library
set_target_properties(qtk PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
)
install(TARGETS qtk-main
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
generate_export_header(qtk-widget)
if(WIN32)
get_target_property(_qt6_qmake_location Qt6::qmake IMPORTED_LOCATION)
execute_process(COMMAND "${_qt6_qmake_location}" -query QT_INSTALL_PREFIX RESULT_VARIABLE return_code OUTPUT_VARIABLE qt6_install_prefix OUTPUT_STRIP_TRAILING_WHITESPACE)
file(TO_NATIVE_PATH "${qt6_install_prefix}/bin" qt6_install_prefix)
if(TARGET Qt6::windeployqt)
add_custom_command(TARGET qtk-main
POST_BUILD
COMMAND set PATH=%PATH%$<SEMICOLON>${qt6_install_prefix}
COMMAND Qt6::windeployqt --dir "${CMAKE_BINARY_DIR}/windeployqt" "$<TARGET_FILE_DIR:qtk-main>/$<TARGET_FILE_NAME:qtk-main>"
)
install(DIRECTORY "${CMAKE_BINARY_DIR}/windeployqt/" DESTINATION bin)
endif()
if(MSVC AND TARGET Qt6::qmake)
set(VSUSER_FILE ${CMAKE_CURRENT_BINARY_DIR}/qtk-main.vcxproj.user)
file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/extern/assimp/assimp/bin" assimp_bin)
file(WRITE ${VSUSER_FILE} "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
file(APPEND ${VSUSER_FILE} "<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n")
file(APPEND ${VSUSER_FILE} " <PropertyGroup>\n")
file(APPEND ${VSUSER_FILE} " <LocalDebuggerEnvironment>Path=$(SolutionDir)\\lib\\$(Configuration);${qt6_install_prefix};${assimp_bin};$(Path)\n")
file(APPEND ${VSUSER_FILE} "$(LocalDebuggerEnvironment)</LocalDebuggerEnvironment>\n")
file(APPEND ${VSUSER_FILE} " <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n")
file(APPEND ${VSUSER_FILE} " </PropertyGroup>\n")
file(APPEND ${VSUSER_FILE} "</Project>\n")
endif()
endif()
generate_export_header(main-widget)

130
README.md
View File

@ -3,110 +3,31 @@
Practice project for learning about using OpenGL in Qt widget applications.
Model loader using [Assimp](https://assimp.org/) within a Qt widget application.
You can import your own models within `app/examplescene.cpp`, inside the
`ExampleScene::init()` function. Rotations and translations
happen in `ExampleScene::update()`.
You can import your own models within `mainwdget.cpp`, inside the
`MainWidget::initObjects()` function. I've commented throughout the code there
to explain which model or example I'm modifying. Rotations and translations
happen in `MainWidget::update()`, to get textures loading on models look into
[material files](http://www.paulbourke.net/dataformats/mtl/) and see some
examples in the `resources/models/` directory. For more in-depth examples, see
`scene.h` and `scene.cpp`
To get textures loading on models look into [material files](http://www.paulbourke.net/dataformats/mtl/)
and see some examples in the `resources/models/` directory.
### Source Builds
Builds are configured for CLion or [Qt Creator](https://github.com/qt-creator/qt-creator).
Simply open the root `CMakeLists.txt` with either of these editors and configurations will be loaded.
Can be built with cmake manually or using
[Qt Creator](https://github.com/qt-creator/qt-creator).
For the build to be successful, I've found through testing on VMs that the system requires around 6GB of RAM.
This is mostly due to the large .obj files that are built into the project using [Qt Resource System](https://doc.qt.io/qt-6/resources.html)
This project has been ported to Qt6, which is not yet available in Ubuntu apt repositories.
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 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
```
By default, the build will initialize Assimp as a git submodule and build from source.
We can turn this off by setting the `-DQTK_UPDATE_SUBMODULES=OFF` flag when running CMake.
This will greatly increase build speed, but we will need to make sure Assimp is available either system-wide or using a custom `CMAKE_PREFIX_PATH`.
Using `-DQTK_UPDATE_SUBMODULES=ON` supports providing assimp on cross-platform builds (Windows / Mac / Linux) and may be easier to configure.
```bash
sudo apt update -y && sudo apt install freeglut3-dev libassimp-dev cmake build-essential git
git clone https://gitlab.com/shaunrd0/qtk
cmake -DQTK_UPDATE_SUBMODULES=OFF -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
# We can also provide a path to assimp -
#cmake -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
./qtk/build/qtk-main
cmake -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64 -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nprocs)
./qtk/build/qtk
```
#### 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` and `clang-tidy`.
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
```
After this is done, we can check that for the correct version of `15.0.5`.
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
cd build
# Run clang-tidy from within build directory
clang-tidy --fix --config-file=../.clang-tidy ../src/*.cpp ../src/*.h ../app/*.cpp ../app/*.h
```
And finally `clang-format` can be run with git integration (or CLion if you prefer).
Don't forget to commit the reformatted files.
```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.
If you see a small triangle floating by a model it represents the light source
that is being used for the shader rendering the model. These appear on models
@ -122,31 +43,9 @@ Spartan with normals -
![](resources/spartan-normals.png)
### QtkWidget in Qt Creator
The `QtkWidget` class is exported as a shared library for use in Qt Creator's design mode.
We can add more QtkWidgets to view and render the scene from multiple perspectives.
There is still some work to be done here, so there isn't a builtin way to add an additional view within the application.
![](resources/qtk-views.png)
After building Qtk, we can drag and drop an `OpenGL Widget` onto the `mainwindow.ui`.
Then right-click the new OpenGLWidget and `Promote To->QtkWidget` to add a second view.
![](resources/qtk-views-setup.png)
If we demote or delete all widgets in `mainwindow.ui` and rebuild the project, Qt Creator will drop `QtkWidget` from the list of possible promoted widgets.
Add an `OpenGL Widget` to the UI, right-click it and navigate to `Promote Widget...` and enter the information below.
![](resources/qtk-reference.png)
After you fill out the `New Promoted Class` form, click `Add` *and*`Promote`, then rebuild.
After following these steps Qt Creator will list `QtkWidget` as an option to promote `OpenGL Widgets` again.
## Model Artists
"Alien Hominid" (https://skfb.ly/onStx) by Nwilly_art is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
"Scythe World Of Warcraft" (https://skfb.ly/6UooG) by Warcraft-3D-Models is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
@ -160,3 +59,4 @@ Modified (learnopengl.com) material assignment (Joey de Vries) for easier load i
"Terror-bird (NHMW-Geo 2012/0007/0001)" (https://skfb.ly/onAWy) by Natural History Museum Vienna is licensed under Creative Commons Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/).
"Golden Lion Sitting OBJ Low Poly FREE" (https://skfb.ly/onZAH) by LordSamueliSolo is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).

View File

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

View File

@ -1,25 +0,0 @@
#include <mainwindow.h>
#include <qtkwidget.h>
#include "ui_mainwindow.h"
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) {
std::string key = qtkWidget->objectName().toStdString();
if(mScenes[key] == nullptr) {
mScenes[qtkWidget->objectName().toStdString()] = new ExampleScene();
}
qtkWidget->setScene(mScenes[qtkWidget->objectName().toStdString()]);
}
}
setWindowIcon(QIcon("../resources/icon.png"));
}
MainWindow::~MainWindow() {
delete ui;
}

View File

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

View File

@ -1,114 +0,0 @@
<?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>
</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/>
</ui>

View File

@ -1,35 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Manage files and resources used by qtk ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <string>
#include <src/qtkresources.h>
#ifndef QTK_RESOURCEMANAGER_H
#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;
}
} RM;
#endif // QTK_RESOURCEMANAGER_H

View File

@ -1,32 +0,0 @@
################################################################################
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
## ##
## CMake function to update git submodules ##
################################################################################
include_guard()
find_package(Git)
# _PATH: Path to git submodule location that we want to update
# + submodule_update(extern/assimp)
function(submodule_update _PATH)
if (NOT QTK_UPDATE_SUBMODULES)
return()
endif()
if (NOT GIT_FOUND)
message(FATAL_ERROR "[Qtk] Error: No git executable found")
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} submodule update --init --force "${_PATH}"
RESULT_VARIABLE result
)
if (NOT result EQUAL 0)
message(
FATAL_ERROR
"[Qtk] Error: Unable to update git submodule at ${_PATH}"
)
endif()
endfunction()

@ -1 +0,0 @@
Subproject commit bd64cc88dff17f118ecf32ebcbacaf566f6b6449

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

View File

@ -1,54 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Classes for managing objects and data within a scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <abstractscene.h>
#include <camera3d.h>
#include <resourcemanager.h>
#include <texture.h>
using namespace Qtk;
Camera3D Scene::mCamera;
QMatrix4x4 Scene::mProjection;
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
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;
}
delete mSkybox;
}
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();
}
}

View File

@ -1,56 +0,0 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Classes for managing objects and data within a scene ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_SCENE_H
#define QTK_SCENE_H
#include <camera3d.h>
#include <meshrenderer.h>
#include <model.h>
#include <skybox.h>
#include <QMatrix4x4>
namespace Qtk {
class Scene : protected QOpenGLFunctions {
friend class MainWidget;
public:
Scene();
~Scene();
virtual void init() = 0;
virtual void draw() { privDraw(); };
virtual void update() = 0;
static Camera3D & Camera() { return mCamera; }
static QMatrix4x4 View() { return mCamera.toMatrix(); }
static QMatrix4x4 & Projection() { return mProjection; }
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

View File

@ -8,18 +8,19 @@
#include <camera3d.h>
using namespace Qtk;
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.getRotation().conjugated());
@ -27,27 +28,32 @@ const QMatrix4x4 & Camera3D::toMatrix() {
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,44 +11,33 @@
#include <QDebug>
#include <qtkapi.h>
#include <transform3D.h>
namespace Qtk {
class QTKAPI Camera3D {
public:
class Camera3D {
public:
// Constants
static const QVector3D LocalForward;
static const QVector3D LocalUp;
static const QVector3D LocalRight;
// Accessors
inline Transform3D & transform() { return mTransform; }
[[nodiscard]] inline const QVector3D & translation() const {
return mTransform.getTranslation();
}
[[nodiscard]] inline const QQuaternion & rotation() const {
return mTransform.getRotation();
}
inline Transform3D & transform() { return mTransform;}
inline const QVector3D & translation() const
{ return mTransform.getTranslation();}
inline const QQuaternion & rotation() const
{ return mTransform.getRotation();}
const QMatrix4x4 & toMatrix();
// Queries
[[nodiscard]] inline QVector3D forward() const {
return mTransform.getRotation().rotatedVector(LocalForward);
}
inline QVector3D forward() const
{ return mTransform.getRotation().rotatedVector(LocalForward);}
inline QVector3D right() const
{ return mTransform.getRotation().rotatedVector(LocalRight);}
inline QVector3D up() const
{ return mTransform.getRotation().rotatedVector(LocalUp);}
[[nodiscard]] inline QVector3D right() const {
return mTransform.getRotation().rotatedVector(LocalRight);
}
[[nodiscard]] inline QVector3D up() const {
return mTransform.getRotation().rotatedVector(LocalUp);
}
private:
private:
Transform3D mTransform;
QMatrix4x4 mWorld;
@ -56,19 +45,18 @@ namespace Qtk {
friend QDataStream & operator<<(QDataStream & out, Camera3D & transform);
friend QDataStream & operator>>(QDataStream & in, Camera3D & transform);
#endif
};
};
Q_DECLARE_TYPEINFO(Camera3D, Q_MOVABLE_TYPE);
// Qt Streams
#ifndef QT_NO_DATASTREAM
QDataStream & operator<<(QDataStream & out, const Camera3D & transform);
QDataStream & operator>>(QDataStream & in, Camera3D & transform);
QDataStream & operator<<(QDataStream & out, const Camera3D & transform);
QDataStream & operator>>(QDataStream & in, Camera3D & transform);
#endif
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Camera3D & transform);
QDebug operator<<(QDebug dbg, const Camera3D & transform);
#endif
} // namespace Qtk
Q_DECLARE_TYPEINFO(Qtk::Camera3D, Q_MOVABLE_TYPE);
#endif // QTK_CAMERA3D_H

View File

@ -13,24 +13,23 @@
#include <input.h>
using namespace Qtk;
/*******************************************************************************
* Static Helper Structs
******************************************************************************/
template <typename T> struct InputInstance : std::pair<T, Input::InputState> {
template <typename T>
struct InputInstance : std::pair<T, Input::InputState>
{
typedef std::pair<T, Input::InputState> base_class;
// Disable clang-tidy from marking this ctor explicit
// NOLINTNEXTLINE
inline InputInstance(T value) : base_class(value, Input::InputInvalid) {}
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,20 +48,26 @@ 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;
@ -78,16 +83,19 @@ template <typename TPair> 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
auto remove =
Iter remove =
std::remove_if(container.begin(), container.end(), &CheckReleased<TPair>);
container.erase(remove, container.end());
@ -95,29 +103,35 @@ template <typename Container> static inline void Update(Container & container) {
std::for_each(container.begin(), container.end(), &UpdateStates<TPair>);
}
/*******************************************************************************
* Input Implementation
******************************************************************************/
Input::InputState Input::keyState(Qt::Key k) {
auto it = FindKey(k);
Input::InputState Input::keyState(Qt::Key k)
{
KeyContainer::iterator it = FindKey(k);
return (it != sg_keyInstances.end()) ? it->second : InputInvalid;
}
Input::InputState Input::buttonState(Qt::MouseButton k) {
auto it = FindButton(k);
Input::InputState Input::buttonState(Qt::MouseButton k)
{
ButtonContainer::iterator 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();
@ -128,35 +142,44 @@ void Input::update() {
Update(sg_keyInstances);
}
void Input::registerKeyPress(int k) {
auto it = FindKey((Qt::Key)k);
if(it == sg_keyInstances.end()) {
void Input::registerKeyPress(int k)
{
KeyContainer::iterator it = FindKey((Qt::Key)k);
if (it == sg_keyInstances.end())
{
sg_keyInstances.push_back(KeyInstance((Qt::Key)k, InputRegistered));
}
}
void Input::registerKeyRelease(int k) {
auto it = FindKey((Qt::Key)k);
if(it != sg_keyInstances.end()) {
void Input::registerKeyRelease(int k)
{
KeyContainer::iterator it = FindKey((Qt::Key)k);
if (it != sg_keyInstances.end())
{
it->second = InputUnregistered;
}
}
void Input::registerMousePress(Qt::MouseButton btn) {
auto it = FindButton(btn);
if(it == sg_buttonInstances.end()) {
void Input::registerMousePress(Qt::MouseButton btn)
{
ButtonContainer::iterator it = FindButton(btn);
if (it == sg_buttonInstances.end())
{
sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered));
}
}
void Input::registerMouseRelease(Qt::MouseButton btn) {
auto it = FindButton(btn);
if(it != sg_buttonInstances.end()) {
void Input::registerMouseRelease(Qt::MouseButton btn)
{
ButtonContainer::iterator 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

@ -12,16 +12,14 @@
#include <QPoint>
#include <Qt>
#include <qtkapi.h>
#include <qtkwidget.h>
namespace Qtk {
class QTKAPI Input {
friend class Qtk::QtkWidget;
class Input {
friend class MainWidget;
public:
public:
// Possible key states
enum InputState {
enum InputState
{
InputInvalid,
InputRegistered,
InputUnregistered,
@ -31,29 +29,18 @@ namespace Qtk {
};
// 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;
}
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;}
// Implementation
static InputState keyState(Qt::Key key);
@ -62,7 +49,8 @@ namespace Qtk {
static QPoint mousePosition();
static QPoint mouseDelta();
private:
private:
// State updating
static void update();
static void registerKeyPress(int key);
@ -70,7 +58,6 @@ namespace Qtk {
static void registerMousePress(Qt::MouseButton button);
static void registerMouseRelease(Qt::MouseButton button);
static void reset();
};
} // namespace Qtk
};
#endif // QTOPENGL_INPUT_H

View File

@ -8,61 +8,124 @@
#include <QKeyEvent>
#include <abstractscene.h>
#include <input.h>
#include <mesh.h>
#include <qtkwidget.h>
#include <object.h>
#include <scene.h>
#include <mainwidget.h>
using namespace Qtk;
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
QtkWidget::QtkWidget() : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
MainWidget::MainWidget() : mDebugLogger(Q_NULLPTR)
{
initializeWidget();
}
// Constructor for using this widget in QtDesigner
QtkWidget::QtkWidget(QWidget * parent) :
QOpenGLWidget(parent), mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
MainWidget::MainWidget(QWidget *parent) : QOpenGLWidget(parent), mDebugLogger(Q_NULLPTR)
{
initializeWidget();
}
QtkWidget::QtkWidget(const QSurfaceFormat & format) :
mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
MainWidget::MainWidget(const QSurfaceFormat &format)
: mDebugLogger(Q_NULLPTR)
{
setFormat(format);
setFocusPolicy(Qt::ClickFocus);
}
QtkWidget::~QtkWidget() {
MainWidget::~MainWidget()
{
makeCurrent();
teardownGL();
}
/*******************************************************************************
* Private Member Functions
******************************************************************************/
void QtkWidget::teardownGL() {
void MainWidget::teardownGL()
{
// Nothing to teardown yet...
}
void MainWidget::initObjects()
{
mScene = new Scene;
// Drawing a primitive object using Qt and OpenGL
// The Object class only stores basic QOpenGL* members and shape data
// + Within mainwidget, mObject serves as a basic QOpenGL example
mObject = new Object("testObject");
mObject->setVertices(Cube(QTK_DRAW_ELEMENTS).getVertices());
mObject->setIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData());
mObject->mProgram.create();
mObject->mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
":/solid-ambient.vert");
mObject->mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
":/solid-ambient.frag");
mObject->mProgram.link();
mObject->mProgram.bind();
mObject->mVAO.create();
mObject->mVAO.bind();
mObject->mVBO.create();
mObject->mVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
mObject->mVBO.bind();
mObject->mVBO.allocate(
mObject->getVertices().data(),
mObject->getVertices().size() * sizeof(mObject->getVertices()[0])
);
mObject->mProgram.enableAttributeArray(0);
mObject->mProgram.setAttributeBuffer(
0, GL_FLOAT, 0, 3, sizeof(mObject->getVertices()[0])
);
mObject->mProgram.setUniformValue("uColor", QVector3D(1.0f, 0.0f, 0.0f));
mObject->mProgram.setUniformValue("uLightColor", WHITE);
mObject->mProgram.setUniformValue("uAmbientStrength", 0.75f);
mObject->mVBO.release();
mObject->mVAO.release();
mObject->mProgram.release();
mObject->mTransform.setTranslation(13.0f, 0.0f, -2.0f);
}
/*******************************************************************************
* Inherited Virtual Member Functions
******************************************************************************/
void QtkWidget::paintGL() {
void MainWidget::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();
}
// Draw any additional objects within mainwidget manually
mObject->mProgram.bind();
mObject->mVAO.bind();
mObject->mProgram.setUniformValue("uModel", mObject->mTransform.toMatrix());
mObject->mProgram.setUniformValue("uView", Scene::Camera().toMatrix());
mObject->mProgram.setUniformValue("uProjection", Scene::Projection());
glDrawElements(GL_TRIANGLES, mObject->getIndexData().size(),
GL_UNSIGNED_INT, mObject->getIndexData().data());
mObject->mVAO.release();
mObject->mProgram.release();
}
void QtkWidget::initializeGL() {
void MainWidget::initializeGL()
{
initializeOpenGLFunctions();
// Connect the frameSwapped signal to call the update() function
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
@ -70,18 +133,17 @@ 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
printContextInformation();
// Initialize opengl settings
// Initialize opengl settings
glEnable(GL_MULTISAMPLE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
@ -90,33 +152,40 @@ void QtkWidget::initializeGL() {
glClearDepth(1.0f);
glClearColor(0.0f, 0.25f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Initialize default objects within the scene
initObjects();
}
void QtkWidget::resizeGL(int width, int height) {
void MainWidget::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 MainWidget::update()
{
updateCameraInput();
if(mScene != Q_NULLPTR) {
mScene->update();
}
QWidget::update();
}
void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
void MainWidget::messageLogged(const QOpenGLDebugMessage &msg)
{
QString error;
// Format based on severity
switch(msg.severity()) {
switch (msg.severity())
{
case QOpenGLDebugMessage::NotificationSeverity:
error += "--";
break;
@ -134,11 +203,9 @@ 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);
@ -152,11 +219,9 @@ 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);
@ -174,12 +239,14 @@ 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 MainWidget::keyPressEvent(QKeyEvent *event)
{
if (event->isAutoRepeat()) {
// Do not repeat input while a key is held down
event->ignore();
} else {
@ -187,27 +254,32 @@ void QtkWidget::keyPressEvent(QKeyEvent * event) {
}
}
void QtkWidget::keyReleaseEvent(QKeyEvent * event) {
if(event->isAutoRepeat()) {
void MainWidget::keyReleaseEvent(QKeyEvent *event)
{
if (event->isAutoRepeat()) {
event->ignore();
} else {
Input::registerKeyRelease(event->key());
}
}
void QtkWidget::mousePressEvent(QMouseEvent * event) {
void MainWidget::mousePressEvent(QMouseEvent *event)
{
Input::registerMousePress(event->button());
}
void QtkWidget::mouseReleaseEvent(QMouseEvent * event) {
void MainWidget::mouseReleaseEvent(QMouseEvent *event)
{
Input::registerMouseRelease(event->button());
}
/*******************************************************************************
* Private Helpers
******************************************************************************/
void QtkWidget::initializeWidget() {
void MainWidget::initializeWidget()
{
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
@ -224,7 +296,8 @@ void QtkWidget::initializeWidget() {
setFocusPolicy(Qt::ClickFocus);
}
void QtkWidget::printContextInformation() {
void MainWidget::printContextInformation()
{
QString glType;
QString glVersion;
QString glProfile;
@ -232,62 +305,65 @@ 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 MainWidget::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

@ -18,49 +18,45 @@
#define QTK_DEBUG
class MeshRenderer;
class Model;
class Object;
class Scene;
class Skybox;
class OpenGLTextureFactory;
class MainWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT;
class MainWidget : public QOpenGLWidget,
protected QOpenGLFunctions {
Q_OBJECT;
public:
public:
// Constructors
MainWidget();
explicit MainWidget(QWidget * parent);
explicit MainWidget(const QSurfaceFormat & format);
explicit MainWidget(QWidget *parent);
explicit MainWidget(const QSurfaceFormat &format);
~MainWidget() override;
private:
private:
void teardownGL();
void initObjects();
public:
public:
// Inherited virtual Members
void paintGL() override;
void initializeGL() override;
void resizeGL(int width, int height) override;
protected slots:
protected slots:
void update();
void messageLogged(const QOpenGLDebugMessage & msg);
void messageLogged(const QOpenGLDebugMessage &msg);
// Protected Helpers
protected:
void keyPressEvent(QKeyEvent * event);
void keyReleaseEvent(QKeyEvent * event);
void mousePressEvent(QMouseEvent * event);
void mouseReleaseEvent(QMouseEvent * event);
protected:
void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
private:
// Private helpers
void initializeWidget();
void printContextInformation();

15
src/mainwindow.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowIcon(QIcon("../resources/icon.png"));
}
MainWindow::~MainWindow()
{
delete ui;
}

24
src/mainwindow.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "main-widget_export.h"
namespace Ui {
class MainWindow;
}
class MAIN_WIDGET_EXPORT MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

104
src/mainwindow.ui Normal file
View File

@ -0,0 +1,104 @@
<?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="MainWidget" name="openGLWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>775</width>
<height>550</height>
</rect>
</property>
</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>MainWidget</class>
<extends>QOpenGLWidget</extends>
<header>mainwidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -8,80 +8,101 @@
#include <mesh.h>
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,
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,
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,
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,
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,
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};
VERTEX_FTR, VERTEX_FBR, VERTEX_BBR,
VERTEX_BBR, VERTEX_BTR, VERTEX_FTR
};
mColors = {// Face 1 (Front)
RED, GREEN, BLUE, BLUE, WHITE, RED,
mColors = {
// Face 1 (Front)
RED, GREEN, BLUE,
BLUE, WHITE, RED,
// Face 2 (Back)
YELLOW, CYAN, MAGENTA, CYAN, YELLOW, BLACK,
YELLOW, CYAN, MAGENTA,
CYAN, YELLOW, BLACK,
// Face 3 (Top)
RED, MAGENTA, CYAN, CYAN, GREEN, RED,
RED, MAGENTA, CYAN,
CYAN, GREEN, RED,
// Face 4 (Bottom)
WHITE, BLUE, BLACK, BLACK, YELLOW, WHITE,
WHITE, BLUE, BLACK,
BLACK, YELLOW, WHITE,
// Face 5 (Left)
BLUE, GREEN, CYAN, BLUE, CYAN, BLACK,
BLUE, GREEN, CYAN,
BLUE, CYAN, BLACK,
// Face 6 (Right)
RED, WHITE, YELLOW, YELLOW, MAGENTA, RED};
RED, WHITE, YELLOW,
YELLOW, MAGENTA, RED
};
mTexCoords = {// Face 1 (Front)
UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, 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,
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,
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,
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,
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};
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
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)
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,
@ -92,14 +113,16 @@ Cube::Cube(DrawMode mode) {
// Face 5 (Left)
2, 1, 5, 2, 5, 6,
// Face 6 (Right)
0, 3, 7, 7, 4, 0};
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)
mVertices = {
// Face 1 (Front)
// 0 1 2 3
VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
// Face 2 (Back)
@ -116,9 +139,11 @@ Cube::Cube(DrawMode mode) {
VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL,
// Face 6 (Right)
// 20 21 22 23
VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR};
VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR
};
mIndices = {// Face 1 (Front)
mIndices = {
// Face 1 (Front)
0, 1, 2, 2, 3, 0,
// Face 2 (Back)
4, 5, 6, 6, 7, 4,
@ -130,7 +155,8 @@ Cube::Cube(DrawMode mode) {
// Face 5 (Left)
16, 17, 18, 18, 19, 16,
// Face 6 (Right)
20, 21, 22, 22, 23, 20};
20, 21, 22, 22, 23, 20
};
mNormals = {
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD,
@ -143,131 +169,84 @@ Cube::Cube(DrawMode mode) {
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};
@ -282,48 +261,36 @@ Triangle::Triangle(DrawMode mode) {
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 = {
@ -338,42 +305,32 @@ Triangle::Triangle(DrawMode mode) {
};
// 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,24 @@
#include <QOpenGLWidget>
#include <QVector2D>
#include <QVector3D>
#include <utility>
#include <qtkapi.h>
#include <transform3D.h>
namespace Qtk {
class MeshRenderer;
class Object;
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_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 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_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
#define VERTEX_BBR QVector3D( 0.5f, -0.5f, -0.5f) // 8
// Direction vectors
#define VECTOR_UP QVector3D(0.0f, 1.0f, 0.0f)
@ -47,20 +43,18 @@ namespace Qtk {
#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
#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
@ -77,46 +71,28 @@ namespace Qtk {
#define UV_RIGHT QVector2D(0.0f, 1.0f)
#define UV_CORNER QVector2D(1.0f, 1.0f)
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
};
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;
struct QTKAPI ShapeBase {
explicit ShapeBase(
DrawMode mode = QTK_DRAW_ARRAYS, Vertices v = {}, Indices i = {},
Colors c = {}, TexCoords t = {}, Normals n = {}) :
mVertices(std::move(std::move(v))),
mColors(std::move(std::move(c))), mIndices(std::move(std::move(i))),
mTexCoords(std::move(std::move(t))),
mNormals(std::move(std::move(n))) {}
enum DrawMode { QTK_DRAW_ARRAYS, QTK_DRAW_ELEMENTS, QTK_DRAW_ELEMENTS_NORMALS };
[[nodiscard]] inline const Vertices & getVertices() const {
return mVertices;
}
struct 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)
{}
[[nodiscard]] inline const Indices & getIndexData() const {
return mIndices;
}
inline const Vertices & getVertices() const { return mVertices;}
inline const Indices & getIndexData() const { return mIndices;}
inline const Colors & getColors() const { return mColors;}
inline const TexCoords & getTexCoords() const { return mTexCoords;}
inline const Normals & getNormals() const { return 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;
}
protected:
protected:
DrawMode mDrawMode;
Vertices mVertices;
@ -124,47 +100,33 @@ namespace Qtk {
Indices mIndices;
TexCoords mTexCoords;
Normals mNormals;
};
};
struct Shape : public ShapeBase {
struct Shape : public ShapeBase {
friend MeshRenderer;
friend Object;
Shape () {}
Shape(const ShapeBase & rhs) : ShapeBase(rhs) {}
Shape() = default;
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;}
};
explicit Shape(const ShapeBase & rhs) : ShapeBase(rhs) {}
// Primitives inherit from ShapeBase, does not allow setting of shape values
class Mesh {
virtual inline void setVertices(const Vertices & value) {
mVertices = value;
}
};
virtual inline void setIndices(const Indices & value) {
mIndices = value;
}
struct Cube : public ShapeBase {
Cube(DrawMode mode=QTK_DRAW_ARRAYS);
};
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 {};
struct QTKAPI Cube : public ShapeBase {
explicit Cube(DrawMode mode = QTK_DRAW_ARRAYS);
};
struct QTKAPI Triangle : public ShapeBase {
explicit Triangle(DrawMode mode = QTK_DRAW_ARRAYS);
};
} // namespace Qtk
struct Triangle : public ShapeBase {
Triangle(DrawMode mode=QTK_DRAW_ARRAYS);
};
#endif // QTK_MESH_H

View File

@ -8,56 +8,54 @@
#include <QImageReader>
#include <abstractscene.h>
#include <meshrenderer.h>
#include <scene.h>
#include <texture.h>
using namespace Qtk;
#include <meshrenderer.h>
// Static QHash that holds all MeshRenderer instances using their mName as keys
Qtk::MeshRenderer::MeshManager Qtk::MeshRenderer::sInstances;
MeshRenderer::MeshManager MeshRenderer::sInstances;
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
Object(name, shape), mVertexShader(":/multi-color.vert"),
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape)
: Object(name, shape), mVertexShader(":/multi-color.vert"),
mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES),
mHasTexture(false) {
mHasTexture(false)
{
mShape = Shape(shape);
init();
sInstances.insert(name, this);
}
MeshRenderer::~MeshRenderer() {
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();
@ -71,16 +69,18 @@ void MeshRenderer::init() {
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, getVertices().size() * sizeof(getVertices()[0]), 3,
sizeof(QVector3D));
mProgram.setAttributeBuffer(1, GL_FLOAT,
getVertices().size() * sizeof(getVertices()[0]),
3, sizeof(QVector3D));
mVBO.release();
@ -88,7 +88,8 @@ void MeshRenderer::init() {
mVAO.release();
}
void MeshRenderer::draw() {
void MeshRenderer::draw()
{
mProgram.bind();
mVAO.bind();
@ -99,14 +100,13 @@ void MeshRenderer::draw() {
// TODO: Automate uniforms some other way
setUniformMVP();
if(mShape.mDrawMode == QTK_DRAW_ARRAYS) {
if (mShape.mDrawMode == QTK_DRAW_ARRAYS) {
glDrawArrays(mDrawType, 0, getVertices().size());
} else if(
mShape.mDrawMode == QTK_DRAW_ELEMENTS
}
else if (mShape.mDrawMode == QTK_DRAW_ELEMENTS
|| mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) {
glDrawElements(
mDrawType, mShape.mIndices.size(), GL_UNSIGNED_INT,
mShape.mIndices.data());
glDrawElements(mDrawType, mShape.mIndices.size(),
GL_UNSIGNED_INT, mShape.mIndices.data());
}
if(mTexture.hasTexture()) {
@ -117,36 +117,41 @@ void MeshRenderer::draw() {
mProgram.release();
}
void MeshRenderer::setShaders(
const std::string & vert, const std::string & frag) {
void MeshRenderer::setShaders(const std::string & vert, const std::string & frag)
{
mVertexShader = vert;
mFragmentShader = frag;
}
void MeshRenderer::setUniformMVP(
const char * model, const char * view, const char * projection) {
void MeshRenderer::setUniformMVP(const char * model, const char * view,
const char * projection)
{
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.getVertices()) {
void MeshRenderer::setColor(const QVector3D & color)
{
if (mShape.mColors.empty()) {
for (const auto & vertex : mShape.getVertices()) {
mShape.mColors.push_back(color);
}
} else {
for(int i = 0; i < mShape.getColors().size(); i++) {
}
else {
for (int i = 0; i < mShape.getColors().size(); i++) {
mShape.mColors[i] = color;
}
}
}
/*******************************************************************************
* Inherited Virtual Member Functions
******************************************************************************/
void MeshRenderer::setShape(const Shape & value) {
void MeshRenderer::setShape(const Shape & value)
{
Object::setShape(value);
init();
}

View File

@ -10,26 +10,23 @@
#include <mesh.h>
#include <object.h>
#include <qtkapi.h>
#include <utility>
namespace Qtk {
class QTKAPI MeshRenderer : public Object {
public:
class 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)) {}
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;
MeshRenderer(const char * name, const ShapeBase &shape);
~MeshRenderer();
// Retrieve a mesh by name stored within a static QHash
static MeshRenderer * getInstance(const QString & name);
@ -38,33 +35,26 @@ namespace Qtk {
void draw();
// Draw types like GL_TRIANGLES, GL_POINTS, GL_LINES, etc
void setDrawType(int drawType) { mDrawType = drawType; }
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;
}
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(int location, T value)
{ mProgram.setUniformValue(location, value);}
template <typename T>
inline void setUniform(const char * location, T value) {
mProgram.setUniformValue(location, value);
}
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");
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
@ -73,14 +63,12 @@ namespace Qtk {
// Static QHash of all mesh objects within the scene
typedef QHash<QString, MeshRenderer *> MeshManager;
private:
private:
static MeshManager sInstances;
int mDrawType;
bool mHasTexture;
std::string mVertexShader, mFragmentShader;
};
} // namespace Qtk
};
#endif // QTK_MESHRENDERER_H

View File

@ -9,25 +9,28 @@
#include <QFileInfo>
#include <abstractscene.h>
#include <model.h>
#include <resourcemanager.h>
#include <scene.h>
#include <texture.h>
#include <resourcemanager.h>
#include <model.h>
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
@ -41,12 +44,14 @@ 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
@ -57,40 +62,46 @@ 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,
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();
@ -103,7 +114,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();
@ -113,26 +124,20 @@ 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();
@ -140,29 +145,33 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
glActiveTexture(GL_TEXTURE0);
}
/*******************************************************************************
* Model Public Member Functions
******************************************************************************/
void Model::draw() {
for(auto & mMeshe : mMeshes) {
mMeshe.mTransform = mTransform;
mMeshe.draw();
void Model::draw()
{
for (GLuint i = 0; i < mMeshes.size(); i++) {
mMeshes[i].mTransform = mTransform;
mMeshes[i].draw();
}
}
void Model::draw(QOpenGLShaderProgram & shader) {
for(auto & mMeshe : mMeshes) {
mMeshe.mTransform = mTransform;
mMeshe.draw(shader);
void Model::draw(QOpenGLShaderProgram & shader)
{
for (GLuint i = 0; i < mMeshes.size(); i++) {
mMeshes[i].mTransform = mTransform;
mMeshes[i].draw(shader);
}
}
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
{
bool modified = false;
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(
@ -170,12 +179,14 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
modified = true;
}
}
if(!modified) {
if (!modified) {
qDebug() << "Attempt to flip texture that doesn't exist: "
<< fullPath.c_str() << "\n";
}
}
/*******************************************************************************
* Model Private Member Functions
******************************************************************************/
@ -195,7 +206,8 @@ 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
@ -207,13 +219,18 @@ void Model::loadModel(const std::string & path) {
// 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;
}
@ -233,26 +250,28 @@ 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;
@ -266,7 +285,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);
@ -276,7 +295,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);
@ -294,7 +313,8 @@ 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};
}
@ -303,56 +323,62 @@ 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 {vertices, indices, textures, mVertexShader, mFragmentShader};
return ModelMesh(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(auto & j : mTexturesLoaded) {
for (GLuint j = 0; j < mTexturesLoaded.size(); j++) {
// If the path to the texture already exists in m_texturesLoaded, skip it
if(std::strcmp(j.mPath.data(), fileName.C_Str()) == 0) {
textures.push_back(j);
if (std::strcmp(mTexturesLoaded[j].mPath.data(), fileName.C_Str()) == 0) {
textures.push_back(mTexturesLoaded[j]);
// If we have loaded the texture, do not load it again
skip = true;
break;
@ -360,11 +386,11 @@ ModelMesh::Textures Model::loadMaterialTextures(
}
// If the texture has not yet been loaded
if(!skip) {
if (!skip) {
ModelTexture texture;
texture.mTexture = OpenGLTextureFactory::initTexture2D(
std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false,
false);
std::string(mDirectory + '/' + fileName.C_Str()).c_str(),
false, false);
texture.mID = texture.mTexture->textureId();
texture.mType = typeName;
texture.mPath = fileName.C_Str();
@ -373,20 +399,23 @@ 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) {
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));
return (cameraPos.getTranslation().distanceToPoint(a.mVertices[0].mPosition))
< (cameraPos.getTranslation().distanceToPoint(b.mVertices[0].mPosition));
};
std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance);
}

View File

@ -16,69 +16,66 @@
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLFunctions>
// 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>
#include <object.h>
namespace Qtk {
struct QTKAPI ModelVertex {
struct ModelVertex {
QVector3D mPosition;
QVector3D mNormal;
QVector3D mTangent;
QVector3D mBitangent;
QVector2D mTextureCoord;
};
};
struct QTKAPI ModelTexture {
struct ModelTexture {
GLuint mID;
QOpenGLTexture * mTexture;
std::string mType;
std::string mPath;
};
};
class Model;
class Model;
class QTKAPI ModelMesh : protected QOpenGLFunctions {
public:
class ModelMesh : protected QOpenGLFunctions {
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),
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);
}
mVertices(std::move(vertices)),
mIndices(std::move(indices)),
mTextures(std::move(textures))
{ initMesh(vertexShader, fragmentShader);}
~ModelMesh() {}
~ModelMesh() = default;
private:
private:
void initMesh(const char * vert, const char * frag);
// ModelMesh Private Members
QOpenGLBuffer *mVBO, *mEBO;
QOpenGLBuffer * mVBO, * mEBO;
QOpenGLVertexArrayObject * mVAO;
QOpenGLShaderProgram * mProgram;
public:
inline void draw() { draw(*mProgram); }
public:
inline void draw() { draw(*mProgram);}
void draw(QOpenGLShaderProgram & shader);
// ModelMesh Public Members
@ -86,31 +83,31 @@ namespace Qtk {
Indices mIndices;
Textures mTextures;
Transform3D mTransform;
};
};
class QTKAPI Model : public QObject {
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);
}
class Model : public QObject {
Q_OBJECT
inline ~Model() override { mManager.remove(mName); }
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);
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) {
template <typename T>
void setUniform(const char * location, T value)
{
for (auto & mesh : mMeshes) {
mesh.mProgram->bind();
mesh.mProgram->setUniformValue(location, value);
@ -124,14 +121,13 @@ namespace Qtk {
static Model * getInstance(const char * name);
typedef QHash<QString, Model *> ModelManager;
private:
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);
ModelMesh::Textures loadMaterialTextures(aiMaterial * mat, aiTextureType type,
const std::string & typeName);
void sortModels();
// Model Private Members
@ -139,8 +135,7 @@ namespace Qtk {
ModelMesh::Textures mTexturesLoaded;
std::vector<ModelMesh> mMeshes;
std::string mDirectory;
const char *mVertexShader, *mFragmentShader, *mName;
};
} // namespace Qtk
const char * mVertexShader, * mFragmentShader, * mName;
};
#endif // QTK_MODEL_H

View File

@ -7,5 +7,3 @@
##############################################################################*/
#include <object.h>
using namespace Qtk;

View File

@ -14,71 +14,42 @@
#include <QOpenGLVertexArrayObject>
#include <mesh.h>
#include <qtkapi.h>
#include <texture.h>
namespace Qtk {
class QTKAPI Object : public QObject {
Q_OBJECT
public:
class Object : public QObject {
Q_OBJECT
public:
friend MeshRenderer;
// Initialize an object with no shape data assigned
explicit Object(const char * name) :
mName(name), mVBO(QOpenGLBuffer::VertexBuffer) {}
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) {}
Object(const char * name, const ShapeBase & shape)
: mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape)
{ }
~Object() override = default;
~Object() {}
inline const Colors & getColors() { return mShape.mColors; }
inline const Colors & getColors() { return mShape.mColors;}
inline const Indices & getIndexData() { return mShape.mIndices;}
inline const Normals & getNormals() { return mShape.mNormals;}
inline const Shape & getShape() { return mShape;}
inline const TexCoords & getTexCoords() { return mShape.mTexCoords;}
inline Texture & getTexture() { return mTexture;}
inline const Vertices & getVertices() { return mShape.mVertices;}
inline const Indices & getIndexData() { return mShape.mIndices; }
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(
OpenGLTextureFactory::initTexture2D(path, flipX, flipY));
}
virtual inline void setTexture(QOpenGLTexture * value) {
mTexture.setTexture(value);
}
virtual inline void setVertices(const Vertices & value) {
mShape.mVertices = value;
}
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(OpenGLTextureFactory::initTexture2D(path, flipX, flipY));}
virtual inline void setTexture(QOpenGLTexture * value) { mTexture.setTexture(value);}
virtual inline void setVertices(const Vertices & value) { mShape.mVertices = value;}
QOpenGLBuffer mVBO, mNBO;
QOpenGLVertexArrayObject mVAO;
@ -89,7 +60,6 @@ namespace Qtk {
Texture mTexture;
const char * mName;
};
} // namespace Qtk
};
#endif // QTK_OBJECT_H

View File

@ -1,2 +0,0 @@
#define QTK_RESOURCES "@QTK_RESOURCES@"

View File

@ -1,75 +0,0 @@
/*##############################################################################
## 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_QTKWIDGET_H
#define QTK_QTKWIDGET_H
#include <iostream>
#include <QMatrix4x4>
#include <QOpenGLDebugLogger>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <abstractscene.h>
#include <qtkapi.h>
namespace Qtk {
class QTKAPI QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT;
public:
// Constructors
QtkWidget();
explicit QtkWidget(QWidget * parent);
explicit QtkWidget(const QSurfaceFormat & format);
~QtkWidget() override;
private:
void teardownGL();
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) {
delete mScene;
mScene = scene;
}
protected slots:
void update();
#ifdef QTK_DEBUG
static void messageLogged(const QOpenGLDebugMessage & msg);
#endif
// 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();
static void updateCameraInput();
Qtk::Scene * mScene;
#ifdef QTK_DEBUG
void printContextInformation();
QOpenGLDebugLogger * mDebugLogger;
#endif
};
} // namespace Qtk
#endif // QTK_QTKWIDGET_H

View File

@ -1,23 +1,24 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main window for Qt6 OpenGL widget application ##
## About: Manage files and resources used by qtk ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_QTKAPI_H
#define QTK_QTKAPI_H
#include <QtCore/QtGlobal>
#include "resourcemanager.h"
#include <algorithm>
#include <string>
#include <QtGlobal>
#ifdef QTK_SHARED
# if defined(QTK_EXPORT)
# define QTKAPI Q_DECL_EXPORT
# else
# define QTKAPI Q_DECL_IMPORT
# endif
#else
# define QTKAPI
static std::string nixPath(std::string path)
{
#ifdef Q_OS_WINDOWS
std::replace(path.begin(), path.end(), '\\', '/');
#endif
return path;
}
#endif // QTK_QTKAPI_H
std::string RM::resourcesDir =
std::string(__FILE__).substr(0, nixPath(__FILE__).find("src/"))
+ "resources/";

34
src/resourcemanager.h Normal file
View File

@ -0,0 +1,34 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Manage files and resources used by qtk ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <string>
#ifndef QTK_RESOURCEMANAGER_H
#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] == ':' ? resourcesDir + path.substr(2) : path; }
static std::string resourcesDir;
} RM;
#endif //QTK_RESOURCEMANAGER_H

View File

@ -6,49 +6,50 @@
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <abstractscene.h>
#include <camera3d.h>
#include <examplescene.h>
#include <texture.h>
#include <meshrenderer.h>
#include <model.h>
#include <resourcemanager.h>
#include <texture.h>
using namespace Qtk;
#include <scene.h>
Camera3D Scene::mCamera;
QMatrix4x4 Scene::mProjection;
/*******************************************************************************
* Constructors, Destructors
******************************************************************************/
ExampleScene::ExampleScene() {
Camera().transform().setTranslation(0.0f, 0.0f, 20.0f);
Camera().transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
Scene::Scene()
{
mCamera.transform().setTranslation(0.0f, 0.0f, 20.0f);
mCamera.transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
init();
}
ExampleScene::~ExampleScene() {
Scene::~Scene()
{
delete mTestPhong;
delete mTestSpecular;
delete mTestDiffuse;
delete mTestAmbient;
for(auto & mesh : mMeshes) {
delete mesh;
}
for(auto & model : mModels) {
delete model;
}
delete mSkybox;
for (auto & mesh : mMeshes) delete mesh;
for (auto & model : mModels) delete model;
}
/*******************************************************************************
* Public Member Functions
******************************************************************************/
void ExampleScene::init() {
auto * sb = new Qtk::Skybox("Skybox");
setSkybox(sb);
void Scene::init()
{
// Initialize Phong example cube
mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube());
mTestPhong = new MeshRenderer("phong", Cube());
mTestPhong->mTransform.setTranslation(3.0f, 0.0f, -2.0f);
mTestPhong->setShaders(":/solid-phong.vert", ":/solid-phong.frag");
mTestPhong->init();
@ -63,17 +64,18 @@ void ExampleScene::init() {
mTestPhong->mNBO.create();
mTestPhong->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
mTestPhong->mNBO.bind();
mTestPhong->mNBO.allocate(
mTestPhong->getNormals().data(),
mTestPhong->getNormals().size() * sizeof(mTestPhong->getNormals()[0]));
mTestPhong->mNBO.allocate(mTestPhong->getNormals().data(),
mTestPhong->getNormals().size()
* sizeof(mTestPhong->getNormals()[0]));
mTestPhong->mProgram.enableAttributeArray(1);
mTestPhong->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mTestPhong->mNBO.release();
mTestPhong->mVAO.release();
mTestPhong->mProgram.release();
// Initialize Ambient example cube
mTestAmbient = new Qtk::MeshRenderer("ambient", Cube());
mTestAmbient = new MeshRenderer("ambient", Cube());
mTestAmbient->mTransform.setTranslation(7.0f, 0.0f, -2.0f);
mTestAmbient->setShaders(":/solid-ambient.vert", ":/solid-ambient.frag");
mTestAmbient->init();
@ -86,19 +88,18 @@ void ExampleScene::init() {
mTestAmbient->mNBO.create();
mTestAmbient->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
mTestAmbient->mNBO.bind();
mTestAmbient->mNBO.allocate(
mTestAmbient->getNormals().data(),
mTestAmbient->mNBO.allocate(mTestAmbient->getNormals().data(),
mTestAmbient->getNormals().size()
* sizeof(mTestAmbient->getNormals()[0]));
mTestAmbient->mProgram.enableAttributeArray(1);
mTestAmbient->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mTestAmbient->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mTestAmbient->mNBO.release();
mTestAmbient->mVAO.release();
mTestAmbient->mProgram.release();
// Initialize Diffuse example cube
mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube());
mTestDiffuse = new MeshRenderer("diffuse", Cube());
mTestDiffuse->mTransform.setTranslation(9.0f, 0.0f, -2.0f);
mTestDiffuse->setShaders(":/solid-diffuse.vert", ":/solid-diffuse.frag");
mTestDiffuse->init();
@ -111,19 +112,18 @@ void ExampleScene::init() {
mTestDiffuse->mNBO.create();
mTestDiffuse->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
mTestDiffuse->mNBO.bind();
mTestDiffuse->mNBO.allocate(
mTestDiffuse->getNormals().data(),
mTestDiffuse->mNBO.allocate(mTestDiffuse->getNormals().data(),
mTestDiffuse->getNormals().size()
* sizeof(mTestDiffuse->getNormals()[0]));
mTestDiffuse->mProgram.enableAttributeArray(1);
mTestDiffuse->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mTestDiffuse->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mTestDiffuse->mNBO.release();
mTestDiffuse->mVAO.release();
mTestDiffuse->mProgram.release();
// Initialize Specular example cube
mTestSpecular = new Qtk::MeshRenderer("specular", Cube());
mTestSpecular = new MeshRenderer("specular", Cube());
mTestSpecular->mTransform.setTranslation(11.0f, 0.0f, -2.0f);
mTestSpecular->setShaders(":/solid-specular.vert", ":/solid-specular.frag");
mTestSpecular->init();
@ -138,13 +138,12 @@ void ExampleScene::init() {
mTestSpecular->mNBO.create();
mTestSpecular->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
mTestSpecular->mNBO.bind();
mTestSpecular->mNBO.allocate(
mTestSpecular->getNormals().data(),
mTestSpecular->mNBO.allocate(mTestSpecular->getNormals().data(),
mTestSpecular->getNormals().size()
* sizeof(mTestSpecular->getNormals()[0]));
mTestSpecular->mProgram.enableAttributeArray(1);
mTestSpecular->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mTestSpecular->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mTestSpecular->mNBO.release();
mTestSpecular->mVAO.release();
mTestSpecular->mProgram.release();
@ -152,52 +151,51 @@ void ExampleScene::init() {
//
// Model loading
mModels.push_back(
new Qtk::Model("backpack", ":/models/backpack/backpack.obj"));
mModels.push_back(new 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);
mModels.push_back(new Qtk::Model("bird", ":/models/bird/bird.obj"));
mModels.push_back(new Model("bird", ":/models/bird/bird.obj"));
mModels.back()->mTransform.setTranslation(2.0f, 2.0f, -10.0f);
// Sometimes the models are very large
mModels.back()->mTransform.scale(0.0025f);
mModels.back()->mTransform.rotate(-110.0f, 0.0f, 1.0f, 0.0f);
mModels.push_back(new Qtk::Model("lion", ":/models/lion/lion.obj"));
mModels.push_back(new Model("lion", ":/models/lion/lion.obj"));
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 Model("alien", ":/models/alien-hominid/alien.obj"));
mModels.back()->mTransform.setTranslation(2.0f, -1.0f, -5.0f);
mModels.back()->mTransform.scale(0.15f);
mModels.push_back(new Qtk::Model("scythe", ":/models/scythe/scythe.obj"));
mModels.push_back(new Model("scythe", ":/models/scythe/scythe.obj"));
mModels.back()->mTransform.setTranslation(-6.0f, 0.0f, -10.0f);
mModels.back()->mTransform.rotate(-90.0f, 1.0f, 0.0f, 0.0f);
mModels.back()->mTransform.rotate(90.0f, 0.0f, 1.0f, 0.0f);
mModels.push_back(
new Qtk::Model("masterChief", ":/models/spartan/spartan.obj"));
mModels.push_back(new 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 MeshRenderer("alienTestLight", Triangle(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 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));
@ -212,18 +210,21 @@ 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 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 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));
@ -237,9 +238,9 @@ 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)));
mMeshes.push_back(new MeshRenderer("testLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 1.25f, 10.0f);
mMeshes.back()->mTransform.scale(0.25f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
@ -247,7 +248,7 @@ void ExampleScene::init() {
mMeshes.back()->setColor(GREEN);
mMeshes.back()->init();
mMeshes.push_back(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS)));
mMeshes.push_back(new 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));
@ -258,13 +259,12 @@ void ExampleScene::init() {
mMeshes.back()->mNBO.create();
mMeshes.back()->mNBO.bind();
mMeshes.back()->mNBO.allocate(
mMeshes.back()->getNormals().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->getNormals().data(),
mMeshes.back()->getNormals().size()
* sizeof(mMeshes.back()->getNormals()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
@ -288,25 +288,24 @@ void ExampleScene::init() {
// Create simple shapes using MeshRenderer class and data in mesh.h
mMeshes.push_back(
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS)));
new MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(-5.0f, 0.0f, -2.0f);
mMeshes.push_back(
new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.push_back(new MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(-7.0f, 0.0f, -2.0f);
mMeshes.push_back(
new Qtk::MeshRenderer("leftTriangle", Triangle(QTK_DRAW_ELEMENTS)));
new MeshRenderer("leftTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(-9.0f, 0.0f, -2.0f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
mMeshes.push_back(
new Qtk::MeshRenderer("topTriangle", Triangle(QTK_DRAW_ELEMENTS)));
new MeshRenderer("topTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(-7.0f, 2.0f, -2.0f);
mMeshes.back()->mTransform.scale(0.25f);
mMeshes.push_back(
new Qtk::MeshRenderer("bottomTriangle", Triangle(QTK_DRAW_ELEMENTS)));
new MeshRenderer("bottomTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(-7.0f, -2.0f, -2.0f);
mMeshes.back()->mTransform.scale(0.25f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
@ -319,7 +318,7 @@ void ExampleScene::init() {
// RGB Normals cube to show normals are correct with QTK_DRAW_ARRAYS
mMeshes.push_back(
new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS)));
new 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();
@ -328,21 +327,21 @@ void ExampleScene::init() {
mMeshes.back()->mNBO.bind();
mMeshes.back()->mProgram.bind();
mMeshes.back()->mNBO.allocate(
mMeshes.back()->getNormals().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->getNormals().data(),
mMeshes.back()->getNormals().size()
* sizeof(mMeshes.back()->getNormals()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mMeshes.back()->mProgram.release();
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
// RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS
mMeshes.push_back(new Qtk::MeshRenderer(
"rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.push_back(
new 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();
@ -351,52 +350,48 @@ void ExampleScene::init() {
mMeshes.back()->mNBO.bind();
mMeshes.back()->mProgram.bind();
mMeshes.back()->mNBO.allocate(
mMeshes.back()->getNormals().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->getNormals().data(),
mMeshes.back()->getNormals().size()
* sizeof(mMeshes.back()->getNormals()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mMeshes.back()->mProgram.release();
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
// 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)));
new 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(
OpenGLTextureFactory::initTexture2D(":/crate.png"));
mMeshes.back()->setTexture(OpenGLTextureFactory::initTexture2D(":/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.getTexCoords().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->mShape.getTexCoords().data(),
mMeshes.back()->mShape.getTexCoords().size()
* sizeof(mMeshes.back()->mShape.getTexCoords()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 2, sizeof(QVector2D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
2, sizeof(QVector2D));
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
mMeshes.back()->mProgram.release();
// Test drawing a cube with texture coordinates using glDrawElements
mMeshes.push_back(new Qtk::MeshRenderer(
"uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.push_back(
new MeshRenderer("uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->mTransform.setTranslation(-1.7f, 0.0f, -2.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->init();
@ -405,15 +400,13 @@ void ExampleScene::init() {
mMeshes.back()->mNBO.bind();
mMeshes.back()->mProgram.bind();
mMeshes.back()->mNBO.allocate(
mMeshes.back()->getTexCoords().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->getTexCoords().data(),
mMeshes.back()->getTexCoords().size()
* sizeof(mMeshes.back()->getTexCoords()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mMeshes.back()->setTexture(
OpenGLTextureFactory::initTexture2D(":/crate.png"));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mMeshes.back()->setTexture(OpenGLTextureFactory::initTexture2D(":/crate.png"));
mMeshes.back()->mProgram.setUniformValue("uTexture", 0);
mMeshes.back()->mProgram.release();
@ -424,11 +417,10 @@ void ExampleScene::init() {
// Texturing a cube using a cube map
// + Cube map texturing works with both QTK_DRAW_ARRAYS and QTK_DRAW_ELEMENTS
mMeshes.push_back(
new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS)));
new 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()->setShaders(":/texture-cubemap.vert", ":/texture-cubemap.frag");
mMeshes.back()->init();
mMeshes.back()->mProgram.bind();
@ -439,21 +431,19 @@ void ExampleScene::init() {
mMeshes.back()->mNBO.destroy();
mMeshes.back()->mNBO.create();
mMeshes.back()->mNBO.bind();
mMeshes.back()->mNBO.allocate(
mMeshes.back()->mShape.getTexCoords().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->mShape.getTexCoords().data(),
mMeshes.back()->mShape.getTexCoords().size()
* sizeof(mMeshes.back()->mShape.getTexCoords()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 2, sizeof(QVector2D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
2, sizeof(QVector2D));
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
mMeshes.back()->mProgram.release();
// Create a cube with custom shaders
// + Apply RGB normals shader and spin the cube for a neat effect
mMeshes.push_back(
new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS)));
mMeshes.push_back(new 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();
@ -462,21 +452,20 @@ void ExampleScene::init() {
mMeshes.back()->mNBO.bind();
mMeshes.back()->mProgram.bind();
mMeshes.back()->mNBO.allocate(
mMeshes.back()->getNormals().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->getNormals().data(),
mMeshes.back()->getNormals().size()
* sizeof(mMeshes.back()->getNormals()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mMeshes.back()->mProgram.release();
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
// RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS
mMeshes.push_back(new Qtk::MeshRenderer(
"rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS)));
mMeshes.push_back(
new 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();
@ -485,20 +474,20 @@ void ExampleScene::init() {
mMeshes.back()->mVAO.bind();
mMeshes.back()->mNBO.create();
mMeshes.back()->mNBO.bind();
mMeshes.back()->mNBO.allocate(
mMeshes.back()->getNormals().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->getNormals().data(),
mMeshes.back()->getNormals().size()
* sizeof(mMeshes.back()->getNormals()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
mMeshes.back()->mProgram.release();
// RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS
mMeshes.push_back(new Qtk::MeshRenderer(
"rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.push_back(
new 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();
@ -507,52 +496,54 @@ void ExampleScene::init() {
mMeshes.back()->mVAO.bind();
mMeshes.back()->mNBO.create();
mMeshes.back()->mNBO.bind();
mMeshes.back()->mNBO.allocate(
mMeshes.back()->getNormals().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->getNormals().data(),
mMeshes.back()->getNormals().size()
* sizeof(mMeshes.back()->getNormals()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 3, sizeof(QVector3D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
3, sizeof(QVector3D));
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
mMeshes.back()->mProgram.release();
// Test drawing triangle with glDrawArrays with texture coordinates
mMeshes.push_back(
new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS)));
new 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(":/crate.png");
// Test assignment of Texture
mMeshes.back()->mTexture = Texture(":/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.getTexCoords().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->mShape.getTexCoords().data(),
mMeshes.back()->mShape.getTexCoords().size()
* sizeof(mMeshes.back()->mShape.getTexCoords()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 2, sizeof(QVector2D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
2, sizeof(QVector2D));
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
mMeshes.back()->mProgram.release();
// Test drawing triangle with glDrawElements with texture coordinates
mMeshes.push_back(new Qtk::MeshRenderer(
"testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.push_back(
new 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(OpenGLTextureFactory::initTexture2D(":/crate.png"));
// mMeshes.back()->setTexture(OpenGLTextureFactory::initTexture2D(":/crate.png"));
mMeshes.back()->mTexture.setTexture(":/crate.png");
mMeshes.back()->setUniform("uTexture", 0);
@ -560,13 +551,12 @@ void ExampleScene::init() {
mMeshes.back()->mNBO.destroy();
mMeshes.back()->mNBO.create();
mMeshes.back()->mNBO.bind();
mMeshes.back()->mNBO.allocate(
mMeshes.back()->mShape.getTexCoords().data(),
mMeshes.back()->mNBO.allocate(mMeshes.back()->mShape.getTexCoords().data(),
mMeshes.back()->mShape.getTexCoords().size()
* sizeof(mMeshes.back()->mShape.getTexCoords()[0]));
mMeshes.back()->mProgram.enableAttributeArray(1);
mMeshes.back()->mProgram.setAttributeBuffer(
1, GL_FLOAT, 0, 2, sizeof(QVector2D));
mMeshes.back()->mProgram.setAttributeBuffer(1, GL_FLOAT, 0,
2, sizeof(QVector2D));
mMeshes.back()->mNBO.release();
mMeshes.back()->mVAO.release();
mMeshes.back()->mProgram.release();
@ -575,10 +565,11 @@ void ExampleScene::init() {
// 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 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()->setShaders(":/solid-perspective.vert",
":/solid-perspective.frag");
mMeshes.back()->init();
mMeshes.back()->mProgram.bind();
mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
@ -586,59 +577,54 @@ void ExampleScene::init() {
// Create objects that represent light sources for lighting examples
mMeshes.push_back(
new Qtk::MeshRenderer("phongLight", Triangle(QTK_DRAW_ELEMENTS)));
new MeshRenderer("phongLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(3.0f, 2.0f, -2.0f);
mMeshes.back()->mTransform.scale(0.25f);
mMeshes.push_back(
new Qtk::MeshRenderer("diffuseLight", Triangle(QTK_DRAW_ELEMENTS)));
new MeshRenderer("diffuseLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(9.0f, 2.0f, -2.0f);
mMeshes.back()->mTransform.scale(0.25f);
mMeshes.push_back(
new Qtk::MeshRenderer("specularLight", Triangle(QTK_DRAW_ELEMENTS)));
new MeshRenderer("specularLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(11.0f, 2.0f, -2.0f);
mMeshes.back()->mTransform.scale(0.25f);
}
void ExampleScene::draw() {
Scene::draw();
void Scene::draw()
{
mSkybox.draw();
for(const auto & model : mModels) {
model->draw();
}
for (const auto & model : mModels) model->draw();
for(const auto & mesh : mMeshes) {
mesh->draw();
}
for (const auto & mesh : mMeshes) mesh->draw();
mTestPhong->mProgram.bind();
mTestPhong->setUniform(
"uModelInverseTransposed",
mTestPhong->setUniform("uModelInverseTransposed",
mTestPhong->mTransform.toMatrix().normalMatrix());
mTestPhong->setUniform(
"uLightPosition",
MeshRenderer::getInstance("phongLight")->mTransform.getTranslation());
mTestPhong->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation());
mTestPhong->setUniform("uCameraPosition",
Scene::Camera().transform().getTranslation());
mTestPhong->mProgram.release();
mTestPhong->draw();
mTestAmbient->mProgram.bind();
mTestAmbient->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation());
mTestAmbient->setUniform("uCameraPosition",
Scene::Camera().transform().getTranslation());
mTestAmbient->mProgram.release();
mTestAmbient->draw();
mTestDiffuse->mProgram.bind();
mTestDiffuse->setUniform(
"uModelInverseTransposed",
mTestDiffuse->setUniform("uModelInverseTransposed",
mTestDiffuse->mTransform.toMatrix().normalMatrix());
mTestDiffuse->setUniform(
"uLightPosition",
MeshRenderer::getInstance("diffuseLight")->mTransform.getTranslation());
mTestDiffuse->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation());
mTestDiffuse->setUniform("uCameraPosition",
Scene::Camera().transform().getTranslation());
mTestDiffuse->mProgram.release();
mTestDiffuse->draw();
@ -649,66 +635,67 @@ void ExampleScene::draw() {
mTestSpecular->setUniform(
"uLightPosition",
MeshRenderer::getInstance("specularLight")->mTransform.getTranslation());
mTestSpecular->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation());
mTestSpecular->setUniform("uCameraPosition",
Scene::Camera().transform().getTranslation());
mTestSpecular->mProgram.release();
mTestSpecular->draw();
}
void ExampleScene::update() {
auto position =
MeshRenderer::getInstance("alienTestLight")->mTransform.getTranslation();
Model::getInstance("alienTest")->setUniform("uLight.position", position);
Model::getInstance("alienTest")
->setUniform(
"uCameraPosition",
ExampleScene::Camera().transform().getTranslation());
void Scene::update()
{
auto position = MeshRenderer::getInstance(
"alienTestLight")->mTransform.getTranslation();
Model::getInstance("alienTest")->setUniform(
"uLight.position", position);
Model::getInstance("alienTest")->setUniform(
"uCameraPosition", Scene::Camera().transform().getTranslation());
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")->setUniform(
"uMVP.normalMatrix", posMatrix.normalMatrix());
Model::getInstance("alienTest")->setUniform(
"uMVP.model", posMatrix);
Model::getInstance("alienTest")->setUniform(
"uMVP.view", Scene::Camera().toMatrix());
Model::getInstance("alienTest")->setUniform(
"uMVP.projection", Scene::Projection());
Model::getInstance("alienTest")->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f);
position = MeshRenderer::getInstance("spartanTestLight")
->mTransform.getTranslation();
Model::getInstance("spartanTest")->setUniform("uLight.position", position);
Model::getInstance("spartanTest")
->setUniform(
"uCameraPosition",
ExampleScene::Camera().transform().getTranslation());
position = MeshRenderer::getInstance(
"spartanTestLight")->mTransform.getTranslation();
Model::getInstance("spartanTest")->setUniform(
"uLight.position", position);
Model::getInstance("spartanTest")->setUniform(
"uCameraPosition", Scene::Camera().transform().getTranslation());
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")->setUniform(
"uMVP.normalMatrix", posMatrix.normalMatrix());
Model::getInstance("spartanTest")->setUniform(
"uMVP.model", posMatrix);
Model::getInstance("spartanTest")->setUniform(
"uMVP.view", Scene::Camera().toMatrix());
Model::getInstance("spartanTest")->setUniform(
"uMVP.projection", Scene::Projection());
Model::getInstance("spartanTest")->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")->mTransform.rotate(
0.75f, 1.0f, 0.5f, 0.0f);
MeshRenderer::getInstance("testPhong")->mProgram.bind();
position =
MeshRenderer::getInstance("testLight")->mTransform.getTranslation();
MeshRenderer::getInstance("testPhong")
->setUniform("uLight.position", position);
MeshRenderer::getInstance("testPhong")
->setUniform(
"uCameraPosition",
ExampleScene::Camera().transform().getTranslation());
position = MeshRenderer::getInstance("testLight")->mTransform.getTranslation();
MeshRenderer::getInstance("testPhong")->setUniform(
"uLight.position", position);
MeshRenderer::getInstance("testPhong")->setUniform(
"uCameraPosition", Scene::Camera().transform().getTranslation());
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")->setUniform(
"uMVP.normalMatrix", posMatrix.normalMatrix());
MeshRenderer::getInstance("testPhong")->setUniform(
"uMVP.model", posMatrix);
MeshRenderer::getInstance("testPhong")->setUniform(
"uMVP.view", Scene::Camera().toMatrix());
MeshRenderer::getInstance("testPhong")->setUniform(
"uMVP.projection", Scene::Projection());
MeshRenderer::getInstance("testPhong")->mProgram.release();
// Rotate lighting example cubes
@ -722,36 +709,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 posX =
MeshRenderer::getInstance("topTriangle")->mTransform.getTranslation().x();
if(posX < limit || posX > limit + 4.0f) {
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

@ -6,29 +6,40 @@
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef QTK_EXAMPLE_SCENE_H
#define QTK_EXAMPLE_SCENE_H
#ifndef QTK_SCENE_H
#define QTK_SCENE_H
#include <abstractscene.h>
#include <camera3d.h>
#include <skybox.h>
#include <QMatrix4x4>
class ExampleScene : public Qtk::Scene {
public:
ExampleScene();
~ExampleScene();
class MeshRenderer;
class Model;
void init() override;
void draw() override;
void update() override;
class Scene {
public:
Scene();
~Scene();
private:
Qtk::MeshRenderer * mTestPhong {};
Qtk::MeshRenderer * mTestSpecular {};
Qtk::MeshRenderer * mTestDiffuse {};
Qtk::MeshRenderer * mTestAmbient {};
void init();
void draw();
void update();
static Camera3D & Camera() { return mCamera;}
static QMatrix4x4 View() { return mCamera.toMatrix();}
static QMatrix4x4 & Projection() { return mProjection;}
private:
static Camera3D mCamera;
static QMatrix4x4 mProjection;
Skybox mSkybox;
MeshRenderer * mTestPhong;
MeshRenderer * mTestSpecular;
MeshRenderer * mTestDiffuse;
MeshRenderer * mTestAmbient;
std::vector<MeshRenderer *> mMeshes;
std::vector<Model *> mModels;
};
#endif // QTK_EXAMPLE_SCENE_H
#endif // QTK_SCENE_H

View File

@ -6,42 +6,40 @@
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <abstractscene.h>
#include <skybox.h>
#include <scene.h>
#include <texture.h>
using namespace Qtk;
#include <skybox.h>
Skybox::Skybox(
const std::string & right, const std::string & top,
const std::string & front, const std::string & left,
const std::string & bottom, const std::string & back,
const std::string & name) :
Skybox::Skybox(std::string right, std::string top, std::string front,
std::string left, std::string bottom, std::string back,
const std::string & name)
: mCubeMap(OpenGLTextureFactory::initCubeMap(
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()))),
mVBO(QOpenGLBuffer::VertexBuffer),
mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData()) {
init();
mCubeMap = OpenGLTextureFactory::initCubeMap(
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()));
}
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData())
{ init();}
Skybox::Skybox(const std::string & name) :
Skybox(
":/right.png", ":/top.png", ":/front.png", ":/left.png", ":/bottom.png",
":/back.png", name) {}
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(QOpenGLTexture * cubeMap, const std::string & name) :
mCubeMap(cubeMap) {
init();
}
/*******************************************************************************
* Public Member Functions
******************************************************************************/
void Skybox::draw() {
void Skybox::draw()
{
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_FALSE);
@ -52,8 +50,8 @@ void Skybox::draw() {
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();
mProgram.release();
@ -64,11 +62,13 @@ void Skybox::draw() {
glActiveTexture(GL_TEXTURE0);
}
/*******************************************************************************
* Private Member Functions
******************************************************************************/
void Skybox::init() {
void Skybox::init()
{
initializeOpenGLFunctions();
// Set up shader program
@ -87,11 +87,13 @@ void Skybox::init() {
mVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
mVBO.bind();
// Allocate vertex positions into VBO
mVBO.allocate(mVertices.data(), mVertices.size() * sizeof(mVertices[0]));
mVBO.allocate(mVertices.data(),
mVertices.size() * sizeof(mVertices[0]));
// Enable attribute array for vertex positions
mProgram.enableAttributeArray(0);
mProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D));
mProgram.setAttributeBuffer(0, GL_FLOAT, 0,
3, sizeof(QVector3D));
// Set shader texture unit to 0
mProgram.setUniformValue("uTexture", 0);

View File

@ -10,37 +10,30 @@
#include <QImage>
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLFunctions>
#include <camera3d.h>
#include <mesh.h>
#include <qtkapi.h>
namespace Qtk {
class QTKAPI Skybox : protected QOpenGLFunctions {
public:
class 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(const std::string & name = "Skybox");
explicit Skybox(
QOpenGLTexture * cubeMap, const std::string & name = "Skybox");
// + 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(
const std::string & right, const std::string & top,
const std::string & front, const std::string & left,
const std::string & bottom, const std::string & back,
const std::string & name = "Skybox");
~Skybox() = default;
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() {}
void draw();
private:
private:
void init();
Vertices mVertices;
@ -50,7 +43,6 @@ namespace Qtk {
QOpenGLVertexArrayObject mVAO;
QOpenGLBuffer mVBO;
QOpenGLTexture * mCubeMap;
};
} // namespace Qtk
};
#endif // QTK_SKYBOX_H

View File

@ -8,18 +8,17 @@
#include <QDebug>
#include <QImageReader>
#include <utility>
#include <texture.h>
using namespace Qtk;
std::unordered_map<std::string, Texture *> TM::textureMap;
QImage * OpenGLTextureFactory::initImage(
const char * image, bool flipX, bool flipY) {
QImage * OpenGLTextureFactory::initImage(const char * image, bool flipX, bool flipY)
{
// Qt6 limits loaded images to 256MB by default
QImageReader::setAllocationLimit(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;
@ -28,39 +27,46 @@ QImage * OpenGLTextureFactory::initImage(
return loadedImage;
}
QOpenGLTexture * OpenGLTextureFactory::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 * OpenGLTextureFactory::initCubeMap(const char * tile) {
return initCubeMap(
QImage(tile), QImage(tile), QImage(tile), QImage(tile), QImage(tile),
QImage(tile));
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * tile)
{
return initCubeMap(QImage(tile), QImage(tile),
QImage(tile), QImage(tile),
QImage(tile), QImage(tile));
}
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(
const char * right, const char * top, const char * front, const char * left,
const char * bottom, const char * back) {
return initCubeMap(
QImage(right), QImage(top), QImage(front), QImage(left), QImage(bottom),
QImage(back));
const char * 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(
QImage right, QImage top, QImage front, QImage left, QImage bottom,
QImage back) {
QImage right, QImage top,
QImage front, QImage left,
QImage bottom, QImage back)
{
auto texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
std::vector<QImage> faceTextures = {std::move(right), std::move(top),
std::move(front), std::move(left),
std::move(bottom), std::move(back)};
std::vector<QImage> faceTextures = {
right, top, front,
left, bottom, back
};
// Initialize skybox cubemap texture
texture->create();
texture->bind();
@ -68,26 +74,26 @@ QOpenGLTexture * OpenGLTextureFactory::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,
texture->setData(0, 0, face,
QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
faceImage.constBits());
i++;
}

View File

@ -11,77 +11,81 @@
#include <QOpenGLTexture>
#include <qtkapi.h>
namespace Qtk {
class QTKAPI OpenGLTextureFactory {
public:
class OpenGLTextureFactory {
public:
~OpenGLTextureFactory() = default;
// QImage
static QImage * initImage(
const char * image, bool flipX = false, bool flipY = false);
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);
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);
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);
static QOpenGLTexture * initCubeMap(const char * right, const char * top,
const char * front, const char * left,
const char * bottom, const char * back);
private:
private:
// Private ctor to prevent creating instances of this class
OpenGLTextureFactory() = default;
};
};
class Texture {
public:
class Texture;
typedef class TextureManager {
public:
static inline Texture * getInstance(std::string path)
{ return textureMap.count(path) > 0 ? textureMap[path] : Q_NULLPTR; }
static inline bool addInstance(const std::string & path, Texture * texture)
{ return textureMap.emplace(path, texture).second ? true : false; }
private:
static std::unordered_map<std::string, Texture *> textureMap;
} TM;
class Texture {
// explicit Texture(QOpenGLTexture * texture) : mOpenGLTexture(texture) { }
public:
Texture() = default;
Texture(const Texture & value) = delete;
// 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;
Texture(const Texture & value) {
if (TM::getInstance(value.mPath)) {
mOpenGLTexture = TM::getInstance(value.mPath)->mOpenGLTexture;
}
void setTexture(
const char * path, bool flipX = false, bool flipY = false) {
mOpenGLTexture =
OpenGLTextureFactory::initTexture2D(path, flipX, flipY);
else {
mOpenGLTexture = OpenGLTextureFactory::initTexture2D(value.mPath);
TM::addInstance(value.mPath, this);
}
inline void setTexture(QOpenGLTexture * texture) {
mOpenGLTexture = texture;
mPath = value.mPath;
}
[[nodiscard]] inline bool hasTexture() const {
return mOpenGLTexture != Q_NULLPTR;
explicit Texture(const char * path, bool flipX=false, bool flipY=false) :
mPath(path) {
if (TM::getInstance(path) == Q_NULLPTR) {
mOpenGLTexture = OpenGLTextureFactory::initTexture2D(path, flipX, flipY);
TM::addInstance(path, this);
}
else {
mOpenGLTexture = TM::getInstance(path)->mOpenGLTexture;
}
}
// ~Texture() { if (mOpenGLTexture) mOpenGLTexture->destroy();}
inline QOpenGLTexture & getOpenGLTexture() { return *mOpenGLTexture;}
inline void setTexture(const char * path, bool flipX=false, bool flipY=false)
{ mOpenGLTexture = OpenGLTextureFactory::initTexture2D(path, flipX, flipY);}
inline void setTexture(QOpenGLTexture * texture) { mOpenGLTexture = texture;}
inline bool hasTexture() const { return mOpenGLTexture != Q_NULLPTR;}
QOpenGLTexture * mOpenGLTexture = Q_NULLPTR;
const char * mPath {};
};
} // namespace Qtk
const char * mPath;
};
#endif // QTOPENGL_TEXTURE_H

View File

@ -9,7 +9,6 @@
#include <transform3D.h>
using namespace Qtk;
const QVector3D Transform3D::LocalForward(0.0f, 0.0f, 1.0f);
const QVector3D Transform3D::LocalUp(0.0f, 1.0f, 0.0f);
@ -19,53 +18,64 @@ 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);
@ -75,63 +85,60 @@ const QMatrix4x4 & Transform3D::toMatrix() {
return mWorld;
}
/*******************************************************************************
* Queries
******************************************************************************/
QVector3D Transform3D::getForward() const {
QVector3D Transform3D::getForward() const
{
return mRotation.rotatedVector(LocalForward);
}
QVector3D Transform3D::getUp() const {
QVector3D Transform3D::getUp() const
{
return mRotation.rotatedVector(LocalUp);
}
QVector3D Transform3D::getRight() const {
QVector3D Transform3D::getRight() const
{
return mRotation.rotatedVector(LocalRight);
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.getTranslation().x() << ", "
<< transform.getTranslation().y() << ", "
<< transform.getTranslation().z() << ">\n";
dbg << "Scale: <" << transform.getScale().x() << ", "
<< transform.getScale().y() << ", " << transform.getScale().z()
<< ">\n";
<< transform.getScale().y() << ", "
<< transform.getScale().z() << ">\n";
dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.getRotation().y() << ", " << transform.getRotation().z()
<< " | " << transform.getRotation().scalar() << ">\n}";
<< 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

@ -10,120 +10,85 @@
#ifndef QTK_TRANSFORM3D_H
#define QTK_TRANSFORM3D_H
#include <QDebug>
#include <QMatrix4x4>
#include <QQuaternion>
#include <QVector3D>
#ifndef QT_NO_DEBUG_STREAM
#include <QDebug>
#endif
#include <qtkapi.h>
namespace Qtk {
class QTKAPI Transform3D {
public:
class Transform3D
{
public:
// Constructors
inline Transform3D() :
m_dirty(true), mScale(1.0f, 1.0f, 1.0f),
mTranslation(0.0f, 0.0f, 0.0f) {}
inline Transform3D() : m_dirty(true),
mScale(1.0f, 1.0f, 1.0f),
mTranslation(0.0f, 0.0f, 0.0f) { }
//
// Transformations
void translate(const QVector3D & dt);
inline void translate(float dx, float dy, float dz) {
translate(QVector3D(dx, dy, dz));
}
inline void translate(float dx, float dy, float dz)
{ translate(QVector3D(dx, dy, dz));}
// Scale object with multiplication
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 scale(float dx, float dy, float dz)
{ scale(QVector3D(dx, dy, dz));}
inline void scale(float factor)
{ scale(QVector3D(factor, factor, factor));}
// 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));
}
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 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 grow(float dx, float dy, float dz)
{ grow(QVector3D(dx, dy, dz));}
inline void grow(float factor)
{ grow(QVector3D(factor, factor, factor));}
//
// Setters
// Set object position
void setTranslation(const QVector3D & t);
inline void setTranslation(float x, float y, float z) {
setTranslation(QVector3D(x, y, z));
}
inline void setTranslation(float x, float y, float z)
{ setTranslation(QVector3D(x, y, z));}
// Set object scale
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 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));
}
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;
}
inline const QVector3D & getTranslation() const { return mTranslation;}
inline const QVector3D & getScale() const { return mScale; }
inline const QQuaternion & getRotation() const { return mRotation; }
const QMatrix4x4 & toMatrix();
[[nodiscard]] QVector3D getForward() const;
[[nodiscard]] QVector3D getUp() const;
[[nodiscard]] QVector3D getRight() const;
QVector3D getForward() const;
QVector3D getUp() const;
QVector3D getRight() const;
static const QVector3D LocalForward, LocalUp, LocalRight;
private:
private:
QVector3D mTranslation;
QQuaternion mRotation;
QVector3D mScale;
@ -132,24 +97,21 @@ namespace Qtk {
bool m_dirty;
#ifndef QT_NO_DATASTREAM
friend QDataStream & operator<<(
QDataStream & out, const Transform3D & transform);
friend QDataStream & operator>>(
QDataStream & in, Transform3D & transform);
friend QDataStream &operator<<(QDataStream & out, const Transform3D & transform);
friend QDataStream &operator>>(QDataStream & in, Transform3D & transform);
#endif
};
};
Q_DECLARE_TYPEINFO(Transform3D, Q_MOVABLE_TYPE);
// Qt Streams
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Transform3D & transform);
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