Updates to osgFX, from Marco Jez, to map Effect across to being derived

from osg::Group rather than from osg::Node.
This commit is contained in:
Robert Osfield 2003-09-09 11:54:05 +00:00
parent 5c3c0ed17c
commit 584f805327
15 changed files with 172 additions and 223 deletions

View File

@ -180,7 +180,8 @@ protected:
root_->removeChild(0, root_->getNumChildren()); root_->removeChild(0, root_->getNumChildren());
osg::ref_ptr<osgFX::Effect> effect = effects_[selected_fx_].get(); osg::ref_ptr<osgFX::Effect> effect = effects_[selected_fx_].get();
effect->setEnabled(fxen_); effect->setEnabled(fxen_);
effect->setChild(scene_.get()); effect->removeChild(0, effect->getNumChildren());
effect->addChild(scene_.get());
effect->setUpDemo(); effect->setUpDemo();
root_->addChild(effect.get()); root_->addChild(effect.get());
} }

View File

@ -25,11 +25,11 @@ namespace osgFX
{ {
/** /**
This effect makes surfaces appear bumpy. The child node must use two textures, This effect makes surfaces appear bumpy. Children nodes must use two textures,
one for diffuse color and one for the normal map (which can be created one for diffuse color and one for the normal map (which can be created
from a height map with tools like nVIDIA's normal map generator). Furthermore, from a height map with tools like nVIDIA's normal map generator). Furthermore,
tangent-space basis vectors must be created and assigned to each Geometry; this tangent-space basis vectors must be created and assigned to each Geometry; this
can be done quickly by calling BumpMapping::prepareChild(). Note that both can be done quickly by calling BumpMapping::prepareChildren(). Note that both
diffuse and normal map textures must have corresponding UV maps defined in diffuse and normal map textures must have corresponding UV maps defined in
Geometry objects. Geometry objects.
This effect defines a preferred technique which uses ARB vertex & fragment This effect defines a preferred technique which uses ARB vertex & fragment
@ -46,11 +46,11 @@ namespace osgFX
"Bump Mapping", "Bump Mapping",
"This effect makes surfaces appear bumpy. The child node must use two textures, " "This effect makes surfaces appear bumpy. Children nodes must use two textures, "
"one for diffuse color and one for the normal map (which can be created " "one for diffuse color and one for the normal map (which can be created "
"from a height map with tools like nVIDIA's normal map generator). Furthermore, " "from a height map with tools like nVIDIA's normal map generator). Furthermore, "
"tangent-space basis vectors must be created and assigned to each Geometry; this " "tangent-space basis vectors must be created and assigned to each Geometry; this "
"can be done quickly by calling BumpMapping::prepareChild(). Note that both " "can be done quickly by calling BumpMapping::prepareChildren(). Note that both "
"diffuse and normal map textures must have corresponding UV maps defined in " "diffuse and normal map textures must have corresponding UV maps defined in "
"Geometry objects.\n" "Geometry objects.\n"
"This effect defines a preferred technique which uses ARB vertex & fragment " "This effect defines a preferred technique which uses ARB vertex & fragment "
@ -79,22 +79,22 @@ namespace osgFX
/** set the texture unit that contains normal map texture. Default is 0 */ /** set the texture unit that contains normal map texture. Default is 0 */
inline void setNormalMapTextureUnit(int n); inline void setNormalMapTextureUnit(int n);
/** get the diffuse color texture that overrides the child's texture */ /** get the diffuse color texture that overrides children's texture */
inline osg::Texture2D *getOverrideDiffuseTexture(); inline osg::Texture2D *getOverrideDiffuseTexture();
/** get the const diffuse color texture that overrides the child's texture */ /** get the const diffuse color texture that overrides children's texture */
inline const osg::Texture2D *getOverrideDiffuseTexture() const; inline const osg::Texture2D *getOverrideDiffuseTexture() const;
/** set the diffuse color texture that overrides the child's texture */ /** set the diffuse color texture that overrides children's texture */
inline void setOverrideDiffuseTexture(osg::Texture2D *texture); inline void setOverrideDiffuseTexture(osg::Texture2D *texture);
/** get the normal map texture that overrides the child's texture */ /** get the normal map texture that overrides children's texture */
inline osg::Texture2D *getOverrideNormalMapTexture(); inline osg::Texture2D *getOverrideNormalMapTexture();
/** get the const normal map texture that overrides the child's texture */ /** get the const normal map texture that overrides children's texture */
inline const osg::Texture2D *getOverrideNormalMapTexture() const; inline const osg::Texture2D *getOverrideNormalMapTexture() const;
/** set the normal map texture that overrides the child's texture */ /** set the normal map texture that overrides children's texture */
inline void setOverrideNormalMapTexture(osg::Texture2D *texture); inline void setOverrideNormalMapTexture(osg::Texture2D *texture);
/** /**
@ -106,8 +106,8 @@ namespace osgFX
/** prepare a Node for bump lighting, calling prepareGeometry() for each Geometry */ /** prepare a Node for bump lighting, calling prepareGeometry() for each Geometry */
void prepareNode(osg::Node *node); void prepareNode(osg::Node *node);
/** prepare the current child for bump lighting. Actually calls prepareNode(getChild()) */ /** prepare children for bump lighting. Actually calls prepareNode() for each child */
void prepareChild(); void prepareChildren();
/** set up a demo environment with predefined diffuse and normal maps, as well as texture coordinates */ /** set up a demo environment with predefined diffuse and normal maps, as well as texture coordinates */
void setUpDemo(); void setUpDemo();

View File

@ -52,10 +52,9 @@ namespace osgFX
selected either manually, with selectTechnique(), or automatically, in which selected either manually, with selectTechnique(), or automatically, in which
case the first technique that is supported by all active rendering contexts case the first technique that is supported by all active rendering contexts
is chosen. is chosen.
If you are an Effect user, then simply use it as a single-child group. If you are an Effect user, then simply use it as a node group. Create an
Create an instance of your desired effect, add it to your scene graph (it instance of your desired effect, add it to your scene graph and call its
is a Node) and call its setChild() method to set its child node as you addChild() method to add a child node as you would do with a Group.
would do with a Group.
If you are an Effect developer, you will have to implement the method If you are an Effect developer, you will have to implement the method
define_techniques() to define the different techniques that can be used define_techniques() to define the different techniques that can be used
for obtaining the desired effect. In define_techniques() you will usually for obtaining the desired effect. In define_techniques() you will usually
@ -64,7 +63,7 @@ namespace osgFX
techniques added first will have higher priority and will be used first as techniques added first will have higher priority and will be used first as
soon as all rendering contexts support it. soon as all rendering contexts support it.
*/ */
class OSGFX_EXPORT Effect: public osg::Node { class OSGFX_EXPORT Effect: public osg::Group {
public: public:
Effect(); Effect();
Effect(const Effect &copy, const osg::CopyOp &copyop = osg::CopyOp::SHALLOW_COPY); Effect(const Effect &copy, const osg::CopyOp &copyop = osg::CopyOp::SHALLOW_COPY);
@ -95,15 +94,6 @@ namespace osgFX
*/ */
inline virtual void setUpDemo() {} inline virtual void setUpDemo() {}
/** get the const child node */
inline const osg::Node *getChild() const;
/** get the child node */
inline osg::Node *getChild();
/** set the child node */
inline void setChild(osg::Node *child);
/** get the number of techniques defined for this effect */ /** get the number of techniques defined for this effect */
inline int getNumTechniques() const; inline int getNumTechniques() const;
@ -123,14 +113,16 @@ namespace osgFX
/** select a technique or enable automatic detection */ /** select a technique or enable automatic detection */
inline void selectTechnique(int i = AUTO_DETECT); inline void selectTechnique(int i = AUTO_DETECT);
virtual void traverse(osg::NodeVisitor &nv); /** custom traversal */
virtual void traverse(osg::NodeVisitor &nv);
/** default traversal */
inline void inherited_traverse(osg::NodeVisitor &nv);
protected: protected:
virtual ~Effect() {} virtual ~Effect();
Effect &operator=(const Effect &) { return *this; } Effect &operator=(const Effect &) { return *this; }
virtual bool computeBound() const;
/** force rebuilding of techniques on next traversal */ /** force rebuilding of techniques on next traversal */
inline void dirtyTechniques(); inline void dirtyTechniques();
@ -144,7 +136,7 @@ namespace osgFX
this method. this method.
*/ */
virtual bool define_techniques() = 0; virtual bool define_techniques() = 0;
private: private:
friend class Validator; friend class Validator;
@ -157,11 +149,11 @@ namespace osgFX
// use int instead of bool to avoid errors // use int instead of bool to avoid errors
mutable osg::buffered_value<int> tech_selected_; mutable osg::buffered_value<int> tech_selected_;
int global_sel_tech_; int global_sel_tech_;
bool techs_defined_; bool techs_defined_;
osg::ref_ptr<osg::Node> child_;
osg::ref_ptr<osg::Geode> dummy_for_validation_; osg::ref_ptr<osg::Geode> dummy_for_validation_;
void build_dummy_node(); void build_dummy_node();
@ -179,33 +171,6 @@ namespace osgFX
enabled_ = v; enabled_ = v;
} }
inline const osg::Node *Effect::getChild() const
{
return child_.get();
}
inline osg::Node *Effect::getChild()
{
return child_.get();
}
inline void Effect::setChild(osg::Node *child)
{
child_ = child;
setNumChildrenRequiringUpdateTraversal(0);
setNumChildrenWithCullingDisabled(0);
setNumChildrenWithOccluderNodes(0);
if (child) {
if (child->getNumChildrenRequiringUpdateTraversal() > 0 || child->getUpdateCallback())
setNumChildrenRequiringUpdateTraversal(1);
if (child->getNumChildrenWithCullingDisabled() > 0 || !child->getCullingActive())
setNumChildrenWithCullingDisabled(1);
if (child->getNumChildrenWithOccluderNodes() > 0 || dynamic_cast<osg::OccluderNode *>(child))
setNumChildrenWithOccluderNodes(1);
}
dirtyBound();
}
inline int Effect::getNumTechniques() const inline int Effect::getNumTechniques() const
{ {
return static_cast<int>(techs_.size()); return static_cast<int>(techs_.size());
@ -240,6 +205,12 @@ namespace osgFX
{ {
techs_defined_ = false; techs_defined_ = false;
} }
inline void Effect::inherited_traverse(osg::NodeVisitor &nv)
{
typedef osg::Group inherited;
inherited::traverse(nv);
}
} }

View File

@ -37,6 +37,8 @@
namespace osgFX namespace osgFX
{ {
class Effect;
/** /**
This is the base class for effect techniques. A technique represents one This is the base class for effect techniques. A technique represents one
of the possible ways to implement a special effect. This base class is of the possible ways to implement a special effect. This base class is
@ -79,12 +81,6 @@ namespace osgFX
/** get the number of rendering passes defined in this technique */ /** get the number of rendering passes defined in this technique */
inline int getNumPasses() const; inline int getNumPasses() const;
/** get the Group object associated to the i-th pass */
inline osg::Group *getPassGroup(int i);
/** get the const Group object associated to the i-th pass */
inline const osg::Group *getPassGroup(int i) const;
/** get the StateSet object associated to the i-th pass */ /** get the StateSet object associated to the i-th pass */
inline osg::StateSet *getPassStateSet(int i); inline osg::StateSet *getPassStateSet(int i);
@ -92,11 +88,13 @@ namespace osgFX
inline const osg::StateSet *getPassStateSet(int i) const; inline const osg::StateSet *getPassStateSet(int i) const;
/** /**
traverses the child nodes with multipass if necessary. traverse children with multipass if necessary. By default this method
simply calls the protected method traverse_implementation(); you can
override it to change the default behavior.
Don't call this method directly as it is called by osgFX::Effect Don't call this method directly as it is called by osgFX::Effect
*/ */
virtual void accept(osg::NodeVisitor &nv, osg::Node *child); inline virtual void traverse(osg::NodeVisitor &nv, Effect *fx);
protected: protected:
Technique(const Technique &): osg::Referenced() {} // copying is nonsense ;) Technique(const Technique &): osg::Referenced() {} // copying is nonsense ;)
virtual ~Technique() {} virtual ~Technique() {}
@ -105,82 +103,54 @@ namespace osgFX
/** force rebuilding of pass nodes on next traversal */ /** force rebuilding of pass nodes on next traversal */
inline void dirtyPasses(); inline void dirtyPasses();
/** optional: return a node that overrides the child node on a specified pass */
inline virtual osg::Node *getOverrideChild(int) { return 0; }
/** create a new pass node, add it to the technique and associate a StateSet */ /** create a new pass node, add it to the technique and associate a StateSet */
void addPass(osg::StateSet *ss = 0); void addPass(osg::StateSet *ss = 0);
/** /** optional: return a node that overrides the child node on a specified pass */
add a new pass to the technique specifying an user-defined pass node. inline virtual osg::Node *getOverrideChild(int) { return 0; }
You should call this version of addPass() only when you need to gain direct
control over the pass node (i.e. for setting up a cull callback); otherwise
please use addPass(StateSet*) and let the class create the pass node for you.
*/
void addPass(osg::Group *pass);
/**
get the control node which holds the user's subgraph.
You may want to do something on it like applying a cull callback.
*/
inline osg::Group *getControlNode();
/** get the const control node which holds the user's subgraph */
inline const osg::Group *getControlNode() const;
/** /**
define the rendering passes that make up this technique. You must define the rendering passes that make up this technique. You must
implement this method in derived classes to add the required passes. implement this method in derived classes to add the required passes.
*/ */
virtual void define_passes() = 0; virtual void define_passes() = 0;
/**
traverse children with multipass if necessary. Don't call this method
directly unless you are in a customized version of traverse().
*/
void traverse_implementation(osg::NodeVisitor &nv, Effect *fx);
private: private:
bool passes_defined_; typedef std::vector<osg::ref_ptr<osg::StateSet> > Pass_list;
Pass_list passes_;
osg::ref_ptr<osg::Group> control_node_;
osg::ref_ptr<osg::Node> prev_child_;
}; };
// INLINE METHODS // INLINE METHODS
inline int Technique::getNumPasses() const inline int Technique::getNumPasses() const
{ {
return static_cast<int>(control_node_->getNumChildren()); return static_cast<int>(passes_.size());
}
inline osg::Group *Technique::getPassGroup(int i)
{
return static_cast<osg::Group *>(control_node_->getChild(i));
}
inline const osg::Group *Technique::getPassGroup(int i) const
{
return static_cast<const osg::Group *>(control_node_->getChild(i));
} }
inline osg::StateSet *Technique::getPassStateSet(int i) inline osg::StateSet *Technique::getPassStateSet(int i)
{ {
return control_node_->getChild(i)->getStateSet(); return passes_[i].get();
} }
inline const osg::StateSet *Technique::getPassStateSet(int i) const inline const osg::StateSet *Technique::getPassStateSet(int i) const
{ {
return control_node_->getChild(i)->getStateSet(); return passes_[i].get();
} }
inline osg::Group *Technique::getControlNode()
{
return control_node_.get();
}
inline const osg::Group *Technique::getControlNode() const
{
return control_node_.get();
}
inline void Technique::dirtyPasses() inline void Technique::dirtyPasses()
{ {
passes_defined_ = false; passes_.clear();
}
inline void Technique::traverse(osg::NodeVisitor &nv, Effect *fx)
{
traverse_implementation(nv, fx);
} }
} }

