Hi,
I've been playing around with the "Computing the world bounding box of
any node" example in the Cookbook, which uses a ShapeDrawable. I've read in the
Doxygen docs that:
>
> The implementation of ShapeDrawable is not geared to efficiency; it's better
> to think of it as a convenience to render Shapes easily (perhaps for test or
> debugging purposes) than as the right way to render basic shapes in some
> efficiency-critical section of code.
>
So, I thought I would try to create a custom box shape using a Geometry:
osgshapes.h
Code:
#ifndef OSGSHAPES_H
#define OSGSHAPES_H
#include "osg/PositionAttitudeTransform"
namespace osg {
class Geode;
}
class Shape: public osg::PositionAttitudeTransform {
public:
Shape( const bool w = true );
virtual bool create() = 0;
int getScaleFactor() const
{
return scaleFactor;
}
void setScaleFactor( const int s );
bool isWireframe() const
{
return wire;
}
void setWireframe( const bool w );
protected:
virtual ~Shape();
private:
bool wire;
int scaleFactor;
};
class Box: public Shape {
public:
Box( const double l = 1. );
bool create();
double getLenght() const
{
return length;
}
void setLenght( const double l );
protected:
~Box();
private:
double length;
};
#endif
osgshapes.cpp
Code:
namespace {
namespace BoxFactory {
void drawBox( osg::ref_ptr<osg::Vec3Array> vert )
{
vert->push_back( osg::Vec3d( -1.0, -1.0, 1.0 ) );
vert->push_back( osg::Vec3d( 1.0, -1.0, 1.0 ) );
vert->push_back( osg::Vec3d( 1.0, 1.0, 1.0 ) );
vert->push_back( osg::Vec3d( -1.0, 1.0, 1.0 ) );
vert->push_back( osg::Vec3d( -1.0, -1.0, -1.0 ) );
vert->push_back( osg::Vec3d( 1.0, -1.0, -1.0 ) );
vert->push_back( osg::Vec3d( 1.0, 1.0, -1.0 ) );
vert->push_back( osg::Vec3d( -1.0, 1.0, -1.0 ) );
}
osg::ref_ptr<osg::DrawElementsUInt> getQuads()
{
osg::ref_ptr<osg::DrawElementsUInt> quads =
new osg::DrawElementsUInt( GL_QUADS )
;
// FRONT
quads->push_back( 0 );
quads->push_back( 1 );
quads->push_back( 2 );
quads->push_back( 3 );
// BOTTOM
quads->push_back( 0 );
quads->push_back( 1 );
quads->push_back( 5 );
quads->push_back( 4 );
// LEFT
quads->push_back( 0 );
quads->push_back( 4 );
quads->push_back( 7 );
quads->push_back( 3 );
// BACK
quads->push_back( 4 );
quads->push_back( 5 );
quads->push_back( 6 );
quads->push_back( 7 );
// TOP
quads->push_back( 3 );
quads->push_back( 2 );
quads->push_back( 6 );
quads->push_back( 7 );
// RIGHT
quads->push_back( 1 );
quads->push_back( 5 );
quads->push_back( 6 );
quads->push_back( 2 );
return quads;
}
osg::ref_ptr<osg::Geode> buildBox()
{
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
geode->addDrawable( geom.get() );
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
geom->setVertexArray( vertices );
// geom->setDataVariance( osg::Object::DYNAMIC );
// geom->setUseDisplayList( false );
// geom->setUseVertexBufferObjects( true );
drawBox( vertices );
osg::ref_ptr<osg::DrawElementsUInt> quads = getQuads();
geom->addPrimitiveSet( quads );
geom->getOrCreateStateSet()->setAttributeAndModes( new osg::Point(
5.0f ) );
geom->addPrimitiveSet( new osg::DrawArrays(GL_POINTS, 0,
vertices->size() ) );
return geode;
}
} // End BoxFactory
}
Shape::Shape( const bool w ) : osg::PositionAttitudeTransform(),
scaleFactor( 1.0 )
{
setWireframe( w );
}
Shape::~Shape()
{
}
void Shape::setScaleFactor( const int s )
{
if( s == scaleFactor ) {
return;
}
scaleFactor = s;
const osg::Vec3d scale = getScale() * s;
setScale( scale );
}
void Shape::setWireframe( const bool w )
{
if( w == wire ) {
return;
}
wire = w;
osg::observer_ptr<osg::StateSet> ss = getOrCreateStateSet();
ss->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
osg::PolygonMode::Mode mode = ( w ) ? osg::PolygonMode::LINE
: osg::PolygonMode::FILL
;
osg::PolygonMode* pmode = new osg::PolygonMode(
osg::PolygonMode::FRONT_AND_BACK,
mode )
;
ss->setAttributeAndModes( pmode, osg::StateAttribute::OVERRIDE
| osg::StateAttribute::ON )
;
}
Box::Box( const double l ) : Shape(), length( l )
{
setName( "Box" );
setLenght( l );
}
Box::~Box()
{
}
bool Box::create()
{
bool toret( false );
osg::ref_ptr<osg::Geode> geode = BoxFactory::buildBox();
if( geode.valid() ) {
toret = addChild( geode );
}
return toret;
}
void Box::setLenght( const double l )
{
if( l == length ) {
return;
}
setScaleFactor( l );
}
Then, in my app:
Cookbook's code
Code:
// This is the callback from the Cookbook
osg::ref_ptr<BoundingBoxCallback> bbcb = new BoundingBoxCallback;
osg::ref_ptr<osg::PositionAttitudeTransform> bboxNode = new
osg::PositionAttitudeTransform;
osg::ref_ptr<osg::Geode> bboxGeode = new osg::Geode;
osg::ShapeDrawable* sd = new osg::ShapeDrawable( new osg::Box );
sd->setColor( osg::Vec4d( 1.0, 0.0, 0.0, 1.0 ) );
bboxGeode->addDrawable( sd );
bboxNode->addChild( bboxGeode.release() );
bboxNode->addUpdateCallback( bbcb.get() );
bboxNode->getOrCreateStateSet()->setAttributeAndModes( new
osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE ) );
bboxNode->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF
);
Using my custom shape
Code:
// This is the callback from the Cookbook
osg::ref_ptr<BoundingBoxCallback> bbcb = new BoundingBoxCallback;
osg::ref_ptr<Box> bboxNode = new Box;
bboxNode->setWireframe( true );
bboxNode->create();
bboxNode->addUpdateCallback( bbcb.get() );
bboxNode->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF
);
Callback (I added an "active" flag so the callback will be run only once.
Otherwise, it's the same code as in the Cookbook):
Code:
void BoundingBoxCallback::operator()( osg::Node* node, osg::NodeVisitor* )
{
if( !isActive() || !getCurrentNode().valid() ) {
return;
}
osg::ComputeBoundsVisitor cbbv;
getCurrentNode()->accept( cbbv );
const osg::BoundingBox localBB = cbbv.getBoundingBox();
if( !localBB.valid() ) {
return;
}
const osg::Matrix localToWorld = osg::computeLocalToWorld( node->getParent(
0 )->getParentalNodePaths()[ 0 ] );
osg::BoundingBox bb;
for( unsigned int i( 0 ); i < 8; ++i ) {
bb.expandBy( localBB.corner( i ) * localToWorld );
}
osg::PositionAttitudeTransform* trans =
static_cast<osg::PositionAttitudeTransform*>( node );
trans->setScale( osg::Vec3d( bb.xMax() - bb.xMin(), bb.yMax() - bb.yMin(),
bb.zMax() - bb.zMin() ) );
trans->setPosition( bb.center() );
std::cout << "BB: " << "\n";
std::cout << "\tX m/M: " << bb.xMin() << " / " << bb.xMax() << std::endl;
std::cout << "\tY m/M: " << bb.yMin() << " / " << bb.yMax() << std::endl;
std::cout << "\tZ m/M: " << bb.zMin() << " / " << bb.zMax() << std::endl;
std::cout << "TRANS->POS: " << trans->getPosition() << std::endl;
disable();
}
You can check what the output is with each code in the attached pics. They're
labelled as original for the original code in the Cookbook and myshape for the
code using my custom shape.
As you can see, when using my custom shape the bounding box doesn't get
updated. Inside the callback, the computed bounding box is the same whenever
I'm using the original code or my shape, so I'm a bit puzzled beacuse when
displaying the box it doesn't fit the model as nice as the Cookbook's code.
If I don't use the callback and modify the box by hand, for example by scaling
it, it works.
So, what did I do wrong for this not to work?
NOTE:
This is for educational purposes only.
...
Cheers and thanks!
Andrés
------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=61558#61558
Attachments:
http://forum.openscenegraph.org//files/myshape_138.png
http://forum.openscenegraph.org//files/original_181.png
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org