From Cedric Pinson, gles and osgjs plugins that support conversion of OSG loaded models into a form that can be used with osgjs JavaScript library

git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14770 16af8721-9629-0410-8352-f15c8da7e697
This commit is contained in:
Robert Osfield
2015-03-09 11:27:26 +00:00
parent 9e2c23c9d3
commit 2a84719f4a
43 changed files with 7988 additions and 0 deletions

View File

@@ -186,6 +186,9 @@ ADD_SUBDIRECTORY(txf)
ADD_SUBDIRECTORY(bsp)
ADD_SUBDIRECTORY(mdl)
ADD_SUBDIRECTORY(gles)
ADD_SUBDIRECTORY(osgjs)
IF(OSG_CPP_EXCEPTIONS_AVAILABLE)
ADD_SUBDIRECTORY(lwo)
ADD_SUBDIRECTORY(ply)

View File

@@ -0,0 +1,31 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef ANIMATION_VISITOR
#define ANIMATION_VISITOR
#include <osgUtil/UpdateVisitor>
#include <osgAnimation/UpdateMatrixTransform>
#include <osgAnimation/AnimationManagerBase>
#include <osgAnimation/BasicAnimationManager>
// the idea is to create true Geometry if skeleton with RigGeometry
class AnimationVisitor : public osgUtil::UpdateVisitor
{
public:
AnimationVisitor() {
setFrameStamp(new osg::FrameStamp());
}
};
#endif

View File

@@ -0,0 +1,242 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef BIND_PER_VERTEX_VISITOR
#define BIND_PER_VERTEX_VISITOR
#include "GeometryUniqueVisitor"
// TODO: deprecated
class BindPerVertexVisitor : public GeometryUniqueVisitor {
public:
BindPerVertexVisitor(): GeometryUniqueVisitor("BindPerVertexVisitor")
{}
void apply(osg::Geometry& geometry) {
if (geometry.getNormalArray() && geometry.getNormalBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getNormalArray(),
geometry.getNormalBinding(),
geometry.getPrimitiveSetList());
geometry.setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
}
if (geometry.getColorArray() && geometry.getColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getColorArray(),
geometry.getColorBinding(),
geometry.getPrimitiveSetList());
geometry.setColorBinding(osg::Geometry::BIND_PER_VERTEX);
}
if (geometry.getSecondaryColorArray() && geometry.getSecondaryColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getSecondaryColorArray(),
geometry.getSecondaryColorBinding(),
geometry.getPrimitiveSetList());
geometry.setSecondaryColorBinding(osg::Geometry::BIND_PER_VERTEX);
}
if (geometry.getFogCoordArray() && geometry.getFogCoordBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getFogCoordArray(),
geometry.getFogCoordBinding(),
geometry.getPrimitiveSetList());
geometry.setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
}
setProcessed(&geometry);
};
protected:
void bindPerVertex(osg::Array* src,
osg::Geometry::AttributeBinding fromBinding,
osg::Geometry::PrimitiveSetList& primitives) {
if (doConvert<osg::Vec3Array>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2Array>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4Array>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4ubArray>(src, fromBinding, primitives))
return;
}
template <class T>
bool doConvert(osg::Array* src,
osg::Geometry::AttributeBinding fromBinding,
osg::Geometry::PrimitiveSetList& primitives) {
T* array= dynamic_cast<T*>(src);
if (array) {
convert(*array, fromBinding, primitives);
return true;
}
return false;
}
template <class T>
void convert(T& array,
osg::Geometry::AttributeBinding fromBinding,
osg::Geometry::PrimitiveSetList& primitives)
{
osg::ref_ptr<T> result = new T();
for (unsigned int p = 0; p < primitives.size(); p++) {
switch ( primitives[p]->getMode() ) {
case osg::PrimitiveSet::POINTS:
osg::notify(osg::WARN) << "ConvertToBindPerVertex not supported for POINTS" << std::endl;
break;
case osg::PrimitiveSet::LINE_STRIP:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
unsigned int nb = primitives[p]->getNumIndices();
for (unsigned int i = 0; i < nb; i++)
result->push_back(array[p]);
}
break;
}
break;
case osg::PrimitiveSet::LINES:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
unsigned int nb = primitives[p]->getNumIndices();
for (unsigned int i = 0; i < nb; i++)
result->push_back(array[p]);
}
break;
}
break;
case osg::PrimitiveSet::TRIANGLES:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
unsigned int nb = primitives[p]->getNumIndices();
for (unsigned int i = 0; i < nb; i++)
result->push_back(array[p]);
}
break;
}
break;
case osg::PrimitiveSet::TRIANGLE_STRIP:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_STRIP" << std::endl;
}
break;
}
break;
case osg::PrimitiveSet::TRIANGLE_FAN:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_FAN" << std::endl;
}
break;
}
break;
case osg::PrimitiveSet::QUADS:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUADS" << std::endl;
}
break;
}
break;
case osg::PrimitiveSet::QUAD_STRIP:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUAD_STRIP" << std::endl;
}
break;
}
break;
}
}
array = *result;
}
};
#endif

View File

@@ -0,0 +1,12 @@
SET(TARGET_SRC
ReaderWriterGLES.cpp
OpenGLESGeometryOptimizer.cpp
TriangleStripVisitor.cpp
IndexMeshVisitor.cpp
UnIndexMeshVisitor.cpp
forsythtriangleorderoptimizer.cpp)
#### end var setup ###
SET(TARGET_ADDED_LIBRARIES
osgUtil)
SETUP_PLUGIN(gles)

View File

@@ -0,0 +1,97 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef DETACH_PRIMITIVE_VISITOR
#define DETACH_PRIMITIVE_VISITOR
#include <osg/ValueObject>
#include "GeometryUniqueVisitor"
class DetachPrimitiveVisitor : public GeometryUniqueVisitor {
public:
DetachPrimitiveVisitor(std::string const& userValue, bool keepGeometryAttributes=false, bool inlined=true):
GeometryUniqueVisitor("DetachPrimitiveVisitor"),
_userValue(userValue), _keepGeometryAttributes(keepGeometryAttributes), _inlined(inlined)
{}
void apply(osg::Geometry& geometry) {
if(shouldDetach(geometry)) {
osg::Geometry* detached = createDetachedGeometry(geometry);
unsigned int nbParents = geometry.getNumParents();
for(unsigned int i = 0 ; i < nbParents ; ++ i) {
osg::Node* parent = geometry.getParent(i);
// TODO: Geode will be soon deprecated
if(parent && parent->asGeode()) {
osg::Geode* geode = parent->asGeode();
geode->addDrawable(detached);
if(!_inlined) {
geode->removeDrawable(&geometry);
}
}
}
setProcessed(detached);
}
setProcessed(&geometry);
}
protected:
bool shouldDetach(osg::Geometry& geometry) {
bool detach = false;
for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) {
osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
if(primitive && primitive->getUserValue(_userValue, detach) && detach) {
return true;
}
}
return false;
}
osg::Geometry* createDetachedGeometry(osg::Geometry& source) {
osg::Geometry* detached = new osg::Geometry(source, osg::CopyOp::SHALLOW_COPY);
if(!_keepGeometryAttributes) {
// we keep only vertexes and clean all other attributes and values
detached->setNormalArray(0);
detached->setColorArray(0);
detached->setSecondaryColorArray(0);
detached->setFogCoordArray(0);
for (unsigned int i = 0 ; i < source.getNumTexCoordArrays(); ++ i) {
detached->setTexCoordArray(i, 0);
}
detached->getVertexAttribArrayList().clear();
detached->setStateSet(0);
detached->setUserDataContainer(0);
}
// filter primitivesets
osg::Geometry::PrimitiveSetList detachedPrimitives;
for(int i = source.getNumPrimitiveSets() - 1 ; i >= 0 ; -- i) {
osg::PrimitiveSet* primitive = source.getPrimitiveSet(i);
bool isTrue = false;
if(primitive && primitive->getUserValue(_userValue, isTrue) && isTrue) {
detachedPrimitives.push_back(primitive);
source.removePrimitiveSet(i);
}
}
detached->setPrimitiveSetList(detachedPrimitives);
detached->setUserValue(_userValue, true);
return detached;
}
std::string _userValue;
bool _keepGeometryAttributes;
bool _inlined;
};
#endif

View File

@@ -0,0 +1,88 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef DRAW_ARRAY_VISITOR
#define DRAW_ARRAY_VISITOR
#include "GeometryUniqueVisitor"
#include "GeometryArray"
class DrawArrayVisitor : public GeometryUniqueVisitor {
public:
DrawArrayVisitor(): GeometryUniqueVisitor("DrawArrayVisitor")
{}
void apply(osg::Geometry& geometry) {
GeometryArrayList srcArrays(geometry);
// clone but clear content
osg::Geometry* newGeometry = new osg::Geometry;
GeometryArrayList dst = srcArrays.cloneType();
for (unsigned int i = 0; i < geometry.getNumPrimitiveSets(); i++) {
osg::PrimitiveSet* ps = geometry.getPrimitiveSet(i);
switch (ps->getType()) {
case osg::PrimitiveSet::DrawArraysPrimitiveType:
{
osg::DrawArrays* dw = dynamic_cast<osg::DrawArrays*>(ps);
unsigned int start = dst.size();
osg::DrawArrays* ndw = new osg::DrawArrays(dw->getMode(), start, dw->getNumIndices());
newGeometry->getPrimitiveSetList().push_back(ndw);
for ( unsigned int j = 0; j < dw->getNumIndices(); j++) {
srcArrays.append(dw->getFirst()+j, dst);
}
}
break;
case osg::PrimitiveSet::DrawElementsUBytePrimitiveType:
case osg::PrimitiveSet::DrawElementsUShortPrimitiveType:
case osg::PrimitiveSet::DrawElementsUIntPrimitiveType:
{
osg::DrawElements* de = ps->getDrawElements();
unsigned int start = dst.size();
osg::DrawArrays* ndw = new osg::DrawArrays(de->getMode(), start, de->getNumIndices());
newGeometry->getPrimitiveSetList().push_back(ndw);
for (unsigned int j = 0; j < de->getNumIndices(); j++) {
unsigned int idx = de->getElement(j);
srcArrays.append(idx, dst);
}
}
break;
case osg::PrimitiveSet::DrawArrayLengthsPrimitiveType:
{
osg::DrawArrayLengths* dal = dynamic_cast<osg::DrawArrayLengths*>(ps);
unsigned int start = dst.size();
unsigned int offset = dal->getFirst();
unsigned int totalDrawArraysVertexes = 0;
for (unsigned int j = 0; j < dal->size(); j++) {
totalDrawArraysVertexes += (*dal)[j];
}
osg::DrawArrays* ndw = new osg::DrawArrays(dal->getMode(), start, totalDrawArraysVertexes);
newGeometry->getPrimitiveSetList().push_back(ndw);
for (unsigned int v = 0; v < totalDrawArraysVertexes; v++) {
srcArrays.append(offset + v, dst);
}
}
break;
default:
break;
}
}
dst.setToGeometry(geometry);
geometry.setPrimitiveSetList(newGeometry->getPrimitiveSetList());
setProcessed(&geometry);
}
};
#endif

View File

@@ -0,0 +1,277 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef EDGE_INDEX_FUNCTOR_H
#define EDGE_INDEX_FUNCTOR_H
#include <osg/PrimitiveSet>
#include <osg/Array>
template<class T>
class EdgeIndexFunctor : public osg::PrimitiveIndexFunctor, public T
{
public:
virtual void setVertexArray(unsigned int,const osg::Vec2*)
{}
virtual void setVertexArray(unsigned int ,const osg::Vec3* )
{}
virtual void setVertexArray(unsigned int,const osg::Vec4* )
{}
virtual void setVertexArray(unsigned int,const osg::Vec2d*)
{}
virtual void setVertexArray(unsigned int ,const osg::Vec3d* )
{}
virtual void setVertexArray(unsigned int,const osg::Vec4d* )
{}
virtual void begin(GLenum mode) {
_modeCache = mode;
_indexCache.clear();
}
virtual void vertex(unsigned int vert) {
_indexCache.push_back(vert);
}
virtual void end() {
if (!_indexCache.empty()) {
drawElements(_modeCache, _indexCache.size(), &_indexCache.front());
}
}
virtual void drawArrays(GLenum mode, GLint first, GLsizei count) {
switch(mode)
{
case(GL_TRIANGLES):
{
unsigned int pos=first;
for(GLsizei i = 2 ; i < count ; i += 3, pos += 3)
{
this->operator()(pos, pos+1);
this->operator()(pos+1, pos+2);
this->operator()(pos+2, pos);
}
break;
}
case(GL_TRIANGLE_STRIP):
{
unsigned int pos=first;
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos)
{
if ((i % 2)) {
this->operator()(pos, pos+2);
this->operator()(pos+2, pos+1);
this->operator()(pos+1, pos);
} else {
this->operator()(pos, pos+1);
this->operator()(pos+1, pos+2);
this->operator()(pos, pos+2);
}
}
break;
}
case(GL_QUADS):
{
unsigned int pos=first;
for(GLsizei i = 3 ; i < count ; i += 4, pos += 4)
{
this->operator()(pos, pos+1);
this->operator()(pos+1, pos+2);
this->operator()(pos+2, pos+3);
this->operator()(pos+3, pos);
}
break;
}
case(GL_QUAD_STRIP):
{
unsigned int pos=first;
for(GLsizei i = 3 ; i < count ; i += 2, pos += 2)
{
this->operator()(pos, pos+1);
this->operator()(pos+1, pos+3);
this->operator()(pos+2, pos+3);
this->operator()(pos+2, pos);
}
break;
}
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
case(GL_TRIANGLE_FAN):
{
unsigned int pos=first+1;
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) {
this->operator()(pos, pos+1);
}
break;
}
case(GL_LINES):
{
unsigned int pos=first;
for(GLsizei i = 0 ; i < count ; i += 2, pos += 2) {
this->operator()(pos, pos+1);
}
}
break;
case(GL_LINE_STRIP):
{
unsigned int pos=first;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
this->operator()(pos, pos+1);
}
}
break;
case(GL_LINE_LOOP):
{
unsigned int pos=first;
for(GLsizei i = 0 ; i < count - 1; i += 1, pos += 1) {
this->operator()(pos, pos+1);
}
this->operator()(pos, first);
}
break;
case(GL_POINTS):
break;
default:
// can't be converted into to edges.
break;
}
}
template<typename I>
void drawElements(GLenum mode, GLsizei count, const I* indices) {
if (indices == 0 || count == 0) return;
switch(mode)
{
case(GL_TRIANGLES):
{
const I* ilast = &indices[count];
for(const I* iptr = indices ; iptr < ilast ; iptr += 3) {
this->operator()(*iptr, *(iptr+1));
this->operator()(*(iptr+1), *(iptr+2));
this->operator()(*iptr, *(iptr+2));
}
break;
}
case(GL_TRIANGLE_STRIP):
{
const I* iptr = indices;
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
I v0 = *(iptr),
v1 = *(iptr + 1),
v2 = *(iptr + 2);
// when merging strips we create degenerate triangles and add
// non existing edges that should be filtered
if(v0 == v1 || v0 == v2 || v1 == v2) continue;
if ((i % 2)) {
this->operator()(v0, v2);
this->operator()(v2, v1);
this->operator()(v0, v1);
} else {
this->operator()(v0, v1);
this->operator()(v1, v2);
this->operator()(v0, v2);
}
}
break;
}
case(GL_QUADS):
{
const I* iptr = indices;
for(GLsizei i = 3 ; i < count ; i += 4, iptr += 4) {
this->operator()(*(iptr), *(iptr+1));
this->operator()(*(iptr+1), *(iptr+2));
this->operator()(*(iptr+2), *(iptr+3) );
this->operator()(*(iptr), *(iptr+3) );
}
break;
}
case(GL_QUAD_STRIP):
{
const I* iptr = indices;
for(GLsizei i = 3 ; i < count ; i += 2, iptr += 2) {
this->operator()(*(iptr), *(iptr+1));
this->operator()(*(iptr+3), *(iptr+1));
this->operator()(*(iptr+2), *(iptr+3));
this->operator()(*(iptr), *(iptr+2));
}
break;
}
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
case(GL_TRIANGLE_FAN):
{
const I* iptr = indices;
++iptr;
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
this->operator()(*(iptr), *(iptr+1));
}
break;
}
case(GL_LINES):
{
const I* iptr = indices;
for(GLsizei i = 0 ; i < count - 1 ; i += 2, iptr += 2) {
this->operator()(*iptr, *(iptr+1));
}
}
break;
case(GL_LINE_STRIP):
{
const I* iptr = indices;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
this->operator()(*iptr, *(iptr+1));
}
}
break;
case(GL_LINE_LOOP):
{
const I* iptr = indices;
I first = *iptr;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
this->operator()(*iptr, *(iptr+1));
}
this->operator()(*iptr, first);
}
break;
case(GL_POINTS):
break;
default:
// can't be converted into to edge lines
break;
}
}
virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) {
drawElements<GLubyte>(mode, count, indices);
}
virtual void drawElements(GLenum mode, GLsizei count, const GLushort* indices) {
drawElements<GLushort>(mode, count, indices);
}
virtual void drawElements(GLenum mode, GLsizei count, const GLuint* indices) {
drawElements<GLuint>(mode, count, indices);
}
GLenum _modeCache;
std::vector<GLuint> _indexCache;
};
#endif

View File

@@ -0,0 +1,339 @@
/* -*-c++-*- */
#ifndef GEOMETRY_ARRAY_UTILS_H
#define GEOMETRY_ARRAY_UTILS_H
#include <osg/Array>
#include <osg/Geometry>
#include <osg/Notify>
#include <osg/Geometry>
typedef std::vector<unsigned int> IndexList;
struct GeometryArrayList {
class ArrayIndexAppendVisitor : public osg::ArrayVisitor
{
public:
ArrayIndexAppendVisitor(const IndexList& indexes, osg::Array* dst): _indexes(indexes), _dst(dst)
{
}
const IndexList& _indexes;
osg::Array* _dst;
template<class T>
inline void copy(T& array)
{
if (!_dst) {
osg::notify(osg::WARN) << "Can't append to array null" << std::endl;
return;
}
T* dstArray = dynamic_cast<T*>(_dst);
for(IndexList::const_iterator it = _indexes.begin(); it != _indexes.end(); ++it)
{
unsigned int idx = *it;
dstArray->push_back(array[idx]);
}
}
virtual void apply(osg::Array&) {}
virtual void apply(osg::ByteArray& array) { copy(array); }
virtual void apply(osg::ShortArray& array) { copy(array); }
virtual void apply(osg::IntArray& array) { copy(array); }
virtual void apply(osg::UByteArray& array) { copy(array); }
virtual void apply(osg::UShortArray& array) { copy(array); }
virtual void apply(osg::UIntArray& array) { copy(array); }
virtual void apply(osg::FloatArray& array) { copy(array); }
virtual void apply(osg::DoubleArray& array) { copy(array); }
virtual void apply(osg::Vec2Array& array) { copy(array); }
virtual void apply(osg::Vec3Array& array) { copy(array); }
virtual void apply(osg::Vec4Array& array) { copy(array); }
virtual void apply(osg::Vec4ubArray& array) { copy(array); }
virtual void apply(osg::Vec2bArray& array) { copy(array); }
virtual void apply(osg::Vec3bArray& array) { copy(array); }
virtual void apply(osg::Vec4bArray& array) { copy(array); }
virtual void apply(osg::Vec2sArray& array) { copy(array); }
virtual void apply(osg::Vec3sArray& array) { copy(array); }
virtual void apply(osg::Vec4sArray& array) { copy(array); }
virtual void apply(osg::Vec2dArray& array) { copy(array); }
virtual void apply(osg::Vec3dArray& array) { copy(array); }
virtual void apply(osg::Vec4dArray& array) { copy(array); }
virtual void apply(osg::MatrixfArray& array) { copy(array); }
protected:
ArrayIndexAppendVisitor& operator = (const ArrayIndexAppendVisitor&) { return *this; }
};
struct ArrayAppendElement {
template <class T> bool arrayAppendElement(osg::Array* src, unsigned int index, osg::Array* dst)
{
T* array= dynamic_cast<T*>(src);
if (array) {
T* arrayDst = dynamic_cast<T*>(dst);
arrayDst->push_back((*array)[index]);
return true;
}
return false;
}
void operator()(osg::Array* src, unsigned int index, osg::Array* dst) {
if (arrayAppendElement<osg::FloatArray>(src, index, dst))
return;
if (arrayAppendElement<osg::Vec2Array>(src, index, dst))
return;
if (arrayAppendElement<osg::Vec3Array>(src, index, dst))
return;
if (arrayAppendElement<osg::Vec4Array>(src, index, dst))
return;
if (arrayAppendElement<osg::Vec4ubArray>(src, index, dst))
return;
}
};
struct ArraySetNumElements {
template <class T> bool arraySetNumElements(osg::Array* src, unsigned int numElements)
{
T* array= dynamic_cast<T*>(src);
if (array) {
array->resize(numElements);
return true;
}
return false;
}
void operator()(osg::Array* array, unsigned int numElements) {
if (arraySetNumElements<osg::FloatArray>(array, numElements))
return;
if (arraySetNumElements<osg::Vec2Array>(array, numElements))
return;
if (arraySetNumElements<osg::Vec3Array>(array, numElements))
return;
if (arraySetNumElements<osg::Vec4Array>(array, numElements))
return;
if (arraySetNumElements<osg::Vec4ubArray>(array, numElements))
return;
}
};
osg::ref_ptr<osg::Array> _vertexes;
osg::ref_ptr<osg::Array> _normals;
osg::ref_ptr<osg::Array> _colors;
osg::ref_ptr<osg::Array> _secondaryColors;
osg::ref_ptr<osg::Array> _fogCoords;
std::vector<osg::ref_ptr<osg::Array> > _texCoordArrays;
std::vector<osg::ref_ptr<osg::Array> > _attributesArrays;
GeometryArrayList() {}
GeometryArrayList(osg::Geometry& geometry) {
_vertexes = geometry.getVertexArray();
unsigned int nbvertexes = _vertexes->getNumElements();
if (geometry.getNormalArray() && nbvertexes == geometry.getNormalArray()->getNumElements())
_normals = geometry.getNormalArray();
if (geometry.getColorArray() && nbvertexes == geometry.getColorArray()->getNumElements())
_colors = geometry.getColorArray();
if (geometry.getSecondaryColorArray() && nbvertexes == geometry.getSecondaryColorArray()->getNumElements())
_secondaryColors = geometry.getSecondaryColorArray();
if (geometry.getFogCoordArray() && nbvertexes == geometry.getFogCoordArray()->getNumElements())
_fogCoords = geometry.getFogCoordArray();
_texCoordArrays.resize(geometry.getNumTexCoordArrays());
for(unsigned int i=0;i<geometry.getNumTexCoordArrays();++i)
if (geometry.getTexCoordArray(i) && nbvertexes == geometry.getTexCoordArray(i)->getNumElements())
_texCoordArrays[i] = geometry.getTexCoordArray(i);
_attributesArrays.resize(geometry.getNumVertexAttribArrays());
for(unsigned int i=0;i<geometry.getNumVertexAttribArrays();++i)
if (geometry.getVertexAttribArrayList()[i] && nbvertexes == geometry.getVertexAttribArrayList()[i]->getNumElements())
_attributesArrays[i] = geometry.getVertexAttribArrayList()[i];
}
void setNumElements(unsigned int nbElements) {
if (_vertexes.valid())
ArraySetNumElements()(_vertexes.get(), nbElements);
if (_normals.valid())
ArraySetNumElements()(_normals.get(), nbElements);
if (_colors.valid())
ArraySetNumElements()(_colors.get(), nbElements);
if (_secondaryColors.valid())
ArraySetNumElements()(_secondaryColors.get(), nbElements);
if (_fogCoords.valid())
ArraySetNumElements()(_fogCoords.get(), nbElements);
for (unsigned int i = 0; i < _texCoordArrays.size(); i++)
if (_texCoordArrays[i].valid())
ArraySetNumElements()(_texCoordArrays[i].get(), nbElements);
for (unsigned int i = 0; i < _attributesArrays.size(); i++)
if (_attributesArrays[i].valid())
ArraySetNumElements()(_attributesArrays[i].get(), nbElements);
}
unsigned int append(unsigned int index, GeometryArrayList& dst) {
if (_vertexes.valid())
ArrayAppendElement()(_vertexes.get(), index, dst._vertexes.get());
if (_normals.valid())
ArrayAppendElement()(_normals.get(), index, dst._normals.get());
if (_colors.valid())
ArrayAppendElement()(_colors.get(), index, dst._colors.get());
if (_secondaryColors.valid())
ArrayAppendElement()(_secondaryColors.get(), index, dst._secondaryColors.get());
if (_fogCoords.valid())
ArrayAppendElement()(_fogCoords.get(), index, dst._fogCoords.get());
for (unsigned int i = 0; i < _texCoordArrays.size(); i++)
if (_texCoordArrays[i].valid())
ArrayAppendElement()(_texCoordArrays[i].get(), index, dst._texCoordArrays[i].get());
for (unsigned int i = 0; i < _attributesArrays.size(); i++)
if (_attributesArrays[i].valid())
ArrayAppendElement()(_attributesArrays[i].get(), index, dst._attributesArrays[i].get());
return dst._vertexes->getNumElements()-1;
}
unsigned int append(const IndexList& indexes, GeometryArrayList& dst) {
if (_vertexes.valid()) {
ArrayIndexAppendVisitor append(indexes, dst._vertexes.get());
_vertexes->accept(append);
}
if (_normals.valid()) {
ArrayIndexAppendVisitor append(indexes, dst._normals.get());
_normals->accept(append);
}
if (_colors.valid()) {
ArrayIndexAppendVisitor append(indexes, dst._colors.get());
_colors->accept(append);
}
if (_secondaryColors.valid()) {
ArrayIndexAppendVisitor append(indexes, dst._secondaryColors.get());
_secondaryColors->accept(append);
}
if (_fogCoords.valid()) {
ArrayIndexAppendVisitor append(indexes, dst._fogCoords.get());
_fogCoords->accept(append);
}
for (unsigned int i = 0; i < _texCoordArrays.size(); i++)
if (_texCoordArrays[i].valid()) {
ArrayIndexAppendVisitor append(indexes, dst._texCoordArrays[i].get());
_texCoordArrays[i]->accept(append);
}
for (unsigned int i = 0; i < _attributesArrays.size(); i++)
if (_attributesArrays[i].valid()) {
ArrayIndexAppendVisitor append(indexes, dst._attributesArrays[i].get());
_attributesArrays[i]->accept(append);
}
return dst._vertexes->getNumElements()-1;
}
GeometryArrayList cloneType() const {
GeometryArrayList array;
if (_vertexes.valid())
array._vertexes = dynamic_cast<osg::Array*>(_vertexes->cloneType());
if (_normals.valid())
array._normals = dynamic_cast<osg::Array*>(_normals->cloneType());
if (_colors.valid())
array._colors = dynamic_cast<osg::Array*>(_colors->cloneType());
if (_secondaryColors.valid())
array._secondaryColors = dynamic_cast<osg::Array*>(_secondaryColors->cloneType());
if (_fogCoords.valid())
array._fogCoords = dynamic_cast<osg::Array*>(_fogCoords->cloneType());
array._texCoordArrays.resize(_texCoordArrays.size());
for (unsigned int i = 0; i < _texCoordArrays.size(); i++) {
if (_texCoordArrays[i].valid())
array._texCoordArrays[i] = dynamic_cast<osg::Array*>(_texCoordArrays[i]->cloneType());
}
array._attributesArrays.resize(_attributesArrays.size());
for (unsigned int i = 0; i < _attributesArrays.size(); i++) {
if (_attributesArrays[i].valid())
array._attributesArrays[i] = dynamic_cast<osg::Array*>(_attributesArrays[i]->cloneType());
}
return array;
}
unsigned int size() const {
return _vertexes->getNumElements();
}
void setToGeometry(osg::Geometry& geom) {
if (_vertexes.valid())
geom.setVertexArray(_vertexes.get());
if (_normals.valid()) {
geom.setNormalArray(_normals.get(), osg::Array::BIND_PER_VERTEX);
}
if (_colors.valid()) {
geom.setColorArray(_colors.get(), osg::Array::BIND_PER_VERTEX);
}
if (_secondaryColors.valid()) {
geom.setSecondaryColorArray(_secondaryColors.get(), osg::Array::BIND_PER_VERTEX);
}
if (_fogCoords.valid()) {
geom.setFogCoordArray(_fogCoords.get(), osg::Array::BIND_PER_VERTEX);
}
for (unsigned int i = 0; i < _texCoordArrays.size(); ++i) {
if (_texCoordArrays[i].valid()) {
geom.setTexCoordArray(i, _texCoordArrays[i].get(), osg::Array::BIND_PER_VERTEX);
}
}
for (unsigned int i = 0; i < _attributesArrays.size(); ++i) {
if (_attributesArrays[i].valid()) {
geom.setVertexAttribArray(i, _attributesArrays[i].get(), osg::Array::BIND_PER_VERTEX);
}
}
}
};
#endif

