Doxygen comments

This commit is contained in:
Shaun Reed 2022-11-26 18:24:38 +00:00
parent 443c09da7c
commit a04ebae42a
30 changed files with 1398 additions and 815 deletions

View File

@ -1,8 +1,5 @@
--- ---
# clang-format off
BasedOnStyle: Google BasedOnStyle: Google
# clang-format on
AlignAfterOpenBracket: AlwaysBreak AlignAfterOpenBracket: AlwaysBreak
AlignArrayOfStructures: Left AlignArrayOfStructures: Left
AlignConsecutiveAssignments: None AlignConsecutiveAssignments: None

View File

@ -55,7 +55,7 @@ list(APPEND CMAKE_PREFIX_PATH "${QT_DIR}")
# Find Qt # Find Qt
find_package(Qt6 COMPONENTS OpenGLWidgets) find_package(Qt6 COMPONENTS OpenGLWidgets)
if (NOT Qt6_FOUND) if(NOT Qt6_FOUND)
message( message(
SEND_ERROR "[Qtk] Error: Unable to find Qt6 at CMAKE_PREFIX_PATH: " SEND_ERROR "[Qtk] Error: Unable to find Qt6 at CMAKE_PREFIX_PATH: "
"${CMAKE_PREFIX_PATH}" "${CMAKE_PREFIX_PATH}"
@ -68,7 +68,7 @@ if (NOT Qt6_FOUND)
) )
endif() endif()
if (QTK_UPDATE_SUBMODULES) if(QTK_UPDATE_SUBMODULES)
message(STATUS "[Qtk] Updating submodules...") message(STATUS "[Qtk] Updating submodules...")
include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake") include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake")
submodule_update("${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/") submodule_update("${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/")
@ -85,7 +85,7 @@ endif()
set( set(
PUBLIC_HEADERS PUBLIC_HEADERS
src/qtkwidget.h src/qtkwidget.h
src/abstractscene.h src/scene.h
src/camera3d.h src/camera3d.h
src/mesh.h src/mesh.h
src/meshrenderer.h src/meshrenderer.h
@ -99,7 +99,7 @@ set(
set( set(
SOURCE_FILES SOURCE_FILES
src/qtkwidget.cpp src/qtkwidget.cpp
src/abstractscene.cpp src/scene.cpp
src/camera3d.cpp src/camera3d.cpp
src/input.cpp src/input.cpp
src/input.h src/input.h
@ -118,7 +118,8 @@ include(GenerateExportHeader)
add_library(qtk-widget STATIC ${PUBLIC_HEADERS} ${SOURCE_FILES}) add_library(qtk-widget STATIC ${PUBLIC_HEADERS} ${SOURCE_FILES})
target_include_directories(qtk-widget PRIVATE src/ app/) target_include_directories(qtk-widget PRIVATE src/ app/)
set_target_properties(qtk-widget PROPERTIES set_target_properties(
qtk-widget PROPERTIES
PUBLIC_HEADER "${PUBLIC_HEADERS}" PUBLIC_HEADER "${PUBLIC_HEADERS}"
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
) )
@ -126,7 +127,7 @@ set_target_properties(qtk-widget PROPERTIES
target_link_libraries(qtk-widget PUBLIC Qt6::OpenGLWidgets) target_link_libraries(qtk-widget PUBLIC Qt6::OpenGLWidgets)
target_link_libraries(qtk-widget PUBLIC Qt6::Widgets) target_link_libraries(qtk-widget PUBLIC Qt6::Widgets)
if (QTK_UPDATE_SUBMODULES OR NOT ASSIMP_NEW_INTERFACE) if(QTK_UPDATE_SUBMODULES OR NOT ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk-widget PUBLIC assimp) target_link_libraries(qtk-widget PUBLIC assimp)
elseif(ASSIMP_NEW_INTERFACE) elseif(ASSIMP_NEW_INTERFACE)
target_link_libraries(qtk-widget PUBLIC assimp::assimp) target_link_libraries(qtk-widget PUBLIC assimp::assimp)
@ -144,7 +145,8 @@ if(WIN32)
endif() endif()
# Install files # Install files
install(TARGETS qtk-widget install(
TARGETS qtk-widget
# Associate qtk-widget target with qtk-export # Associate qtk-widget target with qtk-export
EXPORT qtk-export EXPORT qtk-export
# <prefix>/bin on DLL systems and <prefix>/lib on non-dll systems # <prefix>/bin on DLL systems and <prefix>/lib on non-dll systems
@ -157,7 +159,8 @@ install(TARGETS qtk-widget
# Install export # Install export
# qtkTargets.cmake will only be installed when one of the CONFIGURATIONS is installed # qtkTargets.cmake will only be installed when one of the CONFIGURATIONS is installed
# + The generated import will only reference that qtk configuration # + The generated import will only reference that qtk configuration
install(EXPORT qtk-export install(
EXPORT qtk-export
FILE qtkTargets.cmake FILE qtkTargets.cmake
CONFIGURATIONS Debug|Release CONFIGURATIONS Debug|Release
DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake
@ -175,9 +178,14 @@ configure_file(
) )
# Add our Qt resources.qrc file to our application # Add our Qt resources.qrc file to our application
set(QTK_APP_SOURCES app/main.cpp set(
app/examplescene.cpp app/examplescene.h QTK_APP_SOURCES
app/mainwindow.cpp app/mainwindow.h app/mainwindow.ui app/main.cpp
app/examplescene.cpp
app/examplescene.h
app/mainwindow.cpp
app/mainwindow.h
app/mainwindow.ui
app/resourcemanager.h app/resourcemanager.h
src/qtkresources.h.in src/qtkresources.h.in
) )
@ -189,13 +197,15 @@ target_include_directories(qtk-main PRIVATE src/ app/)
# Link qtk-main executable to main qtk-widget library # Link qtk-main executable to main qtk-widget library
target_link_libraries(qtk-main PUBLIC qtk-widget) target_link_libraries(qtk-main PUBLIC qtk-widget)
set_target_properties(qtk-main PROPERTIES set_target_properties(
qtk-main PROPERTIES
WIN32_EXECUTABLE TRUE WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
) )
install(TARGETS qtk-main install(
TARGETS qtk-main
BUNDLE DESTINATION . BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
) )
@ -206,7 +216,8 @@ if(WIN32)
execute_process(COMMAND "${_qt6_qmake_location}" -query QT_INSTALL_PREFIX RESULT_VARIABLE return_code OUTPUT_VARIABLE qt6_install_prefix OUTPUT_STRIP_TRAILING_WHITESPACE) 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) file(TO_NATIVE_PATH "${qt6_install_prefix}/bin" qt6_install_prefix)
if(TARGET Qt6::windeployqt) if(TARGET Qt6::windeployqt)
add_custom_command(TARGET qtk-main add_custom_command(
TARGET qtk-main
POST_BUILD POST_BUILD
COMMAND set PATH=%PATH%$<SEMICOLON>${qt6_install_prefix} 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>" COMMAND Qt6::windeployqt --dir "${CMAKE_BINARY_DIR}/windeployqt" "$<TARGET_FILE_DIR:qtk-main>/$<TARGET_FILE_NAME:qtk-main>"

View File

@ -2,7 +2,6 @@
[![All Builds](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml) [![All Builds](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml)
[![Linting](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml) [![Linting](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml)
Practice project for learning about using OpenGL in Qt widget applications.
Model loader using [Assimp](https://assimp.org/) within a Qt widget application. Model loader using [Assimp](https://assimp.org/) within a Qt widget application.
You can import your own models within `app/examplescene.cpp`, inside the You can import your own models within `app/examplescene.cpp`, inside the
@ -12,6 +11,41 @@ happen in `ExampleScene::update()`.
To get textures loading on models look into [material files](http://www.paulbourke.net/dataformats/mtl/) 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. and see some examples in the `resources/models/` directory.
The syntax for adding shapes and models is seen in the example below.
This would result in a scene with a red cube and a miniature spartan model placed on top.
```C++
// From: qtk/app/examplescene.cpp
void ExampleScene::init() {
// Add a skybox to the scene using default cube map images and settings.
setSkybox(new Qtk::Skybox("Skybox"));
/* Create a red cube with a mini master chief on top. */
auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS));
myCube->setColor(RED);
mMeshes.push_back(myCube);
auto mySpartan = new Model("My spartan", ":/models/spartan/spartan.obj");
mySpartan->getTransform().setTranslation(0.0f, 0.5f, 0.0f);
mySpartan->getTransform().setScale(0.5f);
mModels.push_back(mySpartan);
}
```
If we want to make our spartan spin, we need to apply rotation in `update`
```C++
// From: qtk/app/examplescene.cpp
void ExampleScene::update() {
auto mySpartan = Model::getInstance("My spartan");
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto myCube = MeshRenderer::getInstance("My cube");
myCube->getTransform().rotate(-0.75f, 0.0f, 1.0f, 0.0f);
}
```
### Source Builds ### Source Builds
@ -132,7 +166,6 @@ Spartan with normals -
### QtkWidget in Qt Creator ### 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. 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. There is still some work to be done here, so there isn't a builtin way to add an additional view within the application.

View File

@ -6,12 +6,12 @@
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/ ##############################################################################*/
#include <abstractscene.h>
#include <camera3d.h> #include <camera3d.h>
#include <examplescene.h> #include <examplescene.h>
#include <meshrenderer.h> #include <meshrenderer.h>
#include <model.h> #include <model.h>
#include <resourcemanager.h> #include <resourcemanager.h>
#include <scene.h>
#include <texture.h> #include <texture.h>
using namespace Qtk; using namespace Qtk;
@ -21,8 +21,8 @@ using namespace Qtk;
******************************************************************************/ ******************************************************************************/
ExampleScene::ExampleScene() { ExampleScene::ExampleScene() {
Camera().transform().setTranslation(0.0f, 0.0f, 20.0f); getCamera().getTransform().setTranslation(0.0f, 0.0f, 20.0f);
Camera().transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
} }
ExampleScene::~ExampleScene() { ExampleScene::~ExampleScene() {
@ -44,50 +44,152 @@ ExampleScene::~ExampleScene() {
******************************************************************************/ ******************************************************************************/
void ExampleScene::init() { void ExampleScene::init() {
auto * sb = new Qtk::Skybox("Skybox"); // Add a skybox to the scene using default cube map images and settings.
setSkybox(sb); setSkybox(new Qtk::Skybox("Skybox"));
// Initialize Phong example cube /* Create a red cube with a mini master chief on top. */
auto myCube = new MeshRenderer("My cube", Cube(Qtk::QTK_DRAW_ELEMENTS));
myCube->setColor(RED);
mMeshes.push_back(myCube);
auto mySpartan = new Model("My spartan", ":/models/spartan/spartan.obj");
mySpartan->getTransform().setTranslation(0.0f, 0.5f, 0.0f);
mySpartan->getTransform().setScale(0.5f);
mModels.push_back(mySpartan);
//
// Create simple shapes using MeshRenderer class and data in mesh.h
mMeshes.push_back(
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-5.0f, 0.0f, -2.0f);
mMeshes.push_back(
new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-7.0f, 0.0f, -2.0f);
mMeshes.push_back(
new Qtk::MeshRenderer("leftTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-9.0f, 0.0f, -2.0f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
mMeshes.push_back(
new Qtk::MeshRenderer("topTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-7.0f, 2.0f, -2.0f);
mMeshes.back()->getTransform().scale(0.25f);
mMeshes.push_back(
new Qtk::MeshRenderer("bottomTriangle", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(-7.0f, -2.0f, -2.0f);
mMeshes.back()->getTransform().scale(0.25f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
mMeshes.back()->setColor(GREEN);
//
// 3D Model loading
mModels.push_back(
new Qtk::Model("backpack", ":/models/backpack/backpack.obj"));
// Sometimes model textures need flipped in certain directions
mModels.back()->flipTexture("diffuse.jpg", false, true);
mModels.back()->getTransform().setTranslation(0.0f, 0.0f, -10.0f);
mModels.push_back(new Qtk::Model("bird", ":/models/bird/bird.obj"));
mModels.back()->getTransform().setTranslation(2.0f, 2.0f, -10.0f);
// Sometimes the models are very large
mModels.back()->getTransform().scale(0.0025f);
mModels.back()->getTransform().rotate(-110.0f, 0.0f, 1.0f, 0.0f);
mModels.push_back(new Qtk::Model("lion", ":/models/lion/lion.obj"));
mModels.back()->getTransform().setTranslation(-3.0f, -1.0f, -10.0f);
mModels.back()->getTransform().scale(0.15f);
mModels.push_back(
new Qtk::Model("alien", ":/models/alien-hominid/alien.obj"));
mModels.back()->getTransform().setTranslation(2.0f, -1.0f, -5.0f);
mModels.back()->getTransform().scale(0.15f);
mModels.push_back(new Qtk::Model("scythe", ":/models/scythe/scythe.obj"));
mModels.back()->getTransform().setTranslation(-6.0f, 0.0f, -10.0f);
mModels.back()->getTransform().rotate(-90.0f, 1.0f, 0.0f, 0.0f);
mModels.back()->getTransform().rotate(90.0f, 0.0f, 1.0f, 0.0f);
mModels.push_back(
new Qtk::Model("masterChief", ":/models/spartan/spartan.obj"));
mModels.back()->getTransform().setTranslation(-1.5f, 0.5f, -2.0f);
//
// Simple cube lighting examples.
/* Phong lighting example on a basic cube. */
mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube()); mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube());
mTestPhong->mTransform.setTranslation(3.0f, 0.0f, -2.0f); mTestPhong->getTransform().setTranslation(3.0f, 0.0f, -2.0f);
// NOTE: You no longer need to manually bind shader program to set uniforms.
// + You can still bind it if you want to for performance reasons.
// + Qtk will only bind / release if the shader program is not already bound.
mTestPhong->setShaders(":/solid-phong.vert", ":/solid-phong.frag"); mTestPhong->setShaders(":/solid-phong.vert", ":/solid-phong.frag");
// You no longer need to manually bind shader program. // For example this would technically not be efficient, because each one of
// + But, you can still bind it if you want to. // these calls will bind, set, release. We could instead bind, set N uniforms,
// and release when we are finished.
// mTestPhong->bindShaders(); // mTestPhong->bindShaders();
mTestPhong->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestPhong->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestPhong->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestPhong->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestPhong->setUniform("uAmbientStrength", 0.2f); mTestPhong->setUniform("uAmbientStrength", 0.2f);
mTestPhong->setUniform("uSpecularStrength", 0.50f); mTestPhong->setUniform("uSpecularStrength", 0.50f);
mTestPhong->setUniform("uSpecularShine", 256); mTestPhong->setUniform("uSpecularShine", 256);
mTestPhong->reallocateNormals(mTestPhong->getNormals());
// mTestPhong->releaseShaders(); // mTestPhong->releaseShaders();
mTestPhong->reallocateNormals(mTestPhong->getNormals());
// NOTE: This is only an example and I won't worry about this kind of
// efficiency while initializing the following objects.
// Initialize Ambient example cube // Phong lighting example light source. This is just for visual reference.
// + We refer to the position of this object in draw() to update lighting.
mMeshes.push_back(
new Qtk::MeshRenderer("phongLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(3.0f, 2.0f, -2.0f);
mMeshes.back()->getTransform().scale(0.25f);
/* Example of a cube with no lighting applied */
mMeshes.push_back(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, -2.0f);
mMeshes.back()->setShaders(
":/solid-perspective.vert", ":/solid-perspective.frag");
mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
// No light source needed for this lighting technique
/* Initialize Ambient example cube */
mTestAmbient = new Qtk::MeshRenderer("ambient", Cube()); mTestAmbient = new Qtk::MeshRenderer("ambient", Cube());
mTestAmbient->mTransform.setTranslation(7.0f, 0.0f, -2.0f); mTestAmbient->getTransform().setTranslation(7.0f, 0.0f, -2.0f);
mTestAmbient->setShaders(":/solid-ambient.vert", ":/solid-ambient.frag"); mTestAmbient->setShaders(":/solid-ambient.vert", ":/solid-ambient.frag");
mTestAmbient->init(); // Changing these uniform values will alter lighting effects.
mTestAmbient->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestAmbient->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestAmbient->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestAmbient->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestAmbient->setUniform("uAmbientStrength", 0.2f); mTestAmbient->setUniform("uAmbientStrength", 0.2f);
mTestAmbient->reallocateNormals(mTestAmbient->getNormals()); mTestAmbient->reallocateNormals(mTestAmbient->getNormals());
// No light source needed for this lighting technique
// Initialize Diffuse example cube /* Initialize Diffuse example cube */
mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube()); mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube());
mTestDiffuse->mTransform.setTranslation(9.0f, 0.0f, -2.0f); mTestDiffuse->getTransform().setTranslation(9.0f, 0.0f, -2.0f);
mTestDiffuse->setShaders(":/solid-diffuse.vert", ":/solid-diffuse.frag"); mTestDiffuse->setShaders(":/solid-diffuse.vert", ":/solid-diffuse.frag");
mTestDiffuse->init();
mTestDiffuse->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestDiffuse->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestDiffuse->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestDiffuse->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestDiffuse->setUniform("uAmbientStrength", 0.2f); mTestDiffuse->setUniform("uAmbientStrength", 0.2f);
mTestDiffuse->reallocateNormals(mTestDiffuse->getNormals()); mTestDiffuse->reallocateNormals(mTestDiffuse->getNormals());
// Initialize Specular example cube // Diffuse lighting example light source. This is just for visual reference.
mMeshes.push_back(
new Qtk::MeshRenderer("diffuseLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(9.0f, 2.0f, -2.0f);
mMeshes.back()->getTransform().scale(0.25f);
/* Initialize Specular example cube */
mTestSpecular = new Qtk::MeshRenderer("specular", Cube()); mTestSpecular = new Qtk::MeshRenderer("specular", Cube());
mTestSpecular->mTransform.setTranslation(11.0f, 0.0f, -2.0f); mTestSpecular->getTransform().setTranslation(11.0f, 0.0f, -2.0f);
mTestSpecular->setShaders(":/solid-specular.vert", ":/solid-specular.frag"); mTestSpecular->setShaders(":/solid-specular.vert", ":/solid-specular.frag");
mTestSpecular->init();
mTestSpecular->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestSpecular->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
mTestSpecular->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestSpecular->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f));
mTestSpecular->setUniform("uAmbientStrength", 0.2f); mTestSpecular->setUniform("uAmbientStrength", 0.2f);
@ -95,105 +197,18 @@ void ExampleScene::init() {
mTestSpecular->setUniform("uSpecularShine", 256); mTestSpecular->setUniform("uSpecularShine", 256);
mTestSpecular->reallocateNormals(mTestSpecular->getNormals()); mTestSpecular->reallocateNormals(mTestSpecular->getNormals());
// Specular lighting example light source. This is just for visual reference.
//
// Model loading
mModels.push_back(
new Qtk::Model("backpack", ":/models/backpack/backpack.obj"));
// Sometimes model textures need flipped in certain directions
mModels.back()->flipTexture("diffuse.jpg", false, true);
mModels.back()->mTransform.setTranslation(0.0f, 0.0f, -10.0f);
mModels.push_back(new Qtk::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.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.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.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.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.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);
mModels.push_back(new Qtk::Model(
"alienTest", ":/models/alien-hominid/alien.obj", ":/model-specular.vert",
":/model-specular.frag"));
mModels.back()->mTransform.setTranslation(3.0f, -1.0f, 10.0f);
mModels.back()->mTransform.scale(0.15f);
mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.ambientStrength", 0.8f);
mModels.back()->setUniform("uMaterial.diffuseStrength", 0.8f);
mModels.back()->setUniform("uMaterial.specularStrength", 1.0f);
mModels.back()->setUniform("uMaterial.shine", 32.0f);
mModels.back()->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
// Test spartan Model with phong lighting, specular and normal mapping
mMeshes.push_back( mMeshes.push_back(
new Qtk::MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS))); new Qtk::MeshRenderer("specularLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(1.0f, 1.5f, 10.0f); mMeshes.back()->getTransform().setTranslation(11.0f, 2.0f, -2.0f);
mMeshes.back()->mTransform.scale(0.25f); mMeshes.back()->getTransform().scale(0.25f);
// This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN);
mModels.push_back(new Qtk::Model(
"spartanTest", ":/models/spartan/spartan.obj", ":/model-normals.vert",
":/model-normals.frag"));
mModels.back()->mTransform.setTranslation(0.0f, -1.0f, 10.0f);
mModels.back()->mTransform.scale(2.0f);
mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.ambientStrength", 1.0f);
mModels.back()->setUniform("uMaterial.diffuseStrength", 1.0f);
mModels.back()->setUniform("uMaterial.specularStrength", 1.0f);
mModels.back()->setUniform("uMaterial.shine", 128.0f);
mModels.back()->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
// Test basic cube with phong.vert and phong.frag shaders
mMeshes.push_back(
new Qtk::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);
// This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN);
/* Test basic cube with phong.vert and phong.frag shaders */
mMeshes.push_back(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS))); mMeshes.push_back(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 10.0f); mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, 10.0f);
mMeshes.back()->setShaders(":/phong.vert", ":/phong.frag"); mMeshes.back()->setShaders(":/phong.vert", ":/phong.frag");
// WARNING: Set color before reallocating normals.
mMeshes.back()->setColor(QVector3D(0.0f, 0.25f, 0.0f)); mMeshes.back()->setColor(QVector3D(0.0f, 0.25f, 0.0f));
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
@ -209,50 +224,84 @@ void ExampleScene::init() {
mMeshes.back()->setUniform("uLight.specular", QVector3D(0.62f, 0.55f, 0.37f)); mMeshes.back()->setUniform("uLight.specular", QVector3D(0.62f, 0.55f, 0.37f));
mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
// Light source for testPhong cube
mMeshes.push_back(
new Qtk::MeshRenderer("testLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(5.0f, 1.25f, 10.0f);
mMeshes.back()->getTransform().scale(0.25f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
mMeshes.back()->setColor(RED);
// //
// Create simple shapes using MeshRenderer class and data in mesh.h // Building more complex objects for showing examples of lighting techniques
mMeshes.push_back( /* Test alien Model with phong lighting and specular mapping. */
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS))); mModels.push_back(new Qtk::Model(
mMeshes.back()->mTransform.setTranslation(-5.0f, 0.0f, -2.0f); "alienTest", ":/models/alien-hominid/alien.obj", ":/model-specular.vert",
":/model-specular.frag"));
mModels.back()->getTransform().setTranslation(3.0f, -1.0f, 10.0f);
mModels.back()->getTransform().scale(0.15f);
mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.ambientStrength", 0.8f);
mModels.back()->setUniform("uMaterial.diffuseStrength", 0.8f);
mModels.back()->setUniform("uMaterial.specularStrength", 1.0f);
mModels.back()->setUniform("uMaterial.shine", 32.0f);
mMeshes.push_back( mModels.back()->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS))); mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mMeshes.back()->mTransform.setTranslation(-7.0f, 0.0f, -2.0f); mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
mMeshes.push_back( // Light source for alienTest object.
new Qtk::MeshRenderer("leftTriangle", Triangle(QTK_DRAW_ELEMENTS))); mMeshes.push_back(new Qtk::MeshRenderer(
mMeshes.back()->mTransform.setTranslation(-9.0f, 0.0f, -2.0f); "alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS)));
mMeshes.back()->setDrawType(GL_LINE_LOOP); mMeshes.back()->getTransform().setTranslation(4.0f, 1.5f, 10.0f);
mMeshes.back()->getTransform().scale(0.25f);
mMeshes.push_back(
new Qtk::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)));
mMeshes.back()->mTransform.setTranslation(-7.0f, -2.0f, -2.0f);
mMeshes.back()->mTransform.scale(0.25f);
mMeshes.back()->setDrawType(GL_LINE_LOOP);
// This function changes values we have allocated in a buffer, so init() after // This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN); mMeshes.back()->setColor(GREEN);
/* Test spartan Model with phong lighting, specular and normal mapping. */
mModels.push_back(new Qtk::Model(
"spartanTest", ":/models/spartan/spartan.obj", ":/model-normals.vert",
":/model-normals.frag"));
mModels.back()->getTransform().setTranslation(0.0f, -1.0f, 10.0f);
mModels.back()->getTransform().scale(2.0f);
mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uMaterial.ambientStrength", 1.0f);
mModels.back()->setUniform("uMaterial.diffuseStrength", 1.0f);
mModels.back()->setUniform("uMaterial.specularStrength", 1.0f);
mModels.back()->setUniform("uMaterial.shine", 128.0f);
mModels.back()->setUniform("uLight.ambient", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.diffuse", QVector3D(1.0f, 1.0f, 1.0f));
mModels.back()->setUniform("uLight.specular", QVector3D(1.0f, 1.0f, 1.0f));
// Light source for spartanTest object.
mMeshes.push_back(
new Qtk::MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS)));
mMeshes.back()->getTransform().setTranslation(1.0f, 1.5f, 10.0f);
mMeshes.back()->getTransform().scale(0.25f);
// This function changes values we have allocated in a buffer, so init() after
mMeshes.back()->setColor(GREEN);
// //
// Testing for normals, texture coordinates // Test drawing simple geometry with various OpenGL drawing modes
// RGB Normals cube to show normals are correct with QTK_DRAW_ARRAYS // RGB Normals cube to show normals are correct with QTK_DRAW_ARRAYS
mMeshes.push_back( mMeshes.push_back(
new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 4.0f); mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, 4.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
// RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS // RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS
mMeshes.push_back(new Qtk::MeshRenderer( mMeshes.push_back(new Qtk::MeshRenderer(
"rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); "rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 2.0f); mMeshes.back()->getTransform().setTranslation(5.0f, 0.0f, 2.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
@ -260,7 +309,7 @@ void ExampleScene::init() {
crateTexture.setTexture(":/crate.png"); crateTexture.setTexture(":/crate.png");
Cube cube; Cube cube;
auto * m = new MeshRenderer("Test Crate", Cube(QTK_DRAW_ARRAYS)); auto * m = new MeshRenderer("Test Crate", Cube(QTK_DRAW_ARRAYS));
m->mTransform.setTranslation(0, 0, 13); m->getTransform().setTranslation(0, 0, 13);
m->setShaders(":/texture2d.vert", ":/texture2d.frag"); m->setShaders(":/texture2d.vert", ":/texture2d.frag");
m->setTexture(crateTexture); m->setTexture(crateTexture);
m->setUniform("uTexture", 0); m->setUniform("uTexture", 0);
@ -274,7 +323,7 @@ void ExampleScene::init() {
// + This is because the same position must use different UV coordinates // + This is because the same position must use different UV coordinates
mMeshes.push_back( mMeshes.push_back(
new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(-3.0f, 0.0f, -2.0f); mMeshes.back()->getTransform().setTranslation(-3.0f, 0.0f, -2.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->setTexture(crateTexture); mMeshes.back()->setTexture(crateTexture);
mMeshes.back()->setUniform("uTexture", 0); mMeshes.back()->setUniform("uTexture", 0);
@ -283,7 +332,7 @@ void ExampleScene::init() {
// Test drawing a cube with texture coordinates using glDrawElements // Test drawing a cube with texture coordinates using glDrawElements
mMeshes.push_back(new Qtk::MeshRenderer( mMeshes.push_back(new Qtk::MeshRenderer(
"uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); "uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->mTransform.setTranslation(-1.7f, 0.0f, -2.0f); mMeshes.back()->getTransform().setTranslation(-1.7f, 0.0f, -2.0f);
mMeshes.back()->setTexture(":/crate.png"); mMeshes.back()->setTexture(":/crate.png");
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->bindShaders(); mMeshes.back()->bindShaders();
@ -291,14 +340,14 @@ void ExampleScene::init() {
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords(), 3); mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords(), 3);
mMeshes.back()->releaseShaders(); mMeshes.back()->releaseShaders();
mMeshes.back()->mTransform.rotate(45.0f, 0.0f, 1.0f, 0.0f); mMeshes.back()->getTransform().rotate(45.0f, 0.0f, 1.0f, 0.0f);
// Texturing a cube using a cube map // Texturing a cube using a cube map
// + Cube map texturing works with both QTK_DRAW_ARRAYS and QTK_DRAW_ELEMENTS // + Cube map texturing works with both QTK_DRAW_ARRAYS and QTK_DRAW_ELEMENTS
mMeshes.push_back( mMeshes.push_back(
new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS))); new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(-3.0f, 1.0f, -2.0f); mMeshes.back()->getTransform().setTranslation(-3.0f, 1.0f, -2.0f);
mMeshes.back()->mTransform.setRotation(45.0f, 0.0f, 1.0f, 0.0f); mMeshes.back()->getTransform().setRotation(45.0f, 0.0f, 1.0f, 0.0f);
mMeshes.back()->setShaders( mMeshes.back()->setShaders(
":/texture-cubemap.vert", ":/texture-cubemap.frag"); ":/texture-cubemap.vert", ":/texture-cubemap.frag");
mMeshes.back()->setCubeMap(":/crate.png"); mMeshes.back()->setCubeMap(":/crate.png");
@ -309,28 +358,28 @@ void ExampleScene::init() {
// + Apply RGB normals shader and spin the cube for a neat effect // + Apply RGB normals shader and spin the cube for a neat effect
mMeshes.push_back( mMeshes.push_back(
new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS))); new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 2.0f, -2.0f); mMeshes.back()->getTransform().setTranslation(5.0f, 2.0f, -2.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
// RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS // RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS
mMeshes.push_back(new Qtk::MeshRenderer( mMeshes.push_back(new Qtk::MeshRenderer(
"rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS))); "rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 2.0f); mMeshes.back()->getTransform().setTranslation(7.0f, 0.0f, 2.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
// RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS // RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS
mMeshes.push_back(new Qtk::MeshRenderer( mMeshes.push_back(new Qtk::MeshRenderer(
"rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS))); "rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 4.0f); mMeshes.back()->getTransform().setTranslation(7.0f, 0.0f, 4.0f);
mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag");
mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals());
// Test drawing triangle with glDrawArrays with texture coordinates // Test drawing triangle with glDrawArrays with texture coordinates
mMeshes.push_back( mMeshes.push_back(
new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS))); new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS)));
mMeshes.back()->mTransform.setTranslation(-3.0f, 2.0f, -2.0f); mMeshes.back()->getTransform().setTranslation(-3.0f, 2.0f, -2.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->setTexture(":/crate.png"); mMeshes.back()->setTexture(":/crate.png");
@ -340,40 +389,16 @@ void ExampleScene::init() {
// Test drawing triangle with glDrawElements with texture coordinates // Test drawing triangle with glDrawElements with texture coordinates
mMeshes.push_back(new Qtk::MeshRenderer( mMeshes.push_back(new Qtk::MeshRenderer(
"testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS))); "testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS)));
mMeshes.back()->mTransform.setTranslation(-2.5f, 0.0f, -1.0f); mMeshes.back()->getTransform().setTranslation(-2.5f, 0.0f, -1.0f);
mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag");
mMeshes.back()->setTexture(":/crate.png"); mMeshes.back()->setTexture(":/crate.png");
mMeshes.back()->setUniform("uTexture", 0); mMeshes.back()->setUniform("uTexture", 0);
mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords());
//
// Lighting cube examples
// Example of a cube with no lighting applied
mMeshes.push_back(new Qtk::MeshRenderer("noLight", Cube(QTK_DRAW_ELEMENTS)));
mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, -2.0f);
mMeshes.back()->setShaders(
":/solid-perspective.vert", ":/solid-perspective.frag");
mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f));
// Create objects that represent light sources for lighting examples
mMeshes.push_back(
new Qtk::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)));
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)));
mMeshes.back()->mTransform.setTranslation(11.0f, 2.0f, -2.0f);
mMeshes.back()->mTransform.scale(0.25f);
} }
void ExampleScene::draw() { void ExampleScene::draw() {
// WARNING: We must call the base class draw() function first.
// + This will handle rendering core scene components like the Skybox.
Scene::draw(); Scene::draw();
for(const auto & model : mModels) { for(const auto & model : mModels) {
@ -387,129 +412,156 @@ void ExampleScene::draw() {
mTestPhong->bindShaders(); mTestPhong->bindShaders();
mTestPhong->setUniform( mTestPhong->setUniform(
"uModelInverseTransposed", "uModelInverseTransposed",
mTestPhong->mTransform.toMatrix().normalMatrix()); mTestPhong->getTransform().toMatrix().normalMatrix());
mTestPhong->setUniform( mTestPhong->setUniform(
"uLightPosition", "uLightPosition",
MeshRenderer::getInstance("phongLight")->mTransform.getTranslation()); MeshRenderer::getInstance("phongLight")->getTransform().getTranslation());
mTestPhong->setUniform( mTestPhong->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation()); "uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
mTestPhong->releaseShaders(); mTestPhong->releaseShaders();
mTestPhong->draw(); mTestPhong->draw();
mTestAmbient->bindShaders(); mTestAmbient->bindShaders();
mTestAmbient->setUniform( mTestAmbient->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation()); "uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
mTestAmbient->releaseShaders(); mTestAmbient->releaseShaders();
mTestAmbient->draw(); mTestAmbient->draw();
mTestDiffuse->bindShaders(); mTestDiffuse->bindShaders();
mTestDiffuse->setUniform( mTestDiffuse->setUniform(
"uModelInverseTransposed", "uModelInverseTransposed",
mTestDiffuse->mTransform.toMatrix().normalMatrix()); mTestDiffuse->getTransform().toMatrix().normalMatrix());
mTestDiffuse->setUniform( mTestDiffuse->setUniform(
"uLightPosition", "uLightPosition", MeshRenderer::getInstance("diffuseLight")
MeshRenderer::getInstance("diffuseLight")->mTransform.getTranslation()); ->getTransform()
.getTranslation());
mTestDiffuse->setUniform( mTestDiffuse->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation()); "uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
mTestDiffuse->releaseShaders(); mTestDiffuse->releaseShaders();
mTestDiffuse->draw(); mTestDiffuse->draw();
mTestSpecular->bindShaders(); mTestSpecular->bindShaders();
mTestSpecular->setUniform( mTestSpecular->setUniform(
"uModelInverseTransposed", "uModelInverseTransposed",
mTestSpecular->mTransform.toMatrix().normalMatrix()); mTestSpecular->getTransform().toMatrix().normalMatrix());
mTestSpecular->setUniform( mTestSpecular->setUniform(
"uLightPosition", "uLightPosition", MeshRenderer::getInstance("specularLight")
MeshRenderer::getInstance("specularLight")->mTransform.getTranslation()); ->getTransform()
.getTranslation());
mTestSpecular->setUniform( mTestSpecular->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation()); "uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
mTestSpecular->releaseShaders(); mTestSpecular->releaseShaders();
mTestSpecular->draw(); mTestSpecular->draw();
} }
void ExampleScene::update() { void ExampleScene::update() {
auto position = auto mySpartan = Model::getInstance("My spartan");
MeshRenderer::getInstance("alienTestLight")->mTransform.getTranslation(); mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto myCube = MeshRenderer::getInstance("My cube");
myCube->getTransform().rotate(-0.75f, 0.0f, 1.0f, 0.0f);
auto position = MeshRenderer::getInstance("alienTestLight")
->getTransform()
.getTranslation();
auto alien = Model::getInstance("alienTest"); auto alien = Model::getInstance("alienTest");
alien->setUniform("uLight.position", position); alien->setUniform("uLight.position", position);
alien->setUniform( alien->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation()); "uCameraPosition",
auto posMatrix = alien->mTransform.toMatrix(); ExampleScene::getCamera().getTransform().getTranslation());
auto posMatrix = alien->getTransform().toMatrix();
alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
alien->setUniform("uMVP.model", posMatrix); alien->setUniform("uMVP.model", posMatrix);
alien->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); alien->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix());
alien->setUniform("uMVP.projection", ExampleScene::Projection()); alien->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix());
alien->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
position = MeshRenderer::getInstance("spartanTestLight") position = MeshRenderer::getInstance("spartanTestLight")
->mTransform.getTranslation(); ->getTransform()
.getTranslation();
auto spartan = Model::getInstance("spartanTest"); auto spartan = Model::getInstance("spartanTest");
spartan->setUniform("uLight.position", position); spartan->setUniform("uLight.position", position);
spartan->setUniform( spartan->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation()); "uCameraPosition",
posMatrix = spartan->mTransform.toMatrix(); ExampleScene::getCamera().getTransform().getTranslation());
posMatrix = spartan->getTransform().toMatrix();
spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
spartan->setUniform("uMVP.model", posMatrix); spartan->setUniform("uMVP.model", posMatrix);
spartan->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); spartan->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix());
spartan->setUniform("uMVP.projection", ExampleScene::Projection()); spartan->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix());
spartan->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto phong = MeshRenderer::getInstance("testPhong"); auto phong = MeshRenderer::getInstance("testPhong");
phong->mTransform.rotate(0.75f, 1.0f, 0.5f, 0.0f); phong->getTransform().rotate(0.75f, 1.0f, 0.5f, 0.0f);
phong->bindShaders(); phong->bindShaders();
position = position =
MeshRenderer::getInstance("testLight")->mTransform.getTranslation(); MeshRenderer::getInstance("testLight")->getTransform().getTranslation();
phong->setUniform("uLight.position", position); phong->setUniform("uLight.position", position);
phong->setUniform( phong->setUniform(
"uCameraPosition", ExampleScene::Camera().transform().getTranslation()); "uCameraPosition",
posMatrix = phong->mTransform.toMatrix(); ExampleScene::getCamera().getTransform().getTranslation());
posMatrix = phong->getTransform().toMatrix();
phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
phong->setUniform("uMVP.model", posMatrix); phong->setUniform("uMVP.model", posMatrix);
phong->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); phong->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix());
phong->setUniform("uMVP.projection", ExampleScene::Projection()); phong->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix());
phong->releaseShaders(); phong->releaseShaders();
// Rotate lighting example cubes // Rotate lighting example cubes
mTestPhong->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); mTestPhong->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
MeshRenderer::getInstance("noLight")->mTransform.rotate( MeshRenderer::getInstance("noLight")->getTransform().rotate(
0.75f, 0.5f, 0.3f, 0.2f); 0.75f, 0.5f, 0.3f, 0.2f);
mTestAmbient->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); mTestAmbient->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
mTestDiffuse->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); mTestDiffuse->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
mTestSpecular->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); mTestSpecular->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
// Examples of various translations and rotations // Examples of various translations and rotations
// Rotate in multiple directions simultaneously // Rotate in multiple directions simultaneously
MeshRenderer::getInstance("rgbNormalsCube") MeshRenderer::getInstance("rgbNormalsCube")
->mTransform.rotate(0.75f, 0.2f, 0.4f, 0.6f); ->getTransform()
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
// Pitch forward and roll sideways // Pitch forward and roll sideways
MeshRenderer::getInstance("leftTriangle") MeshRenderer::getInstance("leftTriangle")
->mTransform.rotate(0.75f, 1.0f, 0.0f, 0.0f); ->getTransform()
.rotate(0.75f, 1.0f, 0.0f, 0.0f);
MeshRenderer::getInstance("rightTriangle") MeshRenderer::getInstance("rightTriangle")
->mTransform.rotate(0.75f, 0.0f, 0.0f, 1.0f); ->getTransform()
.rotate(0.75f, 0.0f, 0.0f, 1.0f);
// Move between two positions over time // Move between two positions over time
static float translateX = 0.025f; static float translateX = 0.025f;
float limit = -9.0f; // Origin position.x - 2.0f float limit = -9.0f; // Origin position.x - 2.0f
float posX = float posX = MeshRenderer::getInstance("topTriangle")
MeshRenderer::getInstance("topTriangle")->mTransform.getTranslation().x(); ->getTransform()
.getTranslation()
.x();
if(posX < limit || posX > limit + 4.0f) { if(posX < limit || posX > limit + 4.0f) {
translateX = -translateX; translateX = -translateX;
} }
MeshRenderer::getInstance("topTriangle") MeshRenderer::getInstance("topTriangle")
->mTransform.translate(translateX, 0.0f, 0.0f); ->getTransform()
.translate(translateX, 0.0f, 0.0f);
MeshRenderer::getInstance("bottomTriangle") MeshRenderer::getInstance("bottomTriangle")
->mTransform.translate(-translateX, 0.0f, 0.0f); ->getTransform()
.translate(-translateX, 0.0f, 0.0f);
// And lets rotate the triangles in two directions at once // And lets rotate the triangles in two directions at once
MeshRenderer::getInstance("topTriangle") MeshRenderer::getInstance("topTriangle")
->mTransform.rotate(0.75f, 0.2f, 0.0f, 0.4f); ->getTransform()
.rotate(0.75f, 0.2f, 0.0f, 0.4f);
MeshRenderer::getInstance("bottomTriangle") MeshRenderer::getInstance("bottomTriangle")
->mTransform.rotate(0.75f, 0.0f, 0.2f, 0.4f); ->getTransform()
.rotate(0.75f, 0.0f, 0.2f, 0.4f);
// And make the bottom triangle green, instead of RGB // And make the bottom triangle green, instead of RGB
// Rotate center cube in several directions simultaneously // Rotate center cube in several directions simultaneously
// + Not subject to gimbal lock since we are using quaternions :) // + Not subject to gimbal lock since we are using quaternions :)
MeshRenderer::getInstance("centerCube") MeshRenderer::getInstance("centerCube")
->mTransform.rotate(0.75f, 0.2f, 0.4f, 0.6f); ->getTransform()
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
} }

View File

@ -9,22 +9,65 @@
#ifndef QTK_EXAMPLE_SCENE_H #ifndef QTK_EXAMPLE_SCENE_H
#define QTK_EXAMPLE_SCENE_H #define QTK_EXAMPLE_SCENE_H
#include <abstractscene.h>
#include <camera3d.h> #include <camera3d.h>
#include <scene.h>
#include <skybox.h> #include <skybox.h>
#include <QMatrix4x4> #include <QMatrix4x4>
/**
* Example scene using QtkWidget to render 3D models and simple geometry within
* QtOpenGLWidgets. This scene also shows some examples of using GLSL shaders to
* apply the basic lighting techniques leading up to Phong.
*
* The Qtk::Scene base class provides containers for N pointers to MeshRenderer
* and Model objects. We can create and insert as many as we like within this
* child class implementation. This child class does not need to manually draw
* objects inserted into these containers. The child class would only need to
* update uniform or other data that may change per-frame.
* See scene.h and `init()` for more information.
*
* To modify the scene objects should be initialized within the `init()` public
* method. Any required movement or updates should be applied within `draw()` or
* `update()`.
*
* To create your own Scene from scratch see Qtk::Scene.
*/
class ExampleScene : public Qtk::Scene { class ExampleScene : public Qtk::Scene {
public: public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
ExampleScene(); ExampleScene();
~ExampleScene(); ~ExampleScene();
/***************************************************************************
* Inherited Public Overrides
**************************************************************************/
/**
* Initialize objects within the scene
*/
void init() override; void init() override;
/**
* Called when OpenGL repaints the widget.
*/
void draw() override; void draw() override;
/**
* Called when the Qt `frameSwapped` signal is caught.
* See definition of `QtkWidget::initializeGL()`
*/
void update() override; void update() override;
private: private:
/***************************************************************************
* Private Members
**************************************************************************/
// Additional example objects created within this example.
// + The base class Scene manages objects stored within mMeshes or mModels
Qtk::MeshRenderer * mTestPhong {}; Qtk::MeshRenderer * mTestPhong {};
Qtk::MeshRenderer * mTestSpecular {}; Qtk::MeshRenderer * mTestSpecular {};
Qtk::MeshRenderer * mTestDiffuse {}; Qtk::MeshRenderer * mTestDiffuse {};

View File

@ -16,20 +16,6 @@
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
QApplication a(argc, argv); QApplication a(argc, argv);
// Set OpenGL Version information
// Note: This format must be set before show() is called.
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(4, 5);
// Set the number of samples used for glEnable(GL_MULTISAMPLING)
format.setSamples(4);
// Set the size of the depth bufer for glEnable(GL_DEPTH_TEST)
format.setDepthBufferSize(16);
#ifdef QTK_DEBUG
format.setOption(QSurfaceFormat::DebugContext);
#endif // QTK_DEBUG
// Create window for Qt application using custom mainwindow.h // Create window for Qt application using custom mainwindow.h
MainWindow w; MainWindow w;
w.show(); w.show();

View File

@ -1,22 +1,37 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: MainWindow for creating an example Qt application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include <mainwindow.h> #include <mainwindow.h>
#include <qtkwidget.h> #include <qtkwidget.h>
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget * parent) : MainWindow::MainWindow(QWidget * parent) :
QMainWindow(parent), ui(new Ui::MainWindow) { QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
// For use in design mode using Qt Creator // For use in design mode using Qt Creator
// + We can use the `ui` member to access nested widgets by name // + We can use the `ui` member to access nested widgets by name
ui->setupUi(this);
// Find all QtkWidgets in this QMainWindow and initialize their scenes.
for(const auto widget : ui->qWidget->children()) { for(const auto widget : ui->qWidget->children()) {
auto qtkWidget = dynamic_cast<Qtk::QtkWidget *>(widget); auto qtkWidget = dynamic_cast<Qtk::QtkWidget *>(widget);
if(qtkWidget != nullptr) { if(qtkWidget != nullptr) {
std::string key = qtkWidget->objectName().toStdString(); std::string key = qtkWidget->objectName().toStdString();
// Initialize each scene into a map if it doesn't exist.
if(mScenes[key] == nullptr) { if(mScenes[key] == nullptr) {
mScenes[qtkWidget->objectName().toStdString()] = new ExampleScene(); mScenes[key] = new ExampleScene();
} }
qtkWidget->setScene(mScenes[qtkWidget->objectName().toStdString()]); // Set the QtkWidget to use the scene associated with this widget.
qtkWidget->setScene(mScenes[key]);
} }
} }
// Set the window icon used for Qtk.
// TODO: Update this to be something other than kilroy.
setWindowIcon(QIcon("../resources/icon.png")); setWindowIcon(QIcon("../resources/icon.png"));
} }

View File

@ -1,3 +1,11 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: MainWindow for creating an example Qt application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
@ -12,16 +20,32 @@ namespace Ui {
class MainWindow; class MainWindow;
} }
class QTK_WIDGET_EXPORT MainWindow /**
* MainWindow class to provide an example of using a QtkWidget within a Qt
: public QMainWindow { * window application.
*/
class QTK_WIDGET_EXPORT MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
public: public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
/**
* This ctor also initializes the Scene for each QtkWidget in the window.
* To load a different scene this would need to be updated.
*
* @param parent The parent for this QMainWindow
*/
explicit MainWindow(QWidget * parent = nullptr); explicit MainWindow(QWidget * parent = nullptr);
~MainWindow() override; ~MainWindow() override;
private: private:
/***************************************************************************
* Private Members
**************************************************************************/
Ui::MainWindow * ui {}; Ui::MainWindow * ui {};
std::unordered_map<std::string, Qtk::Scene *> mScenes {}; std::unordered_map<std::string, Qtk::Scene *> mScenes {};
}; };

View File

@ -13,6 +13,19 @@
#ifndef QTK_RESOURCEMANAGER_H #ifndef QTK_RESOURCEMANAGER_H
#define QTK_RESOURCEMANAGER_H #define QTK_RESOURCEMANAGER_H
/**
* ResourceManager class used to construct absolute paths to files within the Qt
* resources path. There is no need to manually call this method.
* Model::loadModel(...) will use this method if a Qt resource path is provided.
* The Model constructor behaves the same. If a path is prefixed with `:/` this
* static method will be used to resolve a full system path.
*
* This will likely be deprecated. It has a single call site and it is not
* meant for public use. It is public only for convenience.
*
* RM::getPath(":/models/alien-hominid/alien.obj") =
* /full/path/to/models/alien-hominid/alien.obj
*/
typedef class ResourceManager { typedef class ResourceManager {
public: public:
/** /**
@ -24,7 +37,7 @@ typedef class ResourceManager {
* ':/models/backpack/backpack.obj' An asset at location * ':/models/backpack/backpack.obj' An asset at location
* qtk/resources/path/to/asset.obj Should be given in qrc format: * qtk/resources/path/to/asset.obj Should be given in qrc format:
* ':/path/to/asset.obj' * ':/path/to/asset.obj'
* @return Absoulte system path to a qtk asset * @return Absolute system path to a qtk asset
*/ */
static std::string getPath(const std::string & path) { static std::string getPath(const std::string & path) {
// Only construct qtk resource path if in qrc format; else return it as-is // Only construct qtk resource path if in qrc format; else return it as-is

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

@ -32,22 +32,22 @@ const QMatrix4x4 & Camera3D::toMatrix() {
******************************************************************************/ ******************************************************************************/
QDataStream & operator<<(QDataStream & out, Camera3D & transform) { QDataStream & operator<<(QDataStream & out, Camera3D & transform) {
out << transform.transform(); out << transform.getTransform();
return out; return out;
} }
QDataStream & operator>>(QDataStream & in, Camera3D & transform) { QDataStream & operator>>(QDataStream & in, Camera3D & transform) {
in >> transform.transform(); in >> transform.getTransform();
return in; return in;
} }
QDebug operator<<(QDebug dbg, const Camera3D & transform) { QDebug operator<<(QDebug dbg, const Camera3D & transform) {
dbg << "Camera3D\n{\n"; dbg << "Camera3D\n{\n";
dbg << "Position: <" << transform.translation().x() << ", " dbg << "Position: <" << transform.getTranslation().x() << ", "
<< transform.translation().y() << ", " << transform.translation().z() << transform.getTranslation().y() << ", "
<< ">\n"; << transform.getTranslation().z() << ">\n";
dbg << "Rotation: <" << transform.rotation().x() << ", " dbg << "Rotation: <" << transform.getRotation().x() << ", "
<< transform.rotation().y() << ", " << transform.rotation().z() << " | " << transform.getRotation().y() << ", " << transform.getRotation().z()
<< transform.rotation().scalar() << ">\n}"; << " | " << transform.getRotation().scalar() << ">\n}";
return dbg; return dbg;
} }

View File

@ -17,19 +17,25 @@
namespace Qtk { namespace Qtk {
class QTKAPI Camera3D { class QTKAPI Camera3D {
public: public:
// Constants /*************************************************************************
* Constants
************************************************************************/
static const QVector3D LocalForward; static const QVector3D LocalForward;
static const QVector3D LocalUp; static const QVector3D LocalUp;
static const QVector3D LocalRight; static const QVector3D LocalRight;
// Accessors /*************************************************************************
inline Transform3D & transform() { return mTransform; } * Accessors
************************************************************************/
[[nodiscard]] inline const QVector3D & translation() const { inline Transform3D & getTransform() { return mTransform; }
[[nodiscard]] inline const QVector3D & getTranslation() const {
return mTransform.getTranslation(); return mTransform.getTranslation();
} }
[[nodiscard]] inline const QQuaternion & rotation() const { [[nodiscard]] inline const QQuaternion & getRotation() const {
return mTransform.getRotation(); return mTransform.getRotation();
} }
@ -49,9 +55,17 @@ namespace Qtk {
} }
private: private:
/*************************************************************************
* Private Members
************************************************************************/
Transform3D mTransform; Transform3D mTransform;
QMatrix4x4 mWorld; QMatrix4x4 mWorld;
/*************************************************************************
* Private Methods
************************************************************************/
#ifndef QT_NO_DATASTREAM #ifndef QT_NO_DATASTREAM
friend QDataStream & operator<<(QDataStream & out, Camera3D & transform); friend QDataStream & operator<<(QDataStream & out, Camera3D & transform);
friend QDataStream & operator>>(QDataStream & in, Camera3D & transform); friend QDataStream & operator>>(QDataStream & in, Camera3D & transform);

View File

@ -17,10 +17,15 @@
namespace Qtk { namespace Qtk {
class QTKAPI Input { class QTKAPI Input {
public:
/*************************************************************************
* Typedefs
************************************************************************/
friend class Qtk::QtkWidget; friend class Qtk::QtkWidget;
public: /**
// Possible key states * Possible key states
*/
enum InputState { enum InputState {
InputInvalid, InputInvalid,
InputRegistered, InputRegistered,
@ -30,6 +35,10 @@ namespace Qtk {
InputReleased InputReleased
}; };
/*************************************************************************
* Public Methods
************************************************************************/
// State checking // State checking
inline static bool keyTriggered(Qt::Key key) { inline static bool keyTriggered(Qt::Key key) {
return keyState(key) == InputTriggered; return keyState(key) == InputTriggered;
@ -63,6 +72,10 @@ namespace Qtk {
static QPoint mouseDelta(); static QPoint mouseDelta();
private: private:
/*************************************************************************
* Private Methods
************************************************************************/
// State updating // State updating
static void update(); static void update();
static void registerKeyPress(int key); static void registerKeyPress(int key);

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_MAINWIDGET_H
#define QTK_MAINWIDGET_H
#include <iostream>
#include <QMatrix4x4>
#include <QOpenGLDebugLogger>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#define QTK_DEBUG
class MeshRenderer;
class Model;
class Object;
class Scene;
class Skybox;
class OpenGLTextureFactory;
class MainWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT;
public:
// Constructors
MainWidget();
explicit MainWidget(QWidget * parent);
explicit MainWidget(const QSurfaceFormat & format);
~MainWidget() override;
private:
void teardownGL();
void initObjects();
public:
// Inherited virtual Members
void paintGL() override;
void initializeGL() override;
void resizeGL(int width, int height) override;
protected slots:
void update();
void messageLogged(const QOpenGLDebugMessage & msg);
// Protected Helpers
protected:
void keyPressEvent(QKeyEvent * event) override;
void keyReleaseEvent(QKeyEvent * event) override;
void mousePressEvent(QMouseEvent * event) override;
void mouseReleaseEvent(QMouseEvent * event) override;
private:
// Private helpers
void initializeWidget();
void printContextInformation();
void updateCameraInput();
Scene * mScene;
Object * mObject;
QOpenGLDebugLogger * mDebugLogger;
};
#endif // QTK_MAINWIDGET_H

View File

@ -13,6 +13,9 @@ using namespace Qtk;
Cube::Cube(DrawMode mode) { Cube::Cube(DrawMode mode) {
mDrawMode = mode; mDrawMode = mode;
switch(mode) { switch(mode) {
// The order of the following assignment values helps to visualize.
// clang-format off
// Cube data for use with glDrawArrays // Cube data for use with glDrawArrays
case QTK_DRAW_ARRAYS: case QTK_DRAW_ARRAYS:
mIndices = {/* No indices needed for glDrawArrays */}; mIndices = {/* No indices needed for glDrawArrays */};
@ -20,51 +23,76 @@ Cube::Cube(DrawMode mode) {
mNormals = {FACE_FRONT, FACE_BACK, FACE_TOP, mNormals = {FACE_FRONT, FACE_BACK, FACE_TOP,
FACE_BOTTOM, FACE_LEFT, FACE_RIGHT}; FACE_BOTTOM, FACE_LEFT, FACE_RIGHT};
mVertices = {// Face 1 (Front) // We're drawing triangles to construct the geometry of a cube.
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBL, VERTEX_FBR, // Each triangle is made up of three points.
VERTEX_FTR, // The entire cube has 12 triangles to make 6 square faces of the cube.
mVertices = {
// Face 1 (Front)
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL,
VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
// Face 2 (Back) // Face 2 (Back)
VERTEX_BBR, VERTEX_BTL, VERTEX_BTR, VERTEX_BTL, VERTEX_BBR, VERTEX_BBR, VERTEX_BTL, VERTEX_BTR,
VERTEX_BBL, VERTEX_BTL, VERTEX_BBR, VERTEX_BBL,
// Face 3 (Top) // Face 3 (Top)
VERTEX_FTR, VERTEX_BTR, VERTEX_BTL, VERTEX_BTL, VERTEX_FTL, VERTEX_FTR, VERTEX_BTR, VERTEX_BTL,
VERTEX_FTR, VERTEX_BTL, VERTEX_FTL, VERTEX_FTR,
// Face 4 (Bottom) // Face 4 (Bottom)
VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, VERTEX_FBL, VERTEX_BBL,
VERTEX_FBR, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR,
// Face 5 (Left) // Face 5 (Left)
VERTEX_FBL, VERTEX_FTL, VERTEX_BTL, VERTEX_FBL, VERTEX_BTL, VERTEX_FBL, VERTEX_FTL, VERTEX_BTL,
VERTEX_BBL, VERTEX_FBL, VERTEX_BTL, VERTEX_BBL,
// Face 6 (Right) // Face 6 (Right)
VERTEX_FTR, VERTEX_FBR, VERTEX_BBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR, VERTEX_FBR, VERTEX_BBR,
VERTEX_FTR}; VERTEX_BBR, VERTEX_BTR, VERTEX_FTR
};
mColors = {// Face 1 (Front) // Setting colors for each vertex that we defined above.
RED, GREEN, BLUE, BLUE, WHITE, RED, // These are defaults and can be overriden by the caller with setColor().
// The colors below are interpolated from vertex to vertex.
mColors = {
// Face 1 (Front)
RED, GREEN, BLUE,
BLUE, WHITE, RED,
// Face 2 (Back) // Face 2 (Back)
YELLOW, CYAN, MAGENTA, CYAN, YELLOW, BLACK, YELLOW, CYAN, MAGENTA,
CYAN, YELLOW, BLACK,
// Face 3 (Top) // Face 3 (Top)
RED, MAGENTA, CYAN, CYAN, GREEN, RED, RED, MAGENTA, CYAN,
CYAN, GREEN, RED,
// Face 4 (Bottom) // Face 4 (Bottom)
WHITE, BLUE, BLACK, BLACK, YELLOW, WHITE, WHITE, BLUE, BLACK,
BLACK, YELLOW, WHITE,
// Face 5 (Left) // Face 5 (Left)
BLUE, GREEN, CYAN, BLUE, CYAN, BLACK, BLUE, GREEN, CYAN,
BLUE, CYAN, BLACK,
// Face 6 (Right) // Face 6 (Right)
RED, WHITE, YELLOW, YELLOW, MAGENTA, RED}; RED, WHITE, YELLOW,
YELLOW, MAGENTA, RED
};
mTexCoords = {// Face 1 (Front) // Define texture coordinates for the cube.
UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, UV_TOP, // This defines the orientation of the texture when applied the object.
mTexCoords = {
// Face 1 (Front)
UV_TOP, UV_ORIGIN, UV_RIGHT,
UV_RIGHT, UV_CORNER, UV_TOP,
// Face 2 (Back) // Face 2 (Back)
UV_TOP, UV_RIGHT, UV_CORNER, UV_RIGHT, UV_TOP, UV_ORIGIN, UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_TOP, UV_ORIGIN,
// Face 3 (Top) // Face 3 (Top)
UV_CORNER, UV_TOP, UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER, UV_TOP, UV_ORIGIN,
UV_CORNER, UV_ORIGIN, UV_RIGHT, UV_CORNER,
// Face 4 (Bottom) // 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) // 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) // 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; break;
@ -81,7 +109,9 @@ Cube::Cube(DrawMode mode) {
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR,
// 4 5 6 7 // 4 5 6 7
VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR}; VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR};
mIndices = {// Face 1 (Front)
mIndices = {
// Face 1 (Front)
0, 1, 2, 2, 3, 0, 0, 1, 2, 2, 3, 0,
// Face 2 (Back) // Face 2 (Back)
7, 5, 4, 5, 7, 6, 7, 5, 4, 5, 7, 6,
@ -92,14 +122,17 @@ Cube::Cube(DrawMode mode) {
// Face 5 (Left) // Face 5 (Left)
2, 1, 5, 2, 5, 6, 2, 1, 5, 2, 5, 6,
// Face 6 (Right) // Face 6 (Right)
0, 3, 7, 7, 4, 0}; 0, 3, 7, 7, 4, 0
};
break; break;
// Cube shape data for using normals and UVs with glDrawElements // Cube shape data for using normals and UVs with glDrawElements
case QTK_DRAW_ELEMENTS_NORMALS: case QTK_DRAW_ELEMENTS_NORMALS:
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK}; mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
mVertices = {// Face 1 (Front) mVertices = {
// Face 1 (Front)
// 0 1 2 3 // 0 1 2 3
VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
// Face 2 (Back) // Face 2 (Back)
@ -116,9 +149,11 @@ Cube::Cube(DrawMode mode) {
VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL, VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL,
// Face 6 (Right) // Face 6 (Right)
// 20 21 22 23 // 20 21 22 23
VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR}; VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR
};
mIndices = {// Face 1 (Front) mIndices = {
// Face 1 (Front)
0, 1, 2, 2, 3, 0, 0, 1, 2, 2, 3, 0,
// Face 2 (Back) // Face 2 (Back)
4, 5, 6, 6, 7, 4, 4, 5, 6, 6, 7, 4,
@ -126,11 +161,12 @@ Cube::Cube(DrawMode mode) {
8, 9, 10, 10, 11, 8, 8, 9, 10, 10, 11, 8,
// Face 4 (Bottom) // Face 4 (Bottom)
12, 13, 14, 14, 15, 12, 12, 13, 14, 14, 15, 12,
// Face 5 (Left) // Face 5 (Left)
16, 17, 18, 18, 19, 16, 16, 17, 18, 18, 19, 16,
// Face 6 (Right) // Face 6 (Right)
20, 21, 22, 22, 23, 20}; 20, 21, 22, 22, 23, 20
};
mNormals = { mNormals = {
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD,
@ -143,50 +179,27 @@ Cube::Cube(DrawMode mode) {
mTexCoords = { mTexCoords = {
// Face 1 (Front) // Face 1 (Front)
UV_TOP, UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 2 (Back) // Face 2 (Back)
UV_TOP, UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 3 (Top) // Face 3 (Top)
UV_TOP, UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 4 (Bottom) // Face 4 (Bottom)
UV_TOP, UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 5 (Left) // Face 5 (Left)
UV_TOP, UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
// Face 6 (Right) // Face 6 (Right)
UV_TOP, UV_TOP, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_RIGHT, UV_TOP, UV_ORIGIN,
UV_CORNER,
UV_RIGHT,
UV_TOP,
UV_ORIGIN,
}; };
break; break;
// clang-format on
} }
} }
@ -194,6 +207,8 @@ Triangle::Triangle(DrawMode mode) {
mDrawMode = mode; mDrawMode = mode;
const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f); const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f);
switch(mode) { switch(mode) {
// clang-format off
case QTK_DRAW_ARRAYS: case QTK_DRAW_ARRAYS:
mIndices = {/* No indices needed for glDrawArrays */}; mIndices = {/* No indices needed for glDrawArrays */};
@ -201,29 +216,17 @@ Triangle::Triangle(DrawMode mode) {
mVertices = { mVertices = {
// Bottom face (Base of the pyramid) // Bottom face (Base of the pyramid)
VERTEX_BBL, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR,
VERTEX_BBR, VERTEX_FBR, VERTEX_FBL, VERTEX_BBL,
VERTEX_FBR,
VERTEX_FBR,
VERTEX_FBL,
VERTEX_BBL,
// Front face // Front face
VERTEX_FBL, VERTEX_FBL, VERTEX_FBR, triangleTop,
VERTEX_FBR,
triangleTop,
// Back face // Back face
VERTEX_BBR, VERTEX_BBR, VERTEX_BBL, triangleTop,
VERTEX_BBL,
triangleTop,
// Left face // Left face
VERTEX_BBL, VERTEX_BBL, VERTEX_FBL, triangleTop,
VERTEX_FBL,
triangleTop,
// Right face // Right face
VERTEX_FBR, VERTEX_FBR, VERTEX_BBR, triangleTop,
VERTEX_BBR,
triangleTop,
}; };
// Find normals for each triangle of the mesh // Find normals for each triangle of the mesh
@ -238,29 +241,17 @@ Triangle::Triangle(DrawMode mode) {
mTexCoords = { mTexCoords = {
// Bottom face (Base of the pyramid) // Bottom face (Base of the pyramid)
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_RIGHT, UV_CORNER, UV_TOP, UV_ORIGIN,
UV_CORNER,
UV_CORNER,
UV_TOP,
UV_ORIGIN,
// Front face // Front face
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_RIGHT,
UV_CORNER,
// Back face // Back face
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_RIGHT,
UV_CORNER,
// Left face // Left face
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_RIGHT,
UV_CORNER,
// Right face // Right face
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_RIGHT,
UV_CORNER,
}; };
break; break;
@ -296,34 +287,22 @@ Triangle::Triangle(DrawMode mode) {
mVertices = { mVertices = {
// Bottom face // Bottom face
// 0 1 2 // 0 1 2
VERTEX_FBL, VERTEX_FBL, VERTEX_FBR, VERTEX_BBL,
VERTEX_FBR,
VERTEX_BBL,
// 3 4 5 // 3 4 5
VERTEX_BBR, VERTEX_BBR, VERTEX_FBR, VERTEX_BBL,
VERTEX_FBR,
VERTEX_BBL,
// Front face // Front face
// 6 7 8 // 6 7 8
VERTEX_FBL, VERTEX_FBL, VERTEX_FBR, triangleTop,
VERTEX_FBR,
triangleTop,
// Back face // Back face
// 9 10 11 // 9 10 11
VERTEX_BBR, VERTEX_BBR, VERTEX_BBL, triangleTop,
VERTEX_BBL,
triangleTop,
// Left face // Left face
// 12 13 14 // 12 13 14
VERTEX_BBL, VERTEX_BBL, VERTEX_FBL, triangleTop,
VERTEX_FBL,
triangleTop,
// Right face // Right face
// 15 16 17 // 15 16 17
VERTEX_FBR, VERTEX_FBR, VERTEX_BBR, triangleTop,
VERTEX_BBR,
triangleTop,
}; };
mIndices = { mIndices = {
@ -350,30 +329,19 @@ Triangle::Triangle(DrawMode mode) {
mTexCoords = { mTexCoords = {
// Bottom face // Bottom face
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_TOP,
UV_RIGHT, UV_CORNER, UV_RIGHT, UV_TOP,
UV_TOP,
UV_CORNER,
UV_RIGHT,
UV_TOP,
// Front face // Front face
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_RIGHT,
UV_CORNER,
// Back face // Back face
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_RIGHT,
UV_CORNER,
// Left face // Left face
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_RIGHT,
UV_CORNER,
// Right face // Right face
UV_ORIGIN, UV_ORIGIN, UV_RIGHT, UV_CORNER,
UV_RIGHT,
UV_CORNER,
}; };
break; break;
// clang-format on
} }
} }

View File

@ -46,21 +46,22 @@ namespace Qtk {
#define VECTOR_ONE QVector3D(1.0f, 1.0f, 1.0f) #define VECTOR_ONE QVector3D(1.0f, 1.0f, 1.0f)
#define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f) #define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f)
// clang-format off
// A series of direction vectors to represent cube face normal // A series of direction vectors to represent cube face normal
#define FACE_TOP \ #define FACE_TOP VECTOR_UP, VECTOR_UP, VECTOR_UP, \
VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP VECTOR_UP, VECTOR_UP, VECTOR_UP
#define FACE_BOTTOM \ #define FACE_BOTTOM VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, \
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN
#define FACE_LEFT \ #define FACE_LEFT VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, \
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT
#define FACE_RIGHT \ #define FACE_RIGHT VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \ VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT
VECTOR_RIGHT #define FACE_FRONT VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \
#define FACE_FRONT \ VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \ #define FACE_BACK VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, \
VECTOR_FORWARD, VECTOR_FORWARD VECTOR_BACK, VECTOR_BACK, VECTOR_BACK
#define FACE_BACK \ // clang-format on
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK
// Colors using QVector3Ds as RGB values // Colors using QVector3Ds as RGB values
#define WHITE VECTOR_ONE #define WHITE VECTOR_ONE
@ -84,13 +85,26 @@ namespace Qtk {
typedef std::vector<QVector2D> TexCoords; typedef std::vector<QVector2D> TexCoords;
typedef std::vector<QVector3D> Normals; typedef std::vector<QVector3D> Normals;
/**
* The OpenGL draw mode to initialize QTK shape data for.
* Different draw modes require different organization of data.
* This enum allows us to predefine simple geometry for different draw modes.
*/
enum DrawMode { enum DrawMode {
QTK_DRAW_ARRAYS, QTK_DRAW_ARRAYS,
QTK_DRAW_ELEMENTS, QTK_DRAW_ELEMENTS,
QTK_DRAW_ELEMENTS_NORMALS QTK_DRAW_ELEMENTS_NORMALS
}; };
/**
* Base class for all simple shape objects.
*/
struct QTKAPI ShapeBase { struct QTKAPI ShapeBase {
public:
/*************************************************************************
* Constructors / Destructors
************************************************************************/
explicit ShapeBase( explicit ShapeBase(
DrawMode mode = QTK_DRAW_ARRAYS, Vertices v = {}, Indices i = {}, DrawMode mode = QTK_DRAW_ARRAYS, Vertices v = {}, Indices i = {},
Colors c = {}, TexCoords t = {}, Normals n = {}) : Colors c = {}, TexCoords t = {}, Normals n = {}) :
@ -99,6 +113,10 @@ namespace Qtk {
mIndices(std::move(i)), mTexCoords(std::move(t)), mIndices(std::move(i)), mTexCoords(std::move(t)),
mNormals(std::move(n)) {} mNormals(std::move(n)) {}
/*************************************************************************
* Accessors
************************************************************************/
[[nodiscard]] inline const Vertices & getVertices() const { [[nodiscard]] inline const Vertices & getVertices() const {
return mVertices; return mVertices;
} }
@ -122,6 +140,10 @@ namespace Qtk {
} }
protected: protected:
/*************************************************************************
* Protected Members
************************************************************************/
DrawMode mDrawMode; DrawMode mDrawMode;
Vertices mVertices {}; Vertices mVertices {};
@ -132,13 +154,26 @@ namespace Qtk {
}; };
struct Shape : public ShapeBase { struct Shape : public ShapeBase {
public:
/*************************************************************************
* Typedefs
************************************************************************/
friend MeshRenderer; friend MeshRenderer;
friend Object; friend Object;
/*************************************************************************
* Constructors / Destructors
************************************************************************/
Shape() = default; Shape() = default;
explicit Shape(const ShapeBase & rhs) : ShapeBase(rhs) {} explicit Shape(const ShapeBase & rhs) : ShapeBase(rhs) {}
/*************************************************************************
* Setters
************************************************************************/
virtual inline void setVertices(const Vertices & value) { virtual inline void setVertices(const Vertices & value) {
mVertices = value; mVertices = value;
} }
@ -160,13 +195,15 @@ namespace Qtk {
virtual inline void setShape(const Shape & value) { *this = value; } virtual inline void setShape(const Shape & value) { *this = value; }
}; };
// Primitives inherit from ShapeBase, does not allow setting of shape values /* Primitives inherit from ShapeBase, doesn't allow setting shape values. */
class QTKAPI Mesh {}; class QTKAPI Mesh {};
/* Simple Cube shape. */
struct QTKAPI Cube : public ShapeBase { struct QTKAPI Cube : public ShapeBase {
explicit Cube(DrawMode mode = QTK_DRAW_ARRAYS); explicit Cube(DrawMode mode = QTK_DRAW_ARRAYS);
}; };
/* Simple Triangle shape. */
struct QTKAPI Triangle : public ShapeBase { struct QTKAPI Triangle : public ShapeBase {
explicit Triangle(DrawMode mode = QTK_DRAW_ARRAYS); explicit Triangle(DrawMode mode = QTK_DRAW_ARRAYS);
}; };

View File

@ -8,8 +8,8 @@
#include <QImageReader> #include <QImageReader>
#include <abstractscene.h>
#include <meshrenderer.h> #include <meshrenderer.h>
#include <scene.h>
#include <texture.h> #include <texture.h>
using namespace Qtk; using namespace Qtk;
@ -31,6 +31,13 @@ MeshRenderer::~MeshRenderer() {
// Static member function to retrieve instances of MeshRenderers // Static member function to retrieve instances of MeshRenderers
MeshRenderer * MeshRenderer::getInstance(const QString & name) { MeshRenderer * MeshRenderer::getInstance(const QString & name) {
if(!sInstances.contains(name)) {
#if QTK_DEBUG
qDebug() << "Attempt to access MeshRenderer instance that does not exist! ("
<< qPrintable(name) << ")\n";
#endif
return nullptr;
}
return sInstances[name]; return sInstances[name];
} }
@ -126,8 +133,8 @@ void MeshRenderer::setShaders(
void MeshRenderer::setUniformMVP( void MeshRenderer::setUniformMVP(
const char * model, const char * view, const char * projection) { const char * model, const char * view, const char * projection) {
ShaderBindScope lock(&mProgram, mBound); ShaderBindScope lock(&mProgram, mBound);
mProgram.setUniformValue(projection, Scene::Projection()); mProgram.setUniformValue(projection, Scene::getProjectionMatrix());
mProgram.setUniformValue(view, Scene::View()); mProgram.setUniformValue(view, Scene::getViewMatrix());
mProgram.setUniformValue(model, mTransform.toMatrix()); mProgram.setUniformValue(model, mTransform.toMatrix());
} }
@ -141,6 +148,8 @@ void MeshRenderer::setColor(const QVector3D & color) {
mShape.mColors[i] = color; mShape.mColors[i] = color;
} }
} }
// TODO: Factor this out so we don't need to reinitialize
init();
} }
void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims) { void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims) {

View File

@ -15,31 +15,19 @@
#include <utility> #include <utility>
namespace Qtk { namespace Qtk {
class QTKAPI ShaderBindScope {
public:
explicit ShaderBindScope(
QOpenGLShaderProgram * program, bool was_locked) :
mWasBound(was_locked) {
mProgram = program;
if(!mWasBound) {
mProgram->bind();
}
}
~ShaderBindScope() {
if(!mWasBound) {
mProgram->release();
}
}
private:
QOpenGLShaderProgram * mProgram;
bool mWasBound;
};
class QTKAPI MeshRenderer : public Object { class QTKAPI MeshRenderer : public Object {
public: public:
/*************************************************************************
* Typedefs
************************************************************************/
/* Static QHash of all mesh objects within the scene. */
typedef QHash<QString, MeshRenderer *> MeshManager;
/*************************************************************************
* Constructors / Destructors
************************************************************************/
// Delegate constructors // Delegate constructors
MeshRenderer( MeshRenderer(
const char * name, Vertices vertices, Indices indices, const char * name, Vertices vertices, Indices indices,
@ -54,12 +42,28 @@ namespace Qtk {
MeshRenderer(const char * name, const ShapeBase & shape); MeshRenderer(const char * name, const ShapeBase & shape);
~MeshRenderer() override; ~MeshRenderer() override;
// Retrieve a mesh by name stored within a static QHash /*************************************************************************
static MeshRenderer * getInstance(const QString & name); * Public Methods
************************************************************************/
void init(); void init();
void draw(); void draw();
inline void enableAttributeArray(int location) {
ShaderBindScope lock(&mProgram, mBound);
mVAO.bind();
mProgram.enableAttributeArray(location);
mVAO.release();
}
void reallocateTexCoords(const TexCoords & t, unsigned dims = 2);
void reallocateNormals(const Normals & n, unsigned dims = 3);
/*************************************************************************
* Setters
************************************************************************/
// Draw types like GL_TRIANGLES, GL_POINTS, GL_LINES, etc // Draw types like GL_TRIANGLES, GL_POINTS, GL_LINES, etc
void setDrawType(int drawType) { mDrawType = drawType; } void setDrawType(int drawType) { mDrawType = drawType; }
@ -105,21 +109,24 @@ namespace Qtk {
mVAO.release(); mVAO.release();
} }
inline void enableAttributeArray(int location) { /*************************************************************************
ShaderBindScope lock(&mProgram, mBound); * Accessors
mVAO.bind(); ************************************************************************/
mProgram.enableAttributeArray(location);
mVAO.release();
}
void reallocateTexCoords(const TexCoords & t, unsigned dims = 2); /**
* Retrieve a mesh by name stored within static QHash private member
* @param name The name of the MeshRenderer we want to retrieve.
* @return Pointer to the MeshRenderer, or nullptr if not found.
*/
static MeshRenderer * getInstance(const QString & name);
void reallocateNormals(const Normals & n, unsigned dims = 3); Transform3D & getTransform() { return mTransform; }
// Static QHash of all mesh objects within the scene
typedef QHash<QString, MeshRenderer *> MeshManager;
private: private:
/*************************************************************************
* Private Members
************************************************************************/
static MeshManager sInstances; static MeshManager sInstances;
int mDrawType {}; int mDrawType {};

View File

@ -9,9 +9,9 @@
#include <QFileInfo> #include <QFileInfo>
#include <abstractscene.h>
#include <model.h> #include <model.h>
#include <resourcemanager.h> #include <resourcemanager.h>
#include <scene.h>
#include <texture.h> #include <texture.h>
using namespace Qtk; using namespace Qtk;
@ -97,8 +97,8 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) {
// Set Model View Projection values // Set Model View Projection values
shader.setUniformValue("uModel", mTransform.toMatrix()); shader.setUniformValue("uModel", mTransform.toMatrix());
shader.setUniformValue("uView", Scene::View()); shader.setUniformValue("uView", Scene::getViewMatrix());
shader.setUniformValue("uProjection", Scene::Projection()); shader.setUniformValue("uProjection", Scene::getProjectionMatrix());
GLuint diffuseCount = 1; GLuint diffuseCount = 1;
GLuint specularCount = 1; GLuint specularCount = 1;
@ -180,21 +180,6 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) {
* Model Private Member Functions * Model Private Member Functions
******************************************************************************/ ******************************************************************************/
/**
* Loads a model in .obj, .fbx, .gltf, and other formats
* For a full list of formats see assimp documentation:
* https://github.com/assimp/assimp/blob/master/doc/Fileformats.md
*
* Models should not be loaded into Qt resource system
* Instead pass an *absolute* path to this function
* Relative paths will break if Qtk is executed from different locations
*
* Models can also be loaded from the `qtk/resource` directory using qrc format
* loadModel(":/models/backpack/backpack.obj")
* See resourcemanager.h for more information
*
* @param path Absolute path to a model .obj or other format accepted by assimp
*/
void Model::loadModel(const std::string & path) { void Model::loadModel(const std::string & path) {
Assimp::Importer import; Assimp::Importer import;
@ -362,7 +347,7 @@ ModelMesh::Textures Model::loadMaterialTextures(
// If the texture has not yet been loaded // If the texture has not yet been loaded
if(!skip) { if(!skip) {
ModelTexture texture; ModelTexture texture;
texture.mTexture = OpenGLTextureFactory::initTexture2D( texture.mTexture = OpenGLTextureFactory::initTexture(
std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false, std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false,
false); false);
texture.mID = texture.mTexture->textureId(); texture.mID = texture.mTexture->textureId();
@ -380,7 +365,7 @@ ModelMesh::Textures Model::loadMaterialTextures(
} }
void Model::sortModels() { void Model::sortModels() {
auto cameraPos = Scene::Camera().transform(); auto cameraPos = Scene::getCamera().getTransform();
auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) { auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) {
// Sort by the first vertex position in the model // Sort by the first vertex position in the model
return (cameraPos.getTranslation().distanceToPoint( return (cameraPos.getTranslation().distanceToPoint(

View File

@ -28,14 +28,20 @@
#include <transform3D.h> #include <transform3D.h>
namespace Qtk { namespace Qtk {
/**
* 3D models will store this data for each vertex in geometry.
*/
struct QTKAPI ModelVertex { struct QTKAPI ModelVertex {
QVector3D mPosition; QVector3D mPosition;
QVector3D mNormal; QVector3D mNormal;
QVector2D mTextureCoord;
QVector3D mTangent; QVector3D mTangent;
QVector3D mBitangent; QVector3D mBitangent;
QVector2D mTextureCoord;
}; };
/**
* Struct to store model textures. 3D Models may have multiple.
*/
struct QTKAPI ModelTexture { struct QTKAPI ModelTexture {
GLuint mID {}; GLuint mID {};
QOpenGLTexture * mTexture {}; QOpenGLTexture * mTexture {};
@ -45,14 +51,25 @@ namespace Qtk {
class Model; class Model;
/**
* Mesh class specialized for storing 3D model data.
* Eventually this can be consolidated into a more generic class.
*/
class QTKAPI ModelMesh : protected QOpenGLFunctions { class QTKAPI ModelMesh : protected QOpenGLFunctions {
public: public:
/*************************************************************************
* Typedefs
************************************************************************/
friend Model; friend Model;
typedef std::vector<ModelVertex> Vertices; typedef std::vector<ModelVertex> Vertices;
typedef std::vector<GLuint> Indices; typedef std::vector<GLuint> Indices;
typedef std::vector<ModelTexture> Textures; typedef std::vector<ModelTexture> Textures;
// Constructors, Destructors /*************************************************************************
* Constructors, Destructors
************************************************************************/
ModelMesh( ModelMesh(
Vertices vertices, Indices indices, Textures textures, Vertices vertices, Indices indices, Textures textures,
const char * vertexShader = ":/model-basic.vert", const char * vertexShader = ":/model-basic.vert",
@ -68,78 +85,177 @@ namespace Qtk {
~ModelMesh() = default; ~ModelMesh() = default;
private: /*************************************************************************
void initMesh(const char * vert, const char * frag); * Public Methods
************************************************************************/
// ModelMesh Private Members
QOpenGLBuffer *mVBO, *mEBO;
QOpenGLVertexArrayObject * mVAO;
QOpenGLShaderProgram * mProgram;
public:
inline void draw() { draw(*mProgram); } inline void draw() { draw(*mProgram); }
void draw(QOpenGLShaderProgram & shader); void draw(QOpenGLShaderProgram & shader);
// ModelMesh Public Members /*************************************************************************
* Public Members
************************************************************************/
Vertices mVertices {}; Vertices mVertices {};
Indices mIndices {}; Indices mIndices {};
Textures mTextures {}; Textures mTextures {};
Transform3D mTransform; Transform3D mTransform;
private:
/*************************************************************************
* Private Methods
************************************************************************/
void initMesh(const char * vert, const char * frag);
/*************************************************************************
* Private Members
************************************************************************/
QOpenGLBuffer *mVBO, *mEBO;
QOpenGLVertexArrayObject * mVAO;
QOpenGLShaderProgram * mProgram;
}; };
/**
* Model object that has a ModelMesh.
* Top-level object that represents 3D models stored within a scene.
*/
class QTKAPI Model : public QObject { class QTKAPI Model : public QObject {
Q_OBJECT Q_OBJECT
public: public:
/*************************************************************************
* Typedefs
************************************************************************/
/* ModelManager typedef that will manage global model access. */
typedef QHash<QString, Model *> ModelManager;
/*************************************************************************
* Constructors, Destructors
************************************************************************/
// Default model shaders are provided but we can override them in the ctor
inline Model( inline Model(
const char * name, const char * path, const char * name, const char * path,
const char * vertexShader = ":/model-basic.vert", const char * vertexShader = ":/model-basic.vert",
const char * fragmentShader = ":/model-basic.frag") : const char * fragmentShader = ":/model-basic.frag") :
mName(name), mName(name),
mVertexShader(vertexShader), mFragmentShader(fragmentShader) { mModelPath(path), mVertexShader(vertexShader),
mFragmentShader(fragmentShader) {
loadModel(path); loadModel(path);
} }
inline ~Model() override { mManager.remove(mName); } inline ~Model() override { mManager.remove(mName); }
/*************************************************************************
* Public Methods
************************************************************************/
void draw(); void draw();
void draw(QOpenGLShaderProgram & shader); void draw(QOpenGLShaderProgram & shader);
/**
* Flip a texture associated with this model
*
* @param fileName The name of the texture to flip as it is stored on disk
* @param flipX Flip the texture along the X axis
* @param flipY Flip the texture along the Y axis
*/
void flipTexture( void flipTexture(
const std::string & fileName, bool flipX = false, bool flipY = true); const std::string & fileName, bool flipX = false, bool flipY = true);
/*************************************************************************
* Setters
************************************************************************/
/**
* Sets a uniform value
*
* @tparam T The type of the value we are settings
* @param location The uniform location
* @param value The value to assign to the uniform
*/
template <typename T> void setUniform(const char * location, T value) { template <typename T> void setUniform(const char * location, T value) {
for(auto & mesh : mMeshes) { for(auto & mesh : mMeshes) {
mesh.mProgram->bind(); mesh.mProgram->bind();
mesh.mProgram->setUniformValue(location, value); mesh.mProgram->setUniformValue(location, value);
mesh.mProgram->release(); mesh.mProgram->release();
} }
} }
Transform3D mTransform; /*************************************************************************
* Accessors
************************************************************************/
/**
* Accessor function for retrieving a ModelMesh globally.
* The mesh is retrieved from the mManager private member.
*
* @param name The name of the model to load as it was constructed.
* @return Pointer to the model stored within the scene.
*/
static Model * getInstance(const char * name); static Model * getInstance(const char * name);
typedef QHash<QString, Model *> ModelManager; Transform3D & getTransform() { return mTransform; }
private: private:
static ModelManager mManager; /*************************************************************************
* Private Methods
************************************************************************/
/**
* Loads a model in .obj, .fbx, .gltf, and other formats.
* For a full list of formats see assimp documentation:
* https://github.com/assimp/assimp/blob/master/doc/Fileformats.md
*
* Models should not be loaded into Qt resource system.
* Instead pass an *absolute* path to this function.
* Relative paths will break if Qtk is executed from different locations.
*
* Models can also be loaded from the `qtk/resource` directory using qrc
* format loadModel(":/models/backpack/backpack.obj").
* This does not use Qt resource system, it just provides similar syntax
* for accessing files within the same `resources/` directory.
*
* See resourcemanager.h for more information on how this works.
*
* @param path Absolute path to a model in .obj or another format accepted
* by assimp.
*/
void loadModel(const std::string & path); void loadModel(const std::string & path);
void processNode(aiNode * node, const aiScene * scene); void processNode(aiNode * node, const aiScene * scene);
ModelMesh processMesh(aiMesh * mesh, const aiScene * scene); ModelMesh processMesh(aiMesh * mesh, const aiScene * scene);
ModelMesh::Textures loadMaterialTextures( ModelMesh::Textures loadMaterialTextures(
aiMaterial * mat, aiTextureType type, const std::string & typeName); aiMaterial * mat, aiTextureType type, const std::string & typeName);
void sortModels(); void sortModels();
// Model Private Members /*************************************************************************
* Private Members
************************************************************************/
/* The position of this model in 3D space */
Transform3D mTransform;
/* Static QHash used to store and access models globally. */
static ModelManager mManager;
/* Container to store N loaded textures for this model. */
ModelMesh::Textures mTexturesLoaded {}; ModelMesh::Textures mTexturesLoaded {};
/* Container to store N loaded meshes for this model. */
std::vector<ModelMesh> mMeshes {}; std::vector<ModelMesh> mMeshes {};
/* The directory this model and it's textures are stored. */
std::string mDirectory {}; std::string mDirectory {};
const char *mVertexShader, *mFragmentShader, *mName; /* File names for shaders and 3D model on disk. */
const char *mVertexShader, *mFragmentShader, *mModelPath;
/* Name of the model object within the scene. */
const char * mName;
}; };
} // namespace Qtk } // namespace Qtk

View File

@ -18,12 +18,24 @@
#include <texture.h> #include <texture.h>
namespace Qtk { namespace Qtk {
/**
* Object base class for objects that can exist within a scene.
* An object could be a Cube, Skybox, 3D Model, or other standalone entities.
*/
class QTKAPI Object : public QObject { class QTKAPI Object : public QObject {
Q_OBJECT Q_OBJECT
public: public:
/*************************************************************************
* Typedefs
************************************************************************/
friend MeshRenderer; friend MeshRenderer;
/*************************************************************************
* Constructors / Destructors
************************************************************************/
// Initialize an object with no shape data assigned // Initialize an object with no shape data assigned
explicit Object(const char * name) : explicit Object(const char * name) :
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false) {} mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false) {}
@ -35,19 +47,39 @@ namespace Qtk {
~Object() override = default; ~Object() override = default;
inline const Colors & getColors() { return mShape.mColors; } /*************************************************************************
* Accessors
************************************************************************/
inline const Indices & getIndexData() { return mShape.mIndices; } [[nodiscard]] inline const Colors & getColors() const {
return mShape.mColors;
}
inline const Normals & getNormals() { return mShape.mNormals; } [[nodiscard]] inline const Indices & getIndexData() const {
return mShape.mIndices;
}
[[nodiscard]] inline const Normals & getNormals() const {
return mShape.mNormals;
}
[[nodiscard]] inline const Shape & getShape() const { return mShape; } [[nodiscard]] inline const Shape & getShape() const { return mShape; }
inline const TexCoords & getTexCoords() { return mShape.mTexCoords; } [[nodiscard]] inline const TexCoords & getTexCoords() const {
return mShape.mTexCoords;
}
inline Texture & getTexture() { return mTexture; } [[nodiscard]] inline const Texture & getTexture() const {
return mTexture;
}
inline const Vertices & getVertices() { return mShape.mVertices; } [[nodiscard]] inline const Vertices & getVertices() const {
return mShape.mVertices;
}
/*************************************************************************
* Setters
************************************************************************/
virtual inline void setColors(const Colors & value) { virtual inline void setColors(const Colors & value) {
mShape.mColors = value; mShape.mColors = value;
@ -84,6 +116,10 @@ namespace Qtk {
mShape.mVertices = value; mShape.mVertices = value;
} }
/*************************************************************************
* Public Methods
************************************************************************/
virtual inline void bindShaders() { virtual inline void bindShaders() {
mBound = true; mBound = true;
mProgram.bind(); mProgram.bind();
@ -94,21 +130,19 @@ namespace Qtk {
mProgram.release(); mProgram.release();
} }
private:
/*************************************************************************
* Private Members
************************************************************************/
QOpenGLShaderProgram mProgram;
QOpenGLBuffer mVBO, mNBO; QOpenGLBuffer mVBO, mNBO;
QOpenGLVertexArrayObject mVAO; QOpenGLVertexArrayObject mVAO;
Transform3D mTransform; Transform3D mTransform;
Shape mShape; Shape mShape;
Texture mTexture; Texture mTexture;
const char * mName; const char * mName;
bool mBound; bool mBound;
private:
virtual inline void setTexture(QOpenGLTexture * value) {
mTexture.setTexture(value);
}
QOpenGLShaderProgram mProgram;
}; };
} // namespace Qtk } // namespace Qtk

View File

@ -8,10 +8,10 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <abstractscene.h>
#include <input.h> #include <input.h>
#include <mesh.h> #include <mesh.h>
#include <qtkwidget.h> #include <qtkwidget.h>
#include <scene.h>
using namespace Qtk; using namespace Qtk;
@ -23,7 +23,6 @@ QtkWidget::QtkWidget() : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
initializeWidget(); initializeWidget();
} }
// Constructor for using this widget in QtDesigner
QtkWidget::QtkWidget(QWidget * parent) : QtkWidget::QtkWidget(QWidget * parent) :
QOpenGLWidget(parent), mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) { QOpenGLWidget(parent), mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) {
initializeWidget(); initializeWidget();
@ -41,27 +40,9 @@ QtkWidget::~QtkWidget() {
} }
/******************************************************************************* /*******************************************************************************
* Private Member Functions * Public Inherited Virtual Methods
******************************************************************************/ ******************************************************************************/
void QtkWidget::teardownGL() {
// Nothing to teardown yet...
}
/*******************************************************************************
* Inherited Virtual Member Functions
******************************************************************************/
void QtkWidget::paintGL() {
// Clear buffers
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// Draw the scene first, since it handles drawing our skybox
if(mScene != Q_NULLPTR) {
mScene->draw();
}
}
void QtkWidget::initializeGL() { void QtkWidget::initializeGL() {
initializeOpenGLFunctions(); initializeOpenGLFunctions();
// Connect the frameSwapped signal to call the update() function // Connect the frameSwapped signal to call the update() function
@ -93,11 +74,19 @@ void QtkWidget::initializeGL() {
} }
void QtkWidget::resizeGL(int width, int height) { void QtkWidget::resizeGL(int width, int height) {
Scene::Projection().setToIdentity(); Scene::getProjectionMatrix().setToIdentity();
Scene::Projection().perspective( Scene::getProjectionMatrix().perspective(
45.0f, float(width) / float(height), 0.1f, 1000.0f); 45.0f, float(width) / float(height), 0.1f, 1000.0f);
} }
void QtkWidget::paintGL() {
// Clear buffers and draw the scene if it is valid.
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
if(mScene != Q_NULLPTR) {
mScene->draw();
}
}
/******************************************************************************* /*******************************************************************************
* Protected Slots * Protected Slots
******************************************************************************/ ******************************************************************************/
@ -175,7 +164,7 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) {
} }
/******************************************************************************* /*******************************************************************************
* Protected Helpers * Protected Methods
******************************************************************************/ ******************************************************************************/
void QtkWidget::keyPressEvent(QKeyEvent * event) { void QtkWidget::keyPressEvent(QKeyEvent * event) {
@ -204,7 +193,7 @@ void QtkWidget::mouseReleaseEvent(QMouseEvent * event) {
} }
/******************************************************************************* /*******************************************************************************
* Private Helpers * Private Methods
******************************************************************************/ ******************************************************************************/
void QtkWidget::initializeWidget() { void QtkWidget::initializeWidget() {
@ -265,31 +254,31 @@ void QtkWidget::updateCameraInput() {
static const float rotSpeed = 0.5f; static const float rotSpeed = 0.5f;
// Handle rotations // Handle rotations
Scene::Camera().transform().rotate( Scene::getCamera().getTransform().rotate(
-rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp); -rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp);
Scene::Camera().transform().rotate( Scene::getCamera().getTransform().rotate(
-rotSpeed * Input::mouseDelta().y(), Scene::Camera().right()); -rotSpeed * Input::mouseDelta().y(), Scene::getCamera().right());
// Handle translations // Handle translations
QVector3D translation; QVector3D translation;
if(Input::keyPressed(Qt::Key_W)) { if(Input::keyPressed(Qt::Key_W)) {
translation += Scene::Camera().forward(); translation += Scene::getCamera().forward();
} }
if(Input::keyPressed(Qt::Key_S)) { if(Input::keyPressed(Qt::Key_S)) {
translation -= Scene::Camera().forward(); translation -= Scene::getCamera().forward();
} }
if(Input::keyPressed(Qt::Key_A)) { if(Input::keyPressed(Qt::Key_A)) {
translation -= Scene::Camera().right(); translation -= Scene::getCamera().right();
} }
if(Input::keyPressed(Qt::Key_D)) { if(Input::keyPressed(Qt::Key_D)) {
translation += Scene::Camera().right(); translation += Scene::getCamera().right();
} }
if(Input::keyPressed(Qt::Key_Q)) { if(Input::keyPressed(Qt::Key_Q)) {
translation -= Scene::Camera().up() / 2.0f; translation -= Scene::getCamera().up() / 2.0f;
} }
if(Input::keyPressed(Qt::Key_E)) { if(Input::keyPressed(Qt::Key_E)) {
translation += Scene::Camera().up() / 2.0f; translation += Scene::getCamera().up() / 2.0f;
} }
Scene::Camera().transform().translate(transSpeed * translation); Scene::getCamera().getTransform().translate(transSpeed * translation);
} }
} }

View File

@ -15,60 +15,141 @@
#include <QOpenGLFunctions> #include <QOpenGLFunctions>
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <abstractscene.h>
#include <qtkapi.h> #include <qtkapi.h>
#include <scene.h>
namespace Qtk { namespace Qtk {
/**
* QtkWidget class to define required QOpenGLWidget functionality.
*
* This object has a Scene attached which manages the objects to render.
* Client input is passed through this widget to control the camera view.
*/
class QTKAPI QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions { class QTKAPI QtkWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT; Q_OBJECT;
public: public:
// Constructors /*************************************************************************
* Contructors / Destructors
************************************************************************/
/**
* Default ctor will configure a QSurfaceFormat with default settings.
*/
QtkWidget(); QtkWidget();
/**
* Qt Designer will call this ctor when creating this widget as a child.
*
* @param parent The parent QWidget
*/
explicit QtkWidget(QWidget * parent); explicit QtkWidget(QWidget * parent);
/**
* Allow constructing the widget with a preconfigured QSurfaceFormat.
*
* @param format QSurfaceFormat already configured by the caller.
*/
explicit QtkWidget(const QSurfaceFormat & format); explicit QtkWidget(const QSurfaceFormat & format);
~QtkWidget() override; ~QtkWidget() override;
private: private:
void teardownGL(); /*************************************************************************
* Private Methods
************************************************************************/
// clang-format off
void teardownGL() { /* Nothing to teardown yet... */ }
// clang-format on
public: public:
// Inherited virtual Members /*************************************************************************
void paintGL() override; * Public Inherited Virtual Methods
************************************************************************/
/**
* Called when the widget is first constructed.
*/
void initializeGL() override; void initializeGL() override;
/**
* Called when the application window is resized.
*
* @param width The new width of the window.
* @param height The new height of the window.
*/
void resizeGL(int width, int height) override; void resizeGL(int width, int height) override;
/**
* Called when OpenGL repaints the widget.
*/
void paintGL() override;
/*************************************************************************
* Accessors
************************************************************************/
inline Qtk::Scene * getScene() { return mScene; } inline Qtk::Scene * getScene() { return mScene; }
/*************************************************************************
* Setters
************************************************************************/
inline void setScene(Qtk::Scene * scene) { inline void setScene(Qtk::Scene * scene) {
delete mScene; delete mScene;
mScene = scene; mScene = scene;
} }
protected slots: protected slots:
/*************************************************************************
* Qt Slots
************************************************************************/
/**
* Called when the `frameSwapped` signal is caught.
* See definition of initializeGL()
*/
void update(); void update();
#ifdef QTK_DEBUG #ifdef QTK_DEBUG
/**
* Called when the `messageLogged` signal is caught.
* See definition of initializeGL()
*
* @param msg The message logged.
*/
static void messageLogged(const QOpenGLDebugMessage & msg); static void messageLogged(const QOpenGLDebugMessage & msg);
#endif #endif
// Protected Helpers
protected: protected:
/*************************************************************************
* Protected Methods
************************************************************************/
void keyPressEvent(QKeyEvent * event) override; void keyPressEvent(QKeyEvent * event) override;
void keyReleaseEvent(QKeyEvent * event) override; void keyReleaseEvent(QKeyEvent * event) override;
void mousePressEvent(QMouseEvent * event) override; void mousePressEvent(QMouseEvent * event) override;
void mouseReleaseEvent(QMouseEvent * event) override; void mouseReleaseEvent(QMouseEvent * event) override;
private: private:
// Private helpers /*************************************************************************
* Private Methods
************************************************************************/
void initializeWidget(); void initializeWidget();
static void updateCameraInput(); static void updateCameraInput();
Qtk::Scene * mScene;
#ifdef QTK_DEBUG #ifdef QTK_DEBUG
void printContextInformation(); void printContextInformation();
QOpenGLDebugLogger * mDebugLogger; QOpenGLDebugLogger * mDebugLogger;
#endif #endif
/*************************************************************************
* Private Members
************************************************************************/
Qtk::Scene * mScene;
}; };
} // namespace Qtk } // namespace Qtk

View File

@ -6,9 +6,9 @@
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/ ##############################################################################*/
#include <abstractscene.h>
#include <camera3d.h> #include <camera3d.h>
#include <resourcemanager.h> #include <resourcemanager.h>
#include <scene.h>
#include <texture.h> #include <texture.h>
using namespace Qtk; using namespace Qtk;
@ -21,8 +21,8 @@ QMatrix4x4 Scene::mProjection;
******************************************************************************/ ******************************************************************************/
Scene::Scene() { Scene::Scene() {
mCamera.transform().setTranslation(0.0f, 0.0f, 20.0f); mCamera.getTransform().setTranslation(0.0f, 0.0f, 20.0f);
mCamera.transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); mCamera.getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
} }
Scene::~Scene() { Scene::~Scene() {
@ -32,11 +32,10 @@ Scene::~Scene() {
for(auto & model : mModels) { for(auto & model : mModels) {
delete model; delete model;
} }
delete mSkybox; delete mSkybox;
} }
void Scene::privDraw() { void Scene::privateDraw() {
if(!mInit) { if(!mInit) {
initializeOpenGLFunctions(); initializeOpenGLFunctions();
init(); init();

125
src/scene.h Normal file
View File

@ -0,0 +1,125 @@
/*##############################################################################
## 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 {
/**
* An abstract Scene class to inherit from when building new scenes.
*
* This class provides the following objects to any inheriting scene:
* Skybox, Camera
* This class also provides containers for N instances of these objects:
* MeshRenderers, Models
*
* To inherit from this class and define our own scene we must:
*
* Override and define the `init()` virtual member function. If we want our
* scene to render using a Skybox, we should also initialize the mSkybox
* member within the overridden definition of `init()` using
* `Scene::setSkybox(...)`
*
* If the scene is to render any kind of movement we are required to override
* the `update()` virtual method.
*
* If the child scene adds any objects which are not managed (drawn) by this
* base class, the child scene class must also override the `draw()` method.
*/
class Scene : protected QOpenGLFunctions {
public:
/*************************************************************************
* Contructors / Destructors
************************************************************************/
Scene();
~Scene();
/*************************************************************************
* Public Methods
************************************************************************/
/**
* Initialize objects within the scene
*/
virtual void init() = 0;
/**
* Function called during OpenGL drawing event.
*
* This function is only called when the widget is redrawn.
*/
virtual void draw() { privateDraw(); };
/**
* Function called to update the QOpenGLWidget. Does not trigger a redraw.
*
* Calling this several times will still result in only one repaint.
*/
virtual void update() {}
/*************************************************************************
* Accessors
************************************************************************/
static Camera3D & getCamera() { return mCamera; }
static QMatrix4x4 getViewMatrix() { return mCamera.toMatrix(); }
static QMatrix4x4 & getProjectionMatrix() { return mProjection; }
inline Skybox * getSkybox() { return mSkybox; }
/*************************************************************************
* Setters
************************************************************************/
inline void setSkybox(Skybox * skybox) { mSkybox = skybox; }
private:
/*************************************************************************
* Private Members
************************************************************************/
static Camera3D mCamera;
static QMatrix4x4 mProjection;
bool mInit = false;
/*************************************************************************
* Private Methods
************************************************************************/
/**
* Handles drawing members encapsulated by this base class.
* Child classes do not need to draw these objects manually.
*/
void privateDraw();
protected:
/*************************************************************************
* Protected Members
************************************************************************/
/* The skybox for this scene. */
Skybox * mSkybox {};
/* MeshRenderers used simple geometry. */
std::vector<MeshRenderer *> mMeshes {};
/* Models used for storing 3D models in the scene. */
std::vector<Model *> mModels {};
};
} // namespace Qtk
#endif // QTK_SCENE_H

View File

@ -6,7 +6,7 @@
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/ ##############################################################################*/
#include <abstractscene.h> #include <scene.h>
#include <skybox.h> #include <skybox.h>
#include <texture.h> #include <texture.h>
@ -32,8 +32,8 @@ Skybox::Skybox(const std::string & name) :
":/right.png", ":/top.png", ":/front.png", ":/left.png", ":/bottom.png", ":/right.png", ":/top.png", ":/front.png", ":/left.png", ":/bottom.png",
":/back.png", name) {} ":/back.png", name) {}
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) : Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) {
mTexture(cubeMap) { mTexture.setTexture(cubeMap);
init(); init();
} }
@ -49,8 +49,8 @@ void Skybox::draw() {
mProgram.bind(); mProgram.bind();
mTexture.getOpenGLTexture().bind(); mTexture.getOpenGLTexture().bind();
mProgram.setUniformValue("uProjectionMatrix", Scene::Projection()); mProgram.setUniformValue("uProjectionMatrix", Scene::getProjectionMatrix());
mProgram.setUniformValue("uViewMatrix", Scene::Camera().toMatrix()); mProgram.setUniformValue("uViewMatrix", Scene::getCamera().toMatrix());
mProgram.setUniformValue("uTexture", 0); mProgram.setUniformValue("uTexture", 0);
glDrawElements( glDrawElements(
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data()); GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());

View File

@ -21,13 +21,23 @@
#include <texture.h> #include <texture.h>
namespace Qtk { namespace Qtk {
/**
* Skybox object for rendering a skybox within a Scene.
* A skybox is typically implemented using a cube map texture centered around
* the camera and projected outwards in all directions.
*/
class QTKAPI Skybox : protected QOpenGLFunctions { class QTKAPI Skybox : protected QOpenGLFunctions {
public: public:
/*************************************************************************
* Constructors / Destructors
************************************************************************/
// Delegate this constructor to use default skybox images // Delegate this constructor to use default skybox images
explicit Skybox(const std::string & name = "Skybox"); explicit Skybox(const std::string & name = "Skybox");
explicit Skybox( explicit Skybox(
QOpenGLTexture * cubeMap, const std::string & name = "Skybox"); QOpenGLTexture * cubeMap, const std::string & name = "Skybox");
// Constructor, Destructor
Skybox( Skybox(
const std::string & right, const std::string & top, const std::string & right, const std::string & top,
const std::string & front, const std::string & left, const std::string & front, const std::string & left,
@ -36,11 +46,23 @@ namespace Qtk {
~Skybox() = default; ~Skybox() = default;
/*************************************************************************
* Public Methods
************************************************************************/
void draw(); void draw();
private: private:
/*************************************************************************
* Private Methods
************************************************************************/
void init(); void init();
/*************************************************************************
* Private Members
************************************************************************/
Vertices mVertices {}; Vertices mVertices {};
Indices mIndices {}; Indices mIndices {};

View File

@ -28,7 +28,7 @@ QImage * OpenGLTextureFactory::initImage(
return loadedImage; return loadedImage;
} }
QOpenGLTexture * OpenGLTextureFactory::initTexture2D( QOpenGLTexture * OpenGLTextureFactory::initTexture(
const char * texture, bool flipX, bool flipY) { const char * texture, bool flipX, bool flipY) {
QImage * image = initImage(texture, flipX, flipY); QImage * image = initImage(texture, flipX, flipY);
auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);

View File

@ -9,30 +9,124 @@
#ifndef QTOPENGL_TEXTURE_H #ifndef QTOPENGL_TEXTURE_H
#define QTOPENGL_TEXTURE_H #define QTOPENGL_TEXTURE_H
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture> #include <QOpenGLTexture>
#include <utility> #include <utility>
#include <qtkapi.h> #include <qtkapi.h>
namespace Qtk { namespace Qtk {
/**
* Binds shader programs until the end of scope.
* Does nothing if the shader program was already bound.
*
* See MeshRenderer::setUniform() for example.
*/
class QTKAPI ShaderBindScope {
public:
/*************************************************************************
* Constructors / Destructors
************************************************************************/
explicit ShaderBindScope(
QOpenGLShaderProgram * program, bool was_locked) :
mWasBound(was_locked) {
mProgram = program;
if(!mWasBound) {
mProgram->bind();
}
}
~ShaderBindScope() {
if(!mWasBound) {
mProgram->release();
}
}
private:
/*************************************************************************
* Private Members
************************************************************************/
QOpenGLShaderProgram * mProgram;
bool mWasBound;
};
/**
* Factories for initializing various OpenGL textures
*/
class QTKAPI OpenGLTextureFactory { class QTKAPI OpenGLTextureFactory {
public: public:
/*************************************************************************
* Constructors / Destructors
************************************************************************/
~OpenGLTextureFactory() = default; ~OpenGLTextureFactory() = default;
// QImage /*************************************************************************
* Texture Factories
************************************************************************/
/**
* QImage factory
*
* @param image Path to image we want to load.
* Can be absolute or Qt resource path.
* @param flipX If true the image will be flipped on X axis.
* @param flipY If true the image will be flipped on Y axis.
* @return Pointer to an initialized QImage object.
*/
static QImage * initImage( static QImage * initImage(
const char * image, bool flipX = false, bool flipY = false); const char * image, bool flipX = false, bool flipY = false);
// 2D Texture /**
static QOpenGLTexture * initTexture2D( * QOpenGLTexture factory
*
* @param texture Path to texture we want to load.
* Can be absolute or Qt resource path.
* @param flipX If true the image will be flipped on X axis.
* @param flipY If true the image will be flipped on Y axis.
* @return Pointer to an initialized QOpenGLTexture object.
*/
static QOpenGLTexture * initTexture(
const char * texture, bool flipX = false, bool flipY = false); const char * texture, bool flipX = false, bool flipY = false);
// Cube maps /**
* Cube map factory for initializing all sides of a CubeMap.
* All of these parameters can be absolute or Qt resource paths.
*
* @param right Path to image for the right side of the CubeMap.
* @param top Path to image for the top side of the CubeMap.
* @param front Path to image for the front side of the CubeMap.
* @param left Path to image for the left side of the CubeMap.
* @param bottom Path to image for the bottom side of the CubeMap.
* @param back Path to image for the back side of the CubeMap.
* @return Pointer to an initialized QOpenGLTexture object.
*/
static QOpenGLTexture * initCubeMap( static QOpenGLTexture * initCubeMap(
const QImage & right, const QImage & top, const QImage & front, const QImage & right, const QImage & top, const QImage & front,
const QImage & left, const QImage & bottom, const QImage & back); const QImage & left, const QImage & bottom, const QImage & back);
// Overloads for cube map initialization
/**
* CubeMap factory for tiling the same image on all sides.
*
* @param tile Path to the image we want to make into a CubeMap.
* Can be absolute or Qt resource path.
* @return Pointer to an initialized QOpenGLTexture object.
*/
static QOpenGLTexture * initCubeMap(const char * tile); static QOpenGLTexture * initCubeMap(const char * tile);
/**
* Cube map factory for initializing all sides of a CubeMap.
* All of these parameters can be absolute or Qt resource paths.
*
* @param right Path to image for the right side of the CubeMap.
* @param top Path to image for the top side of the CubeMap.
* @param front Path to image for the front side of the CubeMap.
* @param left Path to image for the left side of the CubeMap.
* @param bottom Path to image for the bottom side of the CubeMap.
* @param back Path to image for the back side of the CubeMap.
* @return Pointer to an initialized QOpenGLTexture object.
*/
static QOpenGLTexture * initCubeMap( static QOpenGLTexture * initCubeMap(
const char * right, const char * top, const char * front, const char * right, const char * top, const char * front,
const char * left, const char * bottom, const char * back); const char * left, const char * bottom, const char * back);
@ -42,48 +136,66 @@ namespace Qtk {
OpenGLTextureFactory() = default; OpenGLTextureFactory() = default;
}; };
// TODO: Struct for (re)storing texture state /**
* Texture object component class
*
* TODO: Struct for (re)storing texture state
* A struct to store flipX, flipY and other initial state needed to copy
*/
class Texture { class Texture {
public: public:
/*************************************************************************
* Typedefs
************************************************************************/
friend class Skybox;
/*************************************************************************
* Constructors / Destructors
************************************************************************/
Texture() = default; Texture() = default;
Texture(const Texture & value) { Texture(const Texture & value) {
mOpenGLTexture = OpenGLTextureFactory::initTexture2D(value.mPath); mOpenGLTexture = OpenGLTextureFactory::initTexture(value.mPath);
mPath = value.mPath; mPath = value.mPath;
} }
explicit Texture( explicit Texture(
const char * path, bool flipX = false, bool flipY = false) : const char * path, bool flipX = false, bool flipY = false) :
mOpenGLTexture( mOpenGLTexture(OpenGLTextureFactory::initTexture(path, flipX, flipY)),
OpenGLTextureFactory::initTexture2D(path, flipX, flipY)),
mPath(path) {} mPath(path) {}
explicit Texture(QOpenGLTexture * texture) : mOpenGLTexture(texture) {} explicit Texture(QOpenGLTexture * texture) : mOpenGLTexture(texture) {}
~Texture() { mOpenGLTexture->destroy(); } ~Texture() { mOpenGLTexture->destroy(); }
/*************************************************************************
* Accessors
************************************************************************/
[[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const { [[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const {
return *mOpenGLTexture; return *mOpenGLTexture;
} }
[[nodiscard]] inline std::string getPath() const { return mPath; } [[nodiscard]] inline std::string getPath() const { return mPath; }
/*************************************************************************
* Setters
************************************************************************/
void setTexture( void setTexture(
const std::string & path, bool flipX = false, bool flipY = false) { const std::string & path, bool flipX = false, bool flipY = false) {
mOpenGLTexture = mOpenGLTexture =
OpenGLTextureFactory::initTexture2D(path.data(), flipX, flipY); OpenGLTextureFactory::initTexture(path.data(), flipX, flipY);
mPath = path.data(); mPath = path.data();
} }
void setTexture( void setTexture(
const char * path, bool flipX = false, bool flipY = false) { const char * path, bool flipX = false, bool flipY = false) {
mOpenGLTexture = mOpenGLTexture = OpenGLTextureFactory::initTexture(path, flipX, flipY);
OpenGLTextureFactory::initTexture2D(path, flipX, flipY);
mPath = path; mPath = path;
} }
// TODO: This is unsafe because we don't have a path. Encapsulate it.
inline void setTexture(QOpenGLTexture * texture) {
mOpenGLTexture = texture;
}
virtual inline void setCubeMap(const char * path) { virtual inline void setCubeMap(const char * path) {
mOpenGLTexture = OpenGLTextureFactory::initCubeMap(path); mOpenGLTexture = OpenGLTextureFactory::initCubeMap(path);
mPath = path; mPath = path;
@ -103,12 +215,24 @@ namespace Qtk {
right, top, front, left, bottom, back); right, top, front, left, bottom, back);
} }
/*************************************************************************
* Public Methods
************************************************************************/
[[nodiscard]] inline bool hasTexture() const { [[nodiscard]] inline bool hasTexture() const {
return mOpenGLTexture != Q_NULLPTR; return mOpenGLTexture != Q_NULLPTR;
} }
private: private:
/*************************************************************************
* Private Members
************************************************************************/
inline void setTexture(QOpenGLTexture * texture) {
mOpenGLTexture = texture;
}
QOpenGLTexture * mOpenGLTexture = Q_NULLPTR; QOpenGLTexture * mOpenGLTexture = Q_NULLPTR;
/* Path to this texture on disk or Qt resource. */
const char * mPath {}; const char * mPath {};
}; };

View File

@ -23,15 +23,22 @@
#include <qtkapi.h> #include <qtkapi.h>
namespace Qtk { namespace Qtk {
/**
* Transform3D class to represent and modify object position in 3D space.
*/
class QTKAPI Transform3D { class QTKAPI Transform3D {
public: public:
// Constructors /*************************************************************************
* Constructors, Destructors
************************************************************************/
inline Transform3D() : inline Transform3D() :
m_dirty(true), mScale(1.0f, 1.0f, 1.0f), m_dirty(true), mScale(1.0f, 1.0f, 1.0f),
mTranslation(0.0f, 0.0f, 0.0f) {} mTranslation(0.0f, 0.0f, 0.0f) {}
// /*************************************************************************
// Transformations * Transformations
************************************************************************/
void translate(const QVector3D & dt); void translate(const QVector3D & dt);
@ -72,8 +79,9 @@ namespace Qtk {
grow(QVector3D(factor, factor, factor)); grow(QVector3D(factor, factor, factor));
} }
// /*************************************************************************
// Setters * Setters
************************************************************************/
// Set object position // Set object position
void setTranslation(const QVector3D & t); void setTranslation(const QVector3D & t);
@ -102,8 +110,9 @@ namespace Qtk {
setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle)); setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));
} }
// /*************************************************************************
// Accessors * Getters
************************************************************************/
[[nodiscard]] inline const QVector3D & getTranslation() const { [[nodiscard]] inline const QVector3D & getTranslation() const {
return mTranslation; return mTranslation;
@ -121,9 +130,17 @@ namespace Qtk {
[[nodiscard]] QVector3D getUp() const; [[nodiscard]] QVector3D getUp() const;
[[nodiscard]] QVector3D getRight() const; [[nodiscard]] QVector3D getRight() const;
/*************************************************************************
* Public members
************************************************************************/
static const QVector3D LocalForward, LocalUp, LocalRight; static const QVector3D LocalForward, LocalUp, LocalRight;
private: private:
/*************************************************************************
* Private members
************************************************************************/
QVector3D mTranslation; QVector3D mTranslation;
QQuaternion mRotation; QQuaternion mRotation;
QVector3D mScale; QVector3D mScale;