OpenSceneGraph/src/osgPlugins/OpenFlight/expGeometryRecords.cpp

1067 lines
33 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 "DataOutputStream.h"
#include "Opcodes.h"
#include "MaterialPaletteManager.h"
#include "TexturePaletteManager.h"
#include "VertexPaletteManager.h"
#include <osg/CullFace>
#include <osg/BlendFunc>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/Billboard>
#include <osg/io_utils>
#include <osg/Material>
#include <osg/Texture2D>
#include <sstream>
namespace flt
{
// Bit flags for multitexturing
static unsigned int LAYER_1( 0x80000000 >> 0 );
static unsigned int LAYER_2( 0x80000000 >> 1 );
static unsigned int LAYER_3( 0x80000000 >> 2 );
static unsigned int LAYER_4( 0x80000000 >> 3 );
static unsigned int LAYER_5( 0x80000000 >> 4 );
static unsigned int LAYER_6( 0x80000000 >> 5 );
static unsigned int LAYER_7( 0x80000000 >> 6 );
bool
FltExportVisitor::isLit( const osg::Geometry& geom ) const
{
const osg::StateSet* ss = getCurrentStateSet();
if ( ss->getMode( GL_LIGHTING ) & osg::StateAttribute::ON )
return true;
else
return false; //( geom.getNormalBinding() == osg::Geometry::BIND_PER_VERTEX );
}
bool
FltExportVisitor::isTextured( int unit, const osg::Geometry& geom ) const
{
const osg::StateSet* ss = getCurrentStateSet();
bool texOn( ss->getTextureMode( unit, GL_TEXTURE_2D ) & osg::StateAttribute::ON );
bool hasCoords( geom.getTexCoordArray( unit ) != NULL );
return( texOn && hasCoords );
}
bool
FltExportVisitor::isMesh( const GLenum mode ) const
{
return( (mode == GL_TRIANGLE_STRIP) ||
(mode == GL_TRIANGLE_FAN) ||
(mode == GL_QUAD_STRIP) );
}
bool
FltExportVisitor::atLeastOneFace( const osg::Geometry& geom ) const
{
// Return true if at least one PrimitiveSet mode will use a Face record.
unsigned int jdx;
for (jdx=0; jdx < geom.getNumPrimitiveSets(); jdx++)
{
const osg::PrimitiveSet* prim = geom.getPrimitiveSet( jdx );
if( !isMesh( prim->getMode() ) )
return true;
}
// All PrimitiveSet modes will use Mesh records.
return false;
}
bool
FltExportVisitor::atLeastOneMesh( const osg::Geometry& geom ) const
{
// Return true if at least one PrimitiveSet mode will use a Mesh record.
unsigned int jdx;
for (jdx=0; jdx < geom.getNumPrimitiveSets(); jdx++)
{
const osg::PrimitiveSet* prim = geom.getPrimitiveSet( jdx );
if( isMesh( prim->getMode() ) )
return true;
}
// All PrimitiveSet modes will use Face records.
return false;
}
void
FltExportVisitor::writeFace( const osg::Geode& geode, const osg::Geometry& geom, GLenum mode )
{
enum DrawMode
{
SOLID_BACKFACE = 0,
SOLID_NO_BACKFACE = 1,
WIREFRAME_CLOSED = 2,
WIREFRAME_NOT_CLOSED = 3,
SURROUND_ALTERNATE_COLOR = 4,
OMNIDIRECTIONAL_LIGHT = 8,
UNIDIRECTIONAL_LIGHT = 9,
BIDIRECTIONAL_LIGHT = 10
};
enum TemplateMode
{
FIXED_NO_ALPHA_BLENDING = 0,
FIXED_ALPHA_BLENDING = 1,
AXIAL_ROTATE_WITH_ALPHA_BLENDING = 2,
POINT_ROTATE_WITH_ALPHA_BLENDING = 4
};
const unsigned int TERRAIN_BIT = 0x80000000u >> 0;
const unsigned int NO_COLOR_BIT = 0x80000000u >> 1;
const unsigned int NO_ALT_COLOR_BIT = 0x80000000u >> 2;
const unsigned int PACKED_COLOR_BIT = 0x80000000u >> 3;
const unsigned int FOOTPRINT_BIT = 0x80000000u >> 4; // Terrain culture cutout
const unsigned int HIDDEN_BIT = 0x80000000u >> 5;
const unsigned int ROOFLINE_BIT = 0x80000000u >> 6;
uint32 flags( PACKED_COLOR_BIT );
if (geode.getNodeMask() == 0)
flags |= HIDDEN_BIT;
osg::StateSet const* ss = getCurrentStateSet();
enum LightMode
{
FACE_COLOR = 0,
VERTEX_COLOR = 1,
FACE_COLOR_LIGHTING = 2,
VERTEX_COLOR_LIGHTING = 3
};
int8 lightMode;
osg::Vec4 packedColorRaw( 1., 1., 1., 1. );
uint16 transparency( 0 );
if (geom.getColorBinding() == osg::Geometry::BIND_PER_VERTEX)
{
if( isLit( geom ) )
lightMode = VERTEX_COLOR_LIGHTING;
else
lightMode = VERTEX_COLOR;
}
else
{
const osg::Vec4Array* c = dynamic_cast<const osg::Vec4Array*>( geom.getColorArray() );
if (c && (c->size() > 0))
{
packedColorRaw = (*c)[0];
transparency = flt::uint16((1. - packedColorRaw[3]) * (double)0xffff);
}
if ( isLit( geom ) )
lightMode = FACE_COLOR_LIGHTING;
else
lightMode = FACE_COLOR;
}
uint32 packedColor;
packedColor = (int)(packedColorRaw[3]*255) << 24 |
(int)(packedColorRaw[2]*255) << 16 | (int)(packedColorRaw[1]*255) << 8 |
(int)(packedColorRaw[0]*255);
int8 drawType;
switch( mode )
{
case GL_POINTS:
{
std::string warning( "fltexp: GL_POINTS not supported in FLT export." );
osg::notify( osg::WARN ) << warning << std::endl;
_fltOpt->getWriteResult().warn( warning );
return;
break;
}
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_QUAD_STRIP:
{
std::string warning( "fltexp: Wrong mode in Face record." );
osg::notify( osg::WARN ) << warning << std::endl;
_fltOpt->getWriteResult().warn( warning );
return;
break;
}
case GL_LINES:
case GL_LINE_STRIP:
drawType = WIREFRAME_NOT_CLOSED;
break;
case GL_LINE_LOOP:
drawType = WIREFRAME_CLOSED;
break;
case GL_TRIANGLES:
case GL_QUADS:
case GL_POLYGON:
{
// Default to no facet culling
drawType = SOLID_NO_BACKFACE;
// If facet-culling isn't *dis*abled, check whether the CullFace mode is BACK
if (ss->getMode(GL_CULL_FACE) & osg::StateAttribute::ON)
{
osg::CullFace const* cullFace = static_cast<osg::CullFace const*>(
ss->getAttribute(osg::StateAttribute::CULLFACE) );
if( cullFace->getMode() == osg::CullFace::BACK )
drawType = SOLID_BACKFACE;
// Note: OpenFlt can't handle FRONT or FRONT_AND_BACK settings, so ignore these(??)
}
break;
}
}
// Determine the material properties for the face
int16 materialIndex( -1 );
if (isLit( geom ))
{
osg::Material const* currMaterial = static_cast<osg::Material const*>(
ss->getAttribute(osg::StateAttribute::MATERIAL) );
materialIndex = _materialPalette->add(currMaterial);
}
// Get base texture
int16 textureIndex( -1 );
if (isTextured( 0, geom ))
{
const osg::Texture2D* texture = static_cast<const osg::Texture2D*>(
ss->getTextureAttribute( 0, osg::StateAttribute::TEXTURE ) );
if (texture != NULL)
textureIndex = _texturePalette->add( 0, texture );
else
{
std::string warning( "fltexp: Face is textured, but Texture2D StateAttribute is NULL." );
osg::notify( osg::WARN ) << warning << std::endl;
_fltOpt->getWriteResult().warn( warning );
}
}
// Set the appropriate template mode based
// on blending or Billboarding.
TemplateMode templateMode( FIXED_NO_ALPHA_BLENDING );
const osg::Billboard* bb = dynamic_cast< const osg::Billboard* >( &geode );
if (bb != NULL)
{
if( bb->getMode() == osg::Billboard::AXIAL_ROT )
templateMode = AXIAL_ROTATE_WITH_ALPHA_BLENDING;
else
templateMode = POINT_ROTATE_WITH_ALPHA_BLENDING;
}
else if ( ss->getMode( GL_BLEND ) & osg::StateAttribute::ON )
{
const osg::BlendFunc* bf = static_cast<const osg::BlendFunc*>(
ss->getAttribute(osg::StateAttribute::BLENDFUNC) );
if( (bf->getSource() == osg::BlendFunc::SRC_ALPHA) &&
(bf->getDestination() == osg::BlendFunc::ONE_MINUS_SRC_ALPHA) )
templateMode = FIXED_ALPHA_BLENDING;
}
uint16 length( 80 );
IdHelper id( *this, geode.getName() );
_records->writeInt16( (int16) FACE_OP );
_records->writeUInt16( length );
_records->writeID( id );
_records->writeInt32( 0 ); // IR color code
_records->writeInt16( 0 ); // Relative priority
_records->writeInt8( drawType ); // Draw type
_records->writeInt8( 0 ); // Texture white
_records->writeInt16( -1 ); // Color name index
_records->writeInt16( -1 ); // Alternate color name index
_records->writeInt8( 0 ); // Reserved
_records->writeInt8( templateMode ); // Template (billboard)
_records->writeInt16( -1 ); // Detail texture pattern index
_records->writeInt16( textureIndex ); // Texture pattern index
_records->writeInt16( materialIndex ); // Material index
_records->writeInt16( 0 ); // Surface material code
_records->writeInt16( 0 ); // Feature ID
_records->writeInt32( 0 ); // IR material code
_records->writeUInt16( transparency ); // Transparency
_records->writeInt8( 0 ); // LOD generation control
_records->writeInt8( 0 ); // Line style index
_records->writeUInt32( flags ); // Flags
_records->writeInt8( lightMode ); // Light mode
_records->writeFill( 7 ); // Reserved
_records->writeUInt32( packedColor ); // Packed color, primary
_records->writeUInt32( 0x00ffffff ); // Packed color, alternate
_records->writeInt16( -1 ); // Texture mapping index
_records->writeInt16( 0 ); // Reserved
_records->writeInt32( -1 ); // Primary color index
_records->writeInt32( -1 ); // Alternate color index
// Next four bytes:
// 15.8: two 2-byte "reserved" fields
// 15.9: one 4-byte "reserved" field
_records->writeInt16( 0 ); // Reserved
_records->writeInt16( -1 ); // Shader index
}
void
FltExportVisitor::writeMesh( const osg::Geode& geode, const osg::Geometry& geom )
{
enum DrawMode
{
SOLID_BACKFACE = 0,
SOLID_NO_BACKFACE = 1,
WIREFRAME_CLOSED = 2,
WIREFRAME_NOT_CLOSED = 3,
SURROUND_ALTERNATE_COLOR = 4,
OMNIDIRECTIONAL_LIGHT = 8,
UNIDIRECTIONAL_LIGHT = 9,
BIDIRECTIONAL_LIGHT = 10
};
enum TemplateMode
{
FIXED_NO_ALPHA_BLENDING = 0,
FIXED_ALPHA_BLENDING = 1,
AXIAL_ROTATE_WITH_ALPHA_BLENDING = 2,
POINT_ROTATE_WITH_ALPHA_BLENDING = 4
};
const unsigned int TERRAIN_BIT = 0x80000000u >> 0;
const unsigned int NO_COLOR_BIT = 0x80000000u >> 1;
const unsigned int NO_ALT_COLOR_BIT = 0x80000000u >> 2;
const unsigned int PACKED_COLOR_BIT = 0x80000000u >> 3;
const unsigned int FOOTPRINT_BIT = 0x80000000u >> 4; // Terrain culture cutout
const unsigned int HIDDEN_BIT = 0x80000000u >> 5;
const unsigned int ROOFLINE_BIT = 0x80000000u >> 6;
uint32 flags( PACKED_COLOR_BIT );
if (geode.getNodeMask() == 0)
flags |= HIDDEN_BIT;
enum LightMode
{
FACE_COLOR = 0,
VERTEX_COLOR = 1,
FACE_COLOR_LIGHTING = 2,
VERTEX_COLOR_LIGHTING = 3
};
int8 lightMode;
osg::Vec4 packedColorRaw( 1., 1., 1., 1. );
uint16 transparency( 0 );
if (geom.getColorBinding() == osg::Geometry::BIND_PER_VERTEX)
{
if (isLit( geom ))
lightMode = VERTEX_COLOR_LIGHTING;
else
lightMode = VERTEX_COLOR;
}
else
{
const osg::Vec4Array* c = dynamic_cast<const osg::Vec4Array*>( geom.getColorArray() );
if (c && (c->size() > 0))
{
packedColorRaw = (*c)[0];
transparency = flt::uint16((1. - packedColorRaw[3]) * (double)0xffff);
}
if (isLit( geom ))
lightMode = FACE_COLOR_LIGHTING;
else
lightMode = FACE_COLOR;
}
uint32 packedColor;
packedColor = (int)(packedColorRaw[3]*255) << 24 |
(int)(packedColorRaw[2]*255) << 16 | (int)(packedColorRaw[1]*255) << 8 |
(int)(packedColorRaw[0]*255);
int8 drawType;
osg::StateSet const* ss = getCurrentStateSet();
{
// Default to no facet culling
drawType = SOLID_NO_BACKFACE;
// If facet-culling isn't *dis*abled, check whether the CullFace mode is BACK
if (ss->getMode(GL_CULL_FACE) & osg::StateAttribute::ON)
{
osg::CullFace const* cullFace = static_cast<osg::CullFace const*>(
ss->getAttribute(osg::StateAttribute::CULLFACE) );
if( cullFace->getMode() == osg::CullFace::BACK )
drawType = SOLID_BACKFACE;
// Note: OpenFlt can't handle FRONT or FRONT_AND_BACK settings, so ignore these(??)
}
}
// Determine the material properties for the face
int16 materialIndex( -1 );
if (isLit( geom ))
{
osg::Material const* currMaterial = static_cast<osg::Material const*>(
ss->getAttribute(osg::StateAttribute::MATERIAL) );
materialIndex = _materialPalette->add(currMaterial);
}
// Get base texture
int16 textureIndex( -1 );
if (isTextured( 0, geom ))
{
const osg::Texture2D* texture = static_cast<const osg::Texture2D*>(
ss->getTextureAttribute( 0, osg::StateAttribute::TEXTURE ) );
if (texture != NULL)
textureIndex = _texturePalette->add( 0, texture );
else
{
std::string warning( "fltexp: Mesh is textured, but Texture2D StateAttribute is NULL." );
osg::notify( osg::WARN ) << warning << std::endl;
_fltOpt->getWriteResult().warn( warning );
}
}
// Set the appropriate template mode based
// on blending or Billboarding.
TemplateMode templateMode( FIXED_NO_ALPHA_BLENDING );
const osg::Billboard* bb = dynamic_cast< const osg::Billboard* >( &geode );
if (bb != NULL)
{
if( bb->getMode() == osg::Billboard::AXIAL_ROT )
templateMode = AXIAL_ROTATE_WITH_ALPHA_BLENDING;
else
templateMode = POINT_ROTATE_WITH_ALPHA_BLENDING;
}
else if ( ss->getMode( GL_BLEND ) & osg::StateAttribute::ON )
{
const osg::BlendFunc* bf = static_cast<const osg::BlendFunc*>(
ss->getAttribute(osg::StateAttribute::BLENDFUNC) );
if( (bf->getSource() == osg::BlendFunc::SRC_ALPHA) &&
(bf->getDestination() == osg::BlendFunc::ONE_MINUS_SRC_ALPHA) )
templateMode = FIXED_ALPHA_BLENDING;
}
uint16 length( 84 );
IdHelper id( *this, geode.getName() );
_records->writeInt16( (int16) MESH_OP );
_records->writeUInt16( length );
_records->writeID( id );
_records->writeInt32( 0 ); // Reserved
_records->writeInt32( 0 ); // IR color code
_records->writeInt16( 0 ); // Relative priority
_records->writeInt8( drawType ); // Draw type
_records->writeInt8( 0 ); // Texture white
_records->writeInt16( -1 ); // Color name index
_records->writeInt16( -1 ); // Alternate color name index
_records->writeInt8( 0 ); // Reserved
_records->writeInt8( templateMode ); // Template (billboard)
_records->writeInt16( -1 ); // Detail texture pattern index
_records->writeInt16( textureIndex ); // Texture pattern index
_records->writeInt16( materialIndex ); // Material index
_records->writeInt16( 0 ); // Surface material code
_records->writeInt16( 0 ); // Feature ID
_records->writeInt32( 0 ); // IR material code
_records->writeUInt16( transparency ); // Transparency
_records->writeInt8( 0 ); // LOD generation control
_records->writeInt8( 0 ); // Line style index
_records->writeUInt32( flags ); // Flags
_records->writeInt8( lightMode ); // Light mode
_records->writeFill( 7 ); // Reserved
_records->writeUInt32( packedColor ); // Packed color, primary
_records->writeUInt32( 0x00ffffff ); // Packed color, alternate
_records->writeInt16( -1 ); // Texture mapping index
_records->writeInt16( 0 ); // Reserved
_records->writeInt32( -1 ); // Primary color index
_records->writeInt32( -1 ); // Alternate color index
// Next four bytes:
// 15.8: two 2-byte "reserved" fields
// 15.9: one 4-byte "reserved" field
_records->writeInt16( 0 ); // Reserved
_records->writeInt16( -1 ); // Shader index
}
int
FltExportVisitor::writeVertexList( int first, unsigned int count )
{
_records->writeInt16( (int16) VERTEX_LIST_OP );
_records->writeUInt16( 4 + (count*4) );
unsigned int idx;
for( idx=0; idx<count; idx++)
// I'm imagining that 'first' will be a 0-based index into the
// 'current' set of vertices held by the vertex palette manager.
_records->writeInt32( _vertexPalette->byteOffset( first+idx ) );
return count;
}
int
FltExportVisitor::writeVertexList( const std::vector<unsigned int>& indices, unsigned int count )
{
_records->writeInt16( (int16) VERTEX_LIST_OP );
_records->writeUInt16( 4 + (count*4) );
unsigned int idx;
for( idx=0; idx<count; idx++)
// I'm imagining that 'first' will be a 0-based index into the
// 'current' set of vertices held by the vertex palette manager.
_records->writeInt32( _vertexPalette->byteOffset( indices[ idx ] ) );
return count;
}
void
FltExportVisitor::writeMeshPrimitive( const std::vector<unsigned int>& indices, GLenum mode )
{
int16 primType;
switch( mode )
{
case GL_TRIANGLE_STRIP:
primType = 1;
break;
case GL_TRIANGLE_FAN:
primType = 2;
break;
case GL_QUAD_STRIP:
primType = 3;
break;
default:
// Warning should already be recorded. Do nothing.
return;
break;
}
uint16 length( 12 + (4 * indices.size()) );
_records->writeInt16( (int16) MESH_PRIMITIVE_OP );
_records->writeUInt16( length );
_records->writeInt16( primType ); // primitive type
_records->writeInt16( 4 ); // index size, 4 bytes
_records->writeInt32( indices.size() ); // vertex count
std::vector<unsigned int>::const_iterator it = indices.begin();
while (it != indices.end())
{
_records->writeUInt32( (*it) );
it++;
}
}
void
FltExportVisitor::writeLocalVertexPool( const osg::Geometry& geom )
{
// Attribute Mask
static const unsigned int HAS_POSITION = 0x80000000u >> 0;
static const unsigned int HAS_COLOR_INDEX = 0x80000000u >> 1;
static const unsigned int HAS_RGBA_COLOR = 0x80000000u >> 2;
static const unsigned int HAS_NORMAL = 0x80000000u >> 3;
static const unsigned int HAS_BASE_UV = 0x80000000u >> 4;
static const unsigned int HAS_UV_LAYER1 = 0x80000000u >> 5;
static const unsigned int HAS_UV_LAYER2 = 0x80000000u >> 6;
static const unsigned int HAS_UV_LAYER3 = 0x80000000u >> 7;
static const unsigned int HAS_UV_LAYER4 = 0x80000000u >> 8;
static const unsigned int HAS_UV_LAYER5 = 0x80000000u >> 9;
static const unsigned int HAS_UV_LAYER6 = 0x80000000u >> 10;
static const unsigned int HAS_UV_LAYER7 = 0x80000000u >> 11;
const osg::Array* v = geom.getVertexArray();
uint32 numVerts( v->getNumElements() );
osg::ref_ptr< const osg::Vec3dArray > v3 = VertexPaletteManager::asVec3dArray( v, numVerts );
if (!v3)
{
std::string warning( "fltexp: writeLocalVertexPool: VertexArray is not Vec3Array." );
osg::notify( osg::WARN ) << warning << std::endl;
_fltOpt->getWriteResult().warn( warning );
return;
}
// Compute attribute bits and vertex size.
const osg::Array* c = geom.getColorArray();
const osg::Array* n = geom.getNormalArray();
const osg::Array* t = geom.getTexCoordArray( 0 );
osg::ref_ptr< const osg::Vec4Array > c4 = VertexPaletteManager::asVec4Array( c, numVerts );
osg::ref_ptr< const osg::Vec3Array > n3 = VertexPaletteManager::asVec3Array( n, numVerts );
osg::ref_ptr< const osg::Vec2Array > t2 = VertexPaletteManager::asVec2Array( t, numVerts );
if (c && !c4)
return;
if (n && !n3)
return;
if (t && !t2)
return;
std::vector< osg::ref_ptr< const osg::Vec2Array > > mtc;
mtc.resize( 8 );
int unit=1;
for( ;unit<8; unit++)
mtc[ unit ] = VertexPaletteManager::asVec2Array( geom.getTexCoordArray( unit ), numVerts );
uint32 attr( HAS_POSITION );
unsigned int vertSize( sizeof( float64 ) * 3 );
if ( ( c4 != NULL ) && ( geom.getColorBinding() == osg::Geometry::BIND_PER_VERTEX) )
{
attr |= HAS_RGBA_COLOR;
vertSize += sizeof( unsigned int );
}
if ( ( n3 != NULL ) && ( geom.getNormalBinding() == osg::Geometry::BIND_PER_VERTEX) )
{
attr |= HAS_NORMAL;
vertSize += ( sizeof( float32 ) * 3 );
}
if ( t2 != NULL )
{
attr |= HAS_BASE_UV;
vertSize += ( sizeof( float32 ) * 2 );
}
// Add multitex
if (isTextured( 1, geom ))
{
attr |= HAS_UV_LAYER1;
vertSize += ( sizeof( float32 ) * 2 );
}
if (isTextured( 2, geom ))
{
attr |= HAS_UV_LAYER2;
vertSize += ( sizeof( float32 ) * 2 );
}
if (isTextured( 3, geom ))
{
attr |= HAS_UV_LAYER3;
vertSize += ( sizeof( float32 ) * 2 );
}
if (isTextured( 4, geom ))
{
attr |= HAS_UV_LAYER4;
vertSize += ( sizeof( float32 ) * 2 );
}
if (isTextured( 5, geom ))
{
attr |= HAS_UV_LAYER5;
vertSize += ( sizeof( float32 ) * 2 );
}
if (isTextured( 6, geom ))
{
attr |= HAS_UV_LAYER6;
vertSize += ( sizeof( float32 ) * 2 );
}
if (isTextured( 7, geom ))
{
attr |= HAS_UV_LAYER7;
vertSize += ( sizeof( float32 ) * 2 );
}
unsigned int maxVerts = (0xffff - 12) / vertSize;
unsigned int thisVertCount = (maxVerts > numVerts) ? numVerts : maxVerts;
unsigned int currentIndexLimit = maxVerts;
uint16 length( 12 + (vertSize * thisVertCount) );
_records->writeInt16( (int16) LOCAL_VERTEX_POOL_OP );
_records->writeUInt16( length );
_records->writeUInt32( numVerts ); // number of vertices
_records->writeUInt32( attr ); // attribute bits
unsigned int idx;
for( idx=0; idx<numVerts; idx++ )
{
_records->writeVec3d( (*v3)[ idx ] );
if (attr & HAS_RGBA_COLOR)
{
osg::Vec4 color = (*c4)[ idx ];
unsigned int packedColor = (int)(color[3]*255) << 24 |
(int)(color[2]*255) << 16 | (int)(color[1]*255) << 8 |
(int)(color[0]*255);
_records->writeUInt32( packedColor );
}
if (attr & HAS_NORMAL)
_records->writeVec3f( (*n3)[ idx ] );
if (attr & HAS_BASE_UV)
_records->writeVec2f( (*t2)[ idx ] );
if (attr & HAS_UV_LAYER1)
_records->writeVec2f( (*mtc[1])[ idx ] );
if (attr & HAS_UV_LAYER2)
_records->writeVec2f( (*mtc[2])[ idx ] );
if (attr & HAS_UV_LAYER3)
_records->writeVec2f( (*mtc[3])[ idx ] );
if (attr & HAS_UV_LAYER4)
_records->writeVec2f( (*mtc[4])[ idx ] );
if (attr & HAS_UV_LAYER5)
_records->writeVec2f( (*mtc[5])[ idx ] );
if (attr & HAS_UV_LAYER6)
_records->writeVec2f( (*mtc[6])[ idx ] );
if (attr & HAS_UV_LAYER7)
_records->writeVec2f( (*mtc[7])[ idx ] );
// Handle continuation record if necessary.
if ( (idx+1 == currentIndexLimit) && (idx+1 < numVerts) )
{
currentIndexLimit += maxVerts;
unsigned int remaining( numVerts - (idx+1) );
thisVertCount = (maxVerts > remaining) ? remaining : maxVerts;
writeContinuationRecord( (vertSize * thisVertCount) );
}
}
}
void
FltExportVisitor::writeMultitexture( const osg::Geometry& geom )
{
unsigned int numLayers( 0 );
uint32 flags( 0 );
unsigned int idx;
for( idx=1; idx<8; idx++)
{
if( isTextured( idx, geom ) )
{
flags |= LAYER_1 >> (idx-1);
numLayers++;
}
}
if( numLayers == 0 )
return;
uint16 length( 8 + (8*numLayers) );
_records->writeInt16( (int16) MULTITEXTURE_OP );
_records->writeUInt16( length );
_records->writeInt32( flags );
const osg::StateSet* ss = getCurrentStateSet();
for( idx=1; idx<8; idx++)
{
if( isTextured( idx, geom ) )
{
int16 textureIndex( -1 );
const osg::Texture2D* texture = static_cast<const osg::Texture2D*>(
ss->getTextureAttribute( idx, osg::StateAttribute::TEXTURE ) );
if (texture != NULL)
textureIndex = _texturePalette->add( idx, texture );
else
{
std::ostringstream warning;
warning << "fltexp: No Texture2D for unit " << idx;
osg::notify( osg::WARN ) << warning.str() << std::endl;
_fltOpt->getWriteResult().warn( warning.str() );
}
// texture index (this value is an unsigned int, but has a -1 default per oflt spec: ugh)
_records->writeUInt16( static_cast< uint16 >( textureIndex ) );
_records->writeUInt16( 0 ); // TBD effect
// mapping index (this value is an unsigned int, but has a -1 default per oflt spec: ugh)
_records->writeUInt16( static_cast< uint16 >( -1 ) );
_records->writeUInt16( 0 ); // data
}
}
}
void
FltExportVisitor::writeUVList( int numVerts, const osg::Geometry& geom )
{
unsigned int numLayers( 0 );
uint32 flags( 0 );
unsigned int idx;
for( idx=1; idx<8; idx++)
{
if( isTextured( idx, geom ) )
{
flags |= LAYER_1 >> (idx-1);
numLayers++;
}
}
if( numLayers == 0 )
return;
uint16 length( 8 + (8*numLayers*numVerts) );
_records->writeInt16( (int16) UV_LIST_OP );
_records->writeUInt16( length );
_records->writeInt32( flags );
osg::Vec2 defaultCoord( 0., 0. );
const osg::StateSet* ss = getCurrentStateSet();
for( idx=1; idx<8; idx++)
{
if( isTextured( idx, geom ) )
{
osg::Array* t = const_cast<osg::Array*>( geom.getTexCoordArray( idx ) );
osg::ref_ptr<osg::Vec2Array> t2 = dynamic_cast<osg::Vec2Array*>( t );
if (!t2.valid())
{
std::ostringstream warning;
warning << "fltexp: No Texture2D for unit " << idx;
osg::notify( osg::WARN ) << warning.str() << std::endl;
_fltOpt->getWriteResult().warn( warning.str() );
t2 = new osg::Vec2Array;
}
else if (t2->getNumElements() != numVerts)
{
std::ostringstream warning;
warning << "fltexp: Invalid number of texture coordinates for unit " << idx;
osg::notify( osg::WARN ) << warning.str() << std::endl;
_fltOpt->getWriteResult().warn( warning.str() );
}
const int size = t2->getNumElements();
int vIdx;
for( vIdx=0; vIdx<numVerts; vIdx++)
{
osg::Vec2& tc( defaultCoord );
if (vIdx < size)
tc = (*t2)[ vIdx ];
_records->writeFloat32( tc[0] );
_records->writeFloat32( tc[1] );
}
}
}
}
void
FltExportVisitor::handleDrawArrays( const osg::DrawArrays* da, const osg::Geometry& geom, const osg::Geode& geode )
{
GLint first = da->getFirst();
GLsizei count = da->getCount();
GLenum mode = da->getMode();
int n( 0 );
bool useMesh( false );
switch( mode )
{
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_QUAD_STRIP:
useMesh = true;
break;
case GL_POINTS:
n = 1;
break;
case GL_LINES:
n = 2;
break;
case GL_TRIANGLES:
n = 3;
break;
case GL_QUADS:
n = 4;
break;
case GL_LINE_STRIP:
case GL_LINE_LOOP:
case GL_POLYGON:
default:
n = count;
break;
}
if (useMesh)
{
std::vector< unsigned int > indices;
int jdx;
for (jdx=0; jdx<count; jdx++)
indices.push_back( first+jdx );
writeMeshPrimitive( indices, mode );
}
else
{
const unsigned int max( first+count );
while ((unsigned int)( first+n ) <= max)
{
writeFace( geode, geom, mode );
writeMatrix( geode.getUserData() );
writeComment( geode );
writeMultitexture( geom );
writePush();
// Write vertex list records.
int numVerts = writeVertexList( first, n );
first += n;
writeUVList( numVerts, geom );
writePop();
}
}
}
void
FltExportVisitor::handleDrawArrayLengths( const osg::DrawArrayLengths* dal, const osg::Geometry& geom, const osg::Geode& geode )
{
GLint first = dal->getFirst();
GLenum mode = dal->getMode();
int n( 0 );
bool useMesh( false );
switch( mode )
{
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_QUAD_STRIP:
useMesh = true;
break;
case GL_POINTS:
n = 1;
break;
case GL_LINES:
n = 2;
break;
case GL_TRIANGLES:
n = 3;
break;
case GL_QUADS:
n = 4;
break;
case GL_LINE_STRIP:
case GL_LINE_LOOP:
case GL_POLYGON:
default:
break;
}
// Push and pop subfaces if polygon offset is on.
SubfaceHelper subface( *this, getCurrentStateSet() );
if (useMesh)
{
int idx( 0 );
for( osg::DrawArrayLengths::const_iterator itr=dal->begin();
itr!=dal->end(); itr++ )
{
std::vector< unsigned int > indices;
int jdx;
for (jdx=0; jdx<(*itr); idx++, jdx++)
indices.push_back( idx );
writeMeshPrimitive( indices, mode );
}
}
else
{
// Hm. You wouldn't usually use DrawArrayLengths for non-strip/fan prims...
for( osg::DrawArrayLengths::const_iterator itr=dal->begin();
itr!=dal->end(); itr++ )
{
while (first+n <= *itr)
{
writeFace( geode, geom, mode );
writeMatrix( geode.getUserData() );
writeComment( geode );
writeMultitexture( geom );
writePush();
// Write vertex list records.
int numVerts;
if (n == 0)
{
numVerts = writeVertexList( first, *itr );
first += *itr;
}
else
{
numVerts = writeVertexList( first, n );
first += n;
}
writeUVList( numVerts, geom );
writePop();
}
first += *itr;
}
}
}
void
FltExportVisitor::handleDrawElements( const osg::DrawElements* de, const osg::Geometry& geom, const osg::Geode& geode )
{
GLenum mode = de->getMode();
int n( 0 );
bool useMesh( false );
switch( mode )
{
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_QUAD_STRIP:
n = de->getNumIndices();
useMesh = true;
break;
case GL_POINTS:
n = 1;
break;
case GL_LINES:
n = 2;
break;
case GL_TRIANGLES:
n = 3;
break;
case GL_QUADS:
n = 4;
break;
case GL_LINE_STRIP:
case GL_LINE_LOOP:
case GL_POLYGON:
default:
n = de->getNumIndices();
break;
}
// Push and pop subfaces if polygon offset is on.
SubfaceHelper subface( *this, getCurrentStateSet() );
if (useMesh)
{
std::vector< unsigned int > indices;
int idx;
for (idx=0; idx<n; idx++)
indices.push_back( de->index( idx ) );
writeMeshPrimitive( indices, mode );
}
else
{
unsigned int first( 0 );
while (first+n <= de->getNumIndices())
{
// Need:
// * Geode for record name (but also need to handle
// multi Geometry objects and multi PrimitiveSet objects;
// all Face records can't have the same name).
// * Mode
writeFace( geode, geom, mode );
writeMatrix( geode.getUserData() );
writeComment( geode );
writeMultitexture( geom );
writePush();
// Write vertex list records.
std::vector<unsigned int> indices;
int idx;
for(idx=0; idx<n; idx++)
indices.push_back( de->index( first+idx ) );
int numVerts = writeVertexList( indices, n );
first += n;
writeUVList( numVerts, geom );
writePop();
}
}
}
}