mudlet/src/ShaderManager.cpp
Vadim Peretokin b6738dd8c2
infrastructure: Apply clang-format to all CPP files (#8804)
#### Brief overview of PR changes/additions
Ran clang-format on all 134 CPP files in src/ using the project's
.clang-format config

#### Motivation for adding to Mudlet
Ensures consistent code formatting across the codebase.

#### Other info (issues closed, discussion etc)
None

**Test case:** Build the project and verify it compiles successfully.
2026-01-19 18:10:44 +01:00

249 lines
8.7 KiB
C++

/***************************************************************************
* Copyright (C) 2025 by Vadim Peretokin - vadim.peretokin@mudlet.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "ShaderManager.h"
#include "ResourceManager.h"
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QStandardPaths>
#include <QCoreApplication>
#include <chrono>
#include "utils.h"
ShaderManager::ShaderManager(ResourceManager* resourceManager, QObject* parent)
: QObject(parent)
, mDevelopmentMode(false)
, mInitialized(false)
, mUniformMVP(-1)
, mUniformModel(-1)
, mUniformNormalMatrix(-1)
, mResourceManager(resourceManager)
{
mReloadTimer = new QTimer(this);
mReloadTimer->setSingleShot(true);
mReloadTimer->setInterval(std::chrono::milliseconds(50));
connect(mReloadTimer, &QTimer::timeout, this, &ShaderManager::delayedReload);
}
ShaderManager::~ShaderManager()
{
cleanup();
}
bool ShaderManager::initialize()
{
if (mInitialized) {
return true;
}
initializeOpenGLFunctions();
mDevelopmentMode = detectDevelopmentMode();
qDebug() << "ShaderManager: Hot-reload" << (mDevelopmentMode ? "enabled" : "disabled (using embedded shaders)");
if (mDevelopmentMode) {
// Try multiple well-known locations for shader files
QStringList possiblePaths = {// CMake build from build/src/
qsl("../src/shaders/"),
// CMake build from build/
qsl("src/shaders/"),
// In-source build
qsl("../../src/shaders/"),
// Qt Creator build
qsl("../../../src/shaders/"),
// Relative to current directory
qsl("shaders/")};
QString appDir = QCoreApplication::applicationDirPath();
QString foundPath;
for (const QString& relativePath : possiblePaths) {
QString testPath = QDir(appDir).filePath(relativePath);
QDir testDir(testPath);
if (testDir.exists() && QFile::exists(testDir.filePath(qsl("vertex.glsl"))) && QFile::exists(testDir.filePath(qsl("fragment.glsl")))) {
foundPath = testPath;
break;
}
}
if (foundPath.isEmpty()) {
qWarning() << "ShaderManager: Could not find shader directory, trying fallback locations";
mVertexShaderPath = QDir(appDir).canonicalPath() + qsl("/../src/shaders/vertex.glsl");
mFragmentShaderPath = QDir(appDir).canonicalPath() + qsl("/../src/shaders/fragment.glsl");
} else {
mVertexShaderPath = QDir(foundPath).canonicalPath() + qsl("/vertex.glsl");
mFragmentShaderPath = QDir(foundPath).canonicalPath() + qsl("/fragment.glsl");
}
mFileWatcher = new QFileSystemWatcher(this);
if (QFile::exists(mVertexShaderPath)) {
mFileWatcher->addPath(mVertexShaderPath);
}
if (QFile::exists(mFragmentShaderPath)) {
mFileWatcher->addPath(mFragmentShaderPath);
}
connect(mFileWatcher, &QFileSystemWatcher::fileChanged, this, &ShaderManager::onShaderFileChanged);
qDebug() << "ShaderManager: Watching shader files:";
qDebug() << " Vertex:" << mVertexShaderPath << "(exists:" << QFile::exists(mVertexShaderPath) << ")";
qDebug() << " Fragment:" << mFragmentShaderPath << "(exists:" << QFile::exists(mFragmentShaderPath) << ")";
}
mInitialized = createShaderProgram();
return mInitialized;
}
QOpenGLShaderProgram* ShaderManager::getMainShaderProgram()
{
if (!mInitialized || !mShaderProgram) {
qWarning() << "ShaderManager: Shader program not initialized";
return nullptr;
}
return mShaderProgram.get();
}
bool ShaderManager::reloadShaders()
{
qDebug() << "ShaderManager: Reloading shaders...";
mShaderProgram.reset();
mUniformMVP = -1;
mUniformModel = -1;
mUniformNormalMatrix = -1;
bool success = createShaderProgram();
if (success) {
qDebug() << "ShaderManager: Shaders reloaded successfully";
emit shadersReloaded();
} else {
qWarning() << "ShaderManager: Failed to reload shaders";
}
return success;
}
void ShaderManager::cleanup()
{
mShaderProgram.reset();
mInitialized = false;
}
void ShaderManager::onShaderFileChanged(const QString& path)
{
qDebug() << "ShaderManager: Shader file changed:" << path;
if (mFileWatcher) {
mFileWatcher->addPath(path);
}
mReloadTimer->start();
}
void ShaderManager::delayedReload()
{
reloadShaders();
}
QString ShaderManager::loadShaderSource(const QString& shaderName)
{
QString source;
if (mDevelopmentMode) {
QString filePath = (shaderName == qsl("vertex")) ? mVertexShaderPath : mFragmentShaderPath;
QFile file(filePath);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
source = file.readAll();
qDebug() << "ShaderManager: Loaded" << shaderName << "shader from file:" << filePath;
} else {
qWarning() << "ShaderManager: Failed to load" << shaderName << "shader from file:" << filePath;
}
} else {
QString resourcePath = qsl(":/shaders/%1.glsl").arg(shaderName);
QFile file(resourcePath);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
source = file.readAll();
qDebug() << "ShaderManager: Loaded" << shaderName << "shader from resource:" << resourcePath;
} else {
qWarning() << "ShaderManager: Failed to load" << shaderName << "shader from resource:" << resourcePath;
}
}
return source;
}
bool ShaderManager::createShaderProgram()
{
mShaderProgram = std::make_unique<QOpenGLShaderProgram>();
QString vertexSource = loadShaderSource(qsl("vertex"));
QString fragmentSource = loadShaderSource(qsl("fragment"));
if (vertexSource.isEmpty() || fragmentSource.isEmpty()) {
qWarning() << "ShaderManager: Empty shader sources";
return false;
}
if (!mShaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource)) {
qWarning() << "ShaderManager: Failed to compile vertex shader:" << mShaderProgram->log();
return false;
}
if (mResourceManager) {
mResourceManager->onShaderCreated();
}
if (!mShaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource)) {
qWarning() << "ShaderManager: Failed to compile fragment shader:" << mShaderProgram->log();
return false;
}
if (mResourceManager) {
mResourceManager->onShaderCreated();
}
if (!mShaderProgram->link()) {
qWarning() << "ShaderManager: Failed to link shader program:" << mShaderProgram->log();
return false;
}
mUniformMVP = mShaderProgram->uniformLocation(qsl("uMVP"));
mUniformModel = mShaderProgram->uniformLocation(qsl("uModel"));
mUniformNormalMatrix = mShaderProgram->uniformLocation(qsl("uNormalMatrix"));
if (mUniformMVP == -1) {
qWarning() << "ShaderManager: Failed to get MVP uniform location";
return false;
}
qDebug() << "ShaderManager: Shaders compiled successfully. Uniform locations - MVP:" << mUniformMVP << "Model:" << mUniformModel << "Normal:" << mUniformNormalMatrix;
return true;
}
bool ShaderManager::detectDevelopmentMode()
{
#ifdef MUDLET_SHADER_HOT_RELOAD
return true;
#else
return false;
#endif
}