1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-29 09:32:45 +00:00

Introduce ShaderManager & ShaderVisitor

Actual shaders still to be written.
This commit is contained in:
scrawl 2016-02-16 18:18:48 +01:00
parent e647ee5424
commit a9ad1b09e2
11 changed files with 436 additions and 0 deletions

View File

@ -168,6 +168,7 @@ namespace MWRender
, mFieldOfViewOverridden(false) , mFieldOfViewOverridden(false)
{ {
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager; osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager;
sceneRoot->setLightingMask(Mask_Lighting); sceneRoot->setLightingMask(Mask_Lighting);

View File

@ -44,6 +44,10 @@ add_component_dir (resource
scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager
) )
add_component_dir (shader
shadermanager shadervisitor
)
add_component_dir (sceneutil add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
lightmanager lightutil positionattitudetransform workqueue unrefqueue lightmanager lightutil positionattitudetransform workqueue unrefqueue

View File

@ -22,6 +22,9 @@
#include <components/sceneutil/util.hpp> #include <components/sceneutil/util.hpp>
#include <components/sceneutil/controller.hpp> #include <components/sceneutil/controller.hpp>
#include <components/shader/shadervisitor.hpp>
#include <components/shader/shadermanager.hpp>
#include "imagemanager.hpp" #include "imagemanager.hpp"
#include "niffilemanager.hpp" #include "niffilemanager.hpp"
#include "objectcache.hpp" #include "objectcache.hpp"
@ -205,6 +208,7 @@ namespace Resource
SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)
: ResourceManager(vfs) : ResourceManager(vfs)
, mShaderManager(new Shader::ShaderManager)
, mInstanceCache(new MultiObjectCache) , mInstanceCache(new MultiObjectCache)
, mImageManager(imageManager) , mImageManager(imageManager)
, mNifFileManager(nifFileManager) , mNifFileManager(nifFileManager)
@ -221,6 +225,11 @@ namespace Resource
// this has to be defined in the .cpp file as we can't delete incomplete types // this has to be defined in the .cpp file as we can't delete incomplete types
} }
void SceneManager::setShaderPath(const std::string &path)
{
mShaderManager->setShaderPath(path);
}
/// @brief Callback to read image files from the VFS. /// @brief Callback to read image files from the VFS.
class ImageReadCallback : public osgDB::ReadFileCallback class ImageReadCallback : public osgDB::ReadFileCallback
{ {
@ -329,6 +338,9 @@ namespace Resource
SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor(mMinFilter, mMagFilter, mMaxAnisotropy); SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor(mMinFilter, mMagFilter, mMaxAnisotropy);
loaded->accept(setFilterSettingsControllerVisitor); loaded->accept(setFilterSettingsControllerVisitor);
Shader::ShaderVisitor shaderVisitor(*mShaderManager.get(), "objects_vertex.glsl", "objects_fragment.glsl");
loaded->accept(shaderVisitor);
// share state // share state
mSharedStateMutex.lock(); mSharedStateMutex.lock();
osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get()); osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get());

View File

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <map> #include <map>
#include <memory>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osg/Node> #include <osg/Node>
@ -21,6 +22,11 @@ namespace osgUtil
class IncrementalCompileOperation; class IncrementalCompileOperation;
} }
namespace Shader
{
class ShaderManager;
}
namespace Resource namespace Resource
{ {
@ -34,6 +40,8 @@ namespace Resource
SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager); SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager);
~SceneManager(); ~SceneManager();
void setShaderPath(const std::string& path);
/// Get a read-only copy of this scene "template" /// Get a read-only copy of this scene "template"
/// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead. /// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead.
/// If even the error marker mesh can not be found, an exception is thrown. /// If even the error marker mesh can not be found, an exception is thrown.
@ -97,6 +105,7 @@ namespace Resource
osg::ref_ptr<osg::Node> createInstance(const std::string& name); osg::ref_ptr<osg::Node> createInstance(const std::string& name);
std::auto_ptr<Shader::ShaderManager> mShaderManager;
osg::ref_ptr<MultiObjectCache> mInstanceCache; osg::ref_ptr<MultiObjectCache> mInstanceCache;
OpenThreads::Mutex mSharedStateMutex; OpenThreads::Mutex mSharedStateMutex;