View File

@@ -0,0 +1,381 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef GEOMETRY_SPLITTER_VISITOR
#define GEOMETRY_SPLITTER_VISITOR
#include <set>
#include <algorithm>
#include <osg/ref_ptr>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <osg/ValueObject>
#include <osgUtil/MeshOptimizers>
#include "GeometryArray"
#include "GeometryUniqueVisitor"
#include "glesUtil"
class GeometryIndexSplitter
{
public:
typedef std::vector<osg::ref_ptr<osg::Geometry> > GeometryList;
GeometryIndexSplitter(unsigned int maxIndex, bool disablePostTransform):
_maxIndexToSplit(maxIndex), _disablePostTransform(disablePostTransform)
{}
bool split(osg::Geometry& geometry) {
if(!hasValidPrimitives(geometry) || !needToSplit(geometry)) {
if(!_disablePostTransform) {
// optimize cache for rendering
glesUtil::VertexCacheVisitor posttransform;
posttransform.optimizeVertices(geometry);
}
_geometryList.push_back(&geometry); // remap geometry to itself
return false;
}
// keep bounding box data as user value if needed in subsequent processing
attachBufferBoundingBox(geometry);
// first optimize primitives indexing
{
if(!_disablePostTransform) {
// post-transform for better locality
glesUtil::VertexCacheVisitor posttransform;
posttransform.optimizeVertices(geometry);
}
// pre-transform to reindex correctly
glesUtil::VertexAccessOrderVisitor pretransform;
pretransform.optimizeOrder(geometry);
}
// Clone geometry as we will modify vertex arrays and primitives
// To avoid issues with shared buffers, arrays & primitives should be
// deep cloned.
// As UserValues might be changed on a per-geometry basis afterwards, we
// also deep clone userdata
// We do *not* want to clone statesets as they reference a UniqueID that
// should be unique (see #83056464).
osg::ref_ptr<osg::Geometry> processing = osg::clone(&geometry, osg::CopyOp::DEEP_COPY_ARRAYS |
osg::CopyOp::DEEP_COPY_PRIMITIVES |
osg::CopyOp::DEEP_COPY_USERDATA);
osg::ref_ptr<osg::Geometry> reported;
while (true) {
reported = doSplit(*processing);
// reduce vertex array if needed
if(processing && processing->getNumPrimitiveSets()) {
GeometryArrayList arrayList(*processing);
arrayList.setNumElements(osg::minimum(arrayList.size(), _maxIndexToSplit + 1));
_geometryList.push_back(processing);
}
if (!reported.valid()) {
break;
}
else {
processing = reported;
reported = 0;
// re order index elements
glesUtil::VertexAccessOrderVisitor preTransform;
preTransform.optimizeOrder(*processing);
}
}
osg::notify(osg::NOTICE) << "geometry " << &geometry << " " << geometry.getName()
<< " vertexes (" << geometry.getVertexArray()->getNumElements()
<< ") has DrawElements index > " << _maxIndexToSplit << ", splitted to "
<< _geometryList.size() << " geometry" << std::endl;
return true;
}
protected:
bool hasValidPrimitives(osg::Geometry& geometry) const {
for (unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) {
osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
if (primitive) {
if (!primitive->getDrawElements()) {
osg::notify(osg::INFO) << "can't split Geometry " << geometry.getName()
<< " (" << &geometry << ") contains non indexed primitives"
<< std::endl;
return false;
}
switch (primitive->getMode()) {
case osg::PrimitiveSet::TRIANGLES:
case osg::PrimitiveSet::LINES:
case osg::PrimitiveSet::POINTS:
break;
default:
osg::notify(osg::FATAL) << "can't split Geometry " << geometry.getName()
<< " (" << &geometry << ") contains non point/line/triangle primitives"
<< std::endl;
return false;
break;
}
}
}
return true;
}
bool needToSplit(const osg::Geometry& geometry) const {
for(unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) {
const osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements();
if (needToSplit(*primitive)) {
return true;
}
}
return false;
}
bool needToSplit(const osg::DrawElements& primitive) const {
for(unsigned int j = 0; j < primitive.getNumIndices(); j++) {
if (primitive.index(j) > _maxIndexToSplit){
return true;
}
}
return false;
}
void attachBufferBoundingBox(osg::Geometry& geometry) const {
// positions
setBufferBoundingBox(dynamic_cast<osg::Vec3Array*>(geometry.getVertexArray()));
// uvs
for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) {
setBufferBoundingBox(dynamic_cast<osg::Vec2Array*>(geometry.getTexCoordArray(i)));
}
}
template<typename T>
void setBufferBoundingBox(T* buffer) const {
if(!buffer) return;
typename T::ElementDataType bbl;
typename T::ElementDataType ufr;
const unsigned int dimension = buffer->getDataSize();
if(buffer->getNumElements()) {
for(unsigned int i = 0 ; i < dimension ; ++i) {
bbl[i] = ufr[i] = (*buffer->begin())[i];
}
for(typename T::const_iterator it = buffer->begin() + 1 ; it != buffer->end() ; ++ it) {
for(unsigned int i = 0 ; i < dimension ; ++ i) {
bbl[i] = std::min(bbl[i], (*it)[i]);
ufr[i] = std::max(ufr[i], (*it)[i]);
}
}
buffer->setUserValue("bbl", bbl);
buffer->setUserValue("ufr", ufr);
}
}
osg::Geometry* doSplit(osg::Geometry& geometry) const {
osg::Geometry::PrimitiveSetList geomPrimitives;
osg::Geometry::PrimitiveSetList wirePrimitives;
osg::Geometry::PrimitiveSetList reportedPrimitives;
std::vector< osg::ref_ptr<osg::DrawElements> > primitivesToFix;
osg::Geometry::PrimitiveSetList& primitives = geometry.getPrimitiveSetList();
std::set<unsigned int> validIndices;
osg::ref_ptr<osg::Geometry> reportGeometry;
for (unsigned int i = 0; i < primitives.size(); i++) {
osg::DrawElements *primitive = primitives[i]->getDrawElements();
bool isWireframe = false;
if(primitive->getUserValue("wireframe", isWireframe)) {
wirePrimitives.push_back(primitive);
}
else if (needToSplit(*primitive)) {
primitivesToFix.push_back(primitive);
}
else {
geomPrimitives.push_back(primitive);
setValidIndices(validIndices, primitive);
}
}
if (!primitivesToFix.empty()) {
// filter all indices > _maxIndexValue in primitives
for (unsigned int i = 0; i < primitivesToFix.size(); i++) {
osg::DrawElements* source = primitivesToFix[i].get();
osg::DrawElements* large = removeLargeIndices(dynamic_cast<osg::DrawElementsUInt*>(source));
reportedPrimitives.push_back(large);
geomPrimitives.push_back(source);
setValidIndices(validIndices, source);
}
}
// keep wireframe data associated to the current solid geometry
extractWireframePrimitive(wirePrimitives, validIndices, geomPrimitives, reportedPrimitives);
geometry.setPrimitiveSetList(geomPrimitives);
if (!reportedPrimitives.empty()) {
reportGeometry = osg::clone(&geometry, osg::CopyOp::DEEP_COPY_ARRAYS |
osg::CopyOp::DEEP_COPY_USERDATA);
reportGeometry->setPrimitiveSetList(reportedPrimitives);
return reportGeometry.release();
}
return 0;
}
void setValidIndices(std::set<unsigned int>& indices, const osg::DrawElements* primitive) const {
for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) {
indices.insert(primitive->index(j));
}
}
osg::DrawElements* removeLargeIndices(osg::DrawElementsUInt* source) const {
osg::DrawElementsUInt* large = new osg::DrawElementsUInt(source->getMode());
unsigned int primitive_size = 0;
switch(source->getMode()) {
case osg::PrimitiveSet::POINTS:
primitive_size = 1;
break;
case osg::PrimitiveSet::LINES:
primitive_size = 2;
break;
case osg::PrimitiveSet::TRIANGLES:
primitive_size = 3;
break;
}
for (int id = source->getNumPrimitives() - 1; id >= 0; -- id) {
const unsigned int arrayIndex = id * primitive_size;
for(unsigned int i = 0 ; i < primitive_size ; ++ i) {
if(source->index(arrayIndex + i) > _maxIndexToSplit) {
// add primitive in the large DrawElements
for(unsigned int j = 0 ; j < primitive_size ; ++ j) {
large->addElement(source->index(arrayIndex + j));
}
// remove primitive from source DrawElements
for(int j = primitive_size - 1 ; j >= 0 ; -- j) {
source->erase(source->begin() + arrayIndex + j);
}
break; // skip to next primitive
}
}
}
return large;
}
// keep wireframe data associated to the solid geometry
void extractWireframePrimitive(osg::Geometry::PrimitiveSetList& lines,
const std::set<unsigned int>& indices,
osg::Geometry::PrimitiveSetList& primitives,
osg::Geometry::PrimitiveSetList& reported) const {
if(indices.empty()) {
return;
}
for(unsigned int i = 0 ; i < lines.size() ; ++ i) {
const osg::DrawElementsUInt* line = dynamic_cast<osg::DrawElementsUInt*>(lines[i].get());
if(!line || line->getMode() != osg::PrimitiveSet::LINES) {
osg::notify(osg::INFO) << "Primitive with bad mode flagged as wireframe. Skipping."
<< std::endl;
}
osg::ref_ptr<osg::DrawElementsUInt> small = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES);
osg::ref_ptr<osg::DrawElementsUInt> large = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES);
for (unsigned int id = 0 ; id < line->getNumPrimitives() ; ++ id) {
unsigned int arrayIndex = id * 2;
unsigned int a = line->index(arrayIndex),
b = line->index(arrayIndex + 1);
if(indices.find(a) != indices.end() && indices.find(b) != indices.end()) {
small->addElement(a);
small->addElement(b);
}
else {
large->addElement(a);
large->addElement(b);
}
}
if(small->size()) {
small->setUserValue("wireframe", true);
primitives.push_back(small);
}
if(large->size()) {
large->setUserValue("wireframe", true);
reported.push_back(large);
}
}
}
public:
const unsigned int _maxIndexToSplit;
bool _disablePostTransform;
GeometryList _geometryList;
};
class GeometrySplitterVisitor : public GeometryUniqueVisitor {
public:
typedef std::vector< osg::ref_ptr<osg::Geometry> > GeometryList;
GeometrySplitterVisitor(unsigned int maxIndexValue = 65535, bool disablePostTransform=false):
GeometryUniqueVisitor("GeometrySplitterVisitor"),
_maxIndexValue(maxIndexValue),
_disablePostTransform(disablePostTransform)
{}
void apply(osg::Geode& geode) {
GeometryUniqueVisitor::apply(geode);
GeometryList remapped;
for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) {
osg::Geometry* geometry = geode.getDrawable(i)->asGeometry();
if(geometry) {
std::map<osg::Geometry*, GeometryList>::iterator lookup = _split.find(geometry);
if(lookup != _split.end() && !lookup->second.empty()) {
remapped.insert(remapped.end(), lookup->second.begin(), lookup->second.end());
}
}
}
// remove all drawables
geode.removeDrawables(0, geode.getNumDrawables());
for(unsigned int i = 0 ; i < remapped.size() ; ++ i) {
geode.addDrawable(remapped[i].get());
}
}
void apply(osg::Geometry& geometry) {
GeometryIndexSplitter splitter(_maxIndexValue, _disablePostTransform);
splitter.split(geometry);
setProcessed(&geometry, splitter._geometryList);
}
protected:
bool isProcessed(osg::Geometry* node) {
return _split.find(node) != _split.end();
}
void setProcessed(osg::Geometry* node, const GeometryList& list) {
_split.insert(std::pair<osg::Geometry*, GeometryList>(node, GeometryList(list)));
}
unsigned int _maxIndexValue;
std::map<osg::Geometry*, GeometryList> _split;
bool _disablePostTransform;
};
#endif

View File

@@ -0,0 +1,66 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef GEOMETRY_UNIQUE_VISITOR_H
#define GEOMETRY_UNIQUE_VISITOR_H
#include <osg/NodeVisitor>
#include <osg/Geometry>
#include <osg/Geode>
#include <set>
#include <string>
#include "StatLogger"
class GeometryUniqueVisitor : public osg::NodeVisitor {
public:
GeometryUniqueVisitor(const std::string label=std::string("GeometryUniqueVisitor")):
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_logger(formatStatLabel(label))
{}
virtual void apply(osg::Geode& geode){
for (unsigned int i = 0; i < geode.getNumDrawables(); i++) {
apply(*geode.getDrawable(i));
}
}
virtual void apply(osg::Drawable& drawable){
osg::Geometry* geometry = drawable.asGeometry();
if (!geometry || isProcessed(geometry)) {
return;
}
apply(*geometry);
}
virtual void apply(osg::Geometry& geometry) {} // to be implemented by actual visitors
protected:
bool isProcessed(osg::Geometry* node) {
return _processed.find(node) != _processed.end();
}
void setProcessed(osg::Geometry* node) {
_processed.insert(node);
}
std::string formatStatLabel(const std::string& label) const {
return label + "::apply(..)";
}
std::set<osg::Geometry*> _processed;
StatLogger _logger;
};
#endif

View File

@@ -0,0 +1,34 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef INDEX_MESH_VISITOR
#define INDEX_MESH_VISITOR
#include "GeometryUniqueVisitor"
class IndexMeshVisitor : public GeometryUniqueVisitor
{
public:
IndexMeshVisitor(): GeometryUniqueVisitor("IndexMeshVisitor")
{}
void apply(osg::Geometry& geom);
protected:
typedef std::vector<unsigned int> IndexList;
void addDrawElements(IndexList&,
osg::PrimitiveSet::Mode,
osg::Geometry::PrimitiveSetList&,
std::string userValue = std::string());
};
#endif

View File

@@ -0,0 +1,174 @@
#include <vector>
#include <algorithm> // sort
#include <limits> // numeric_limits
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <osg/ValueObject>
#include <osgUtil/MeshOptimizers>
#include "glesUtil"
#include "IndexMeshVisitor"
#include "PrimitiveIndexors"
using namespace glesUtil;
void IndexMeshVisitor::apply(osg::Geometry& geom) {
// TODO: this is deprecated
if (geom.getNormalBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
if (geom.getColorBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
if (geom.getSecondaryColorBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
if (geom.getFogCoordBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
// no point optimizing if we don't have enough vertices.
if (!geom.getVertexArray() || geom.getVertexArray()->getNumElements() < 3) return;
osgUtil::SharedArrayOptimizer deduplicator;
deduplicator.findDuplicatedUVs(geom);
// duplicate shared arrays as it isn't safe to rearrange vertices when arrays are shared.
if (geom.containsSharedArrays()) {
geom.duplicateSharedArrays();
}
osg::Geometry::PrimitiveSetList& primitives = geom.getPrimitiveSetList();
osg::Geometry::PrimitiveSetList::iterator itr;
osg::Geometry::PrimitiveSetList new_primitives;
new_primitives.reserve(primitives.size());
// compute duplicate vertices
typedef std::vector<unsigned int> IndexList;
unsigned int numVertices = geom.getVertexArray()->getNumElements();
IndexList indices(numVertices);
unsigned int i, j;
for(i = 0 ; i < numVertices ; ++ i) {
indices[i] = i;
}
VertexAttribComparitor arrayComparitor(geom);
std::sort(indices.begin(), indices.end(), arrayComparitor);
unsigned int lastUnique = 0;
unsigned int numUnique = 1;
for(i = 1 ; i < numVertices ; ++ i) {
if (arrayComparitor.compare(indices[lastUnique], indices[i]) != 0) {
lastUnique = i;
++ numUnique;
}
}
IndexList remapDuplicatesToOrignals(numVertices);
lastUnique = 0;
for(i = 1 ; i < numVertices ; ++ i) {
if (arrayComparitor.compare(indices[lastUnique],indices[i]) != 0) {
// found a new vertex entry, so previous run of duplicates needs
// to be put together.
unsigned int min_index = indices[lastUnique];
for(j = lastUnique + 1 ; j < i ; ++ j) {
min_index = osg::minimum(min_index, indices[j]);
}
for(j = lastUnique ; j < i ; ++ j) {
remapDuplicatesToOrignals[indices[j]] = min_index;
}
lastUnique = i;
}
}
unsigned int min_index = indices[lastUnique];
for(j = lastUnique + 1 ; j < i ; ++ j) {
min_index = osg::minimum(min_index, indices[j]);
}
for(j = lastUnique ; j < i ; ++ j) {
remapDuplicatesToOrignals[indices[j]] = min_index;
}
// copy the arrays.
IndexList finalMapping(numVertices);
IndexList copyMapping;
copyMapping.reserve(numUnique);
unsigned int currentIndex = 0;
for(i = 0 ; i < numVertices ; ++ i) {
if (remapDuplicatesToOrignals[i] == i) {
finalMapping[i] = currentIndex;
copyMapping.push_back(i);
currentIndex++;
}
else {
finalMapping[i] = finalMapping[remapDuplicatesToOrignals[i]];
}
}
// remap any shared vertex attributes
RemapArray ra(copyMapping);
arrayComparitor.accept(ra);
// triangulate faces
{
TriangleIndexor ti;
ti._maxIndex = numVertices;
ti._remapping = finalMapping;
for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) {
(*itr)->accept(ti);
}
addDrawElements(ti._indices, osg::PrimitiveSet::TRIANGLES, new_primitives);
}
// line-ify line-type primitives
{
LineIndexor li, wi; // lines and wireframes
li._maxIndex = numVertices;
wi._maxIndex = numVertices;
li._remapping = finalMapping;
wi._remapping = finalMapping;
for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) {
bool isWireframe = false;
if((*itr)->getUserValue("wireframe", isWireframe) && isWireframe) {
(*itr)->accept(wi);
}
else {
(*itr)->accept(li);
}
}
addDrawElements(li._indices, osg::PrimitiveSet::LINES, new_primitives);
addDrawElements(wi._indices, osg::PrimitiveSet::LINES, new_primitives, "wireframe");
}
// adds points primitives
{
IndexList points;
for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) {
if((*itr) && (*itr)->getMode() == osg::PrimitiveSet::POINTS) {
for(unsigned int k = 0 ; k < (*itr)->getNumIndices() ; ++ k) {
points.push_back(finalMapping[(*itr)->index(k)]);
}
}
}
addDrawElements(points, osg::PrimitiveSet::POINTS, new_primitives);
}
geom.setPrimitiveSetList(new_primitives);
deduplicator.deduplicateUVs(geom);
setProcessed(&geom);
}
void IndexMeshVisitor::addDrawElements(IndexList& data,
osg::PrimitiveSet::Mode mode,
osg::Geometry::PrimitiveSetList& primitives,
std::string userValue) {
if(!data.empty()) {
osg::DrawElementsUInt* elements = new osg::DrawElementsUInt(mode,
data.begin(),
data.end());
if(!userValue.empty()) {
elements->setUserValue(userValue, true);
}
primitives.push_back(elements);
}
}

View File

@@ -0,0 +1,178 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef LINE_INDEX_FUNCTOR_H
#define LINE_INDEX_FUNCTOR_H
#include <osg/PrimitiveSet>
#include <osg/Array>
#include <algorithm>
#include <set>
class Line {
public:
Line(unsigned int a, unsigned int b) {
_a = std::min<unsigned int>(a, b);
_b = std::max<unsigned int>(a, b);
}
bool operator<(const Line& e) {
return _a < e._a || (_a == e._a && _b < e._b);
}
unsigned int _a, _b;
};
struct LineCompare {
bool operator()(const Line& lhs, const Line& rhs) {
return lhs._a < rhs._a || (lhs._a == rhs._a && lhs._b < rhs._b);
}
};
template<class T>
class LineIndexFunctor : public osg::PrimitiveIndexFunctor, public T
{
public:
virtual void setVertexArray(unsigned int,const osg::Vec2*)
{}
virtual void setVertexArray(unsigned int ,const osg::Vec3* )
{}
virtual void setVertexArray(unsigned int,const osg::Vec4* )
{}
virtual void setVertexArray(unsigned int,const osg::Vec2d*)
{}
virtual void setVertexArray(unsigned int ,const osg::Vec3d* )
{}
virtual void setVertexArray(unsigned int,const osg::Vec4d* )
{}
virtual void begin(GLenum mode) {
_modeCache = mode;
_indexCache.clear();
}
virtual void vertex(unsigned int vert) {
_indexCache.push_back(vert);
}
virtual void end() {
if (!_indexCache.empty()) {
drawElements(_modeCache, _indexCache.size(), &_indexCache.front());
}
}
virtual void drawArrays(GLenum mode, GLint first, GLsizei count) {
switch(mode)
{
case(GL_LINES):
{
unsigned int pos = first;
for(GLsizei i = 0 ; i < count ; i += 2, pos += 2) {
line(pos, pos + 1);
}
}
break;
case(GL_LINE_STRIP):
{
unsigned int pos = first;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
line(pos, pos + 1);
}
}
break;
case(GL_LINE_LOOP):
{
unsigned int pos = first;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
line(pos, pos + 1);
}
line(pos, first);
}
break;
default: // not a line
break;
}
}
template<typename I>
void drawElements(GLenum mode, GLsizei count, const I* indices)
{
if (indices == 0 || count == 0) return;
switch(mode)
{
case(GL_LINES):
{
const I* iptr = indices;
for(GLsizei i = 0 ; i < count ; i += 2, iptr += 2) {
line(*iptr, *(iptr+1));
}
}
break;
case(GL_LINE_STRIP):
{
const I* iptr = indices;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
line(*iptr, *(iptr+1));
}
}
break;
case(GL_LINE_LOOP):
{
const I* iptr = indices;
I first = *iptr;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
line(*iptr, *(iptr+1));
}
line(*iptr, first);
}
break;
default: // not a line
break;
}
}
virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) {
drawElements<GLubyte>(mode, count, indices);
}
virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) {
drawElements<GLushort>(mode, count, indices);
}
virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) {
drawElements<GLuint>(mode, count, indices);
}
void line(unsigned int a, unsigned int b) {
Line e(T::index(a), T::index(b)); // use remapped indices to deduplicate lines
if(_lineCache.find(e) == _lineCache.end()) {
this->operator()(a, b);
_lineCache.insert(e);
}
}
GLenum _modeCache;
std::vector<GLuint> _indexCache;
std::set<Line, LineCompare> _lineCache;
};
#endif

View File

@@ -0,0 +1,143 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef OPENGLES_GEOMETRY_OPTIMIZER
#define OPENGLES_GEOMETRY_OPTIMIZER
#include <osg/Node>
#include <algorithm> //std::max
#include "AnimationVisitor"
#include "BindPerVertexVisitor"
#include "DetachPrimitiveVisitor"
#include "DrawArrayVisitor"
#include "GeometrySplitterVisitor"
#include "IndexMeshVisitor"
#include "PreTransformVisitor"
#include "TangentSpaceVisitor"
#include "TriangleStripVisitor"
#include "UnIndexMeshVisitor"
#include "WireframeVisitor"
class OpenGLESGeometryOptimizer
{
public:
OpenGLESGeometryOptimizer() :
_useDrawArray(false),
_disableTriStrip(false),
_disableMergeTriStrip(false),
_disablePreTransform(false),
_disablePostTransform(false),
_triStripCacheSize(16),
_triStripMinSize(2),
_generateTangentSpace(false),
_tangentUnit(0),
_maxIndexValue(65535),
_wireframe("")
{}
// run the optimizer
osg::Node* optimize(osg::Node& node);
// handle options
void setUseDrawArray(bool s) { _useDrawArray = s; }
void setDisableTriStrip(bool s) { _disableTriStrip = s; }
void setDisableMergeTriStrip(bool s) { _disableMergeTriStrip = s; }
void setDisablePreTransform(bool s) { _disablePreTransform = s; }
void setDisablePostTransform(bool s) { _disablePostTransform = s; }
void setTripStripCacheSize(unsigned int size) { _triStripCacheSize = size; }
void setTripStripMinSize(unsigned int size) { _triStripMinSize = std::max<unsigned int>(size, 2); }
void setTexCoordChannelForTangentSpace(int uv) {
_tangentUnit = uv;
_generateTangentSpace = true;
}
void setMaxIndexValue(unsigned int s) { _maxIndexValue = s; }
void setWireframe(const std::string& s) {
_wireframe = s;
if(_wireframe == std::string("outline")) {
// no use to build strip if we only want wireframe
setDisableTriStrip(true);
}
}
protected:
void makeAnimation(osg::Node* node) {
AnimationVisitor anim;
node->accept(anim);
}
void makeWireframe(osg::Node* node) {
WireframeVisitor wireframe(_wireframe == std::string("inline"));
node->accept(wireframe);
}
void makeBindPerVertex(osg::Node* node) {
BindPerVertexVisitor bindpervertex;
node->accept(bindpervertex);
}
void makeIndexMesh(osg::Node* node) {
IndexMeshVisitor indexer;
node->accept(indexer);
}
void makeTangentSpace(osg::Node* node) {
TangentSpaceVisitor tangent(_tangentUnit);
node->accept(tangent);
}
void makeSplit(osg::Node* node) {
GeometrySplitterVisitor splitter(_maxIndexValue, _disablePostTransform);
node->accept(splitter);
}
void makeTriStrip(osg::Node* node) {
TriangleStripVisitor strip(_triStripCacheSize, _triStripMinSize, !_disableMergeTriStrip);
node->accept(strip);
}
void makeDrawArray(osg::Node* node) {
DrawArrayVisitor drawarray;
node->accept(drawarray);
}
void makePreTransform(osg::Node* node) {
PreTransformVisitor preTransform;
node->accept(preTransform);
}
void makeDetach(osg::Node* node) {
DetachPrimitiveVisitor detacher("wireframe", false, _wireframe == std::string("inline"));
node->accept(detacher);
}
bool _useDrawArray;
bool _disableTriStrip;
bool _disableMergeTriStrip;
bool _disablePreTransform;
bool _disablePostTransform;
unsigned int _triStripCacheSize;
unsigned int _triStripMinSize;
bool _generateTangentSpace;
int _tangentUnit;
unsigned int _maxIndexValue;
std::string _wireframe;
};
#endif

View File

@@ -0,0 +1,54 @@
#include <limits>
#include "OpenGLESGeometryOptimizer"
#include "glesUtil"
const unsigned glesUtil::Remapper::invalidIndex = std::numeric_limits<unsigned>::max();
osg::Node* OpenGLESGeometryOptimizer::optimize(osg::Node& node) {
osg::ref_ptr<osg::Node> model = osg::clone(&node);
// animation: create regular Geometry if RigGeometry
makeAnimation(model.get());
// wireframe
if (!_wireframe.empty()) {
makeWireframe(model.get());
}
// bind per vertex
makeBindPerVertex(model.get());
// index (merge exact duplicates + uses simple triangles & lines i.e. no strip/fan/loop)
makeIndexMesh(model.get());
// tangent space
if (_generateTangentSpace) {
makeTangentSpace(model.get());
}
if(!_useDrawArray) {
// split geometries having some primitive index > _maxIndexValue
makeSplit(model.get());
}
// strip
if(!_disableTriStrip) {
makeTriStrip(model.get());
}
if(_useDrawArray) {
// drawelements to drawarrays
makeDrawArray(model.get());
}
else if(!_disablePreTransform) {
// pre-transform
makePreTransform(model.get());
}
// detach wireframe
makeDetach(model.get());
return model.release();
}

View File

@@ -0,0 +1,153 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef POINT_INDEX_FUNCTOR_H
#define POINT_INDEX_FUNCTOR_H
#include <osg/PrimitiveSet>
#include <osg/Array>
template<class T>
class PointIndexFunctor : public osg::PrimitiveIndexFunctor, public T
{
public:
virtual void setVertexArray(unsigned int,const osg::Vec2*)
{
}
virtual void setVertexArray(unsigned int ,const osg::Vec3* )
{
}
virtual void setVertexArray(unsigned int,const osg::Vec4* )
{
}
virtual void setVertexArray(unsigned int,const osg::Vec2d*)
{
}
virtual void setVertexArray(unsigned int ,const osg::Vec3d* )
{
}
virtual void setVertexArray(unsigned int,const osg::Vec4d* )
{
}
virtual void begin(GLenum mode)
{
_modeCache = mode;
_indexCache.clear();
}
virtual void vertex(unsigned int vert)
{
_indexCache.push_back(vert);
}
virtual void end()
{
if (!_indexCache.empty())
{
drawElements(_modeCache,_indexCache.size(),&_indexCache.front());
}
}
virtual void drawArrays(GLenum mode,GLint first,GLsizei count)
{
switch(mode)
{
case(GL_POINTS):
{
unsigned int pos=first;
for(GLsizei i=0;i<count;++i)
this->operator()(pos+i);
}
break;
default:
// can't be converted into to triangles.
break;
}
}
virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices)
{
if (indices==0 || count==0) return;
typedef GLubyte Index;
typedef const Index* IndexPointer;
switch(mode)
{
case (GL_POINTS):
{
IndexPointer ilast = &indices[count];
for(IndexPointer iptr=indices;iptr<ilast;iptr+=1)
this->operator()(*iptr);
}
break;
default:
break;
}
}
virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices)
{
if (indices==0 || count==0) return;
typedef GLushort Index;
typedef const Index* IndexPointer;
switch(mode)
{
case (GL_POINTS):
{
IndexPointer ilast = &indices[count];
for(IndexPointer iptr=indices;iptr<ilast;iptr+=1)
this->operator()(*iptr);
}
break;
default:
break;
}
}
virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices)
{
if (indices==0 || count==0) return;
typedef GLuint Index;
typedef const Index* IndexPointer;
switch(mode)
{
case (GL_POINTS):
{
IndexPointer ilast = &indices[count];
for(IndexPointer iptr=indices;iptr<ilast;iptr+=1)
this->operator()(*iptr);
}
break;
default:
break;
}
}
GLenum _modeCache;
std::vector<GLuint> _indexCache;
};
#endif

