diff --git a/.clang-format b/.clang-format index aad4bd6..14b0695 100644 --- a/.clang-format +++ b/.clang-format @@ -1,8 +1,5 @@ - --- -# clang-format off BasedOnStyle: Google -# clang-format on AlignAfterOpenBracket: AlwaysBreak AlignArrayOfStructures: Left AlignConsecutiveAssignments: None diff --git a/CMakeLists.txt b/CMakeLists.txt index bbfebb5..af0f0dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,8 +30,8 @@ option(QTK_DEBUG "Enable debugger" ON) message(STATUS "[Qtk] Compiling with QTK_DEBUG=${QTK_DEBUG}") option(QTK_UPDATE_SUBMODULES "Update external project (assimp) submodule" ON) message( - STATUS - "[Qtk] Compiling with QTK_UPDATE_SUBMODULES=${QTK_UPDATE_SUBMODULES}" + STATUS + "[Qtk] Compiling with QTK_UPDATE_SUBMODULES=${QTK_UPDATE_SUBMODULES}" ) # Qt options @@ -55,7 +55,7 @@ list(APPEND CMAKE_PREFIX_PATH "${QT_DIR}") # Find Qt find_package(Qt6 COMPONENTS OpenGLWidgets) -if (NOT Qt6_FOUND) +if(NOT Qt6_FOUND) message( SEND_ERROR "[Qtk] Error: Unable to find Qt6 at CMAKE_PREFIX_PATH: " "${CMAKE_PREFIX_PATH}" @@ -68,7 +68,7 @@ if (NOT Qt6_FOUND) ) endif() -if (QTK_UPDATE_SUBMODULES) +if(QTK_UPDATE_SUBMODULES) message(STATUS "[Qtk] Updating submodules...") include("${CMAKE_SOURCE_DIR}/cmake/include/git_submodule.cmake") submodule_update("${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/") @@ -83,34 +83,34 @@ endif() ################################################################################ set( - PUBLIC_HEADERS - src/qtkwidget.h - src/abstractscene.h - src/camera3d.h - src/mesh.h - src/meshrenderer.h - src/model.h - src/object.h - src/skybox.h - src/texture.h - src/transform3D.h + PUBLIC_HEADERS + src/qtkwidget.h + src/scene.h + src/camera3d.h + src/mesh.h + src/meshrenderer.h + src/model.h + src/object.h + src/skybox.h + src/texture.h + src/transform3D.h ) set( - SOURCE_FILES - src/qtkwidget.cpp - src/abstractscene.cpp - src/camera3d.cpp - src/input.cpp - src/input.h - src/mesh.cpp - src/meshrenderer.cpp - src/model.cpp - src/object.cpp - src/qtkapi.h - src/skybox.cpp - src/texture.cpp - src/transform3D.cpp + SOURCE_FILES + src/qtkwidget.cpp + src/scene.cpp + src/camera3d.cpp + src/input.cpp + src/input.h + src/mesh.cpp + src/meshrenderer.cpp + src/model.cpp + src/object.cpp + src/qtkapi.h + src/skybox.cpp + src/texture.cpp + src/transform3D.cpp ) include(GenerateExportHeader) @@ -118,7 +118,8 @@ include(GenerateExportHeader) add_library(qtk-widget STATIC ${PUBLIC_HEADERS} ${SOURCE_FILES}) target_include_directories(qtk-widget PRIVATE src/ app/) -set_target_properties(qtk-widget PROPERTIES +set_target_properties( + qtk-widget PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}" 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::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) elseif(ASSIMP_NEW_INTERFACE) target_link_libraries(qtk-widget PUBLIC assimp::assimp) @@ -144,7 +145,8 @@ if(WIN32) endif() # Install files -install(TARGETS qtk-widget +install( + TARGETS qtk-widget # Associate qtk-widget target with qtk-export EXPORT qtk-export # /bin on DLL systems and /lib on non-dll systems @@ -157,7 +159,8 @@ install(TARGETS qtk-widget # Install export # qtkTargets.cmake will only be installed when one of the CONFIGURATIONS is installed # + The generated import will only reference that qtk configuration -install(EXPORT qtk-export +install( + EXPORT qtk-export FILE qtkTargets.cmake CONFIGURATIONS Debug|Release DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake @@ -175,9 +178,14 @@ configure_file( ) # Add our Qt resources.qrc file to our application -set(QTK_APP_SOURCES app/main.cpp - app/examplescene.cpp app/examplescene.h - app/mainwindow.cpp app/mainwindow.h app/mainwindow.ui +set( + QTK_APP_SOURCES + app/main.cpp + app/examplescene.cpp + app/examplescene.h + app/mainwindow.cpp + app/mainwindow.h + app/mainwindow.ui app/resourcemanager.h src/qtkresources.h.in ) @@ -189,13 +197,15 @@ target_include_directories(qtk-main PRIVATE src/ app/) # Link qtk-main executable to main qtk-widget library target_link_libraries(qtk-main PUBLIC qtk-widget) -set_target_properties(qtk-main PROPERTIES +set_target_properties( + qtk-main PROPERTIES WIN32_EXECUTABLE TRUE MACOSX_BUNDLE TRUE MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} ) -install(TARGETS qtk-main +install( + TARGETS qtk-main BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) @@ -206,17 +216,18 @@ 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) file(TO_NATIVE_PATH "${qt6_install_prefix}/bin" qt6_install_prefix) if(TARGET Qt6::windeployqt) - add_custom_command(TARGET qtk-main + add_custom_command( + TARGET qtk-main POST_BUILD COMMAND set PATH=%PATH%$${qt6_install_prefix} COMMAND Qt6::windeployqt --dir "${CMAKE_BINARY_DIR}/windeployqt" "$/$" - ) + ) install(DIRECTORY "${CMAKE_BINARY_DIR}/windeployqt/" DESTINATION bin) endif() if(MSVC AND TARGET Qt6::qmake) set(VSUSER_FILE ${CMAKE_CURRENT_BINARY_DIR}/qtk-main.vcxproj.user) file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/extern/assimp/assimp/bin" assimp_bin) - file(WRITE ${VSUSER_FILE} "\n") + file(WRITE ${VSUSER_FILE} "\n") file(APPEND ${VSUSER_FILE} "\n") file(APPEND ${VSUSER_FILE} " \n") file(APPEND ${VSUSER_FILE} " Path=$(SolutionDir)\\lib\\$(Configuration);${qt6_install_prefix};${assimp_bin};$(Path)\n") diff --git a/README.md b/README.md index 4d200ca..25a984b 100644 --- a/README.md +++ b/README.md @@ -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) [![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. 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/) 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 @@ -132,7 +166,6 @@ Spartan with normals - ### QtkWidget in Qt Creator -The `QtkWidget` class is exported as a shared library for use in Qt Creator's design mode. We can add more QtkWidgets to view and render the scene from multiple perspectives. There is still some work to be done here, so there isn't a builtin way to add an additional view within the application. diff --git a/app/examplescene.cpp b/app/examplescene.cpp index 9aa433f..2c5d8f3 100644 --- a/app/examplescene.cpp +++ b/app/examplescene.cpp @@ -6,12 +6,12 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include #include #include #include #include #include +#include #include using namespace Qtk; @@ -21,8 +21,8 @@ using namespace Qtk; ******************************************************************************/ ExampleScene::ExampleScene() { - Camera().transform().setTranslation(0.0f, 0.0f, 20.0f); - Camera().transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); + getCamera().getTransform().setTranslation(0.0f, 0.0f, 20.0f); + getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); } ExampleScene::~ExampleScene() { @@ -44,50 +44,152 @@ ExampleScene::~ExampleScene() { ******************************************************************************/ void ExampleScene::init() { - auto * sb = new Qtk::Skybox("Skybox"); - setSkybox(sb); + // Add a skybox to the scene using default cube map images and settings. + 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->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"); - // You no longer need to manually bind shader program. - // + But, you can still bind it if you want to. + // For example this would technically not be efficient, because each one of + // these calls will bind, set, release. We could instead bind, set N uniforms, + // and release when we are finished. // mTestPhong->bindShaders(); mTestPhong->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestPhong->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestPhong->setUniform("uAmbientStrength", 0.2f); mTestPhong->setUniform("uSpecularStrength", 0.50f); mTestPhong->setUniform("uSpecularShine", 256); - mTestPhong->reallocateNormals(mTestPhong->getNormals()); // 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->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->init(); + // Changing these uniform values will alter lighting effects. mTestAmbient->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestAmbient->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestAmbient->setUniform("uAmbientStrength", 0.2f); mTestAmbient->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->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->init(); mTestDiffuse->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestDiffuse->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestDiffuse->setUniform("uAmbientStrength", 0.2f); mTestDiffuse->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->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->init(); mTestSpecular->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestSpecular->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestSpecular->setUniform("uAmbientStrength", 0.2f); @@ -95,105 +197,18 @@ void ExampleScene::init() { mTestSpecular->setUniform("uSpecularShine", 256); mTestSpecular->reallocateNormals(mTestSpecular->getNormals()); - - // - // 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 + // Specular lighting example light source. This is just for visual reference. mMeshes.push_back( - new Qtk::MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->mTransform.setTranslation(1.0f, 1.5f, 10.0f); - mMeshes.back()->mTransform.scale(0.25f); - // This function changes values we have allocated in a buffer, so init() after - mMeshes.back()->setColor(GREEN); + new Qtk::MeshRenderer("specularLight", Triangle(QTK_DRAW_ELEMENTS))); + mMeshes.back()->getTransform().setTranslation(11.0f, 2.0f, -2.0f); + mMeshes.back()->getTransform().scale(0.25f); - 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.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"); + // WARNING: Set color before reallocating normals. mMeshes.back()->setColor(QVector3D(0.0f, 0.25f, 0.0f)); 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("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( - new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->mTransform.setTranslation(-5.0f, 0.0f, -2.0f); + /* Test alien Model with phong lighting and specular mapping. */ + mModels.push_back(new Qtk::Model( + "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( - new Qtk::MeshRenderer("centerCube", Cube(QTK_DRAW_ELEMENTS))); - mMeshes.back()->mTransform.setTranslation(-7.0f, 0.0f, -2.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)); - mMeshes.push_back( - new Qtk::MeshRenderer("leftTriangle", Triangle(QTK_DRAW_ELEMENTS))); - mMeshes.back()->mTransform.setTranslation(-9.0f, 0.0f, -2.0f); - mMeshes.back()->setDrawType(GL_LINE_LOOP); - - mMeshes.push_back( - new Qtk::MeshRenderer("topTriangle", Triangle(QTK_DRAW_ELEMENTS))); - 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); + // Light source for alienTest object. + mMeshes.push_back(new Qtk::MeshRenderer( + "alienTestLight", Triangle(Qtk::QTK_DRAW_ELEMENTS))); + mMeshes.back()->getTransform().setTranslation(4.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); + /* 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 mMeshes.push_back( 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()->reallocateNormals(mMeshes.back()->getNormals()); // RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS mMeshes.push_back(new Qtk::MeshRenderer( "rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); - mMeshes.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()->reallocateNormals(mMeshes.back()->getNormals()); @@ -260,7 +309,7 @@ void ExampleScene::init() { crateTexture.setTexture(":/crate.png"); Cube cube; 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->setTexture(crateTexture); m->setUniform("uTexture", 0); @@ -274,7 +323,7 @@ void ExampleScene::init() { // + This is because the same position must use different UV coordinates mMeshes.push_back( new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); - mMeshes.back()->mTransform.setTranslation(-3.0f, 0.0f, -2.0f); + mMeshes.back()->getTransform().setTranslation(-3.0f, 0.0f, -2.0f); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->setTexture(crateTexture); mMeshes.back()->setUniform("uTexture", 0); @@ -283,7 +332,7 @@ void ExampleScene::init() { // Test drawing a cube with texture coordinates using glDrawElements mMeshes.push_back(new Qtk::MeshRenderer( "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()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->bindShaders(); @@ -291,14 +340,14 @@ void ExampleScene::init() { mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords(), 3); mMeshes.back()->releaseShaders(); - mMeshes.back()->mTransform.rotate(45.0f, 0.0f, 1.0f, 0.0f); + mMeshes.back()->getTransform().rotate(45.0f, 0.0f, 1.0f, 0.0f); // Texturing a cube using a cube map // + Cube map texturing works with both QTK_DRAW_ARRAYS and QTK_DRAW_ELEMENTS mMeshes.push_back( new Qtk::MeshRenderer("testCubeMap", Cube(QTK_DRAW_ELEMENTS))); - mMeshes.back()->mTransform.setTranslation(-3.0f, 1.0f, -2.0f); - mMeshes.back()->mTransform.setRotation(45.0f, 0.0f, 1.0f, 0.0f); + mMeshes.back()->getTransform().setTranslation(-3.0f, 1.0f, -2.0f); + mMeshes.back()->getTransform().setRotation(45.0f, 0.0f, 1.0f, 0.0f); mMeshes.back()->setShaders( ":/texture-cubemap.vert", ":/texture-cubemap.frag"); mMeshes.back()->setCubeMap(":/crate.png"); @@ -309,28 +358,28 @@ void ExampleScene::init() { // + Apply RGB normals shader and spin the cube for a neat effect mMeshes.push_back( 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()->reallocateNormals(mMeshes.back()->getNormals()); // RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS mMeshes.push_back(new Qtk::MeshRenderer( "rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS))); - mMeshes.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()->reallocateNormals(mMeshes.back()->getNormals()); // RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS mMeshes.push_back(new Qtk::MeshRenderer( "rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS))); - mMeshes.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()->reallocateNormals(mMeshes.back()->getNormals()); // Test drawing triangle with glDrawArrays with texture coordinates mMeshes.push_back( new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS))); - mMeshes.back()->mTransform.setTranslation(-3.0f, 2.0f, -2.0f); + mMeshes.back()->getTransform().setTranslation(-3.0f, 2.0f, -2.0f); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); mMeshes.back()->setTexture(":/crate.png"); @@ -340,40 +389,16 @@ void ExampleScene::init() { // Test drawing triangle with glDrawElements with texture coordinates mMeshes.push_back(new Qtk::MeshRenderer( "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()->setTexture(":/crate.png"); mMeshes.back()->setUniform("uTexture", 0); 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() { + // WARNING: We must call the base class draw() function first. + // + This will handle rendering core scene components like the Skybox. Scene::draw(); for(const auto & model : mModels) { @@ -387,129 +412,156 @@ void ExampleScene::draw() { mTestPhong->bindShaders(); mTestPhong->setUniform( "uModelInverseTransposed", - mTestPhong->mTransform.toMatrix().normalMatrix()); + mTestPhong->getTransform().toMatrix().normalMatrix()); mTestPhong->setUniform( "uLightPosition", - MeshRenderer::getInstance("phongLight")->mTransform.getTranslation()); + MeshRenderer::getInstance("phongLight")->getTransform().getTranslation()); mTestPhong->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + "uCameraPosition", + ExampleScene::getCamera().getTransform().getTranslation()); mTestPhong->releaseShaders(); mTestPhong->draw(); mTestAmbient->bindShaders(); mTestAmbient->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + "uCameraPosition", + ExampleScene::getCamera().getTransform().getTranslation()); mTestAmbient->releaseShaders(); mTestAmbient->draw(); mTestDiffuse->bindShaders(); mTestDiffuse->setUniform( "uModelInverseTransposed", - mTestDiffuse->mTransform.toMatrix().normalMatrix()); + mTestDiffuse->getTransform().toMatrix().normalMatrix()); mTestDiffuse->setUniform( - "uLightPosition", - MeshRenderer::getInstance("diffuseLight")->mTransform.getTranslation()); + "uLightPosition", MeshRenderer::getInstance("diffuseLight") + ->getTransform() + .getTranslation()); mTestDiffuse->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + "uCameraPosition", + ExampleScene::getCamera().getTransform().getTranslation()); mTestDiffuse->releaseShaders(); mTestDiffuse->draw(); mTestSpecular->bindShaders(); mTestSpecular->setUniform( "uModelInverseTransposed", - mTestSpecular->mTransform.toMatrix().normalMatrix()); + mTestSpecular->getTransform().toMatrix().normalMatrix()); mTestSpecular->setUniform( - "uLightPosition", - MeshRenderer::getInstance("specularLight")->mTransform.getTranslation()); + "uLightPosition", MeshRenderer::getInstance("specularLight") + ->getTransform() + .getTranslation()); mTestSpecular->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + "uCameraPosition", + ExampleScene::getCamera().getTransform().getTranslation()); mTestSpecular->releaseShaders(); mTestSpecular->draw(); } void ExampleScene::update() { - auto position = - MeshRenderer::getInstance("alienTestLight")->mTransform.getTranslation(); + 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); + + auto position = MeshRenderer::getInstance("alienTestLight") + ->getTransform() + .getTranslation(); auto alien = Model::getInstance("alienTest"); alien->setUniform("uLight.position", position); alien->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); - auto posMatrix = alien->mTransform.toMatrix(); + "uCameraPosition", + ExampleScene::getCamera().getTransform().getTranslation()); + auto posMatrix = alien->getTransform().toMatrix(); alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); alien->setUniform("uMVP.model", posMatrix); - alien->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); - alien->setUniform("uMVP.projection", ExampleScene::Projection()); - alien->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); + alien->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix()); + alien->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix()); + alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f); position = MeshRenderer::getInstance("spartanTestLight") - ->mTransform.getTranslation(); + ->getTransform() + .getTranslation(); auto spartan = Model::getInstance("spartanTest"); spartan->setUniform("uLight.position", position); spartan->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); - posMatrix = spartan->mTransform.toMatrix(); + "uCameraPosition", + ExampleScene::getCamera().getTransform().getTranslation()); + posMatrix = spartan->getTransform().toMatrix(); spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); spartan->setUniform("uMVP.model", posMatrix); - spartan->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); - spartan->setUniform("uMVP.projection", ExampleScene::Projection()); - spartan->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); + spartan->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix()); + spartan->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix()); + spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f); auto phong = MeshRenderer::getInstance("testPhong"); - phong->mTransform.rotate(0.75f, 1.0f, 0.5f, 0.0f); + phong->getTransform().rotate(0.75f, 1.0f, 0.5f, 0.0f); phong->bindShaders(); position = - MeshRenderer::getInstance("testLight")->mTransform.getTranslation(); + MeshRenderer::getInstance("testLight")->getTransform().getTranslation(); phong->setUniform("uLight.position", position); phong->setUniform( - "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); - posMatrix = phong->mTransform.toMatrix(); + "uCameraPosition", + ExampleScene::getCamera().getTransform().getTranslation()); + posMatrix = phong->getTransform().toMatrix(); phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); phong->setUniform("uMVP.model", posMatrix); - phong->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); - phong->setUniform("uMVP.projection", ExampleScene::Projection()); + phong->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix()); + phong->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix()); phong->releaseShaders(); // Rotate lighting example cubes - mTestPhong->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); - MeshRenderer::getInstance("noLight")->mTransform.rotate( + mTestPhong->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f); + MeshRenderer::getInstance("noLight")->getTransform().rotate( 0.75f, 0.5f, 0.3f, 0.2f); - mTestAmbient->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); - mTestDiffuse->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); - mTestSpecular->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); + mTestAmbient->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f); + mTestDiffuse->getTransform().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 // Rotate in multiple directions simultaneously 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 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") - ->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 static float translateX = 0.025f; float limit = -9.0f; // Origin position.x - 2.0f - float posX = - MeshRenderer::getInstance("topTriangle")->mTransform.getTranslation().x(); + float posX = MeshRenderer::getInstance("topTriangle") + ->getTransform() + .getTranslation() + .x(); if(posX < limit || posX > limit + 4.0f) { translateX = -translateX; } MeshRenderer::getInstance("topTriangle") - ->mTransform.translate(translateX, 0.0f, 0.0f); + ->getTransform() + .translate(translateX, 0.0f, 0.0f); 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 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") - ->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 // Rotate center cube in several directions simultaneously // + Not subject to gimbal lock since we are using quaternions :) MeshRenderer::getInstance("centerCube") - ->mTransform.rotate(0.75f, 0.2f, 0.4f, 0.6f); + ->getTransform() + .rotate(0.75f, 0.2f, 0.4f, 0.6f); } diff --git a/app/examplescene.h b/app/examplescene.h index 117cbcf..9ea0cb2 100644 --- a/app/examplescene.h +++ b/app/examplescene.h @@ -9,22 +9,65 @@ #ifndef QTK_EXAMPLE_SCENE_H #define QTK_EXAMPLE_SCENE_H -#include #include +#include #include #include +/** + * 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 { public: + /*************************************************************************** + * Contructors / Destructors + **************************************************************************/ + ExampleScene(); ~ExampleScene(); + /*************************************************************************** + * Inherited Public Overrides + **************************************************************************/ + + /** + * Initialize objects within the scene + */ void init() override; + /** + * Called when OpenGL repaints the widget. + */ void draw() override; + + /** + * Called when the Qt `frameSwapped` signal is caught. + * See definition of `QtkWidget::initializeGL()` + */ void update() override; 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 * mTestSpecular {}; Qtk::MeshRenderer * mTestDiffuse {}; diff --git a/app/main.cpp b/app/main.cpp index 1adf4d2..3fe4a92 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -16,20 +16,6 @@ int main(int argc, char * 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 MainWindow w; w.show(); diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index 2369f4d..a7a912e 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -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 #include #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent), ui(new Ui::MainWindow) { - ui->setupUi(this); // For use in design mode using Qt Creator // + We can use the `ui` member to access nested widgets by name + ui->setupUi(this); + + // Find all QtkWidgets in this QMainWindow and initialize their scenes. for(const auto widget : ui->qWidget->children()) { auto qtkWidget = dynamic_cast(widget); if(qtkWidget != nullptr) { std::string key = qtkWidget->objectName().toStdString(); + // Initialize each scene into a map if it doesn't exist. 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")); } diff --git a/app/mainwindow.h b/app/mainwindow.h index c389295..dc83675 100644 --- a/app/mainwindow.h +++ b/app/mainwindow.h @@ -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 #define MAINWINDOW_H @@ -12,16 +20,32 @@ namespace Ui { class MainWindow; } -class QTK_WIDGET_EXPORT MainWindow - - : public QMainWindow { +/** + * MainWindow class to provide an example of using a QtkWidget within a Qt + * window application. + */ +class QTK_WIDGET_EXPORT MainWindow : public QMainWindow { Q_OBJECT 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); ~MainWindow() override; private: + /*************************************************************************** + * Private Members + **************************************************************************/ + Ui::MainWindow * ui {}; std::unordered_map mScenes {}; }; diff --git a/app/resourcemanager.h b/app/resourcemanager.h index 47dcf82..2df646e 100644 --- a/app/resourcemanager.h +++ b/app/resourcemanager.h @@ -13,6 +13,19 @@ #ifndef 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 { public: /** @@ -24,7 +37,7 @@ typedef class ResourceManager { * ':/models/backpack/backpack.obj' An asset at location * qtk/resources/path/to/asset.obj Should be given in qrc format: * ':/path/to/asset.obj' - * @return Absoulte system path to a qtk asset + * @return Absolute system path to a qtk asset */ static std::string getPath(const std::string & path) { // Only construct qtk resource path if in qrc format; else return it as-is diff --git a/src/abstractscene.h b/src/abstractscene.h deleted file mode 100644 index 8e27178..0000000 --- a/src/abstractscene.h +++ /dev/null @@ -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 -#include -#include -#include - -#include - -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 mMeshes {}; - std::vector mModels {}; - }; -} // namespace Qtk - -#endif // QTK_SCENE_H diff --git a/src/camera3d.cpp b/src/camera3d.cpp index a40d8ad..68ce14c 100644 --- a/src/camera3d.cpp +++ b/src/camera3d.cpp @@ -32,22 +32,22 @@ const QMatrix4x4 & Camera3D::toMatrix() { ******************************************************************************/ QDataStream & operator<<(QDataStream & out, Camera3D & transform) { - out << transform.transform(); + out << transform.getTransform(); return out; } QDataStream & operator>>(QDataStream & in, Camera3D & transform) { - in >> transform.transform(); + in >> transform.getTransform(); return in; } QDebug operator<<(QDebug dbg, const Camera3D & transform) { dbg << "Camera3D\n{\n"; - dbg << "Position: <" << transform.translation().x() << ", " - << transform.translation().y() << ", " << transform.translation().z() - << ">\n"; - dbg << "Rotation: <" << transform.rotation().x() << ", " - << transform.rotation().y() << ", " << transform.rotation().z() << " | " - << transform.rotation().scalar() << ">\n}"; + dbg << "Position: <" << transform.getTranslation().x() << ", " + << transform.getTranslation().y() << ", " + << transform.getTranslation().z() << ">\n"; + dbg << "Rotation: <" << transform.getRotation().x() << ", " + << transform.getRotation().y() << ", " << transform.getRotation().z() + << " | " << transform.getRotation().scalar() << ">\n}"; return dbg; } diff --git a/src/camera3d.h b/src/camera3d.h index 01e14ab..89b714d 100644 --- a/src/camera3d.h +++ b/src/camera3d.h @@ -17,19 +17,25 @@ namespace Qtk { class QTKAPI Camera3D { public: - // Constants + /************************************************************************* + * Constants + ************************************************************************/ + static const QVector3D LocalForward; static const QVector3D LocalUp; 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(); } - [[nodiscard]] inline const QQuaternion & rotation() const { + [[nodiscard]] inline const QQuaternion & getRotation() const { return mTransform.getRotation(); } @@ -49,9 +55,17 @@ namespace Qtk { } private: + /************************************************************************* + * Private Members + ************************************************************************/ + Transform3D mTransform; QMatrix4x4 mWorld; + /************************************************************************* + * Private Methods + ************************************************************************/ + #ifndef QT_NO_DATASTREAM friend QDataStream & operator<<(QDataStream & out, Camera3D & transform); friend QDataStream & operator>>(QDataStream & in, Camera3D & transform); diff --git a/src/input.h b/src/input.h index 5863105..b9cdfec 100644 --- a/src/input.h +++ b/src/input.h @@ -17,10 +17,15 @@ namespace Qtk { class QTKAPI Input { + public: + /************************************************************************* + * Typedefs + ************************************************************************/ friend class Qtk::QtkWidget; - public: - // Possible key states + /** + * Possible key states + */ enum InputState { InputInvalid, InputRegistered, @@ -30,6 +35,10 @@ namespace Qtk { InputReleased }; + /************************************************************************* + * Public Methods + ************************************************************************/ + // State checking inline static bool keyTriggered(Qt::Key key) { return keyState(key) == InputTriggered; @@ -63,6 +72,10 @@ namespace Qtk { static QPoint mouseDelta(); private: + /************************************************************************* + * Private Methods + ************************************************************************/ + // State updating static void update(); static void registerKeyPress(int key); diff --git a/src/mainwidget.h b/src/mainwidget.h deleted file mode 100644 index 589b852..0000000 --- a/src/mainwidget.h +++ /dev/null @@ -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 - -#include -#include -#include -#include - -#define QTK_DEBUG - -class MeshRenderer; - -class Model; - -class Object; - -class Scene; - -class Skybox; - -class OpenGLTextureFactory; - -class MainWidget : public QOpenGLWidget, protected QOpenGLFunctions { - Q_OBJECT; - - public: - // Constructors - MainWidget(); - explicit MainWidget(QWidget * parent); - explicit MainWidget(const QSurfaceFormat & format); - ~MainWidget() override; - - private: - void teardownGL(); - void initObjects(); - - public: - // Inherited virtual Members - void paintGL() override; - void initializeGL() override; - void resizeGL(int width, int height) override; - - protected slots: - void update(); - void messageLogged(const QOpenGLDebugMessage & msg); - - // Protected Helpers - protected: - void keyPressEvent(QKeyEvent * event) override; - void keyReleaseEvent(QKeyEvent * event) override; - void mousePressEvent(QMouseEvent * event) override; - void mouseReleaseEvent(QMouseEvent * event) override; - - private: - // Private helpers - void initializeWidget(); - void printContextInformation(); - void updateCameraInput(); - - Scene * mScene; - Object * mObject; - - QOpenGLDebugLogger * mDebugLogger; -}; - -#endif // QTK_MAINWIDGET_H diff --git a/src/mesh.cpp b/src/mesh.cpp index 2b1266e..a714582 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -13,6 +13,9 @@ using namespace Qtk; Cube::Cube(DrawMode mode) { mDrawMode = mode; switch(mode) { + // The order of the following assignment values helps to visualize. + // clang-format off + // Cube data for use with glDrawArrays case QTK_DRAW_ARRAYS: mIndices = {/* No indices needed for glDrawArrays */}; @@ -20,51 +23,76 @@ Cube::Cube(DrawMode mode) { mNormals = {FACE_FRONT, FACE_BACK, FACE_TOP, FACE_BOTTOM, FACE_LEFT, FACE_RIGHT}; - mVertices = {// Face 1 (Front) - VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBL, VERTEX_FBR, - VERTEX_FTR, - // Face 2 (Back) - VERTEX_BBR, VERTEX_BTL, VERTEX_BTR, VERTEX_BTL, VERTEX_BBR, - VERTEX_BBL, - // Face 3 (Top) - VERTEX_FTR, VERTEX_BTR, VERTEX_BTL, VERTEX_BTL, VERTEX_FTL, - VERTEX_FTR, - // Face 4 (Bottom) - VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, VERTEX_BBL, VERTEX_BBR, - VERTEX_FBR, - // Face 5 (Left) - VERTEX_FBL, VERTEX_FTL, VERTEX_BTL, VERTEX_FBL, VERTEX_BTL, - VERTEX_BBL, - // Face 6 (Right) - VERTEX_FTR, VERTEX_FBR, VERTEX_BBR, VERTEX_BBR, VERTEX_BTR, - VERTEX_FTR}; + // We're drawing triangles to construct the geometry of a cube. + // Each triangle is made up of three points. + // 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) + VERTEX_BBR, VERTEX_BTL, VERTEX_BTR, + VERTEX_BTL, VERTEX_BBR, VERTEX_BBL, + // Face 3 (Top) + VERTEX_FTR, VERTEX_BTR, VERTEX_BTL, + VERTEX_BTL, VERTEX_FTL, VERTEX_FTR, + // Face 4 (Bottom) + VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, + VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, + // Face 5 (Left) + VERTEX_FBL, VERTEX_FTL, VERTEX_BTL, + VERTEX_FBL, VERTEX_BTL, VERTEX_BBL, + // Face 6 (Right) + VERTEX_FTR, VERTEX_FBR, VERTEX_BBR, + VERTEX_BBR, VERTEX_BTR, VERTEX_FTR + }; - mColors = {// Face 1 (Front) - RED, GREEN, BLUE, BLUE, WHITE, RED, - // Face 2 (Back) - YELLOW, CYAN, MAGENTA, CYAN, YELLOW, BLACK, - // Face 3 (Top) - RED, MAGENTA, CYAN, CYAN, GREEN, RED, - // Face 4 (Bottom) - WHITE, BLUE, BLACK, BLACK, YELLOW, WHITE, - // Face 5 (Left) - BLUE, GREEN, CYAN, BLUE, CYAN, BLACK, - // Face 6 (Right) - RED, WHITE, YELLOW, YELLOW, MAGENTA, RED}; + // Setting colors for each vertex that we defined above. + // 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) + YELLOW, CYAN, MAGENTA, + CYAN, YELLOW, BLACK, + // Face 3 (Top) + RED, MAGENTA, CYAN, + CYAN, GREEN, RED, + // Face 4 (Bottom) + WHITE, BLUE, BLACK, + BLACK, YELLOW, WHITE, + // Face 5 (Left) + BLUE, GREEN, CYAN, + BLUE, CYAN, BLACK, + // Face 6 (Right) + RED, WHITE, YELLOW, + YELLOW, MAGENTA, RED + }; - mTexCoords = {// Face 1 (Front) - UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, UV_TOP, - // Face 2 (Back) - UV_TOP, UV_RIGHT, UV_CORNER, UV_RIGHT, UV_TOP, UV_ORIGIN, - // Face 3 (Top) - UV_CORNER, UV_TOP, UV_ORIGIN, UV_ORIGIN, UV_RIGHT, - UV_CORNER, - // Face 4 (Bottom) - UV_TOP, UV_ORIGIN, UV_RIGHT, UV_RIGHT, UV_CORNER, UV_TOP, - // Face 5 (Left) - UV_TOP, UV_CORNER, UV_RIGHT, UV_TOP, UV_RIGHT, UV_ORIGIN, - // Face 6 (Right) - UV_TOP, UV_CORNER, UV_RIGHT, UV_RIGHT, UV_ORIGIN, UV_TOP}; + // Define texture coordinates for the cube. + // 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) + UV_TOP, UV_RIGHT, UV_CORNER, + UV_RIGHT, UV_TOP, UV_ORIGIN, + // Face 3 (Top) + UV_CORNER, UV_TOP, UV_ORIGIN, + UV_ORIGIN, UV_RIGHT, UV_CORNER, + // Face 4 (Bottom) + UV_TOP, UV_ORIGIN, UV_RIGHT, + UV_RIGHT, UV_CORNER, UV_TOP, + // Face 5 (Left) + UV_TOP, UV_CORNER, UV_RIGHT, + UV_TOP, UV_RIGHT, UV_ORIGIN, + // Face 6 (Right) + UV_TOP, UV_CORNER, UV_RIGHT, + UV_RIGHT, UV_ORIGIN, UV_TOP + }; break; @@ -81,56 +109,64 @@ Cube::Cube(DrawMode mode) { VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, // 4 5 6 7 VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR}; - mIndices = {// Face 1 (Front) - 0, 1, 2, 2, 3, 0, - // Face 2 (Back) - 7, 5, 4, 5, 7, 6, - // Face 3 (Top) - 0, 4, 5, 5, 1, 0, - // Face 4 (Bottom) - 3, 2, 6, 6, 7, 3, - // Face 5 (Left) - 2, 1, 5, 2, 5, 6, - // Face 6 (Right) - 0, 3, 7, 7, 4, 0}; + + mIndices = { + // Face 1 (Front) + 0, 1, 2, 2, 3, 0, + // Face 2 (Back) + 7, 5, 4, 5, 7, 6, + // Face 3 (Top) + 0, 4, 5, 5, 1, 0, + // Face 4 (Bottom) + 3, 2, 6, 6, 7, 3, + // Face 5 (Left) + 2, 1, 5, 2, 5, 6, + // Face 6 (Right) + 0, 3, 7, 7, 4, 0 + }; + break; // Cube shape data for using normals and UVs with glDrawElements case QTK_DRAW_ELEMENTS_NORMALS: mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK}; - mVertices = {// Face 1 (Front) - // 0 1 2 3 - VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR, - // Face 2 (Back) - // 4 5 6 7 - VERTEX_BTL, VERTEX_BBL, VERTEX_BBR, VERTEX_BTR, - // Face 3 (Top) - // 8 9 10 11 - VERTEX_FTL, VERTEX_BTL, VERTEX_BTR, VERTEX_FTR, - // Face 4 (Bottom) - // 12 13 14 15 - VERTEX_FBL, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, - // Face 5 (Left) - // 16 17 18 19 - VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL, - // Face 6 (Right) - // 20 21 22 23 - VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR}; + mVertices = { + // Face 1 (Front) + // 0 1 2 3 + VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR, + // Face 2 (Back) + // 4 5 6 7 + VERTEX_BTL, VERTEX_BBL, VERTEX_BBR, VERTEX_BTR, + // Face 3 (Top) + // 8 9 10 11 + VERTEX_FTL, VERTEX_BTL, VERTEX_BTR, VERTEX_FTR, + // Face 4 (Bottom) + // 12 13 14 15 + VERTEX_FBL, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, + // Face 5 (Left) + // 16 17 18 19 + VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL, + // Face 6 (Right) + // 20 21 22 23 + VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR + }; - mIndices = {// Face 1 (Front) - 0, 1, 2, 2, 3, 0, - // Face 2 (Back) - 4, 5, 6, 6, 7, 4, - // Face 3 (Top) - 8, 9, 10, 10, 11, 8, - // Face 4 (Bottom) - 12, 13, 14, 14, 15, 12, + mIndices = { + // Face 1 (Front) + 0, 1, 2, 2, 3, 0, + // Face 2 (Back) + 4, 5, 6, 6, 7, 4, + // Face 3 (Top) + 8, 9, 10, 10, 11, 8, + // Face 4 (Bottom) + 12, 13, 14, 14, 15, 12, + // Face 5 (Left) + 16, 17, 18, 18, 19, 16, + // Face 6 (Right) + 20, 21, 22, 22, 23, 20 + }; - // Face 5 (Left) - 16, 17, 18, 18, 19, 16, - // Face 6 (Right) - 20, 21, 22, 22, 23, 20}; mNormals = { VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, @@ -143,50 +179,27 @@ Cube::Cube(DrawMode mode) { mTexCoords = { // Face 1 (Front) - UV_TOP, - UV_RIGHT, - UV_CORNER, - UV_RIGHT, - UV_TOP, - UV_ORIGIN, + UV_TOP, UV_RIGHT, UV_CORNER, + UV_RIGHT, UV_TOP, UV_ORIGIN, // Face 2 (Back) - UV_TOP, - UV_RIGHT, - UV_CORNER, - UV_RIGHT, - UV_TOP, - UV_ORIGIN, + UV_TOP, UV_RIGHT, UV_CORNER, + UV_RIGHT, UV_TOP, UV_ORIGIN, // Face 3 (Top) - UV_TOP, - UV_RIGHT, - UV_CORNER, - UV_RIGHT, - UV_TOP, - UV_ORIGIN, + UV_TOP, UV_RIGHT, UV_CORNER, + UV_RIGHT, UV_TOP, UV_ORIGIN, // Face 4 (Bottom) - UV_TOP, - UV_RIGHT, - UV_CORNER, - UV_RIGHT, - UV_TOP, - UV_ORIGIN, + UV_TOP, UV_RIGHT, UV_CORNER, + UV_RIGHT, UV_TOP, UV_ORIGIN, // Face 5 (Left) - UV_TOP, - UV_RIGHT, - UV_CORNER, - UV_RIGHT, - UV_TOP, - UV_ORIGIN, + UV_TOP, UV_RIGHT, UV_CORNER, + UV_RIGHT, UV_TOP, UV_ORIGIN, // Face 6 (Right) - UV_TOP, - UV_RIGHT, - UV_CORNER, - UV_RIGHT, - UV_TOP, - UV_ORIGIN, + UV_TOP, UV_RIGHT, UV_CORNER, + UV_RIGHT, UV_TOP, UV_ORIGIN, }; break; + // clang-format on } } @@ -194,6 +207,8 @@ Triangle::Triangle(DrawMode mode) { mDrawMode = mode; const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f); switch(mode) { + // clang-format off + case QTK_DRAW_ARRAYS: mIndices = {/* No indices needed for glDrawArrays */}; @@ -201,29 +216,17 @@ Triangle::Triangle(DrawMode mode) { mVertices = { // Bottom face (Base of the pyramid) - VERTEX_BBL, - VERTEX_BBR, - VERTEX_FBR, - VERTEX_FBR, - VERTEX_FBL, - VERTEX_BBL, + VERTEX_BBL, VERTEX_BBR, VERTEX_FBR, + VERTEX_FBR, VERTEX_FBL, VERTEX_BBL, // Front face - VERTEX_FBL, - VERTEX_FBR, - triangleTop, + VERTEX_FBL, VERTEX_FBR, triangleTop, // Back face - VERTEX_BBR, - VERTEX_BBL, - triangleTop, + VERTEX_BBR, VERTEX_BBL, triangleTop, // Left face - VERTEX_BBL, - VERTEX_FBL, - triangleTop, + VERTEX_BBL, VERTEX_FBL, triangleTop, // Right face - VERTEX_FBR, - VERTEX_BBR, - triangleTop, + VERTEX_FBR, VERTEX_BBR, triangleTop, }; // Find normals for each triangle of the mesh @@ -238,29 +241,17 @@ Triangle::Triangle(DrawMode mode) { mTexCoords = { // Bottom face (Base of the pyramid) - UV_ORIGIN, - UV_RIGHT, - UV_CORNER, - UV_CORNER, - UV_TOP, - UV_ORIGIN, + UV_ORIGIN, UV_RIGHT, UV_CORNER, + UV_CORNER, UV_TOP, UV_ORIGIN, // Front face - UV_ORIGIN, - UV_RIGHT, - UV_CORNER, + UV_ORIGIN, UV_RIGHT, UV_CORNER, // Back face - UV_ORIGIN, - UV_RIGHT, - UV_CORNER, + UV_ORIGIN, UV_RIGHT, UV_CORNER, // Left face - UV_ORIGIN, - UV_RIGHT, - UV_CORNER, + UV_ORIGIN, UV_RIGHT, UV_CORNER, // Right face - UV_ORIGIN, - UV_RIGHT, - UV_CORNER, + UV_ORIGIN, UV_RIGHT, UV_CORNER, }; break; @@ -296,34 +287,22 @@ Triangle::Triangle(DrawMode mode) { mVertices = { // Bottom face // 0 1 2 - VERTEX_FBL, - VERTEX_FBR, - VERTEX_BBL, + VERTEX_FBL, VERTEX_FBR, VERTEX_BBL, // 3 4 5 - VERTEX_BBR, - VERTEX_FBR, - VERTEX_BBL, + VERTEX_BBR, VERTEX_FBR, VERTEX_BBL, // Front face // 6 7 8 - VERTEX_FBL, - VERTEX_FBR, - triangleTop, + VERTEX_FBL, VERTEX_FBR, triangleTop, // Back face // 9 10 11 - VERTEX_BBR, - VERTEX_BBL, - triangleTop, + VERTEX_BBR, VERTEX_BBL, triangleTop, // Left face // 12 13 14 - VERTEX_BBL, - VERTEX_FBL, - triangleTop, + VERTEX_BBL, VERTEX_FBL, triangleTop, // Right face // 15 16 17 - VERTEX_FBR, - VERTEX_BBR, - triangleTop, + VERTEX_FBR, VERTEX_BBR, triangleTop, }; mIndices = { @@ -350,30 +329,19 @@ Triangle::Triangle(DrawMode mode) { mTexCoords = { // Bottom face - UV_ORIGIN, - UV_RIGHT, - UV_TOP, - UV_CORNER, - UV_RIGHT, - UV_TOP, + UV_ORIGIN, UV_RIGHT, UV_TOP, + UV_CORNER, UV_RIGHT, UV_TOP, // Front face - UV_ORIGIN, - UV_RIGHT, - UV_CORNER, + UV_ORIGIN, UV_RIGHT, UV_CORNER, // Back face - UV_ORIGIN, - UV_RIGHT, - UV_CORNER, + UV_ORIGIN, UV_RIGHT, UV_CORNER, // Left face - UV_ORIGIN, - UV_RIGHT, - UV_CORNER, + UV_ORIGIN, UV_RIGHT, UV_CORNER, // Right face - UV_ORIGIN, - UV_RIGHT, - UV_CORNER, + UV_ORIGIN, UV_RIGHT, UV_CORNER, }; break; + // clang-format on } } diff --git a/src/mesh.h b/src/mesh.h index a411fef..14934be 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -46,21 +46,22 @@ namespace Qtk { #define VECTOR_ONE QVector3D(1.0f, 1.0f, 1.0f) #define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f) +// clang-format off // A series of direction vectors to represent cube face normal -#define FACE_TOP \ - VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP -#define FACE_BOTTOM \ - VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN -#define FACE_LEFT \ - VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT -#define FACE_RIGHT \ - VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \ - VECTOR_RIGHT -#define FACE_FRONT \ - VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \ - VECTOR_FORWARD, VECTOR_FORWARD -#define FACE_BACK \ - VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK +#define FACE_TOP VECTOR_UP, VECTOR_UP, VECTOR_UP, \ + VECTOR_UP, VECTOR_UP, VECTOR_UP +#define FACE_BOTTOM VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, \ + VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN +#define FACE_LEFT VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, \ + VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT +#define FACE_RIGHT VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \ + VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT +#define FACE_FRONT VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \ + VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD +#define FACE_BACK VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, \ + VECTOR_BACK, VECTOR_BACK, VECTOR_BACK +// clang-format on + // Colors using QVector3Ds as RGB values #define WHITE VECTOR_ONE @@ -84,13 +85,26 @@ namespace Qtk { typedef std::vector TexCoords; typedef std::vector 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 { QTK_DRAW_ARRAYS, QTK_DRAW_ELEMENTS, QTK_DRAW_ELEMENTS_NORMALS }; + /** + * Base class for all simple shape objects. + */ struct QTKAPI ShapeBase { + public: + /************************************************************************* + * Constructors / Destructors + ************************************************************************/ + explicit ShapeBase( DrawMode mode = QTK_DRAW_ARRAYS, Vertices v = {}, Indices i = {}, Colors c = {}, TexCoords t = {}, Normals n = {}) : @@ -99,6 +113,10 @@ namespace Qtk { mIndices(std::move(i)), mTexCoords(std::move(t)), mNormals(std::move(n)) {} + /************************************************************************* + * Accessors + ************************************************************************/ + [[nodiscard]] inline const Vertices & getVertices() const { return mVertices; } @@ -122,6 +140,10 @@ namespace Qtk { } protected: + /************************************************************************* + * Protected Members + ************************************************************************/ + DrawMode mDrawMode; Vertices mVertices {}; @@ -132,13 +154,26 @@ namespace Qtk { }; struct Shape : public ShapeBase { + public: + /************************************************************************* + * Typedefs + ************************************************************************/ + friend MeshRenderer; friend Object; + /************************************************************************* + * Constructors / Destructors + ************************************************************************/ + Shape() = default; explicit Shape(const ShapeBase & rhs) : ShapeBase(rhs) {} + /************************************************************************* + * Setters + ************************************************************************/ + virtual inline void setVertices(const Vertices & value) { mVertices = value; } @@ -160,13 +195,15 @@ namespace Qtk { 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 {}; + /* Simple Cube shape. */ struct QTKAPI Cube : public ShapeBase { explicit Cube(DrawMode mode = QTK_DRAW_ARRAYS); }; + /* Simple Triangle shape. */ struct QTKAPI Triangle : public ShapeBase { explicit Triangle(DrawMode mode = QTK_DRAW_ARRAYS); }; diff --git a/src/meshrenderer.cpp b/src/meshrenderer.cpp index 1d8dc21..7372cf4 100644 --- a/src/meshrenderer.cpp +++ b/src/meshrenderer.cpp @@ -8,8 +8,8 @@ #include -#include #include +#include #include using namespace Qtk; @@ -31,6 +31,13 @@ MeshRenderer::~MeshRenderer() { // Static member function to retrieve instances of MeshRenderers 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]; } @@ -126,8 +133,8 @@ void MeshRenderer::setShaders( void MeshRenderer::setUniformMVP( const char * model, const char * view, const char * projection) { ShaderBindScope lock(&mProgram, mBound); - mProgram.setUniformValue(projection, Scene::Projection()); - mProgram.setUniformValue(view, Scene::View()); + mProgram.setUniformValue(projection, Scene::getProjectionMatrix()); + mProgram.setUniformValue(view, Scene::getViewMatrix()); mProgram.setUniformValue(model, mTransform.toMatrix()); } @@ -141,6 +148,8 @@ void MeshRenderer::setColor(const QVector3D & 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) { diff --git a/src/meshrenderer.h b/src/meshrenderer.h index f33a2d6..f8a64b9 100644 --- a/src/meshrenderer.h +++ b/src/meshrenderer.h @@ -15,31 +15,19 @@ #include 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 { public: + /************************************************************************* + * Typedefs + ************************************************************************/ + + /* Static QHash of all mesh objects within the scene. */ + typedef QHash MeshManager; + + /************************************************************************* + * Constructors / Destructors + ************************************************************************/ + // Delegate constructors MeshRenderer( const char * name, Vertices vertices, Indices indices, @@ -54,12 +42,28 @@ namespace Qtk { MeshRenderer(const char * name, const ShapeBase & shape); ~MeshRenderer() override; - // Retrieve a mesh by name stored within a static QHash - static MeshRenderer * getInstance(const QString & name); + /************************************************************************* + * Public Methods + ************************************************************************/ void init(); 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 void setDrawType(int drawType) { mDrawType = drawType; } @@ -105,21 +109,24 @@ namespace Qtk { mVAO.release(); } - inline void enableAttributeArray(int location) { - ShaderBindScope lock(&mProgram, mBound); - mVAO.bind(); - mProgram.enableAttributeArray(location); - mVAO.release(); - } + /************************************************************************* + * Accessors + ************************************************************************/ - 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); - - // Static QHash of all mesh objects within the scene - typedef QHash MeshManager; + Transform3D & getTransform() { return mTransform; } private: + /************************************************************************* + * Private Members + ************************************************************************/ + static MeshManager sInstances; int mDrawType {}; diff --git a/src/model.cpp b/src/model.cpp index 373e9c8..8d6f520 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -9,9 +9,9 @@ #include -#include #include #include +#include #include using namespace Qtk; @@ -97,8 +97,8 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) { // Set Model View Projection values shader.setUniformValue("uModel", mTransform.toMatrix()); - shader.setUniformValue("uView", Scene::View()); - shader.setUniformValue("uProjection", Scene::Projection()); + shader.setUniformValue("uView", Scene::getViewMatrix()); + shader.setUniformValue("uProjection", Scene::getProjectionMatrix()); GLuint diffuseCount = 1; GLuint specularCount = 1; @@ -180,21 +180,6 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY) { * 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) { Assimp::Importer import; @@ -362,7 +347,7 @@ ModelMesh::Textures Model::loadMaterialTextures( // If the texture has not yet been loaded if(!skip) { ModelTexture texture; - texture.mTexture = OpenGLTextureFactory::initTexture2D( + texture.mTexture = OpenGLTextureFactory::initTexture( std::string(mDirectory + '/' + fileName.C_Str()).c_str(), false, false); texture.mID = texture.mTexture->textureId(); @@ -380,7 +365,7 @@ ModelMesh::Textures Model::loadMaterialTextures( } void Model::sortModels() { - auto cameraPos = Scene::Camera().transform(); + auto cameraPos = Scene::getCamera().getTransform(); auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) { // Sort by the first vertex position in the model return (cameraPos.getTranslation().distanceToPoint( diff --git a/src/model.h b/src/model.h index 432add0..4c654bd 100644 --- a/src/model.h +++ b/src/model.h @@ -28,14 +28,20 @@ #include namespace Qtk { + /** + * 3D models will store this data for each vertex in geometry. + */ struct QTKAPI ModelVertex { QVector3D mPosition; QVector3D mNormal; + QVector2D mTextureCoord; QVector3D mTangent; QVector3D mBitangent; - QVector2D mTextureCoord; }; + /** + * Struct to store model textures. 3D Models may have multiple. + */ struct QTKAPI ModelTexture { GLuint mID {}; QOpenGLTexture * mTexture {}; @@ -45,14 +51,25 @@ namespace Qtk { 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 { public: + /************************************************************************* + * Typedefs + ************************************************************************/ + friend Model; typedef std::vector Vertices; typedef std::vector Indices; typedef std::vector Textures; - // Constructors, Destructors + /************************************************************************* + * Constructors, Destructors + ************************************************************************/ + ModelMesh( Vertices vertices, Indices indices, Textures textures, const char * vertexShader = ":/model-basic.vert", @@ -68,78 +85,177 @@ namespace Qtk { ~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); } void draw(QOpenGLShaderProgram & shader); - // ModelMesh Public Members + /************************************************************************* + * Public Members + ************************************************************************/ + Vertices mVertices {}; Indices mIndices {}; Textures mTextures {}; 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 { Q_OBJECT public: + /************************************************************************* + * Typedefs + ************************************************************************/ + + /* ModelManager typedef that will manage global model access. */ + typedef QHash ModelManager; + + /************************************************************************* + * Constructors, Destructors + ************************************************************************/ + + // Default model shaders are provided but we can override them in the ctor inline Model( const char * name, const char * path, const char * vertexShader = ":/model-basic.vert", const char * fragmentShader = ":/model-basic.frag") : mName(name), - mVertexShader(vertexShader), mFragmentShader(fragmentShader) { + mModelPath(path), mVertexShader(vertexShader), + mFragmentShader(fragmentShader) { loadModel(path); } inline ~Model() override { mManager.remove(mName); } + /************************************************************************* + * Public Methods + ************************************************************************/ + void draw(); 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( 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 void setUniform(const char * location, T value) { for(auto & mesh : mMeshes) { mesh.mProgram->bind(); - mesh.mProgram->setUniformValue(location, value); - 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); - typedef QHash ModelManager; + Transform3D & getTransform() { return mTransform; } 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 processNode(aiNode * node, const aiScene * scene); + ModelMesh processMesh(aiMesh * mesh, const aiScene * scene); + ModelMesh::Textures loadMaterialTextures( aiMaterial * mat, aiTextureType type, const std::string & typeName); + void sortModels(); - // Model Private Members + /************************************************************************* + * 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 {}; + /* Container to store N loaded meshes for this model. */ std::vector mMeshes {}; + /* The directory this model and it's textures are stored. */ 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 diff --git a/src/object.h b/src/object.h index 90098f0..e281d40 100644 --- a/src/object.h +++ b/src/object.h @@ -18,12 +18,24 @@ #include 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 { Q_OBJECT public: + /************************************************************************* + * Typedefs + ************************************************************************/ + friend MeshRenderer; + /************************************************************************* + * Constructors / Destructors + ************************************************************************/ + // Initialize an object with no shape data assigned explicit Object(const char * name) : mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false) {} @@ -35,19 +47,39 @@ namespace Qtk { ~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; } - 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) { mShape.mColors = value; @@ -84,6 +116,10 @@ namespace Qtk { mShape.mVertices = value; } + /************************************************************************* + * Public Methods + ************************************************************************/ + virtual inline void bindShaders() { mBound = true; mProgram.bind(); @@ -94,21 +130,19 @@ namespace Qtk { mProgram.release(); } + private: + /************************************************************************* + * Private Members + ************************************************************************/ + + QOpenGLShaderProgram mProgram; QOpenGLBuffer mVBO, mNBO; QOpenGLVertexArrayObject mVAO; - Transform3D mTransform; Shape mShape; Texture mTexture; const char * mName; bool mBound; - - private: - virtual inline void setTexture(QOpenGLTexture * value) { - mTexture.setTexture(value); - } - - QOpenGLShaderProgram mProgram; }; } // namespace Qtk diff --git a/src/qtkwidget.cpp b/src/qtkwidget.cpp index 34cd9a3..14ffccc 100644 --- a/src/qtkwidget.cpp +++ b/src/qtkwidget.cpp @@ -8,10 +8,10 @@ #include -#include #include #include #include +#include using namespace Qtk; @@ -23,7 +23,6 @@ QtkWidget::QtkWidget() : mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) { initializeWidget(); } -// Constructor for using this widget in QtDesigner QtkWidget::QtkWidget(QWidget * parent) : QOpenGLWidget(parent), mScene(Q_NULLPTR), mDebugLogger(Q_NULLPTR) { 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() { initializeOpenGLFunctions(); // Connect the frameSwapped signal to call the update() function @@ -93,11 +74,19 @@ void QtkWidget::initializeGL() { } void QtkWidget::resizeGL(int width, int height) { - Scene::Projection().setToIdentity(); - Scene::Projection().perspective( + Scene::getProjectionMatrix().setToIdentity(); + Scene::getProjectionMatrix().perspective( 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 ******************************************************************************/ @@ -175,7 +164,7 @@ void QtkWidget::messageLogged(const QOpenGLDebugMessage & msg) { } /******************************************************************************* - * Protected Helpers + * Protected Methods ******************************************************************************/ void QtkWidget::keyPressEvent(QKeyEvent * event) { @@ -204,7 +193,7 @@ void QtkWidget::mouseReleaseEvent(QMouseEvent * event) { } /******************************************************************************* - * Private Helpers + * Private Methods ******************************************************************************/ void QtkWidget::initializeWidget() { @@ -265,31 +254,31 @@ void QtkWidget::updateCameraInput() { static const float rotSpeed = 0.5f; // Handle rotations - Scene::Camera().transform().rotate( + Scene::getCamera().getTransform().rotate( -rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp); - Scene::Camera().transform().rotate( - -rotSpeed * Input::mouseDelta().y(), Scene::Camera().right()); + Scene::getCamera().getTransform().rotate( + -rotSpeed * Input::mouseDelta().y(), Scene::getCamera().right()); // Handle translations QVector3D translation; if(Input::keyPressed(Qt::Key_W)) { - translation += Scene::Camera().forward(); + translation += Scene::getCamera().forward(); } if(Input::keyPressed(Qt::Key_S)) { - translation -= Scene::Camera().forward(); + translation -= Scene::getCamera().forward(); } if(Input::keyPressed(Qt::Key_A)) { - translation -= Scene::Camera().right(); + translation -= Scene::getCamera().right(); } if(Input::keyPressed(Qt::Key_D)) { - translation += Scene::Camera().right(); + translation += Scene::getCamera().right(); } if(Input::keyPressed(Qt::Key_Q)) { - translation -= Scene::Camera().up() / 2.0f; + translation -= Scene::getCamera().up() / 2.0f; } 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); } } diff --git a/src/qtkwidget.h b/src/qtkwidget.h index f3647d4..a19e7b6 100644 --- a/src/qtkwidget.h +++ b/src/qtkwidget.h @@ -15,60 +15,141 @@ #include #include -#include #include +#include 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 { Q_OBJECT; public: - // Constructors + /************************************************************************* + * Contructors / Destructors + ************************************************************************/ + + /** + * Default ctor will configure a QSurfaceFormat with default settings. + */ QtkWidget(); + + /** + * Qt Designer will call this ctor when creating this widget as a child. + * + * @param parent The parent QWidget + */ 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); + ~QtkWidget() override; private: - void teardownGL(); + /************************************************************************* + * Private Methods + ************************************************************************/ + + // clang-format off + void teardownGL() { /* Nothing to teardown yet... */ } + // clang-format on public: - // Inherited virtual Members - void paintGL() override; + /************************************************************************* + * Public Inherited Virtual Methods + ************************************************************************/ + + /** + * Called when the widget is first constructed. + */ 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; + /** + * Called when OpenGL repaints the widget. + */ + void paintGL() override; + + /************************************************************************* + * Accessors + ************************************************************************/ + inline Qtk::Scene * getScene() { return mScene; } + /************************************************************************* + * Setters + ************************************************************************/ + inline void setScene(Qtk::Scene * scene) { delete mScene; - mScene = scene; } protected slots: + /************************************************************************* + * Qt Slots + ************************************************************************/ + + /** + * Called when the `frameSwapped` signal is caught. + * See definition of initializeGL() + */ void update(); + #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); #endif - // Protected Helpers protected: + /************************************************************************* + * Protected Methods + ************************************************************************/ + void keyPressEvent(QKeyEvent * event) override; void keyReleaseEvent(QKeyEvent * event) override; void mousePressEvent(QMouseEvent * event) override; void mouseReleaseEvent(QMouseEvent * event) override; private: - // Private helpers + /************************************************************************* + * Private Methods + ************************************************************************/ + void initializeWidget(); static void updateCameraInput(); - Qtk::Scene * mScene; #ifdef QTK_DEBUG void printContextInformation(); QOpenGLDebugLogger * mDebugLogger; #endif + + /************************************************************************* + * Private Members + ************************************************************************/ + + Qtk::Scene * mScene; }; } // namespace Qtk diff --git a/src/abstractscene.cpp b/src/scene.cpp similarity index 88% rename from src/abstractscene.cpp rename to src/scene.cpp index 6e6fcdc..9a05e66 100644 --- a/src/abstractscene.cpp +++ b/src/scene.cpp @@ -6,9 +6,9 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include #include #include +#include #include using namespace Qtk; @@ -21,8 +21,8 @@ QMatrix4x4 Scene::mProjection; ******************************************************************************/ Scene::Scene() { - mCamera.transform().setTranslation(0.0f, 0.0f, 20.0f); - mCamera.transform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); + mCamera.getTransform().setTranslation(0.0f, 0.0f, 20.0f); + mCamera.getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f); } Scene::~Scene() { @@ -32,11 +32,10 @@ Scene::~Scene() { for(auto & model : mModels) { delete model; } - delete mSkybox; } -void Scene::privDraw() { +void Scene::privateDraw() { if(!mInit) { initializeOpenGLFunctions(); init(); diff --git a/src/scene.h b/src/scene.h new file mode 100644 index 0000000..439b6f9 --- /dev/null +++ b/src/scene.h @@ -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 +#include +#include +#include + +#include + +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 mMeshes {}; + /* Models used for storing 3D models in the scene. */ + std::vector mModels {}; + }; +} // namespace Qtk + +#endif // QTK_SCENE_H diff --git a/src/skybox.cpp b/src/skybox.cpp index 7088c35..29cda26 100644 --- a/src/skybox.cpp +++ b/src/skybox.cpp @@ -6,7 +6,7 @@ ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ##############################################################################*/ -#include +#include #include #include @@ -32,8 +32,8 @@ Skybox::Skybox(const std::string & name) : ":/right.png", ":/top.png", ":/front.png", ":/left.png", ":/bottom.png", ":/back.png", name) {} -Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) : - mTexture(cubeMap) { +Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) { + mTexture.setTexture(cubeMap); init(); } @@ -49,8 +49,8 @@ void Skybox::draw() { mProgram.bind(); mTexture.getOpenGLTexture().bind(); - mProgram.setUniformValue("uProjectionMatrix", Scene::Projection()); - mProgram.setUniformValue("uViewMatrix", Scene::Camera().toMatrix()); + mProgram.setUniformValue("uProjectionMatrix", Scene::getProjectionMatrix()); + mProgram.setUniformValue("uViewMatrix", Scene::getCamera().toMatrix()); mProgram.setUniformValue("uTexture", 0); glDrawElements( GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data()); diff --git a/src/skybox.h b/src/skybox.h index edbace4..6033c19 100644 --- a/src/skybox.h +++ b/src/skybox.h @@ -21,13 +21,23 @@ #include 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 { public: + /************************************************************************* + * Constructors / Destructors + ************************************************************************/ + // Delegate this constructor to use default skybox images explicit Skybox(const std::string & name = "Skybox"); + explicit Skybox( QOpenGLTexture * cubeMap, const std::string & name = "Skybox"); - // Constructor, Destructor + Skybox( const std::string & right, const std::string & top, const std::string & front, const std::string & left, @@ -36,11 +46,23 @@ namespace Qtk { ~Skybox() = default; + /************************************************************************* + * Public Methods + ************************************************************************/ + void draw(); private: + /************************************************************************* + * Private Methods + ************************************************************************/ + void init(); + /************************************************************************* + * Private Members + ************************************************************************/ + Vertices mVertices {}; Indices mIndices {}; diff --git a/src/texture.cpp b/src/texture.cpp index 72e4b06..5745bf0 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -28,7 +28,7 @@ QImage * OpenGLTextureFactory::initImage( return loadedImage; } -QOpenGLTexture * OpenGLTextureFactory::initTexture2D( +QOpenGLTexture * OpenGLTextureFactory::initTexture( const char * texture, bool flipX, bool flipY) { QImage * image = initImage(texture, flipX, flipY); auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); diff --git a/src/texture.h b/src/texture.h index acf3464..548968f 100644 --- a/src/texture.h +++ b/src/texture.h @@ -9,30 +9,124 @@ #ifndef QTOPENGL_TEXTURE_H #define QTOPENGL_TEXTURE_H +#include #include #include #include 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 { public: + /************************************************************************* + * Constructors / Destructors + ************************************************************************/ ~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( 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); - // 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( const QImage & right, const QImage & top, const QImage & front, 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); + + /** + * 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( const char * right, const char * top, const char * front, const char * left, const char * bottom, const char * back); @@ -42,48 +136,66 @@ namespace Qtk { 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 { public: + /************************************************************************* + * Typedefs + ************************************************************************/ + friend class Skybox; + + /************************************************************************* + * Constructors / Destructors + ************************************************************************/ + Texture() = default; + Texture(const Texture & value) { - mOpenGLTexture = OpenGLTextureFactory::initTexture2D(value.mPath); + mOpenGLTexture = OpenGLTextureFactory::initTexture(value.mPath); mPath = value.mPath; } + explicit Texture( const char * path, bool flipX = false, bool flipY = false) : - mOpenGLTexture( - OpenGLTextureFactory::initTexture2D(path, flipX, flipY)), + mOpenGLTexture(OpenGLTextureFactory::initTexture(path, flipX, flipY)), mPath(path) {} + explicit Texture(QOpenGLTexture * texture) : mOpenGLTexture(texture) {} ~Texture() { mOpenGLTexture->destroy(); } + /************************************************************************* + * Accessors + ************************************************************************/ + [[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const { return *mOpenGLTexture; } [[nodiscard]] inline std::string getPath() const { return mPath; } + /************************************************************************* + * Setters + ************************************************************************/ + void setTexture( const std::string & path, bool flipX = false, bool flipY = false) { mOpenGLTexture = - OpenGLTextureFactory::initTexture2D(path.data(), flipX, flipY); + OpenGLTextureFactory::initTexture(path.data(), flipX, flipY); mPath = path.data(); } void setTexture( const char * path, bool flipX = false, bool flipY = false) { - mOpenGLTexture = - OpenGLTextureFactory::initTexture2D(path, flipX, flipY); + mOpenGLTexture = OpenGLTextureFactory::initTexture(path, flipX, flipY); mPath = path; } - // TODO: This is unsafe because we don't have a path. Encapsulate it. - inline void setTexture(QOpenGLTexture * texture) { - mOpenGLTexture = texture; - } - virtual inline void setCubeMap(const char * path) { mOpenGLTexture = OpenGLTextureFactory::initCubeMap(path); mPath = path; @@ -103,12 +215,24 @@ namespace Qtk { right, top, front, left, bottom, back); } + /************************************************************************* + * Public Methods + ************************************************************************/ + [[nodiscard]] inline bool hasTexture() const { return mOpenGLTexture != Q_NULLPTR; } private: + /************************************************************************* + * Private Members + ************************************************************************/ + inline void setTexture(QOpenGLTexture * texture) { + mOpenGLTexture = texture; + } + QOpenGLTexture * mOpenGLTexture = Q_NULLPTR; + /* Path to this texture on disk or Qt resource. */ const char * mPath {}; }; diff --git a/src/transform3D.h b/src/transform3D.h index 974aabd..81c815d 100644 --- a/src/transform3D.h +++ b/src/transform3D.h @@ -23,15 +23,22 @@ #include namespace Qtk { + /** + * Transform3D class to represent and modify object position in 3D space. + */ class QTKAPI Transform3D { public: - // Constructors + /************************************************************************* + * Constructors, Destructors + ************************************************************************/ + inline Transform3D() : m_dirty(true), mScale(1.0f, 1.0f, 1.0f), mTranslation(0.0f, 0.0f, 0.0f) {} - // - // Transformations + /************************************************************************* + * Transformations + ************************************************************************/ void translate(const QVector3D & dt); @@ -72,8 +79,9 @@ namespace Qtk { grow(QVector3D(factor, factor, factor)); } - // - // Setters + /************************************************************************* + * Setters + ************************************************************************/ // Set object position void setTranslation(const QVector3D & t); @@ -102,8 +110,9 @@ namespace Qtk { setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle)); } - // - // Accessors + /************************************************************************* + * Getters + ************************************************************************/ [[nodiscard]] inline const QVector3D & getTranslation() const { return mTranslation; @@ -121,9 +130,17 @@ namespace Qtk { [[nodiscard]] QVector3D getUp() const; [[nodiscard]] QVector3D getRight() const; + /************************************************************************* + * Public members + ************************************************************************/ + static const QVector3D LocalForward, LocalUp, LocalRight; private: + /************************************************************************* + * Private members + ************************************************************************/ + QVector3D mTranslation; QQuaternion mRotation; QVector3D mScale;