Added support for Effect schemes

Effect schemes are a way of rendering an object in different ways depending on the Compositor pipeline. A new <scheme> tag in the Compositor pipeline definition allows the user to choose which Effect scheme is going to be used for that pass. Every Effect will then be rendered using the technique that has a matching scheme name. If no valid technique is found, it won't be rendered. Since it would be a pain to define a valid technique for every scheme and for every Effect, the file '$FG_ROOT/Effects/schemes.xml' is introduced:

<scheme>
  <name>test</name>
  <fallback>Effects/test</fallback>
</scheme>

If an Effect doesn't have a valid technique for the 'test' scheme, the 'Effects/test.eff' Effect will be merged with it. This process is done at initialization when techniques are being realized.
This commit is contained in:
Fernando García Liñán 2019-05-04 19:15:22 +02:00
parent dd38e399ca
commit 3e57b50066
8 changed files with 69 additions and 25 deletions

View File

@ -77,6 +77,7 @@
#include <simgear/scene/util/StateAttributeFactory.hxx> #include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx> #include <simgear/structure/OSGUtils.hxx>
#include <simgear/structure/SGExpression.hxx> #include <simgear/structure/SGExpression.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/vectorPropTemplates.hxx> #include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/threads/SGThread.hxx> #include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.hxx> #include <simgear/threads/SGGuard.hxx>
@ -245,11 +246,12 @@ int Effect::getGenerator(Effect::Generator what) const
// There should always be a valid technique in an effect. // There should always be a valid technique in an effect.
Technique* Effect::chooseTechnique(RenderInfo* info) Technique* Effect::chooseTechnique(RenderInfo* info, const std::string &scheme)
{ {
BOOST_FOREACH(ref_ptr<Technique>& technique, techniques) BOOST_FOREACH(ref_ptr<Technique>& technique, techniques)
{ {
if (technique->valid(info) == Technique::VALID) if (technique->valid(info) == Technique::VALID &&
technique->getScheme() == scheme)
return technique.get(); return technique.get();
} }
return 0; return 0;
@ -1308,6 +1310,7 @@ void buildTechnique(Effect* effect, const SGPropertyNode* prop,
{ {
Technique* tniq = new Technique; Technique* tniq = new Technique;
effect->techniques.push_back(tniq); effect->techniques.push_back(tniq);
tniq->setScheme(prop->getStringValue("scheme"));
const SGPropertyNode* predProp = prop->getChild("predicate"); const SGPropertyNode* predProp = prop->getChild("predicate");
if (!predProp) { if (!predProp) {
tniq->setAlwaysValid(true); tniq->setAlwaysValid(true);
@ -1411,12 +1414,60 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
return true; return true;
} }
SGPropertyNode_ptr schemeList;
void mergeSchemesFallbacks(Effect *effect, const SGReaderWriterOptions *options)
{
if (!schemeList) {
schemeList = new SGPropertyNode;
const string schemes_file("Effects/schemes.xml");
string absFileName
= SGModelLib::findDataFile(schemes_file, options);
if (absFileName.empty()) {
SG_LOG(SG_INPUT, SG_ALERT, "Could not find '" << schemes_file << "'");
return;
}
try {
readProperties(absFileName, schemeList, 0, true);
} catch (sg_io_exception& e) {
SG_LOG(SG_INPUT, SG_ALERT, "Error reading '" << schemes_file <<
"': " << e.getFormattedMessage());
return;
}
}
PropertyList p_schemes = schemeList->getChildren("scheme");
for (const auto &p_scheme : p_schemes) {
string scheme_name = p_scheme->getStringValue("name");
string fallback_name = p_scheme->getStringValue("fallback");
if (scheme_name.empty() || fallback_name.empty())
continue;
vector<SGPropertyNode_ptr> techniques = effect->root->getChildren("technique");
auto it = std::find_if(techniques.begin(), techniques.end(),
[&scheme_name](const SGPropertyNode_ptr &tniq) {
return tniq->getStringValue("scheme") == scheme_name;
});
// Only merge the fallback effect if we haven't found a technique
// implementing the scheme
if (it == techniques.end()) {
ref_ptr<Effect> fallback = makeEffect(fallback_name, false, options);
if (fallback) {
SGPropertyNode *new_root = new SGPropertyNode;
mergePropertyTrees(new_root, effect->root, fallback->root);
effect->root = new_root;
effect->parametersProp = effect->root->getChild("parameters");
}
}
}
}
// Walk the techniques property tree, building techniques and // Walk the techniques property tree, building techniques and
// passes. // passes.
static SGMutex realizeTechniques_lock; static SGMutex realizeTechniques_lock;
bool Effect::realizeTechniques(const SGReaderWriterOptions* options) bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
{ {
SGGuard<SGMutex> g(realizeTechniques_lock); SGGuard<SGMutex> g(realizeTechniques_lock);
mergeSchemesFallbacks(this, options);
if (_isRealized) if (_isRealized)
return true; return true;

View File

@ -92,7 +92,7 @@ public:
SGPropertyNode_ptr root; SGPropertyNode_ptr root;
// Pointer to the parameters node, if it exists // Pointer to the parameters node, if it exists
SGPropertyNode_ptr parametersProp; SGPropertyNode_ptr parametersProp;
Technique* chooseTechnique(osg::RenderInfo* renderInfo); Technique* chooseTechnique(osg::RenderInfo* renderInfo, const std::string &scheme);
virtual void resizeGLObjectBuffers(unsigned int maxSize); virtual void resizeGLObjectBuffers(unsigned int maxSize);
virtual void releaseGLObjects(osg::State* state = 0) const; virtual void releaseGLObjects(osg::State* state = 0) const;
/** /**

View File

@ -34,9 +34,9 @@ namespace simgear
using osgUtil::CullVisitor; using osgUtil::CullVisitor;
EffectCullVisitor::EffectCullVisitor(bool collectLights, Effect *effectOverride) : EffectCullVisitor::EffectCullVisitor(bool collectLights, const std::string &effScheme) :
_collectLights(collectLights), _collectLights(collectLights),
_effectOverride(effectOverride) _effScheme(effScheme)
{ {
} }
@ -62,18 +62,12 @@ void EffectCullVisitor::apply(osg::Geode& node)
if (_collectLights && ( eg->getNodeMask() & MODELLIGHT_BIT ) ) { if (_collectLights && ( eg->getNodeMask() & MODELLIGHT_BIT ) ) {
_lightList.push_back( eg ); _lightList.push_back( eg );
} }
Effect *effect; Effect* effect = eg->getEffect();
if (_effectOverride) { Technique* technique = 0;
effect = _effectOverride;
} else {
effect = eg->getEffect();
if (!effect) { if (!effect) {
CullVisitor::apply(node); CullVisitor::apply(node);
return; return;
} } else if (!(technique = effect->chooseTechnique(&getRenderInfo(), _effScheme))) {
}
Technique* technique = 0;
if (!(technique = effect->chooseTechnique(&getRenderInfo()))) {
return; return;
} }
// push the node's state. // push the node's state.

View File

@ -34,7 +34,7 @@ class EffectGeode;
class EffectCullVisitor : public osgUtil::CullVisitor class EffectCullVisitor : public osgUtil::CullVisitor
{ {
public: public:
EffectCullVisitor(bool collectLights = false, Effect *effectOverride = 0); EffectCullVisitor(bool collectLights = false, const std::string &effScheme = "");
EffectCullVisitor(const EffectCullVisitor&); EffectCullVisitor(const EffectCullVisitor&);
virtual osgUtil::CullVisitor* clone() const; virtual osgUtil::CullVisitor* clone() const;
using osgUtil::CullVisitor::apply; using osgUtil::CullVisitor::apply;
@ -49,7 +49,7 @@ private:
std::map<std::string,osg::ref_ptr<osg::Texture2D> > _bufferList; std::map<std::string,osg::ref_ptr<osg::Texture2D> > _bufferList;
std::vector<osg::ref_ptr<EffectGeode> > _lightList; std::vector<osg::ref_ptr<EffectGeode> > _lightList;
bool _collectLights; bool _collectLights;
osg::ref_ptr<Effect> _effectOverride; std::string _effScheme;
}; };
} }
#endif #endif

View File

@ -98,6 +98,8 @@ public:
void setGLExtensionsPred(float glVersion, void setGLExtensionsPred(float glVersion,
const std::vector<std::string>& extensions); const std::vector<std::string>& extensions);
void refreshValidity(); void refreshValidity();
const std::string &getScheme() const { return _scheme; }
void setScheme(const std::string &scheme) { _scheme = scheme; }
protected: protected:
// Validity of technique in a graphics context. // Validity of technique in a graphics context.
struct ContextInfo : public osg::Referenced struct ContextInfo : public osg::Referenced
@ -117,6 +119,7 @@ protected:
osg::ref_ptr<osg::StateSet> _shadowingStateSet; osg::ref_ptr<osg::StateSet> _shadowingStateSet;
SGSharedPtr<SGExpressionb> _validExpression; SGSharedPtr<SGExpressionb> _validExpression;
int _contextIdLocation; int _contextIdLocation;
std::string _scheme;
}; };
class TechniquePredParser : public expression::ExpressionParser class TechniquePredParser : public expression::ExpressionParser

View File

@ -274,7 +274,7 @@ Compositor::addPass(Pass *pass)
identifier = sceneView->getCullVisitor()->getIdentifier(); identifier = sceneView->getCullVisitor()->getIdentifier();
sceneView->setCullVisitor( sceneView->setCullVisitor(
new EffectCullVisitor(false, pass->effect_override)); new EffectCullVisitor(false, pass->effect_scheme));
sceneView->getCullVisitor()->setIdentifier(identifier.get()); sceneView->getCullVisitor()->setIdentifier(identifier.get());
identifier = sceneView->getCullVisitorLeft()->getIdentifier(); identifier = sceneView->getCullVisitorLeft()->getIdentifier();

View File

@ -67,10 +67,7 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root,
<< " has no name. It won't be addressable by name!"); << " has no name. It won't be addressable by name!");
} }
pass->type = root->getStringValue("type"); pass->type = root->getStringValue("type");
pass->effect_scheme = root->getStringValue("effect-scheme");
std::string eff_override_file = root->getStringValue("effect-override");
if (!eff_override_file.empty())
pass->effect_override = makeEffect(eff_override_file, true, options);
osg::Camera *camera = new Camera; osg::Camera *camera = new Camera;
pass->camera = camera; pass->camera = camera;

View File

@ -57,9 +57,8 @@ struct Pass : public osg::Referenced {
int render_order; int render_order;
std::string name; std::string name;
std::string type; std::string type;
std::string effect_scheme;
osg::ref_ptr<osg::Camera> camera; osg::ref_ptr<osg::Camera> camera;
/** If null, there is no effect override for this pass. */
osg::ref_ptr<Effect> effect_override;
bool useMastersSceneData; bool useMastersSceneData;
osg::Node::NodeMask cull_mask; osg::Node::NodeMask cull_mask;
/** Whether the cull mask is ANDed with the view master camera cull mask. */ /** Whether the cull mask is ANDed with the view master camera cull mask. */