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());
osg::ref_ptr<osgFX::Effect> effect = effects_[selected_fx_].get();
effect->setEnabled(fxen_);
effect->setChild(scene_.get());
effect->removeChild(0, effect->getNumChildren());
effect->addChild(scene_.get());
effect->setUpDemo();
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
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
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
Geometry objects.
This effect defines a preferred technique which uses ARB vertex & fragment
@ -46,11 +46,11 @@ namespace osgFX
"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 "
"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 "
"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 "
"Geometry objects.\n"
"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 */
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();
/** 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;
/** 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);
/** 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();
/** 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;
/** 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);
/**
@ -106,8 +106,8 @@ namespace osgFX
/** prepare a Node for bump lighting, calling prepareGeometry() for each Geometry */
void prepareNode(osg::Node *node);
/** prepare the current child for bump lighting. Actually calls prepareNode(getChild()) */
void prepareChild();
/** prepare children for bump lighting. Actually calls prepareNode() for each child */
void prepareChildren();
/** set up a demo environment with predefined diffuse and normal maps, as well as texture coordinates */
void setUpDemo();

View File

@ -52,10 +52,9 @@ namespace osgFX
selected either manually, with selectTechnique(), or automatically, in which
case the first technique that is supported by all active rendering contexts
is chosen.
If you are an Effect user, then simply use it as a single-child group.
Create an instance of your desired effect, add it to your scene graph (it
is a Node) and call its setChild() method to set its child node as you
would do with a Group.
If you are an Effect user, then simply use it as a node group. Create an
instance of your desired effect, add it to your scene graph and call its
addChild() method to add a child node as you would do with a Group.
If you are an Effect developer, you will have to implement the method
define_techniques() to define the different techniques that can be used
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
soon as all rendering contexts support it.
*/
class OSGFX_EXPORT Effect: public osg::Node {
class OSGFX_EXPORT Effect: public osg::Group {
public:
Effect();
Effect(const Effect &copy, const osg::CopyOp &copyop = osg::CopyOp::SHALLOW_COPY);
@ -95,15 +94,6 @@ namespace osgFX
*/
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 */
inline int getNumTechniques() const;
@ -123,13 +113,15 @@ namespace osgFX
/** select a technique or enable automatic detection */
inline void selectTechnique(int i = AUTO_DETECT);
/** custom traversal */
virtual void traverse(osg::NodeVisitor &nv);
protected:
virtual ~Effect() {}
Effect &operator=(const Effect &) { return *this; }
/** default traversal */
inline void inherited_traverse(osg::NodeVisitor &nv);
virtual bool computeBound() const;
protected:
virtual ~Effect();
Effect &operator=(const Effect &) { return *this; }
/** force rebuilding of techniques on next traversal */
inline void dirtyTechniques();
@ -157,11 +149,11 @@ namespace osgFX
// use int instead of bool to avoid errors
mutable osg::buffered_value<int> tech_selected_;
int global_sel_tech_;
bool techs_defined_;
osg::ref_ptr<osg::Node> child_;
osg::ref_ptr<osg::Geode> dummy_for_validation_;
void build_dummy_node();
@ -179,33 +171,6 @@ namespace osgFX
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
{
return static_cast<int>(techs_.size());
@ -241,6 +206,12 @@ namespace osgFX
techs_defined_ = false;
}
inline void Effect::inherited_traverse(osg::NodeVisitor &nv)
{
typedef osg::Group inherited;
inherited::traverse(nv);
}
}
#endif

View File

@ -37,6 +37,8 @@
namespace osgFX
{
class Effect;
/**
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
@ -79,12 +81,6 @@ namespace osgFX
/** get the number of rendering passes defined in this technique */
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 */
inline osg::StateSet *getPassStateSet(int i);
@ -92,10 +88,12 @@ namespace osgFX
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
*/
virtual void accept(osg::NodeVisitor &nv, osg::Node *child);
inline virtual void traverse(osg::NodeVisitor &nv, Effect *fx);
protected:
Technique(const Technique &): osg::Referenced() {} // copying is nonsense ;)
@ -105,28 +103,11 @@ namespace osgFX
/** force rebuilding of pass nodes on next traversal */
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 */
void addPass(osg::StateSet *ss = 0);
/**
add a new pass to the technique specifying an user-defined pass node.
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;
/** optional: return a node that overrides the child node on a specified pass */
inline virtual osg::Node *getOverrideChild(int) { return 0; }
/**
define the rendering passes that make up this technique. You must
@ -134,53 +115,42 @@ namespace osgFX
*/
virtual void define_passes() = 0;
private:
bool passes_defined_;
/**
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);
osg::ref_ptr<osg::Group> control_node_;
osg::ref_ptr<osg::Node> prev_child_;
private:
typedef std::vector<osg::ref_ptr<osg::StateSet> > Pass_list;
Pass_list passes_;
};
// INLINE METHODS
inline int Technique::getNumPasses() const
{
return static_cast<int>(control_node_->getNumChildren());
}
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));
return static_cast<int>(passes_.size());
}
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
{
return control_node_->getChild(i)->getStateSet();
}
inline osg::Group *Technique::getControlNode()
{
return control_node_.get();
}
inline const osg::Group *Technique::getControlNode() const
{
return control_node_.get();
return passes_[i].get();
}
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 {
public:
enum {
VALIDATOR = 0x56616C69
};
Validator();
Validator(Effect *effect);
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;
inline int compare(const osg::StateAttribute &sa) const;
inline void disable() { effect_ = 0; }
protected:
virtual ~Validator() {}
Validator &operator=(const Validator &) { return *this; }
private:
mutable osg::ref_ptr<Effect> effect_;
mutable Effect *effect_;
};
// INLINE METHODS
@ -59,8 +65,8 @@ namespace osgFX
const Validator *v = dynamic_cast<const Validator *>(&sa);
if (!v) return -1;
if (effect_.get() < v->effect_.get()) return -1;
if (effect_.get() > v->effect_.get()) return 1;
if (effect_ < v->effect_) return -1;
if (effect_ > v->effect_) return 1;
return 0;
}

View File

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

View File

@ -6,10 +6,12 @@
#include <osg/StateAttribute>
#include <osg/Geometry>
#include <osgUtil/CullVisitor>
using namespace osgFX;
Effect::Effect()
: osg::Node(),
: osg::Group(),
enabled_(true),
global_sel_tech_(AUTO_DETECT),
techs_defined_(false)
@ -18,56 +20,59 @@ Effect::Effect()
}
Effect::Effect(const Effect &copy, const osg::CopyOp &copyop)
: osg::Node(copy, copyop),
: osg::Group(copy, copyop),
enabled_(copy.enabled_),
global_sel_tech_(copy.global_sel_tech_),
techs_defined_(false),
child_(static_cast<osg::Node *>(copyop(copy.child_.get())))
techs_defined_(false)
{
build_dummy_node();
}
bool Effect::computeBound() const
Effect::~Effect()
{
_bsphere.init();
_bsphere_computed = true;
if (child_.valid()) {
_bsphere.expandBy(child_->getBound());
// disable the validator for safety, so it won't try to access us
// even if it stays alive for some reason
if (dummy_for_validation_.valid()) {
osg::StateSet *ss = dummy_for_validation_->getStateSet();
if (ss) {
Validator *validator = dynamic_cast<Validator *>(ss->getAttribute(Validator::VALIDATOR));
if (validator) {
validator->disable();
}
}
}
return _bsphere.valid();
}
void Effect::traverse(osg::NodeVisitor &nv)
{
typedef osg::Node Inherited;
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 this effect is not enabled, then go for default traversal
if (!enabled_) {
child_->accept(nv);
Inherited::traverse(nv);
inherited_traverse(nv);
return;
}
// ensure that at least one technique is defined
if (!techs_defined_) {
// clear existing techniques if necessary
// clear existing techniques
techs_.clear();
// clear technique selection indices
sel_tech_.clear();
// clear technique selection flags
tech_selected_.clear();
// define new techniques
techs_defined_ = define_techniques();
// check for errors, return on failure
if (!techs_defined_) {
osg::notify(osg::WARN) << "Warning: osgFX::Effect: could not define techniques for effect " << className() << std::endl;
return;
}
// ensure that at least one technique has been defined
if (techs_.empty()) {
osg::notify(osg::WARN) << "Warning: osgFX::Effect: no techniques defined for effect " << className() << std::endl;
return;
@ -76,7 +81,11 @@ void Effect::traverse(osg::NodeVisitor &nv)
Technique *tech = 0;
// 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;
for (unsigned i=0; i<tech_selected_.size(); ++i) {
if (tech_selected_[i] != 0) {
@ -85,10 +94,14 @@ 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) {
dummy_for_validation_->accept(nv);
}
// 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) {
if (tech_selected_[j] != 0) {
@ -98,21 +111,23 @@ void Effect::traverse(osg::NodeVisitor &nv)
}
}
// found a valid technique?
if (max_index >= 0) {
tech = techs_[max_index].get();
}
} else {
// the active technique was selected manually
tech = techs_[global_sel_tech_].get();
}
// if we could find an active technique, then continue with traversal
if (tech) {
tech->accept(nv, child_.get());
} else {
child_->accept(nv);
tech->traverse(nv, this);
}
Inherited::traverse(nv);
// wow, we're finished! :)
}
void Effect::build_dummy_node()

View File

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

View File

@ -6,7 +6,8 @@
using namespace osgFX;
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)
: 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
{
if (!effect_.valid()) return;
if (!effect_) return;
if (effect_->tech_selected_[state.getContextID()] == 0) {
Effect::Technique_list::iterator i;

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ osgDB::RegisterDotOsgWrapperProxy Effect_Proxy
(
0,
"osgFX::Effect",
"Object Node osgFX::Effect",
"Object Node Group osgFX::Effect",
Effect_readLocalData,
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;
}
@ -65,7 +60,6 @@ bool Effect_writeLocalData(const osg::Object &obj, osgDB::Output &fw)
} else {
fw << myobj.getSelectedTechnique() << "\n";
}
fw.writeObject(*myobj.getChild());
return true;
}

View File

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

View File

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