Factor out more resources.

+ Use QOpenGLShader to get compiled shader code in TreeView.
+ Remove last remaining Qt resources dependency in libqtk.
+ shaders.h in libqtk to define default GLSL shader programs.
This commit is contained in:
Shaun Reed 2025-03-16 20:06:27 -04:00
parent f7b807d3a5
commit bae408636e
24 changed files with 255 additions and 143 deletions

View File

@ -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) [![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) [![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. 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. [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 To get textures loading on models look
into [material files](http://www.paulbourke.net/dataformats/mtl/) 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 ### Source Builds

View File

@ -0,0 +1,22 @@
<RCC>
<qresource prefix="/textures">
<file alias="plaster.png">images/plaster.png</file>
<file alias="crate.png">images/crate.png</file>
<file alias="stone.png">images/stone.png</file>
<file alias="wood.png">images/wood.png</file>
<file>skybox/back.png</file>
<file>skybox/bottom.png</file>
<file>skybox/front.png</file>
<file>skybox/left.png</file>
<file>skybox/right.png</file>
<file>skybox/top.png</file>
</qresource>
<qresource prefix="/icons">
<file>fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</file>
<file>fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</file>
<file>fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</file>
<file>fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</file>
<file>fontawesome-free-6.2.1-desktop/svgs/brands/git-alt.svg</file>
<file alias="icon.png">icons/icon.png</file>
</qresource>
</RCC>

View File

@ -28,8 +28,6 @@
<file alias="solid.vert">shaders/vertex/solid.vert</file> <file alias="solid.vert">shaders/vertex/solid.vert</file>
<file alias="solid-perspective.frag">shaders/fragment/solid-perspective.frag</file> <file alias="solid-perspective.frag">shaders/fragment/solid-perspective.frag</file>
<file alias="solid-perspective.vert">shaders/vertex/solid-perspective.vert</file> <file alias="solid-perspective.vert">shaders/vertex/solid-perspective.vert</file>
<file alias="multi-color.frag">shaders/fragment/multi-color.frag</file>
<file alias="multi-color.vert">shaders/vertex/multi-color.vert</file>
<file alias="rgb-normals.frag">shaders/fragment/rgb-normals.frag</file> <file alias="rgb-normals.frag">shaders/fragment/rgb-normals.frag</file>
<file alias="rgb-normals.vert">shaders/vertex/rgb-normals.vert</file> <file alias="rgb-normals.vert">shaders/vertex/rgb-normals.vert</file>
<file alias="texture-cubemap.frag">shaders/fragment/texture-cubemap.frag</file> <file alias="texture-cubemap.frag">shaders/fragment/texture-cubemap.frag</file>
@ -44,13 +42,9 @@
<file alias="solid-specular.vert">shaders/vertex/solid-specular.vert</file> <file alias="solid-specular.vert">shaders/vertex/solid-specular.vert</file>
<file alias="solid-phong.frag">shaders/fragment/solid-phong.frag</file> <file alias="solid-phong.frag">shaders/fragment/solid-phong.frag</file>
<file alias="solid-phong.vert">shaders/vertex/solid-phong.vert</file> <file alias="solid-phong.vert">shaders/vertex/solid-phong.vert</file>
<file alias="model-basic.frag">shaders/fragment/model-basic.frag</file>
<file alias="model-basic.vert">shaders/vertex/model-basic.vert</file>
<file alias="model-phong.frag">shaders/fragment/model-phong.frag</file> <file alias="model-phong.frag">shaders/fragment/model-phong.frag</file>
<file alias="model-phong.vert">shaders/vertex/model-phong.vert</file> <file alias="model-phong.vert">shaders/vertex/model-phong.vert</file>
<file alias="model-normals.frag">shaders/fragment/model-normals.frag</file> <file alias="model-normals.frag">shaders/fragment/model-normals.frag</file>
<file alias="model-normals.vert">shaders/vertex/model-normals.vert</file> <file alias="model-normals.vert">shaders/vertex/model-normals.vert</file>
<file alias="skybox.frag">skybox/skybox.frag</file>
<file alias="skybox.vert">skybox/skybox.vert</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -1,11 +0,0 @@
#version 330 core
out vec4 fColor;
in vec2 vTextureCoord;
uniform sampler2D texture_diffuse1;
void main()
{
fColor = texture(texture_diffuse1, vTextureCoord);
}

View File

@ -1,9 +0,0 @@
#version 330
in vec4 vColor;
out vec4 fColor;
void main()
{
fColor = vColor;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -1,9 +0,0 @@
#version 330
uniform samplerCube uTexture;
varying vec3 vTexCoord;
void main()
{
gl_FragColor = texture(uTexture, vTexCoord);
}

View File

@ -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;
}

View File

@ -26,7 +26,17 @@ else()
) )
endif() 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}) qt_add_executable(qtk_gui ${QTK_GUI_SOURCES})
target_link_libraries(qtk_gui PRIVATE qtk_plugin_library) target_link_libraries(qtk_gui PRIVATE qtk_plugin_library)
if (QTK_GUI_SCENE) if (QTK_GUI_SCENE)

View File

@ -10,17 +10,9 @@
#include "qtkmainwindow.h" #include "qtkmainwindow.h"
#ifdef QTK_GUI_SCENE
#include "qtkscene.h"
using AppScene = QtkScene;
#else
using AppScene = EmptyScene;
#endif
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
Q_INIT_RESOURCE(resources); initResources();
QApplication a(argc, argv); QApplication a(argc, argv);
auto window = MainWindow::getMainWindow(); auto window = MainWindow::getMainWindow();

View File

@ -16,6 +16,21 @@
#include "designer-plugins/debugconsole.h" #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 namespace Ui
{ {
class MainWindow; class MainWindow;

View File

@ -42,8 +42,8 @@ void QtkScene::init()
// Clone qtk-resources if it doesn't already exist. // Clone qtk-resources if it doesn't already exist.
QDir repoDir("resources/"); QDir repoDir("resources/");
if (!repoDir.exists()) { if (!repoDir.exists()) {
qDebug() << "Cloning qtk-resources repository to '" qDebug() << "Cloning qtk-resources repository to " << repoDir.absolutePath()
<< repoDir.absolutePath() << "'..."; << "...";
// Run git clone // Run git clone
QProcess gitProcess; QProcess gitProcess;

View File

@ -130,13 +130,8 @@ void ToolBox::createPageShader(const Object * object)
auto shaderView = new QTextEdit; auto shaderView = new QTextEdit;
shaderView->setReadOnly(true); shaderView->setReadOnly(true);
auto vertexFile = QFile(object->getVertexShader().c_str()); shaderView->setText(object->getVertexShaderSourceCode().c_str());
if (vertexFile.exists()) { mainLayout->addRow(shaderView);
vertexFile.open(QIODeviceBase::ReadOnly);
shaderView->setText(vertexFile.readAll());
vertexFile.close();
mainLayout->addRow(shaderView);
}
rowLayout = new QHBoxLayout; rowLayout = new QHBoxLayout;
rowLayout->addWidget(new QLabel("Fragment Shader:")); rowLayout->addWidget(new QLabel("Fragment Shader:"));
@ -145,13 +140,8 @@ void ToolBox::createPageShader(const Object * object)
shaderView = new QTextEdit; shaderView = new QTextEdit;
shaderView->setReadOnly(true); shaderView->setReadOnly(true);
auto fragmentfile = QFile(object->getFragmentShader().c_str()); shaderView->setText(object->getFragmentShaderSourceCode().c_str());
if (fragmentfile.exists()) { mainLayout->addRow(shaderView);
fragmentfile.open(QIODeviceBase::ReadOnly);
shaderView->setText(fragmentfile.readAll());
fragmentfile.close();
mainLayout->addRow(shaderView);
}
widget->setLayout(mainLayout); widget->setLayout(mainLayout);
} }

View File

@ -24,6 +24,7 @@ set(
skybox.h skybox.h
texture.h texture.h
transform3D.h transform3D.h
shaders.h
) )
set( set(
@ -43,7 +44,6 @@ set(
transform3D.cpp transform3D.cpp
) )
qt6_add_big_resources(QTK_LIBRARY_SOURCES "${QTK_RESOURCES}/resources.qrc")
qt_add_library(qtk STATIC EXCLUDE_FROM_ALL) qt_add_library(qtk STATIC EXCLUDE_FROM_ALL)
target_sources(qtk PRIVATE ${QTK_LIBRARY_SOURCES}) target_sources(qtk PRIVATE ${QTK_LIBRARY_SOURCES})
target_sources( target_sources(

View File

@ -10,6 +10,7 @@
#include "meshrenderer.h" #include "meshrenderer.h"
#include "scene.h" #include "scene.h"
#include "shaders.h"
#include "texture.h" #include "texture.h"
using namespace Qtk; using namespace Qtk;
@ -35,8 +36,7 @@ MeshRenderer::MeshRenderer(const char * name) :
} }
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) : MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
Object(name, shape, QTK_MESH), mVertexShader(":/shaders/multi-color.vert"), Object(name, shape, QTK_MESH), mDrawType(GL_TRIANGLES)
mFragmentShader(":/shaders/multi-color.frag"), mDrawType(GL_TRIANGLES)
{ {
mShape = Shape(shape); mShape = Shape(shape);
init(); init();
@ -68,10 +68,22 @@ void MeshRenderer::init()
mVAO.bind(); mVAO.bind();
mProgram.create(); mProgram.create();
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, // If no shader is provided, use a default one.
mVertexShader.c_str()); if (mVertexShader.empty()) {
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, mProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,
mFragmentShader.c_str()); 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.link();
mProgram.bind(); mProgram.bind();

View File

@ -55,8 +55,8 @@ namespace Qtk
*/ */
inline Model(const char * name, inline Model(const char * name,
const char * path, const char * path,
const char * vertexShader = ":/shaders/model-basic.vert", const char * vertexShader = "",
const char * fragmentShader = ":/shaders/model-basic.frag") : const char * fragmentShader = "") :
Object(name, QTK_MODEL), mModelPath(path), Object(name, QTK_MODEL), mModelPath(path),
mVertexShader(vertexShader), mFragmentShader(fragmentShader) mVertexShader(vertexShader), mFragmentShader(fragmentShader)
{ {

View File

@ -8,6 +8,7 @@
#include "modelmesh.h" #include "modelmesh.h"
#include "scene.h" #include "scene.h"
#include "shaders.h"
using namespace Qtk; using namespace Qtk;
@ -73,12 +74,12 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader)
* Private Member Functions * Private Member Functions
******************************************************************************/ ******************************************************************************/
void ModelMesh::initMesh(const char * vert, const char * frag) void ModelMesh::initMesh(const std::string & vert, const std::string & frag)
{ {
initializeOpenGLFunctions(); initializeOpenGLFunctions();
// Create VAO, VBO, EBO // Create VAO, VBO, EBO
bool status = mVAO->create(); mVAO->create();
mVBO->create(); mVBO->create();
mEBO->create(); mEBO->create();
@ -97,10 +98,26 @@ void ModelMesh::initMesh(const char * vert, const char * frag)
mEBO->release(); mEBO->release();
// Load and link shaders // Load and link shaders
mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert); if (!vert.empty()) {
mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag); mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert.c_str());
mProgram->link(); } else {
mProgram->bind(); 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 // Positions
mProgram->enableAttributeArray(0); mProgram->enableAttributeArray(0);

View File

@ -97,8 +97,8 @@ namespace Qtk
ModelMesh(Vertices vertices, ModelMesh(Vertices vertices,
Indices indices, Indices indices,
Textures textures, Textures textures,
const char * vertexShader = ":/model-basic.vert", const char * vertexShader = "",
const char * fragmentShader = ":/model-basic.frag") : const char * fragmentShader = "") :
mProgram(new QOpenGLShaderProgram), mProgram(new QOpenGLShaderProgram),
mVAO(new QOpenGLVertexArrayObject), mVAO(new QOpenGLVertexArrayObject),
mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)), mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)),
@ -146,7 +146,7 @@ namespace Qtk
* @param vert Path to vertex shader to use for this model. * @param vert Path to vertex shader to use for this model.
* @param frag Path to fragment 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 * Private Members