View File

@@ -0,0 +1,35 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef PRE_TRANSFORM_VISITOR
#define PRE_TRANSFORM_VISITOR
#include "GeometryUniqueVisitor"
#include "glesUtil"
class PreTransformVisitor : public GeometryUniqueVisitor
{
public:
PreTransformVisitor(): GeometryUniqueVisitor("PreTransformVisitor")
{}
void apply(osg::Geometry& geometry) {
glesUtil::VertexAccessOrderVisitor optimizer;
optimizer.optimizeOrder(geometry);
setProcessed(&geometry);
}
};
#endif

View File

@@ -0,0 +1,114 @@
#ifndef PRIMITIVE_OPERATORS_H
#define PRIMITIVE_OPERATORS_H
#include <vector>
#include <osg/TriangleIndexFunctor>
#include <osgUtil/MeshOptimizers>
#include "TriangleLinePointIndexFunctor"
#include "EdgeIndexFunctor"
#include "LineIndexFunctor"
#include "PointIndexFunctor"
typedef std::vector<unsigned int> IndexList;
// Construct an index list of triangles for DrawElements for any input
// primitives.
struct IndexOperator {
unsigned int _maxIndex;
IndexList _remapping;
IndexList _indices;
IndexOperator() : _maxIndex(0)
{}
inline unsigned int index(unsigned int v) {
if(_remapping.empty()) {
return v;
}
else {
return _remapping[v];
}
}
// triangles
inline void operator()(unsigned int p1, unsigned int p2, unsigned int p3) {
if(!valid(p1, p2, p3)) {
return;
}
if (_remapping.empty()) {
_indices.push_back(p1);
_indices.push_back(p2);
_indices.push_back(p3);
}
else {
_indices.push_back(_remapping[p1]);
_indices.push_back(_remapping[p2]);
_indices.push_back(_remapping[p3]);
}
}
// edges
inline void operator()(unsigned int p1, unsigned int p2) {
if(!valid(p1, p2)) {
return;
}
if (_remapping.empty()) {
_indices.push_back(p1);
_indices.push_back(p2);
}
else {
_indices.push_back(_remapping[p1]);
_indices.push_back(_remapping[p2]);
}
}
// points
inline void operator()(unsigned int p1) {
if(!valid(p1)) {
return;
}
if (_remapping.empty()) {
_indices.push_back(p1);
}
else {
_indices.push_back(_remapping[p1]);
}
}
// filter primitives referencing bad indices
inline bool valid(unsigned int v1, unsigned int v2, unsigned int v3) {
if(_maxIndex) {
return (v1 < _maxIndex && v2 < _maxIndex && v3 < _maxIndex);
}
return true;
}
inline bool valid(unsigned int v1, unsigned int v2) {
if(_maxIndex) {
return (v1 < _maxIndex && v2 < _maxIndex);
}
return true;
}
inline bool valid(unsigned int v1) {
if(_maxIndex) {
return (v1 < _maxIndex);
}
return true;
}
};
typedef osg::TriangleIndexFunctor<IndexOperator> TriangleIndexor;
typedef EdgeIndexFunctor<IndexOperator> EdgeIndexor;
typedef LineIndexFunctor<IndexOperator> LineIndexor; // indexes only primitives in LINES, LINE_STRIP, LINE_LOOP mode
typedef PointIndexFunctor<IndexOperator> PointIndexor;
#endif

View File

@@ -0,0 +1,272 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#include <osg/Notify>
#include <osg/Geode>
#include <osg/Version>
#include <osg/Endian>
#include <osgDB/ReaderWriter>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <string>
#include "UnIndexMeshVisitor"
#include "OpenGLESGeometryOptimizer"
using namespace osg;
class ReaderWriterGLES : public osgDB::ReaderWriter
{
public:
struct OptionsStruct {
std::string enableWireframe;
bool generateTangentSpace;
int tangentSpaceTextureUnit;
bool disableTriStrip;
bool disableMergeTriStrip;
bool disablePreTransform;
bool disablePostTransform;
unsigned int triStripCacheSize;
unsigned int triStripMinSize;
bool useDrawArray;
bool disableIndex;
unsigned int maxIndexValue;
OptionsStruct() {
enableWireframe = "";
generateTangentSpace = false;
tangentSpaceTextureUnit = 0;
disableTriStrip = false;
disableMergeTriStrip = false;
disablePreTransform = false;
disablePostTransform = false;
triStripCacheSize = 16;
triStripMinSize = 2;
useDrawArray = false;
disableIndex = false;
maxIndexValue = 0;
}
};
ReaderWriterGLES()
{
supportsExtension("gles","OpenGL ES optimized format");
supportsOption("enableWireframe[=inline]","create a wireframe geometry for each triangles geometry. The wire geometry will be stored along the solid geometry if 'inline' is specified.");
supportsOption("generateTangentSpace","Build tangent space to each geometry");
supportsOption("tangentSpaceTextureUnit=<unit>","Specify on wich texture unit normal map is");
supportsOption("triStripCacheSize=<int>","set the cache size when doing tristrip");
supportsOption("triStripMinSize=<int>","set the minimum accepted length for a strip");
supportsOption("disableMergeTriStrip","disable the merge of all tristrip into one");
supportsOption("disableTriStrip","disable generation of tristrip");
supportsOption("disablePreTransform","disable pre-transform of geometries after split");
supportsOption("disablePostTransform","disable post-transform of geometries (called during geometry splitting)");
supportsOption("useDrawArray","prefer drawArray instead of drawelement with split of geometry");
supportsOption("disableIndex","Do not index the geometry");
supportsOption("maxIndexValue=<int>","set the maximum index value (first index is 0)");
}
virtual const char* className() const { return "GLES Optimizer"; }
virtual osg::Node* optimizeModel(const Node& node, const OptionsStruct& options) const
{
osg::Node* model = osg::clone(&node);
if (options.disableIndex) {
UnIndexMeshVisitor unindex;
model->accept(unindex);
}
else {
OpenGLESGeometryOptimizer optimizer;
optimizer.setUseDrawArray(options.useDrawArray);
optimizer.setTripStripCacheSize(options.triStripCacheSize);
optimizer.setTripStripMinSize(options.triStripMinSize);
optimizer.setDisableTriStrip(options.disableTriStrip);
optimizer.setDisableMergeTriStrip(options.disableMergeTriStrip);
optimizer.setDisablePreTransform(options.disablePreTransform);
optimizer.setDisablePostTransform(options.disablePostTransform);
optimizer.setWireframe(options.enableWireframe);
if (options.generateTangentSpace) {
optimizer.setTexCoordChannelForTangentSpace(options.tangentSpaceTextureUnit);
}
if(options.maxIndexValue) {
optimizer.setMaxIndexValue(options.maxIndexValue);
}
model = optimizer.optimize(*model);
}
return model;
}
virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
{
std::string ext = osgDB::getLowerCaseFileExtension(fileName);
if( !acceptsExtension(ext) )
return ReadResult::FILE_NOT_HANDLED;
OSG_INFO << "ReaderWriterGLES( \"" << fileName << "\" )" << std::endl;
// strip the pseudo-loader extension
std::string realName = osgDB::getNameLessExtension( fileName );
if (realName.empty())
return ReadResult::FILE_NOT_HANDLED;
// recursively load the subfile.
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile( realName, options );
if( !node )
{
// propagate the read failure upwards
OSG_WARN << "Subfile \"" << realName << "\" could not be loaded" << std::endl;
return ReadResult::FILE_NOT_HANDLED;
}
OptionsStruct _options;
_options = parseOptions(options);
node = optimizeModel(*node, _options);
return node.release();
}
virtual osgDB::ReaderWriter::WriteResult writeNode(const Node& node,
const std::string& fileName,
const osgDB::ReaderWriter::Options* options) const
{
std::string ext = osgDB::getLowerCaseFileExtension(fileName);
if (!acceptsExtension(ext))
return WriteResult::FILE_NOT_HANDLED;
std::string realFileName = osgDB::getNameLessExtension(fileName);
if(realFileName.empty())
return WriteResult::FILE_NOT_HANDLED;
// gles optimization
OptionsStruct _options;
_options = parseOptions(options);
ref_ptr<Node> optimizedNode = optimizeModel(node, _options);
// forward writing to next plugin
ref_ptr<ReaderWriter> rw = getReaderWriter(realFileName);
if(rw) {
return rw->writeNode(*optimizedNode, realFileName, options);
}
else {
return WriteResult::ERROR_IN_WRITING_FILE;
}
}
ReaderWriterGLES::OptionsStruct parseOptions(const osgDB::ReaderWriter::Options* options) const
{
OptionsStruct localOptions;
if (options)
{
osg::notify(NOTICE) << "options " << options->getOptionString() << std::endl;
std::istringstream iss(options->getOptionString());
std::string opt;
while (iss >> opt)
{
// split opt into pre= and post=
std::string pre_equals;
std::string post_equals;
size_t found = opt.find("=");
if(found!=std::string::npos)
{
pre_equals = opt.substr(0,found);
post_equals = opt.substr(found+1);
}
else
{
pre_equals = opt;
}
if (pre_equals == "enableWireframe")
{
if(post_equals == "inline") {
localOptions.enableWireframe = "inline";
}
else {
localOptions.enableWireframe = "outline";
}
}
if (pre_equals == "useDrawArray")
{
localOptions.useDrawArray = true;
}
if (pre_equals == "disableMergeTriStrip")
{
localOptions.disableMergeTriStrip = true;
}
if (pre_equals == "disablePreTransform")
{
localOptions.disablePreTransform = true;
}
if (pre_equals == "disablePostTransform")
{
localOptions.disablePostTransform = true;
}
if (pre_equals == "disableTriStrip")
{
localOptions.disableTriStrip = true;
}
if (pre_equals == "generateTangentSpace")
{
localOptions.generateTangentSpace = true;
}
if (pre_equals == "disableIndex")
{
localOptions.disableIndex = true;
}
if (post_equals.length() > 0) {
if (pre_equals == "tangentSpaceTextureUnit") {
localOptions.tangentSpaceTextureUnit = atoi(post_equals.c_str());
}
if (pre_equals == "triStripCacheSize") {
localOptions.triStripCacheSize = atoi(post_equals.c_str());
}
if (pre_equals == "triStripMinSize") {
localOptions.triStripMinSize = atoi(post_equals.c_str());
}
if (pre_equals == "maxIndexValue") {
localOptions.maxIndexValue = atoi(post_equals.c_str());
}
}
}
}
return localOptions;
}
protected:
ReaderWriter* getReaderWriter(const std::string& fileName) const
{
ref_ptr<osgDB::Registry> registry = osgDB::Registry::instance();
std::string ext = osgDB::getLowerCaseFileExtension(fileName);
return registry->getReaderWriterForExtension(ext);
}
};
// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(gles, ReaderWriterGLES)

View File

@@ -0,0 +1,32 @@
#ifndef STAT_LOGGER
#define STAT_LOGGER
#include <osg/Timer>
#include <osg/Notify>
class StatLogger
{
public:
StatLogger(const std::string& label): _start(getTick()), _label(label) {}
~StatLogger() {
_stop = getTick();
OSG_INFO << std::flush
<< "Info: " << _label << " timing: " << getElapsedSeconds() << "s"
<< std::endl << std::flush;
}
protected:
osg::Timer_t _start, _stop;
std::string _label;
inline osg::Timer_t getTick() const {
return osg::Timer::instance()->tick();
}
inline double getElapsedSeconds() const {
return osg::Timer::instance()->delta_s(_start, _stop);
}
};
#endif

View File

@@ -0,0 +1,106 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef TANGENT_SPACE_VISITOR
#define TANGENT_SPACE_VISITOR
#define TANGENT_ATTRIBUTE_INDEX 20
#include <osgUtil/TangentSpaceGenerator>
#include <osg/ValueObject> // {get,set}UserValue
#include <osg/Array>
#include "GeometryUniqueVisitor"
// we will store only tangent and rebuilt tangent2 in the vertex shader
// http://www.terathon.com/code/tangent.html
class TangentSpaceVisitor : public GeometryUniqueVisitor
{
public:
TangentSpaceVisitor(int textureUnit = 0):
GeometryUniqueVisitor("TangentSpaceVisitor"),
_textureUnit(textureUnit)
{}
void apply(osg::Geometry& geom) {
if (!geom.getTexCoordArray(_textureUnit)){
int texUnit = 0;
bool found = false;
while(texUnit < 32){
if (_textureUnit != texUnit && geom.getTexCoordArray(texUnit)){
_textureUnit = texUnit;
found = true;
break;
}
texUnit++;
}
if (!found)
return;
}
osg::ref_ptr<osgUtil::TangentSpaceGenerator> generator = new osgUtil::TangentSpaceGenerator;
generator->generate(&geom, _textureUnit);
// keep original normal array
if (!geom.getNormalArray()) {
if (generator->getNormalArray()) {
osg::Vec3Array* vec3Normals = new osg::Vec3Array();
osg::Vec4Array* vec4Normals = generator->getNormalArray();
for (unsigned int i = 0; i < vec4Normals->size(); i++) {
osg::Vec3 n = osg::Vec3((*vec4Normals)[i][0],
(*vec4Normals)[i][1],
(*vec4Normals)[i][2]);
vec3Normals->push_back(n);
}
geom.setNormalArray(vec3Normals, osg::Array::BIND_PER_VERTEX);
}
}
if (generator->getTangentArray()) {
osg::Vec4Array* normal = generator->getNormalArray();
osg::Vec4Array* tangent = generator->getTangentArray();
osg::Vec4Array* tangent2 = generator->getBinormalArray();
osg::Vec4Array* finalTangent = dynamic_cast<osg::Vec4Array*>(generator->getTangentArray()
->clone(osg::CopyOp::DEEP_COPY_ALL));
for (unsigned int i = 0; i < tangent->size(); i++) {
osg::Vec3 n = osg::Vec3((*normal)[i][0],
(*normal)[i][1],
(*normal)[i][2]);
osg::Vec3 t = osg::Vec3((*tangent)[i][0],
(*tangent)[i][1],
(*tangent)[i][2]);
osg::Vec3 t2 = osg::Vec3((*tangent2)[i][0],
(*tangent2)[i][1],
(*tangent2)[i][2]);
// Gram-Schmidt orthogonalize
osg::Vec3 t3 = (t - n * (n * t));
t3.normalize();
(*finalTangent)[i] = osg::Vec4(t3, 0.0);
// Calculate handedness
(*finalTangent)[i][3] = (((n ^ t) * t2) < 0.0) ? -1.0 : 1.0;
// The bitangent vector B is then given by B = (N × T) · Tw
}
finalTangent->setUserValue("tangent", true);
geom.setVertexAttribArray(geom.getNumVertexAttribArrays(), finalTangent, osg::Array::BIND_PER_VERTEX);
}
setProcessed(&geom);
}
protected:
int _textureUnit;
};
#endif

View File

@@ -0,0 +1,257 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef TRIANGLE_LINE_POINT_INDEX_FUNCTOR
#define TRIANGLE_LINE_POINT_INDEX_FUNCTOR
#include <osg/PrimitiveSet>
#include <osg/Array>
template<class T>
class TriangleLinePointIndexFunctor : public osg::PrimitiveIndexFunctor, public T
{
public:
virtual void setVertexArray(unsigned int,const osg::Vec2*)
{}
virtual void setVertexArray(unsigned int ,const osg::Vec3* )
{}
virtual void setVertexArray(unsigned int,const osg::Vec4* )
{}
virtual void setVertexArray(unsigned int,const osg::Vec2d*)
{}
virtual void setVertexArray(unsigned int ,const osg::Vec3d* )
{}
virtual void setVertexArray(unsigned int,const osg::Vec4d* )
{}
virtual void begin(GLenum mode) {
_modeCache = mode;
_indexCache.clear();
}
virtual void vertex(unsigned int vert) {
_indexCache.push_back(vert);
}
virtual void end() {
if (!_indexCache.empty()) {
drawElements(_modeCache,_indexCache.size(),&_indexCache.front());
}
}
virtual void drawArrays(GLenum mode, GLint first, GLsizei count) {
switch(mode)
{
case(GL_TRIANGLES):
{
unsigned int pos=first;
for(GLsizei i = 2 ; i < count ; i += 3, pos += 3) {
this->operator()(pos, pos + 1, pos + 2);
}
break;
}
case(GL_TRIANGLE_STRIP):
{
unsigned int pos = first;
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) {
if ((i%2)) this->operator()(pos, pos + 2, pos + 1);
else this->operator()(pos, pos + 1, pos + 2);
}
break;
}
case(GL_QUADS):
{
unsigned int pos = first;
for(GLsizei i = 3 ; i < count ; i += 4, pos += 4) {
this->operator()(pos,pos + 1, pos + 2);
this->operator()(pos,pos + 2, pos + 3);
}
break;
}
case(GL_QUAD_STRIP):
{
unsigned int pos = first;
for(GLsizei i = 3 ; i < count ; i += 2, pos += 2) {
this->operator()(pos, pos + 1,pos + 2);
this->operator()(pos + 1,pos + 3,pos + 2);
}
break;
}
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
case(GL_TRIANGLE_FAN):
{
unsigned int pos = first + 1;
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) {
this->operator()(first, pos, pos + 1);
}
break;
}
case(GL_LINES):
{
unsigned int pos = first;
for(GLsizei i = 0 ; i < count ; i += 2, pos += 2) {
this->operator()(pos, pos + 1);
}
break;
}
case(GL_LINE_STRIP):
{
unsigned int pos = first;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
this->operator()(pos, pos + 1);
}
break;
}
case(GL_LINE_LOOP):
{
unsigned int pos = first;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
this->operator()(pos, pos + 1);
}
this->operator()(pos, first);
break;
}
case(GL_POINTS):
{
unsigned int pos=first;
for(GLsizei i = 0 ; i < count ; ++ i) {
this->operator()(pos + i);
}
break;
}
default:
break;
}
}
template<typename I>
void drawElements(GLenum mode, GLsizei count, const I* indices)
{
typedef I Index;
typedef const I* IndexPointer;
if (indices == 0 || count == 0) {
return;
}
switch(mode)
{
case(GL_TRIANGLES):
{
IndexPointer ilast = &indices[count];
for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 3) {
this->operator()(*iptr, *(iptr + 1), *(iptr + 2));
}
break;
}
case(GL_TRIANGLE_STRIP):
{
IndexPointer iptr = indices;
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
if ((i%2)) this->operator()(*(iptr), *(iptr + 2), *(iptr + 1));
else this->operator()(*(iptr), *(iptr + 1), *(iptr + 2));
}
break;
}
case(GL_QUADS):
{
IndexPointer iptr = indices;
for(GLsizei i = 3 ; i < count ; i += 4, iptr += 4) {
this->operator()(*(iptr), *(iptr + 1), *(iptr + 2));
this->operator()(*(iptr), *(iptr + 2), *(iptr + 3));
}
break;
}
case(GL_QUAD_STRIP):
{
IndexPointer iptr = indices;
for(GLsizei i = 3 ; i < count ; i += 2, iptr += 2) {
this->operator()(*(iptr), *(iptr + 1), *(iptr + 2));
this->operator()(*(iptr + 1), *(iptr + 3), *(iptr + 2));
}
break;
}
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
case(GL_TRIANGLE_FAN):
{
IndexPointer iptr = indices;
Index first = *iptr;
++iptr;
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
this->operator()(first, *(iptr), *(iptr + 1));
}
break;
}
case(GL_LINES):
{
const I* iptr = indices;
for(GLsizei i = 0 ; i < count ; i += 2, iptr += 2) {
this->operator()(*iptr, *(iptr + 1));
}
break;
}
case(GL_LINE_STRIP):
{
const I* iptr = indices;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
this->operator()(*iptr, *(iptr + 1));
}
break;
}
case(GL_LINE_LOOP):
{
const I* iptr = indices;
I first = *iptr;
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
this->operator()(*iptr, *(iptr + 1));
}
this->operator()(*iptr, first);
break;
}
case GL_POINTS:
{
IndexPointer ilast = &indices[count];
for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 1) {
this->operator()(*iptr);
}
break;
}
default:
break;
}
}
virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) {
drawElements<GLubyte>(mode, count, indices);
}
virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) {
drawElements<GLushort>(mode, count, indices);
}
virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) {
drawElements<GLuint>(mode, count, indices);
}
GLenum _modeCache;
std::vector<GLuint> _indexCache;
std::vector<unsigned int> _remap;
};
#endif

View File

@@ -0,0 +1,35 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef TRIANGLE_STRIP_VISITOR_H
#define TRIANGLE_STRIP_VISITOR_H
#include "GeometryUniqueVisitor"
class TriangleStripVisitor : public GeometryUniqueVisitor {
public:
TriangleStripVisitor(unsigned int cacheSize, unsigned int minSize, bool merge):
GeometryUniqueVisitor("TriangleStripVisitor"),
_cacheSize(cacheSize), _minSize(minSize), _merge(merge)
{}
void apply(osg::Geometry& geometry);
protected:
void mergeTrianglesStrip(osg::Geometry& geometry);
unsigned int _cacheSize;
unsigned int _minSize;
bool _merge;
};
#endif

View File

@@ -0,0 +1,94 @@
#include <osgUtil/TriStripVisitor>
#include "TriangleStripVisitor"
void TriangleStripVisitor::apply(osg::Geometry& geometry) {
osgUtil::TriStripVisitor tristrip;
tristrip.setCacheSize(_cacheSize);
tristrip.setMinStripSize(_minSize);
tristrip.stripify(geometry);
// merge stritrip to one using degenerated triangles as glue
if (_merge) {
mergeTrianglesStrip(geometry);
}
}
void TriangleStripVisitor::mergeTrianglesStrip(osg::Geometry& geometry)
{
int nbtristrip = 0;
int nbtristripVertexes = 0;
for (unsigned int i = 0; i < geometry.getNumPrimitiveSets(); i++) {
osg::PrimitiveSet* ps = geometry.getPrimitiveSet(i);
osg::DrawElements* de = ps->getDrawElements();
if (de && de->getMode() == osg::PrimitiveSet::TRIANGLE_STRIP) {
nbtristrip++;
nbtristripVertexes += de->getNumIndices();
}
}
if (nbtristrip > 0) {
osg::notify(osg::NOTICE) << "found " << nbtristrip << " tristrip, "
<< "total vertexes " << nbtristripVertexes
<< " should result to " << nbtristripVertexes + nbtristrip*2
<< " after connection" << std::endl;
osg::DrawElementsUShort* ndw = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP);
for (unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) {
osg::PrimitiveSet* ps = geometry.getPrimitiveSet(i);
if (ps && ps->getMode() == osg::PrimitiveSet::TRIANGLE_STRIP) {
osg::DrawElements* de = ps->getDrawElements();
if (de) {
// if connection needed insert degenerate triangles
if (ndw->getNumIndices() != 0 && ndw->back() != de->getElement(0)) {
// duplicate last vertex
ndw->addElement(ndw->back());
// insert first vertex of next strip
ndw->addElement(de->getElement(0));
}
if (ndw->getNumIndices() % 2 != 0 ) {
// add a dummy vertex to reverse the strip
ndw->addElement(de->getElement(0));
}
for (unsigned int j = 0; j < de->getNumIndices(); j++) {
ndw->addElement(de->getElement(j));
}
}
else if (ps->getType() == osg::PrimitiveSet::DrawArraysPrimitiveType) {
// trip strip can generate drawarray of 5 elements we want to merge them too
osg::DrawArrays* da = dynamic_cast<osg::DrawArrays*> (ps);
// if connection needed insert degenerate triangles
if (ndw->getNumIndices() != 0 && ndw->back() != da->getFirst()) {
// duplicate last vertex
ndw->addElement(ndw->back());
// insert first vertex of next strip
ndw->addElement(da->getFirst());
}
if (ndw->getNumIndices() % 2 != 0 ) {
// add a dummy vertex to reverse the strip
ndw->addElement(da->getFirst());
}
for (unsigned int j = 0; j < da->getNumIndices(); j++) {
ndw->addElement(da->getFirst() + j);
}
}
}
}
for (int i = geometry.getNumPrimitiveSets() - 1 ; i >= 0 ; -- i) {
osg::PrimitiveSet* ps = geometry.getPrimitiveSet(i);
// remove null primitive sets and all primitives that have been merged
// (i.e. all TRIANGLE_STRIP DrawElements and DrawArrays)
if (!ps || (ps && ps->getMode() == osg::PrimitiveSet::TRIANGLE_STRIP)) {
geometry.getPrimitiveSetList().erase(geometry.getPrimitiveSetList().begin() + i);
}
}
geometry.getPrimitiveSetList().insert(geometry.getPrimitiveSetList().begin(), ndw);
}
}

View File

@@ -0,0 +1,28 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef UNINDEX_MESH_VISITOR
#define UNINDEX_MESH_VISITOR
#include "GeometryUniqueVisitor"
class UnIndexMeshVisitor : public GeometryUniqueVisitor
{
public:
UnIndexMeshVisitor(): GeometryUniqueVisitor("UnIndexMeshVisitor")
{}
void apply(osg::Geometry& geom);
};
#endif

View File

@@ -0,0 +1,128 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include "UnIndexMeshVisitor"
#include "GeometryArray"
#include "PrimitiveIndexors"
typedef std::vector<unsigned int> IndexList;
// this help works only for indexed primitive to unindex it
void UnIndexMeshVisitor::apply(osg::Geometry& geom)
{
// no point optimizing if we don't have enough vertices.
if (!geom.getVertexArray()) return;
// check for the existence of surface primitives
unsigned int numIndexedPrimitives = 0;
osg::Geometry::PrimitiveSetList& primitives = geom.getPrimitiveSetList();
osg::Geometry::PrimitiveSetList::iterator itr;
for(itr=primitives.begin();
itr!=primitives.end();
++itr)
{
osg::PrimitiveSet::Type type = (*itr)->getType();
if ((type == osg::PrimitiveSet::DrawElementsUBytePrimitiveType
|| type == osg::PrimitiveSet::DrawElementsUShortPrimitiveType
|| type == osg::PrimitiveSet::DrawElementsUIntPrimitiveType))
numIndexedPrimitives++;
}
// no polygons or no indexed primitive, nothing to do
if (!numIndexedPrimitives) {
return;
}
// we dont manage lines
GeometryArrayList arraySrc(geom);
GeometryArrayList arrayList = arraySrc.cloneType();
osg::Geometry::PrimitiveSetList newPrimitives;
for(itr=primitives.begin();
itr!=primitives.end();
++itr)
{
osg::PrimitiveSet::Mode mode = (osg::PrimitiveSet::Mode)(*itr)->getMode();
switch(mode) {
// manage triangles
case(osg::PrimitiveSet::TRIANGLES):
case(osg::PrimitiveSet::TRIANGLE_STRIP):
case(osg::PrimitiveSet::TRIANGLE_FAN):
case(osg::PrimitiveSet::QUADS):
case(osg::PrimitiveSet::QUAD_STRIP):
case(osg::PrimitiveSet::POLYGON):
{
// for each geometry list indexes of vertexes
// to makes triangles
TriangleIndexor triangleIndexList;
(*itr)->accept(triangleIndexList);
unsigned int index = arrayList.size();
// now copy each vertex to new array, like a draw array
arraySrc.append(triangleIndexList._indices, arrayList);
newPrimitives.push_back(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
index,
triangleIndexList._indices.size()));
}
break;
// manage lines
case(osg::PrimitiveSet::LINES):
case(osg::PrimitiveSet::LINE_STRIP):
case(osg::PrimitiveSet::LINE_LOOP):
{
EdgeIndexor edgesIndexList;
(*itr)->accept(edgesIndexList);
unsigned int index = arrayList.size();
// now copy each vertex to new array, like a draw array
arraySrc.append(edgesIndexList._indices, arrayList);
newPrimitives.push_back(new osg::DrawArrays(osg::PrimitiveSet::LINES,
index,
edgesIndexList._indices.size()));
}
break;
case(osg::PrimitiveSet::POINTS):
{
PointIndexor pointsIndexList;
(*itr)->accept(pointsIndexList);
unsigned int index = arrayList.size();
// now copy each vertex to new array, like a draw array
arraySrc.append(pointsIndexList._indices, arrayList);
newPrimitives.push_back(new osg::DrawArrays(osg::PrimitiveSet::POINTS,
index,
pointsIndexList._indices.size()));
}
break;
default:
break;
}
}
arrayList.setToGeometry(geom);
geom.setPrimitiveSetList(newPrimitives);
setProcessed(&geom);
}