View File

@ -34,22 +34,28 @@ namespace osgFX
*/ */
class OSGFX_EXPORT Validator: public osg::StateAttribute { class OSGFX_EXPORT Validator: public osg::StateAttribute {
public: public:
enum {
VALIDATOR = 0x56616C69
};
Validator(); Validator();
Validator(Effect *effect); Validator(Effect *effect);
Validator(const Validator &copy, const osg::CopyOp &copyop = osg::CopyOp::SHALLOW_COPY); Validator(const Validator &copy, const osg::CopyOp &copyop = osg::CopyOp::SHALLOW_COPY);
META_StateAttribute(osgFX, Validator, 0x56616C69); META_StateAttribute(osgFX, Validator, VALIDATOR);
void apply(osg::State &state) const; void apply(osg::State &state) const;
inline int compare(const osg::StateAttribute &sa) const; inline int compare(const osg::StateAttribute &sa) const;
inline void disable() { effect_ = 0; }
protected: protected:
virtual ~Validator() {} virtual ~Validator() {}
Validator &operator=(const Validator &) { return *this; } Validator &operator=(const Validator &) { return *this; }
private: private:
mutable osg::ref_ptr<Effect> effect_; mutable Effect *effect_;
}; };
// INLINE METHODS // INLINE METHODS
@ -59,8 +65,8 @@ namespace osgFX
const Validator *v = dynamic_cast<const Validator *>(&sa); const Validator *v = dynamic_cast<const Validator *>(&sa);
if (!v) return -1; if (!v) return -1;
if (effect_.get() < v->effect_.get()) return -1; if (effect_ < v->effect_) return -1;
if (effect_.get() > v->effect_.get()) return 1; if (effect_ > v->effect_) return 1;
return 0; return 0;
} }