View File

@ -9,3 +9,15 @@
#include "object.h" #include "object.h"
using namespace Qtk; 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 "";
}

View File

@ -51,7 +51,6 @@ namespace Qtk
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false), mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false),
mType(type) mType(type)
{ {
initResources();
setObjectName(name); setObjectName(name);
} }
@ -60,7 +59,6 @@ namespace Qtk
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape), mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape),
mBound(false), mType(type) mBound(false), mType(type)
{ {
initResources();
setObjectName(name); setObjectName(name);
} }
@ -126,6 +124,21 @@ namespace Qtk
return "Base Object has no fragment shader."; 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 * Setters
************************************************************************/ ************************************************************************/

View File

@ -22,16 +22,6 @@
#define QTKAPI #define QTKAPI
#endif #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 namespace Qtk
{ {
/** /**

120
src/qtk/shaders.h Normal file
View File

@ -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

View File

@ -8,6 +8,7 @@
#include "skybox.h" #include "skybox.h"
#include "scene.h" #include "scene.h"
#include "shaders.h"
#include "texture.h" #include "texture.h"
using namespace Qtk; using namespace Qtk;
@ -80,10 +81,10 @@ void Skybox::init()
// Set up shader program // Set up shader program
mProgram.create(); mProgram.create();
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, mProgram.addShaderFromSourceCode(QOpenGLShader::Fragment,
":/shaders/skybox.vert"); QTK_SHADER_FRAGMENT_SKYBOX);
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, mProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,
":/shaders/skybox.frag"); QTK_SHADER_VERTEX_SKYBOX);
mProgram.link(); mProgram.link();
mProgram.bind(); mProgram.bind();