View File

@ -0,0 +1,81 @@
#include "shadermanager.hpp"
#include <fstream>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <OpenThreads/ScopedLock>
namespace Shader
{
void ShaderManager::setShaderPath(const std::string &path)
{
mPath = path;
}
osg::ref_ptr<osg::Shader> ShaderManager::getShader(const std::string &shaderTemplate, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
// read the template if we haven't already
TemplateMap::iterator templateIt = mShaderTemplates.find(shaderTemplate);
if (templateIt == mShaderTemplates.end())
{
boost::filesystem::path p = (boost::filesystem::path(mPath) / shaderTemplate);
boost::filesystem::ifstream stream;
stream.open(p);
if (stream.fail())
{
std::cerr << "Failed to open " << p.string() << std::endl;
return NULL;
}
std::stringstream buffer;
buffer << stream.rdbuf();
templateIt = mShaderTemplates.insert(std::make_pair(shaderTemplate, buffer.str())).first;
}
ShaderMap::iterator shaderIt = mShaders.find(std::make_pair(shaderTemplate, defines));
if (shaderIt == mShaders.end())
{
std::string shaderSource = templateIt->second;
const char escapeCharacter = '@';
size_t foundPos = 0;
while ((foundPos = shaderSource.find(escapeCharacter)) != std::string::npos)
{
size_t endPos = shaderSource.find_first_of(" \n\r()[].;", foundPos);
if (endPos == std::string::npos)
{
std::cerr << "Unexpected EOF" << std::endl;
return NULL;
}
std::string define = shaderSource.substr(foundPos+1, endPos - (foundPos+1));
DefineMap::const_iterator defineFound = defines.find(define);
if (defineFound == defines.end())
{
std::cerr << "Undefined " << define << " in shader " << shaderTemplate << std::endl;
return NULL;
}
else
{
shaderSource.replace(foundPos, endPos-foundPos, defineFound->second);
}
}
osg::ref_ptr<osg::Shader> shader (new osg::Shader(shaderType));
shader->setShaderSource(shaderSource);
// Assign a unique name to allow the SharedStateManager to compare shaders efficiently
static unsigned int counter = 0;
shader->setName(boost::lexical_cast<std::string>(counter++));
shaderIt = mShaders.insert(std::make_pair(std::make_pair(shaderTemplate, defines), shader)).first;
}
return shaderIt->second;
}
}

View File

@ -0,0 +1,49 @@
#ifndef OPENMW_COMPONENTS_SHADERMANAGER_H
#define OPENMW_COMPONENTS_SHADERMANAGER_H
#include <string>
#include <map>
#include <osg/ref_ptr>
#include <osg/Shader>
#include <OpenThreads/Mutex>
namespace Shader
{
/// @brief Reads shader template files and turns them into a concrete shader, based on a list of define's.
/// @par Shader templates can get the value of a define with the syntax @define.
class ShaderManager
{
public:
void setShaderPath(const std::string& path);
typedef std::map<std::string, std::string> DefineMap;
/// Create or retrieve a shader instance.
/// @param shaderTemplate The filename of the shader template.
/// @param defines Define values that can be retrieved by the shader template.
/// @param shaderType The type of shader (usually vertex or fragment shader).
/// @note May return NULL on failure.
/// @note Thread safe.
osg::ref_ptr<osg::Shader> getShader(const std::string& shaderTemplate, const DefineMap& defines, osg::Shader::Type shaderType);
private:
std::string mPath;
// <name, code>
typedef std::map<std::string, std::string> TemplateMap;
TemplateMap mShaderTemplates;
typedef std::pair<std::string, DefineMap> MapKey;
typedef std::map<MapKey, osg::ref_ptr<osg::Shader> > ShaderMap;
ShaderMap mShaders;
OpenThreads::Mutex mMutex;
};
}
#endif