View File

@@ -0,0 +1,84 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef WIREFRAME_VISITOR
#define WIREFRAME_VISITOR
#include <set>
#include <osg/ValueObject>
#include <osg/Geode>
#include <osg/Geometry>
#include "GeometryUniqueVisitor"
#include "PrimitiveIndexors"
class WireframeVisitor : public GeometryUniqueVisitor
{
public:
WireframeVisitor(bool inlined=false):
GeometryUniqueVisitor("WireframeVisitor"),
_inlined(inlined)
{}
void apply(osg::Node& node) {
handleStateSet(node);
traverse(node);
}
void apply(osg::Geode& geode) {
handleStateSet(geode);
for (unsigned int i = 0; i < geode.getNumDrawables(); i++) {
apply(*geode.getDrawable(i));
}
}
void apply(osg::Drawable& drawable) {
osg::Geometry* geometry = drawable.asGeometry();
if (!geometry) {
return;
}
apply(*geometry);
}
void apply(osg::Geometry& geometry) {
if(_processed.find(&geometry) == _processed.end()) {
const unsigned int nbSourcePrimitives = geometry.getNumPrimitiveSets();
for(unsigned int i = 0 ; i < nbSourcePrimitives ; ++ i) {
osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
EdgeIndexor edges;
primitive->accept(edges);
if(!edges._indices.empty()) {
osg::DrawElementsUInt* wireframe = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES,
edges._indices.begin(),
edges._indices.end());
wireframe->setUserValue("wireframe", true);
geometry.getPrimitiveSetList().push_back(wireframe);
}
}
_processed.insert(&geometry);
}
}
void handleStateSet(osg::Node& node) {
if(!_inlined) {
node.setStateSet(0);
}
}
std::set<osg::Geometry*> _processed;
bool _inlined;
};
#endif

View File

@@ -0,0 +1,346 @@
//-----------------------------------------------------------------------------
// This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
// Optimization" algorithm as described here:
// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
//
// This code was authored and released into the public domain by
// Adrian Stone (stone@gameangst.com).
//
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
// LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <assert.h>
#include <math.h>
#include <vector>
#include <limits>
#include <algorithm>
namespace Forsyth
{
//-----------------------------------------------------------------------------
// OptimizeFaces
//-----------------------------------------------------------------------------
// Parameters:
// indexList
// input index list
// indexCount
// the number of indices in the list
// vertexCount
// the largest index value in indexList
// newIndexList
// a pointer to a preallocated buffer the same size as indexList to
// hold the optimized index list
// lruCacheSize
// the size of the simulated post-transform cache (max:64)
//-----------------------------------------------------------------------------
void OptimizeFaces(const unsigned int* indexList, unsigned int indexCount, unsigned int vertexCount, unsigned int* newIndexList, unsigned int lruCacheSize);
namespace
{
// code for computing vertex score was taken, as much as possible
// directly from the original publication.
float ComputeVertexCacheScore(int cachePosition, int vertexCacheSize)
{
const float FindVertexScore_CacheDecayPower = 1.5f;
const float FindVertexScore_LastTriScore = 0.75f;
float score = 0.0f;
if ( cachePosition < 0 )
{
// Vertex is not in FIFO cache - no score.
}
else
{
if ( cachePosition < 3 )
{
// This vertex was used in the last triangle,
// so it has a fixed score, whichever of the three
// it's in. Otherwise, you can get very different
// answers depending on whether you add
// the triangle 1,2,3 or 3,1,2 - which is silly.
score = FindVertexScore_LastTriScore;
}
else
{
assert ( cachePosition < vertexCacheSize );
// Points for being high in the cache.
const float scaler = 1.0f / ( vertexCacheSize - 3 );
score = 1.0f - ( cachePosition - 3 ) * scaler;
score = powf ( score, FindVertexScore_CacheDecayPower );
}
}
return score;
}
float ComputeVertexValenceScore(unsigned int numActiveFaces)
{
const float FindVertexScore_ValenceBoostScale = 2.0f;
const float FindVertexScore_ValenceBoostPower = 0.5f;
float score = 0.f;
// Bonus points for having a low number of tris still to
// use the vert, so we get rid of lone verts quickly.
float valenceBoost = powf ( static_cast<float>(numActiveFaces),
-FindVertexScore_ValenceBoostPower );
score += FindVertexScore_ValenceBoostScale * valenceBoost;
return score;
}
const int kMaxVertexCacheSize = 64;
const unsigned int kMaxPrecomputedVertexValenceScores = 64;
float s_vertexCacheScores[kMaxVertexCacheSize+1][kMaxVertexCacheSize];
float s_vertexValenceScores[kMaxPrecomputedVertexValenceScores];
bool ComputeVertexScores()
{
for (int cacheSize=0; cacheSize<=kMaxVertexCacheSize; ++cacheSize)
{
for (int cachePos=0; cachePos<cacheSize; ++cachePos)
{
s_vertexCacheScores[cacheSize][cachePos] = ComputeVertexCacheScore(cachePos, cacheSize);
}
}
for (unsigned int valence=0; valence<kMaxPrecomputedVertexValenceScores; ++valence)
{
s_vertexValenceScores[valence] = ComputeVertexValenceScore(valence);
}
return true;
}
bool s_vertexScoresComputed = ComputeVertexScores();
// inline float FindVertexCacheScore(unsigned int cachePosition, unsigned int maxSizeVertexCache)
// {
// return s_vertexCacheScores[maxSizeVertexCache][cachePosition];
// }
// inline float FindVertexValenceScore(unsigned int numActiveTris)
// {
// return s_vertexValenceScores[numActiveTris];
// }
float FindVertexScore(unsigned int numActiveFaces, unsigned int cachePosition, unsigned int vertexCacheSize)
{
assert(s_vertexScoresComputed);
if ( numActiveFaces == 0 )
{
// No tri needs this vertex!
return -1.0f;
}
float score = 0.f;
if (cachePosition < vertexCacheSize)
{
score += s_vertexCacheScores[vertexCacheSize][cachePosition];
}
if (numActiveFaces < kMaxPrecomputedVertexValenceScores)
{
score += s_vertexValenceScores[numActiveFaces];
}
else
{
score += ComputeVertexValenceScore(numActiveFaces);
}
return score;
}
struct OptimizeVertexData
{
float score;
unsigned int activeFaceListStart;
unsigned int activeFaceListSize;
unsigned int cachePos0;
unsigned int cachePos1;
OptimizeVertexData() : score(0.f), activeFaceListStart(0), activeFaceListSize(0), cachePos0(0), cachePos1(0) { }
};
}
void OptimizeFaces(const unsigned int* indexList, unsigned int indexCount, unsigned int vertexCount, unsigned int* newIndexList, unsigned int lruCacheSize)
{
std::vector<OptimizeVertexData> vertexDataList;
vertexDataList.resize(vertexCount);
// compute face count per vertex
for (unsigned int i=0; i<indexCount; ++i)
{
unsigned int index = indexList[i];
assert(index < vertexCount);
OptimizeVertexData& vertexData = vertexDataList[index];
vertexData.activeFaceListSize++;
}
std::vector<unsigned int> activeFaceList;
const unsigned int kEvictedCacheIndex = std::numeric_limits<unsigned int>::max();
{
// allocate face list per vertex
unsigned int curActiveFaceListPos = 0;
for (unsigned int i=0; i<vertexCount; ++i)
{
OptimizeVertexData& vertexData = vertexDataList[i];
vertexData.cachePos0 = kEvictedCacheIndex;
vertexData.cachePos1 = kEvictedCacheIndex;
vertexData.activeFaceListStart = curActiveFaceListPos;
curActiveFaceListPos += vertexData.activeFaceListSize;
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos0, lruCacheSize);
vertexData.activeFaceListSize = 0;
}
activeFaceList.resize(curActiveFaceListPos);
}
// fill out face list per vertex
for (unsigned int i=0; i<indexCount; i+=3)
{
for (unsigned int j=0; j<3; ++j)
{
unsigned int index = indexList[i+j];
OptimizeVertexData& vertexData = vertexDataList[index];
activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize] = i;
vertexData.activeFaceListSize++;
}
}
std::vector<unsigned int> processedFaceList;
processedFaceList.resize(indexCount);
unsigned int vertexCacheBuffer[(kMaxVertexCacheSize+3)*2];
unsigned int* cache0 = vertexCacheBuffer;
unsigned int* cache1 = vertexCacheBuffer+(kMaxVertexCacheSize+3);
unsigned int entriesInCache0 = 0;
unsigned int bestFace = 0;
float bestScore = -1.f;
const float maxValenceScore = FindVertexScore(1, kEvictedCacheIndex, lruCacheSize) * 3.f;
for (unsigned int i = 0; i < indexCount; i += 3)
{
if (bestScore < 0.f)
{
// no verts in the cache are used by any unprocessed faces so
// search all unprocessed faces for a new starting point
for (unsigned int j = 0; j < indexCount; j += 3)
{
if (processedFaceList[j] == 0)
{
unsigned int face = j;
float faceScore = 0.f;
for (unsigned int k=0; k<3; ++k)
{
unsigned int index = indexList[face+k];
OptimizeVertexData& vertexData = vertexDataList[index];
assert(vertexData.activeFaceListSize > 0);
assert(vertexData.cachePos0 >= lruCacheSize);
faceScore += vertexData.score;
}
if (faceScore > bestScore)
{
bestScore = faceScore;
bestFace = face;
assert(bestScore <= maxValenceScore);
if (bestScore >= maxValenceScore)
{
break;
}
}
}
}
assert(bestScore >= 0.f);
}
processedFaceList[bestFace] = 1;
unsigned int entriesInCache1 = 0;
// add bestFace to LRU cache and to newIndexList
for (unsigned int v = 0; v < 3; ++v)
{
unsigned int index = indexList[bestFace+v];
newIndexList[i+v] = index;
OptimizeVertexData& vertexData = vertexDataList[index];
if (vertexData.cachePos1 >= entriesInCache1)
{
vertexData.cachePos1 = entriesInCache1;
cache1[entriesInCache1++] = index;
if (vertexData.activeFaceListSize == 1)
{
--vertexData.activeFaceListSize;
continue;
}
}
assert(vertexData.activeFaceListSize > 0);
unsigned int* begin = &activeFaceList[vertexData.activeFaceListStart];
unsigned int* end = &activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize];
unsigned int* it = std::find(begin, end, bestFace);
assert(it != end);
std::swap(*it, *(end-1));
--vertexData.activeFaceListSize;
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
}
// move the rest of the old verts in the cache down and compute their new scores
for (unsigned int c0 = 0; c0 < entriesInCache0; ++c0)
{
unsigned int index = cache0[c0];
OptimizeVertexData& vertexData = vertexDataList[index];
if (vertexData.cachePos1 >= entriesInCache1)
{
vertexData.cachePos1 = entriesInCache1;
cache1[entriesInCache1++] = index;
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
}
}
// find the best scoring triangle in the current cache (including up to 3 that were just evicted)
bestScore = -1.f;
for (unsigned int c1 = 0; c1 < entriesInCache1; ++c1)
{
unsigned int index = cache1[c1];
OptimizeVertexData& vertexData = vertexDataList[index];
vertexData.cachePos0 = vertexData.cachePos1;
vertexData.cachePos1 = kEvictedCacheIndex;
for (unsigned int j=0; j<vertexData.activeFaceListSize; ++j)
{
unsigned int face = activeFaceList[vertexData.activeFaceListStart+j];
float faceScore = 0.f;
for (unsigned int v=0; v<3; v++)
{
unsigned int faceIndex = indexList[face+v];
OptimizeVertexData& faceVertexData = vertexDataList[faceIndex];
faceScore += faceVertexData.score;
}
if (faceScore > bestScore)
{
bestScore = faceScore;
bestFace = face;
}
}
}
std::swap(cache0, cache1);
entriesInCache0 = std::min(entriesInCache1, lruCacheSize);
}
}
} // namespace Forsyth

View File

@@ -0,0 +1,46 @@
//-----------------------------------------------------------------------------
// This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
// Optimization" algorithm as described here:
// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
//
// This code was authored and released into the public domain by
// Adrian Stone (stone@gameangst.com).
//
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
// LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef __FORSYTH_TRIANGLE_REORDER__
#define __FORSYTH_TRIANGLE_REORDER__
namespace Forsyth
{
//-----------------------------------------------------------------------------
// OptimizeFaces
//-----------------------------------------------------------------------------
// Parameters:
// indexList
// input index list
// indexCount
// the number of indices in the list
// vertexCount
// the largest index value in indexList
// newIndexList
// a pointer to a preallocated buffer the same size as indexList to
// hold the optimized index list
// lruCacheSize
// the size of the simulated post-transform cache (max:64)
//-----------------------------------------------------------------------------
void OptimizeFaces(const unsigned int* indexList,
unsigned int indexCount,
unsigned int vertexCount,
unsigned int* newIndexList,
unsigned int lruCacheSize);
} // namespace Forsyth
#endif // __FORSYTH_TRIANGLE_REORDER__

View File

@@ -0,0 +1,574 @@
#ifndef GLES_UTIL
#define GLES_UTIL
#include <cassert>
#include <map>
#include <vector>
#include <algorithm>
#include <osg/Array>
#include <osg/ref_ptr>
#include <osgUtil/MeshOptimizers>
#include <osg/TriangleIndexFunctor>
#include "TriangleLinePointIndexFunctor"
#include "StatLogger"
#include "forsythtriangleorderoptimizer.h"
namespace glesUtil {
using namespace std;
using namespace osg;
typedef std::vector<unsigned int> IndexList;
/////////// Post-transform
// A list of triangles that use a vertex is associated with the Vertex
// structure in another array.
struct Vertex
{
int trisUsing;
size_t triList; // index to start of triangle storage
Vertex()
: trisUsing(0), triList(0)
{
}
};
typedef vector<Vertex> VertexList;
struct Triangle
{
unsigned verts[3];
};
typedef vector<Triangle> TriangleList;
struct TriangleCounterOperator
{
VertexList* vertices;
int triangleCount;
TriangleCounterOperator() : vertices(0), triangleCount(0) {}
void doVertex(unsigned p)
{
if (vertices->size() <= p)
vertices->resize(p + 1);
(*vertices)[p].trisUsing++;
}
void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
{
if (p1 == p2 || p2 == p3 || p1 == p3)
return;
doVertex(p1);
doVertex(p2);
doVertex(p3);
triangleCount++;
}
};
struct TriangleCounter : public TriangleIndexFunctor<TriangleCounterOperator>
{
TriangleCounter(vector<Vertex>* vertices_)
{
vertices = vertices_;
}
};
// Initialize the vertex triangle lists and the triangle data structures
struct TriangleAddOperator
{
VertexList* vertices;
TriangleList* triangles;
int triIdx;
TriangleAddOperator() : vertices(0), triIdx(0) {}
void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
{
if (p1 == p2 || p2 == p3 || p1 == p3)
return;
(*triangles)[triIdx].verts[0] = p1;
(*triangles)[triIdx].verts[1] = p2;
(*triangles)[triIdx].verts[2] = p3;
triIdx++;
}
};
struct TriangleAdder : public TriangleIndexFunctor<TriangleAddOperator>
{
TriangleAdder(VertexList* vertices_, TriangleList* triangles_)
{
vertices = vertices_;
triangles = triangles_;
}
};
struct is_not_soup
{
is_not_soup(const VertexList& vertices) : _vertices(vertices) {}
bool operator()(const Triangle &t)
{
return _vertices[t.verts[0]].trisUsing > 1 ||
_vertices[t.verts[1]].trisUsing > 1 ||
_vertices[t.verts[2]].trisUsing > 1;
}
VertexList _vertices;
};
class VertexCacheVisitor : osgUtil::VertexCacheVisitor
{
public:
void optimizeVertices(Geometry& geom)
{
StatLogger logger("glesUtil::VertexCacheVisitor::optimizeVertices(" + geom.getName() + ")");
Array* vertArray = geom.getVertexArray();
if (!vertArray)
return;
unsigned vertArraySize = vertArray->getNumElements();
// If all the vertices fit in the cache, there's no point in
// doing this optimization.
if (vertArraySize <= 16)
return;
osg::ref_ptr<osg::Geometry> dummy = new osg::Geometry;
osg::Geometry::PrimitiveSetList newPrims;
for (int ii = geom.getNumPrimitiveSets() - 1 ; ii >= 0 ; -- ii) {
osg::PrimitiveSet* primitive = geom.getPrimitiveSet(ii);
if(!primitive || !primitive->getNumIndices()) {
continue;
}
// Collect all 'surface' primitives in the dummy geometry
if(primitive->getMode() >= PrimitiveSet::TRIANGLES && primitive->getDrawElements()) {
dummy->addPrimitiveSet(primitive);
}
else {
newPrims.push_back(primitive);
}
}
if(!dummy->getNumPrimitiveSets()) {
return;
}
vector<unsigned> newVertList;
doVertexOptimization(*dummy, newVertList);
osg::DrawElementsUInt* elements = new DrawElementsUInt(GL_TRIANGLES, newVertList.begin(), newVertList.end());
if (geom.getUseVertexBufferObjects()) {
elements->setElementBufferObject(new ElementBufferObject);
}
newPrims.insert(newPrims.begin(), elements);
geom.setPrimitiveSetList(newPrims);
geom.dirtyDisplayList();
}
void doVertexOptimization(Geometry& geom, vector<unsigned>& vertDrawList)
{
Geometry::PrimitiveSetList& primSets = geom.getPrimitiveSetList();
// lists for all the vertices and triangles
VertexList vertices;
TriangleList triangles;
TriangleCounter triCounter(&vertices);
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
end = primSets.end();
itr != end;
++itr)
(*itr)->accept(triCounter);
triangles.resize(triCounter.triangleCount);
// Get total of triangles used by all the vertices
size_t vertTrisSize = 0;
for (VertexList::iterator itr = vertices.begin(), end = vertices.end();
itr != end;
++itr)
{
itr->triList = vertTrisSize;
vertTrisSize += itr->trisUsing;
}
// Store for lists of triangles (indices) used by the vertices
vector<unsigned> vertTriListStore(vertTrisSize);
TriangleAdder triAdder(&vertices, &triangles);
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
end = primSets.end();
itr != end;
++itr)
(*itr)->accept(triAdder);
// discard triangle soup as it cannot be cache-optimized
TriangleList::iterator soupIterator = std::partition(triangles.begin(), triangles.end(),
is_not_soup(vertices));
TriangleList soup(soupIterator, triangles.end());
triangles.erase(soupIterator, triangles.end());
OSG_INFO << "Info: glesUtil::VertexCacheVisitor::doVertexOptimization(..) found "
<< soup.size() << " soup triangles" << std::endl << std::flush;
std::vector<unsigned int> indices;
for(TriangleList::const_iterator it_tri = triangles.begin() ; it_tri != triangles.end() ; ++ it_tri) {
indices.push_back(it_tri->verts[0]);
indices.push_back(it_tri->verts[1]);
indices.push_back(it_tri->verts[2]);
}
// call bgfx forsyth-algorithm implementation
vertDrawList.resize(indices.size());
Forsyth::OptimizeFaces(&indices[0], indices.size(), vertices.size(), &vertDrawList[0], 16);
for(TriangleList::iterator itr = soup.begin() ; itr != soup.end() ; ++ itr) {
vertDrawList.push_back(itr->verts[0]);
vertDrawList.push_back(itr->verts[1]);
vertDrawList.push_back(itr->verts[2]);
}
}
}; // Post-transform
// A helper class that gathers up all the attribute arrays of an
// osg::Geometry object that are BIND_PER_VERTEX and runs an
// ArrayVisitor on them.
struct GeometryArrayGatherer
{
typedef std::vector<osg::Array*> ArrayList;
GeometryArrayGatherer(osg::Geometry& geometry) {
add(geometry.getVertexArray());
add(geometry.getNormalArray());
add(geometry.getColorArray());
add(geometry.getSecondaryColorArray());
add(geometry.getFogCoordArray());
for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) {
add(geometry.getTexCoordArray(i));
}
for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) {
add(geometry.getVertexAttribArray(i));
}
}
void add(osg::Array* array) {
if (array) {
_arrayList.push_back(array);
}
}
void accept(osg::ArrayVisitor& av) {
for(ArrayList::iterator itr = _arrayList.begin() ; itr != _arrayList.end(); ++ itr) {
(*itr)->accept(av);
}
}
ArrayList _arrayList;
};
// Compact the vertex attribute arrays. Also stolen from TriStripVisitor
class RemapArray : public osg::ArrayVisitor
{
public:
RemapArray(const IndexList& remapping) : _remapping(remapping)
{}
const IndexList& _remapping;
template<class T>
inline void remap(T& array) {
for(unsigned int i = 0 ; i < _remapping.size() ; ++ i) {
if(i != _remapping[i]) {
array[i] = array[_remapping[i]];
}
}
array.erase(array.begin() + _remapping.size(),
array.end());
}
virtual void apply(osg::Array&) {}
virtual void apply(osg::ByteArray& array) { remap(array); }
virtual void apply(osg::ShortArray& array) { remap(array); }
virtual void apply(osg::IntArray& array) { remap(array); }
virtual void apply(osg::UByteArray& array) { remap(array); }
virtual void apply(osg::UShortArray& array) { remap(array); }
virtual void apply(osg::UIntArray& array) { remap(array); }
virtual void apply(osg::FloatArray& array) { remap(array); }
virtual void apply(osg::DoubleArray& array) { remap(array); }
virtual void apply(osg::Vec2dArray& array) { remap(array); }
virtual void apply(osg::Vec3dArray& array) { remap(array); }
virtual void apply(osg::Vec4dArray& array) { remap(array); }
virtual void apply(osg::Vec2Array& array) { remap(array); }
virtual void apply(osg::Vec3Array& array) { remap(array); }
virtual void apply(osg::Vec4Array& array) { remap(array); }
virtual void apply(osg::Vec2iArray& array) { remap(array); }
virtual void apply(osg::Vec3iArray& array) { remap(array); }
virtual void apply(osg::Vec4iArray& array) { remap(array); }
virtual void apply(osg::Vec2uiArray& array) { remap(array); }
virtual void apply(osg::Vec3uiArray& array) { remap(array); }
virtual void apply(osg::Vec4uiArray& array) { remap(array); }
virtual void apply(osg::Vec2sArray& array) { remap(array); }
virtual void apply(osg::Vec3sArray& array) { remap(array); }
virtual void apply(osg::Vec4sArray& array) { remap(array); }
virtual void apply(osg::Vec2usArray& array) { remap(array); }
virtual void apply(osg::Vec3usArray& array) { remap(array); }
virtual void apply(osg::Vec4usArray& array) { remap(array); }
virtual void apply(osg::Vec2bArray& array) { remap(array); }
virtual void apply(osg::Vec3bArray& array) { remap(array); }
virtual void apply(osg::Vec4bArray& array) { remap(array); }
virtual void apply(osg::Vec4ubArray& array) { remap(array); }
virtual void apply(osg::Vec3ubArray& array) { remap(array); }
virtual void apply(osg::Vec2ubArray& array) { remap(array); }
virtual void apply(osg::MatrixfArray& array) { remap(array); }
protected:
RemapArray& operator= (const RemapArray&) { return *this; }
};
// Compare vertices in a mesh using all their attributes. The vertices
// are identified by their index. Extracted from TriStripVisitor.cpp
struct VertexAttribComparitor : public GeometryArrayGatherer
{
VertexAttribComparitor(osg::Geometry& geometry) : GeometryArrayGatherer(geometry)
{}
bool operator() (unsigned int lhs, unsigned int rhs) const {
for(ArrayList::const_iterator itr = _arrayList.begin(); itr != _arrayList.end(); ++ itr) {
int compare = (*itr)->compare(lhs, rhs);
if (compare == -1) { return true; }
if (compare == 1) { return false; }
}
return false;
}
int compare(unsigned int lhs, unsigned int rhs) {
for(ArrayList::iterator itr = _arrayList.begin(); itr != _arrayList.end(); ++ itr) {
int compare = (*itr)->compare(lhs, rhs);
if (compare == -1) { return -1; }
if (compare == 1) { return 1; }
}
return 0;
}
protected:
VertexAttribComparitor& operator= (const VertexAttribComparitor&) { return *this; }
};
// Move the values in an array to new positions, based on the
// remapping table. remapping[i] contains element i's new position, if
// any. Unlike RemapArray in TriStripVisitor, this code doesn't
// assume that elements only move downward in the array.
class Remapper : public osg::ArrayVisitor
{
public:
static const unsigned invalidIndex;
Remapper(const vector<unsigned>& remapping)
: _remapping(remapping), _newsize(0)
{
for (vector<unsigned>::const_iterator itr = _remapping.begin(),
end = _remapping.end();
itr != end;
++itr)
if (*itr != invalidIndex)
++_newsize;
}
const vector<unsigned>& _remapping;
size_t _newsize;
template<class T>
inline void remap(T& array)
{
ref_ptr<T> newarray = new T(_newsize);
T* newptr = newarray.get();
for (size_t i = 0; i < array.size(); ++i)
if (_remapping[i] != invalidIndex)
(*newptr)[_remapping[i]] = array[i];
array.swap(*newptr);
}
virtual void apply(osg::Array&) {}
virtual void apply(osg::ByteArray& array) { remap(array); }
virtual void apply(osg::ShortArray& array) { remap(array); }
virtual void apply(osg::IntArray& array) { remap(array); }
virtual void apply(osg::UByteArray& array) { remap(array); }
virtual void apply(osg::UShortArray& array) { remap(array); }
virtual void apply(osg::UIntArray& array) { remap(array); }
virtual void apply(osg::FloatArray& array) { remap(array); }
virtual void apply(osg::DoubleArray& array) { remap(array); }
virtual void apply(osg::Vec2Array& array) { remap(array); }
virtual void apply(osg::Vec3Array& array) { remap(array); }
virtual void apply(osg::Vec4Array& array) { remap(array); }
virtual void apply(osg::Vec4ubArray& array) { remap(array); }
virtual void apply(osg::Vec2bArray& array) { remap(array); }
virtual void apply(osg::Vec3bArray& array) { remap(array); }
virtual void apply(osg::Vec4bArray& array) { remap(array); }
virtual void apply(osg::Vec2sArray& array) { remap(array); }
virtual void apply(osg::Vec3sArray& array) { remap(array); }
virtual void apply(osg::Vec4sArray& array) { remap(array); }
virtual void apply(osg::Vec2dArray& array) { remap(array); }
virtual void apply(osg::Vec3dArray& array) { remap(array); }
virtual void apply(osg::Vec4dArray& array) { remap(array); }
virtual void apply(osg::MatrixfArray& array) { remap(array); }
};
// Record the order in which vertices in a Geometry are used.
struct VertexReorderOperator
{
unsigned seq;
std::vector<unsigned int> remap;
VertexReorderOperator() : seq(0)
{
}
void inline doVertex(unsigned v)
{
if (remap[v] == glesUtil::Remapper::invalidIndex) {
remap[v] = seq ++;
}
}
void operator()(unsigned p1, unsigned p2, unsigned p3)
{
doVertex(p1);
doVertex(p2);
doVertex(p3);
}
void operator()(unsigned p1, unsigned p2)
{
doVertex(p1);
doVertex(p2);
}
void operator()(unsigned p1)
{
doVertex(p1);
}
};
struct VertexReorder : public TriangleLinePointIndexFunctor<glesUtil::VertexReorderOperator>
{
VertexReorder(unsigned numVerts)
{
remap.resize(numVerts, glesUtil::Remapper::invalidIndex);
}
};
template<typename DE>
inline void reorderDrawElements(DE& drawElements,
const vector<unsigned>& reorder)
{
for (typename DE::iterator itr = drawElements.begin(), end = drawElements.end();
itr != end;
++itr)
{
*itr = static_cast<typename DE::value_type>(reorder[*itr]);
}
}
class VertexAccessOrderVisitor : osgUtil::VertexAccessOrderVisitor
{
struct OrderByPrimitiveMode
{
inline bool operator() (const osg::ref_ptr<osg::PrimitiveSet>& prim1, const osg::ref_ptr<osg::PrimitiveSet>& prim2)
{
if(prim1.get() && prim2.get()) {
return prim1->getMode() >= prim2->getMode();
}
else if(prim1.get()) {
return true;
}
return false;
}
} order_by_primitive_mode;
public:
void optimizeOrder(Geometry& geom)
{
StatLogger logger("glesUtil::VertexAccessOrderVisitor::optimizeOrder(" + geom.getName() + ")");
Array* vertArray = geom.getVertexArray();
if (!vertArray || !vertArray->getNumElements())
return;
Geometry::PrimitiveSetList& primSets = geom.getPrimitiveSetList();
// sort primitives: first triangles, then lines and finally points
std::sort(primSets.begin(), primSets.end(), order_by_primitive_mode);
glesUtil::VertexReorder vr(vertArray->getNumElements());
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
end = primSets.end();
itr != end;
++itr)
{
PrimitiveSet* ps = itr->get();
PrimitiveSet::Type type = ps->getType();
if (type != PrimitiveSet::DrawElementsUBytePrimitiveType
&& type != PrimitiveSet::DrawElementsUShortPrimitiveType
&& type != PrimitiveSet::DrawElementsUIntPrimitiveType)
return;
ps->accept(vr);
}
// search for UVs array shared only within the geometry
osgUtil::SharedArrayOptimizer deduplicator;
deduplicator.findDuplicatedUVs(geom);
// duplicate shared arrays as it isn't safe to rearrange vertices when arrays are shared.
if (geom.containsSharedArrays()) geom.duplicateSharedArrays();
GeometryArrayGatherer gatherer(geom);
Remapper remapper(vr.remap);
gatherer.accept(remapper);
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
end = primSets.end();
itr != end;
++itr)
{
PrimitiveSet* ps = itr->get();
switch (ps->getType())
{
case PrimitiveSet::DrawElementsUBytePrimitiveType:
reorderDrawElements(*static_cast<DrawElementsUByte*>(ps), vr.remap);
break;
case PrimitiveSet::DrawElementsUShortPrimitiveType:
reorderDrawElements(*static_cast<DrawElementsUShort*>(ps), vr.remap);
break;
case PrimitiveSet::DrawElementsUIntPrimitiveType:
reorderDrawElements(*static_cast<DrawElementsUInt*>(ps), vr.remap);
break;
default:
break;
}
}
// deduplicate UVs array that were only shared within the geometry
deduplicator.deduplicateUVs(geom);
geom.dirtyDisplayList();
}
};
} // glesUtil namespace
#endif