View File

@ -599,22 +599,23 @@ void BumpMapping::prepareNode(osg::Node *node)
node->accept(tv); node->accept(tv);
} }
void BumpMapping::prepareChild() void BumpMapping::prepareChildren()
{ {
if (getChild()) for (unsigned i=0; i<getNumChildren(); ++i)
prepareNode(getChild()); prepareNode(getChild(i));
} }
void BumpMapping::setUpDemo() void BumpMapping::setUpDemo()
{ {
// generate texture coordinates // generate texture coordinates
TexCoordGenerator tcg(diffuseunit_, normalunit_); TexCoordGenerator tcg(diffuseunit_, normalunit_);
getChild()->accept(tcg); for (unsigned i=0; i<getNumChildren(); ++i)
getChild(i)->accept(tcg);
// set up diffuse texture // set up diffuse texture
if (!diffuse_tex_.valid()) { if (!diffuse_tex_.valid()) {
diffuse_tex_ = new osg::Texture2D; diffuse_tex_ = new osg::Texture2D;
diffuse_tex_->setImage(osgDB::readImageFile("whitemetal_diffuse.jpg")); diffuse_tex_->setImage(osgDB::readImageFile("Images/whitemetal_diffuse.jpg"));
diffuse_tex_->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); diffuse_tex_->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
diffuse_tex_->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); diffuse_tex_->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
diffuse_tex_->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); diffuse_tex_->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
@ -625,7 +626,7 @@ void BumpMapping::setUpDemo()
// set up normal map texture // set up normal map texture
if (!normal_tex_.valid()) { if (!normal_tex_.valid()) {
normal_tex_ = new osg::Texture2D; normal_tex_ = new osg::Texture2D;
normal_tex_->setImage(osgDB::readImageFile("whitemetal_normal.jpg")); normal_tex_->setImage(osgDB::readImageFile("Images/whitemetal_normal.jpg"));
normal_tex_->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); normal_tex_->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
normal_tex_->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); normal_tex_->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
normal_tex_->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); normal_tex_->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
@ -634,7 +635,7 @@ void BumpMapping::setUpDemo()
} }
// generate tangent-space basis vector // generate tangent-space basis vector
prepareChild(); prepareChildren();
// recreate techniques on next step // recreate techniques on next step
dirtyTechniques(); dirtyTechniques();

