From Jan Peciva, "I am sending pov plugin for exporting scene to POV-Ray format.

POV-Ray is photorealistic ray tracer."
This commit is contained in:
Robert Osfield
2010-07-31 10:33:20 +00:00
parent 930f5ab620
commit 83d56b52b5
6 changed files with 1207 additions and 0 deletions

View File

@@ -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)

View File

@@ -0,0 +1,12 @@
INCLUDE(OsgMacroUtils)
SET(TARGET_SRC
POVWriterNodeVisitor.cpp
ReaderWriterPOV.cpp
)
SET(TARGET_HDRS
POVWriterNodeVisitor.h
ReaderWriterPOV.h
)
SETUP_PLUGIN(pov)

View File

@@ -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 <osg/Geode>
#include <osg/Geometry>
#include <osg/Material>
#include <osg/TexEnv>
#include <sstream>
#include <cassert>
#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; i<size; i++ )
valueVisitor->apply( 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();
}

View File

@@ -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 <osg/NodeVisitor>
#include <stack>
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 */

View File

@@ -0,0 +1,123 @@
#include <osg/ComputeBoundsVisitor>
#include <osg/io_utils>
#include <osg/Notify>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#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<c; i++ )
const_cast< Camera* >( 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 );
}

View File

@@ -0,0 +1,33 @@
#ifndef READER_WRITER_POV_H
#define READER_WRITER_POV_H
#include <osgDB/Registry>
#include <osgDB/FileNameUtils>
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 */