View File

@@ -0,0 +1,117 @@
/* -*-c++-*-
* Copyright (C) 2010 Cedric Pinson <cedric.pinson@plopbyte.net>
*
* 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 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.
*
* Authors:
* Cedric Pinson <cedric.pinson@plopbyte.net>
*/
#include <osg/TriangleFunctor>
struct DrawArrayLenghtAdaptator
{
unsigned int _normalArraySize;
const Vec3* _normalArrayPtr;
unsigned int _colorArraySize;
const Vec4* _colorArrayPtr;
std::vector<std::pair<unsigned int, const osg::Vec2*> > _uvs;
virtual void setNormalArray(unsigned int count,const Vec3* vertices) {
_normalArraySize = count;
_normalArrayPtr = vertices;
}
virtual void setNormalArray(unsigned int count,const Vec4* colors) {
_colorArraySize = count;
_colorArrayPtr = colors;
}
virtual void setTexCoordArray(int unit, unsigned int count,const Vec2* uvs) {
if (_uvs.size() <= unit)
_uvs.resize(unit+1);
_uvs[i].first = count;
_uvs[i].second = uvs;
}
};
struct AdaptDraw : public osg::TriangleFunctor<DrawArrayLenghtAdaptator>
{
virtual void drawArrays(GLenum mode,GLint first,GLsizei count)
{
if (_vertexArrayPtr==0 || count==0) return;
switch(mode)
{
case(GL_TRIANGLES):
{
unsigned int last = first+count;
for(unsigned int current = first; current<last ; current+=3) {
this->operator()(current, current+1, current+2,_treatVertexDataAsTemporary);
}
break;
}
case(GL_TRIANGLE_STRIP):
{
unsigned int current = first;
for(GLsizei i=2;i<count;++i, current+=1)
{
if ((i%2)) this->operator()(current, current+2, current+1,_treatVertexDataAsTemporary);
else this->operator()(current, current+1, current+2,_treatVertexDataAsTemporary);
}
break;
}
case(GL_QUADS):
{
unsigned int current = first;
for(GLsizei i=3;i<count;i+=4,current+=4)
{
this->operator()(current, current+1, current+2,_treatVertexDataAsTemporary);
this->operator()(current, current+2, current+3,_treatVertexDataAsTemporary);
}
break;
}
case(GL_QUAD_STRIP):
{
unsigned int current = first;
for(GLsizei i=3;i<count;i+=2,current+=2)
{
this->operator()(current, current+1, current+2,_treatVertexDataAsTemporary);
this->operator()(current+1, current+3, current+2,_treatVertexDataAsTemporary);
}
break;
}
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
case(GL_TRIANGLE_FAN):
{
unsigned int current = first + 1;
for(GLsizei i=2;i<count;++i,++current)
{
this->operator()(first),current, current+1,_treatVertexDataAsTemporary);
}
break;
}
case(GL_POINTS):
case(GL_LINES):
case(GL_LINE_STRIP):
case(GL_LINE_LOOP):
default:
// can't be converted into to triangles.
break;
}
}
};

View File

@@ -0,0 +1,12 @@
/* -*-c++-*-
* Copyright (C) 2011 Cedric Pinson <cedric.pinson@plopbyte.com>
*/
#ifndef JSON_ANIMATION_H
#define JSON_ANIMATION_H
#include <osgAnimation/Animation>
#include <osgAnimation/UpdateMatrixTransform>
JSONObject* createJSONAnimation(osgAnimation::Animation* anim);
JSONObject* createJSONUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform& acb);
#endif

View File

@@ -0,0 +1,203 @@
/* -*-c++-*-
* Copyright (C) 2011 Cedric Pinson <cedric.pinson@plopbyte.com>
*/
#include <osgAnimation/Animation>
#include <osgAnimation/Channel>
#include <osgAnimation/Sampler>
#include <osgAnimation/UpdateMatrixTransform>
#include <osgAnimation/StackedTranslateElement>
#include <osgAnimation/StackedQuaternionElement>
#include <osgAnimation/StackedRotateAxisElement>
#include "JSON_Objects"
static bool addJSONChannelVec3(osgAnimation::Vec3LinearChannel* channel, JSONObject& anim)
{
if (channel && channel->getSampler()) {
osg::ref_ptr<JSONObject> json = new JSONObject;
json->getMaps()["Name"] = new JSONValue<std::string>(channel->getName());
json->getMaps()["TargetName"] = new JSONValue<std::string>(channel->getTargetName());
osgAnimation::Vec3KeyframeContainer* keys = channel->getSamplerTyped()->getKeyframeContainerTyped();
JSONKeyframes* jsonKeys = new JSONKeyframes();
//if (!keys->getName().empty()) {
// jsonKeys->getMaps()["Name"] = new JSONValue<std::string>(keys->getName());
//}
for (unsigned int i = 0; i < keys->size(); i++) {
JSONVec4Array* kf = new JSONVec4Array(osg::Vec4((*keys)[i].getTime(),
(*keys)[i].getValue()[0],
(*keys)[i].getValue()[1],
(*keys)[i].getValue()[2]));
jsonKeys->getArray().push_back(kf);
}
json->getMaps()["KeyFrames"] = jsonKeys;
osg::ref_ptr<JSONObject> jsonChannel = new JSONObject();
jsonChannel->getMaps()["osgAnimation.Vec3LerpChannel"] = json;
anim.getMaps()["Channels"]->asArray()->getArray().push_back(jsonChannel);
return true;
}
return false;
}
static bool addJSONChannelFloat(osgAnimation::FloatLinearChannel* channel, JSONObject& anim)
{
if (channel->getSampler()) {
osg::ref_ptr<JSONObject> json = new JSONObject;
json->getMaps()["Name"] = new JSONValue<std::string>(channel->getName());
json->getMaps()["TargetName"] = new JSONValue<std::string>(channel->getTargetName());
osgAnimation::FloatKeyframeContainer* keys = channel->getSamplerTyped()->getKeyframeContainerTyped();
JSONKeyframes* jsonKeys = new JSONKeyframes();
//if (!keys->getName().empty()) {
// jsonKeys->getMaps()["Name"] = new JSONValue<std::string>(keys->getName());
//}
for (unsigned int i = 0; i < keys->size(); i++) {
JSONVec2Array* kf = new JSONVec2Array(osg::Vec2((*keys)[i].getTime(),
(*keys)[i].getValue()));
jsonKeys->getArray().push_back(kf);
}
json->getMaps()["KeyFrames"] = jsonKeys;
osg::ref_ptr<JSONObject> jsonChannel = new JSONObject();
jsonChannel->getMaps()["osgAnimation.FloatLerpChannel"] = json;
anim.getMaps()["Channels"]->asArray()->getArray().push_back(jsonChannel);
return true;
}
return false;
}
static bool addJSONChannelQuaternion(osgAnimation::QuatSphericalLinearChannel* channel, JSONObject& anim)
{
if (channel->getSampler()) {
osg::ref_ptr<JSONObject> json = new JSONObject;
json->getMaps()["Name"] = new JSONValue<std::string>(channel->getName());
json->getMaps()["TargetName"] = new JSONValue<std::string>(channel->getTargetName());
osgAnimation::QuatKeyframeContainer* keys = channel->getSamplerTyped()->getKeyframeContainerTyped();
JSONKeyframes* jsonKeys = new JSONKeyframes();
for (unsigned int i = 0; i < keys->size(); i++) {
JSONVec5Array* kf = new JSONVec5Array(Vec5((*keys)[i].getTime(),
(*keys)[i].getValue()[0],
(*keys)[i].getValue()[1],
(*keys)[i].getValue()[2],
(*keys)[i].getValue()[3]));
jsonKeys->getArray().push_back(kf);
}
json->getMaps()["KeyFrames"] = jsonKeys;
osg::ref_ptr<JSONObject> jsonChannel = new JSONObject();
jsonChannel->getMaps()["osgAnimation.QuatSlerpChannel"] = json;
anim.getMaps()["Channels"]->asArray()->getArray().push_back(jsonChannel);
return true;
}
return false;
}
static void addJSONChannel(osgAnimation::Channel* channel, JSONObject& anim)
{
{
osgAnimation::Vec3LinearChannel* c = dynamic_cast<osgAnimation::Vec3LinearChannel*>(channel);
if (c) {
if (addJSONChannelVec3(c, anim))
return;
}
}
{
osgAnimation::FloatLinearChannel* c = dynamic_cast<osgAnimation::FloatLinearChannel*>(channel);
if (c) {
if (addJSONChannelFloat(c, anim))
return;
}
}
{
osgAnimation::QuatSphericalLinearChannel* c = dynamic_cast<osgAnimation::QuatSphericalLinearChannel*>(channel);
if (c) {
if (addJSONChannelQuaternion(c, anim))
return;
}
}
}
JSONObject* createJSONAnimation(osgAnimation::Animation* anim)
{
osg::ref_ptr<JSONObject> json = new JSONObject;
json->getMaps()["Channels"] = new JSONArray();
json->getMaps()["Name"] = new JSONValue<std::string>(anim->getName());
for (unsigned int i = 0; i < anim->getChannels().size(); i++) {
addJSONChannel(anim->getChannels()[i].get(), *json);
}
return json.release();
}
JSONObject* createJSONUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform& acb)
{
std::string name = acb.getName();
osg::ref_ptr<JSONObject> json = new JSONObject;
json->getMaps()["Name"] = new JSONValue<std::string>(acb.getName());
osg::ref_ptr<JSONArray> jsonStackedArray = new JSONArray();
json->getMaps()["StackedTransforms"] = jsonStackedArray;
osgAnimation::StackedTransform& st = acb.getStackedTransforms();
for (unsigned int i = 0; i < st.size(); i++) {
{
osgAnimation::StackedTranslateElement* element = dynamic_cast<osgAnimation::StackedTranslateElement*>(st[i].get());
if (element) {
osg::ref_ptr<JSONObject> jsonElement = new JSONObject;
jsonElement->getMaps()["Name"] = new JSONValue<std::string>(element->getName());
jsonElement->getMaps()["Translate"] = new JSONVec3Array(element->getTranslate());
osg::ref_ptr<JSONObject> jsonElementObject = new JSONObject;
jsonElementObject->getMaps()["osgAnimation.StackedTranslate"] = jsonElement;
jsonStackedArray->getArray().push_back(jsonElementObject);
continue;
}
}
{
osgAnimation::StackedQuaternionElement* element = dynamic_cast<osgAnimation::StackedQuaternionElement*>(st[i].get());
if (element) {
osg::ref_ptr<JSONObject> jsonElement = new JSONObject;
jsonElement->getMaps()["Name"] = new JSONValue<std::string>(element->getName());
jsonElement->getMaps()["Quaternion"] = new JSONVec4Array(element->getQuaternion().asVec4());
osg::ref_ptr<JSONObject> jsonElementObject = new JSONObject;
jsonElementObject->getMaps()["osgAnimation.StackedQuaternion"] = jsonElement;
jsonStackedArray->getArray().push_back(jsonElementObject);
continue;
}
}
{
osgAnimation::StackedRotateAxisElement* element = dynamic_cast<osgAnimation::StackedRotateAxisElement*>(st[i].get());
if (element) {
osg::ref_ptr<JSONObject> jsonElement = new JSONObject;
jsonElement->getMaps()["Name"] = new JSONValue<std::string>(element->getName());
jsonElement->getMaps()["Axis"] = new JSONVec3Array(element->getAxis());
jsonElement->getMaps()["Angle"] = new JSONValue<double>(element->getAngle());
osg::ref_ptr<JSONObject> jsonElementObject = new JSONObject;
jsonElementObject->getMaps()["osgAnimation.StackedRotateAxis"] = jsonElement;
jsonStackedArray->getArray().push_back(jsonElementObject);
continue;
}
}
}
if (jsonStackedArray->getArray().empty()) {
return 0;
}
return json.release();
}

170
src/osgPlugins/osgjs/Base64 Normal file
View File

@@ -0,0 +1,170 @@
/* base64.hpp - base64 encoder/decoder implementing section 6.8 of RFC2045
Copyright (C) 2002 Ryan Petrie (ryanpetrie@netscape.net)
and released under the zlib license:
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
--------------------------------------------------------------------------
base64::encoder and base64::decoder are intended to be identical in usage
as the STL's std::copy and similar algorithms. They require as parameters
an input iterator range and an output iterator, and just as std::copy, the
iterators can be bare pointers, container iterators, or even adaptors such
as ostream_iterator.
Examples:
// encode/decode with in-place memory buffers
char source[size], dest[size*2];
base64::encode(source, source+size, dest);
base64::decode(dest, dest+size*2, source);
// encode from memory to a file
char source[size];
ofstream file("output.txt");
base64::encode((char*)source, source+size, ostream_iterator<char>(file));
// decode from file to a standard container
ifstream file("input.txt");
vector<char> dest;
base64::decode(istream_iterator<char>(file), istream_iterator<char>(),
back_inserter(dest)
);
*/
#ifndef _BASE64_HPP
#define _BASE64_HPP
#include <algorithm>
namespace base64
{
typedef unsigned uint32;
typedef unsigned char uint8;
extern const char* to_table;
extern const char* to_table_end;
extern const char* from_table;
template <class InputIterator, class OutputIterator>
void encode(const InputIterator& begin,
const InputIterator& end,
OutputIterator out, bool wrap)
{
InputIterator it = begin;
int lineSize = 0;
int bytes;
do
{
uint32 input = 0;
// get the next three bytes into "in" (and count how many we actually get)
bytes = 0;
for(; (bytes < 3) && (it != end); ++bytes, ++it)
{
input <<= 8;
input += static_cast<uint8>(*it);
}
// convert to base64
int bits = bytes*8;
while (bits > 0)
{
bits -= 6;
const uint8 index = ((bits < 0) ? input << -bits : input >> bits) & 0x3F;
*out = to_table[index];
++out;
++lineSize;
}
if (lineSize >= 76 && wrap) // ensure proper line length
{
*out = 13;
++out;
*out = 10;
++out;
lineSize = 0;
}
} while (bytes == 3);
// add pad characters if necessary
if (bytes > 0)
for(int i=bytes; i < 3; ++i)
{
*out = '=';
++out;
}
}
template <class InputIterator, class OutputIterator>
void decode(const InputIterator& begin,
const InputIterator& end,
OutputIterator out)
{
InputIterator it = begin;
int chars;
do
{
uint8 input[4] = {0, 0, 0, 0};
// get four characters
chars=0;
while((chars<4) && (it != end))
{
uint8 c = static_cast<char>(*it);
if (c == '=') break; // pad character marks the end of the stream
++it;
if (std::find(to_table, to_table_end, c) != to_table_end)
{
input[chars] = from_table[c];
chars++;
}
}
// output the binary data
if (chars >= 2)
{
*out = static_cast<uint8>((input[0] << 2) + (input[1] >> 4));
++out;
if (chars >= 3)
{
*out = static_cast<uint8>((input[1] << 4) + (input[2] >> 2));
++out;
if (chars >= 4)
{
*out = static_cast<uint8>((input[2] << 6) + input[3]);
++out;
}
}
}
} while (chars == 4);
}
} // end namespace
#endif

View File

@@ -0,0 +1,42 @@
#include "Base64"
namespace base64
{
const char _to_table[64] =
{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
const char* to_table = _to_table;
const char* to_table_end =
_to_table + sizeof(_to_table);
const char _from_table[128] =
{
-1, -1, -1, -1, -1, -1, -1, -1, // 0
-1, -1, -1, -1, -1, -1, -1, -1, // 8
-1, -1, -1, -1, -1, -1, -1, -1, // 16
-1, -1, -1, -1, -1, -1, -1, -1, // 24
-1, -1, -1, -1, -1, -1, -1, -1, // 32
-1, -1, -1, 62, -1, -1, -1, 63, // 40
52, 53, 54, 55, 56, 57, 58, 59, // 48
60, 61, -1, -1, -1, 0, -1, -1, // 56
-1, 0, 1, 2, 3, 4, 5, 6, // 64
7, 8, 9, 10, 11, 12, 13, 14, // 72
15, 16, 17, 18, 19, 20, 21, 22, // 80
23, 24, 25, -1, -1, -1, -1, -1, // 88
-1, 26, 27, 28, 29, 30, 31, 32, // 96
33, 34, 35, 36, 37, 38, 39, 40, // 104
41, 42, 43, 44, 45, 46, 47, 48, // 112
49, 50, 51, -1, -1, -1, -1, -1 // 120
};
const char* from_table = _from_table;
}

View File

@@ -0,0 +1,24 @@
SET(TARGET_SRC
Animation.cpp
Base64.cpp
JSON_Objects.cpp
ReaderWriterJSON.cpp
WriteVisitor.cpp)
SET(TARGET_H
Adaptator
Animation
Base64
CompactBufferVisitor
JSON_Objects
json_stream
WriteVisitor
)
#### end var setup ###
SET(TARGET_ADDED_LIBRARIES
osgAnimation
osgSim
osgUtil)
SETUP_PLUGIN(osgjs)

View File

@@ -0,0 +1,115 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application 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.
*
*/
#ifndef COMPACT_BUFFER_VISITOR
#define COMPACT_BUFFER_VISITOR
#include <algorithm>
////// taken from gles/GeometryUniqueVisitor
#include <osg/NodeVisitor>
#include <osg/Geometry>
#include <osg/Geode>
#include <map>
class CompactBufferVisitor : public osg::NodeVisitor {
public:
CompactBufferVisitor():
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
{}
void apply(osg::Geode& geode){
for (unsigned int i = 0; i < geode.getNumDrawables(); i++) {
apply(*geode.getDrawable(i));
}
}
void apply(osg::Drawable& drawable){
osg::Geometry* geometry = drawable.asGeometry();
if (!geometry || isProcessed(geometry)) {
return;
}
apply(*geometry);
}
void apply(osg::Geometry& geometry) {
compactPrimitiveSets(geometry);
setProcessed(&geometry);
}
protected:
bool isProcessed(const osg::Object* buffer)
{ return (_processed.find(buffer) != _processed.end()); }
void setProcessed(const osg::Object* source, osg::Object* processed=0)
{ _processed.insert(std::pair<const osg::Object*, osg::Object*>(source, processed)); }
void compactPrimitiveSets(osg::Geometry& geometry) {
osg::Geometry::PrimitiveSetList& primitives = geometry.getPrimitiveSetList();
for (unsigned int i = 0 ; i < primitives.size() ; i++) {
osg::DrawElementsUInt* de = dynamic_cast<osg::DrawElementsUInt*>(primitives[i].get());
if(isProcessed(de)) {
geometry.setPrimitiveSet(i, dynamic_cast<osg::DrawElements*>(getProcessedBuffer(de)));
}
else {
if(de && de->getNumIndices()) {
unsigned int maximum = maxIndex(de);
if(maximum < 256) {
osg::DrawElementsUByte* elements = new osg::DrawElementsUByte(de->getMode());
for (unsigned int j = 0 ; j < de->getNumIndices() ; ++ j) {
elements->push_back(static_cast<GLubyte>(de->index(j)));
}
geometry.setPrimitiveSet(i, elements);
setProcessed(de, elements);
}
else if (maximum < 65536) {
osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(de->getMode());
for (unsigned int j = 0 ; j < de->getNumIndices() ; ++ j) {
elements->push_back(static_cast<GLushort>(de->index(j)));
}
geometry.setPrimitiveSet(i, elements);
setProcessed(de, elements);
}
}
}
}
}
unsigned int maxIndex(osg::DrawElements* de) {
unsigned int maximum = de->index(0);
for(unsigned int i = 1 ; i < de->getNumIndices() ; ++ i){
maximum = std::max(maximum, static_cast<unsigned int>(de->index(i)));
}
return maximum;
}
osg::Object* getProcessedBuffer(const osg::Object* buffer) {
std::map<const osg::Object*, osg::Object*>::iterator it = _processed.find(buffer);
if(it == _processed.end()) {
return 0;
}
else {
return it->second;
}
}
std::map<const osg::Object*, osg::Object*> _processed;
};
#endif

View File

@@ -0,0 +1,372 @@
/* -*-c++-*-
* Copyright (C) 2010 Cedric Pinson <cedric.pinson@plopbyte.net>
*/
#ifndef JSON_OBJECT
#define JSON_OBJECT
#include <osg/Matrix>
#include <osg/Array>
#include <osg/Light>
#include <osg/Notify>
#include <osg/PrimitiveSet>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include "json_stream"
#ifdef WIN32
#include <cstdint>
#endif
class WriteVisitor;
struct Vec5
{
float _v[5];
Vec5() {}
Vec5(float a0, float a1, float a2, float a3, float a4) {
_v[0] = a0;
_v[1] = a1;
_v[2] = a2;
_v[3] = a3;
_v[4] = a4;
};
inline float& operator [] (int i) { return _v[i]; }
inline float operator [] (int i) const { return _v[i]; }
};
struct JSONObjectBase : public osg::Referenced
{
static int level;
static std::string indent();
virtual void write(json_stream& str, WriteVisitor& visitor) {}
};
template <class T> struct JSONValue;
struct JSONArray;
struct JSONObject : public JSONObjectBase
{
typedef std::map<std::string, osg::ref_ptr<JSONObject> > JSONMap;
typedef std::vector<std::string> OrderList;
JSONMap _maps;
JSONMap& getMaps() { return _maps; }
void writeOrder(json_stream& str, const OrderList& order, WriteVisitor& visitor);
virtual void write(json_stream& str, WriteVisitor& visitor);
void addChild(const std::string& type, JSONObject* child);
virtual JSONArray* asArray() { return 0; }
template<class T> JSONValue<T>* asValue() {
return dynamic_cast<JSONValue<T> * > ( this);
}
JSONObject(const unsigned int id, const std::string& bufferName = "");
JSONObject();
void addUniqueID();
unsigned int getUniqueID() const { return _uniqueID; }
JSONObject* getShadowObject() { return new JSONObject(_uniqueID, _bufferName); }
unsigned int _uniqueID;
static unsigned int uniqueID;
std::string _bufferName;
virtual void setBufferName(const std::string& name) { _bufferName = name; }
std::string getBufferName() { return _bufferName; }
bool isVarintableIntegerBuffer(osg::Array const*) const;
void encodeArrayAsVarintBuffer(osg::Array const*, std::vector<uint8_t>&) const;
template<typename T>
void dumpVarintVector(std::vector<uint8_t>&, T const*, bool) const;
template<typename T>
void dumpVarintValue(std::vector<uint8_t>&, T const*, bool) const;
std::vector<uint8_t> varintEncoding(unsigned int value) const;
// see https://developers.google.com/protocol-buffers/docs/encoding?hl=fr&csw=1#types
inline unsigned int toVarintUnsigned(int const v) const
{ return (v << 1) ^ (v >> ((sizeof(v) << 3) - 1)); }
};
JSONObject* createLight(osg::Light* light);
struct JSONNode : public JSONObject
{
void write(json_stream& str, WriteVisitor& visitor);
};
typedef JSONObject JSONStateSet;
typedef JSONObject JSONMaterial;
typedef JSONObject JSONLight;
struct JSONArray : public JSONObject
{
typedef std::vector<osg::ref_ptr<JSONObject> > JSONList;
JSONList _array;
JSONArray() {}
virtual void write(json_stream& str, WriteVisitor& visitor);
JSONList& getArray() { return _array; }
JSONArray* asArray() { return this; }
};
struct JSONKeyframes : public JSONArray
{
virtual void write(json_stream& str, WriteVisitor& visitor);
};
struct JSONVec3Array : public JSONArray
{
JSONVec3Array() {}
JSONVec3Array(const osg::Vec3&);
virtual void write(json_stream& str, WriteVisitor& visitor);
};
struct JSONVec4Array : public JSONVec3Array
{
JSONVec4Array(const osg::Vec4&);
};
struct JSONVec5Array : public JSONVec3Array
{
JSONVec5Array(const Vec5&);
};
struct JSONVec2Array : public JSONVec3Array
{
JSONVec2Array(const osg::Vec2&);
};
template <class T>
struct JSONValue : public JSONObject
{
T _value;
JSONValue(const T& v) {
_value = v;
}
T& getValue() { return _value; }
virtual void write(json_stream& str, WriteVisitor& visitor) {
str << _value ;
}
};
template <>
struct JSONValue<double> : public JSONObject
{
double _value;
JSONValue(const double& v) {
_value = v;
}
void write(json_stream& str, WriteVisitor& visitor) {
if (osg::isNaN(_value)) {
_value = 0.0;
}
str << _value;
}
};
template <>
struct JSONValue<std::string> : public JSONObject
{
std::string _value;
JSONValue(const std::string& v) {
_value = jsonEscape(v);
}
void write(json_stream& str, WriteVisitor& visitor) {
str << '"' << _value << '"';
}
protected:
std::string jsonEscape(const std::string& input) {
std::string value = input;
replace(value, std::string("\\"), std::string("\\\\"));
replace(value, std::string("\""), std::string("\\\""));
replace(value, std::string("\b"), std::string("\\b"));
replace(value, std::string("\f"), std::string("\\f"));
replace(value, std::string("\n"), std::string("\\n"));
replace(value, std::string("\r"), std::string("\\r"));
replace(value, std::string("\t"), std::string("\\t"));
return value;
}
void replace(std::string& str, const std::string& from, const std::string& to) {
if(from.empty())
return;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}
};
struct JSONMatrix : public JSONArray
{
JSONMatrix(const osg::Matrix& matrix) {
for (int i = 0; i < 16; i++) {
_array.push_back(new JSONValue<double>(matrix.ptr()[i]));
}
}
void write(json_stream& str, WriteVisitor& visitor);
};
struct JSONVertexArray : public JSONArray
{
osg::ref_ptr<const osg::Array> _arrayData;
std::string _filename;
std::pair<unsigned int, unsigned int> writeMergeData(const osg::Array* array,
WriteVisitor &visitor,
const std::string& filename,
std::string& encoding);
unsigned int writeData(const osg::Array* array, const std::string& filename)
{
std::ofstream myfile;
myfile.open(filename.c_str(), std::ios::binary );
const char* b = static_cast<const char*>(array->getDataPointer());
myfile.write(b, array->getTotalDataSize());
unsigned int fsize = myfile.tellp();
myfile.close();
return fsize;
}
template <class T> void writeInlineArray(json_stream& str, unsigned int size, const T* array) {
str << JSONObjectBase::indent() << "\"Elements\": [ " << array[0];
for (unsigned int i = 1; i < size; i++) {
T v = array[i];
str << ", " << v;
}
str << " ]," << std::endl;
}
template <typename T, typename U> void writeInlineArray(json_stream& str, unsigned int size, const T* array) {
str << JSONObjectBase::indent() << "\"Elements\": [ " << static_cast<U>(array[0]);
for (unsigned int i = 1; i < size; i++) {
str << ", " << static_cast<U>(array[i]);
}
str << " ]," << std::endl;
}
template <class T> void writeInlineArrayReal(json_stream& str, unsigned int size, const T* array) {
str << JSONObjectBase::indent() << "\"Elements\": [ " << array[0];
for (unsigned int i = 1; i < size; i++) {
float v = array[i];
if (osg::isNaN(v))
v = 0;
str << ", " << v;
}
str << " ]," << std::endl;
}
void write(json_stream& str, WriteVisitor& visitor);
JSONVertexArray() {}
JSONVertexArray(const osg::Array* array) {
_arrayData = array;
}
};
struct JSONBufferArray : public JSONObject
{
JSONBufferArray() {}
JSONBufferArray(const osg::Array* array)
{
JSONVertexArray* b = new JSONVertexArray(array);
getMaps()["Array"] = b;
getMaps()["ItemSize"] = new JSONValue<int>(array->getDataSize());
getMaps()["Type"] = new JSONValue<std::string>("ARRAY_BUFFER"); //0x8892);
}
void setBufferName(const std::string& bufferName) {
JSONObject::setBufferName(bufferName);
getMaps()["Array"]->setBufferName(bufferName);
}
};
JSONObject* getDrawMode(GLenum mode);
struct JSONDrawArray : public JSONObject
{
JSONDrawArray(osg::DrawArrays& array);
};
struct JSONDrawArrayLengths : public JSONObject
{
JSONDrawArrayLengths(osg::DrawArrayLengths& array);
void setBufferName(const std::string& bufferName) {
JSONObject::setBufferName(bufferName);
getMaps()["ArrayLengths"]->setBufferName(bufferName);
}
};
template<typename T>
struct BufferArray
{ typedef osg::UShortArray type; };
#define ADD_INDEX_BUFFER(T, B)\
template<> \
struct BufferArray<T> \
{ typedef B type; }
ADD_INDEX_BUFFER(osg::DrawElementsUInt, osg::UIntArray);
ADD_INDEX_BUFFER(osg::DrawElementsUByte, osg::UByteArray);
template <class T>
struct JSONDrawElements : public JSONObject
{
JSONDrawElements(T& array) {
typedef typename BufferArray<T>::type array_type;
typedef typename array_type::ElementDataType element_type;
JSONBufferArray* buf;
if (array.getMode() == GL_QUADS) {
int size = array.getNumIndices();
osg::ref_ptr<array_type> buffer = new array_type(size);
unsigned int idx = 0;
for (int i = 0; i < size/4; ++i) {
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 0));
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 1));
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 3));
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 1));
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 2));
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 3));
}
buf = new JSONBufferArray(buffer.get());
getMaps()["Mode"] = getDrawMode(osg::PrimitiveSet::TRIANGLES);
}
else {
osg::ref_ptr<array_type> buffer = new array_type(array.getNumIndices());
for(unsigned int i = 0 ; i < array.getNumIndices() ; ++ i)
(*buffer)[i] = static_cast<element_type>(array.index(i));
buf = new JSONBufferArray(buffer.get());
getMaps()["Mode"] = getDrawMode(array.getMode());
}
buf->getMaps()["Type"] = new JSONValue<std::string>("ELEMENT_ARRAY_BUFFER");
getMaps()["Indices"] = buf;
}
void setBufferName(const std::string& bufferName) {
JSONObject::setBufferName(bufferName);
getMaps()["Indices"]->setBufferName(bufferName);
}
};
#endif

