From 42970781dbbd7d10cb3b5fd7660c77f251a70c0d Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Thu, 24 Nov 2022 15:30:12 -0500 Subject: [PATCH] Refactor handling of textures and shaders --- README.md | 8 +- app/examplescene.cpp | 398 +++++++++---------------------------------- src/mesh.h | 13 +- src/meshrenderer.cpp | 42 ++++- src/meshrenderer.h | 46 ++++- src/object.h | 36 +++- src/skybox.cpp | 8 +- src/skybox.h | 5 +- src/texture.h | 46 ++++- 9 files changed, 245 insertions(+), 357 deletions(-) diff --git a/README.md b/README.md index 66562bb..b59e43b 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ cmake -DASSIMP_NEW_INTERFACE=ON -DQTK_UPDATE_SUBMODULES=OFF -DCMAKE_PREFIX_PATH= #### Development -This project uses version `15.0.5` of `clang-format` and `clang-tidy`. +This project uses version `15.0.5` of `clang-format`. Before merging any branch we should run `clang-tidy` followed by `clang-format`. ```bash @@ -67,7 +67,6 @@ cmake --build build -j $(nproc --ignore=2) sudo cmake --build build -j $(nproc --ignore=2) --target install ``` -After this is done, we can check that for the correct version of `15.0.5`. If this version is any earlier than `15.0.0`, running `clang-format` will fail because this project uses configuration options made available since `15.0.0`. ```bash @@ -75,7 +74,8 @@ clang-format --version clang-format version 15.0.5 (git@github.com:llvm/llvm-project.git 154e88af7ec97d9b9f389e55d45bf07108a9a097) ``` -CLion has integration for IDE code reformatting actions with `clang-format`. If you're using CLion, the `.clang-format` configuration will be picked up by CLion automatically. +CLion has integration for IDE code reformatting actions with `clang-format`. +If you're using CLion, the `.clang-format` configuration will be picked up by CLion automatically. `clang-tidy` can be run with the following commands. @@ -86,7 +86,7 @@ cd qtk cmake -B build && cmake --build build cd build # Run clang-tidy from within build directory -clang-tidy --fix --config-file=../.clang-tidy ../src/*.cpp ../src/*.h ../app/*.cpp ../app/*.h +clang-tidy --fix --fix-errors --config-file=../.clang-tidy ../src/*.cpp ../src/*.h ../app/*.cpp ../app/*.h ``` Last we need to run `clang-format`, this can be done with the command directly. diff --git a/app/examplescene.cpp b/app/examplescene.cpp index 9ac2f7e..9aa433f 100644 --- a/app/examplescene.cpp +++ b/app/examplescene.cpp @@ -51,103 +51,50 @@ void ExampleScene::init() { mTestPhong = new Qtk::MeshRenderer("phong", Qtk::Cube()); mTestPhong->mTransform.setTranslation(3.0f, 0.0f, -2.0f); mTestPhong->setShaders(":/solid-phong.vert", ":/solid-phong.frag"); - mTestPhong->init(); - mTestPhong->mProgram.bind(); + + // You no longer need to manually bind shader program. + // + But, you can still bind it if you want to. + // mTestPhong->bindShaders(); mTestPhong->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestPhong->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestPhong->setUniform("uAmbientStrength", 0.2f); mTestPhong->setUniform("uSpecularStrength", 0.50f); mTestPhong->setUniform("uSpecularShine", 256); - - mTestPhong->mVAO.bind(); - mTestPhong->mNBO.create(); - mTestPhong->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw); - mTestPhong->mNBO.bind(); - mTestPhong->mNBO.allocate( - mTestPhong->getNormals().data(), - mTestPhong->getNormals().size() * sizeof(mTestPhong->getNormals()[0])); - mTestPhong->mProgram.enableAttributeArray(1); - mTestPhong->mProgram.setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - mTestPhong->mNBO.release(); - mTestPhong->mVAO.release(); - mTestPhong->mProgram.release(); + mTestPhong->reallocateNormals(mTestPhong->getNormals()); + // mTestPhong->releaseShaders(); // Initialize Ambient example cube mTestAmbient = new Qtk::MeshRenderer("ambient", Cube()); mTestAmbient->mTransform.setTranslation(7.0f, 0.0f, -2.0f); mTestAmbient->setShaders(":/solid-ambient.vert", ":/solid-ambient.frag"); mTestAmbient->init(); - mTestAmbient->mProgram.bind(); mTestAmbient->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestAmbient->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestAmbient->setUniform("uAmbientStrength", 0.2f); - - mTestAmbient->mVAO.bind(); - mTestAmbient->mNBO.create(); - mTestAmbient->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw); - mTestAmbient->mNBO.bind(); - mTestAmbient->mNBO.allocate( - mTestAmbient->getNormals().data(), - mTestAmbient->getNormals().size() - * sizeof(mTestAmbient->getNormals()[0])); - mTestAmbient->mProgram.enableAttributeArray(1); - mTestAmbient->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - mTestAmbient->mNBO.release(); - mTestAmbient->mVAO.release(); - mTestAmbient->mProgram.release(); + mTestAmbient->reallocateNormals(mTestAmbient->getNormals()); // Initialize Diffuse example cube mTestDiffuse = new Qtk::MeshRenderer("diffuse", Cube()); mTestDiffuse->mTransform.setTranslation(9.0f, 0.0f, -2.0f); mTestDiffuse->setShaders(":/solid-diffuse.vert", ":/solid-diffuse.frag"); mTestDiffuse->init(); - mTestDiffuse->mProgram.bind(); mTestDiffuse->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestDiffuse->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestDiffuse->setUniform("uAmbientStrength", 0.2f); - - mTestDiffuse->mVAO.bind(); - mTestDiffuse->mNBO.create(); - mTestDiffuse->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw); - mTestDiffuse->mNBO.bind(); - mTestDiffuse->mNBO.allocate( - mTestDiffuse->getNormals().data(), - mTestDiffuse->getNormals().size() - * sizeof(mTestDiffuse->getNormals()[0])); - mTestDiffuse->mProgram.enableAttributeArray(1); - mTestDiffuse->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - mTestDiffuse->mNBO.release(); - mTestDiffuse->mVAO.release(); - mTestDiffuse->mProgram.release(); + mTestDiffuse->reallocateNormals(mTestDiffuse->getNormals()); // Initialize Specular example cube mTestSpecular = new Qtk::MeshRenderer("specular", Cube()); mTestSpecular->mTransform.setTranslation(11.0f, 0.0f, -2.0f); mTestSpecular->setShaders(":/solid-specular.vert", ":/solid-specular.frag"); mTestSpecular->init(); - mTestSpecular->mProgram.bind(); mTestSpecular->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); mTestSpecular->setUniform("uLightColor", QVector3D(1.0f, 1.0f, 1.0f)); mTestSpecular->setUniform("uAmbientStrength", 0.2f); mTestSpecular->setUniform("uSpecularStrength", 0.50f); mTestSpecular->setUniform("uSpecularShine", 256); + mTestSpecular->reallocateNormals(mTestSpecular->getNormals()); - mTestSpecular->mVAO.bind(); - mTestSpecular->mNBO.create(); - mTestSpecular->mNBO.setUsagePattern(QOpenGLBuffer::StaticDraw); - mTestSpecular->mNBO.bind(); - mTestSpecular->mNBO.allocate( - mTestSpecular->getNormals().data(), - mTestSpecular->getNormals().size() - * sizeof(mTestSpecular->getNormals()[0])); - mTestSpecular->mProgram.enableAttributeArray(1); - mTestSpecular->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - mTestSpecular->mNBO.release(); - mTestSpecular->mVAO.release(); - mTestSpecular->mProgram.release(); // // Model loading @@ -193,7 +140,6 @@ void ExampleScene::init() { mMeshes.back()->mTransform.scale(0.25f); // This function changes values we have allocated in a buffer, so init() after mMeshes.back()->setColor(GREEN); - mMeshes.back()->init(); mModels.push_back(new Qtk::Model( "alienTest", ":/models/alien-hominid/alien.obj", ":/model-specular.vert", @@ -219,7 +165,6 @@ void ExampleScene::init() { mMeshes.back()->mTransform.scale(0.25f); // This function changes values we have allocated in a buffer, so init() after mMeshes.back()->setColor(GREEN); - mMeshes.back()->init(); mModels.push_back(new Qtk::Model( "spartanTest", ":/models/spartan/spartan.obj", ":/model-normals.vert", @@ -245,33 +190,15 @@ void ExampleScene::init() { mMeshes.back()->setDrawType(GL_LINE_LOOP); // This function changes values we have allocated in a buffer, so init() after mMeshes.back()->setColor(GREEN); - mMeshes.back()->init(); mMeshes.push_back(new Qtk::MeshRenderer("testPhong", Cube(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 10.0f); mMeshes.back()->setShaders(":/phong.vert", ":/phong.frag"); mMeshes.back()->setColor(QVector3D(0.0f, 0.25f, 0.0f)); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - - mMeshes.back()->mNBO.allocate( - mMeshes.back()->getNormals().data(), - mMeshes.back()->getNormals().size() - * sizeof(mMeshes.back()->getNormals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); mMeshes.back()->setUniform("uMaterial.ambient", QVector3D(0.0f, 0.3f, 0.0f)); mMeshes.back()->setUniform("uMaterial.diffuse", QVector3D(0.0f, 0.2f, 0.0f)); - mMeshes.back()->setUniform("uMaterial.specular", QVector3D(1.0f, 1.0f, 1.0f)); mMeshes.back()->setUniform("uMaterial.ambientStrength", 1.0f); mMeshes.back()->setUniform("uMaterial.diffuseStrength", 1.0f); @@ -282,7 +209,6 @@ void ExampleScene::init() { mMeshes.back()->setUniform("uLight.specular", QVector3D(0.62f, 0.55f, 0.37f)); mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); - mMeshes.back()->mProgram.release(); // // Create simple shapes using MeshRenderer class and data in mesh.h @@ -312,7 +238,6 @@ void ExampleScene::init() { mMeshes.back()->setDrawType(GL_LINE_LOOP); // This function changes values we have allocated in a buffer, so init() after mMeshes.back()->setColor(GREEN); - mMeshes.back()->init(); // // Testing for normals, texture coordinates @@ -322,46 +247,25 @@ void ExampleScene::init() { new Qtk::MeshRenderer("rgbNormalsCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 4.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mNBO.allocate( - mMeshes.back()->getNormals().data(), - mMeshes.back()->getNormals().size() - * sizeof(mMeshes.back()->getNormals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - - mMeshes.back()->mProgram.release(); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); // RGB Normals cube to show normals are correct with QTK_DRAW_ELEMENTS_NORMALS mMeshes.push_back(new Qtk::MeshRenderer( "rgbNormalsCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, 2.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mProgram.bind(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); - mMeshes.back()->mNBO.allocate( - mMeshes.back()->getNormals().data(), - mMeshes.back()->getNormals().size() - * sizeof(mMeshes.back()->getNormals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - - mMeshes.back()->mProgram.release(); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + Texture crateTexture; + crateTexture.setTexture(":/crate.png"); + Cube cube; + auto * m = new MeshRenderer("Test Crate", Cube(QTK_DRAW_ARRAYS)); + m->mTransform.setTranslation(0, 0, 13); + m->setShaders(":/texture2d.vert", ":/texture2d.frag"); + m->setTexture(crateTexture); + m->setUniform("uTexture", 0); + m->reallocateTexCoords(cube.getTexCoords()); + mMeshes.push_back(m); // Texturing a cube using texture coordinates and glDrawArrays // + Texturing with UVs using glDrawElements requires @@ -372,53 +276,21 @@ void ExampleScene::init() { new Qtk::MeshRenderer("uvCubeArraysTest", Cube(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(-3.0f, 0.0f, -2.0f); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->setTexture( - OpenGLTextureFactory::initTexture2D(":/crate.png")); + mMeshes.back()->setTexture(crateTexture); mMeshes.back()->setUniform("uTexture", 0); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.destroy(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate( - mMeshes.back()->mShape.getTexCoords().data(), - mMeshes.back()->mShape.getTexCoords().size() - * sizeof(mMeshes.back()->mShape.getTexCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 2, sizeof(QVector2D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); // Test drawing a cube with texture coordinates using glDrawElements mMeshes.push_back(new Qtk::MeshRenderer( "uvCubeElementsTest", Cube(QTK_DRAW_ELEMENTS_NORMALS))); mMeshes.back()->mTransform.setTranslation(-1.7f, 0.0f, -2.0f); + mMeshes.back()->setTexture(":/crate.png"); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->init(); - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mNBO.allocate( - mMeshes.back()->getTexCoords().data(), - mMeshes.back()->getTexCoords().size() - * sizeof(mMeshes.back()->getTexCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - mMeshes.back()->setTexture( - OpenGLTextureFactory::initTexture2D(":/crate.png")); - - mMeshes.back()->mProgram.setUniformValue("uTexture", 0); - mMeshes.back()->mProgram.release(); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + mMeshes.back()->bindShaders(); + mMeshes.back()->setUniform("uTexture", 0); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords(), 3); + mMeshes.back()->releaseShaders(); mMeshes.back()->mTransform.rotate(45.0f, 0.0f, 1.0f, 0.0f); // Texturing a cube using a cube map @@ -429,26 +301,9 @@ void ExampleScene::init() { mMeshes.back()->mTransform.setRotation(45.0f, 0.0f, 1.0f, 0.0f); mMeshes.back()->setShaders( ":/texture-cubemap.vert", ":/texture-cubemap.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->setTexture(OpenGLTextureFactory::initCubeMap(":/crate.png")); + mMeshes.back()->setCubeMap(":/crate.png"); mMeshes.back()->setUniform("uTexture", 0); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.destroy(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate( - mMeshes.back()->mShape.getTexCoords().data(), - mMeshes.back()->mShape.getTexCoords().size() - * sizeof(mMeshes.back()->mShape.getTexCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 2, sizeof(QVector2D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); // Create a cube with custom shaders // + Apply RGB normals shader and spin the cube for a neat effect @@ -456,120 +311,40 @@ void ExampleScene::init() { new Qtk::MeshRenderer("rgbNormalsCube", Cube(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(5.0f, 2.0f, -2.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mNBO.allocate( - mMeshes.back()->getNormals().data(), - mMeshes.back()->getNormals().size() - * sizeof(mMeshes.back()->getNormals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - - mMeshes.back()->mProgram.release(); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); // RGB Normals triangle to show normals are correct with QTK_DRAW_ARRAYS mMeshes.push_back(new Qtk::MeshRenderer( "rgbTriangleArraysTest", Triangle(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 2.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate( - mMeshes.back()->getNormals().data(), - mMeshes.back()->getNormals().size() - * sizeof(mMeshes.back()->getNormals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); // RGB Normals triangle to show normals are correct with QTK_DRAW_ELEMENTS mMeshes.push_back(new Qtk::MeshRenderer( "rgbTriangleElementsTest", Triangle(QTK_DRAW_ELEMENTS_NORMALS))); mMeshes.back()->mTransform.setTranslation(7.0f, 0.0f, 4.0f); mMeshes.back()->setShaders(":/rgb-normals.vert", ":/rgb-normals.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate( - mMeshes.back()->getNormals().data(), - mMeshes.back()->getNormals().size() - * sizeof(mMeshes.back()->getNormals()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 3, sizeof(QVector3D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateNormals(mMeshes.back()->getNormals()); // Test drawing triangle with glDrawArrays with texture coordinates mMeshes.push_back( new Qtk::MeshRenderer("testTriangleArraysUV", Triangle(QTK_DRAW_ARRAYS))); mMeshes.back()->mTransform.setTranslation(-3.0f, 2.0f, -2.0f); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); mMeshes.back()->setTexture(":/crate.png"); mMeshes.back()->setUniform("uTexture", 0); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.destroy(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate( - mMeshes.back()->mShape.getTexCoords().data(), - mMeshes.back()->mShape.getTexCoords().size() - * sizeof(mMeshes.back()->mShape.getTexCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 2, sizeof(QVector2D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); // Test drawing triangle with glDrawElements with texture coordinates mMeshes.push_back(new Qtk::MeshRenderer( "testTriangleElementsUV", Triangle(QTK_DRAW_ELEMENTS_NORMALS))); mMeshes.back()->mTransform.setTranslation(-2.5f, 0.0f, -1.0f); mMeshes.back()->setShaders(":/texture2d.vert", ":/texture2d.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); - - // mMeshes.back()->setTexture(OpenGLTextureFactory::initTexture2D(":/crate.png")); - mMeshes.back()->mTexture.setTexture(":/crate.png"); + mMeshes.back()->setTexture(":/crate.png"); mMeshes.back()->setUniform("uTexture", 0); - - mMeshes.back()->mVAO.bind(); - mMeshes.back()->mNBO.destroy(); - mMeshes.back()->mNBO.create(); - mMeshes.back()->mNBO.bind(); - mMeshes.back()->mNBO.allocate( - mMeshes.back()->mShape.getTexCoords().data(), - mMeshes.back()->mShape.getTexCoords().size() - * sizeof(mMeshes.back()->mShape.getTexCoords()[0])); - mMeshes.back()->mProgram.enableAttributeArray(1); - mMeshes.back()->mProgram.setAttributeBuffer( - 1, GL_FLOAT, 0, 2, sizeof(QVector2D)); - mMeshes.back()->mNBO.release(); - mMeshes.back()->mVAO.release(); - mMeshes.back()->mProgram.release(); + mMeshes.back()->reallocateTexCoords(mMeshes.back()->getTexCoords()); // // Lighting cube examples @@ -579,10 +354,7 @@ void ExampleScene::init() { mMeshes.back()->mTransform.setTranslation(5.0f, 0.0f, -2.0f); mMeshes.back()->setShaders( ":/solid-perspective.vert", ":/solid-perspective.frag"); - mMeshes.back()->init(); - mMeshes.back()->mProgram.bind(); mMeshes.back()->setUniform("uColor", QVector3D(0.0f, 0.25f, 0.0f)); - mMeshes.back()->mProgram.release(); // Create objects that represent light sources for lighting examples mMeshes.push_back( @@ -612,7 +384,7 @@ void ExampleScene::draw() { mesh->draw(); } - mTestPhong->mProgram.bind(); + mTestPhong->bindShaders(); mTestPhong->setUniform( "uModelInverseTransposed", mTestPhong->mTransform.toMatrix().normalMatrix()); @@ -621,16 +393,16 @@ void ExampleScene::draw() { MeshRenderer::getInstance("phongLight")->mTransform.getTranslation()); mTestPhong->setUniform( "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); - mTestPhong->mProgram.release(); + mTestPhong->releaseShaders(); mTestPhong->draw(); - mTestAmbient->mProgram.bind(); + mTestAmbient->bindShaders(); mTestAmbient->setUniform( "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); - mTestAmbient->mProgram.release(); + mTestAmbient->releaseShaders(); mTestAmbient->draw(); - mTestDiffuse->mProgram.bind(); + mTestDiffuse->bindShaders(); mTestDiffuse->setUniform( "uModelInverseTransposed", mTestDiffuse->mTransform.toMatrix().normalMatrix()); @@ -639,10 +411,10 @@ void ExampleScene::draw() { MeshRenderer::getInstance("diffuseLight")->mTransform.getTranslation()); mTestDiffuse->setUniform( "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); - mTestDiffuse->mProgram.release(); + mTestDiffuse->releaseShaders(); mTestDiffuse->draw(); - mTestSpecular->mProgram.bind(); + mTestSpecular->bindShaders(); mTestSpecular->setUniform( "uModelInverseTransposed", mTestSpecular->mTransform.toMatrix().normalMatrix()); @@ -651,65 +423,51 @@ void ExampleScene::draw() { MeshRenderer::getInstance("specularLight")->mTransform.getTranslation()); mTestSpecular->setUniform( "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); - mTestSpecular->mProgram.release(); + mTestSpecular->releaseShaders(); mTestSpecular->draw(); } void ExampleScene::update() { auto position = MeshRenderer::getInstance("alienTestLight")->mTransform.getTranslation(); - Model::getInstance("alienTest")->setUniform("uLight.position", position); - Model::getInstance("alienTest") - ->setUniform( - "uCameraPosition", - ExampleScene::Camera().transform().getTranslation()); - auto posMatrix = Model::getInstance("alienTest")->mTransform.toMatrix(); - Model::getInstance("alienTest") - ->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); - Model::getInstance("alienTest")->setUniform("uMVP.model", posMatrix); - Model::getInstance("alienTest") - ->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); - Model::getInstance("alienTest") - ->setUniform("uMVP.projection", ExampleScene::Projection()); - Model::getInstance("alienTest")->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); + auto alien = Model::getInstance("alienTest"); + alien->setUniform("uLight.position", position); + alien->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + auto posMatrix = alien->mTransform.toMatrix(); + alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); + alien->setUniform("uMVP.model", posMatrix); + alien->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); + alien->setUniform("uMVP.projection", ExampleScene::Projection()); + alien->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); position = MeshRenderer::getInstance("spartanTestLight") ->mTransform.getTranslation(); - Model::getInstance("spartanTest")->setUniform("uLight.position", position); - Model::getInstance("spartanTest") - ->setUniform( - "uCameraPosition", - ExampleScene::Camera().transform().getTranslation()); - posMatrix = Model::getInstance("spartanTest")->mTransform.toMatrix(); - Model::getInstance("spartanTest") - ->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); - Model::getInstance("spartanTest")->setUniform("uMVP.model", posMatrix); - Model::getInstance("spartanTest") - ->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); - Model::getInstance("spartanTest") - ->setUniform("uMVP.projection", ExampleScene::Projection()); - Model::getInstance("spartanTest")->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); + auto spartan = Model::getInstance("spartanTest"); + spartan->setUniform("uLight.position", position); + spartan->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + posMatrix = spartan->mTransform.toMatrix(); + spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); + spartan->setUniform("uMVP.model", posMatrix); + spartan->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); + spartan->setUniform("uMVP.projection", ExampleScene::Projection()); + spartan->mTransform.rotate(0.75f, 0.0f, 1.0f, 0.0f); - MeshRenderer::getInstance("testPhong") - ->mTransform.rotate(0.75f, 1.0f, 0.5f, 0.0f); - MeshRenderer::getInstance("testPhong")->mProgram.bind(); + auto phong = MeshRenderer::getInstance("testPhong"); + phong->mTransform.rotate(0.75f, 1.0f, 0.5f, 0.0f); + phong->bindShaders(); position = MeshRenderer::getInstance("testLight")->mTransform.getTranslation(); - MeshRenderer::getInstance("testPhong") - ->setUniform("uLight.position", position); - MeshRenderer::getInstance("testPhong") - ->setUniform( - "uCameraPosition", - ExampleScene::Camera().transform().getTranslation()); - posMatrix = MeshRenderer::getInstance("testPhong")->mTransform.toMatrix(); - MeshRenderer::getInstance("testPhong") - ->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); - MeshRenderer::getInstance("testPhong")->setUniform("uMVP.model", posMatrix); - MeshRenderer::getInstance("testPhong") - ->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); - MeshRenderer::getInstance("testPhong") - ->setUniform("uMVP.projection", ExampleScene::Projection()); - MeshRenderer::getInstance("testPhong")->mProgram.release(); + phong->setUniform("uLight.position", position); + phong->setUniform( + "uCameraPosition", ExampleScene::Camera().transform().getTranslation()); + posMatrix = phong->mTransform.toMatrix(); + phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix()); + phong->setUniform("uMVP.model", posMatrix); + phong->setUniform("uMVP.view", ExampleScene::Camera().toMatrix()); + phong->setUniform("uMVP.projection", ExampleScene::Projection()); + phong->releaseShaders(); // Rotate lighting example cubes mTestPhong->mTransform.rotate(0.75f, 0.5f, 0.3f, 0.2f); diff --git a/src/mesh.h b/src/mesh.h index b3fc21b..cd6a9e8 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -77,6 +77,7 @@ namespace Qtk { #define UV_RIGHT QVector2D(0.0f, 1.0f) #define UV_CORNER QVector2D(1.0f, 1.0f) + // TODO: Vertices.getData(); Vertices.getStride(); typedef std::vector Vertices; typedef std::vector Colors; typedef std::vector Indices; @@ -93,10 +94,10 @@ namespace Qtk { explicit ShapeBase( DrawMode mode = QTK_DRAW_ARRAYS, Vertices v = {}, Indices i = {}, Colors c = {}, TexCoords t = {}, Normals n = {}) : - mVertices(std::move(std::move(v))), - mColors(std::move(std::move(c))), mIndices(std::move(std::move(i))), - mTexCoords(std::move(std::move(t))), - mNormals(std::move(std::move(n))) {} + mDrawMode(mode), + mVertices(std::move(v)), mColors(std::move(c)), + mIndices(std::move(i)), mTexCoords(std::move(t)), + mNormals(std::move(n)) {} [[nodiscard]] inline const Vertices & getVertices() const { return mVertices; @@ -116,6 +117,10 @@ namespace Qtk { return mNormals; } + [[nodiscard]] inline size_t getTexCoordsStride() const { + return mTexCoords.size() * sizeof(mTexCoords[0]); + } + protected: DrawMode mDrawMode; diff --git a/src/meshrenderer.cpp b/src/meshrenderer.cpp index b14e338..1d8dc21 100644 --- a/src/meshrenderer.cpp +++ b/src/meshrenderer.cpp @@ -19,8 +19,7 @@ Qtk::MeshRenderer::MeshManager Qtk::MeshRenderer::sInstances; MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) : Object(name, shape), mVertexShader(":/multi-color.vert"), - mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES), - mHasTexture(false) { + mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES) { mShape = Shape(shape); init(); sInstances.insert(name, this); @@ -89,7 +88,7 @@ void MeshRenderer::init() { } void MeshRenderer::draw() { - mProgram.bind(); + bindShaders(); mVAO.bind(); if(mTexture.hasTexture()) { @@ -114,17 +113,19 @@ void MeshRenderer::draw() { } mVAO.release(); - mProgram.release(); + releaseShaders(); } void MeshRenderer::setShaders( const std::string & vert, const std::string & frag) { mVertexShader = vert; mFragmentShader = frag; + init(); } void MeshRenderer::setUniformMVP( const char * model, const char * view, const char * projection) { + ShaderBindScope lock(&mProgram, mBound); mProgram.setUniformValue(projection, Scene::Projection()); mProgram.setUniformValue(view, Scene::View()); mProgram.setUniformValue(model, mTransform.toMatrix()); @@ -142,6 +143,39 @@ void MeshRenderer::setColor(const QVector3D & color) { } } +void MeshRenderer::reallocateTexCoords(const TexCoords & t, unsigned dims) { + mVAO.bind(); + mNBO.destroy(); + mNBO.create(); + mNBO.bind(); + mNBO.allocate(t.data(), t.size() * sizeof(t[0])); + enableAttributeArray(1); + if(dims == 2) { + setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D)); + } else if(dims == 3) { + setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D)); + } + mNBO.release(); + mVAO.release(); +} + +void MeshRenderer::reallocateNormals(const Normals & n, unsigned dims) { + // TODO: Store state to track if buffer objects are bound + mVAO.bind(); + mNBO.destroy(); + mNBO.create(); + mNBO.bind(); + mNBO.allocate(n.data(), n.size() * sizeof(n[0])); + enableAttributeArray(1); + if(dims == 2) { + setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D)); + } else if(dims == 3) { + setAttributeBuffer(1, GL_FLOAT, 0, 3, sizeof(QVector3D)); + } + mNBO.release(); + mVAO.release(); +} + /******************************************************************************* * Inherited Virtual Member Functions ******************************************************************************/ diff --git a/src/meshrenderer.h b/src/meshrenderer.h index aa62d38..db5d2e2 100644 --- a/src/meshrenderer.h +++ b/src/meshrenderer.h @@ -15,6 +15,29 @@ #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: // Delegate constructors @@ -52,11 +75,13 @@ namespace Qtk { void setShaders(const std::string & vert, const std::string & frag); template inline void setUniform(int location, T value) { + ShaderBindScope lock(&mProgram, mBound); mProgram.setUniformValue(location, value); } template inline void setUniform(const char * location, T value) { + ShaderBindScope lock(&mProgram, mBound); mProgram.setUniformValue(location, value); } @@ -71,6 +96,26 @@ namespace Qtk { void setShape(const Shape & value) override; void setColor(const QVector3D & color); + void setAttributeBuffer( + int location, GLenum type, int offset, int tupleSize, + int stride = 0) { + ShaderBindScope lock(&mProgram, mBound); + mVAO.bind(); + mProgram.setAttributeBuffer(location, type, offset, tupleSize, stride); + mVAO.release(); + } + + inline void enableAttributeArray(int location) { + ShaderBindScope lock(&mProgram, mBound); + mVAO.bind(); + mProgram.enableAttributeArray(location); + mVAO.release(); + } + + void reallocateTexCoords(const TexCoords & t, unsigned dims = 2); + + void reallocateNormals(const Normals & n, unsigned dims = 3); + // Static QHash of all mesh objects within the scene typedef QHash MeshManager; @@ -78,7 +123,6 @@ namespace Qtk { static MeshManager sInstances; int mDrawType; - bool mHasTexture; std::string mVertexShader, mFragmentShader; }; } // namespace Qtk diff --git a/src/object.h b/src/object.h index 66889f1..90098f0 100644 --- a/src/object.h +++ b/src/object.h @@ -26,11 +26,12 @@ namespace Qtk { // Initialize an object with no shape data assigned explicit Object(const char * name) : - mName(name), mVBO(QOpenGLBuffer::VertexBuffer) {} + mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false) {} // Initialize an object with shape data assigned Object(const char * name, const ShapeBase & shape) : - mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape) {} + mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape), + mBound(false) {} ~Object() override = default; @@ -68,27 +69,46 @@ namespace Qtk { virtual inline void setTexture( const char * path, bool flipX = false, bool flipY = false) { - mTexture.setTexture( - OpenGLTextureFactory::initTexture2D(path, flipX, flipY)); + mTexture.setTexture(path, flipX, flipY); } - virtual inline void setTexture(QOpenGLTexture * value) { - mTexture.setTexture(value); + virtual inline void setCubeMap(const char * path) { + mTexture.setCubeMap(path); + } + + virtual inline void setTexture(const Texture & t) { + mTexture.setTexture(t.getPath()); } virtual inline void setVertices(const Vertices & value) { mShape.mVertices = value; } + virtual inline void bindShaders() { + mBound = true; + mProgram.bind(); + } + + virtual inline void releaseShaders() { + mBound = false; + mProgram.release(); + } + QOpenGLBuffer mVBO, mNBO; QOpenGLVertexArrayObject mVAO; - QOpenGLShaderProgram mProgram; 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/skybox.cpp b/src/skybox.cpp index 6ac5d9c..7088c35 100644 --- a/src/skybox.cpp +++ b/src/skybox.cpp @@ -21,7 +21,7 @@ Skybox::Skybox( mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()), mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData()) { init(); - mCubeMap = OpenGLTextureFactory::initCubeMap( + mTexture.setCubeMap( QImage(right.c_str()).mirrored(), QImage(top.c_str()), QImage(front.c_str()), QImage(left.c_str()), QImage(bottom.c_str()), QImage(back.c_str())); @@ -33,7 +33,7 @@ Skybox::Skybox(const std::string & name) : ":/back.png", name) {} Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) : - mCubeMap(cubeMap) { + mTexture(cubeMap) { init(); } @@ -47,7 +47,7 @@ void Skybox::draw() { mVAO.bind(); mProgram.bind(); - mCubeMap->bind(); + mTexture.getOpenGLTexture().bind(); mProgram.setUniformValue("uProjectionMatrix", Scene::Projection()); mProgram.setUniformValue("uViewMatrix", Scene::Camera().toMatrix()); @@ -55,7 +55,7 @@ void Skybox::draw() { glDrawElements( GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data()); - mCubeMap->release(); + mTexture.getOpenGLTexture().bind(); mProgram.release(); mVAO.release(); diff --git a/src/skybox.h b/src/skybox.h index 2d725c6..d206951 100644 --- a/src/skybox.h +++ b/src/skybox.h @@ -23,9 +23,6 @@ namespace Qtk { class QTKAPI Skybox : protected QOpenGLFunctions { public: // Delegate this constructor to use default skybox images - // + This allows creating a skybox with no arguments ( auto s = new - // Skybox; - // ) explicit Skybox(const std::string & name = "Skybox"); explicit Skybox( QOpenGLTexture * cubeMap, const std::string & name = "Skybox"); @@ -49,7 +46,7 @@ namespace Qtk { QOpenGLShaderProgram mProgram; QOpenGLVertexArrayObject mVAO; QOpenGLBuffer mVBO; - QOpenGLTexture * mCubeMap; + Texture mTexture; }; } // namespace Qtk diff --git a/src/texture.h b/src/texture.h index 694b437..2758fd6 100644 --- a/src/texture.h +++ b/src/texture.h @@ -10,6 +10,7 @@ #define QTOPENGL_TEXTURE_H #include +#include #include @@ -41,43 +42,72 @@ namespace Qtk { OpenGLTextureFactory() = default; }; + // TODO: Struct for (re)storing texture state class Texture { public: Texture() = default; - Texture(const Texture & value) = delete; - - // Texture(const Texture & value) { - // mOpenGLTexture = OpenGLTextureFactory::initTexture2D(value.mPath); - // mPath = value.mPath; - // } + Texture(const Texture & value) { + mOpenGLTexture = OpenGLTextureFactory::initTexture2D(value.mPath); + mPath = value.mPath; + } explicit Texture( const char * path, bool flipX = false, bool flipY = false) : mOpenGLTexture( OpenGLTextureFactory::initTexture2D(path, flipX, flipY)), mPath(path) {} + explicit Texture(QOpenGLTexture * texture) : mOpenGLTexture(texture) {} - // explicit Texture(QOpenGLTexture * texture) : mOpenGLTexture(texture) { - // } ~Texture() { mOpenGLTexture->destroy(); } [[nodiscard]] inline QOpenGLTexture & getOpenGLTexture() const { return *mOpenGLTexture; } + [[nodiscard]] inline std::string getPath() const { return mPath; } + + void setTexture( + const std::string & path, bool flipX = false, bool flipY = false) { + mOpenGLTexture = + OpenGLTextureFactory::initTexture2D(path.data(), flipX, flipY); + mPath = path.data(); + } + void setTexture( const char * path, bool flipX = false, bool flipY = false) { mOpenGLTexture = OpenGLTextureFactory::initTexture2D(path, flipX, flipY); + mPath = path; } + // TODO: This is unsafe because we don't have a path. Encapsulate it. inline void setTexture(QOpenGLTexture * texture) { mOpenGLTexture = texture; } + virtual inline void setCubeMap(const char * path) { + mOpenGLTexture = OpenGLTextureFactory::initCubeMap(path); + mPath = path; + } + + virtual inline void setCubeMap( + const char * right, const char * top, const char * front, + const char * left, const char * bottom, const char * back) { + mOpenGLTexture = OpenGLTextureFactory::initCubeMap( + right, top, front, left, bottom, back); + } + + virtual inline void setCubeMap( + const QImage & right, const QImage & top, const QImage & front, + const QImage & left, const QImage & bottom, const QImage & back) { + mOpenGLTexture = OpenGLTextureFactory::initCubeMap( + right, top, front, left, bottom, back); + } + [[nodiscard]] inline bool hasTexture() const { return mOpenGLTexture != Q_NULLPTR; } + private: QOpenGLTexture * mOpenGLTexture = Q_NULLPTR; const char * mPath {}; };