diff --git a/src/osgPlugins/normals/GNUmakefile b/src/osgPlugins/normals/GNUmakefile new file mode 100644 index 000000000..098a33f97 --- /dev/null +++ b/src/osgPlugins/normals/GNUmakefile @@ -0,0 +1,14 @@ +TOPDIR = ../../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + ReaderWriterNormals.cpp\ + Normals.cpp\ + +LIBS += $(OSG_LIBS) $(OTHER_LIBS) $(SOCKET_LIBS) + +TARGET_BASENAME = normals +include $(TOPDIR)/Make/cygwin_plugin_def +PLUGIN = $(PLUGIN_PREFIX)$(TARGET_BASENAME).$(PLUGIN_EXT) + +include $(TOPDIR)/Make/makerules diff --git a/src/osgPlugins/normals/Normals.cpp b/src/osgPlugins/normals/Normals.cpp new file mode 100644 index 000000000..5ebd327bd --- /dev/null +++ b/src/osgPlugins/normals/Normals.cpp @@ -0,0 +1,230 @@ +#include "Normals.h" + +using namespace osg; + +Normals::Normals( Node *node, float scale, Mode mode ) +{ + MakeNormalsVisitor mnv(scale); + mnv.setMode( mode ); + node->accept( mnv ); + + ref_ptr coords = mnv.getCoords(); + ref_ptr colors = new Vec4Array; + if( mode == SurfaceNormals ) + colors->push_back( Vec4( 0, 1, 0, 1 )); + else if( mode == VertexNormals ) + colors->push_back( Vec4( 1, 0, 0, 1 )); + + ref_ptr geom = new Geometry; + geom->setVertexArray( coords.get() ); + geom->setColorArray( colors.get() ); + geom->setColorBinding( Geometry::BIND_OVERALL ); + + geom->addPrimitiveSet( new DrawArrays( PrimitiveSet::LINES, 0, coords->size())); + + StateSet *sset = new StateSet; + sset->setMode( GL_LIGHTING, StateAttribute::OFF); + geom->setStateSet( sset ); + addDrawable( geom.get() ); +} + + + +Normals::MakeNormalsVisitor::MakeNormalsVisitor( float normalScale, Normals::Mode mode): + NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN), + _normal_scale(normalScale), + _mode(mode) +{ + _local_coords = new Vec3Array; + _mat = osg::Matrix::identity(); +} + + +void Normals::MakeNormalsVisitor::apply(osg::MatrixTransform& tx) +{ + _matStack.push( _mat ); + _mat = _mat * tx.getMatrix(); + + traverse( tx ); + + _mat = _matStack.top(); + _matStack.pop(); +} + +void Normals::MakeNormalsVisitor::apply( Geode &geode ) +{ + for( unsigned int i = 0; i < geode.getNumDrawables(); i++ ) + { + Geometry *geom = dynamic_cast(geode.getDrawable(i)); + if( geom ) + { + Vec3Array *coords = dynamic_cast(geom->getVertexArray()); + if( coords == 0L ) + continue; + + Vec3Array *normals = dynamic_cast(geom->getNormalArray()); + if( normals == 0L ) + continue; + + Geometry::AttributeBinding binding = geom->getNormalBinding(); + if( binding == Geometry::BIND_OFF ) + continue; + + if( binding == Geometry::BIND_OVERALL ) + { + Vec3 v(0,0,0); + Vec3 n = normals->front(); + + Vec3Array::iterator coord_index = coords->begin(); + while( coord_index != coords->end() ) + v += *(coord_index++) * _mat; + v /= (float)(coords->size()); + + n *= _normal_scale; + _local_coords->push_back( v ); + _local_coords->push_back( (v + n)); + } + else // BIND_PER_PRIMTIVE_SET, BIND_PER_PRIMTITIV, BIND_PER_VERTEX + { + Geometry::PrimitiveSetList& primitiveSets = geom->getPrimitiveSetList(); + Geometry::PrimitiveSetList::iterator itr; + + Vec3Array::iterator coord_index = coords->begin(); + Vec3Array::iterator normals_index = normals->begin(); + + for(itr=primitiveSets.begin(); itr!=primitiveSets.end(); ++itr) + { +#ifdef DEBUG + _printPrimitiveType( (*itr).get() ); +#endif + if( binding == Geometry::BIND_PER_PRIMITIVE_SET ) + { + Vec3 v(0,0,0); + Vec3 n = *(normals_index++); + int ni = (*itr)->getNumIndices(); + for( int i = 0; i < ni; i++ ) + v += *(coord_index++) * _mat; + v /= (float)(ni); + + n *= _normal_scale; + _local_coords->push_back( v ); + _local_coords->push_back( (v + n)); + + } + else + { + switch((*itr)->getMode()) + { + case(PrimitiveSet::TRIANGLES): + for( unsigned int j = 0; j < (*itr)->getNumPrimitives(); j++ ) + { + _processPrimitive( 3, coord_index, normals_index, binding ); + coord_index += 3; + if( binding == Geometry::BIND_PER_PRIMITIVE ) + normals_index++; + else + normals_index+=3; + + } + break; + + case(PrimitiveSet::TRIANGLE_STRIP): + for( unsigned int j = 0; j < (*itr)->getNumIndices()-2; j++ ) + { + _processPrimitive( 3, coord_index, normals_index, binding ); + coord_index++; + normals_index++; + } + coord_index += 2; + if( binding == Geometry::BIND_PER_VERTEX ) + normals_index += 2; + break; + + case(PrimitiveSet::TRIANGLE_FAN): + break; + + case(PrimitiveSet::QUADS): + for( unsigned int j = 0; j < (*itr)->getNumPrimitives(); j++ ) + { + _processPrimitive( 4, coord_index, normals_index, binding ); + coord_index += 4; + if( binding == Geometry::BIND_PER_PRIMITIVE ) + normals_index++; + else + normals_index+=4; + } + break; + + case(PrimitiveSet::QUAD_STRIP): + case(PrimitiveSet::POLYGON): + break; + default: + break; + } + } + } + } + } + } + traverse( geode ); +} + + +void Normals::MakeNormalsVisitor::_processPrimitive( unsigned int nv, + Vec3Array::iterator coords, + Vec3Array::iterator normals, + Geometry::AttributeBinding binding ) +{ + Vec3 v(0,0,0); + Vec3 n(0,0,0); + if( _mode == SurfaceNormals || binding == Geometry::BIND_PER_PRIMITIVE ) + { + if( binding == Geometry::BIND_PER_PRIMITIVE ) + { + n = *(normals++); + } + else if( binding == Geometry::BIND_PER_VERTEX ) + { + for( unsigned int i = 0; i < nv; i++ ) + n += *(normals++); + n /= (float)(nv); + } + + for( unsigned int i = 0; i < nv; i++ ) + v += *(coords++) * _mat; + v /= (float)(nv); + + n *= _normal_scale; + _local_coords->push_back( v ); + _local_coords->push_back( (v + n)); + } + else if( _mode == VertexNormals ) + { + for( unsigned int i = 0; i < nv; i++ ) + { + v = *(coords++) * _mat; + n = *(normals++); + n *= _normal_scale; + _local_coords->push_back( v ); + _local_coords->push_back( (v + n)); + } + } +} + +#ifdef DEBUG +void Normals::_printPrimitiveType( osg::PrimitiveSet *pset ) +{ + std::cout << ( + pset->getMode() == PrimitiveSet::POINTS ? "POINTS" : + pset->getMode() == PrimitiveSet::LINES ? "LINES" : + pset->getMode() == PrimitiveSet::LINE_STRIP ? "LINE_STRIP" : + pset->getMode() == PrimitiveSet::LINE_LOOP ? "LINE_LOOP" : + pset->getMode() == PrimitiveSet::TRIANGLES ? "TRIANGLES" : + pset->getMode() == PrimitiveSet::TRIANGLE_STRIP ? "TRIANGLE_STRIP" : + pset->getMode() == PrimitiveSet::TRIANGLE_FAN ? "TRIANGLE_FAN" : + pset->getMode() == PrimitiveSet::QUADS ? "QUADS" : + pset->getMode() == PrimitiveSet::QUAD_STRIP ? "QUAD_STRIP" : + pset->getMode() == PrimitiveSet::POLYGON ? "POLYGON" : "Dunno" ) << std::endl; +} +#endif + diff --git a/src/osgPlugins/normals/Normals.h b/src/osgPlugins/normals/Normals.h new file mode 100644 index 000000000..2e24fa74a --- /dev/null +++ b/src/osgPlugins/normals/Normals.h @@ -0,0 +1,78 @@ +#ifndef NORMALS_DEF +#define NORMALS_DEF + +//#define DEBUG 1 +#ifdef DEBUG +#include +#endif + +#include + +#include +#include +#include +#include + +class Normals: public osg::Geode +{ + public: + enum Mode { + SurfaceNormals, + VertexNormals + }; + + Normals( osg::Node *node, float scale=1.0, Mode mode=SurfaceNormals ); + + private: + + class MakeNormalsVisitor : public osg::NodeVisitor + { + public: + MakeNormalsVisitor(float normalScale=1.0, Normals::Mode=Normals::SurfaceNormals ); + + void setMode( Mode mode ) { _mode = mode; } + + virtual void apply(osg::MatrixTransform& tx); + + virtual void apply( osg::Geode &geode ); + + osg::Vec3Array *getCoords() { return _local_coords.get(); } + + + private: + osg::ref_ptr _local_coords; + float _normal_scale; + Mode _mode; + osg::Matrix _mat; + std::stack _matStack; + + + void _processPrimitive( unsigned int nv, + osg::Vec3Array::iterator coords, + osg::Vec3Array::iterator normals, + osg::Geometry::AttributeBinding binding ); + }; + +#ifdef DEBUG + static void _printPrimitiveType( osg::PrimitiveSet *pset ); +#endif + +}; + + +class SurfaceNormals: public Normals +{ + public: + SurfaceNormals( Node *node, float scale=1.0 ): + Normals( node, scale, Normals::SurfaceNormals ) {} +}; + +class VertexNormals: public Normals +{ + public: + VertexNormals( Node *node, float scale=1.0 ): + Normals( node, scale, Normals::VertexNormals ) {} +}; + + +#endif diff --git a/src/osgPlugins/normals/ReaderWriterNormals.cpp b/src/osgPlugins/normals/ReaderWriterNormals.cpp new file mode 100644 index 000000000..1c7cd4661 --- /dev/null +++ b/src/osgPlugins/normals/ReaderWriterNormals.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "Normals.h" + +class NormalsReader: public osgDB::ReaderWriter +{ + public: + NormalsReader() {} + + virtual const char* className() { return "Normals Pseudo Loader"; } + + virtual bool acceptsExtension(const std::string& extension) const + { + return osgDB::equalCaseInsensitive(extension,"normals"); + } + + virtual ReadResult readObject(const std::string& fileName, const Options* opt) const + { return readNode(fileName,opt); } + + virtual ReadResult readNode(const std::string& fileName, const Options* options) const + { + std::string ext = osgDB::getFileExtension(fileName); + if (!acceptsExtension(ext)) + return ReadResult::FILE_NOT_HANDLED; + + float scale = 1.0; + Normals::Mode mode = Normals::VertexNormals; + + if (options) + { + std::istringstream iss(options->getOptionString()); + std::string opt; + while (iss >> opt) + { + if( opt == "help" || opt == "HELP" ) + { + osg::notify( osg::INFO ) << + "Normals Plugin usage: [-O options] .normals\n" + " options: \"scale=\" (default = 1.0)\n" + " \"mode=\" (default = VertexNormals)" << std::endl; + + } + else + { + int index = opt.find( "=" ); + if( opt.substr( 0, index ) == "scale" || + opt.substr( 0, index ) == "SCALE" ) + { + scale = atof( opt.substr( index+1 ).c_str() ); + } + else if( opt.substr( 0, index ) == "mode" || opt.substr( 0, index ) == "MODE" ) + { + std::string modestr = opt.substr(index+1); + if( modestr == "VertexNormals" ) + mode = Normals::VertexNormals; + else if( modestr == "SurfaceNormals" ) + mode = Normals::SurfaceNormals; + else + mode = Normals::VertexNormals; + } + } + } + } + + std::string nodeName = osgDB::getNameLessExtension( fileName ); + if( !nodeName.empty() ) + { + osg::ref_ptr node = osgDB::readNodeFile( nodeName ); + if( node.valid() ) + { + osg::ref_ptr group = new osg::Group; + group->addChild( node.get() ); + if( mode == Normals::VertexNormals ) + group->addChild( new VertexNormals( node.get(), scale )); + else if( mode == Normals::SurfaceNormals ) + group->addChild( new SurfaceNormals( node.get(), scale )); + + return group.get(); + } + } + return 0L; + } +}; + +osgDB::RegisterReaderWriterProxy g_normalsReader_Proxy; + +