View File

@@ -0,0 +1,713 @@
/* -*-c++-*-
* Copyright (C) 2010 Cedric Pinson <cedric.pinson@plopbyte.net>
*/
#include "JSON_Objects"
#include <osgDB/WriteFile>
#include <osgDB/FileNameUtils>
#include <osg/Material>
#include <osg/BlendFunc>
#include <osg/BlendColor>
#include <osg/Texture>
#include <osg/CullFace>
#include <osg/Texture2D>
#include <osg/Texture1D>
#include <osg/Image>
#include <sstream>
#include "WriteVisitor"
int JSONObjectBase::level = 0;
unsigned int JSONObject::uniqueID = 0;
std::string JSONObjectBase::indent()
{
std::string str;
for (int i = 0; i < JSONObjectBase::level; ++i) {
str += " ";
}
return str;
}
void JSONMatrix::write(json_stream& str, WriteVisitor& visitor)
{
str << "[ ";
for (unsigned int i = 0; i < _array.size(); i++) {
_array[i]->write(str, visitor);
if (i != _array.size() -1)
str << ", ";
}
str << " ]";
}
void JSONNode::write(json_stream& str, WriteVisitor& visitor)
{
std::vector<std::string> order;
order.push_back("UniqueID");
order.push_back("Name");
order.push_back("TargetName");
order.push_back("Matrix");
order.push_back("UpdateCallbacks");
order.push_back("StateSet");
writeOrder(str, order, visitor);
}
JSONObject::JSONObject(const unsigned int id, const std::string& bufferName)
{
_uniqueID = id;
_bufferName = bufferName;
_maps["UniqueID"] = new JSONValue<unsigned int>(id);
}
JSONObject::JSONObject()
{
_uniqueID = -1;
}
void JSONObject::addUniqueID()
{
_uniqueID = JSONObject::uniqueID++;
_maps["UniqueID"] = new JSONValue<unsigned int>(_uniqueID);
}
void JSONObject::addChild(const std::string& type, JSONObject* child)
{
if (!getMaps()["Children"])
getMaps()["Children"] = new JSONArray;
JSONObject* jsonObject = new JSONObject();
jsonObject->getMaps()[type] = child;
getMaps()["Children"]->asArray()->getArray().push_back(jsonObject);
}
bool JSONObject::isVarintableIntegerBuffer(osg::Array const* array) const
{
// Return true for buffers representing integer values and that therefore
// can be binary encoded compactly using the varint protocol.
// Note: as Byte/UByte array are already compact we do not consider
// them as compactable
bool isInteger = false;
switch(static_cast<int>(array->getType()))
{
case osg::Array::IntArrayType:
isInteger = (dynamic_cast<osg::IntArray const*>(array) != NULL);
break;
case osg::Array::ShortArrayType:
isInteger = (dynamic_cast<osg::ShortArray const*>(array) != NULL);
break;
case osg::Array::UIntArrayType:
isInteger = (dynamic_cast<osg::UIntArray const*>(array) != NULL);
break;
case osg::Array::UShortArrayType:
isInteger = (dynamic_cast<osg::UShortArray const*>(array) != NULL);
break;
case osg::Array::Vec2iArrayType:
isInteger = (dynamic_cast<osg::Vec2iArray const*>(array) != NULL);
break;
case osg::Array::Vec3iArrayType:
isInteger = (dynamic_cast<osg::Vec3iArray const*>(array) != NULL);
break;
case osg::Array::Vec4iArrayType:
isInteger = (dynamic_cast<osg::Vec4iArray const*>(array) != NULL);
break;
case osg::Array::Vec2uiArrayType:
isInteger = (dynamic_cast<osg::Vec2uiArray const*>(array) != NULL);
break;
case osg::Array::Vec3uiArrayType:
isInteger = (dynamic_cast<osg::Vec3uiArray const*>(array) != NULL);
break;
case osg::Array::Vec4uiArrayType:
isInteger = (dynamic_cast<osg::Vec4uiArray const*>(array) != NULL);
break;
case osg::Array::Vec2sArrayType:
isInteger = (dynamic_cast<osg::Vec2sArray const*>(array) != NULL);
break;
case osg::Array::Vec3sArrayType:
isInteger = (dynamic_cast<osg::Vec3sArray const*>(array) != NULL);
break;
case osg::Array::Vec4sArrayType:
isInteger = (dynamic_cast<osg::Vec4sArray const*>(array) != NULL);
break;
case osg::Array::Vec2usArrayType:
isInteger = (dynamic_cast<osg::Vec2usArray const*>(array) != NULL);
break;
case osg::Array::Vec3usArrayType:
isInteger = (dynamic_cast<osg::Vec3usArray const*>(array) != NULL);
break;
case osg::Array::Vec4usArrayType:
isInteger = (dynamic_cast<osg::Vec4usArray const*>(array) != NULL);
break;
default:
isInteger = false;
break;
}
return isInteger;
}
void JSONObject::encodeArrayAsVarintBuffer(osg::Array const* array, std::vector<uint8_t>& buffer) const
{
switch(static_cast<int>(array->getType()))
{
case osg::Array::IntArrayType:
dumpVarintValue<osg::IntArray>(buffer, dynamic_cast<osg::IntArray const*>(array), false);
break;
case osg::Array::ShortArrayType:
dumpVarintValue<osg::ShortArray>(buffer, dynamic_cast<osg::ShortArray const*>(array), false);
break;
case osg::Array::UIntArrayType:
dumpVarintValue<osg::UIntArray>(buffer, dynamic_cast<osg::UIntArray const*>(array), true);
break;
case osg::Array::UShortArrayType:
dumpVarintValue<osg::UShortArray>(buffer, dynamic_cast<osg::UShortArray const*>(array), true);
break;
case osg::Array::Vec2iArrayType:
dumpVarintVector<osg::Vec2iArray>(buffer, dynamic_cast<osg::Vec2iArray const*>(array), false);
break;
case osg::Array::Vec3iArrayType:
dumpVarintVector<osg::Vec3iArray>(buffer, dynamic_cast<osg::Vec3iArray const*>(array), false);
break;
case osg::Array::Vec4iArrayType:
dumpVarintVector<osg::Vec4iArray>(buffer, dynamic_cast<osg::Vec4iArray const*>(array), false);
break;
case osg::Array::Vec2sArrayType:
dumpVarintVector<osg::Vec2sArray>(buffer, dynamic_cast<osg::Vec2sArray const*>(array), false);
break;
case osg::Array::Vec3sArrayType:
dumpVarintVector<osg::Vec3sArray>(buffer, dynamic_cast<osg::Vec3sArray const*>(array), false);
break;
case osg::Array::Vec4sArrayType:
dumpVarintVector<osg::Vec4sArray>(buffer, dynamic_cast<osg::Vec4sArray const*>(array), false);
break;
case osg::Array::Vec2uiArrayType:
dumpVarintVector<osg::Vec2uiArray>(buffer, dynamic_cast<osg::Vec2uiArray const*>(array), true);
break;
case osg::Array::Vec3uiArrayType:
dumpVarintVector<osg::Vec3uiArray>(buffer, dynamic_cast<osg::Vec3uiArray const*>(array), true);
break;
case osg::Array::Vec4uiArrayType:
dumpVarintVector<osg::Vec4uiArray>(buffer, dynamic_cast<osg::Vec4uiArray const*>(array), true);
break;
case osg::Array::Vec2usArrayType:
dumpVarintVector<osg::Vec2usArray>(buffer, dynamic_cast<osg::Vec2usArray const*>(array), true);
break;
case osg::Array::Vec3usArrayType:
dumpVarintVector<osg::Vec3usArray>(buffer, dynamic_cast<osg::Vec3usArray const*>(array), true);
break;
case osg::Array::Vec4usArrayType:
dumpVarintVector<osg::Vec4usArray>(buffer, dynamic_cast<osg::Vec4usArray const*>(array), true);
break;
default:
break;
}
}
template<typename T>
void JSONObject::dumpVarintVector(std::vector<uint8_t>& oss, T const* buffer, bool isUnsigned) const {
unsigned int n = buffer->getDataSize();
for(typename T::const_iterator it = buffer->begin() ; it != buffer->end() ; ++ it) {
for(unsigned int i = 0 ; i < n ; ++ i) {
unsigned int value = isUnsigned ? (*it)[i] : JSONObject::toVarintUnsigned((*it)[i]);
std::vector<uint8_t> encoding = varintEncoding(value);
oss.insert(oss.end(), encoding.begin(), encoding.end());
}
}
}
template<typename T>
void JSONObject::dumpVarintValue(std::vector<uint8_t>& oss, T const* buffer, bool isUnsigned) const {
for(typename T::const_iterator it = buffer->begin() ; it != buffer->end() ; ++ it) {
unsigned int value = isUnsigned ? (*it) : JSONObject::toVarintUnsigned(*it);
std::vector<uint8_t> encoding = varintEncoding(value);
oss.insert(oss.end(), encoding.begin(), encoding.end());
}
}
// varint encoding adapted from http://stackoverflow.com/questions/19758270/read-varint-from-linux-sockets
std::vector<uint8_t> JSONObject::varintEncoding(unsigned int value) const
{
std::vector<uint8_t> buffer;
do {
uint8_t next_byte = value & 0x7F;
value >>= 7;
if (value) {
next_byte |= 0x80;
}
buffer.push_back(next_byte);
}
while (value);
return buffer;
}
static void writeEntry(json_stream& str, const std::string& key, JSONObject::JSONMap& map, WriteVisitor& visitor)
{
if (key.empty())
return;
if ( map.find(key) != map.end() &&
map[ key ].valid() ) {
str << JSONObjectBase::indent() << '"' << key << '"' << ": ";
map[ key ]->write(str, visitor);
map.erase(key);
if (!map.empty()) {
str << ", ";
str << "\n";
}
}
}
void JSONObject::writeOrder(json_stream& str, const std::vector<std::string>& order, WriteVisitor& visitor)
{
str << "{" << std::endl;
JSONObjectBase::level++;
for (unsigned int i = 0; i < order.size(); i++) {
writeEntry(str, order[i], _maps, visitor);
}
while(!_maps.empty()) {
std::string key = _maps.begin()->first;
writeEntry(str, key, _maps, visitor);
}
JSONObjectBase::level--;
str << std::endl << JSONObjectBase::indent() << "}";
}
void JSONObject::write(json_stream& str, WriteVisitor& visitor)
{
OrderList defaultOrder;
defaultOrder.push_back("UniqueID");
defaultOrder.push_back("Name");
defaultOrder.push_back("TargetName");
writeOrder(str, defaultOrder, visitor);
}
std::pair<unsigned int,unsigned int> JSONVertexArray::writeMergeData(const osg::Array* array,
WriteVisitor &visitor,
const std::string& filename,
std::string& encoding)
{
std::ofstream& output = visitor.getBufferFile(filename);
unsigned int offset = output.tellp();
if(visitor._varint && isVarintableIntegerBuffer(array))
{
std::vector<uint8_t> varintByteBuffer;
encodeArrayAsVarintBuffer(array, varintByteBuffer);
output.write((char*)&varintByteBuffer[0], varintByteBuffer.size() * sizeof(uint8_t));
encoding = std::string("varint");
}
else
{
const char* b = static_cast<const char*>(array->getDataPointer());
size_t totalDataSize = array->getTotalDataSize();
output.write(b, totalDataSize);
}
unsigned int fsize = output.tellp();
// pad to 4 bytes
unsigned int remainder = fsize % 4;
if (remainder) {
unsigned int null = 0;
output.write((char*) (&null), 4 - remainder);
fsize = output.tellp();
}
return std::pair<unsigned int, unsigned int>(offset, fsize - offset);
}
void JSONVertexArray::write(json_stream& str, WriteVisitor& visitor)
{
bool _useExternalBinaryArray = visitor._useExternalBinaryArray;
bool _mergeAllBinaryFiles = visitor._mergeAllBinaryFiles;
std::string basename = visitor._baseName;
addUniqueID();
std::stringstream url;
if (visitor._useExternalBinaryArray) {
std::string bufferName = getBufferName();
if(bufferName.empty())
bufferName = visitor.getBinaryFilename();
if (visitor._mergeAllBinaryFiles)
url << bufferName;
else
url << basename << "_" << _uniqueID << ".bin";
}
std::string type;
osg::ref_ptr<const osg::Array> array = _arrayData;
switch (array->getType()) {
case osg::Array::FloatArrayType:
case osg::Array::Vec2ArrayType:
case osg::Array::Vec3ArrayType:
case osg::Array::Vec4ArrayType:
type = "Float32Array";
break;
case osg::Array::Vec4ubArrayType:
{
osg::ref_ptr<osg::Vec4Array> converted = new osg::Vec4Array;
converted->reserve(array->getNumElements());
const osg::Vec4ubArray* a = dynamic_cast<const osg::Vec4ubArray*>(array.get());
for (unsigned int i = 0; i < a->getNumElements(); ++i) {
converted->push_back(osg::Vec4( (*a)[i][0]/255.0,
(*a)[i][1]/255.0,
(*a)[i][2]/255.0,
(*a)[i][3]/255.0));
}
array = converted;
type = "Float32Array";
}
break;
case osg::Array::UByteArrayType:
case osg::Array::Vec2ubArrayType:
case osg::Array::Vec3ubArrayType:
type = "Uint8Array";
break;
case osg::Array::UShortArrayType:
case osg::Array::Vec2usArrayType:
case osg::Array::Vec3usArrayType:
case osg::Array::Vec4usArrayType:
type = "Uint16Array";
break;
case osg::Array::UIntArrayType:
case osg::Array::Vec2uiArrayType:
case osg::Array::Vec3uiArrayType:
case osg::Array::Vec4uiArrayType:
type = "Uint32Array";
break;
case osg::Array::ByteArrayType:
case osg::Array::Vec2bArrayType:
case osg::Array::Vec3bArrayType:
case osg::Array::Vec4bArrayType:
type = "Int8Array";
break;
case osg::Array::ShortArrayType:
case osg::Array::Vec2sArrayType:
case osg::Array::Vec3sArrayType:
case osg::Array::Vec4sArrayType:
type = "Int16Array";
break;
case osg::Array::IntArrayType:
case osg::Array::Vec2iArrayType:
case osg::Array::Vec3iArrayType:
case osg::Array::Vec4iArrayType:
type = "Int32Array";
break;
default:
osg::notify(osg::WARN) << "Array of type " << array->getType() << " not supported" << std::endl;
break;
}
str << "{ " << std::endl;
JSONObjectBase::level++;
str << JSONObjectBase::indent() << "\"" << type << "\"" << ": { " << std::endl;
JSONObjectBase::level++;
if (_useExternalBinaryArray) {
str << JSONObjectBase::indent() << "\"File\": \"" << osgDB::getSimpleFileName(url.str()) << "\","<< std::endl;
} else {
if (array->getNumElements() == 0) {
str << JSONObjectBase::indent() << "\"Elements\": [ ],";
} else {
switch (array->getType()) {
case osg::Array::FloatArrayType:
case osg::Array::Vec2ArrayType:
case osg::Array::Vec3ArrayType:
case osg::Array::Vec4ArrayType:
{
const float* a = static_cast<const float*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArrayReal<float>(str, size, a);
}
break;
case osg::Array::DoubleArrayType:
case osg::Array::Vec2dArrayType:
case osg::Array::Vec3dArrayType:
case osg::Array::Vec4dArrayType:
{
const double* a = static_cast<const double*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArrayReal<double>(str, size, a);
}
break;
case osg::Array::ByteArrayType:
case osg::Array::Vec2bArrayType:
case osg::Array::Vec3bArrayType:
case osg::Array::Vec4bArrayType:
{
const char* a = static_cast<const char*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<char, short>(str, size, a); // using short to write readable numbers and not `char`s
}
break;
case osg::Array::UByteArrayType:
case osg::Array::Vec2ubArrayType:
case osg::Array::Vec3ubArrayType:
case osg::Array::Vec4ubArrayType:
{
const unsigned char* a = static_cast<const unsigned char*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<unsigned char, unsigned short>(str, size, a); // using short to write readable numbers and not `char`s
}
break;
case osg::Array::ShortArrayType:
case osg::Array::Vec2sArrayType:
case osg::Array::Vec3sArrayType:
case osg::Array::Vec4sArrayType:
{
const short* a = static_cast<const short*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<short>(str, size, a);
}
break;
case osg::Array::UShortArrayType:
case osg::Array::Vec2usArrayType:
case osg::Array::Vec3usArrayType:
case osg::Array::Vec4usArrayType:
{
const unsigned short* a = static_cast<const unsigned short*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<unsigned short>(str, size, a);
}
break;
case osg::Array::IntArrayType:
case osg::Array::Vec2iArrayType:
case osg::Array::Vec3iArrayType:
case osg::Array::Vec4iArrayType:
{
const int* a = static_cast<const int*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<int>(str, size, a);
}
break;
case osg::Array::UIntArrayType:
case osg::Array::Vec2uiArrayType:
case osg::Array::Vec3uiArrayType:
case osg::Array::Vec4uiArrayType:
{
const unsigned int* a = static_cast<const unsigned int*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<unsigned int>(str, size, a);
}
break;
case osg::Array::MatrixdArrayType:
default:
break;
}
}
}
str << JSONObjectBase::indent() << "\"Size\": " << array->getNumElements();
if (_useExternalBinaryArray) {
str << "," << std::endl;
} else {
str << std::endl;
}
if (_useExternalBinaryArray) {
unsigned int size;
if (_mergeAllBinaryFiles) {
std::pair<unsigned int, unsigned int> result;
std::string encoding;
result = writeMergeData(array.get(), visitor, url.str(), encoding);
unsigned int offset = result.first;
size = result.second;
if(!encoding.empty()) {
str << JSONObjectBase::indent() << "\"Offset\": " << offset << "," << std::endl;
str << JSONObjectBase::indent() << "\"Encoding\": \"" << encoding << "\"" << std::endl;
}
else {
str << JSONObjectBase::indent() << "\"Offset\": " << offset << std::endl;
}
} else {
size = writeData(array.get(), url.str());
str << JSONObjectBase::indent() << "\"Offset\": " << 0 << std::endl;
}
std::stringstream b;
osg::notify(osg::NOTICE) << "TypedArray " << type << " " << url.str() << " ";
if (size/1024.0 < 1.0) {
osg::notify(osg::NOTICE) << size << " bytes" << std::endl;
} else if (size/(1024.0*1024.0) < 1.0) {
osg::notify(osg::NOTICE) << size/1024.0 << " kb" << std::endl;
} else {
osg::notify(osg::NOTICE) << size/(1024.0*1024.0) << " mb" << std::endl;
}
}
JSONObjectBase::level--;
str << JSONObjectBase::indent() << "}" << std::endl;
JSONObjectBase::level--;
str << JSONObjectBase::indent() << "}";
}
JSONVec4Array::JSONVec4Array(const osg::Vec4& v) : JSONVec3Array()
{
for (int i = 0; i < 4; ++i) {
_array.push_back(new JSONValue<float>(v[i]));
}
}
JSONVec5Array::JSONVec5Array(const Vec5& v) : JSONVec3Array()
{
for (int i = 0; i < 5; ++i) {
_array.push_back(new JSONValue<float>(v[i]));
}
}
JSONVec2Array::JSONVec2Array(const osg::Vec2& v) : JSONVec3Array()
{
for (int i = 0; i < 2; ++i) {
_array.push_back(new JSONValue<float>(v[i]));
}
}
JSONVec3Array::JSONVec3Array(const osg::Vec3& v)
{
for (int i = 0; i < 3; ++i) {
_array.push_back(new JSONValue<float>(v[i]));
}
}
void JSONVec3Array::write(json_stream& str,WriteVisitor& visitor)
{
str << "[ ";
for (unsigned int i = 0; i < _array.size(); i++) {
if (_array[i].valid()) {
_array[i]->write(str, visitor);
} else {
str << "undefined";
}
if (i != _array.size()-1)
str << ", ";
}
str << "]";
}
void JSONKeyframes::write(json_stream& str,WriteVisitor& visitor)
{
JSONObjectBase::level++;
str << "[" << std::endl << JSONObjectBase::indent();
for (unsigned int i = 0; i < _array.size(); i++) {
if (_array[i].valid()) {
_array[i]->write(str, visitor);
} else {
str << "undefined";
}
if (i != _array.size() -1) {
str << ",";
str << "\n" << JSONObjectBase::indent();
}
}
str << " ]";
JSONObjectBase::level--;
}
void JSONArray::write(json_stream& str,WriteVisitor& visitor)
{
str << "[ ";
for (unsigned int i = 0; i < _array.size(); i++) {
if (_array[i].valid()) {
_array[i]->write(str, visitor);
} else {
str << "undefined";
}
if (i != _array.size() -1) {
str << ",";
str << "\n" << JSONObjectBase::indent();
}
}
//str << std::endl << JSONObjectBase::indent() << "]";
str << " ]";
}
JSONObject* getDrawMode(GLenum mode)
{
JSONObject* result = 0;
switch (mode) {
case GL_POINTS:
result = new JSONValue<std::string>("POINTS");
break;
case GL_LINES:
result = new JSONValue<std::string>("LINES");
break;
case GL_LINE_LOOP:
result = new JSONValue<std::string>("LINE_LOOP");
break;
case GL_LINE_STRIP:
result = new JSONValue<std::string>("LINE_STRIP");
break;
case GL_TRIANGLES:
result = new JSONValue<std::string>("TRIANGLES");
break;
case GL_POLYGON:
result = new JSONValue<std::string>("TRIANGLE_FAN");
break;
case GL_QUAD_STRIP:
case GL_TRIANGLE_STRIP:
result = new JSONValue<std::string>("TRIANGLE_STRIP");
break;
case GL_TRIANGLE_FAN:
result = new JSONValue<std::string>("TRIANGLE_FAN");
break;
case GL_QUADS:
osg::notify(osg::WARN) << "exporting quads will not be able to work on opengl es" << std::endl;
break;
}
return result;
}
JSONDrawArray::JSONDrawArray(osg::DrawArrays& array)
{
getMaps()["First"] = new JSONValue<int>(array.getFirst());
getMaps()["Count"] = new JSONValue<int>(array.getCount());
getMaps()["Mode"] = getDrawMode(array.getMode());
}
JSONDrawArrayLengths::JSONDrawArrayLengths(osg::DrawArrayLengths& array)
{
getMaps()["First"] = new JSONValue<int>(array.getFirst());
getMaps()["Mode"] = getDrawMode(array.getMode());
JSONArray* jsonArray = new JSONArray;
for (unsigned int i = 0; i < array.size(); i++) {
jsonArray->getArray().push_back(new JSONValue<int>(array[i]));
}
getMaps()["ArrayLengths"] = jsonArray;
}

View File

