OpenSceneGraph/src/osgPlugins/flt/GeoSetBuilder.cpp
Robert Osfield facb0e2638 From Pavel Moloshtan, Byte2,3,4 and Short2,3,4 classes and their Array counterparts.
With a few build tweaks and bug fixes by Robert Osfield.
2005-07-05 15:57:53 +00:00

396 lines
12 KiB
C++

// GeoSetBuilder.cpp
// Modify DynGeoSet::addToGeometry to generate texture coordinates for texture unit 1
// that is used to detail texture
// Julian Ortiz, June 18th 2003.
#if defined(_MSC_VER)
#pragma warning( disable : 4786 )
#endif
#include "flt.h"
#include "FltFile.h"
#include "Pool.h"
#include "opcodes.h"
#include "GeoSetBuilder.h"
#include <osg/Object>
#include <osg/LOD>
#include <osg/BlendFunc>
#include <osg/Geode>
#include <osg/StateSet>
#include <osg/Material>
#include <osg/Texture2D>
#include <osg/TexEnv>
#include <osg/CullFace>
#include <osg/PolygonOffset>
#include <osg/Point>
#include <osg/Notify>
#include <osgUtil/Optimizer>
#include <map>
#include <algorithm>
using namespace flt;
////////////////////////////////////////////////////////////////////
//
// DynGeoSet
//
////////////////////////////////////////////////////////////////////
#define APPEND_DynGeoSet_List(list) \
if (source->list.size() > 0) \
list.insert(list.end(), \
source->list.begin(), source->list.end());
void DynGeoSet::append(DynGeoSet* source)
{
APPEND_DynGeoSet_List(_primLenList)
APPEND_DynGeoSet_List(_coordList)
if (_normal_binding==osg::Geometry::BIND_PER_VERTEX || _normal_binding==osg::Geometry::BIND_PER_PRIMITIVE) APPEND_DynGeoSet_List(_normalList)
if (_color_binding==osg::Geometry::BIND_PER_VERTEX || _color_binding==osg::Geometry::BIND_PER_PRIMITIVE) APPEND_DynGeoSet_List(_colorList)
for (unsigned int i = 0; i < source->_tcoordLists.size(); i++)
{
if ((getTextureBinding(i)==osg::Geometry::BIND_PER_VERTEX) ||
(getTextureBinding(i)==osg::Geometry::BIND_PER_PRIMITIVE))
{
if (source->_tcoordLists.size() > 0)
{
if (_tcoordLists.size() <= i)
_tcoordLists.resize(i+1);
_tcoordLists[i].insert(_tcoordLists[i].end(),
source->_tcoordLists[i].begin(),
source->_tcoordLists[i].end());
}
}
}
}
#define VERIFY_DynGeoSet_Binding(binding,list) \
switch (binding) \
{ \
case osg::Geometry::BIND_PER_VERTEX: \
if (list.size() < _coordList.size()) { \
binding = osg::Geometry::BIND_OFF; \
list.clear(); } \
break; \
case osg::Geometry::BIND_PER_PRIMITIVE: \
if (list.size() < _primLenList.size()) { \
binding = osg::Geometry::BIND_OFF; \
list.clear(); } \
break; \
case osg::Geometry::BIND_OVERALL: \
if (list.size() < 1) { \
binding = osg::Geometry::BIND_OFF; \
list.clear(); } \
break; \
default: \
break; \
}
const osg::PrimitiveSet::Mode NO_PRIMITIVE_TYPE = (osg::PrimitiveSet::Mode)0xffff;
DynGeoSet::DynGeoSet()
{
_primtype = NO_PRIMITIVE_TYPE;
_normal_binding = osg::Geometry::BIND_OFF;
_color_binding = osg::Geometry::BIND_OFF;
_detailTextureEnabled = false;
_geom = new osg::Geometry;
}
void DynGeoSet::setBinding()
{
unsigned int i;
VERIFY_DynGeoSet_Binding(_normal_binding, _normalList)
VERIFY_DynGeoSet_Binding(_color_binding, _colorList)
for (i = 0; i < _tcoordLists.size(); i++)
VERIFY_DynGeoSet_Binding(_texture_bindings[i], _tcoordLists[i])
// Set bindings
setNormalBinding(_normal_binding);
setColorBinding(_color_binding);
for (i = 0; i < _tcoordLists.size(); i++)
setTextureBinding(i, _texture_bindings[i]);
osg::StateSet* stateset = getStateSet();
if (stateset)
{
if (_normal_binding == osg::Geometry::BIND_OFF)
stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
}
}
void DynGeoSet::addToGeometry(osg::Geometry* geom)
{
int indexBase = 0;
osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geom->getVertexArray());
if (vertices)
{
indexBase = vertices->size();
vertices->insert(vertices->end(),_coordList.begin(),_coordList.end());
}
else
{
vertices = new osg::Vec3Array(_coordList.begin(),_coordList.end());
geom->setVertexArray(vertices);
}
if (!_normalList.empty())
{
osg::Vec3Array* normals = dynamic_cast<osg::Vec3Array*>(geom->getNormalArray());
if (normals)
{
if (_normal_binding==osg::Geometry::BIND_PER_VERTEX || _normal_binding==osg::Geometry::BIND_PER_PRIMITIVE)
normals->insert(normals->end(),_normalList.begin(),_normalList.end());
}
else
{
normals = new osg::Vec3Array(_normalList.begin(),_normalList.end());
geom->setNormalArray(normals);
switch(_normal_binding)
{
case(osg::Geometry::BIND_OVERALL):geom->setNormalBinding(osg::Geometry::BIND_OVERALL);break;
case(osg::Geometry::BIND_PER_VERTEX):geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);break;
case(osg::Geometry::BIND_PER_PRIMITIVE):geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);break;
default:geom->setNormalBinding(osg::Geometry::BIND_OFF); break;
}
}
}
for (unsigned int i = 0; i < _tcoordLists.size(); i++)
{
if (!_tcoordLists[i].empty())
{
// Grab the current layer's texture coordinate array from the
// geometry
osg::Vec2Array* texcoords =
dynamic_cast<osg::Vec2Array*>(geom->getTexCoordArray(i));
// See if we need to create the texture coordinate array or add to
// it
if (texcoords)
{
// Append the new texture coordinates to the end of the existing
// list
texcoords->insert(texcoords->end(),_tcoordLists[i].begin(),
_tcoordLists[i].end());
}
else
{
// Create a new texture coordinate array
texcoords = new osg::Vec2Array(_tcoordLists[i].begin(),
_tcoordLists[i].end());
geom->setTexCoordArray(i,texcoords);
}
}
}
// If this geometry uses a detail texture, we apply the detail texture
// scalars to the texture coordinates on layer 0 to get the detail texture
// coordinates, which we put on layer 1. Note that this assumes that
// layer 1 is not in use for multitexturing. This means that
// multitexturing and detail texturing are not supported at the same time.
if ((_detailTextureEnabled) && (!_tcoordLists.empty()) &&
(!_tcoordLists[0].empty()))
{
// Create a new texture coordinate array for the detail texture
// coordinates
osg::Vec2Array *texcoords2 = new osg::Vec2Array(_tcoordLists[0].begin(),
_tcoordLists[0].end());
// Scale the texture coordinates from layer 0
for(unsigned int index=0;index<texcoords2->size();index++)
{
(*texcoords2)[index][0] *= _detailTexCoord_m;
(*texcoords2)[index][1] *= _detailTexCoord_n;
}
// Set the new texcoord array on layer 1 (this wipes out any existing
// texture coordinates there)
geom->setTexCoordArray(1,texcoords2);
}
if (!_colorList.empty())
{
osg::Vec4Array* colors = dynamic_cast<osg::Vec4Array*>(geom->getColorArray());
if (colors)
{
if (_color_binding==osg::Geometry::BIND_PER_VERTEX || _color_binding==osg::Geometry::BIND_PER_PRIMITIVE)
colors->insert(colors->end(),_colorList.begin(),_colorList.end());
}
else
{
colors = new osg::Vec4Array(_colorList.begin(),_colorList.end());
geom->setColorArray(colors);
switch(_color_binding)
{
case(osg::Geometry::BIND_OVERALL):geom->setColorBinding(osg::Geometry::BIND_OVERALL);break;
case(osg::Geometry::BIND_PER_VERTEX):geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);break;
case(osg::Geometry::BIND_PER_PRIMITIVE):geom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE);break;
default:geom->setColorBinding(osg::Geometry::BIND_OFF); break;
}
}
}
if (_primtype!=osg::PrimitiveSet::POLYGON)
{
geom->addPrimitiveSet(new osg::DrawArrays(_primtype,indexBase,_coordList.size()));
}
else
{
for(PrimLenList::iterator itr=_primLenList.begin();
itr!=_primLenList.end();
++itr)
{
geom->addPrimitiveSet(new osg::DrawArrays(_primtype,indexBase,*itr));
indexBase += *itr;
}
}
}
////////////////////////////////////////////////////////////////////
//
// GeoSetBuilder
//
////////////////////////////////////////////////////////////////////
// OpenFlight don't save data in GeoSets. This class tries to find
// existing GeoSets with matching state before creating a new GeoSet.
GeoSetBuilder::GeoSetBuilder(osg::Geode* geode)
{
_geode = geode;
initPrimData();
}
void GeoSetBuilder::initPrimData()
{
_dynGeoSet = new DynGeoSet;
_dynGeoSet->setStateSet(new osg::StateSet);
}
struct SortDynGeoSet
{
bool operator () (const osg::ref_ptr<DynGeoSet>& lhs,const osg::ref_ptr<DynGeoSet>& rhs)
{
return *lhs<*rhs;
}
};
osg::Geode* GeoSetBuilder::createOsgGeoSets(osg::Geode* geode)
{
if( geode == NULL) geode = _geode.get();
if( geode == NULL) return geode;
for(DynGeoSetList::iterator itr = _dynGeoSetList.begin();
itr!=_dynGeoSetList.end();
++itr)
{
DynGeoSet* dgset = itr->get();
osg::Geometry* geom = dgset->getGeometry();
geode->addDrawable(geom);
dgset->addToGeometry(geom);
osg::StateSet* stateset = dgset->getStateSet();
assert( stateset == geom->getStateSet() );
}
osgUtil::Optimizer optimizer;
optimizer.optimize(geode, osgUtil::Optimizer::SHARE_DUPLICATE_STATE |
osgUtil::Optimizer::MERGE_GEOMETRY |
osgUtil::Optimizer::CHECK_GEOMETRY |
osgUtil::Optimizer::TESSELATE_GEOMETRY);
return geode;
}
bool GeoSetBuilder::addPrimitive(bool dontMerge)
{
DynGeoSet* dgset = getDynGeoSet(); // This is the new geoset we want to add
if (dgset->getPrimType()==NO_PRIMITIVE_TYPE)
{
dgset->setPrimType(findPrimType(dgset->coordListSize()));
}
// Still no primitive type?
if (dgset->getPrimType()==NO_PRIMITIVE_TYPE)
return false;
dgset->setBinding();
dontMerge = true;
if( dontMerge == true)
{
_dynGeoSetList.push_back(dgset);
}
else
{
DynGeoSet* match = findMatchingGeoSet();
if (match)
match->append(dgset);
else
_dynGeoSetList.push_back(dgset);
}
initPrimData(); // initialize _dynGeoSet
return true;
}
DynGeoSet* GeoSetBuilder::findMatchingGeoSet()
{
DynGeoSet* new_dgset = getDynGeoSet();
for(DynGeoSetList::iterator itr=_dynGeoSetList.begin();
itr!=_dynGeoSetList.end();
++itr)
{
DynGeoSet* dgset = itr->get();
if (*new_dgset == *dgset)
return dgset;
}
return NULL;
}
osg::PrimitiveSet::Mode GeoSetBuilder::findPrimType(const int nVertices)
{
switch (nVertices)
{
case 1: return osg::PrimitiveSet::POINTS;
case 2: return osg::PrimitiveSet::LINES;
case 3: return osg::PrimitiveSet::TRIANGLES;
case 4: return osg::PrimitiveSet::QUADS;
}
if (nVertices>=5) return osg::PrimitiveSet::POLYGON;
return NO_PRIMITIVE_TYPE;
}