diff --git a/src/osgPlugins/CMakeLists.txt b/src/osgPlugins/CMakeLists.txt index e6229f99a..01a1aed7d 100644 --- a/src/osgPlugins/CMakeLists.txt +++ b/src/osgPlugins/CMakeLists.txt @@ -175,6 +175,7 @@ ADD_SUBDIRECTORY(pic) ADD_SUBDIRECTORY(stl) ADD_SUBDIRECTORY(3ds) ADD_SUBDIRECTORY(ac) +ADD_SUBDIRECTORY(pov) ADD_SUBDIRECTORY(logo) ADD_SUBDIRECTORY(lws) diff --git a/src/osgPlugins/pov/CMakeLists.txt b/src/osgPlugins/pov/CMakeLists.txt new file mode 100644 index 000000000..f47b6ceb0 --- /dev/null +++ b/src/osgPlugins/pov/CMakeLists.txt @@ -0,0 +1,12 @@ +INCLUDE(OsgMacroUtils) + +SET(TARGET_SRC + POVWriterNodeVisitor.cpp + ReaderWriterPOV.cpp +) +SET(TARGET_HDRS + POVWriterNodeVisitor.h + ReaderWriterPOV.h +) + +SETUP_PLUGIN(pov) diff --git a/src/osgPlugins/pov/POVWriterNodeVisitor.cpp b/src/osgPlugins/pov/POVWriterNodeVisitor.cpp new file mode 100644 index 000000000..d71cce5d4 --- /dev/null +++ b/src/osgPlugins/pov/POVWriterNodeVisitor.cpp @@ -0,0 +1,945 @@ +// +// POVWriterNodeVisitor converts OSG scene graph to POV (povray) +// and writes it to the stream. +// +// +// Autor: PCJohn (peciva _at fit.vutbr.cz) +// developed for research purposes of Cadwork (c) and +// Brno University of Technology (Czech Rep., EU) +// +// License: public domain +// +// +// THIS SOFTWARE IS NOT COPYRIGHTED +// +// This source code is offered for use in the public domain. +// You may use, modify or distribute it freely. +// +// This source code is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED +// ARE HEREBY DISCLAIMED. This includes but is not limited to +// warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// +// If you find the source code useful, authors will kindly welcome +// if you give them credit and keep their names with their source +// code, but you are not forced to do so. +// + + +#include +#include +#include +#include +#include +#include +#include "POVWriterNodeVisitor.h" + +using namespace std; +using namespace osg; + + + +void POVWriterNodeVisitor::apply( Node& node ) +{ + POVWriterNodeVisitor::traverse( node ); +} + + +void POVWriterNodeVisitor::traverse( Node& node ) +{ + pushStateSet( node.getStateSet() ); + + NodeVisitor::traverse( node ); + + popStateSet( node.getStateSet() ); +} + + +void POVWriterNodeVisitor::pushStateSet( const StateSet *ss ) +{ + if( ss ) { + + // merge state set + StateSet *mergedSS = new StateSet( + *_stateSetStack.top().get(), CopyOp::SHALLOW_COPY ); + mergedSS->merge( *ss ); + + // push state set + _stateSetStack.push( mergedSS ); + + } +} + + +void POVWriterNodeVisitor::popStateSet( const StateSet *ss ) +{ + if( ss ) { + + // restore the previous stateset + assert( _stateSetStack.size() > 0 && "_stateSetStack underflow" ); + _stateSetStack.pop(); + + } +} + + +void POVWriterNodeVisitor::apply( Geode& node ) +{ + pushStateSet( node.getStateSet() ); + + // iterate through drawables + const Geode::DrawableList& dl = node.getDrawableList(); + for( Geode::DrawableList::const_iterator itr = dl.begin(); + itr != dl.end(); ++itr) + { + // get drawable + const Drawable *d = *itr; + + // push state set + const StateSet *ss = d->getStateSet(); + if( ss ) pushStateSet( ss ); + + // transformation matrix + Matrix m = _transformationStack.top(); + + // process lights + processLights( _stateSetStack.top(), m ); + + // process geometry + const Geometry *g = d->asGeometry(); + if( g ) + processGeometry( g, _stateSetStack.top(), m ); + + // pop state set + if( ss ) popStateSet( ss ); + } + + popStateSet( node.getStateSet() ); +} + + + + +/** + * ValueVisitor writes all values of an array out to a stream, + * applies a matrix beforehand if necessary. + */ +// The code was adapted from the OBJWriterNodeVisitor.cpp. +// See the file for the list of authors and contributors. +class PovVec3WriterVisitor : public ConstValueVisitor +{ +public: + + PovVec3WriterVisitor( ostream& fout, const Matrix& m = + Matrix::identity(), bool isNormal = false) + : ConstValueVisitor(), + _fout( fout ), + _m( m ), + _isNormal( isNormal ) + { + _applyMatrix = ( _m != Matrix::identity() ); + if ( _isNormal ) + _origin = Vec3( 0, 0, 0 ) * _m; + } + + virtual void apply( const Vec2& v ) + { + apply( Vec3( v[0], v[1], 0. ) ); + } + + virtual void apply( const Vec3& v ) + { + Vec3 a; + if (_applyMatrix) + a = (_isNormal) ? (v * _m) - _origin : v * _m; + else + a = v; + _fout << " < " << a[0] << ", " << a[1] << ", " << a[2] + << " >" << endl; + } + + virtual void apply( const Vec2b& v ) + { + apply( Vec3b( v[0], v[1], 0 ) ); + } + + virtual void apply( const Vec3b & v ) + { + apply( Vec3s( v[0], v[1], v[2] ) ); + } + + virtual void apply( const Vec2s& v ) + { + apply( Vec3s( v[0], v[1], 0 ) ); + } + + virtual void apply( const Vec3s& v ) + { + apply( Vec3( v[0], v[1], v[2] ) ); + } + +private: + + PovVec3WriterVisitor& operator = ( const PovVec3WriterVisitor& ) { return *this; } + + ostream& _fout; + Matrix _m; + bool _applyMatrix; + bool _isNormal; + Vec3 _origin; +}; + + +class PovVec2WriterVisitor : public ConstValueVisitor +{ +public: + + PovVec2WriterVisitor( ostream& fout, const Matrix& m = + Matrix::identity(), bool isNormal = false) + : ConstValueVisitor(), + _fout( fout ), + _m( m ), + _isNormal( isNormal ) + { + _applyMatrix = ( _m != Matrix::identity() ); + if ( _isNormal ) + _origin = Vec3( 0, 0, 0 ) * _m; + } + + virtual void apply( const Vec2& v ) + { + Vec2 a; + if (_applyMatrix) { + Vec3 b( v[0], v[1], 0. ); + b = (_isNormal) ? (b * _m) - _origin : b * _m; + a = Vec2( b[0], b[1] ); + } else + a = v; + _fout << " < " << a[0] << ", " << a[1] << " >" << endl; + } + + virtual void apply( const Vec3& v ) + { + apply( Vec2( v[0], v[1] ) ); + } + + virtual void apply( const Vec2b& v ) + { + apply( Vec2( v[0], v[1] ) ); + } + + virtual void apply( const Vec3b & v ) + { + apply( Vec2b( v[0], v[1] ) ); + } + + virtual void apply( const Vec2s& v ) + { + apply( Vec2( v[0], v[1] ) ); + } + + virtual void apply( const Vec3s& v ) + { + apply( Vec2s( v[0], v[1] ) ); + } + +private: + + PovVec2WriterVisitor& operator = ( const PovVec2WriterVisitor& ) { return *this; } + + ostream& _fout; + Matrix _m; + bool _applyMatrix; + bool _isNormal; + Vec3 _origin; +}; + + +class ArrayValueFunctor : public ConstArrayVisitor +{ +public: + + ArrayValueFunctor( ConstValueVisitor &vv ) + : valueVisitor( &vv ) {} + + template< class ArrayType, class ValueType > + void visitAll( const ArrayType& a ) { + const ValueType *v = static_cast< const ValueType* >( a.getDataPointer() ); + unsigned int size = a.getNumElements(); + for( unsigned int i=0; iapply( v[i] ); + } + virtual void apply( const ByteArray& a ) { visitAll< ByteArray, GLbyte >( a ); } + virtual void apply( const ShortArray& a ) { visitAll< ShortArray, GLshort >( a ); } + virtual void apply( const IntArray& a ) { visitAll< IntArray, GLint >( a ); } + virtual void apply( const UByteArray& a ) { visitAll< UByteArray, GLubyte >( a ); } + virtual void apply( const UShortArray& a ) { visitAll< UShortArray, GLushort >( a ); } + virtual void apply( const UIntArray& a ) { visitAll< UIntArray, GLuint >( a ); } + virtual void apply( const FloatArray& a ) { visitAll< FloatArray, GLfloat >( a ); } + virtual void apply( const DoubleArray& a ) { visitAll< DoubleArray, GLdouble >( a ); } + virtual void apply( const Vec2Array& a ) { visitAll< Vec2Array, Vec2 >( a ); } + virtual void apply( const Vec3Array& a ) { visitAll< Vec3Array, Vec3 >( a ); } + virtual void apply( const Vec4Array& a ) { visitAll< Vec4Array, Vec4 >( a ); } + virtual void apply( const Vec4ubArray& a ) { visitAll< Vec4ubArray, Vec4ub >( a ); } + virtual void apply( const Vec2bArray& a ) { visitAll< Vec2bArray, Vec2b >( a ); } + virtual void apply( const Vec3bArray& a ) { visitAll< Vec3bArray, Vec3b >( a ); } + virtual void apply( const Vec4bArray& a ) { visitAll< Vec4bArray, Vec4b >( a ); } + virtual void apply( const Vec2sArray& a ) { visitAll< Vec2sArray, Vec2s >( a ); } + virtual void apply( const Vec3sArray& a ) { visitAll< Vec3sArray, Vec3s >( a ); } + virtual void apply( const Vec4sArray& a ) { visitAll< Vec4sArray, Vec4s >( a ); } + virtual void apply( const Vec2dArray& a ) { visitAll< Vec2dArray, Vec2d >( a ); } + virtual void apply( const Vec3dArray& a ) { visitAll< Vec3dArray, Vec3d >( a ); } + virtual void apply( const Vec4dArray& a ) { visitAll< Vec4dArray, Vec4d >( a ); } + + virtual void apply( const Array& ) { + assert( false && "Not using overloaded methods." ); + } + +protected: + ConstValueVisitor *valueVisitor; +}; + + +template < class Type > +class PovArrayWriterFunctor : public ArrayValueFunctor +{ +public: + PovArrayWriterFunctor( ostream& fout, const Matrix& m = + Matrix::identity(), bool isNormal = false) + : ArrayValueFunctor( povValueVisitor ), + povValueVisitor( fout, m, isNormal ) {} + +protected: + Type povValueVisitor; +}; + + +static void writeIndex( ostream& _fout, unsigned int &numTriangles, + int i1, int i2, int i3, int &numOnLine ) +{ + // produce comma, except the first record + if( numTriangles != 0 ) + _fout << ","; + + // new line each four triangles + if( numOnLine == 3 ) { + _fout << endl; + _fout << " "; + numOnLine = 0; + } + + _fout << " < " << i1 << ", " << i2 << ", " << i3 << " >"; + numOnLine++; +} + + +static void processDrawArrays( ostream& _fout, unsigned int &numTriangles, + GLenum mode, int startIndex, int stopIndex ) +{ + int numOnLine = 0; + + int i = startIndex; + switch( mode ) { + + case GL_TRIANGLES: + for( i += 2; i < stopIndex; i += 3, numTriangles++ ) + writeIndex( _fout, numTriangles, i-2, i-1, i, numOnLine ); + break; + + case GL_TRIANGLE_STRIP: + for( i += 2; i < stopIndex; i += 1, numTriangles++ ) + writeIndex( _fout, numTriangles, i-2, i-1, i, numOnLine ); + break; + + case GL_TRIANGLE_FAN: + for( i += 2; i < stopIndex; i += 1, numTriangles++ ) + writeIndex( _fout, numTriangles, startIndex, i-1, i, numOnLine ); + break; + + case GL_QUADS: + case GL_QUAD_STRIP: + case GL_POLYGON: + assert( 0 && "Not implemented yet." ); + + default: + assert( false ); + } + + // put new line when primitive set is over + _fout << endl; +} + + +class DrawElementsWriter : public ConstValueVisitor { +public: + + DrawElementsWriter( ostream& fout ) : _fout(fout), + numIndices( 0 ), numOnLine( 0 ), + numTriangles( 0 ) {} + + virtual void apply( const GLubyte& b ) { processIndex( b ); } + virtual void apply( const GLushort& s ) { processIndex( s ); } + virtual void apply( const GLuint& i ) { processIndex( i ); } + + virtual void processIndex( const GLuint i ) = 0; + + inline unsigned int getNumTriangles() { return numTriangles; } + +protected: + ostream& _fout; + GLuint indices[3]; + int numIndices; + int numOnLine; + unsigned int numTriangles; + + virtual bool processTriangle() + { + // dont produce trinagle until we have got three vertices + if( numIndices < 3 ) + return false; + + // produce comma, except the first record + if( numTriangles != 0 ) + _fout << ","; + + // new line each four triangles + if( numOnLine == 3 ) { + _fout << endl; + _fout << " "; + numOnLine = 0; + } + + // print out indices + _fout << " <" << indices[0] << "," << indices[1] + << "," << indices[2] << ">"; + + // increment counters + numTriangles++; + numOnLine++; + + // triangle produced => return true + return true; + } +}; + + +class TriangleWriter : public DrawElementsWriter { +public: + TriangleWriter( ostream& fout ) : DrawElementsWriter( fout ) {} + + virtual void processIndex( const GLuint i ) + { + indices[numIndices++] = i; + + if( processTriangle() ) + numIndices = 0; + } +}; + + +class TriangleStripWriter : public DrawElementsWriter { +public: + TriangleStripWriter( ostream& fout ) : DrawElementsWriter( fout ) {} + + virtual void processIndex( const GLuint i ) + { + indices[0] = indices[1]; + indices[1] = indices[2]; + indices[2] = i; + numIndices++; + + processTriangle(); + } +}; + + +class TriangleFanWriter : public DrawElementsWriter { +public: + TriangleFanWriter( ostream& fout ) : DrawElementsWriter( fout ) {} + + virtual void processIndex( const GLuint i ) + { + if( numIndices == 0 ) { + indices[0] = i; + numIndices++; + return; + } + + indices[1] = indices[2]; + indices[2] = i; + numIndices++; + + processTriangle(); + } +}; + + +template < class Type, class IterType > +static void processDrawElements( const PrimitiveSet *pset, DrawElementsWriter *w ) +{ + const Type *drawElements = + dynamic_cast< const Type* >( pset ); + for( IterType primItr = drawElements->begin(); + primItr != drawElements->end(); + ++primItr ) + w->apply( *primItr ); +} + + +class Vec4ConvertVisitor : public ConstValueVisitor +{ +public: + //virtual void apply( const GLbyte& v ) + //virtual void apply( const GLshort& v ) + //virtual void apply( const GLint& v ) + //virtual void apply( const GLushort& v ) + //virtual void apply( const GLubyte& v ) + //virtual void apply( const GLuint& v ) + //virtual void apply( const GLfloat& v ) + //virtual void apply( const GLdouble& v ) + virtual void apply( const Vec4ub& v ) { r = Vec4( v[0], v[1], v[2], v[3] ); } + //virtual void apply( const Vec2& v ) + virtual void apply( const Vec3& v ) { r = Vec4( v[0], v[1], v[2], 1. ); } + virtual void apply( const Vec4& v ) { r = v; } + //virtual void apply( const Vec2b& v ) + virtual void apply( const Vec3b& v ) { r = Vec4( v[0], v[1], v[2], 1. ); } + virtual void apply( const Vec4b& v ) { r = Vec4( v[0], v[1], v[2], v[3] ); } + //virtual void apply( const Vec2s& v ) + virtual void apply( const Vec3s& v ) { r = Vec4( v[0], v[1], v[2], 1. ); } + virtual void apply( const Vec4s& v ) { r = Vec4( v[0], v[1], v[2], v[3] ); } + //virtual void apply( const Vec2d& v ) + virtual void apply( const Vec3d& v ) { r = Vec4( v[0], v[1], v[2], 1. ); } + virtual void apply( const Vec4d& v ) { r = Vec4( v[0], v[1], v[2], v[3] ); } + + const Vec4& getResult() const { return r; } + +protected: + Vec4 r; +}; + + +void POVWriterNodeVisitor::processGeometry( const Geometry *g, + const StateSet *ss, + const Matrix &m ) +{ + // ignore empty geometries because they cause povray to fail loading the model + // (seen on POV-Ray 3.6.1) + if( g->getVertexArray() == NULL || g->getVertexArray()->getNumElements() == 0 ) + return; + + if(( g->getVertexIndices() != NULL && + g->getVertexIndices()->getNumElements() != 0 ) || + ( g->getNormalIndices() != NULL && + g->getNormalIndices()->getNumElements() != 0 ) || + ( g->getTexCoordIndices(0) != NULL && + g->getTexCoordIndices(0)->getNumElements() != 0 )) + { + notify( WARN ) << "POV Writer WARNING: " + "Geometry with indices is not " << endl << + "supported yet by POV plugin. Skipping geometry." << endl; + return; + } + + // mesh2 + _fout << "mesh2" << endl; + _fout << "{" << endl; + + // Convert coordinates + // OSG represents coordinates by: Vec2, Vec3, Vec4 + if( g->getVertexArray() ) { + + _fout << " vertex_vectors" << endl; + _fout << " {" << endl; + _fout << " " << g->getVertexArray()->getNumElements() << "," << endl; + + PovArrayWriterFunctor< PovVec3WriterVisitor > povArrayWriter( _fout, m, false ); + g->getVertexArray()->accept( povArrayWriter ); + + _fout << " }" << endl; + } + + // reuse coord indices for normal and texCoord + bool needNormalIndices = true; + bool needTexCoordIndices = true; + + // Convert normals + // OSG represents normals by: Vec3,Vec3s,Vec3b + // and can handle: Vec4s,Vec4b by truncating them to three components + if( g->getNormalArray() ) { + + _fout << " normal_vectors" << endl; + _fout << " {" << endl; + _fout << " " << g->getNormalArray()->getNumElements() << "," << endl; + + PovArrayWriterFunctor< PovVec3WriterVisitor > povArrayWriter( _fout, m, true ); + g->getNormalArray()->accept( povArrayWriter ); + + _fout << " }" << endl; + + // need normal indices? + if( g->getVertexArray() ) + if( g->getVertexArray()->getNumElements() == g->getNormalArray()->getNumElements() ) + needNormalIndices = false; + } + + // Convert texture coordinates + if( g->getTexCoordArray( 0 ) ) { + + _fout << " uv_vectors" << endl; + _fout << " {" << endl; + _fout << " " << g->getTexCoordArray( 0 )->getNumElements() << "," << endl; + + // texture coordinates + PovArrayWriterFunctor< PovVec2WriterVisitor > povArrayWriter( _fout ); + g->getTexCoordArray( 0 )->accept( povArrayWriter ); + + _fout << " }" << endl; + + // need texCoord indices? + if( g->getVertexArray() ) + if( g->getVertexArray()->getNumElements() == g->getTexCoordArray( 0 )->getNumElements() ) + needTexCoordIndices = false; + } + + // indices string stream + stringstream indicesStream; + indicesStream << " "; + unsigned int numTriangles = 0; + + // process primitive sets + int psetIndex, numPsets = g->getNumPrimitiveSets(); + for( psetIndex = 0; psetIndex < numPsets; psetIndex++ ) { + + // get primitive set + const PrimitiveSet *pset = g->getPrimitiveSet( psetIndex ); + PrimitiveSet::Type type = pset->getType(); + GLenum mode = pset->getMode(); + + // skip not supported primitives + switch( mode ) { + case GL_TRIANGLES: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_QUADS: + case GL_QUAD_STRIP: + case GL_POLYGON: + break; // only supported types + default: + continue; // skip primitive set + // Povray does not support lines and points + }; + + switch( type ) { + case PrimitiveSet::DrawArraysPrimitiveType: + { + const DrawArrays *drawArrays = dynamic_cast< const DrawArrays* >( pset ); + + int startIndex = drawArrays->getFirst(); + int stopIndex = startIndex + drawArrays->getCount(); + + // FIXME: Am I using startIndex for all bundles that are PER_VERTEX? + + processDrawArrays( indicesStream, numTriangles, + mode, startIndex, stopIndex ); + + break; + } + + case PrimitiveSet::DrawArrayLengthsPrimitiveType: + { + const DrawArrayLengths *drawArrayLengths = + dynamic_cast< const DrawArrayLengths* >( pset ); + + int startIndex = drawArrayLengths->getFirst(); + DrawArrayLengths::vector_type::const_iterator itr = drawArrayLengths->begin(); + + for( ; itr != drawArrayLengths->end(); itr++ ) { + processDrawArrays( indicesStream, numTriangles, + mode, startIndex, *itr ); + startIndex += *itr; + } + + break; + } + + case PrimitiveSet::DrawElementsUBytePrimitiveType: + case PrimitiveSet::DrawElementsUShortPrimitiveType: + case PrimitiveSet::DrawElementsUIntPrimitiveType: + { + DrawElementsWriter *w; + switch( mode ) { + case GL_TRIANGLES: + w = new TriangleWriter( indicesStream ); + break; + case GL_TRIANGLE_STRIP: + w = new TriangleStripWriter( indicesStream ); + break; + case GL_TRIANGLE_FAN: + w = new TriangleFanWriter( indicesStream ); + break; + default: + assert( false && "Not implemented yet." ); + goto skip; + } + + switch( type ) { + + case PrimitiveSet::DrawElementsUBytePrimitiveType: + processDrawElements< DrawElementsUByte, + DrawElementsUByte::const_iterator >( pset, w ); + break; + + case PrimitiveSet::DrawElementsUShortPrimitiveType: + processDrawElements< DrawElementsUShort, + DrawElementsUShort::const_iterator >( pset, w ); + break; + + case PrimitiveSet::DrawElementsUIntPrimitiveType: + processDrawElements< DrawElementsUInt, + DrawElementsUInt::const_iterator >( pset, w ); + break; + + default: + assert( false ); + } + + numTriangles += w->getNumTriangles(); + + delete w; + } + skip: + break; + + default: + assert( false && "Primitive set not handled." ); + } + } + + const Texture *texture = dynamic_cast< const Texture* >( + ss->getTextureAttribute( 0, StateAttribute::TEXTURE )); + const Image *image = ( texture ? texture->getImage( 0 ) : NULL ); + // TexEnv not used yet + //const TexEnv *texEnv = dynamic_cast< const TexEnv* >( + // ss->getTextureAttribute( 0, StateAttribute::TEXENV )); + bool texturing2D = ss->getTextureMode( 0, GL_TEXTURE_2D ) != 0; + + _fout << " face_indices" << endl + << " {" << endl + << " " << numTriangles << "," << endl + << indicesStream.str() + << " }" << endl; + + if( needNormalIndices ) + _fout << " normal_indices" << endl + << " {" << endl + << " " << numTriangles << "," << endl + << indicesStream.str() + << " }" << endl; + + if( needTexCoordIndices ) + _fout << " uv_indices" << endl + << " {" << endl + << " " << numTriangles << "," << endl + << indicesStream.str() + << " }" << endl; + + // POV-Ray's surface properties + _fout << " texture {" << endl; + +#if 1 + + // color variables + Vec4f ambient( 0.2f, 0.2f, 0.2f, 1.f ); + Vec4f diffuse( 0.8f, 0.8f, 0.8f, 1.f ); + Vec4f specular( 0.f, 0.f, 0.f, 1.f ); + Vec4f pigmentColor; + + // get ambient and diffuse + const Material *material = dynamic_cast< const Material* >( + ss->getAttribute( StateAttribute::MATERIAL ) ); + if( !material ) { + const Array *a = g->getColorArray(); + if( a && a->getNumElements() > 0 ) { + Vec4ConvertVisitor cv; + g->getColorArray()->accept( 0, cv ); + diffuse = cv.getResult(); + } + } else { + diffuse = material->getDiffuse( Material::FRONT ); + ambient = material->getAmbient( Material::FRONT ); + specular = material->getSpecular( Material::FRONT ); + } + + // compute intensities + float diffuseIntensity = diffuse.r() + diffuse.g() + diffuse.b(); + float ambientIntensity = ambient.r() + ambient.g() + ambient.b(); + float specularIntensity = specular.r() + specular.g() + specular.b(); + + // pigment color defaults to diffuse + pigmentColor = diffuse; + float pigmentIntensity = diffuseIntensity; + + // if diffuse is too dark and ambient is strong, use it instead + if( diffuseIntensity < 2 * ambientIntensity ) { + pigmentColor = ambient; + pigmentIntensity = ambientIntensity; + } + +#else + Vec4ConvertVisitor cv; + g->getColorArray()->accept( 0, cv ); + Vec4 pigmentColor = cv.getResult(); +#endif + + // has 2D texture => produce image_map + if( texturing2D && texture && image ) { + string fileName = image->getFileName(); + + // replace '\' by '/' + // ('\' does not work on Linux POV-Ray) + // FIXME: is '/' working on Windows? + string::size_type i=0; + while( ( i = fileName.find( '\\', i ) ) != string::npos ) + fileName[i] = '/'; + + _fout << " uv_mapping pigment {" << endl + << " image_map { png \"" << fileName << "\" }" << endl + << " }" << endl; + + // no 2D texture => produce single color + } else { + + // rgb and f as filter (for transparency, 0 - opaque, 1 - transparent) + _fout << " pigment {" << endl + << " color rgbf < " << pigmentColor[0] << ", " + << pigmentColor[1] << ", " + << pigmentColor[2] << ", 0 >" << endl + << " }" << endl; + } + + // POV-Ray's finish + float ai = (pigmentIntensity != 0) ? ambientIntensity/pigmentIntensity : 0.f; + float di = (pigmentIntensity != 0) ? diffuseIntensity/pigmentIntensity : 0.f; + _fout << " finish { ambient " << ai << endl + << " diffuse " << di << endl + << " specular " << specularIntensity/3. << endl + << " reflection " << specularIntensity/3. << endl + << " }" << endl + << " }" << endl; + + _fout << "}" << endl; + + numProducedTriangles += numTriangles; +} + + +void POVWriterNodeVisitor::processLights( const StateSet *ss, const Matrix &m ) +{ + const StateSet::AttributeList &attributeList = ss->getAttributeList(); + StateSet::AttributeList::const_iterator it = attributeList.begin(); + for( ; it!=attributeList.end(); ++it ) + if( it->first.first == StateAttribute::LIGHT ) { + Light *l = dynamic_cast< Light* >( it->second.first.get() ); + if( l && + ss->getMode( GL_LIGHT0 + l->getLightNum() ) & StateAttribute::ON && + lights.find( l ) == lights.end() ) { + + // append the light into the map + lights[ l ] = 1; // use any number (the number is not used) + + // directional light requires special treatment + Vec4 pos4 = l->getPosition(); + Vec3 pos3( pos4.x(), pos4.y(), pos4.z() ); + bool directional = (pos4.w() == 0.); + bool spot = false; + if( directional ) { + pos3.normalize(); + pos3 = bound.center() + ( pos3 * bound.radius() * 1.01f ); + } else { + pos3 /= pos4.w(); + spot = !equivalent( l->getSpotCutoff(), 180.f ); + } + + // create the light code + _fout << "light_source {" << endl; + + // position + PovVec3WriterVisitor povVec3Writer( _fout, m ); + povVec3Writer.apply( pos3 ); + + // color + // note: transmit and filter color values are ignored + // by POV-Ray for light sources + // (see POV-Ray's (version 3.6.1, chapter 2.4.7) doc: + // http://www.povray.org/documentation/view/3.6.1/308/) + _fout << " color rgb"; + PovVec3WriterVisitor povColorWriter( _fout ); + const Vec3::value_type *f = l->getDiffuse().ptr(); + povColorWriter.apply( Vec3( f[0], f[1], f[2] ) ); + + // directional light + if( directional ) { + _fout << " parallel" << endl + << " point_at"; + povVec3Writer.apply( bound.center() ); + } + + // spot light + if( spot ) { + _fout << " spotlight" << endl + << " point_at"; + povVec3Writer.apply( pos3 + l->getDirection() ); + // FIXME: radius and tightness models the light distribution + // differently than OpenGL's shininess. So, different + // visual results are produced. The difference can be lowered + // much by computing radius. + _fout << " falloff " << l->getSpotCutoff() << endl + << " radius 0" << endl + << " tightness " << l->getSpotExponent() / 128.f * 100.f << endl; + } + + // light source end + _fout << "}" << endl; + } + } +} + + +void POVWriterNodeVisitor::apply( Transform& node ) +{ + // push new transformation on transformation stack + Matrix m = _transformationStack.top(); + node.computeLocalToWorldMatrix( m, this ); + _transformationStack.push( m ); + + // traverse the node + apply( (Group&)node ); + + // pop transformation + _transformationStack.pop(); +} + + +POVWriterNodeVisitor::POVWriterNodeVisitor( ostream& fout, const BoundingSphere& b ) + : NodeVisitor(TRAVERSE_ALL_CHILDREN), + _fout( fout ), + bound( b ), + numProducedTriangles( 0 ) +{ + _stateSetStack.push( new StateSet() ); + _transformationStack.push( Matrix( 1., 0.,0.,0., + 0., 0.,1.,0., + 0., 1.,0.,0., + 0., 0.,0.,1. ) ); +} + + +POVWriterNodeVisitor::~POVWriterNodeVisitor() +{ + assert( _stateSetStack.size() >= 1 && "_stateSetStack underflow." ); + assert( _stateSetStack.size() <= 1 && "_stateSetStack overflow." ); + assert( _transformationStack.size() >= 1 && "_transformationStack underflow." ); + assert( _transformationStack.size() <= 1 && "_transformationStack overflow." ); + _stateSetStack.pop(); + _transformationStack.pop(); +} diff --git a/src/osgPlugins/pov/POVWriterNodeVisitor.h b/src/osgPlugins/pov/POVWriterNodeVisitor.h new file mode 100644 index 000000000..d55a57d0b --- /dev/null +++ b/src/osgPlugins/pov/POVWriterNodeVisitor.h @@ -0,0 +1,93 @@ +#ifndef POV_WRITER_NODE_VISITOR_H +#define POV_WRITER_NODE_VISITOR_H +// +// POVWriterNodeVisitor converts OSG scene graph to POV (povray) +// and writes it to the stream. +// +// +// Autor: PCJohn (peciva _at fit.vutbr.cz) +// developed for research purposes of Cadwork (c) and +// Brno University of Technology (Czech Rep., EU) +// +// License: public domain +// +// +// THIS SOFTWARE IS NOT COPYRIGHTED +// +// This source code is offered for use in the public domain. +// You may use, modify or distribute it freely. +// +// This source code is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED +// ARE HEREBY DISCLAIMED. This includes but is not limited to +// warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// +// If you find the source code useful, authors will kindly welcome +// if you give them credit and keep their names with their source +// code, but you are not forced to do so. +// + +#include +#include + +namespace osg { + class Light; + class Transform; +} + + +class POVWriterNodeVisitor : public osg::NodeVisitor +{ +public: + + POVWriterNodeVisitor( std::ostream& fout, const osg::BoundingSphere& bound ); + virtual ~POVWriterNodeVisitor(); + + void traverse( osg::Node& node ); + + virtual void apply( osg::Node &node ); + virtual void apply( osg::Geode &node ); + //virtual void apply(osg::Group &node); + + //virtual void apply(osg::Billboard& node); + //virtual void apply(ProxyNode& node) { apply((Group&)node); } + //virtual void apply(Projection& node) { apply((Group&)node); } + //virtual void apply(CoordinateSystemNode& node) { apply((Group&)node); } + + //virtual void apply(ClipNode& node) { apply((Group&)node); } + //virtual void apply(TexGenNode& node) { apply((Group&)node); } + //virtual void apply(LightSource& node) { apply((Group&)node); } + + virtual void apply( osg::Transform& node ); + //virtual void apply(Camera& node) { apply((Transform&)node); } + //virtual void apply(CameraView& node) { apply((Transform&)node); } + //virtual void apply(osg::MatrixTransform& node); + //virtual void apply(osg::PositionAttitudeTransform& node); + + //virtual void apply(Switch& node) { apply((Group&)node); } + //virtual void apply(Sequence& node) { apply((Group&)node); } + //virtual void apply(osg::LOD& node); + //virtual void apply(PagedLOD& node) { apply((LOD&)node); } + //virtual void apply(ClearNode& node) { apply((Group&)node); } + //virtual void apply(OccluderNode& node) { apply((Group&)node); } + + unsigned int getNumProducedTriangles() const { return numProducedTriangles; } +protected: + + std::ostream& _fout; + osg::BoundingSphere bound; + std::stack< osg::ref_ptr< osg::StateSet > > _stateSetStack; + std::stack< osg::Matrix > _transformationStack; + unsigned int numProducedTriangles; + std::map< osg::Light*, int > lights; + + void pushStateSet( const osg::StateSet *ss ); + void popStateSet( const osg::StateSet *ss ); + + virtual void processGeometry( const osg::Geometry *g, + const osg::StateSet *ss, const osg::Matrix &m ); + virtual void processLights( const osg::StateSet *ss, const osg::Matrix &m ); +}; + + +#endif /* POV_WRITER_NODE_VISITOR_H */ diff --git a/src/osgPlugins/pov/ReaderWriterPOV.cpp b/src/osgPlugins/pov/ReaderWriterPOV.cpp new file mode 100644 index 000000000..6873b255b --- /dev/null +++ b/src/osgPlugins/pov/ReaderWriterPOV.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include "ReaderWriterPOV.h" +#include "POVWriterNodeVisitor.h" + +using namespace std; +using namespace osg; + + +// Register with Registry to instantiate the Povray writer. +REGISTER_OSGPLUGIN( Povray, ReaderWriterPOV ) + + +/** + * Constructor. + * Initializes the ReaderWriterPOV. + */ +ReaderWriterPOV::ReaderWriterPOV() +{ + // Set supported extensions and options + supportsExtension( "pov", "POV-Ray format" ); +} + + +static osgDB::ReaderWriter::WriteResult +writeNodeImplementation( const Node& node, ostream& fout, + const osgDB::ReaderWriter::Options* options ) +{ + // get camera on the top of scene graph + const Camera *camera = dynamic_cast< const Camera* >( &node ); + + Vec3d eye, center, up, right; + double fovy, aspectRatio, tmp; + if( camera ) { + + // view matrix + camera->getViewMatrixAsLookAt( eye, center, up ); + up = Vec3d( 0.,0.,1. ); + right = Vec3d( 1.,0.,0. ); + //up.normalize(); + //right = (center-eye) ^ up; + //right.normalize(); + + // projection matrix + camera->getProjectionMatrixAsPerspective( fovy, aspectRatio, tmp, tmp ); + right *= aspectRatio; + + } else { + + // get POV-Ray camera setup from scene bounding sphere + // constructed from bounding box + // (bounding box computes model center more precisely than bounding sphere) + ComputeBoundsVisitor cbVisitor; + const_cast< Node& >( node ).accept( cbVisitor ); + BoundingBox &bb = cbVisitor.getBoundingBox(); + BoundingSphere bs( bb ); + + // set + eye = bs.center() + Vec3( 0., -3.0 * bs.radius(), 0. ); + center = bs.center(); + up = Vec3d( 0.,1.,0. ); + right = Vec3d( 4./3.,0.,0. ); + + } + + // camera + fout << "camera { // following POV-Ray, x is right, y is up, and z is to the scene" << endl + << " location <" << eye[0] << ", " << eye[2] << ", " << eye[1] << ">" << endl + << " up <" << up[0] << ", " << up[2] << ", " << up[1] << ">" << endl + << " right <" << right[0] << ", " << right[2] << ", " << right[1] << ">" << endl + << " look_at <" << center[0] << ", " << center[2] << ", " << center[1] << ">" << endl + << "}" << endl + << endl; + + POVWriterNodeVisitor povWriter( fout, node.getBound() ); + if( camera ) + + for( int i=0, c=camera->getNumChildren(); i( camera )->getChild( i )->accept( povWriter ); + + else + + const_cast< Node* >( &node )->accept( povWriter ); + + notify( NOTICE ) << "ReaderWriterPOV::writeNode() Done. (" + << povWriter.getNumProducedTriangles() + << " triangles written)" << endl; + + return osgDB::ReaderWriter::WriteResult::FILE_SAVED; +} + + +osgDB::ReaderWriter::WriteResult +ReaderWriterPOV::writeNode( const Node& node, const string& fileName, + const osgDB::ReaderWriter::Options* options ) const +{ + // accept extension + string ext = osgDB::getLowerCaseFileExtension( fileName ); + if( !acceptsExtension( ext ) ) return WriteResult::FILE_NOT_HANDLED; + + notify( NOTICE ) << "ReaderWriterPOV::writeNode() Writing file " + << fileName.data() << endl; + + osgDB::ofstream fout( fileName.c_str(), ios::out | ios::trunc ); + if( !fout ) + return WriteResult::ERROR_IN_WRITING_FILE; + else + return writeNodeImplementation( node, fout, options ); +} + + +osgDB::ReaderWriter::WriteResult +ReaderWriterPOV::writeNode( const Node& node, ostream& fout, + const osgDB::ReaderWriter::Options* options ) const +{ + notify( osg::NOTICE ) << "ReaderWriterPOV::writeNode() Writing to " + << "stream" << endl; + + return writeNodeImplementation( node, fout, options ); +} diff --git a/src/osgPlugins/pov/ReaderWriterPOV.h b/src/osgPlugins/pov/ReaderWriterPOV.h new file mode 100644 index 000000000..be5cff9e7 --- /dev/null +++ b/src/osgPlugins/pov/ReaderWriterPOV.h @@ -0,0 +1,33 @@ +#ifndef READER_WRITER_POV_H +#define READER_WRITER_POV_H + +#include +#include + + +class ReaderWriterPOV : public osgDB::ReaderWriter +{ + public: + ReaderWriterPOV(); + + virtual const char* className() const + { + return "POV reader/writer"; + } + + bool isInventorExtension( const std::string& extension ) const + { + return osgDB::equalCaseInsensitive( extension, "pov" ) ? true : false; + } + + virtual WriteResult writeNode( const osg::Node& node, + const std::string& filename, + const osgDB::ReaderWriter::Options* options = NULL) const; + + virtual WriteResult writeNode( const osg::Node& node, std::ostream& fout, + const Options* = NULL) const; + +}; + + +#endif /* READER_WRITER_POV_H */