@@ -0,0 +1,243 @@
// copyright: 'Cedric Pinson cedric@plopbyte.com'
#include <osg/Image>
#include <osg/Notify>
#include <osg/Geode>
#include <osg/GL>
#include <osg/Version>
#include <osg/Endian>
#include <osg/Projection>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osgUtil/UpdateVisitor>
#include <osgDB/ReaderWriter>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <osgAnimation/UpdateMatrixTransform>
#include <osgAnimation/AnimationManagerBase>
#include <osgAnimation/BasicAnimationManager>
#include <vector>
#include "json_stream"
#include "JSON_Objects"
#include "Animation"
#include "CompactBufferVisitor"
#include "WriteVisitor"
using namespace osg;
class ReaderWriterJSON : public osgDB::ReaderWriter
{
public:
struct OptionsStruct {
int resizeTextureUpToPowerOf2;
bool useExternalBinaryArray;
bool mergeAllBinaryFiles;
bool disableCompactBuffer;
bool inlineImages;
bool varint;
bool strictJson;
std::vector<std::string> useSpecificBuffer;
OptionsStruct() {
resizeTextureUpToPowerOf2 = 0;
useExternalBinaryArray = false;
mergeAllBinaryFiles = false;
disableCompactBuffer = false;
inlineImages = false;
varint = false;
strictJson = true;
}
};
ReaderWriterJSON()
{
supportsExtension("osgjs","OpenSceneGraph Javascript implementation format");
supportsOption("resizeTextureUpToPowerOf2=<int>","Specify the maximum power of 2 allowed dimension for texture. Using 0 will disable the functionality and no image resizing will occur.");
supportsOption("useExternalBinaryArray","create binary files for vertex arrays");
supportsOption("mergeAllBinaryFiles","merge all binary files into one to avoid multi request on a server");
supportsOption("inlineImages","insert base64 encoded images instead of referring to them");
supportsOption("varint","Use varint encoding to serialize integer buffers");
supportsOption("useSpecificBuffer=uservalue1,uservalue2","uses specific buffers for unshared buffers attached to geometries having a specified user value");
supportsOption("disableCompactBuffer","keep source types and do not try to optimize buffers size");
supportsOption("disableStrictJson","do not clean string (to utf8) or floating point (should be finite) values");
}
virtual const char* className() const { return "OSGJS json Writer"; }
virtual ReadResult readNode(const std::string& fileName, const Options* options) const;
virtual WriteResult writeNode(const Node& node,
const std::string& fileName,
const osgDB::ReaderWriter::Options* options) const
{
std::string ext = osgDB::getFileExtension(fileName);
if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
OptionsStruct _options = parseOptions(options);
json_stream fout(fileName, _options.strictJson);
if(fout) {
WriteResult res = writeNodeModel(node, fout, osgDB::getNameLessExtension(fileName), _options);
return res;
}
return WriteResult("Unable to open file for output");
}
virtual WriteResult writeNode(const Node& node,
json_stream& fout,
const osgDB::ReaderWriter::Options* options) const
{
if (!fout) {
return WriteResult("Unable to write to output stream");
}
OptionsStruct _options;
_options = parseOptions(options);
return writeNodeModel(node, fout, "stream", _options);
}
virtual WriteResult writeNodeModel(const Node& node, json_stream& fout, const std::string& basename, const OptionsStruct& options) const
{
// process regular model
osg::ref_ptr<osg::Node> model = osg::clone(&node);
if(!options.disableCompactBuffer) {
CompactBufferVisitor compact;
model->accept(compact);
}
WriteVisitor writer;
try {
//osgDB::writeNodeFile(*model, "/tmp/debug_osgjs.osg");
writer.setBaseName(basename);
writer.useExternalBinaryArray(options.useExternalBinaryArray);
writer.mergeAllBinaryFiles(options.mergeAllBinaryFiles);
writer.inlineImages(options.inlineImages);
writer.setMaxTextureDimension(options.resizeTextureUpToPowerOf2);
writer.setVarint(options.varint);
for(std::vector<std::string>::const_iterator specificBuffer = options.useSpecificBuffer.begin() ;
specificBuffer != options.useSpecificBuffer.end() ; ++ specificBuffer) {
writer.addSpecificBuffer(*specificBuffer);
}
model->accept(writer);
if (writer._root.valid()) {
writer.write(fout);
return WriteResult::FILE_SAVED;
}
} catch (...) {
osg::notify(osg::FATAL) << "can't save osgjs file" << std::endl;
return WriteResult("Unable to write to output stream");
}
return WriteResult("Unable to write to output stream");
}
ReaderWriterJSON::OptionsStruct parseOptions(const osgDB::ReaderWriter::Options* options) const
{
OptionsStruct localOptions;
if (options)
{
osg::notify(NOTICE) << "options " << options->getOptionString() << std::endl;
std::istringstream iss(options->getOptionString());
std::string opt;
while (iss >> opt)
{
// split opt into pre= and post=
std::string pre_equals;
std::string post_equals;
size_t found = opt.find("=");
if(found!=std::string::npos)
{
pre_equals = opt.substr(0,found);
post_equals = opt.substr(found+1);
}
else
{
pre_equals = opt;
}
if (pre_equals == "useExternalBinaryArray")
{
localOptions.useExternalBinaryArray = true;
}
if (pre_equals == "mergeAllBinaryFiles")
{
localOptions.mergeAllBinaryFiles = true;
}
if (pre_equals == "disableCompactBuffer")
{
localOptions.disableCompactBuffer = true;
}
if (pre_equals == "disableStrictJson")
{
localOptions.strictJson = false;
}
if (pre_equals == "inlineImages")
{
localOptions.inlineImages = true;
}
if (pre_equals == "varint")
{
localOptions.varint = true;
}
if (pre_equals == "resizeTextureUpToPowerOf2" && post_equals.length() > 0)
{
int value = atoi(post_equals.c_str());
localOptions.resizeTextureUpToPowerOf2 = osg::Image::computeNearestPowerOfTwo(value);
}
if (pre_equals == "useSpecificBuffer" && !post_equals.empty())
{
size_t stop_pos = 0, start_pos = 0;
while((stop_pos = post_equals.find(",", start_pos)) != std::string::npos) {
localOptions.useSpecificBuffer.push_back(post_equals.substr(start_pos,
stop_pos - start_pos));
start_pos = stop_pos + 1;
++ stop_pos;
}
localOptions.useSpecificBuffer.push_back(post_equals.substr(start_pos,
post_equals.length() - start_pos));
}
}
}
return localOptions;
}
};
osgDB::ReaderWriter::ReadResult ReaderWriterJSON::readNode(const std::string& file, const Options* options) const
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
// strip the pseudo-loader extension
std::string fileName = osgDB::getNameLessExtension( file );
fileName = osgDB::findDataFile( fileName, options );
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
osg::Node *node = osgDB::readNodeFile( fileName, options );
if (!node)
return ReadResult::FILE_NOT_HANDLED;
return ReadResult::FILE_NOT_HANDLED;
}
// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(osgjs, ReaderWriterJSON)

View File

@@ -0,0 +1,451 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 2012 Cedric Pinson */
#ifndef WRITE_VISITOR_H
#define WRITE_VISITOR_H
#include <osg/Image>
#include <osg/Notify>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/GL>
#include <osg/Version>
#include <osg/Endian>
#include <osg/Projection>
#include <osg/MatrixTransform>
#include <osg/PagedLOD>
#include <osg/PositionAttitudeTransform>
#include <osgAnimation/BasicAnimationManager>
#include <osg/LightSource>
#include <osg/CullFace>
#include <osg/Material>
#include <osg/BlendColor>
#include <osg/BlendFunc>
#include <osg/ValueObject>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
#include "JSON_Objects"
#include "Animation"
#include "json_stream"
#define WRITER_VERSION 7
osg::Array* getTangentSpaceArray(osg::Geometry& geometry);
void translateObject(JSONObject* json, osg::Object* osg);
void getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value);
template<typename T>
bool getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value);
class WriteVisitor : public osg::NodeVisitor
{
public:
typedef std::vector<osg::ref_ptr<osg::StateSet> > StateSetStack;
std::map<osg::ref_ptr<osg::Object>, osg::ref_ptr<JSONObject> > _maps;
std::vector<osg::ref_ptr<JSONObject> > _parents;
osg::ref_ptr<JSONObject> _root;
StateSetStack _stateset;
std::string _baseName;
bool _useExternalBinaryArray;
bool _mergeAllBinaryFiles;
bool _inlineImages;
bool _varint;
int _maxTextureDimension;
std::vector<std::string> _specificBuffers;
std::map<std::string, std::ofstream*> _buffers;
std::ofstream& getBufferFile(const std::string& name) {
if(_buffers.find(name) == _buffers.end()) {
_buffers[name] = new std::ofstream(name.c_str(), std::ios::binary);
}
return *_buffers[name];
}
WriteVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {
_mergeAllBinaryFiles = false;
_useExternalBinaryArray = false;
_inlineImages = false;
_maxTextureDimension = 0;
_varint = false;
}
~WriteVisitor() {
for(std::map<std::string, std::ofstream*>::iterator buffer = _buffers.begin() ;
buffer != _buffers.end() ; ++ buffer) {
delete buffer->second;
}
}
std::string getBinaryFilename(const std::string& buffer = "") {
std::string suffix;
if(!buffer.empty()) {
suffix = "_" + buffer;
}
return std::string(_baseName) + suffix + ".bin";
}
void closeBuffers() {
for(std::map<std::string, std::ofstream*>::iterator buffer = _buffers.begin() ;
buffer != _buffers.end() ; ++ buffer) {
buffer->second->close();
}
}
unsigned int getBuffersSize() {
unsigned int size = 0;
for(std::map<std::string, std::ofstream*>::iterator buffer = _buffers.begin() ;
buffer != _buffers.end() ; ++ buffer) {
size += buffer->second->tellp();
}
return size;
}
void write(json_stream& str) {
osg::ref_ptr<JSONObject> o = new JSONObject();
o->getMaps()["Version"] = new JSONValue<int>(WRITER_VERSION);
o->getMaps()["Generator"] = new JSONValue<std::string>("OpenSceneGraph " + std::string(osgGetVersion()) );
o->getMaps()["osg.Node"] = _root.get();
o->write(str, *this);
if (_mergeAllBinaryFiles) {
closeBuffers();
unsigned int size = getBuffersSize();
osg::notify(osg::NOTICE) << "Use a merged binary file ";
if (size/1024.0 < 1.0) {
osg::notify(osg::NOTICE) << size << " bytes" << std::endl;
} else if (size/(1024.0*1024.0) < 1.0) {
osg::notify(osg::NOTICE) << size/1024.0 << " kb" << std::endl;
} else {
osg::notify(osg::NOTICE) << size/(1024.0*1024.0) << " mb" << std::endl;
}
}
}
void error() {
throw "Error occur";
}
void setBufferName(JSONObject *json, osg::Geometry* geometry) {
if(!_mergeAllBinaryFiles || _specificBuffers.empty())
return;
std::string bufferName = getBufferName(geometry);
std::string defaultBufferName = getBinaryFilename();
std::string jsonBufferName = json->getBufferName();
// if the buffer is shared we will always favor dumping it in the default
// buffer and otherwise we keep the first buffer name set.
if(!jsonBufferName.empty()) {
if(jsonBufferName != defaultBufferName && bufferName == defaultBufferName) {
json->setBufferName(defaultBufferName);
}
}
else {
json->setBufferName(bufferName);
}
}
std::string getBufferName(osg::Geometry* geometry) {
std::string name("");
bool isSpecific = false;
for(std::vector<std::string>::iterator it_flag = _specificBuffers.begin() ;
it_flag != _specificBuffers.end() ; ++ it_flag) {
if(geometry->getUserValue(*it_flag, isSpecific) && isSpecific) {
name = *it_flag;
break;
}
}
return getBinaryFilename(name);
}
JSONObject* createJSONPagedLOD(osg::PagedLOD* plod);
JSONObject* createJSONStateSet(osg::StateSet* ss);
JSONObject* createJSONTexture(osg::Texture* sa);
JSONObject* createJSONMaterial(osg::Material* sa);
JSONObject* createJSONLight(osg::Light* sa);
JSONObject* createJSONCullFace(osg::CullFace* sa);
JSONObject* createJSONBlendColor(osg::BlendColor* sa);
JSONObject* createJSONBlendFunc(osg::BlendFunc* sa);
JSONObject* createJSONBufferArray(osg::Array* array, osg::Geometry* geom = 0);
JSONObject* createJSONDrawElements(osg::DrawArrays* drawArray, osg::Geometry* geom = 0);
JSONObject* createJSONDrawElementsUInt(osg::DrawElementsUInt* de, osg::Geometry* geom = 0);
JSONObject* createJSONDrawElementsUShort(osg::DrawElementsUShort* de, osg::Geometry* geom = 0);
JSONObject* createJSONDrawElementsUByte(osg::DrawElementsUByte* de, osg::Geometry* geom = 0);
JSONObject* createJSONDrawArray(osg::DrawArrays* drawArray, osg::Geometry* geom = 0);
JSONObject* createJSONDrawArrayLengths(osg::DrawArrayLengths* drawArray, osg::Geometry* geom = 0);
JSONObject* createJSONGeometry(osg::Geometry* geom);
JSONObject* getParent() {
if (_parents.empty()) {
_root = new JSONObject;
_parents.push_back(_root.get());
}
return _parents.back().get();
}
void initJsonObjectFromNode(osg::Node& node, JSONObject& json) {
translateObject(&json, &node);
}
void createJSONStateSet(JSONObject* json, osg::StateSet* ss) {
JSONObject* json_stateset = createJSONStateSet(ss);
if (json_stateset) {
JSONObject* obj = new JSONObject;
obj->getMaps()["osg.StateSet"] = json_stateset;
json->getMaps()["StateSet"] = obj;
}
}
void createJSONStateSet(osg::Node& node, JSONObject* json) {
if (node.getStateSet()) {
createJSONStateSet(json, node.getStateSet());
}
}
void applyCallback(osg::Node& node, JSONObject* json) {
JSONArray* updateCallbacks = new JSONArray;
osg::Callback* nc = node.getUpdateCallback();
while (nc) {
osgAnimation::BasicAnimationManager* am = dynamic_cast<osgAnimation::BasicAnimationManager*>(nc);
if (am) {
JSONArray* array = new JSONArray;
JSONObject* bam = new JSONObject;
bam->getMaps()["Animations"] = array;
JSONObject* nodeCallbackObject = new JSONObject;
nodeCallbackObject->getMaps()["osgAnimation.BasicAnimationManager"] = bam;
updateCallbacks->getArray().push_back(nodeCallbackObject);
for ( unsigned int i = 0; i < am->getAnimationList().size(); i++) {
osg::ref_ptr<JSONObject> jsonAnim = createJSONAnimation(am->getAnimationList()[i].get());
if (jsonAnim) {
JSONObject* obj = new JSONObject;
obj->getMaps()["osgAnimation.Animation"] = jsonAnim;
array->getArray().push_back(obj);
//std::stringstream ss;
//jsonAnim->write(ss);
//std::cout << ss.str() << std::endl;
}
}
} else {
osgAnimation::UpdateMatrixTransform* updateMT = dynamic_cast<osgAnimation::UpdateMatrixTransform*>(nc);
if (updateMT) {
osg::ref_ptr<JSONObject> jsonCallback = createJSONUpdateMatrixTransform(*updateMT);
if (jsonCallback.valid()) {
osg::ref_ptr<JSONObject> jsonObject = new JSONObject;
jsonObject->getMaps()["osgAnimation.UpdateMatrixTransform"] = jsonCallback;
updateCallbacks->getArray().push_back(jsonObject);
}
}
}
nc = nc->getNestedCallback();
}
if (!updateCallbacks->getArray().empty()) {
json->getMaps()["UpdateCallbacks"] = updateCallbacks;
}
}
void apply(osg::Drawable& drw) {
osg::Geometry* geom = dynamic_cast<osg::Geometry*>(&drw);
if (geom) {
JSONObject* json = createJSONGeometry(geom);
JSONObject* parent = getParent();
parent->addChild("osg.Geometry", json);
}
}
void apply(osg::Geode& node) {
JSONObject* parent = getParent();
if (_maps.find(&node) != _maps.end()) {
parent->addChild("osg.Node", _maps[&node]->getShadowObject());
return;
}
osg::ref_ptr<JSONObject> json = new JSONNode;
json->addUniqueID();
_maps[&node] = json;
applyCallback(node, json.get());
createJSONStateSet(node, json.get());
parent->addChild("osg.Node", json.get());
initJsonObjectFromNode(node, *json);
_parents.push_back(json);
for (unsigned int i = 0; i < node.getNumDrawables(); ++i) {
if (node.getDrawable(i))
apply(*node.getDrawable(i));
}
_parents.pop_back();
}
void apply(osg::Group& node) {
JSONObject* parent = getParent();
if (_maps.find(&node) != _maps.end()) {
parent->addChild("osg.Node", _maps[&node]->getShadowObject());
return;
}
osg::ref_ptr<JSONObject> json = new JSONNode;
json->addUniqueID();
_maps[&node] = json;
parent->addChild("osg.Node", json.get());
applyCallback(node, json.get());
createJSONStateSet(node, json.get());
initJsonObjectFromNode(node, *json);
_parents.push_back(json);
traverse(node);
_parents.pop_back();
}
void apply(osg::PagedLOD& node)
{
JSONObject* parent = getParent();
if (_maps.find(&node) != _maps.end()) {
parent->addChild("osg.PagedLOD", _maps[&node]->getShadowObject());
return;
}
osg::ref_ptr<JSONObject> json = createJSONPagedLOD(&node);
json->addUniqueID();
_maps[&node] = json;
parent->addChild("osg.PagedLOD", json.get());
applyCallback(node, json.get());
createJSONStateSet(node, json.get());
initJsonObjectFromNode(node, *json);
_parents.push_back(json);
traverse(node);
_parents.pop_back();
}
void apply(osg::LightSource& node) {
JSONObject* parent = getParent();
if (_maps.find(&node) != _maps.end()) {
parent->addChild("osg.LightSource", _maps[&node]->getShadowObject());
return;
}
osg::ref_ptr<JSONObject> json = new JSONNode;
json->addUniqueID();
_maps[&node] = json;
applyCallback(node, json.get());
createJSONStateSet(node, json.get());
parent->addChild("osg.LightSource", json.get());
initJsonObjectFromNode(node, *json);
if (node.getLight()) {
JSONObject* obj = new JSONObject;
obj->getMaps()["osg.Light"] = createJSONLight(node.getLight());
json->getMaps()["Light"] = obj;
}
_parents.push_back(json);
traverse(node);
_parents.pop_back();
}
void apply(osg::Projection& node) {
JSONObject* parent = getParent();
if (_maps.find(&node) != _maps.end()) {
parent->addChild("osg.Projection", _maps[&node]->getShadowObject());
return;
}
osg::ref_ptr<JSONObject> json = new JSONNode;
json->addUniqueID();
_maps[&node] = json;
applyCallback(node, json.get());
createJSONStateSet(node, json.get());
parent->addChild("osg.Projection", json.get());
initJsonObjectFromNode(node, *json);
json->getMaps()["Matrix"] = new JSONMatrix(node.getMatrix());
_parents.push_back(json);
traverse(node);
_parents.pop_back();
}
void apply(osg::MatrixTransform& node) {
JSONObject* parent = getParent();
if (_maps.find(&node) != _maps.end()) {
parent->addChild("osg.MatrixTransform", _maps[&node]->getShadowObject());
return;
}
osg::ref_ptr<JSONObject> json = new JSONNode;
json->addUniqueID();
_maps[&node] = json;
applyCallback(node, json.get());
createJSONStateSet(node, json.get());
parent->addChild("osg.MatrixTransform", json.get());
initJsonObjectFromNode(node, *json);
json->getMaps()["Matrix"] = new JSONMatrix(node.getMatrix());
_parents.push_back(json);
traverse(node);
_parents.pop_back();
}
void apply(osg::PositionAttitudeTransform& node)
{
JSONObject* parent = getParent();
if (_maps.find(&node) != _maps.end()) {
parent->addChild("osg.MatrixTransform", _maps[&node]->getShadowObject());
return;
}
osg::ref_ptr<JSONObject> json = new JSONNode;
json->addUniqueID();
_maps[&node] = json;
applyCallback(node, json.get());
createJSONStateSet(node, json.get());
parent->addChild("osg.MatrixTransform", json.get());
initJsonObjectFromNode(node, *json);
osg::Matrix matrix = osg::Matrix::identity();
node.computeLocalToWorldMatrix(matrix,0);
json->getMaps()["Matrix"] = new JSONMatrix(matrix);
_parents.push_back(json);
traverse(node);
_parents.pop_back();
}
void setBaseName(const std::string& basename) { _baseName = basename; }
void useExternalBinaryArray(bool use) { _useExternalBinaryArray = use; }
void mergeAllBinaryFiles(bool use) { _mergeAllBinaryFiles = use; }
void inlineImages(bool use) { _inlineImages = use; }
void setVarint(bool use) { _varint = use; }
void setMaxTextureDimension(int use) { _maxTextureDimension = use; }
void addSpecificBuffer(const std::string& bufferFlag) { _specificBuffers.push_back(bufferFlag); }
};
#endif

View File

