Files
OpenSceneGraph/src/osgPlugins/OpenFlight/FltExportVisitor.cpp
Robert Osfield 2208303496 Warning fixes for Clang-3.6
git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@15016 16af8721-9629-0410-8352-f15c8da7e697
2015-07-23 11:11:58 +00:00

680 lines
21 KiB
C++

/*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at
* your option) any later version. The full license is in the LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
//
// Copyright(c) 2008 Skew Matrix Software LLC.
//
#include "FltExportVisitor.h"
#include "ExportOptions.h"
#include "FltWriteResult.h"
#include "DataOutputStream.h"
#include "Opcodes.h"
#include "LightSourcePaletteManager.h"
#include "MaterialPaletteManager.h"
#include "TexturePaletteManager.h"
#include "VertexPaletteManager.h"
#include "AttrData.h"
#include "Utils.h"
#include <osgDB/FileUtils>
#include <osgDB/WriteFile>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/LightSource>
#include <osg/LOD>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osg/PrimitiveSet>
#include <osg/ProxyNode>
#include <osg/Quat>
#include <osg/Sequence>
#include <osg/Texture2D>
#include <osg/TexEnv>
#include <osg/Switch>
#include <osg/Material>
#include <osg/CullFace>
#include <osg/BlendFunc>
#include <osg/PolygonOffset>
#include <osgSim/DOFTransform>
#include <osgSim/MultiSwitch>
#include <osgSim/LightPointNode>
#include <osgSim/ObjectRecordData>
#ifdef _MSC_VER
// Disable this warning. It's OK for us to use 'this' in initializer list,
// as the texturePaletteManager merely stores a ref to it.
#pragma warning( disable : 4355 )
#endif
namespace flt
{
FltExportVisitor::FltExportVisitor( DataOutputStream* dos,
ExportOptions* fltOpt )
: osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
_fltOpt( fltOpt ),
_dos( *dos ),
_materialPalette( new MaterialPaletteManager( *fltOpt ) ),
_texturePalette( new TexturePaletteManager( *this, *fltOpt ) ),
_lightSourcePalette( new LightSourcePaletteManager( ) ),
_vertexPalette( new VertexPaletteManager( *fltOpt ) ),
_firstNode( true )
{
// Init the StateSet stack.
osg::StateSet* ss = new osg::StateSet;
int unit;
for(unit=0; unit<8; unit++)
{
osg::TexEnv* texenv = new osg::TexEnv;
ss->setTextureAttributeAndModes( unit, texenv, osg::StateAttribute::OFF );
// TBD other texture state?
}
osg::Material* material = new osg::Material;
ss->setAttribute( material, osg::StateAttribute::OFF );
if (fltOpt->getLightingDefault())
ss->setMode( GL_LIGHTING, osg::StateAttribute::ON );
else
ss->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
osg::CullFace* cf = new osg::CullFace;
ss->setAttributeAndModes( cf, osg::StateAttribute::OFF );
osg::BlendFunc* bf = new osg::BlendFunc;
ss->setAttributeAndModes( bf, osg::StateAttribute::OFF );
osg::PolygonOffset* po = new osg::PolygonOffset;
ss->setAttributeAndModes( po, osg::StateAttribute::OFF );
_stateSetStack.push_back( ss );
// Temp file for storing records. Need a temp file because we don't
// write header and palette until FltExportVisitor completes traversal.
_recordsTempName = fltOpt->getTempDir() + "/ofw_temp_records";
_recordsStr.open( _recordsTempName.c_str(), std::ios::out | std::ios::binary );
_records = new DataOutputStream( _recordsStr.rdbuf(), fltOpt->getValidateOnly() );
// Always write initial push level
writePush();
}
FltExportVisitor::~FltExportVisitor()
{
// Delete our temp file.
if (_recordsStr.is_open())
{
OSG_WARN << "fltexp: FltExportVisitor destructor has an open temp file." << std::endl;
// This should not happen. FltExportVisitor::complete should close
// this file before we get to this destructor.
return;
}
OSG_INFO << "fltexp: Deleting temp file " << _recordsTempName << std::endl;
FLTEXP_DELETEFILE( _recordsTempName.c_str() );
}
void
FltExportVisitor::apply( osg::Group& node )
{
ScopedStatePushPop guard( this, node.getStateSet() );
if (_firstNode)
{
// On input, a FLT header creates a Group node.
// On export, we always write a Header record, but then the first Node
// we export is the Group that was created from the original input Header.
// On successive roundtrips, this results in increased redundant top-level Group nodes/records.
// Avoid this by NOT outputting anything for a top-level Group node.
_firstNode = false;
traverse( node );
return;
}
// A Group node could indicate one of many possible records.
// Header record -- Don't need to support this here. We always output a header.
// Group record -- HIGH
// Child of an LOD node -- HIGH Currently write out a Group record regardless.
// InstanceDefinition/InstanceReference -- MED -- multiparented Group is an instance
// Extension record -- MED
// Object record -- MED
// LightPointSystem record (if psgSim::MultiSwitch) -- LOW
osgSim::MultiSwitch* multiSwitch = dynamic_cast<osgSim::MultiSwitch*>( &node );
if (multiSwitch)
{
writeSwitch( multiSwitch );
}
else
{
osgSim::ObjectRecordData* ord =
dynamic_cast< osgSim::ObjectRecordData* >( node.getUserData() );
if (ord)
{
// This Group should write an Object Record.
writeObject( node, ord );
}
else
{
// Handle other cases here.
// For now, just output a Group record.
writeGroup( node );
}
}
writeMatrix( node.getUserData() );
writeComment( node );
writePushTraverseWritePop( node );
}
void
FltExportVisitor::apply( osg::Sequence& node )
{
_firstNode = false;
ScopedStatePushPop guard( this, node.getStateSet() );
writeSequence( node );
writeMatrix( node.getUserData() );
writeComment( node );
writePushTraverseWritePop( node );
}
void
FltExportVisitor::apply( osg::Switch& node )
{
_firstNode = false;
ScopedStatePushPop guard( this, node.getStateSet() );
writeSwitch( &node );
writeMatrix( node.getUserData() );
writeComment( node );
writePushTraverseWritePop( node );
}
void
FltExportVisitor::apply( osg::LOD& lodNode )
{
_firstNode = false;
ScopedStatePushPop guard( this, lodNode.getStateSet() );
// LOD center - same for all children
osg::Vec3d center = lodNode.getCenter();
// Iterate children of the LOD and write a separate LOD record for each,
// with that child's individual switchIn and switchOut properties
for ( size_t i = 0; i < lodNode.getNumChildren(); ++i )
{
osg::Node* lodChild = lodNode.getChild(i);
// Switch-in/switch-out distances may vary per child
double switchInDist = lodNode.getMaxRange(i);
double switchOutDist = lodNode.getMinRange(i);
writeLevelOfDetail( lodNode, center, switchInDist, switchOutDist);
writeMatrix( lodNode.getUserData() );
writeComment( lodNode );
// Traverse each child of the LOD
writePushTraverseChildWritePop( *lodChild );
}
}
void
FltExportVisitor::apply( osg::MatrixTransform& node )
{
// Importer reads a Matrix record and inserts a MatrixTransform above
// the current node. We need to do the opposite: Write a Matrix record
// as an ancillary record for each child. We do that by storing the
// MatrixTransform in each child's UserData. Each child then checks
// UserData and writes a Matrix record if UserData is a MatrixTransform.
_firstNode = false;
ScopedStatePushPop guard( this, node.getStateSet() );
osg::ref_ptr< osg::RefMatrix > m = new osg::RefMatrix;
m->set( node.getMatrix() );
if (node.getUserData())
{
const osg::RefMatrix* rm = dynamic_cast<const osg::RefMatrix*>( node.getUserData() );
if (rm)
(*m) *= *rm;
}
typedef std::vector< osg::ref_ptr< osg::Referenced > > UserDataList;
UserDataList saveUserDataList( node.getNumChildren() );
unsigned int idx;
for( idx=0; idx<node.getNumChildren(); ++idx )
{
saveUserDataList[ idx ] = node.getChild( idx )->getUserData();
node.getChild( idx )->setUserData( m.get() );
}
traverse( (osg::Node&)node );
// Restore saved UserData.
for( idx=0; idx< node.getNumChildren(); ++idx )
{
node.getChild( idx )->setUserData( saveUserDataList[ idx ].get() );
}
}
void
FltExportVisitor::apply( osg::PositionAttitudeTransform& node )
{
_firstNode = false;
ScopedStatePushPop guard( this, node.getStateSet() );
osg::ref_ptr<osg::RefMatrix> m = new osg::RefMatrix(
osg::Matrix::translate( -node.getPivotPoint() ) *
osg::Matrix::scale( node.getScale() ) *
osg::Matrix::rotate( node.getAttitude() ) *
osg::Matrix::translate( node.getPosition() ) );
typedef std::vector< osg::ref_ptr< osg::Referenced > > UserDataList;
UserDataList saveUserDataList( node.getNumChildren() );
unsigned int idx;
for( idx=0; idx<node.getNumChildren(); ++idx )
{
saveUserDataList[ idx ] = node.getChild( idx )->getUserData();
node.getChild( idx )->setUserData( m.get() );
}
traverse( (osg::Node&)node );
// Restore saved UserData.
for( idx=0; idx<node.getNumChildren(); ++idx )
{
node.getChild( idx )->setUserData( saveUserDataList[ idx ].get() );
}
}
void
FltExportVisitor::apply( osg::Transform& node )
{
_firstNode = false;
ScopedStatePushPop guard( this, node.getStateSet() );
osgSim::DOFTransform* dof = dynamic_cast<osgSim::DOFTransform*>( &node );
if (dof)
{
writeDegreeOfFreedom( dof);
}
writeMatrix( node.getUserData() );
writeComment( node );
writePushTraverseWritePop( node );
}
void
FltExportVisitor::apply( osg::LightSource& node )
{
_firstNode = false;
ScopedStatePushPop guard( this, node.getStateSet() );
writeLightSource( node );
writeMatrix( node.getUserData() );
writeComment( node );
writePushTraverseWritePop( node );
}
// Billboards also go through this code. The Geode is passed
// to writeFace and writeMesh. If those methods successfully cast
// the Geode to a Billboard, then they set the template mode
// bit accordingly.
void
FltExportVisitor::apply( osg::Geode& node )
{
_firstNode = false;
ScopedStatePushPop guard( this, node.getStateSet() );
unsigned int idx;
for (idx=0; idx<node.getNumDrawables(); idx++)
{
osg::Geometry* geom = node.getDrawable( idx )->asGeometry();
if (!geom)
{
std::string warning( "fltexp: Non-Geometry Drawable encountered. Ignoring." );
OSG_WARN << warning << std::endl;
_fltOpt->getWriteResult().warn( warning );
continue;
}
ScopedStatePushPop drawableGuard( this, geom->getStateSet() );
// Push and pop subfaces if polygon offset is on.
SubfaceHelper subface( *this, getCurrentStateSet() );
if (atLeastOneFace( *geom ))
{
// If at least one record will be a Face record, then we
// need to write to the vertex palette.
_vertexPalette->add( *geom );
// Iterate over all PrimitiveSets and output Face records.
unsigned int jdx;
for (jdx=0; jdx < geom->getNumPrimitiveSets(); jdx++)
{
osg::PrimitiveSet* prim = geom->getPrimitiveSet( jdx );
if ( isMesh( prim->getMode() ) )
continue;
if (prim->getType() == osg::PrimitiveSet::DrawArraysPrimitiveType)
handleDrawArrays( dynamic_cast<osg::DrawArrays*>( prim ), *geom, node );
else if (prim->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType)
handleDrawArrayLengths( dynamic_cast<osg::DrawArrayLengths*>( prim ), *geom, node );
else if ( (prim->getType() == osg::PrimitiveSet::DrawElementsUBytePrimitiveType) ||
(prim->getType() == osg::PrimitiveSet::DrawElementsUShortPrimitiveType) ||
(prim->getType() == osg::PrimitiveSet::DrawElementsUIntPrimitiveType) )
handleDrawElements( dynamic_cast<osg::DrawElements*>( prim ), *geom, node );
else
{
std::string warning( "fltexp: Unknown PrimitiveSet type." );
OSG_WARN << warning << std::endl;
_fltOpt->getWriteResult().warn( warning );
return;
}
}
}
if (atLeastOneMesh( *geom ))
{
// If at least one Mesh record, write out preamble mesh records
// followed by a Mesh Primitive record per PrimitiveSet.
writeMesh( node, *geom );
writeMatrix( node.getUserData() );
writeComment( node );
writeMultitexture( *geom );
writeLocalVertexPool( *geom );
writePush();
unsigned int jdx;
for (jdx=0; jdx < geom->getNumPrimitiveSets(); jdx++)
{
osg::PrimitiveSet* prim = geom->getPrimitiveSet( jdx );
if ( !isMesh( prim->getMode() ) )
continue;
if (prim->getType() == osg::PrimitiveSet::DrawArraysPrimitiveType)
handleDrawArrays( dynamic_cast<osg::DrawArrays*>( prim ), *geom, node );
else if (prim->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType)
handleDrawArrayLengths( dynamic_cast<osg::DrawArrayLengths*>( prim ), *geom, node );
else if ( (prim->getType() == osg::PrimitiveSet::DrawElementsUBytePrimitiveType) ||
(prim->getType() == osg::PrimitiveSet::DrawElementsUShortPrimitiveType) ||
(prim->getType() == osg::PrimitiveSet::DrawElementsUIntPrimitiveType) )
handleDrawElements( dynamic_cast<osg::DrawElements*>( prim ), *geom, node );
else
{
std::string warning( "fltexp: Unknown PrimitiveSet type." );
OSG_WARN << warning << std::endl;
_fltOpt->getWriteResult().warn( warning );
return;
}
}
writePop();
}
}
// Would traverse here if this node could have children.
// traverse( (osg::Node&)node );
}
void
FltExportVisitor::apply( osg::Node& node )
{
_firstNode = false;
ScopedStatePushPop guard( this, node.getStateSet() );
osgSim::LightPointNode* lpn = dynamic_cast< osgSim::LightPointNode* >( &node );
if (lpn)
{
writeLightPoint( lpn );
}
else
{
// Unknown Node. Warn and return.
// (Note, if the base class of this Node was a Group, then apply(Group&)
// would export a Group record then continue traversal. Because we are
// a Node, there's no way to continue traversal, so just return.)
std::string warning( "fltexp: Unknown Node in OpenFlight export." );
OSG_WARN << warning << std::endl;
_fltOpt->getWriteResult().warn( warning );
return;
}
}
void
FltExportVisitor::apply( osg::ProxyNode& node )
{
_firstNode = false;
ScopedStatePushPop guard( this, node.getStateSet() );
writeExternalReference( node );
writeMatrix( node.getUserData() );
writeComment( node );
}
bool
FltExportVisitor::complete( const osg::Node& node )
{
// Always write final pop level
writePop();
// Done writing records, close the record data temp file.
_recordsStr.close();
// Write OpenFlight file front matter: header, vertex palette, etc.
writeHeader( node.getName() );
writeColorPalette();
_materialPalette->write( _dos );
_texturePalette->write( _dos );
_lightSourcePalette->write( _dos );
_vertexPalette->write( _dos );
// Write Comment ancillary record and specify the _dos DataOutputStream.
writeComment( node, &_dos );
// Copy record data temp file into final OpenFlight file.
// Yee-uck. TBD need better stream copy routine.
char buf;
osgDB::ifstream recIn;
recIn.open( _recordsTempName.c_str(), std::ios::in | std::ios::binary );
while (!recIn.eof() )
{
recIn.read( &buf, 1 );
if (recIn.good())
_dos << buf;
}
recIn.close();
return true;
}
//
// StateSet stack support
void
FltExportVisitor::pushStateSet( const osg::StateSet* rhs )
{
osg::StateSet* ss = new osg::StateSet( *( _stateSetStack.back().get() ) );
if (rhs)
ss->merge( *rhs );
_stateSetStack.push_back( ss );
}
void
FltExportVisitor::popStateSet()
{
_stateSetStack.pop_back();
}
const osg::StateSet*
FltExportVisitor::getCurrentStateSet() const
{
return _stateSetStack.back().get();
}
void
FltExportVisitor::clearStateSetStack()
{
_stateSetStack.clear();
}
void
FltExportVisitor::writeATTRFile( int unit, const osg::Texture2D* texture ) const
{
std::string name;
if (_fltOpt->getStripTextureFilePath())
name = osgDB::getSimpleFileName( texture->getImage()->getFileName() );
else
name = texture->getImage()->getFileName();
name += std::string( ".attr" );
if ( osgDB::findDataFile( name ).empty() )
{
// No .attr file found. We should write one out.
// Fill AttrData fields from current state.
AttrData ad;
ad.texels_u = texture->getImage()->s();
ad.texels_v = texture->getImage()->t();
switch( texture->getFilter( osg::Texture::MIN_FILTER ) )
{
case osg::Texture::LINEAR:
ad.minFilterMode = AttrData::MIN_FILTER_BILINEAR;
break;
case osg::Texture::LINEAR_MIPMAP_LINEAR:
ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_TRILINEAR;
break;
case osg::Texture::LINEAR_MIPMAP_NEAREST:
ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_BILINEAR;
break;
case osg::Texture::NEAREST:
ad.minFilterMode = AttrData::MIN_FILTER_POINT;
break;
case osg::Texture::NEAREST_MIPMAP_LINEAR:
ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_LINEAR;
break;
case osg::Texture::NEAREST_MIPMAP_NEAREST:
ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_POINT;
break;
default:
ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_TRILINEAR;
break;
}
switch( texture->getFilter( osg::Texture::MAG_FILTER ) )
{
case osg::Texture::NEAREST:
ad.magFilterMode = AttrData::MAG_FILTER_POINT;
break;
default:
ad.magFilterMode = AttrData::MAG_FILTER_BILINEAR;
break;
}
// Repeat and Clamp
switch( texture->getWrap( osg::Texture::WRAP_S ) )
{
case osg::Texture::CLAMP:
case osg::Texture::CLAMP_TO_EDGE:
case osg::Texture::CLAMP_TO_BORDER:
ad.wrapMode_u = AttrData::WRAP_CLAMP;
break;
case osg::Texture::REPEAT:
default:
ad.wrapMode_u = AttrData::WRAP_REPEAT;
break;
case osg::Texture::MIRROR:
if (_fltOpt->getFlightFileVersionNumber() >= 1610)
ad.wrapMode_u = AttrData::WRAP_MIRRORED_REPEAT;
else
ad.wrapMode_u = AttrData::WRAP_REPEAT;
break;
}
switch( texture->getWrap( osg::Texture::WRAP_T ) )
{
case osg::Texture::CLAMP:
case osg::Texture::CLAMP_TO_EDGE:
case osg::Texture::CLAMP_TO_BORDER:
ad.wrapMode_v = AttrData::WRAP_CLAMP;
break;
case osg::Texture::REPEAT:
default:
ad.wrapMode_v = AttrData::WRAP_REPEAT;
break;
case osg::Texture::MIRROR:
if (_fltOpt->getFlightFileVersionNumber() >= 1610)
ad.wrapMode_v = AttrData::WRAP_MIRRORED_REPEAT;
else
ad.wrapMode_v = AttrData::WRAP_REPEAT;
break;
}
const osg::StateSet* ss = getCurrentStateSet();
const osg::TexEnv* texenv = dynamic_cast<const osg::TexEnv*>(
ss->getTextureAttribute( unit, osg::StateAttribute::TEXENV ) );
if (texenv)
{
switch( texenv->getMode())
{
case osg::TexEnv::DECAL:
ad.texEnvMode = AttrData::TEXENV_DECAL;
break;
case osg::TexEnv::MODULATE:
default:
ad.texEnvMode = AttrData::TEXENV_MODULATE;
break;
case osg::TexEnv::BLEND:
ad.texEnvMode = AttrData::TEXENV_BLEND;
break;
case osg::TexEnv::REPLACE:
ad.texEnvMode = AttrData::TEXENV_COLOR;
break;
case osg::TexEnv::ADD:
ad.texEnvMode = AttrData::TEXENV_ADD;
break;
}
}
osgDB::writeObjectFile( ad, name, _fltOpt.get() );
}
}
}