diff --git a/README.md b/README.md index df4870d..95b1315 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![All Builds](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/build.yml) [![Linting](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml/badge.svg)](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml) -Qtk desktop application provides a model loader using [Assimp](https://assimp.org/) within a Qt widget application. +The Qtk desktop application provides a model loader using [Assimp](https://assimp.org/) within a Qt widget application. You can fly around the scene using WASD while holding down the left or right mouse button. [QtkWidget](./src/designer-plugins/qtkwidget.h) is the primary QOpenGLWidget used to render the scene and handle input. @@ -72,7 +72,7 @@ project in the root of this repository. To get textures loading on models look into [material files](http://www.paulbourke.net/dataformats/mtl/) -and see some examples in the `resources/models/` directory. +and see some examples at [qtk-resources/resources/models](https://git.shaunreed.com/shaunrd0/qtk-resources/src/branch/master/models). ### Source Builds diff --git a/resources/minimal_resources.qrc b/resources/minimal_resources.qrc new file mode 100644 index 0000000..cf719b1 --- /dev/null +++ b/resources/minimal_resources.qrc @@ -0,0 +1,22 @@ + + + images/plaster.png + images/crate.png + images/stone.png + images/wood.png + skybox/back.png + skybox/bottom.png + skybox/front.png + skybox/left.png + skybox/right.png + skybox/top.png + + + fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg + fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg + fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg + fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg + fontawesome-free-6.2.1-desktop/svgs/brands/git-alt.svg + icons/icon.png + + diff --git a/resources/resources.qrc b/resources/resources.qrc index 551eed7..7f88b11 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -28,8 +28,6 @@ shaders/vertex/solid.vert shaders/fragment/solid-perspective.frag shaders/vertex/solid-perspective.vert - shaders/fragment/multi-color.frag - shaders/vertex/multi-color.vert shaders/fragment/rgb-normals.frag shaders/vertex/rgb-normals.vert shaders/fragment/texture-cubemap.frag @@ -44,13 +42,9 @@ shaders/vertex/solid-specular.vert shaders/fragment/solid-phong.frag shaders/vertex/solid-phong.vert - shaders/fragment/model-basic.frag - shaders/vertex/model-basic.vert shaders/fragment/model-phong.frag shaders/vertex/model-phong.vert shaders/fragment/model-normals.frag shaders/vertex/model-normals.vert - skybox/skybox.frag - skybox/skybox.vert diff --git a/resources/shaders/fragment/model-basic.frag b/resources/shaders/fragment/model-basic.frag deleted file mode 100644 index 8af8279..0000000 --- a/resources/shaders/fragment/model-basic.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 330 core -out vec4 fColor; - -in vec2 vTextureCoord; - -uniform sampler2D texture_diffuse1; - -void main() -{ - fColor = texture(texture_diffuse1, vTextureCoord); -} diff --git a/resources/shaders/fragment/multi-color.frag b/resources/shaders/fragment/multi-color.frag deleted file mode 100644 index 896bda7..0000000 --- a/resources/shaders/fragment/multi-color.frag +++ /dev/null @@ -1,9 +0,0 @@ -#version 330 -in vec4 vColor; - -out vec4 fColor; - -void main() -{ - fColor = vColor; -} diff --git a/resources/shaders/vertex/model-basic.vert b/resources/shaders/vertex/model-basic.vert deleted file mode 100644 index 3c305ab..0000000 --- a/resources/shaders/vertex/model-basic.vert +++ /dev/null @@ -1,16 +0,0 @@ -#version 330 core -layout (location = 0) in vec3 aPosition; -layout (location = 1) in vec3 aNormal; -layout (location = 2) in vec2 aTextureCoord; - -out vec2 vTextureCoord; - -uniform mat4 uModel; -uniform mat4 uView; -uniform mat4 uProjection; - -void main() -{ - vTextureCoord = aTextureCoord; - gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0); -} diff --git a/resources/shaders/vertex/multi-color.vert b/resources/shaders/vertex/multi-color.vert deleted file mode 100644 index 6d4da59..0000000 --- a/resources/shaders/vertex/multi-color.vert +++ /dev/null @@ -1,16 +0,0 @@ -#version 330 -layout(location = 0) in vec3 aPosition; -layout(location = 1) in vec3 aColor; - -out vec4 vColor; - -uniform mat4 uModel; // Model -uniform mat4 uView; // View -uniform mat4 uProjection; // Projection - -void main() -{ - gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0); - - vColor = vec4(aColor, 1.0f); -} diff --git a/resources/skybox/skybox.frag b/resources/skybox/skybox.frag deleted file mode 100644 index de8ed1b..0000000 --- a/resources/skybox/skybox.frag +++ /dev/null @@ -1,9 +0,0 @@ -#version 330 -uniform samplerCube uTexture; - -varying vec3 vTexCoord; - -void main() -{ - gl_FragColor = texture(uTexture, vTexCoord); -} \ No newline at end of file diff --git a/resources/skybox/skybox.vert b/resources/skybox/skybox.vert deleted file mode 100644 index 28d9110..0000000 --- a/resources/skybox/skybox.vert +++ /dev/null @@ -1,15 +0,0 @@ -#version 330 -layout(location = 0) in vec3 aPosition; - -out vec3 vTexCoord; - -uniform mat4 uProjectionMatrix; -uniform mat4 uViewMatrix; - -void main() -{ - // Strip translation column from camera's 4x4 matrix - mat4 view = mat4(mat3(uViewMatrix)); - gl_Position = uProjectionMatrix * view * vec4(aPosition, 1.0); - vTexCoord = aPosition; -} \ No newline at end of file diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index c42ab1a..5883f2f 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -26,7 +26,17 @@ else() ) endif() +if(QTK_GUI_SCENE) + qt6_add_big_resources(QTK_GUI_SOURCES "${QTK_RESOURCES}/resources.qrc") +else() + qt6_add_big_resources( + QTK_GUI_SOURCES + "${QTK_RESOURCES}/minimal_resources.qrc" + ) +endif() + qt_add_executable(qtk_gui ${QTK_GUI_SOURCES}) + target_link_libraries(qtk_gui PRIVATE qtk_plugin_library) if (QTK_GUI_SCENE) diff --git a/src/app/main.cpp b/src/app/main.cpp index 58fbca7..11ce085 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -10,17 +10,9 @@ #include "qtkmainwindow.h" -#ifdef QTK_GUI_SCENE -#include "qtkscene.h" -using AppScene = QtkScene; -#else -using AppScene = EmptyScene; -#endif - int main(int argc, char * argv[]) { - Q_INIT_RESOURCE(resources); - + initResources(); QApplication a(argc, argv); auto window = MainWindow::getMainWindow(); diff --git a/src/app/qtkmainwindow.h b/src/app/qtkmainwindow.h index 781ad7e..2c3e274 100644 --- a/src/app/qtkmainwindow.h +++ b/src/app/qtkmainwindow.h @@ -16,6 +16,21 @@ #include "designer-plugins/debugconsole.h" +#ifdef QTK_GUI_SCENE +#include "qtkscene.h" +using AppScene = QtkScene; +inline void initResources() +{ + Q_INIT_RESOURCE(resources); +} +#else +using AppScene = EmptyScene; +inline void initResources() +{ + Q_INIT_RESOURCE(minimal_resources); +} +#endif + namespace Ui { class MainWindow; diff --git a/src/app/qtkscene.cpp b/src/app/qtkscene.cpp index 9de11bb..527c226 100644 --- a/src/app/qtkscene.cpp +++ b/src/app/qtkscene.cpp @@ -42,8 +42,8 @@ void QtkScene::init() // Clone qtk-resources if it doesn't already exist. QDir repoDir("resources/"); if (!repoDir.exists()) { - qDebug() << "Cloning qtk-resources repository to '" - << repoDir.absolutePath() << "'..."; + qDebug() << "Cloning qtk-resources repository to " << repoDir.absolutePath() + << "..."; // Run git clone QProcess gitProcess; diff --git a/src/designer-plugins/toolbox.cpp b/src/designer-plugins/toolbox.cpp index e03213a..b59b55e 100644 --- a/src/designer-plugins/toolbox.cpp +++ b/src/designer-plugins/toolbox.cpp @@ -130,13 +130,8 @@ void ToolBox::createPageShader(const Object * object) auto shaderView = new QTextEdit; shaderView->setReadOnly(true); - auto vertexFile = QFile(object->getVertexShader().c_str()); - if (vertexFile.exists()) { - vertexFile.open(QIODeviceBase::ReadOnly); - shaderView->setText(vertexFile.readAll()); - vertexFile.close(); - mainLayout->addRow(shaderView); - } + shaderView->setText(object->getVertexShaderSourceCode().c_str()); + mainLayout->addRow(shaderView); rowLayout = new QHBoxLayout; rowLayout->addWidget(new QLabel("Fragment Shader:")); @@ -145,13 +140,8 @@ void ToolBox::createPageShader(const Object * object) shaderView = new QTextEdit; shaderView->setReadOnly(true); - auto fragmentfile = QFile(object->getFragmentShader().c_str()); - if (fragmentfile.exists()) { - fragmentfile.open(QIODeviceBase::ReadOnly); - shaderView->setText(fragmentfile.readAll()); - fragmentfile.close(); - mainLayout->addRow(shaderView); - } + shaderView->setText(object->getFragmentShaderSourceCode().c_str()); + mainLayout->addRow(shaderView); widget->setLayout(mainLayout); } diff --git a/src/qtk/CMakeLists.txt b/src/qtk/CMakeLists.txt index 25ca8bc..34c7cd3 100644 --- a/src/qtk/CMakeLists.txt +++ b/src/qtk/CMakeLists.txt @@ -24,6 +24,7 @@ set( skybox.h texture.h transform3D.h + shaders.h ) set( @@ -43,7 +44,6 @@ set( transform3D.cpp ) -qt6_add_big_resources(QTK_LIBRARY_SOURCES "${QTK_RESOURCES}/resources.qrc") qt_add_library(qtk STATIC EXCLUDE_FROM_ALL) target_sources(qtk PRIVATE ${QTK_LIBRARY_SOURCES}) target_sources( diff --git a/src/qtk/meshrenderer.cpp b/src/qtk/meshrenderer.cpp index 04f1ea9..fd8cb12 100644 --- a/src/qtk/meshrenderer.cpp +++ b/src/qtk/meshrenderer.cpp @@ -10,6 +10,7 @@ #include "meshrenderer.h" #include "scene.h" +#include "shaders.h" #include "texture.h" using namespace Qtk; @@ -35,8 +36,7 @@ MeshRenderer::MeshRenderer(const char * name) : } MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) : - Object(name, shape, QTK_MESH), mVertexShader(":/shaders/multi-color.vert"), - mFragmentShader(":/shaders/multi-color.frag"), mDrawType(GL_TRIANGLES) + Object(name, shape, QTK_MESH), mDrawType(GL_TRIANGLES) { mShape = Shape(shape); init(); @@ -68,10 +68,22 @@ void MeshRenderer::init() mVAO.bind(); mProgram.create(); - mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, - mVertexShader.c_str()); - mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, - mFragmentShader.c_str()); + // If no shader is provided, use a default one. + if (mVertexShader.empty()) { + mProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, + QTK_SHADER_VERTEX_MESH); + } else { + mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, + mVertexShader.c_str()); + } + + if (mFragmentShader.empty()) { + mProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, + QTK_SHADER_FRAGMENT_MESH); + } else { + mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, + mFragmentShader.c_str()); + } mProgram.link(); mProgram.bind(); diff --git a/src/qtk/model.h b/src/qtk/model.h index d87f1cf..ee1ed1d 100644 --- a/src/qtk/model.h +++ b/src/qtk/model.h @@ -55,8 +55,8 @@ namespace Qtk */ inline Model(const char * name, const char * path, - const char * vertexShader = ":/shaders/model-basic.vert", - const char * fragmentShader = ":/shaders/model-basic.frag") : + const char * vertexShader = "", + const char * fragmentShader = "") : Object(name, QTK_MODEL), mModelPath(path), mVertexShader(vertexShader), mFragmentShader(fragmentShader) { diff --git a/src/qtk/modelmesh.cpp b/src/qtk/modelmesh.cpp index 51e71a5..d7aa0cb 100644 --- a/src/qtk/modelmesh.cpp +++ b/src/qtk/modelmesh.cpp @@ -8,6 +8,7 @@ #include "modelmesh.h" #include "scene.h" +#include "shaders.h" using namespace Qtk; @@ -73,12 +74,12 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader) * Private Member Functions ******************************************************************************/ -void ModelMesh::initMesh(const char * vert, const char * frag) +void ModelMesh::initMesh(const std::string & vert, const std::string & frag) { initializeOpenGLFunctions(); // Create VAO, VBO, EBO - bool status = mVAO->create(); + mVAO->create(); mVBO->create(); mEBO->create(); @@ -97,10 +98,26 @@ void ModelMesh::initMesh(const char * vert, const char * frag) mEBO->release(); // Load and link shaders - mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert); - mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag); - mProgram->link(); - mProgram->bind(); + if (!vert.empty()) { + mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert.c_str()); + } else { + mProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, + QTK_SHADER_VERTEX_MODEL); + } + + if (!frag.empty()) { + mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag.c_str()); + } else { + mProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, + QTK_SHADER_FRAGMENT_MODEL); + } + + if (!mProgram->link()) { + qDebug() << "Failed to link shader: " << mProgram->log(); + } + if (!mProgram->bind()) { + qDebug() << "Failed to bind shader: " << mProgram->log(); + } // Positions mProgram->enableAttributeArray(0); diff --git a/src/qtk/modelmesh.h b/src/qtk/modelmesh.h index e2dc5b5..eda2979 100644 --- a/src/qtk/modelmesh.h +++ b/src/qtk/modelmesh.h @@ -97,8 +97,8 @@ namespace Qtk ModelMesh(Vertices vertices, Indices indices, Textures textures, - const char * vertexShader = ":/model-basic.vert", - const char * fragmentShader = ":/model-basic.frag") : + const char * vertexShader = "", + const char * fragmentShader = "") : mProgram(new QOpenGLShaderProgram), mVAO(new QOpenGLVertexArrayObject), mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)), @@ -146,7 +146,7 @@ namespace Qtk * @param vert Path to vertex shader to use for this model. * @param frag Path to fragment shader to use for this model. */ - void initMesh(const char * vert, const char * frag); + void initMesh(const std::string & vert, const std::string & frag); /************************************************************************* * Private Members diff --git a/src/qtk/object.cpp b/src/qtk/object.cpp index 7c9993e..0510477 100644 --- a/src/qtk/object.cpp +++ b/src/qtk/object.cpp @@ -9,3 +9,15 @@ #include "object.h" using namespace Qtk; + +std::string Object::getShaderSourceCode( + QOpenGLShader::ShaderType shader_type) const +{ + for (const auto & shader : mProgram.shaders()) { + if (shader->shaderType() == shader_type) { + return shader->sourceCode().toStdString(); + } + } + qDebug() << "Failed to find shader of type " << shader_type; + return ""; +} diff --git a/src/qtk/object.h b/src/qtk/object.h index 2046bd7..342c3e6 100644 --- a/src/qtk/object.h +++ b/src/qtk/object.h @@ -51,7 +51,6 @@ namespace Qtk mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false), mType(type) { - initResources(); setObjectName(name); } @@ -60,7 +59,6 @@ namespace Qtk mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape), mBound(false), mType(type) { - initResources(); setObjectName(name); } @@ -126,6 +124,21 @@ namespace Qtk return "Base Object has no fragment shader."; } + [[nodiscard]] virtual std::string getShaderSourceCode( + QOpenGLShader::ShaderType shader_type) const; + + + [[nodiscard]] virtual inline std::string getVertexShaderSourceCode() const + { + return getShaderSourceCode(QOpenGLShader::Vertex); + } + + [[nodiscard]] virtual inline std::string getFragmentShaderSourceCode() + const + { + return getShaderSourceCode(QOpenGLShader::Fragment); + } + /************************************************************************* * Setters ************************************************************************/ diff --git a/src/qtk/qtkapi.h b/src/qtk/qtkapi.h index 82ab817..9464720 100644 --- a/src/qtk/qtkapi.h +++ b/src/qtk/qtkapi.h @@ -22,16 +22,6 @@ #define QTKAPI #endif -/** - * Initialize Qt resources required by the Qtk library. - * This cannot be defined within any namespace, but can be called by ctors. - * See object.h for example. - */ -inline void initResources() -{ - Q_INIT_RESOURCE(resources); -} - namespace Qtk { /** diff --git a/src/qtk/shaders.h b/src/qtk/shaders.h new file mode 100644 index 0000000..2dc6ff4 --- /dev/null +++ b/src/qtk/shaders.h @@ -0,0 +1,120 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ## +## About: Default GLSL shaders to use for objects if no shader if provided. ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +##############################################################################*/ +#ifndef QTK_SHADERS_H +#define QTK_SHADERS_H + +// +// Model + +#define QTK_SHADER_VERTEX_MODEL \ + R"( +#version 330 core +layout (location = 0) in vec3 aPosition; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTextureCoord; + +out vec2 vTextureCoord; + +uniform mat4 uModel; +uniform mat4 uView; +uniform mat4 uProjection; + +void main() +{ + vTextureCoord = aTextureCoord; + gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0); +} +)" + +#define QTK_SHADER_FRAGMENT_MODEL \ + R"( +#version 330 core +out vec4 fColor; + +in vec2 vTextureCoord; + +uniform sampler2D texture_diffuse1; + +void main() +{ + fColor = texture(texture_diffuse1, vTextureCoord); +} +)" + +// +// MeshRenderer + +#define QTK_SHADER_VERTEX_MESH \ + R"( +#version 330 +layout(location = 0) in vec3 aPosition; +layout(location = 1) in vec3 aColor; + +out vec4 vColor; + +uniform mat4 uModel; // Model +uniform mat4 uView; // View +uniform mat4 uProjection; // Projection + +void main() +{ + gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0); + + vColor = vec4(aColor, 1.0f); +} +)" + +#define QTK_SHADER_FRAGMENT_MESH \ + R"( +#version 330 +in vec4 vColor; + +out vec4 fColor; + +void main() +{ + fColor = vColor; +} +)" + +// +// Skybox + +#define QTK_SHADER_VERTEX_SKYBOX \ + R"( +#version 330 +layout(location = 0) in vec3 aPosition; + +out vec3 vTexCoord; + +uniform mat4 uProjectionMatrix; +uniform mat4 uViewMatrix; + +void main() +{ + // Strip translation column from camera's 4x4 matrix + mat4 view = mat4(mat3(uViewMatrix)); + gl_Position = uProjectionMatrix * view * vec4(aPosition, 1.0); + vTexCoord = aPosition; +} +)" + +#define QTK_SHADER_FRAGMENT_SKYBOX \ + R"( +#version 330 +uniform samplerCube uTexture; + +varying vec3 vTexCoord; + +void main() +{ + gl_FragColor = texture(uTexture, vTexCoord); +} +)" + +#endif // QTK_SHADERS_H diff --git a/src/qtk/skybox.cpp b/src/qtk/skybox.cpp index c02323e..78b4eee 100644 --- a/src/qtk/skybox.cpp +++ b/src/qtk/skybox.cpp @@ -8,6 +8,7 @@ #include "skybox.h" #include "scene.h" +#include "shaders.h" #include "texture.h" using namespace Qtk; @@ -80,10 +81,10 @@ void Skybox::init() // Set up shader program mProgram.create(); - mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, - ":/shaders/skybox.vert"); - mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, - ":/shaders/skybox.frag"); + mProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, + QTK_SHADER_FRAGMENT_SKYBOX); + mProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, + QTK_SHADER_VERTEX_SKYBOX); mProgram.link(); mProgram.bind();