@@ -0,0 +1,829 @@
#include "WriteVisitor"
#include <osgDB/WriteFile>
#include <osgDB/FileUtils>
#include <osgDB/ReadFile>
#include <osgDB/FileNameUtils>
#include <osg/UserDataContainer>
#include <osg/TextureRectangle>
#include <osg/Texture2D>
#include <osg/Texture1D>
#include <osg/Material>
#include <osg/BlendFunc>
#include <osgSim/ShapeAttribute>
#include "Base64"
osg::Array* getTangentSpaceArray(osg::Geometry& geometry) {
for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) {
osg::Array* attribute = geometry.getVertexAttribArray(i);
bool isTangentArray = false;
if(attribute && attribute->getUserValue("tangent", isTangentArray) && isTangentArray) {
return attribute;
}
}
return 0;
}
void translateObject(JSONObject* json, osg::Object* osg)
{
if (!osg->getName().empty()) {
json->getMaps()["Name"] = new JSONValue<std::string>(osg->getName());
}
osgSim::ShapeAttributeList* osgSim_userdata = dynamic_cast<osgSim::ShapeAttributeList* >(osg->getUserData());
if (osgSim_userdata) {
JSONObject* jsonUDC = new JSONObject();
jsonUDC->addUniqueID();
JSONArray* jsonUDCArray = new JSONArray();
jsonUDC->getMaps()["Values"] = jsonUDCArray;
for (unsigned int i = 0; i < osgSim_userdata->size(); i++) {
const osgSim::ShapeAttribute& attr = (*osgSim_userdata)[i];
JSONObject* jsonEntry = new JSONObject();
jsonEntry->getMaps()["Name"] = new JSONValue<std::string>(attr.getName());
osg::ref_ptr<JSONValue<std::string> > value;
switch(attr.getType()) {
case osgSim::ShapeAttribute::INTEGER:
{
std::stringstream ss;
ss << attr.getInt();
value = new JSONValue<std::string>(ss.str());
}
break;
case osgSim::ShapeAttribute::DOUBLE:
{
std::stringstream ss;
ss << attr.getDouble();
value = new JSONValue<std::string>(ss.str());
}
break;
case osgSim::ShapeAttribute::STRING:
{
std::stringstream ss;
ss << attr.getString();
value = new JSONValue<std::string>(ss.str());
}
break;
case osgSim::ShapeAttribute::UNKNOWN:
default:
break;
}
jsonEntry->getMaps()["Value"] = value;
jsonUDCArray->getArray().push_back(jsonEntry);
}
json->getMaps()["UserDataContainer"] = jsonUDC;
} else if (osg->getUserDataContainer()) {
JSONObject* jsonUDC = new JSONObject();
jsonUDC->addUniqueID();
if (!osg->getUserDataContainer()->getName().empty()) {
jsonUDC->getMaps()["Name"] = new JSONValue<std::string>(osg->getUserDataContainer()->getName());
}
JSONArray* jsonUDCArray = new JSONArray();
jsonUDC->getMaps()["Values"] = jsonUDCArray;
for (unsigned int i = 0; i < osg->getUserDataContainer()->getNumUserObjects(); i++) {
osg::Object* o = osg->getUserDataContainer()->getUserObject(i);
std::string name, value;
getStringifiedUserValue(o, name, value);
if(!name.empty() && !value.empty())
{
JSONObject* jsonEntry = new JSONObject();
jsonEntry->getMaps()["Name"] = new JSONValue<std::string>(name);
jsonEntry->getMaps()["Value"] = new JSONValue<std::string>(value);
jsonUDCArray->getArray().push_back(jsonEntry);
}
}
json->getMaps()["UserDataContainer"] = jsonUDC;
}
}
void getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value) {
if(getStringifiedUserValue<std::string>(o, name, value)) return;
if(getStringifiedUserValue<char>(o, name, value)) return;
if(getStringifiedUserValue<bool>(o, name, value)) return;
if(getStringifiedUserValue<short>(o, name, value)) return;
if(getStringifiedUserValue<unsigned short>(o, name, value)) return;
if(getStringifiedUserValue<int>(o, name, value)) return;
if(getStringifiedUserValue<unsigned int>(o, name, value)) return;
if(getStringifiedUserValue<float>(o, name, value)) return;
if(getStringifiedUserValue<double>(o, name, value)) return;
}
template<typename T>
bool getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value) {
osg::TemplateValueObject<T>* vo = dynamic_cast< osg::TemplateValueObject<T>* >(o);
if (vo) {
std::ostringstream oss;
oss << vo->getValue();
name = vo->getName();
value = oss.str();
return true;
}
return false;
}
static JSONValue<std::string>* getBlendFuncMode(GLenum mode) {
switch (mode) {
case osg::BlendFunc::DST_ALPHA: return new JSONValue<std::string>("DST_ALPHA");
case osg::BlendFunc::DST_COLOR: return new JSONValue<std::string>("DST_COLOR");
case osg::BlendFunc::ONE: return new JSONValue<std::string>("ONE");
case osg::BlendFunc::ONE_MINUS_DST_ALPHA: return new JSONValue<std::string>("ONE_MINUS_DST_ALPHA");
case osg::BlendFunc::ONE_MINUS_DST_COLOR: return new JSONValue<std::string>("ONE_MINUS_DST_COLOR");
case osg::BlendFunc::ONE_MINUS_SRC_ALPHA: return new JSONValue<std::string>("ONE_MINUS_SRC_ALPHA");
case osg::BlendFunc::ONE_MINUS_SRC_COLOR: return new JSONValue<std::string>("ONE_MINUS_SRC_COLOR");
case osg::BlendFunc::SRC_ALPHA: return new JSONValue<std::string>("SRC_ALPHA");
case osg::BlendFunc::SRC_ALPHA_SATURATE: return new JSONValue<std::string>("SRC_ALPHA_SATURATE");
case osg::BlendFunc::SRC_COLOR: return new JSONValue<std::string>("SRC_COLOR");
case osg::BlendFunc::CONSTANT_COLOR: return new JSONValue<std::string>("CONSTANT_COLOR");
case osg::BlendFunc::ONE_MINUS_CONSTANT_COLOR: return new JSONValue<std::string>("ONE_MINUS_CONSTANT_COLOR");
case osg::BlendFunc::CONSTANT_ALPHA: return new JSONValue<std::string>("CONSTANT_ALPHA");
case osg::BlendFunc::ONE_MINUS_CONSTANT_ALPHA: return new JSONValue<std::string>("ONE_MINUS_CONSTANT_ALPHA");
case osg::BlendFunc::ZERO: return new JSONValue<std::string>("ZERO");
default:
return new JSONValue<std::string>("ONE");
}
return new JSONValue<std::string>("ONE");
}
static JSONValue<std::string>* getJSONFilterMode(osg::Texture::FilterMode mode)
{
switch(mode) {
case GL_LINEAR:
return new JSONValue<std::string>("LINEAR");
case GL_LINEAR_MIPMAP_LINEAR:
return new JSONValue<std::string>("LINEAR_MIPMAP_LINEAR");
case GL_LINEAR_MIPMAP_NEAREST:
return new JSONValue<std::string>("LINEAR_MIPMAP_NEAREST");
case GL_NEAREST:
return new JSONValue<std::string>("NEAREST");
case GL_NEAREST_MIPMAP_LINEAR:
return new JSONValue<std::string>("NEAREST_MIPMAP_LINEAR");
case GL_NEAREST_MIPMAP_NEAREST:
return new JSONValue<std::string>("NEAREST_MIPMAP_NEAREST");
default:
return 0;
}
return 0;
}
static JSONValue<std::string>* getJSONWrapMode(osg::Texture::WrapMode mode)
{
switch(mode) {
case GL_CLAMP:
// clamp does not exist in opengles 2.0
//return new JSONValue<std::string>("CLAMP");
return new JSONValue<std::string>("CLAMP_TO_EDGE");
case GL_CLAMP_TO_EDGE:
return new JSONValue<std::string>("CLAMP_TO_EDGE");
case GL_CLAMP_TO_BORDER_ARB:
return new JSONValue<std::string>("CLAMP_TO_BORDER");
case GL_REPEAT:
return new JSONValue<std::string>("REPEAT");
case GL_MIRRORED_REPEAT_IBM:
return new JSONValue<std::string>("MIRROR");
default:
return 0;
}
return 0;
}
JSONObject* createImage(osg::Image* image, bool inlineImages, int maxTextureDimension, const std::string &baseName)
{
if (!image) {
osg::notify(osg::WARN) << "unknown image from texture2d " << std::endl;
return new JSONValue<std::string>("/unknown.png");
} else {
std::string modelDir = osgDB::getFilePath(baseName);
if (!image->getFileName().empty() && image->getWriteHint() != osg::Image::STORE_INLINE) {
if(maxTextureDimension) {
int new_s = osg::Image::computeNearestPowerOfTwo(image->s());
int new_t = osg::Image::computeNearestPowerOfTwo(image->t());
bool notValidPowerOf2 = false;
if (new_s != image->s() || image->s() > maxTextureDimension) notValidPowerOf2 = true;
if (new_t != image->t() || image->t() > maxTextureDimension) notValidPowerOf2 = true;
if (notValidPowerOf2) {
image->ensureValidSizeForTexturing(maxTextureDimension);
if(osgDB::isAbsolutePath(image->getFileName()))
osgDB::writeImageFile(*image, image->getFileName());
else
osgDB::writeImageFile(*image,
osgDB::concatPaths(modelDir,
image->getFileName()));
}
}
} else {
// no image file so use this inline name image and create a file
std::stringstream ss;
ss << osgDB::getFilePath(baseName) << osgDB::getNativePathSeparator();
ss << (long int)image << ".inline_conv_generated.png"; // write the pointer location
std::string filename = ss.str();
if (osgDB::writeImageFile(*image, filename)) {
image->setFileName(filename);
}
}
if (!image->getFileName().empty()) { // means that everything went ok
if (inlineImages) {
std::ifstream in(osgDB::findDataFile(image->getFileName()).c_str());
if (in.is_open())
{
std::stringstream out;
out << "data:image/" << osgDB::getLowerCaseFileExtension(image->getFileName()) << ";base64,";
base64::encode(std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(out), false);
return new JSONValue<std::string>(out.str());
}
}
return new JSONValue<std::string>(image->getFileName());
}
}
return 0;
}
JSONObject* WriteVisitor::createJSONBufferArray(osg::Array* array, osg::Geometry* geom)
{
if (_maps.find(array) != _maps.end())
return _maps[array]->getShadowObject();
osg::ref_ptr<JSONBufferArray> json = new JSONBufferArray(array);
json->addUniqueID();
_maps[array] = json;
if(geom && _mergeAllBinaryFiles) {
setBufferName(json.get(), geom);
}
return json.get();
}
JSONObject* WriteVisitor::createJSONDrawElementsUInt(osg::DrawElementsUInt* de, osg::Geometry* geom)
{
if (_maps.find(de) != _maps.end())
return _maps[de]->getShadowObject();
JSONDrawElements<osg::DrawElementsUInt>* json = new JSONDrawElements<osg::DrawElementsUInt>(*de);
json->addUniqueID();
_maps[de] = json;
if(geom && _mergeAllBinaryFiles) {
setBufferName(json, geom);
}
return json;
}
JSONObject* WriteVisitor::createJSONDrawElementsUShort(osg::DrawElementsUShort* de, osg::Geometry* geom)
{
if (_maps.find(de) != _maps.end())
return _maps[de]->getShadowObject();
JSONDrawElements<osg::DrawElementsUShort>* json = new JSONDrawElements<osg::DrawElementsUShort>(*de);
json->addUniqueID();
_maps[de] = json;
if(geom && _mergeAllBinaryFiles) {
setBufferName(json, geom);
}
return json;
}
JSONObject* WriteVisitor::createJSONDrawElementsUByte(osg::DrawElementsUByte* de, osg::Geometry* geom)
{
if (_maps.find(de) != _maps.end())
return _maps[de]->getShadowObject();
JSONDrawElements<osg::DrawElementsUByte>* json = new JSONDrawElements<osg::DrawElementsUByte>(*de);
json->addUniqueID();
_maps[de] = json;
if(geom && _mergeAllBinaryFiles) {
setBufferName(json, geom);
}
return json;
}
// use to convert draw array quads to draw elements triangles
JSONObject* WriteVisitor::createJSONDrawElements(osg::DrawArrays* drawArray, osg::Geometry* geom)
{
if (_maps.find(drawArray) != _maps.end())
return _maps[drawArray]->getShadowObject();
if (drawArray->getMode() != GL_QUADS) {
osg::notify(osg::WARN) << "" << std::endl;
return 0;
}
osg::ref_ptr<osg::DrawElementsUShort> de = new osg::DrawElementsUShort(GL_TRIANGLES);
for (int i = 0; i < drawArray->getCount()/4; ++i) {
int base = drawArray->getFirst() + i*4;
de->push_back(base + 0);
de->push_back(base + 1);
de->push_back(base + 3);
de->push_back(base + 1);
de->push_back(base + 2);
de->push_back(base + 3);
}
JSONDrawElements<osg::DrawElementsUShort>* json = new JSONDrawElements<osg::DrawElementsUShort>(*de);
json->addUniqueID();
_maps[drawArray] = json;
if(geom && _mergeAllBinaryFiles) {
setBufferName(json, geom);
}
return json;
}
JSONObject* WriteVisitor::createJSONDrawArray(osg::DrawArrays* da, osg::Geometry* geom)
{
if (_maps.find(da) != _maps.end())
return _maps[da]->getShadowObject();
osg::ref_ptr<JSONDrawArray> json = new JSONDrawArray(*da);
json->addUniqueID();
_maps[da] = json;
if(geom && _mergeAllBinaryFiles) {
setBufferName(json.get(), geom);
}
return json.get();
}
JSONObject* WriteVisitor::createJSONDrawArrayLengths(osg::DrawArrayLengths* da, osg::Geometry* geom)
{
if (_maps.find(da) != _maps.end())
return _maps[da]->getShadowObject();
osg::ref_ptr<JSONDrawArrayLengths> json = new JSONDrawArrayLengths(*da);
json->addUniqueID();
_maps[da] = json;
if(geom && _mergeAllBinaryFiles) {
setBufferName(json.get(), geom);
}
return json.get();
}
JSONObject* WriteVisitor::createJSONGeometry(osg::Geometry* geom)
{
if (_maps.find(geom) != _maps.end())
return _maps[geom]->getShadowObject();
//if (needToSplit(*geom))
// error();
osg::ref_ptr<JSONObject> json = new JSONNode;
json->addUniqueID();
_maps[geom] = json;
if (geom->getStateSet())
createJSONStateSet(json.get(), geom->getStateSet());
translateObject(json.get(), geom);
osg::ref_ptr<JSONObject> attributes = new JSONObject;
int nbVertexes = 0;
if (geom->getVertexArray()) {
nbVertexes = geom->getVertexArray()->getNumElements();
attributes->getMaps()["Vertex"] = createJSONBufferArray(geom->getVertexArray(), geom);
}
if (geom->getNormalArray()) {
attributes->getMaps()["Normal"] = createJSONBufferArray(geom->getNormalArray(), geom);
int nb = geom->getNormalArray()->getNumElements();
if (nbVertexes != nb) {
osg::notify(osg::FATAL) << "Fatal nb normals " << nb << " != " << nbVertexes << std::endl;
error();
}
}
if (geom->getColorArray()) {
attributes->getMaps()["Color"] = createJSONBufferArray(geom->getColorArray(), geom);
int nb = geom->getColorArray()->getNumElements();
if (nbVertexes != nb) {
osg::notify(osg::FATAL) << "Fatal nb colors " << nb << " != " << nbVertexes << std::endl;
error();
}
}
std::stringstream ss;
for ( int i = 0; i < 32; i++) {
ss.str("");
ss << "TexCoord" << i;
//osg::notify(osg::NOTICE) << ss.str() << std::endl;
if (geom->getTexCoordArray(i)) {
attributes->getMaps()[ss.str()] = createJSONBufferArray(geom->getTexCoordArray(i), geom);
int nb = geom->getTexCoordArray(i)->getNumElements();
if (nbVertexes != nb) {
osg::notify(osg::FATAL) << "Fatal nb tex coord " << i << " " << nb << " != " << nbVertexes << std::endl;
error();
}
}
}
osg::Array* tangents = getTangentSpaceArray(*geom);
if (tangents) {
attributes->getMaps()["Tangent"] = createJSONBufferArray(tangents, geom);
int nb = tangents->getNumElements();
if (nbVertexes != nb) {
osg::notify(osg::FATAL) << "Fatal nb tangent " << nb << " != " << nbVertexes << std::endl;
error();
}
}
json->getMaps()["VertexAttributeList"] = attributes;
if (!geom->getPrimitiveSetList().empty()) {
osg::ref_ptr<JSONArray> primitives = new JSONArray();
for (unsigned int i = 0; i < geom->getNumPrimitiveSets(); ++i) {
osg::ref_ptr<JSONObject> obj = new JSONObject;
osg::PrimitiveSet* primitive = geom->getPrimitiveSet(i);
if(!primitive) continue;
if (primitive->getType() == osg::PrimitiveSet::DrawArraysPrimitiveType) {
osg::DrawArrays* da = dynamic_cast<osg::DrawArrays*>((primitive));
primitives->getArray().push_back(obj);
if (da->getMode() == GL_QUADS) {
obj->getMaps()["DrawElementsUShort"] = createJSONDrawElements(da, geom);
} else {
obj->getMaps()["DrawArrays"] = createJSONDrawArray(da, geom);
}
} else if (primitive->getType() == osg::PrimitiveSet::DrawElementsUIntPrimitiveType) {
osg::DrawElementsUInt* da = dynamic_cast<osg::DrawElementsUInt*>((primitive));
primitives->getArray().push_back(obj);
obj->getMaps()["DrawElementsUInt"] = createJSONDrawElementsUInt(da, geom);
} else if (primitive->getType() == osg::PrimitiveSet::DrawElementsUShortPrimitiveType) {
osg::DrawElementsUShort* da = dynamic_cast<osg::DrawElementsUShort*>((primitive));
primitives->getArray().push_back(obj);
obj->getMaps()["DrawElementsUShort"] = createJSONDrawElementsUShort(da, geom);
} else if (primitive->getType() == osg::PrimitiveSet::DrawElementsUBytePrimitiveType) {
osg::DrawElementsUByte* da = dynamic_cast<osg::DrawElementsUByte*>((primitive));
primitives->getArray().push_back(obj);
obj->getMaps()["DrawElementsUByte"] = createJSONDrawElementsUByte(da, geom);
} else if (primitive->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType) {
osg::DrawArrayLengths* dal = dynamic_cast<osg::DrawArrayLengths*>((primitive));
primitives->getArray().push_back(obj);
obj->getMaps()["DrawArrayLengths"] = createJSONDrawArrayLengths(dal, geom);
} else {
osg::notify(osg::WARN) << "Primitive Type " << geom->getPrimitiveSetList()[i]->getType() << " not supported, skipping" << std::endl;
}
}
json->getMaps()["PrimitiveSetList"] = primitives;
}
return json.get();
}
JSONObject* WriteVisitor::createJSONBlendFunc(osg::BlendFunc* sa)
{
if (_maps.find(sa) != _maps.end())
return _maps[sa]->getShadowObject();
osg::ref_ptr<JSONObject> json = new JSONObject;
json->addUniqueID();
_maps[sa] = json;
translateObject(json.get(), sa);
json->getMaps()["SourceRGB"] = getBlendFuncMode(sa->getSource());
json->getMaps()["DestinationRGB"] = getBlendFuncMode(sa->getDestination());
json->getMaps()["SourceAlpha"] = getBlendFuncMode(sa->getSourceAlpha());
json->getMaps()["DestinationAlpha"] = getBlendFuncMode(sa->getDestinationAlpha());
return json.release();
}
JSONObject* WriteVisitor::createJSONBlendColor(osg::BlendColor* sa)
{
if (_maps.find(sa) != _maps.end())
return _maps[sa]->getShadowObject();
osg::ref_ptr<JSONObject> json = new JSONObject;
json->addUniqueID();
_maps[sa] = json;
translateObject(json.get(), sa);
json->getMaps()["ConstantColor"] = new JSONVec4Array(sa->getConstantColor());
return json.release();
}
JSONObject* WriteVisitor::createJSONCullFace(osg::CullFace* sa)
{
if (_maps.find(sa) != _maps.end())
return _maps[sa]->getShadowObject();
osg::ref_ptr<JSONObject> json = new JSONObject;
json->addUniqueID();
_maps[sa] = json;
translateObject(json.get(), sa);
osg::ref_ptr<JSONValue<std::string> > mode = new JSONValue<std::string>("BACK");
if (sa->getMode() == osg::CullFace::FRONT) {
mode = new JSONValue<std::string>("BACK");
}
if (sa->getMode() == osg::CullFace::FRONT_AND_BACK) {
mode = new JSONValue<std::string>("FRONT_AND_BACK");
}
json->getMaps()["Mode"] = mode;
return json.release();
}
JSONObject* WriteVisitor::createJSONMaterial(osg::Material* material)
{
if (_maps.find(material) != _maps.end())
return _maps[material]->getShadowObject();
osg::ref_ptr<JSONObject> jsonMaterial = new JSONMaterial;
jsonMaterial->addUniqueID();
_maps[material] = jsonMaterial;
translateObject(jsonMaterial.get(), material);
jsonMaterial->getMaps()["Ambient"] = new JSONVec4Array(material->getAmbient(osg::Material::FRONT));
jsonMaterial->getMaps()["Diffuse"] = new JSONVec4Array(material->getDiffuse(osg::Material::FRONT));
jsonMaterial->getMaps()["Specular"] = new JSONVec4Array(material->getSpecular(osg::Material::FRONT));
jsonMaterial->getMaps()["Emission"] = new JSONVec4Array(material->getEmission(osg::Material::FRONT));
jsonMaterial->getMaps()["Shininess"] = new JSONValue<float>(material->getShininess(osg::Material::FRONT));
return jsonMaterial.release();
}
JSONObject* WriteVisitor::createJSONLight(osg::Light* light)
{
if (_maps.find(light) != _maps.end())
return _maps[light]->getShadowObject();
osg::ref_ptr<JSONObject> jsonLight = new JSONLight;
jsonLight->addUniqueID();
_maps[light] = jsonLight;
translateObject(jsonLight.get(), light);
jsonLight->getMaps()["LightNum"] = new JSONValue<int>(light->getLightNum());
jsonLight->getMaps()["Ambient"] = new JSONVec4Array(light->getAmbient());
jsonLight->getMaps()["Diffuse"] = new JSONVec4Array(light->getDiffuse());
jsonLight->getMaps()["Specular"] = new JSONVec4Array(light->getSpecular());
jsonLight->getMaps()["Position"] = new JSONVec4Array(light->getPosition());
jsonLight->getMaps()["Direction"] = new JSONVec3Array(light->getDirection());
jsonLight->getMaps()["ConstantAttenuation"] = new JSONValue<float>(light->getConstantAttenuation());
jsonLight->getMaps()["LinearAttenuation"] = new JSONValue<float>(light->getLinearAttenuation());
jsonLight->getMaps()["QuadraticAttenuation"] = new JSONValue<float>(light->getQuadraticAttenuation());
jsonLight->getMaps()["SpotExponent"] = new JSONValue<float>(light->getSpotExponent());
jsonLight->getMaps()["SpotCutoff"] = new JSONValue<float>(light->getSpotCutoff());
return jsonLight.release();
}
template <class T> JSONObject* createImageFromTexture(osg::Texture* texture, JSONObject* jsonTexture, bool inlineImages,
int maxTextureDimension, const std::string &baseName = "")
{
T* text = dynamic_cast<T*>( texture);
if (text) {
translateObject(jsonTexture,text);
JSONObject* image = createImage(text->getImage(), inlineImages, maxTextureDimension, baseName);
if (image)
jsonTexture->getMaps()["File"] = image;
return jsonTexture;
}
return 0;
}
JSONObject* WriteVisitor::createJSONPagedLOD(osg::PagedLOD *plod)
{
if (!plod) { return 0; }
if (_maps.find(plod) != _maps.end()) {
return _maps[plod]->getShadowObject();
}
osg::ref_ptr<JSONObject> jsonPlod = new JSONNode;
jsonPlod->addUniqueID();
_maps[plod] = jsonPlod;
// Center Mode
osg::ref_ptr<JSONValue<std::string> > centerMode = new JSONValue<std::string>("USE_BOUNDING_SPHERE_CENTER");
if (plod->getCenterMode() == osg::LOD::USER_DEFINED_CENTER) {
centerMode = new JSONValue<std::string>("USER_DEFINED_CENTER");
} else if (plod->getCenterMode() == osg::LOD::UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED){
centerMode = new JSONValue<std::string>("UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED");
}
jsonPlod->getMaps()["CenterMode"] = centerMode;
// User defined center and radius
jsonPlod->getMaps()["UserCenter"] = new JSONVec4Array(osg::Vec4(plod->getCenter().x(), plod->getCenter().y(),plod->getCenter().z(), plod->getRadius()));
// Range Mode
osg::ref_ptr<JSONValue<std::string> > rangeMode = new JSONValue<std::string>("DISTANCE_FROM_EYE_POINT");
if (plod->getRangeMode() == osg::LOD::PIXEL_SIZE_ON_SCREEN) {
rangeMode = new JSONValue<std::string>("PIXEL_SIZE_ON_SCREEN");
}
jsonPlod->getMaps()["RangeMode"] = rangeMode;
// Range List
//osg::ref_ptr<JSONArray> rangeList = new JSONArray;
JSONObject* rangeObject = new JSONObject;
for (unsigned int i =0; i< plod->getRangeList().size(); i++)
{
std::stringstream ss;
ss << "Range ";
ss << i;
std::string str = ss.str();
rangeObject->getMaps()[str] = new JSONVec2Array(osg::Vec2(plod->getRangeList()[i].first, plod->getRangeList()[i].second));
}
jsonPlod->getMaps()["RangeList"] = rangeObject;
// File List
JSONObject* fileObject = new JSONObject;
for (unsigned int i =0; i< plod->getNumFileNames(); i++)
{
std::stringstream ss;
ss << "File ";
ss << i;
std::string str = ss.str();
// We need to convert first from osg format to osgjs format.
osg::ref_ptr<osg::Node> n = osgDB::readNodeFile(plod->getFileName(i)+".gles");
if (n)
{
std::string filename(osgDB::getStrippedName(plod->getFileName(i))+".osgjs");
osgDB::writeNodeFile(*n,filename);
fileObject->getMaps()[str] = new JSONValue<std::string>(filename);
}
else
fileObject->getMaps()[str] = new JSONValue<std::string>("");
}
jsonPlod->getMaps()["RangeDataList"] = fileObject;
return jsonPlod.get();
}
JSONObject* WriteVisitor::createJSONTexture(osg::Texture* texture)
{
if (!texture) {
return 0;
}
if (_maps.find(texture) != _maps.end()) {
return _maps[texture]->getShadowObject();
}
osg::ref_ptr<JSONObject> jsonTexture = new JSONObject;
jsonTexture->addUniqueID();
_maps[texture] = jsonTexture;
jsonTexture->getMaps()["MagFilter"] = getJSONFilterMode(texture->getFilter(osg::Texture::MAG_FILTER));
jsonTexture->getMaps()["MinFilter"] = getJSONFilterMode(texture->getFilter(osg::Texture::MIN_FILTER));
jsonTexture->getMaps()["WrapS"] = getJSONWrapMode(texture->getWrap(osg::Texture::WRAP_S));
jsonTexture->getMaps()["WrapT"] = getJSONWrapMode(texture->getWrap(osg::Texture::WRAP_T));
{
JSONObject* obj = createImageFromTexture<osg::Texture1D>(texture, jsonTexture.get(), this->_inlineImages,
this->_maxTextureDimension, this->_baseName);
if (obj) {
return obj;
}
}
{
JSONObject* obj = createImageFromTexture<osg::Texture2D>(texture, jsonTexture.get(), this->_inlineImages,
this->_maxTextureDimension, this->_baseName);
if (obj) {
return obj;
}
}
{
JSONObject* obj = createImageFromTexture<osg::TextureRectangle>(texture, jsonTexture.get(), this->_inlineImages,
this->_maxTextureDimension, this->_baseName);
if (obj) {
return obj;
}
}
return 0;
}
JSONObject* WriteVisitor::createJSONStateSet(osg::StateSet* stateset)
{
if (_maps.find(stateset) != _maps.end()) {
return _maps[stateset]->getShadowObject();
}
osg::ref_ptr<JSONObject> jsonStateSet = new JSONStateSet;
_maps[stateset] = jsonStateSet;
jsonStateSet->addUniqueID();
translateObject(jsonStateSet.get(), stateset);
if (stateset->getRenderingHint() == osg::StateSet::TRANSPARENT_BIN) {
jsonStateSet->getMaps()["RenderingHint"] = new JSONValue<std::string>("TRANSPARENT_BIN");
}
bool blendEnabled = false;
if (stateset->getMode(GL_BLEND) == osg::StateAttribute::ON) {
blendEnabled = true;
}
osg::ref_ptr<JSONArray> textureAttributeList = new JSONArray;
int lastTextureIndex = -1;
for (int i = 0; i < 32; ++i) {
osg::Texture* texture = dynamic_cast<osg::Texture*>(stateset->getTextureAttribute(i,osg::StateAttribute::TEXTURE));
JSONArray* textureUnit = new JSONArray;
JSONObject* jsonTexture = createJSONTexture(texture);
textureAttributeList->getArray().push_back(textureUnit);
if (jsonTexture) {
JSONObject* textureObject = new JSONObject;
textureObject->getMaps()["osg.Texture"] = jsonTexture;
textureUnit->getArray().push_back(textureObject);
lastTextureIndex = i;
}
}
if (lastTextureIndex > -1) {
textureAttributeList->getArray().resize(lastTextureIndex+1);
jsonStateSet->getMaps()["TextureAttributeList"] = textureAttributeList;
}
osg::ref_ptr<JSONArray> attributeList = new JSONArray;
osg::Material* material = dynamic_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
if (material) {
JSONObject* obj = new JSONObject;
obj->getMaps()["osg.Material"] = createJSONMaterial(material);
attributeList->getArray().push_back(obj);
}
osg::BlendFunc* blendFunc = dynamic_cast<osg::BlendFunc*>(stateset->getAttribute(osg::StateAttribute::BLENDFUNC));
if (blendFunc) {
JSONObject* obj = new JSONObject;
obj->getMaps()["osg.BlendFunc"] = createJSONBlendFunc(blendFunc);
attributeList->getArray().push_back(obj);
} else if (blendEnabled == true) {
JSONObject* obj = new JSONObject;
osg::ref_ptr<osg::BlendFunc> defaultBlend = new osg::BlendFunc();
obj->getMaps()["osg.BlendFunc"] = createJSONBlendFunc(defaultBlend.get());
attributeList->getArray().push_back(obj);
}
osg::ref_ptr<osg::CullFace> cullFace = dynamic_cast<osg::CullFace*>(stateset->getAttribute(osg::StateAttribute::CULLFACE));
osg::StateAttribute::GLModeValue cullMode = stateset->getMode(GL_CULL_FACE);
if (cullFace || cullMode != osg::StateAttribute::INHERIT) {
JSONObject* obj = new JSONObject;
JSONObject* cf = 0;
if (cullMode == osg::StateAttribute::OFF) {
osg::ref_ptr<osg::CullFace> defaultCull = new osg::CullFace();
cf = createJSONCullFace(defaultCull.get());
cf->getMaps()["Mode"] = new JSONValue<std::string>("DISABLE");
obj->getMaps()["osg.CullFace"] = cf;
attributeList->getArray().push_back(obj);
} else {
if (!cullFace) {
cullFace = new osg::CullFace();
}
cf = createJSONCullFace(cullFace.get());
}
obj->getMaps()["osg.CullFace"] = cf;
attributeList->getArray().push_back(obj);
}
osg::BlendColor* blendColor = dynamic_cast<osg::BlendColor*>(stateset->getAttribute(osg::StateAttribute::BLENDCOLOR));
if (blendColor) {
JSONObject* obj = new JSONObject;
obj->getMaps()["osg.BlendColor"] = createJSONBlendColor(blendColor);
attributeList->getArray().push_back(obj);
}
if (!attributeList->getArray().empty()) {
jsonStateSet->getMaps()["AttributeList"] = attributeList;
}
osg::StateSet::ModeList modeList = stateset->getModeList();
for (unsigned int i = 0; i < modeList.size(); ++i) {
// add modes
}
if (jsonStateSet->getMaps().empty())
return 0;
return jsonStateSet.release();
}

View File

@@ -0,0 +1,274 @@
#ifndef JSON_STREAM
#define JSON_STREAM
#include <iostream>
#include <iomanip>
#include <cctype> // control characters
#include <sstream>
#include <fstream>
#include <string>
#include <cmath>
#include <limits>
// A simple class wrapping ofstream calls to enable generic cleaning of json data.
// Especially 'standard' json should:
// * have utf-8 encoded string
// * disallow some control characters
// * does not support inf or nan values
class json_stream : public std::ofstream {
public:
json_stream(const std::string& filename, bool strict=true) :
_stream(filename.c_str()),
_strict(strict)
{}
~json_stream() {
_stream.close();
}
operator bool() const {
return _stream.is_open();
}
// forward std::endl
typedef std::ostream& (*ostream_manipulator)(std::ostream&);
json_stream& operator<<(ostream_manipulator pf) {
if (_stream.is_open()) {
_stream << pf;
}
return *this;
}
template<typename T>
json_stream& operator<<(const T& data) {
if (_stream.is_open()) {
_stream << sanitize(data);
}
return *this;
}
template<typename T>
const T sanitize(const T& t) {
return t;
}
double sanitize(const float f) {
return sanitize(static_cast<double>(f));
}
double sanitize(const double d) {
if(_strict) {
return to_valid_float(d);
}
return d;
}
double to_valid_float(const double d) {
if(std::isfinite(d)) {
return d;
}
else {
if(std::isinf(d)) {
return std::numeric_limits<double>::max();
}
// no much way to do better than replace invalid float NaN by 0
return 0.;
}
}
std::string sanitize(const std::string& s) {
if(_strict) {
return to_json_utf8(s);
}
return s;
}
std::string sanitize(const char* s) {
return sanitize(std::string(s));
}
std::string to_json_utf8(const std::string& s) {
// TODO: try to decode latin1 if string is not valid utf8
// before actually fixing bad 'chars'
return clean_invalid_utf8(s);
}
protected:
std::ofstream _stream;
bool _strict;
std::string json_encode_control_char(int ctrl) {
// see http://json.org
std::ostringstream oss;
if(ctrl == 8 || // \b
ctrl == 9 || // \t
ctrl == 10 || // \n
ctrl == 12 || // \f
ctrl == 13 || // \r
ctrl == 27 || //
ctrl == 34 || // \"
ctrl == 47 // \/
) {
oss << static_cast<char>(ctrl);
}
else {
oss.fill('0');
oss << "\\u" << std::setw(4) << std::hex << ctrl;
}
return oss.str();
}
inline bool is_valid_continuation_byte(unsigned int byte) {
return ((byte & 0xC0) == 0x80);
}
inline int get_next_byte(std::string::const_iterator& iterator, std::string::const_iterator end_iterator) {
if(iterator != end_iterator) {
return *(++ iterator);
}
else {
return 0; // invalid continuation byte
}
}
// from http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
std::string utf8_encode_codepoint(unsigned code_point)
{
std::string output;
if(code_point > 0x590 && code_point < 0x5F4) {
return output;
}
// out of range
if(code_point > 1114112) {
return utf8_encode_codepoint(0xfffd);
}
if (code_point < 0x80) {
output.push_back(code_point);
}
else if (code_point <= 0x7FF) {
output.push_back((code_point >> 6) + 0xC0);
output.push_back((code_point & 0x3F) + 0x80);
}
else if (code_point <= 0xFFFF) {
output.push_back((code_point >> 12) + 0xE0);
output.push_back(((code_point >> 6) & 0x3F) + 0x80);
output.push_back((code_point & 0x3F) + 0x80);
}
else if (code_point <= 0x10FFFF) {
output.push_back((code_point >> 18) + 0xF0);
output.push_back(((code_point >> 12) & 0x3F) + 0x80);
output.push_back(((code_point >> 6) & 0x3F) + 0x80);
output.push_back((code_point & 0x3F) + 0x80);
}
return output;
}
// from http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
std::string clean_invalid_utf8(const std::string& input,
const int replacement_codepoint=0xfffd) {
int code_unit1, code_unit2, code_unit3, code_unit4;
std::string output, replacement = utf8_encode_codepoint(replacement_codepoint);
for(std::string::const_iterator iterator = input.begin() ; iterator != input.end() ; ++ iterator) {
code_unit1 = *iterator;
if (code_unit1 < 0x80) {
if(std::iscntrl(code_unit1)) {
output += json_encode_control_char(code_unit1);
}
else {
output.push_back(code_unit1);
}
}
else if (code_unit1 < 0xC2) {
// continuation or overlong 2-byte sequence
output += replacement;
}
else if (code_unit1 < 0xE0) {
// 2-byte sequence
code_unit2 = get_next_byte(iterator, input.end());
if (!is_valid_continuation_byte(code_unit2)) {
output += replacement;
output += replacement;
}
else {
output += utf8_encode_codepoint((code_unit1 << 6) + code_unit2 - 0x3080);
}
}
else if (code_unit1 < 0xF0) {
// 3-byte sequence
code_unit2 = get_next_byte(iterator, input.end());
if (!is_valid_continuation_byte(code_unit2) ||
(code_unit1 == 0xE0 && code_unit2 < 0xA0)) /* overlong */ {
output += replacement;
output += replacement;
}
else {
code_unit3 = get_next_byte(iterator, input.end());
if (!is_valid_continuation_byte(code_unit3)) {
output += replacement;
output += replacement;
output += replacement;
}
else {
output += utf8_encode_codepoint((code_unit1 << 12) +
(code_unit2 << 6) +
code_unit3 - 0xE2080);
}
}
}
else if (code_unit1 < 0xF5) {
// 4-byte sequence
code_unit2 = get_next_byte(iterator, input.end());
if(!is_valid_continuation_byte(code_unit2) ||
(code_unit1 == 0xF0 && code_unit2 < 0x90) || /* overlong */
(code_unit1 == 0xF4 && code_unit2 >= 0x90)) { /* > U+10FFFF */
output += replacement;
output += replacement;
}
else {
code_unit3 = get_next_byte(iterator, input.end());
if(!is_valid_continuation_byte(code_unit3)) {
output += replacement;
output += replacement;
output += replacement;
}
else {
code_unit4 = get_next_byte(iterator, input.end());
if(!is_valid_continuation_byte(code_unit4)) {
output += replacement;
output += replacement;
output += replacement;
output += replacement;
}
else {
output += utf8_encode_codepoint((code_unit1 << 18) +
(code_unit2 << 12) +
(code_unit3 << 6) +
code_unit4 - 0x3C82080);
}
}
}
}
else {
/* > U+10FFFF */
output += replacement;
}
}
return output;
}
};
#endif