mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-29 09:32:45 +00:00
allow soft particles on meshes and add extra data extensions
This commit is contained in:
parent
776cae4c95
commit
b09411d396
@ -87,8 +87,10 @@ namespace MWRender
|
||||
class PerViewUniformStateUpdater final : public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
PerViewUniformStateUpdater()
|
||||
PerViewUniformStateUpdater(Resource::SceneManager* sceneManager)
|
||||
: mSceneManager(sceneManager)
|
||||
{
|
||||
mOpaqueTextureUnit = mSceneManager->getShaderManager().reserveGlobalTextureUnits(Shader::ShaderManager::Slot::OpaqueDepthTexture);
|
||||
}
|
||||
|
||||
void setDefaults(osg::StateSet* stateset) override
|
||||
@ -108,6 +110,8 @@ namespace MWRender
|
||||
osg::Texture* skyTexture = mSkyRTT->getColorTexture(static_cast<osgUtil::CullVisitor*>(nv));
|
||||
stateset->setTextureAttribute(mSkyTextureUnit, skyTexture, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
}
|
||||
|
||||
stateset->setTextureAttribute(mOpaqueTextureUnit, mSceneManager->getOpaqueDepthTex(nv->getTraversalNumber()), osg::StateAttribute::ON);
|
||||
}
|
||||
|
||||
void applyLeft(osg::StateSet* stateset, osgUtil::CullVisitor* nv) override
|
||||
@ -144,6 +148,9 @@ namespace MWRender
|
||||
osg::Matrixf mProjectionMatrix;
|
||||
int mSkyTextureUnit = -1;
|
||||
SceneUtil::RTTNode* mSkyRTT = nullptr;
|
||||
|
||||
Resource::SceneManager* mSceneManager;
|
||||
int mOpaqueTextureUnit = -1;
|
||||
};
|
||||
|
||||
class SharedUniformStateUpdater : public SceneUtil::StateSetUpdater
|
||||
@ -536,7 +543,7 @@ namespace MWRender
|
||||
mSharedUniformStateUpdater = new SharedUniformStateUpdater(groundcover);
|
||||
rootNode->addUpdateCallback(mSharedUniformStateUpdater);
|
||||
|
||||
mPerViewUniformStateUpdater = new PerViewUniformStateUpdater();
|
||||
mPerViewUniformStateUpdater = new PerViewUniformStateUpdater(mResourceSystem->getSceneManager());
|
||||
rootNode->addCullCallback(mPerViewUniformStateUpdater);
|
||||
|
||||
mPostProcessor = new PostProcessor(*this, viewer, mRootNode, resourceSystem->getVFS());
|
||||
@ -581,7 +588,7 @@ namespace MWRender
|
||||
mSky->setCamera(mViewer->getCamera());
|
||||
if (mSkyBlending)
|
||||
{
|
||||
int skyTextureUnit = mResourceSystem->getSceneManager()->getShaderManager().reserveGlobalTextureUnits(1);
|
||||
int skyTextureUnit = mResourceSystem->getSceneManager()->getShaderManager().reserveGlobalTextureUnits(Shader::ShaderManager::Slot::SkyTexture);
|
||||
Log(Debug::Info) << "Reserving texture unit for sky RTT: " << skyTextureUnit;
|
||||
mPerViewUniformStateUpdater->enableSkyRTT(skyTextureUnit, mSky->getSkyRTT());
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem*
|
||||
|
||||
createWaterRippleStateSet(resourceSystem, mParticleNode);
|
||||
|
||||
resourceSystem->getSceneManager()->recreateShaders(mParticleNode, "objects", false, nullptr, true);
|
||||
resourceSystem->getSceneManager()->recreateShaders(mParticleNode);
|
||||
|
||||
mParent->addChild(mParticleNode);
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ add_component_dir (sceneutil
|
||||
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
|
||||
lightmanager lightutil positionattitudetransform workqueue pathgridutil waterutil writescene serialize optimizer
|
||||
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller rtt
|
||||
screencapture depth color riggeometryosgaextension
|
||||
screencapture depth color riggeometryosgaextension extradata
|
||||
)
|
||||
|
||||
add_component_dir (nif
|
||||
|
@ -3,4 +3,6 @@
|
||||
namespace Misc
|
||||
{
|
||||
const std::string OsgUserValues::sFileHash = "fileHash";
|
||||
const std::string OsgUserValues::sExtraData = "xData";
|
||||
const std::string OsgUserValues::sXSoftEffect = "xSoftEffect";
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ namespace Misc
|
||||
struct OsgUserValues
|
||||
{
|
||||
static const std::string sFileHash;
|
||||
static const std::string sExtraData;
|
||||
static const std::string sXSoftEffect;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -614,17 +614,24 @@ namespace NifOsg
|
||||
else if(e->recType == Nif::RC_NiStringExtraData)
|
||||
{
|
||||
const Nif::NiStringExtraData *sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr());
|
||||
|
||||
constexpr std::string_view extraDataIdentifer = "omw:data";
|
||||
|
||||
// String markers may contain important information
|
||||
// affecting the entire subtree of this obj
|
||||
if(sd->string == "MRK" && !Loader::getShowMarkers())
|
||||
if (sd->string == "MRK" && !Loader::getShowMarkers())
|
||||
{
|
||||
// Marker objects. These meshes are only visible in the editor.
|
||||
hasMarkers = true;
|
||||
}
|
||||
else if(sd->string == "BONE")
|
||||
else if (sd->string == "BONE")
|
||||
{
|
||||
node->getOrCreateUserDataContainer()->addDescription("CustomBone");
|
||||
}
|
||||
else if (sd->string.rfind(extraDataIdentifer, 0) == 0)
|
||||
{
|
||||
node->setUserValue(Misc::OsgUserValues::sExtraData, sd->string.substr(extraDataIdentifer.length()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/riggeometryosgaextension.hpp>
|
||||
#include <components/sceneutil/extradata.hpp>
|
||||
|
||||
#include <components/shader/shadervisitor.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
@ -366,15 +367,13 @@ namespace Resource
|
||||
return mForceShaders;
|
||||
}
|
||||
|
||||
void SceneManager::recreateShaders(osg::ref_ptr<osg::Node> node, const std::string& shaderPrefix, bool forceShadersForNode, const osg::Program* programTemplate, bool disableSoftParticles)
|
||||
void SceneManager::recreateShaders(osg::ref_ptr<osg::Node> node, const std::string& shaderPrefix, bool forceShadersForNode, const osg::Program* programTemplate)
|
||||
{
|
||||
osg::ref_ptr<Shader::ShaderVisitor> shaderVisitor(createShaderVisitor(shaderPrefix));
|
||||
shaderVisitor->setAllowedToModifyStateSets(false);
|
||||
shaderVisitor->setProgramTemplate(programTemplate);
|
||||
if (forceShadersForNode)
|
||||
shaderVisitor->setForceShaders(true);
|
||||
if (disableSoftParticles)
|
||||
shaderVisitor->setOpaqueDepthTex(nullptr, nullptr);
|
||||
node->accept(*shaderVisitor);
|
||||
}
|
||||
|
||||
@ -461,6 +460,11 @@ namespace Resource
|
||||
mOpaqueDepthTex = { texturePing, texturePong };
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Texture> SceneManager::getOpaqueDepthTex(size_t frame)
|
||||
{
|
||||
return mOpaqueDepthTex[frame % 2];
|
||||
}
|
||||
|
||||
SceneManager::~SceneManager()
|
||||
{
|
||||
// this has to be defined in the .cpp file as we can't delete incomplete types
|
||||
@ -737,6 +741,9 @@ namespace Resource
|
||||
try
|
||||
{
|
||||
loaded = load(normalized, mVFS, mImageManager, mNifFileManager);
|
||||
|
||||
SceneUtil::ProcessExtraDataVisitor extraDataVisitor(this);
|
||||
loaded->accept(extraDataVisitor);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@ -990,7 +997,6 @@ namespace Resource
|
||||
shaderVisitor->setSpecularMapPattern(mSpecularMapPattern);
|
||||
shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps);
|
||||
shaderVisitor->setConvertAlphaTestToAlphaToCoverage(mConvertAlphaTestToAlphaToCoverage);
|
||||
shaderVisitor->setOpaqueDepthTex(mOpaqueDepthTex[0], mOpaqueDepthTex[1]);
|
||||
shaderVisitor->setSupportsNormalsRT(mSupportsNormalsRT);
|
||||
return shaderVisitor;
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ namespace Resource
|
||||
Shader::ShaderManager& getShaderManager();
|
||||
|
||||
/// Re-create shaders for this node, need to call this if alpha testing, texture stages or vertex color mode have changed.
|
||||
void recreateShaders(osg::ref_ptr<osg::Node> node, const std::string& shaderPrefix = "objects", bool forceShadersForNode = false, const osg::Program* programTemplate = nullptr, bool disableSoftParticles = false);
|
||||
void recreateShaders(osg::ref_ptr<osg::Node> node, const std::string& shaderPrefix = "objects", bool forceShadersForNode = false, const osg::Program* programTemplate = nullptr);
|
||||
|
||||
/// Applying shaders to a node may replace some fixed-function state.
|
||||
/// This restores it.
|
||||
@ -111,6 +111,8 @@ namespace Resource
|
||||
|
||||
void setOpaqueDepthTex(osg::ref_ptr<osg::Texture> texturePing, osg::ref_ptr<osg::Texture> texturePong);
|
||||
|
||||
osg::ref_ptr<osg::Texture> getOpaqueDepthTex(size_t frame);
|
||||
|
||||
enum class UBOBinding
|
||||
{
|
||||
// If we add more UBO's, we should probably assign their bindings dynamically according to the current count of UBO's in the programTemplate
|
||||
|
66
components/sceneutil/extradata.cpp
Normal file
66
components/sceneutil/extradata.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "extradata.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <osg/Node>
|
||||
#include <osg/ValueObject>
|
||||
#include <osgParticle/ParticleSystem>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include <components/misc/osguservalues.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
#include <components/serialization/osgyaml.hpp>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
void ProcessExtraDataVisitor::setupSoftEffect(osg::Node& node, float size, bool falloff)
|
||||
{
|
||||
const int unitSoftEffect = mSceneMgr->getShaderManager().reserveGlobalTextureUnits(Shader::ShaderManager::Slot::OpaqueDepthTexture);
|
||||
static const osg::ref_ptr<SceneUtil::AutoDepth> depth = new SceneUtil::AutoDepth(osg::Depth::LESS, 0, 1, false);
|
||||
|
||||
osg::StateSet* stateset = node.getOrCreateStateSet();
|
||||
|
||||
stateset->addUniform(new osg::Uniform("opaqueDepthTex", unitSoftEffect));
|
||||
stateset->addUniform(new osg::Uniform("particleSize", size));
|
||||
stateset->addUniform(new osg::Uniform("particleFade", falloff));
|
||||
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
|
||||
node.setUserValue(Misc::OsgUserValues::sXSoftEffect, true);
|
||||
}
|
||||
|
||||
void ProcessExtraDataVisitor::apply(osg::Node& node)
|
||||
{
|
||||
std::string source;
|
||||
|
||||
if (node.getUserValue(Misc::OsgUserValues::sExtraData, source) && !source.empty())
|
||||
{
|
||||
YAML::Node root = YAML::Load(source);
|
||||
|
||||
for (const auto& it : root["shader"])
|
||||
{
|
||||
std::string key = it.first.as<std::string>();
|
||||
|
||||
if (key == "soft_effect")
|
||||
{
|
||||
auto size = it.second["size"].as<float>(45.f);
|
||||
auto falloff = it.second["falloff"].as<bool>(false);
|
||||
|
||||
setupSoftEffect(node, size, falloff);
|
||||
}
|
||||
}
|
||||
|
||||
node.setUserValue(Misc::OsgUserValues::sExtraData, std::string{});
|
||||
}
|
||||
else if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(&node))
|
||||
{
|
||||
setupSoftEffect(node, partsys->getDefaultParticleTemplate().getSizeRange().maximum, false);
|
||||
}
|
||||
|
||||
traverse(node);
|
||||
}
|
||||
}
|
35
components/sceneutil/extradata.hpp
Normal file
35
components/sceneutil/extradata.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef OPENMW_COMPONENTS_RESOURCE_EXTRADATA_H
|
||||
#define OPENMW_COMPONENTS_RESOURCE_EXTRADATA_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/StateAttribute>
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
class SceneManager;
|
||||
}
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Node;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class ProcessExtraDataVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
ProcessExtraDataVisitor(Resource::SceneManager* sceneMgr) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mSceneMgr(sceneMgr) {}
|
||||
|
||||
void apply(osg::Node& node) override;
|
||||
|
||||
private:
|
||||
void setupSoftEffect(osg::Node& node, float size, bool falloff);
|
||||
|
||||
Resource::SceneManager* mSceneMgr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -510,22 +510,31 @@ namespace Shader
|
||||
program->addShader(linkedShader);
|
||||
}
|
||||
|
||||
int ShaderManager::reserveGlobalTextureUnits(int count)
|
||||
int ShaderManager::reserveGlobalTextureUnits(Slot slot)
|
||||
{
|
||||
int unit = mReservedTextureUnitsBySlot[static_cast<int>(slot)];
|
||||
if (unit >= 0)
|
||||
return unit;
|
||||
|
||||
{
|
||||
// Texture units from `8 - numberOfShadowMaps` to `8` are used for shadows, so we skip them here.
|
||||
// TODO: Maybe instead of fixed texture units use `reserveGlobalTextureUnits` for shadows as well.
|
||||
static const int numberOfShadowMaps = Settings::Manager::getBool("enable shadows", "Shadows") ?
|
||||
std::clamp(Settings::Manager::getInt("number of shadow maps", "Shadows"), 1, 8) :
|
||||
0;
|
||||
if (getAvailableTextureUnits() >= 8 && getAvailableTextureUnits() - count < 8)
|
||||
if (getAvailableTextureUnits() >= 8 && getAvailableTextureUnits() - 1 < 8)
|
||||
mReservedTextureUnits = mMaxTextureUnits - (8 - numberOfShadowMaps);
|
||||
}
|
||||
|
||||
if (getAvailableTextureUnits() < count + 1)
|
||||
if (getAvailableTextureUnits() < 2)
|
||||
throw std::runtime_error("Can't reserve texture unit; no available units");
|
||||
mReservedTextureUnits += count;
|
||||
return mMaxTextureUnits - mReservedTextureUnits;
|
||||
mReservedTextureUnits++;
|
||||
|
||||
unit = mMaxTextureUnits - mReservedTextureUnits;
|
||||
|
||||
mReservedTextureUnitsBySlot[static_cast<int>(slot)] = unit;
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
@ -58,7 +59,13 @@ namespace Shader
|
||||
int getMaxTextureUnits() const { return mMaxTextureUnits; }
|
||||
int getAvailableTextureUnits() const { return mMaxTextureUnits - mReservedTextureUnits; }
|
||||
|
||||
int reserveGlobalTextureUnits(int count);
|
||||
enum class Slot
|
||||
{
|
||||
OpaqueDepthTexture,
|
||||
SkyTexture,
|
||||
};
|
||||
|
||||
int reserveGlobalTextureUnits(Slot slot);
|
||||
|
||||
private:
|
||||
void getLinkedShaders(osg::ref_ptr<osg::Shader> shader, const std::vector<std::string>& linkedShaderNames, const DefineMap& defines);
|
||||
@ -89,6 +96,8 @@ namespace Shader
|
||||
|
||||
int mMaxTextureUnits = 0;
|
||||
int mReservedTextureUnits = 0;
|
||||
|
||||
std::array<int, 2> mReservedTextureUnitsBySlot = {-1, -1};
|
||||
};
|
||||
|
||||
bool parseForeachDirective(std::string& source, const std::string& templateName, size_t foundPos);
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/misc/osguservalues.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
@ -30,50 +31,6 @@
|
||||
#include "removedalphafunc.hpp"
|
||||
#include "shadermanager.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
class OpaqueDepthAttribute : public osg::StateAttribute
|
||||
{
|
||||
public:
|
||||
OpaqueDepthAttribute() = default;
|
||||
|
||||
OpaqueDepthAttribute(const OpaqueDepthAttribute& copy, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
|
||||
: osg::StateAttribute(copy, copyop), mTextures(copy.mTextures), mUnit(copy.mUnit) {}
|
||||
|
||||
void setTexturesAndUnit(const std::array<osg::ref_ptr<osg::Texture>, 2>& textures, int unit)
|
||||
{
|
||||
mTextures = textures;
|
||||
mUnit = unit;
|
||||
}
|
||||
|
||||
META_StateAttribute(Shader, OpaqueDepthAttribute, osg::StateAttribute::TEXTURE)
|
||||
|
||||
int compare(const StateAttribute& sa) const override
|
||||
{
|
||||
COMPARE_StateAttribute_Types(OpaqueDepthAttribute, sa);
|
||||
|
||||
COMPARE_StateAttribute_Parameter(mTextures);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void apply(osg::State& state) const override
|
||||
{
|
||||
auto index = state.getFrameStamp()->getFrameNumber() % 2;
|
||||
|
||||
if (!mTextures[index])
|
||||
return;
|
||||
|
||||
state.setActiveTextureUnit(mUnit);
|
||||
state.applyTextureAttribute(mUnit, mTextures[index]);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::array<osg::ref_ptr<osg::Texture>, 2> mTextures;
|
||||
int mUnit;
|
||||
};
|
||||
}
|
||||
|
||||
namespace Shader
|
||||
{
|
||||
/**
|
||||
@ -198,7 +155,6 @@ namespace Shader
|
||||
, mNormalHeight(false)
|
||||
, mTexStageRequiringTangents(-1)
|
||||
, mSoftParticles(false)
|
||||
, mSoftParticleSize(0.f)
|
||||
, mNode(nullptr)
|
||||
{
|
||||
}
|
||||
@ -308,6 +264,10 @@ namespace Shader
|
||||
if (node.getUserValue("shaderRequired", shaderRequired) && shaderRequired)
|
||||
mRequirements.back().mShaderRequired = true;
|
||||
|
||||
bool softEffect = false;
|
||||
if (node.getUserValue(Misc::OsgUserValues::sXSoftEffect, softEffect) && softEffect)
|
||||
mRequirements.back().mSoftParticles = true;
|
||||
|
||||
// Make sure to disregard any state that came from a previous call to createProgram
|
||||
osg::ref_ptr<AddedState> addedState = getAddedState(*stateset);
|
||||
|
||||
@ -681,28 +641,7 @@ namespace Shader
|
||||
updateRemovedState(*writableUserData, removedState);
|
||||
}
|
||||
|
||||
if (reqs.mSoftParticles && mOpaqueDepthTex.front())
|
||||
{
|
||||
osg::ref_ptr<osg::Depth> depth = new SceneUtil::AutoDepth;
|
||||
depth->setWriteMask(false);
|
||||
writableStateSet->setAttributeAndModes(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
addedState->setAttributeAndModes(depth);
|
||||
|
||||
writableStateSet->addUniform(new osg::Uniform("particleSize", reqs.mSoftParticleSize));
|
||||
addedState->addUniform("particleSize");
|
||||
|
||||
constexpr int unit = 2;
|
||||
|
||||
writableStateSet->addUniform(new osg::Uniform("opaqueDepthTex", unit));
|
||||
addedState->addUniform("opaqueDepthTex");
|
||||
|
||||
osg::ref_ptr<OpaqueDepthAttribute> opaqueDepthAttr = new OpaqueDepthAttribute;
|
||||
opaqueDepthAttr->setTexturesAndUnit(mOpaqueDepthTex, unit);
|
||||
writableStateSet->setAttributeAndModes(opaqueDepthAttr, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
addedState->setAttributeAndModes(opaqueDepthAttr);
|
||||
}
|
||||
|
||||
defineMap["softParticles"] = reqs.mSoftParticles && mOpaqueDepthTex.front() ? "1" : "0";
|
||||
defineMap["softParticles"] = reqs.mSoftParticles ? "1" : "0";
|
||||
|
||||
Stereo::Manager::instance().shaderStereoDefines(defineMap);
|
||||
|
||||
@ -888,20 +827,12 @@ namespace Shader
|
||||
|
||||
void ShaderVisitor::apply(osg::Drawable& drawable)
|
||||
{
|
||||
auto partsys = dynamic_cast<osgParticle::ParticleSystem*>(&drawable);
|
||||
|
||||
bool needPop = drawable.getStateSet() || partsys;
|
||||
bool needPop = drawable.getStateSet();
|
||||
|
||||
if (needPop)
|
||||
{
|
||||
pushRequirements(drawable);
|
||||
|
||||
if (partsys)
|
||||
{
|
||||
mRequirements.back().mSoftParticles = true;
|
||||
mRequirements.back().mSoftParticleSize = partsys->getDefaultParticleTemplate().getSizeRange().maximum;
|
||||
}
|
||||
|
||||
if (drawable.getStateSet())
|
||||
applyStateSet(drawable.getStateSet(), drawable);
|
||||
}
|
||||
@ -982,11 +913,6 @@ namespace Shader
|
||||
mConvertAlphaTestToAlphaToCoverage = convert;
|
||||
}
|
||||
|
||||
void ShaderVisitor::setOpaqueDepthTex(osg::ref_ptr<osg::Texture> texturePing, osg::ref_ptr<osg::Texture> texturePong)
|
||||
{
|
||||
mOpaqueDepthTex = { texturePing, texturePong };
|
||||
}
|
||||
|
||||
ReinstateRemovedStateVisitor::ReinstateRemovedStateVisitor(bool allowedToModifyStateSets)
|
||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||
, mAllowedToModifyStateSets(allowedToModifyStateSets)
|
||||
|
@ -48,8 +48,6 @@ namespace Shader
|
||||
|
||||
void setConvertAlphaTestToAlphaToCoverage(bool convert);
|
||||
|
||||
void setOpaqueDepthTex(osg::ref_ptr<osg::Texture> texturePing, osg::ref_ptr<osg::Texture> texturePong);
|
||||
|
||||
void setSupportsNormalsRT(bool supports) { mSupportsNormalsRT = supports; }
|
||||
|
||||
void apply(osg::Node& node) override;
|
||||
@ -108,7 +106,6 @@ namespace Shader
|
||||
int mTexStageRequiringTangents;
|
||||
|
||||
bool mSoftParticles;
|
||||
float mSoftParticleSize;
|
||||
|
||||
// the Node that requested these requirements
|
||||
osg::Node* mNode;
|
||||
@ -122,7 +119,6 @@ namespace Shader
|
||||
bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);
|
||||
|
||||
osg::ref_ptr<const osg::Program> mProgramTemplate;
|
||||
std::array<osg::ref_ptr<osg::Texture>, 2> mOpaqueDepthTex;
|
||||
};
|
||||
|
||||
class ReinstateRemovedStateVisitor : public osg::NodeVisitor
|
||||
|
54
docs/source/reference/modding/custom-shader-effects.rst
Normal file
54
docs/source/reference/modding/custom-shader-effects.rst
Normal file
@ -0,0 +1,54 @@
|
||||
Custom Shader Effects
|
||||
#####################
|
||||
|
||||
OpenMW leverages the `NiStringExtraData` node to inject special shader flags and effects.
|
||||
This nodes must have the prefix `omw:data` and have a valid JSON object that follows.
|
||||
|
||||
.. note::
|
||||
|
||||
This is a new feature to inject OpenMW specific effects. Only a single
|
||||
effect is currently supported. By default, the shader effects will propogate
|
||||
to all a nodes children. Other propogation modes and effects will come with
|
||||
future releases.
|
||||
|
||||
See below to see the currently supported effects.
|
||||
|
||||
Soft Effect
|
||||
-----------
|
||||
|
||||
This effect softens the intersection of alpha-blended planes with other opaque
|
||||
geometry. This effect is automatically applied to all particle systems, but can
|
||||
be applied to any mesh or node. This is useful when layering many alpha-blended
|
||||
planes for various effects like steam over a hotspring or low hanging fog for
|
||||
dungeons.
|
||||
|
||||
To use this feature the :ref:`soft particles` setting must be enabled.
|
||||
This settings can either be activated in the OpenMW launcher or changed in `settings.cfg`:
|
||||
|
||||
::
|
||||
|
||||
[Shaders]
|
||||
soft particles = true
|
||||
|
||||
Variables.
|
||||
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| Name | Description | Type | Default |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| size | Scaling ratio. Larger values will make a softer fade effect. Larger geometry requires higher values. | integer | 45 |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| falloff | Fades away geometry as camera gets closer. Geometry full fades when parallel to camera. | boolean | false |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
|
||||
Example usage.
|
||||
|
||||
::
|
||||
|
||||
omw:data {
|
||||
"shader" : {
|
||||
"soft_effect" : {
|
||||
"size": 250,
|
||||
"falloff" : false,
|
||||
}
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ about creating new content for OpenMW, please refer to
|
||||
custom-models/index
|
||||
font
|
||||
sky-system
|
||||
custom-shader-effects
|
||||
extended
|
||||
paths
|
||||
localisation
|
||||
|
@ -1,4 +1,3 @@
|
||||
#if !@lightingMethodFFP
|
||||
float quickstep(float x)
|
||||
{
|
||||
x = clamp(x, 0.0, 1.0);
|
||||
@ -6,7 +5,6 @@ float quickstep(float x)
|
||||
x = 1.0 - x*x;
|
||||
return x;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if @lightingMethodUBO
|
||||
|
||||
|
@ -95,6 +95,7 @@ void main()
|
||||
#endif
|
||||
|
||||
vec3 worldNormal = normalize(passNormal);
|
||||
vec3 viewVec = normalize(passViewPos.xyz);
|
||||
|
||||
#if @normalMap
|
||||
vec4 normalTex = texture2D(normalMap, normalMapUV);
|
||||
@ -164,7 +165,6 @@ void main()
|
||||
|
||||
#if @normalMap
|
||||
// if using normal map + env map, take advantage of per-pixel normals for envTexCoordGen
|
||||
vec3 viewVec = normalize(passViewPos.xyz);
|
||||
vec3 r = reflect( viewVec, viewNormal );
|
||||
float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) );
|
||||
envTexCoordGen = vec2(r.x/m + 0.5, r.y/m + 0.5);
|
||||
@ -226,13 +226,13 @@ void main()
|
||||
#if (!@normalMap && !@parallax && !@forcePPL)
|
||||
vec3 viewNormal = gl_NormalMatrix * worldNormal;
|
||||
#endif
|
||||
gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing;
|
||||
gl_FragData[0].xyz += getSpecular(normalize(viewNormal), viewVec, shininess, matSpec) * shadowing;
|
||||
}
|
||||
|
||||
gl_FragData[0] = applyFogAtPos(gl_FragData[0], passViewPos);
|
||||
|
||||
#if !defined(FORCE_OPAQUE) && @softParticles
|
||||
gl_FragData[0].a *= calcSoftParticleFade();
|
||||
gl_FragData[0].a *= calcSoftParticleFade(viewVec, viewNormal, passViewPos);
|
||||
#endif
|
||||
|
||||
#if defined(FORCE_OPAQUE) && FORCE_OPAQUE
|
||||
|
@ -1,6 +1,7 @@
|
||||
uniform float near;
|
||||
uniform sampler2D opaqueDepthTex;
|
||||
uniform float particleSize;
|
||||
uniform bool particleFade;
|
||||
|
||||
float viewDepth(float depth)
|
||||
{
|
||||
@ -10,21 +11,28 @@ float viewDepth(float depth)
|
||||
return (near * far) / ((far - near) * depth - far);
|
||||
}
|
||||
|
||||
float calcSoftParticleFade()
|
||||
float calcSoftParticleFade(in vec3 viewDir, in vec3 viewNormal, in vec3 viewPos)
|
||||
{
|
||||
float euclidianDepth = length(viewPos);
|
||||
|
||||
const float falloffMultiplier = 0.33;
|
||||
const float contrast = 1.30;
|
||||
|
||||
vec2 screenCoords = gl_FragCoord.xy / screenRes;
|
||||
float sceneDepth = viewDepth(texture2D(opaqueDepthTex, screenCoords).x);
|
||||
float particleDepth = viewDepth(gl_FragCoord.z);
|
||||
|
||||
float depth = texture2D(opaqueDepthTex, screenCoords).x;
|
||||
|
||||
float sceneDepth = viewDepth(depth);
|
||||
float particleDepth = passViewPos.z;
|
||||
float falloff = particleSize * falloffMultiplier;
|
||||
float delta = particleDepth - sceneDepth;
|
||||
|
||||
if (delta < 0.0)
|
||||
discard;
|
||||
const float nearMult = 300.0;
|
||||
float viewBias = 1.0;
|
||||
|
||||
if (particleFade)
|
||||
viewBias = abs(dot(-viewDir, viewNormal) * quickstep(euclidianDepth / nearMult));
|
||||
|
||||
const float shift = 0.845;
|
||||
|
||||
return shift * pow(clamp(delta/falloff, 0.0, 1.0), contrast);
|
||||
return shift * pow(clamp(delta/falloff, 0.0, 1.0), contrast) * viewBias;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user