View File

@ -0,0 +1,195 @@
#include "shadervisitor.hpp"
#include <iostream>
#include <osg/Texture>
#include <osg/Material>
#include <osg/Geometry>
#include <osgUtil/TangentSpaceGenerator>
#include <boost/lexical_cast.hpp>
#include "shadermanager.hpp"
namespace Shader
{
ShaderVisitor::ShaderRequirements::ShaderRequirements()
: mColorMaterial(false)
, mVertexColorMode(GL_AMBIENT_AND_DIFFUSE)
, mTexStageRequiringTangents(-1)
{
}
ShaderVisitor::ShaderVisitor(ShaderManager& shaderManager, const std::string &defaultVsTemplate, const std::string &defaultFsTemplate)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mShaderManager(shaderManager)
, mDefaultVsTemplate(defaultVsTemplate)
, mDefaultFsTemplate(defaultFsTemplate)
{
mRequirements.push_back(ShaderRequirements());
}
void ShaderVisitor::apply(osg::Node& node)
{
if (node.getStateSet())
{
pushRequirements();
applyStateSet(node.getStateSet());
traverse(node);
popRequirements();
}
else
traverse(node);
}
void ShaderVisitor::applyStateSet(osg::StateSet* stateset)
{
const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList();
for(unsigned int unit=0;unit<texAttributes.size();++unit)
{
const osg::StateAttribute *attr = stateset->getTextureAttribute(unit, osg::StateAttribute::TEXTURE);
if (attr)
{
const osg::Texture* texture = attr->asTexture();
if (texture)
{
if (!texture->getName().empty())
{
mRequirements.back().mTextures[unit] = texture->getName();
if (texture->getName() == "normalMap")
{
mRequirements.back().mTexStageRequiringTangents = unit;
// normal maps are by default off since the FFP can't render them, now that we'll use shaders switch to On
stateset->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON);
}
}
else
std::cerr << "ShaderVisitor encountered unknown texture " << texture << std::endl;
}
}
// remove state that has no effect when rendering with shaders
stateset->removeTextureAttribute(unit, osg::StateAttribute::TEXENV);
}
const osg::StateSet::AttributeList& attributes = stateset->getAttributeList();
for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it)
{
if (it->first.first == osg::StateAttribute::MATERIAL)
{
const osg::Material* mat = static_cast<const osg::Material*>(it->second.first.get());
mRequirements.back().mVertexColorMode = mat->getColorMode();
}
}
}
void ShaderVisitor::pushRequirements()
{
mRequirements.push_back(mRequirements.back());
}
void ShaderVisitor::popRequirements()
{
mRequirements.pop_back();
}
void ShaderVisitor::createProgram(const ShaderRequirements &reqs, osg::StateSet *stateset)
{
ShaderManager::DefineMap defineMap;
const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap" };
for (unsigned int i=0; i<sizeof(defaultTextures)/sizeof(defaultTextures[0]); ++i)
{
defineMap[defaultTextures[i]] = "0";
defineMap[std::string(defaultTextures[i]) + std::string("UV")] = "0";
}
for (std::map<int, std::string>::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt)
{
defineMap[texIt->second] = "1";
defineMap[texIt->second + std::string("UV")] = boost::lexical_cast<std::string>(texIt->first);
}
if (!reqs.mColorMaterial)
defineMap["colorMode"] = "0";
else
{
switch (reqs.mVertexColorMode)
{
default:
case GL_AMBIENT_AND_DIFFUSE:
defineMap["colorMode"] = "2";
break;
case GL_AMBIENT:
defineMap["colorMode"] = "1";
break;
}
}
osg::ref_ptr<osg::Shader> vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX));
osg::ref_ptr<osg::Shader> fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT));
if (vertexShader && fragmentShader)
{
osg::ref_ptr<osg::Program> program (new osg::Program);
program->addShader(vertexShader);
program->addShader(fragmentShader);
stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
for (std::map<int, std::string>::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt)
{
stateset->addUniform(new osg::Uniform(texIt->second.c_str(), texIt->first), osg::StateAttribute::ON);
}
}
}
void ShaderVisitor::apply(osg::Geometry& geometry)
{
bool needPop = (geometry.getStateSet() != NULL);
if (geometry.getStateSet())
{
pushRequirements();
applyStateSet(geometry.getStateSet());
}
if (!mRequirements.empty())
{
const ShaderRequirements& reqs = mRequirements.back();
if (reqs.mTexStageRequiringTangents != -1)
{
osg::ref_ptr<osgUtil::TangentSpaceGenerator> generator (new osgUtil::TangentSpaceGenerator);
generator->generate(&geometry, reqs.mTexStageRequiringTangents);
geometry.setTexCoordArray(7, generator->getTangentArray(), osg::Array::BIND_PER_VERTEX);
}
// TODO: find a better place for the stateset
createProgram(reqs, geometry.getOrCreateStateSet());
}
if (needPop)
popRequirements();
}
void ShaderVisitor::apply(osg::Drawable& drawable)
{
// non-Geometry drawable (e.g. particle system)
bool needPop = (drawable.getStateSet() != NULL);
if (drawable.getStateSet())
{
pushRequirements();
applyStateSet(drawable.getStateSet());
}
if (!mRequirements.empty())
{
// TODO: find a better place for the stateset
createProgram(mRequirements.back(), drawable.getOrCreateStateSet());
}
if (needPop)
popRequirements();
}
}