View File

@ -6,10 +6,12 @@
#include <osg/StateAttribute> #include <osg/StateAttribute>
#include <osg/Geometry> #include <osg/Geometry>
#include <osgUtil/CullVisitor>
using namespace osgFX; using namespace osgFX;
Effect::Effect() Effect::Effect()
: osg::Node(), : osg::Group(),
enabled_(true), enabled_(true),
global_sel_tech_(AUTO_DETECT), global_sel_tech_(AUTO_DETECT),
techs_defined_(false) techs_defined_(false)
@ -18,56 +20,59 @@ Effect::Effect()
} }
Effect::Effect(const Effect &copy, const osg::CopyOp &copyop) Effect::Effect(const Effect &copy, const osg::CopyOp &copyop)
: osg::Node(copy, copyop), : osg::Group(copy, copyop),
enabled_(copy.enabled_), enabled_(copy.enabled_),
global_sel_tech_(copy.global_sel_tech_), global_sel_tech_(copy.global_sel_tech_),
techs_defined_(false), techs_defined_(false)
child_(static_cast<osg::Node *>(copyop(copy.child_.get())))
{ {
build_dummy_node(); build_dummy_node();
} }
bool Effect::computeBound() const Effect::~Effect()
{ {
_bsphere.init(); // disable the validator for safety, so it won't try to access us
_bsphere_computed = true; // even if it stays alive for some reason
if (dummy_for_validation_.valid()) {
if (child_.valid()) { osg::StateSet *ss = dummy_for_validation_->getStateSet();
_bsphere.expandBy(child_->getBound()); if (ss) {
} Validator *validator = dynamic_cast<Validator *>(ss->getAttribute(Validator::VALIDATOR));
if (validator) {
return _bsphere.valid(); validator->disable();
}
}
}
} }
void Effect::traverse(osg::NodeVisitor &nv) void Effect::traverse(osg::NodeVisitor &nv)
{ {
typedef osg::Node Inherited; // if this effect is not enabled, then go for default traversal
if (!child_.valid()) return;
// we are not a Group, so children will not notify us when their
// bounding box has changed. We need to recompute it at each traversal... :(
dirtyBound();
if (!enabled_) { if (!enabled_) {
child_->accept(nv); inherited_traverse(nv);
Inherited::traverse(nv);
return; return;
} }
// ensure that at least one technique is defined
if (!techs_defined_) { if (!techs_defined_) {
// clear existing techniques if necessary // clear existing techniques
techs_.clear(); techs_.clear();
// clear technique selection indices
sel_tech_.clear(); sel_tech_.clear();
// clear technique selection flags
tech_selected_.clear(); tech_selected_.clear();
// define new techniques // define new techniques
techs_defined_ = define_techniques(); techs_defined_ = define_techniques();
// check for errors, return on failure
if (!techs_defined_) { if (!techs_defined_) {
osg::notify(osg::WARN) << "Warning: osgFX::Effect: could not define techniques for effect " << className() << std::endl; osg::notify(osg::WARN) << "Warning: osgFX::Effect: could not define techniques for effect " << className() << std::endl;
return; return;
} }
// ensure that at least one technique has been defined
if (techs_.empty()) { if (techs_.empty()) {
osg::notify(osg::WARN) << "Warning: osgFX::Effect: no techniques defined for effect " << className() << std::endl; osg::notify(osg::WARN) << "Warning: osgFX::Effect: no techniques defined for effect " << className() << std::endl;
return; return;
@ -76,7 +81,11 @@ void Effect::traverse(osg::NodeVisitor &nv)
Technique *tech = 0; Technique *tech = 0;
if (global_sel_tech_ == AUTO_DETECT) { // if the selection mode is set to AUTO_DETECT then we have to
// choose the active technique!
if (global_sel_tech_ == AUTO_DETECT) {
// test whether at least one technique has been selected
bool none_selected = true; bool none_selected = true;
for (unsigned i=0; i<tech_selected_.size(); ++i) { for (unsigned i=0; i<tech_selected_.size(); ++i) {
if (tech_selected_[i] != 0) { if (tech_selected_[i] != 0) {
@ -85,11 +94,15 @@ void Effect::traverse(osg::NodeVisitor &nv)
} }
} }
// no techniques selected, traverse a dummy node that
// contains the Validator (it will select a technique)
if (none_selected) { if (none_selected) {
dummy_for_validation_->accept(nv); dummy_for_validation_->accept(nv);
} }
int max_index = -1; // find the highest priority technique that could be validated
// in all active rendering contexts
int max_index = -1;
for (unsigned j=0; j<sel_tech_.size(); ++j) { for (unsigned j=0; j<sel_tech_.size(); ++j) {
if (tech_selected_[j] != 0) { if (tech_selected_[j] != 0) {
if (sel_tech_[j] > max_index) { if (sel_tech_[j] > max_index) {
@ -98,26 +111,28 @@ void Effect::traverse(osg::NodeVisitor &nv)
} }
} }
if (max_index >= 0) { // found a valid technique?
if (max_index >= 0) {
tech = techs_[max_index].get(); tech = techs_[max_index].get();
} }
} else { } else {
// the active technique was selected manually
tech = techs_[global_sel_tech_].get(); tech = techs_[global_sel_tech_].get();
} }
if (tech) { // if we could find an active technique, then continue with traversal
tech->accept(nv, child_.get()); if (tech) {
} else { tech->traverse(nv, this);
child_->accept(nv); }
}
Inherited::traverse(nv); // wow, we're finished! :)
} }
void Effect::build_dummy_node() void Effect::build_dummy_node()
{ {
dummy_for_validation_ = new osg::Geode; dummy_for_validation_ = new osg::Geode;
osg::ref_ptr<osg::Geometry> geo = new osg::Geometry; osg::ref_ptr<osg::Geometry> geo = new osg::Geometry;
dummy_for_validation_->addDrawable(geo.get()); dummy_for_validation_->addDrawable(geo.get());
dummy_for_validation_->getOrCreateStateSet()->setAttribute(new Validator(this)); dummy_for_validation_->getOrCreateStateSet()->setAttribute(new Validator(this));

View File

@ -1,30 +1,23 @@
#include <osgFX/Technique> #include <osgFX/Technique>
#include <osgFX/Effect>
#include <osg/GLExtensions> #include <osg/GLExtensions>
#include <osgUtil/CullVisitor>
using namespace osgFX; using namespace osgFX;
Technique::Technique() Technique::Technique()
: osg::Referenced(), : osg::Referenced()
passes_defined_(false),
control_node_(new osg::Group)
{ {
} }
void Technique::addPass(osg::StateSet *ss) void Technique::addPass(osg::StateSet *ss)
{ {
osg::ref_ptr<osg::Group> pass = new osg::Group; if (ss) {
control_node_->addChild(pass.get()); passes_.push_back(ss);
ss->setRenderBinDetails(static_cast<int>(passes_.size()), "RenderBin");
if (ss) { }
ss->setRenderBinDetails(static_cast<int>(control_node_->getNumChildren()), "RenderBin");
pass->setStateSet(ss);
}
}
void Technique::addPass(osg::Group *pass)
{
control_node_->addChild(pass);
} }
bool Technique::validate(osg::State &) const bool Technique::validate(osg::State &) const
@ -41,39 +34,36 @@ bool Technique::validate(osg::State &) const
return true; return true;
} }
void Technique::accept(osg::NodeVisitor &nv, osg::Node *child) void Technique::traverse_implementation(osg::NodeVisitor &nv, Effect *fx)
{ {
// define passes if necessary // define passes if necessary
if (!passes_defined_) { if (passes_.empty()) {
// clear existing pass nodes
if (control_node_->getNumChildren() > 0) {
control_node_->removeChild(0, control_node_->getNumChildren());
}
define_passes(); define_passes();
passes_defined_ = true;
} }
// update pass children if necessary // special actions must be taken if the node visitor is actually a CullVisitor
if (child != prev_child_.get()) { osgUtil::CullVisitor *cv = dynamic_cast<osgUtil::CullVisitor *>(&nv);
for (unsigned i=0; i<control_node_->getNumChildren(); ++i) {
osg::Group *pass = dynamic_cast<osg::Group *>(control_node_->getChild(i));
if (pass) {
if (pass->getNumChildren() > 0) {
pass->removeChild(0, pass->getNumChildren());
}
osg::Node *oc = getOverrideChild(i);
if (oc) {
pass->addChild(oc);
} else {
pass->addChild(child);
}
}
}
prev_child_ = child;
}
// traverse the control node // traverse all passes
control_node_->accept(nv); for (int i=0; i<getNumPasses(); ++i) {
// push the i-th pass' StateSet if necessary
if (cv) {
cv->pushStateSet(passes_[i].get());
}
// traverse the override node if defined, otherwise
// traverse children as a Group would do
osg::Node *override = getOverrideChild(i);
if (override) {
override->accept(nv);
} else {
fx->inherited_traverse(nv);
}
// pop the StateSet if necessary
if (cv) {
cv->popStateSet();
}
}
} }

View File

@ -6,7 +6,8 @@
using namespace osgFX; using namespace osgFX;
Validator::Validator() Validator::Validator()
: osg::StateAttribute() : osg::StateAttribute(),
effect_(0)
{ {
} }
@ -18,13 +19,13 @@ Validator::Validator(Effect *effect)
Validator::Validator(const Validator &copy, const osg::CopyOp &copyop) Validator::Validator(const Validator &copy, const osg::CopyOp &copyop)
: osg::StateAttribute(copy, copyop), : osg::StateAttribute(copy, copyop),
effect_(static_cast<Effect *>(copyop(copy.effect_.get()))) effect_(static_cast<Effect *>(copyop(copy.effect_)))
{ {
} }
void Validator::apply(osg::State &state) const void Validator::apply(osg::State &state) const
{ {
if (!effect_.valid()) return; if (!effect_) return;
if (effect_->tech_selected_[state.getContextID()] == 0) { if (effect_->tech_selected_[state.getContextID()] == 0) {
Effect::Technique_list::iterator i; Effect::Technique_list::iterator i;

View File

@ -11,7 +11,7 @@ osgDB::RegisterDotOsgWrapperProxy AnisotropicLighting_Proxy
( (
new osgFX::AnisotropicLighting, new osgFX::AnisotropicLighting,
"osgFX::AnisotropicLighting", "osgFX::AnisotropicLighting",
"Object Node osgFX::Effect osgFX::AnisotropicLighting", "Object Node Group osgFX::Effect osgFX::AnisotropicLighting",
AnisotropicLighting_readLocalData, AnisotropicLighting_readLocalData,
AnisotropicLighting_writeLocalData AnisotropicLighting_writeLocalData
); );

View File

@ -11,7 +11,7 @@ osgDB::RegisterDotOsgWrapperProxy BumpMapping_Proxy
( (
new osgFX::BumpMapping, new osgFX::BumpMapping,
"osgFX::BumpMapping", "osgFX::BumpMapping",
"Object Node osgFX::Effect osgFX::BumpMapping", "Object Node Group osgFX::Effect osgFX::BumpMapping",
BumpMapping_readLocalData, BumpMapping_readLocalData,
BumpMapping_writeLocalData BumpMapping_writeLocalData
); );

View File

@ -11,7 +11,7 @@ osgDB::RegisterDotOsgWrapperProxy Cartoon_Proxy
( (
new osgFX::Cartoon, new osgFX::Cartoon,
"osgFX::Cartoon", "osgFX::Cartoon",
"Object Node osgFX::Effect osgFX::Cartoon", "Object Node Group osgFX::Effect osgFX::Cartoon",
Cartoon_readLocalData, Cartoon_readLocalData,
Cartoon_writeLocalData Cartoon_writeLocalData
); );

View File

@ -11,7 +11,7 @@ osgDB::RegisterDotOsgWrapperProxy Effect_Proxy
( (
0, 0,
"osgFX::Effect", "osgFX::Effect",
"Object Node osgFX::Effect", "Object Node Group osgFX::Effect",
Effect_readLocalData, Effect_readLocalData,
Effect_writeLocalData Effect_writeLocalData
); );
@ -46,11 +46,6 @@ bool Effect_readLocalData(osg::Object &obj, osgDB::Input &fr)
} }
} }
osg::ref_ptr<osg::Node> node = static_cast<osg::Node *>(fr.readObjectOfType(osgDB::type_wrapper<osg::Node>()));
if (node.valid()) {
myobj.setChild(node.get());
}
return itAdvanced; return itAdvanced;
} }
@ -65,7 +60,6 @@ bool Effect_writeLocalData(const osg::Object &obj, osgDB::Output &fw)
} else { } else {
fw << myobj.getSelectedTechnique() << "\n"; fw << myobj.getSelectedTechnique() << "\n";
} }
fw.writeObject(*myobj.getChild());
return true; return true;
} }

View File

@ -11,7 +11,7 @@ osgDB::RegisterDotOsgWrapperProxy Scribe_Proxy
( (
new osgFX::Scribe, new osgFX::Scribe,
"osgFX::Scribe", "osgFX::Scribe",
"Object Node osgFX::Effect osgFX::Scribe", "Object Node Group osgFX::Effect osgFX::Scribe",
Scribe_readLocalData, Scribe_readLocalData,
Scribe_writeLocalData Scribe_writeLocalData
); );

View File

@ -11,7 +11,7 @@ osgDB::RegisterDotOsgWrapperProxy SpecularHighlights_Proxy
( (
new osgFX::SpecularHighlights, new osgFX::SpecularHighlights,
"osgFX::SpecularHighlights", "osgFX::SpecularHighlights",
"Object Node osgFX::Effect osgFX::SpecularHighlights", "Object Node Group osgFX::Effect osgFX::SpecularHighlights",
SpecularHighlights_readLocalData, SpecularHighlights_readLocalData,
SpecularHighlights_writeLocalData SpecularHighlights_writeLocalData
); );