Added osg::Geometry::verifyArray(std::ostream&) method to enable checks to be done on osg::Geometry to see if the arrays

are big enough to handle the primitives used in the geometry.

Added usage of verifyArray into osgfilecache so that it reports any problems on reading files in paged database.
This commit is contained in:
Robert Osfield 2011-01-03 19:41:06 +00:00
parent 25ef1a9e58
commit d644a6dfe3
3 changed files with 302 additions and 6 deletions

View File

@ -130,6 +130,40 @@ struct Extents
osg::Vec2d _max; osg::Vec2d _max;
}; };
class CheckValidVisitor : public osg::NodeVisitor
{
public:
CheckValidVisitor():
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_numInvalidGeometries(0) {}
void apply(osg::Geode& geode)
{
unsigned int local_numInvalidGeometries = 0;
for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
{
osg::Geometry* geometry = dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
if (geometry)
{
if (!geometry->verifyArrays(_errorReports)) ++local_numInvalidGeometries;
}
}
if (local_numInvalidGeometries)
{
_errorReports<<"Geode "<<geode.getName()<<" contains problem geometries"<<std::endl;
_numInvalidGeometries += local_numInvalidGeometries;
}
}
bool valid() const { return _numInvalidGeometries==0; }
unsigned int _numInvalidGeometries;
std::stringstream _errorReports;
};
class LoadDataVisitor : public osg::NodeVisitor class LoadDataVisitor : public osg::NodeVisitor
{ {
public: public:
@ -274,34 +308,46 @@ public:
osg::Node* readNodeFileAndWriteToCache(const std::string& filename) osg::Node* readNodeFileAndWriteToCache(const std::string& filename)
{ {
osg::Node* node = 0;
if (_fileCache.valid() && osgDB::containsServerAddress(filename)) if (_fileCache.valid() && osgDB::containsServerAddress(filename))
{ {
if (_fileCache->existsInCache(filename)) if (_fileCache->existsInCache(filename))
{ {
osg::notify(osg::NOTICE)<<"reading from FileCache: "<<filename<<std::endl; osg::notify(osg::NOTICE)<<"reading from FileCache: "<<filename<<std::endl;
return _fileCache->readNode(filename, osgDB::Registry::instance()->getOptions()).takeNode(); node = _fileCache->readNode(filename, osgDB::Registry::instance()->getOptions()).takeNode();
} }
else else
{ {
osg::notify(osg::NOTICE)<<"reading : "<<filename<<std::endl; osg::notify(osg::NOTICE)<<"reading : "<<filename<<std::endl;
osg::Node* node = osgDB::readNodeFile(filename); node = osgDB::readNodeFile(filename);
if (node) if (node)
{ {
osg::notify(osg::NOTICE)<<"write to FileCache : "<<filename<<std::endl; osg::notify(osg::NOTICE)<<"write to FileCache : "<<filename<<std::endl;
_fileCache->writeNode(*node, filename, osgDB::Registry::instance()->getOptions()); _fileCache->writeNode(*node, filename, osgDB::Registry::instance()->getOptions());
} }
return node;
} }
} }
else else
{ {
osg::notify(osg::NOTICE)<<"reading : "<<filename<<std::endl; osg::notify(osg::NOTICE)<<"reading : "<<filename<<std::endl;
return osgDB::readNodeFile(filename); node = osgDB::readNodeFile(filename);
}
if (node)
{
CheckValidVisitor cvv;
node->accept(cvv);
if (!cvv.valid())
{
OSG_NOTICE<<"Warning, errors in geometry found in file "<<filename<<std::endl;
OSG_NOTICE<<cvv._errorReports.str()<<std::endl;
} }
} }
return node;
}
protected: protected:

View File

@ -327,6 +327,7 @@ class OSG_EXPORT Geometry : public Drawable
/** Get whether fast paths should be used when supported. */ /** Get whether fast paths should be used when supported. */
bool getFastPathHint() const { return _fastPathHint; } bool getFastPathHint() const { return _fastPathHint; }
/** Return true if OpenGL fast paths will be used with drawing this Geometry. /** Return true if OpenGL fast paths will be used with drawing this Geometry.
* Fast paths directly use vertex arrays, and glDrawArrays/glDrawElements so have low CPU overhead. * Fast paths directly use vertex arrays, and glDrawArrays/glDrawElements so have low CPU overhead.
* With Slow paths the osg::Geometry::drawImplementation has to dynamically assemble OpenGL * With Slow paths the osg::Geometry::drawImplementation has to dynamically assemble OpenGL
@ -348,6 +349,8 @@ class OSG_EXPORT Geometry : public Drawable
void computeCorrectBindingsAndArraySizes(); void computeCorrectBindingsAndArraySizes();
/** check whether the arrays, indices, bindings and primitives all match correctly, return false is .*/
bool verifyArrays(std::ostream& out) const;
bool suitableForOptimization() const; bool suitableForOptimization() const;

View File

@ -997,6 +997,14 @@ void Geometry::compileGLObjects(RenderInfo& renderInfo) const
void Geometry::drawImplementation(RenderInfo& renderInfo) const void Geometry::drawImplementation(RenderInfo& renderInfo) const
{ {
#if 0
if (!validGeometry())
{
OSG_NOTICE<<"Error, osg::Geometry has invalid array/primitive set usage"<<std::endl;
return;
}
#endif
if (_internalOptimizedGeometry.valid()) if (_internalOptimizedGeometry.valid())
{ {
_internalOptimizedGeometry->drawImplementation(renderInfo); _internalOptimizedGeometry->drawImplementation(renderInfo);
@ -2483,6 +2491,245 @@ void Geometry::computeInternalOptimizedGeometry()
} }
} }
class CheckArrayValidity
{
public:
CheckArrayValidity(const osg::Geometry* geometry)
{
numPrimitiveSets = geometry->getNumPrimitiveSets();
primitiveNum = 0;
maxVertexNumber = 0;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// draw the primitives themselves.
//
for(unsigned int primitiveSetNum=0; primitiveSetNum != numPrimitiveSets; ++primitiveSetNum)
{
const PrimitiveSet* primitiveset = geometry->getPrimitiveSet(primitiveSetNum);
GLenum mode=primitiveset->getMode();
unsigned int primLength;
switch(mode)
{
case(GL_POINTS): primLength=1; break;
case(GL_LINES): primLength=2; break;
case(GL_TRIANGLES): primLength=3; break;
case(GL_QUADS): primLength=4; break;
default: primLength=0; break; // compute later when =0.
}
// draw primitives by the more flexible "slow" path,
// sending OpenGL glBegin/glVertex.../glEnd().
switch(primitiveset->getType())
{
case(PrimitiveSet::DrawArraysPrimitiveType):
{
if (primLength==0) primLength=primitiveset->getNumIndices();
const DrawArrays* drawArray = static_cast<const DrawArrays*>(primitiveset);
unsigned int primCount=0;
unsigned int indexEnd = drawArray->getFirst()+drawArray->getCount();
for(unsigned int vindex=drawArray->getFirst();
vindex<indexEnd;
++vindex,++primCount)
{
if ((primCount%primLength)==0)
{
primitiveNum++;
}
}
if ((indexEnd-1) > maxVertexNumber) maxVertexNumber = (indexEnd-1);
break;
}
case(PrimitiveSet::DrawArrayLengthsPrimitiveType):
{
const DrawArrayLengths* drawArrayLengths = static_cast<const DrawArrayLengths*>(primitiveset);
unsigned int vindex=drawArrayLengths->getFirst();
for(DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin();
primItr!=drawArrayLengths->end();
++primItr)
{
unsigned int localPrimLength;
if (primLength==0) localPrimLength=*primItr;
else localPrimLength=primLength;
for(GLsizei primCount=0;
primCount<*primItr;
++vindex,++primCount)
{
if ((primCount%localPrimLength)==0)
{
primitiveNum++;
}
}
}
if ((vindex-1) > maxVertexNumber) maxVertexNumber = (vindex-1);
break;
}
case(PrimitiveSet::DrawElementsUBytePrimitiveType):
{
if (primLength==0) primLength=primitiveset->getNumIndices();
const DrawElementsUByte* drawElements = static_cast<const DrawElementsUByte*>(primitiveset);
unsigned int primCount=0;
for(DrawElementsUByte::const_iterator primItr=drawElements->begin();
primItr!=drawElements->end();
++primCount,++primItr)
{
if ((primCount%primLength)==0)
{
primitiveNum++;
}
unsigned int vindex = *primItr;
if (vindex > maxVertexNumber) maxVertexNumber = vindex;
}
break;
}
case(PrimitiveSet::DrawElementsUShortPrimitiveType):
{
if (primLength==0) primLength=primitiveset->getNumIndices();
const DrawElementsUShort* drawElements = static_cast<const DrawElementsUShort*>(primitiveset);
unsigned int primCount=0;
for(DrawElementsUShort::const_iterator primItr=drawElements->begin();
primItr!=drawElements->end();
++primCount,++primItr)
{
if ((primCount%primLength)==0)
{
primitiveNum++;
}
unsigned int vindex = *primItr;
if (vindex > maxVertexNumber) maxVertexNumber = vindex;
}
break;
}
case(PrimitiveSet::DrawElementsUIntPrimitiveType):
{
if (primLength==0) primLength=primitiveset->getNumIndices();
const DrawElementsUInt* drawElements = static_cast<const DrawElementsUInt*>(primitiveset);
unsigned int primCount=0;
for(DrawElementsUInt::const_iterator primItr=drawElements->begin();
primItr!=drawElements->end();
++primCount,++primItr)
{
if ((primCount%primLength)==0)
{
primitiveNum++;
}
unsigned int vindex = *primItr;
if (vindex > maxVertexNumber) maxVertexNumber = vindex;
}
break;
}
default:
{
break;
}
}
}
}
bool validArray(std::ostream& out, const osg::Geometry::ArrayData& arrayData, const char* arrayName)
{
unsigned int numRequired = 0;
switch(arrayData.binding)
{
case(osg::Geometry::BIND_OFF): numRequired = 0; break;
case(osg::Geometry::BIND_OVERALL): numRequired = 1; break;
case(osg::Geometry::BIND_PER_PRIMITIVE): numRequired = primitiveNum; break;
case(osg::Geometry::BIND_PER_PRIMITIVE_SET): numRequired = numPrimitiveSets; break;
case(osg::Geometry::BIND_PER_VERTEX): numRequired = maxVertexNumber+1; break;
}
if (arrayData.indices.valid())
{
unsigned int numIndices= arrayData.indices.valid() ? arrayData.indices->getNumElements() : 0;
if (numIndices<numRequired)
{
out<<"Not enough "<<arrayName<<" indices, numRequired="<<numRequired<<std::endl;
return false;
}
unsigned int numNormals = arrayData.array.valid() ? arrayData.array->getNumElements() : 0;
for(unsigned int i=0; i<numIndices; ++i)
{
if (arrayData.indices->index(i)>=numNormals)
{
out<<arrayName<<" index out of bounds of normal array"<<std::endl;
return false;
}
}
}
else
{
unsigned int numElements = arrayData.array.valid() ? arrayData.array->getNumElements() : 0;
if (numElements<numRequired)
{
out<<"Not enough "<<arrayName<<"s, numRequired="<<numRequired<<", but number in array="<<numElements<<std::endl;
return false;
}
}
return true;
}
unsigned int numPrimitiveSets;
unsigned int primitiveNum;
unsigned int maxVertexNumber;
};
bool Geometry::verifyArrays(std::ostream& out) const
{
CheckArrayValidity cav(this);
bool result = true;
// check _vertexData
if (_vertexData.indices.valid())
{
unsigned int numIndices= _vertexData.indices.valid() ? _vertexData.indices->getNumElements() : 0;
if (numIndices<=cav.maxVertexNumber)
{
out<<"Not enough vertex indices"<<std::endl;
result = false;
}
unsigned int numVertices = _vertexData.array.valid() ? _vertexData.array->getNumElements() : 0;
for(unsigned int i=0; i<numIndices; ++i)
{
if (_vertexData.indices->index(i)>=numVertices)
{
out<<"Vertex indice out of bounds of vertex array"<<std::endl;
result = false;
}
}
}
else
{
unsigned int numVertices = _vertexData.array.valid() ? _vertexData.array->getNumElements() : 0;
if (numVertices<cav.maxVertexNumber)
{
out<<"Not enough vertices"<<std::endl;
result = false;
}
}
// check _normalData
if (!cav.validArray(out, _normalData, "Normal")) result = false;
if (!cav.validArray(out, _colorData, "Color")) result = false;
if (!cav.validArray(out, _secondaryColorData, "SecondaryColor")) result = false;
return result;
}
Geometry* osg::createTexturedQuadGeometry(const Vec3& corner,const Vec3& widthVec,const Vec3& heightVec, float l, float b, float r, float t) Geometry* osg::createTexturedQuadGeometry(const Vec3& corner,const Vec3& widthVec,const Vec3& heightVec, float l, float b, float r, float t)
{ {