View File

@ -0,0 +1,54 @@
#ifndef OPENMW_COMPONENTS_SHADERVISITOR_H
#define OPENMW_COMPONENTS_SHADERVISITOR_H
#include <osg/NodeVisitor>
namespace Shader
{
class ShaderManager;
/// @brief Adjusts the given subgraph to render using shaders.
class ShaderVisitor : public osg::NodeVisitor
{
public:
ShaderVisitor(ShaderManager& shaderManager, const std::string& defaultVsTemplate, const std::string& defaultFsTemplate);
virtual void apply(osg::Node& node);
virtual void apply(osg::Drawable& drawable);
virtual void apply(osg::Geometry& geometry);
void applyStateSet(osg::StateSet* stateset);
void pushRequirements();
void popRequirements();
private:
ShaderManager& mShaderManager;
struct ShaderRequirements
{
ShaderRequirements();
// <texture stage, texture name>
std::map<int, std::string> mTextures;
bool mColorMaterial;
// osg::Material::ColorMode
int mVertexColorMode;
// -1 == no tangents required
int mTexStageRequiringTangents;
};
std::vector<ShaderRequirements> mRequirements;
std::string mDefaultVsTemplate;
std::string mDefaultFsTemplate;
void createProgram(const ShaderRequirements& reqs, osg::StateSet* stateset);
};
}
#endif

View File

@ -6,6 +6,8 @@ set(SHADER_FILES
water_vertex.glsl water_vertex.glsl
water_fragment.glsl water_fragment.glsl
water_nm.png water_nm.png
objects_vertex.glsl
objects_fragment.glsl
) )
copy_all_files(${CMAKE_CURRENT_SOURCE_DIR} ${DDIR} "${SHADER_FILES}") copy_all_files(${CMAKE_CURRENT_SOURCE_DIR} ${DDIR} "${SHADER_FILES}")

View File

@ -0,0 +1,15 @@
#version 120
#if @diffuseMap
uniform sampler2D diffuseMap;
varying vec2 diffuseMapUV;
#endif
void main()
{
#if @diffuseMap
gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV);
#else
gl_FragData[0] = vec4(1,1,1,1);
#endif
}

View File

@ -0,0 +1,14 @@
#version 120
#if @diffuseMap
varying vec2 diffuseMapUV;
#endif
void main(void)
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
#if @diffuseMap
diffuseMapUV = gl_MultiTexCoord@diffuseMapUV.xy;
#endif
}