Files
OpenSceneGraph/src/osgPlugins/dx/AreaGeoSetTriangulator.cpp
Robert Osfield 8f1ba9d21b Removed include/osg/Types header defining osg::ubyte, osg::ushort etc. Changed
any reference to these in the distribution across to using unsigned char,
unsigned short etc.  This has been done to keep the OSG code more opaque
to what types are.
2003-02-12 19:20:47 +00:00

643 lines
24 KiB
C++

#include <osg/GeoSet>
#include <stdio.h>
#include <string.h>
#include "AreaGeoSetTriangulator.h"
namespace dx {
class AreaGeoSetTriangulator
// Used to convert area primitive GeoSets into TRIANGLE Geosets.
// Efficiently collects the various data arrays, which GeoSet can't do.
{
protected:
std::vector< unsigned int > _cindex;
std::vector< unsigned int > _nindex;
std::vector< unsigned int > _colindex;
std::vector< unsigned int > _tindex;
const osg::GeoSet &_area_geoset;
public:
AreaGeoSetTriangulator( const osg::GeoSet &area_geoset );
void operator() ( unsigned int v1, unsigned int v2, unsigned int v3,
unsigned int prim_index, unsigned int prim_tri_index,
unsigned int v1_geoset, unsigned int v2_geoset,
unsigned int v3_geoset );
// Passed triangles as primitives are triangulated
osg::GeoSet *BuildGeoSet();
};
//---------------------------------------------------------------------------
AreaGeoSetTriangulator::AreaGeoSetTriangulator( const osg::GeoSet &area_geoset)
: _area_geoset(area_geoset)
{
// Check type
switch ( area_geoset.getPrimType() ) {
case osg::GeoSet::TRIANGLE_STRIP:
case osg::GeoSet::FLAT_TRIANGLE_STRIP:
case osg::GeoSet::TRIANGLES:
case osg::GeoSet::QUAD_STRIP:
case osg::GeoSet::QUADS:
case osg::GeoSet::TRIANGLE_FAN:
case osg::GeoSet::FLAT_TRIANGLE_FAN:
case osg::GeoSet::POLYGON:
break;
default:
fprintf( stderr,
"Invalid primitive type passed to AreaGeoSetTriangulator\n" );
exit(1);
}
// NOTE: _coords, _normals, _colors, and _tcoords are maintained
// from the original geoset. The new geoset will always have
// indexed coords/normals/colors/tcoords (to save space).
}
//---------------------------------------------------------------------------
void AreaGeoSetTriangulator::operator() (
unsigned int v1, unsigned int v2, unsigned int v3,
unsigned int prim_index, unsigned int prim_tri_index,
unsigned int v1_geoset, unsigned int v2_geoset,
unsigned int v3_geoset )
// Passed triangles as primitives are triangulated from the original
// GeoSet. Note that the v? params are indicies into the Coord array,
// whereas the v?_geoset params are vertex numbers relative to the
// entire "area GeoSet" (which are different when the coords are
// indexed). The latter is needed to look up colors/normals/tcoords).
{
osg::GeoSet::BindingType binding;
osg::GeoSet::PrimitiveType primtype = _area_geoset.getPrimType();
int area_is_flatprim = ( primtype == osg::GeoSet::FLAT_TRIANGLE_STRIP ||
primtype == osg::GeoSet::FLAT_TRIANGLE_FAN );
// Store the triangle coord indicies
_cindex.push_back( v1 );
_cindex.push_back( v2 );
_cindex.push_back( v3 );
int index;
const osg::GeoSet::IndexPointer *ip;
// Store normals (as needed)
if ( _area_geoset.getNumNormals() ) {
ip = &_area_geoset.getNormalIndices();
// Grrr... FLAT_ primitives lie about binding type... like Performer
binding = _area_geoset.getNormalBinding();
if ( area_is_flatprim && binding == osg::GeoSet::BIND_PERVERTEX )
binding = osg::GeoSet::BIND_PERPRIM;
switch ( binding ) {
case osg::GeoSet::BIND_OVERALL : // Only once
if ( prim_index == 0 && prim_tri_index == 0 ) {
index = ip->valid() ? (*ip)[0] : 0;
_nindex.push_back( index );
break;
}
case osg::GeoSet::BIND_PERPRIM : // Once per tri
index = ip->valid() ? (*ip)[prim_index] : prim_index;
_nindex.push_back( index );
break;
case osg::GeoSet::BIND_PERVERTEX : // Each vertex
index = ip->valid() ? (*ip)[v1_geoset] : v1_geoset;
_nindex.push_back( index );
index = ip->valid() ? (*ip)[v2_geoset] : v2_geoset;
_nindex.push_back( index );
index = ip->valid() ? (*ip)[v3_geoset] : v3_geoset;
_nindex.push_back( index );
break;
case osg::GeoSet::BIND_OFF:
case osg::GeoSet::BIND_DEFAULT:
// Nonsensical cases
break;
}
}
// Store colors (as needed)
if ( _area_geoset.getNumColors() ) {
ip = &_area_geoset.getColorIndices();
// Grrr... FLAT_ primitives lie about binding type... like Performer
binding = _area_geoset.getColorBinding();
if ( area_is_flatprim && binding == osg::GeoSet::BIND_PERVERTEX )
binding = osg::GeoSet::BIND_PERPRIM;
switch ( binding ) {
case osg::GeoSet::BIND_OVERALL : // Only once
if ( prim_index == 0 && prim_tri_index == 0 ) {
index = ip->valid() ? (*ip)[0] : 0;
_colindex.push_back( index );
break;
}
case osg::GeoSet::BIND_PERPRIM : // Once per tri
index = ip->valid() ? (*ip)[prim_index] : prim_index;
_colindex.push_back( index );
break;
case osg::GeoSet::BIND_PERVERTEX : // Each vertex
index = ip->valid() ? (*ip)[v1_geoset] : v1_geoset;
_colindex.push_back( index );
index = ip->valid() ? (*ip)[v2_geoset] : v2_geoset;
_colindex.push_back( index );
index = ip->valid() ? (*ip)[v3_geoset] : v3_geoset;
_colindex.push_back( index );
break;
case osg::GeoSet::BIND_OFF:
case osg::GeoSet::BIND_DEFAULT:
// Nonsensical cases
break;
}
}
// Store tcoords (as needed)
if ( _area_geoset.getNumTextureCoords() ) {
ip = &_area_geoset.getTextureIndices();
switch ( _area_geoset.getTextureBinding() ) {
case osg::GeoSet::BIND_OVERALL : // Only once
if ( prim_index == 0 && prim_tri_index == 0 ) {
index = ip->valid() ? (*ip)[0] : 0;
_tindex.push_back( index );
break;
}
case osg::GeoSet::BIND_PERPRIM : // Once per tri
index = ip->valid() ? (*ip)[prim_index] : prim_index;
_tindex.push_back( index );
break;
case osg::GeoSet::BIND_PERVERTEX : // Each vertex
index = ip->valid() ? (*ip)[v1_geoset] : v1_geoset;
_tindex.push_back( index );
index = ip->valid() ? (*ip)[v2_geoset] : v2_geoset;
_tindex.push_back( index );
index = ip->valid() ? (*ip)[v3_geoset] : v3_geoset;
_tindex.push_back( index );
break;
case osg::GeoSet::BIND_OFF:
case osg::GeoSet::BIND_DEFAULT:
// Nonsensical cases
break;
}
}
}
//----------------------------------------------------------------------------
// Shamelessly lifted from the GeoSet header and adapted for our uses
// This is the same except that: 1) it passes the coordinate indices
// to the op function insted of the Vec3s, and 2) it passes an original
// primitive index and "triangle-in-primitive" index to the op function,
// 3) it also passes the absolute vertex numbers in the primitive so they
// can be used to look up normals/colors/tcoords, and 4) fixes some
// problems with vertex ordering which caused FRONT/BACK confusion with
// normals and lighting.
// With this, we can construct a new TRIANGLE GeoSet from any area GeoSet.
/** Template function for iterating through a GeoSet operating on triangles
with templated functor. Function automatically decomposes quads and polygons
into sub triangles which are passed onto functor.*/
template<class T>
void for_each_triangle2(const osg::GeoSet& gset,T& op)
{
switch(gset.getPrimType())
{
case(osg::GeoSet::TRIANGLE_STRIP):
case(osg::GeoSet::FLAT_TRIANGLE_STRIP):
{
if (gset.getCoordIndices().valid())
{
if (gset.getCoordIndices()._is_ushort)
{
unsigned short* base = gset.getCoordIndices()._ptr._ushort;
unsigned short* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
const int primLength = gset.getPrimLengths()[i];
unsigned short* iend = iptr+primLength;
int tri=0;
for(int j = 2; j < primLength; j++ )
{
if( !(j%2) )
op(*(iptr),*(iptr+1),*(iptr+2),i,tri++,
iptr-base,(iptr+1)-base,(iptr+2)-base);
else
op(*(iptr),*(iptr+2),*(iptr+1),i,tri++,
iptr-base,(iptr+2)-base,(iptr+1)-base);
++iptr;
}
iptr=iend;
}
}
else
{
GLuint* base = gset.getCoordIndices()._ptr._uint;
GLuint* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
const int primLength = gset.getPrimLengths()[i];
GLuint* iend = iptr+primLength;
int tri=0;
for(int j = 2; j < primLength; j++ )
{
if( !(j%2) )
op(*(iptr),*(iptr+1),*(iptr+2),i,tri++,
iptr-base,(iptr+1)-base,(iptr+2)-base);
else
op(*(iptr),*(iptr+2),*(iptr+1),i,tri++,
iptr-base,(iptr+2)-base,(iptr+1)-base);
++iptr;
}
iptr=iend;
}
}
}
else
{
unsigned int cindex = 0;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
const int primLength = gset.getPrimLengths()[i];
unsigned int cindex_end = cindex+primLength;
int tri=0;
for(int j = 2; j < primLength; j++ )
{
if( !(j%2) )
op(cindex,cindex+1,cindex+2,i,tri++,
cindex,cindex+1,cindex+2);
else
op(cindex,cindex+2,cindex+1,i,tri++,
cindex,cindex+2,cindex+1);
++cindex;
}
cindex = cindex_end;
}
}
}
break;
case(osg::GeoSet::TRIANGLES):
{
if (gset.getCoordIndices().valid())
{
if (gset.getCoordIndices()._is_ushort)
{
unsigned short* base = gset.getCoordIndices()._ptr._ushort;
unsigned short* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
op(*(iptr),*(iptr+1),*(iptr+2),i,0,
iptr-base,(iptr+1)-base,(iptr+2)-base);
iptr+=3;
}
}
else
{
GLuint* base = gset.getCoordIndices()._ptr._uint;
GLuint* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
op(*(iptr),*(iptr+1),*(iptr+2),i,0,
iptr-base,(iptr+1)-base,(iptr+2)-base);
iptr+=3;
}
}
}
else
{
unsigned int cindex = 0;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
op(cindex,cindex+1,cindex+2,i,0,
cindex,cindex+1,cindex+2);
cindex+=3;
}
}
}
break;
case(osg::GeoSet::QUAD_STRIP):
{
if (gset.getCoordIndices().valid())
{
if (gset.getCoordIndices()._is_ushort)
{
unsigned short* base = gset.getCoordIndices()._ptr._ushort;
unsigned short* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
const int primLength = gset.getPrimLengths()[i];
unsigned short* iend = iptr+primLength;
int tri=0;
for(int j = 3; j < primLength; j+=2 )
{
op(*(iptr),*(iptr+1),*(iptr+2),i,tri++,
iptr-base,(iptr+1)-base,(iptr+2)-base);
// rhh FIXME: FIXED 1st vert offset
op(*(iptr+1),*(iptr+3),*(iptr+2),i,tri++,
(iptr+1)-base,(iptr+3)-base,(iptr+2)-base);
iptr+=2;
}
iptr=iend;
}
}
else
{
GLuint* base = gset.getCoordIndices()._ptr._uint;
GLuint* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
const int primLength = gset.getPrimLengths()[i];
GLuint* iend = iptr+primLength;
int tri=0;
for(int j = 3; j < primLength; j+=2 )
{
op(*(iptr),*(iptr+1),*(iptr+2),i,tri++,
iptr-base,(iptr+1)-base,(iptr+2)-base);
op(*(iptr+1),*(iptr+3),*(iptr+2),i,tri++,
(iptr+1)-base,(iptr+3)-base,(iptr+2)-base);
iptr+=2;
}
iptr=iend;
}
}
}
else
{
unsigned int cindex = 0;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
const int primLength = gset.getPrimLengths()[i];
unsigned int cindex_end = cindex+primLength;
int tri=0;
for(int j = 3; j < primLength; j+=2 )
{
op(cindex,cindex+1,cindex+2,i,tri++,
cindex,cindex+1,cindex+2);
op(cindex+1,cindex+3,cindex+2,i,tri++,
cindex+1,cindex+3,cindex+2);
cindex+=2;
}
cindex = cindex_end;
}
}
}
break;
case(osg::GeoSet::QUADS):
{
if (gset.getCoordIndices().valid())
{
if (gset.getCoordIndices()._is_ushort)
{
unsigned short* base = gset.getCoordIndices()._ptr._ushort;
unsigned short* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
op(*(iptr),*(iptr+1),*(iptr+2),i,0,
iptr-base,(iptr+1)-base,(iptr+2)-base);
// rhh FIXME: FIXED 2nd & 3rd vert offset
op(*(iptr),*(iptr+2),*(iptr+3),i,1,
iptr-base,(iptr+2)-base,(iptr+3)-base);
iptr+=4;
}
}
else
{
GLuint* base = gset.getCoordIndices()._ptr._uint;
GLuint* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
op(*(iptr),*(iptr+1),*(iptr+2),i,0,
iptr-base,(iptr+1)-base,(iptr+2)-base);
op(*(iptr),*(iptr+2),*(iptr+3),i,1,
iptr-base,(iptr+2)-base,(iptr+3)-base);
iptr+=4;
}
}
}
else
{
unsigned int cindex = 0;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
op(cindex,cindex+1,cindex+2,i,0,
cindex,cindex+1,cindex+2);
op(cindex,cindex+2,cindex+3,i,1,
cindex,cindex+2,cindex+3);
cindex+=4;
}
}
}
break;
case(osg::GeoSet::TRIANGLE_FAN):
case(osg::GeoSet::FLAT_TRIANGLE_FAN): // FIXME: rhh added
case(osg::GeoSet::POLYGON):
{
if (gset.getCoordIndices().valid())
{
if (gset.getCoordIndices()._is_ushort)
{
unsigned short* base = gset.getCoordIndices()._ptr._ushort;
unsigned short* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
const int primLength = gset.getPrimLengths()[i];
int tri=0;
if (primLength>0)
{
unsigned short *start = iptr;
unsigned short* iend = iptr+primLength;
++iptr;
for(int j = 2; j < primLength; ++j )
{
op(*start,*(iptr),*(iptr+1),i,tri++,
start-base, iptr-base,(iptr+1)-base);
++iptr;
}
iptr=iend;
}
}
}
else
{
GLuint* base = gset.getCoordIndices()._ptr._uint;
GLuint* iptr = base;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
const int primLength = gset.getPrimLengths()[i];
int tri=0;
if (primLength>0)
{
GLuint *start = iptr;
GLuint* iend = iptr+primLength;
++iptr;
for(int j = 2; j < primLength; ++j )
{
op(*start,*(iptr),*(iptr+1),i,tri++,
start-base, iptr-base,(iptr+1)-base);
++iptr;
}
iptr=iend;
}
}
}
}
else
{
unsigned int cindex = 0;
const int numPrim = gset.getNumPrims();
for(int i=0; i<numPrim; ++i )
{
const int primLength = gset.getPrimLengths()[i];
int tri=0;
if (primLength>0)
{
unsigned int cindex_start = cindex;
unsigned int cindex_end = cindex+primLength;
++cindex;
for(int j = 2; j < primLength; ++j)
{
op(cindex_start,cindex,cindex+1,i,tri++,
cindex_start,cindex,cindex+1);
++cindex;
}
cindex = cindex_end;
}
}
}
}
break;
default:
break;
}
}
//---------------------------------------------------------------------------
osg::GeoSet *AreaGeoSetTriangulator::BuildGeoSet()
// After a triangulation pass, generate a valid TRIANGLE GeoSet containing
// the resulting triangles (w/ colors, normals, and tcoords).
{
int num_tris = _cindex.size() / 3;
osg::GeoSet *res = new osg::GeoSet;
res->setPrimType( osg::GeoSet::TRIANGLES );
res->setNumPrims( num_tris );
// First, dup all of the data arrays -- they haven't changed
// NOTE: We generate fresh arrays for the new GeoSet to be compatible
// with the delete [] behavior of the default GeoSet
// AttributeDeleteFunctor, which is invoked in GeoSet::~GeoSet.
osg::Vec3 *new_coords = 0;
osg::Vec3 *new_normals = 0;
osg::Vec4 *new_colors = 0;
osg::Vec2 *new_tcoords = 0;
const osg::Vec3 *coords = _area_geoset.getCoords();
const osg::Vec3 *normals = _area_geoset.getNormals();
const osg::Vec4 *colors = _area_geoset.getColors();
const osg::Vec2 *tcoords = _area_geoset.getTextureCoords();
if ( coords )
new_coords = new osg::Vec3[ _area_geoset.getNumCoords() ];
if ( normals )
new_normals = new osg::Vec3[ _area_geoset.getNumNormals() ];
if ( colors )
new_colors = new osg::Vec4[ _area_geoset.getNumColors() ];
if ( tcoords )
new_tcoords = new osg::Vec2[ _area_geoset.getNumTextureCoords() ];
memcpy( new_coords, coords,
sizeof(osg::Vec3) * _area_geoset.getNumCoords() );
memcpy( new_normals, normals,
sizeof(osg::Vec3) * _area_geoset.getNumNormals() );
memcpy( new_colors, colors,
sizeof(osg::Vec4) * _area_geoset.getNumColors() );
memcpy( new_tcoords, tcoords,
sizeof(osg::Vec2) * _area_geoset.getNumTextureCoords() );
// Now generate the index arrays
GLuint *new_cindex = 0;
GLuint *new_nindex = 0;
GLuint *new_colindex = 0;
GLuint *new_tindex = 0;
if ( _cindex.size() ) new_cindex = new GLuint[ _cindex.size() ];
if ( _nindex.size() ) new_nindex = new GLuint[ _nindex.size() ];
if ( _colindex.size() ) new_colindex = new GLuint[ _colindex.size() ];
if ( _tindex.size() ) new_tindex = new GLuint[ _tindex.size() ];
memcpy( new_cindex , &_cindex [0], sizeof(GLuint) * _cindex.size() );
memcpy( new_nindex , &_nindex [0], sizeof(GLuint) * _nindex.size() );
memcpy( new_colindex, &_colindex[0], sizeof(GLuint) * _colindex.size() );
memcpy( new_tindex , &_tindex [0], sizeof(GLuint) * _tindex.size() );
res->setCoords ( new_coords , new_cindex );
res->setNormals( new_normals, new_nindex );
res->setColors ( new_colors , new_colindex );
res->setTextureCoords( new_tcoords, new_tindex );
// And finally, set the normal/color/tcoord binding
// (Grrr... FLAT_ primitives lie about binding type... like Performer)
osg::GeoSet::BindingType nbinding = _area_geoset.getNormalBinding();
osg::GeoSet::BindingType cbinding = _area_geoset.getColorBinding();
if ( _area_geoset.getPrimType() == osg::GeoSet::FLAT_TRIANGLE_STRIP ||
_area_geoset.getPrimType() == osg::GeoSet::FLAT_TRIANGLE_FAN ) {
if ( nbinding == osg::GeoSet::BIND_PERVERTEX )
nbinding = osg::GeoSet::BIND_PERPRIM;
if ( cbinding == osg::GeoSet::BIND_PERVERTEX )
cbinding = osg::GeoSet::BIND_PERPRIM;
}
res->setNormalBinding ( nbinding );
res->setColorBinding ( cbinding );
res->setTextureBinding( _area_geoset.getTextureBinding() );
return res;
}
//---------------------------------------------------------------------------
osg::GeoSet *TriangulateAreaGeoSet( const osg::GeoSet &geoset )
// The strategy here is to generate new coord/normal/color/tcoord index
// arrays as we bust up the primitives into triangles. The data arrays
// don't change between the old and new geoset.
{
geoset.computeNumVerts(); // Update # coords/# normals from index arrays
AreaGeoSetTriangulator triangulator( geoset );
for_each_triangle2( geoset, triangulator );
return triangulator.BuildGeoSet();
}
}; // namespace dx