From 2a84719f4aef6f0f2722622290ee3b696d6b9980 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 9 Mar 2015 11:27:26 +0000 Subject: [PATCH] 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 --- src/osgPlugins/CMakeLists.txt | 3 + src/osgPlugins/gles/AnimationVisitor | 31 + src/osgPlugins/gles/BindPerVertexVisitor | 242 +++++ src/osgPlugins/gles/CMakeLists.txt | 12 + src/osgPlugins/gles/DetachPrimitiveVisitor | 97 ++ src/osgPlugins/gles/DrawArrayVisitor | 88 ++ src/osgPlugins/gles/EdgeIndexFunctor | 277 ++++++ src/osgPlugins/gles/GeometryArray | 339 +++++++ src/osgPlugins/gles/GeometrySplitterVisitor | 381 ++++++++ src/osgPlugins/gles/GeometryUniqueVisitor | 66 ++ src/osgPlugins/gles/IndexMeshVisitor | 34 + src/osgPlugins/gles/IndexMeshVisitor.cpp | 174 ++++ src/osgPlugins/gles/LineIndexFunctor | 178 ++++ src/osgPlugins/gles/OpenGLESGeometryOptimizer | 143 +++ .../gles/OpenGLESGeometryOptimizer.cpp | 54 ++ src/osgPlugins/gles/PointIndexFunctor | 153 ++++ src/osgPlugins/gles/PreTransformVisitor | 35 + src/osgPlugins/gles/PrimitiveIndexors | 114 +++ src/osgPlugins/gles/ReaderWriterGLES.cpp | 272 ++++++ src/osgPlugins/gles/StatLogger | 32 + src/osgPlugins/gles/TangentSpaceVisitor | 106 +++ .../gles/TriangleLinePointIndexFunctor | 257 ++++++ src/osgPlugins/gles/TriangleStripVisitor | 35 + src/osgPlugins/gles/TriangleStripVisitor.cpp | 94 ++ src/osgPlugins/gles/UnIndexMeshVisitor | 28 + src/osgPlugins/gles/UnIndexMeshVisitor.cpp | 128 +++ src/osgPlugins/gles/WireframeVisitor | 84 ++ .../gles/forsythtriangleorderoptimizer.cpp | 346 ++++++++ .../gles/forsythtriangleorderoptimizer.h | 46 + src/osgPlugins/gles/glesUtil | 574 ++++++++++++ src/osgPlugins/osgjs/Adaptator | 117 +++ src/osgPlugins/osgjs/Animation | 12 + src/osgPlugins/osgjs/Animation.cpp | 203 +++++ src/osgPlugins/osgjs/Base64 | 170 ++++ src/osgPlugins/osgjs/Base64.cpp | 42 + src/osgPlugins/osgjs/CMakeLists.txt | 24 + src/osgPlugins/osgjs/CompactBufferVisitor | 115 +++ src/osgPlugins/osgjs/JSON_Objects | 372 ++++++++ src/osgPlugins/osgjs/JSON_Objects.cpp | 713 +++++++++++++++ src/osgPlugins/osgjs/ReaderWriterJSON.cpp | 243 +++++ src/osgPlugins/osgjs/WriteVisitor | 451 ++++++++++ src/osgPlugins/osgjs/WriteVisitor.cpp | 829 ++++++++++++++++++ src/osgPlugins/osgjs/json_stream | 274 ++++++ 43 files changed, 7988 insertions(+) create mode 100644 src/osgPlugins/gles/AnimationVisitor create mode 100644 src/osgPlugins/gles/BindPerVertexVisitor create mode 100644 src/osgPlugins/gles/CMakeLists.txt create mode 100644 src/osgPlugins/gles/DetachPrimitiveVisitor create mode 100644 src/osgPlugins/gles/DrawArrayVisitor create mode 100644 src/osgPlugins/gles/EdgeIndexFunctor create mode 100644 src/osgPlugins/gles/GeometryArray create mode 100644 src/osgPlugins/gles/GeometrySplitterVisitor create mode 100644 src/osgPlugins/gles/GeometryUniqueVisitor create mode 100644 src/osgPlugins/gles/IndexMeshVisitor create mode 100644 src/osgPlugins/gles/IndexMeshVisitor.cpp create mode 100644 src/osgPlugins/gles/LineIndexFunctor create mode 100644 src/osgPlugins/gles/OpenGLESGeometryOptimizer create mode 100644 src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp create mode 100644 src/osgPlugins/gles/PointIndexFunctor create mode 100644 src/osgPlugins/gles/PreTransformVisitor create mode 100644 src/osgPlugins/gles/PrimitiveIndexors create mode 100644 src/osgPlugins/gles/ReaderWriterGLES.cpp create mode 100644 src/osgPlugins/gles/StatLogger create mode 100644 src/osgPlugins/gles/TangentSpaceVisitor create mode 100644 src/osgPlugins/gles/TriangleLinePointIndexFunctor create mode 100644 src/osgPlugins/gles/TriangleStripVisitor create mode 100644 src/osgPlugins/gles/TriangleStripVisitor.cpp create mode 100644 src/osgPlugins/gles/UnIndexMeshVisitor create mode 100644 src/osgPlugins/gles/UnIndexMeshVisitor.cpp create mode 100644 src/osgPlugins/gles/WireframeVisitor create mode 100644 src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp create mode 100644 src/osgPlugins/gles/forsythtriangleorderoptimizer.h create mode 100644 src/osgPlugins/gles/glesUtil create mode 100644 src/osgPlugins/osgjs/Adaptator create mode 100644 src/osgPlugins/osgjs/Animation create mode 100644 src/osgPlugins/osgjs/Animation.cpp create mode 100644 src/osgPlugins/osgjs/Base64 create mode 100644 src/osgPlugins/osgjs/Base64.cpp create mode 100644 src/osgPlugins/osgjs/CMakeLists.txt create mode 100644 src/osgPlugins/osgjs/CompactBufferVisitor create mode 100644 src/osgPlugins/osgjs/JSON_Objects create mode 100644 src/osgPlugins/osgjs/JSON_Objects.cpp create mode 100644 src/osgPlugins/osgjs/ReaderWriterJSON.cpp create mode 100644 src/osgPlugins/osgjs/WriteVisitor create mode 100644 src/osgPlugins/osgjs/WriteVisitor.cpp create mode 100644 src/osgPlugins/osgjs/json_stream diff --git a/src/osgPlugins/CMakeLists.txt b/src/osgPlugins/CMakeLists.txt index e91ddae63..151f49b0e 100644 --- a/src/osgPlugins/CMakeLists.txt +++ b/src/osgPlugins/CMakeLists.txt @@ -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) diff --git a/src/osgPlugins/gles/AnimationVisitor b/src/osgPlugins/gles/AnimationVisitor new file mode 100644 index 000000000..24a053b89 --- /dev/null +++ b/src/osgPlugins/gles/AnimationVisitor @@ -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 +#include +#include +#include + + +// the idea is to create true Geometry if skeleton with RigGeometry +class AnimationVisitor : public osgUtil::UpdateVisitor +{ +public: + AnimationVisitor() { + setFrameStamp(new osg::FrameStamp()); + } +}; + +#endif diff --git a/src/osgPlugins/gles/BindPerVertexVisitor b/src/osgPlugins/gles/BindPerVertexVisitor new file mode 100644 index 000000000..e60a23a96 --- /dev/null +++ b/src/osgPlugins/gles/BindPerVertexVisitor @@ -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(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + } + + template + bool doConvert(osg::Array* src, + osg::Geometry::AttributeBinding fromBinding, + osg::Geometry::PrimitiveSetList& primitives) { + T* array= dynamic_cast(src); + if (array) { + convert(*array, fromBinding, primitives); + return true; + } + return false; + } + + template + void convert(T& array, + osg::Geometry::AttributeBinding fromBinding, + osg::Geometry::PrimitiveSetList& primitives) + { + osg::ref_ptr 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 diff --git a/src/osgPlugins/gles/CMakeLists.txt b/src/osgPlugins/gles/CMakeLists.txt new file mode 100644 index 000000000..51aefc4b2 --- /dev/null +++ b/src/osgPlugins/gles/CMakeLists.txt @@ -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) diff --git a/src/osgPlugins/gles/DetachPrimitiveVisitor b/src/osgPlugins/gles/DetachPrimitiveVisitor new file mode 100644 index 000000000..c7649701f --- /dev/null +++ b/src/osgPlugins/gles/DetachPrimitiveVisitor @@ -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 +#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 diff --git a/src/osgPlugins/gles/DrawArrayVisitor b/src/osgPlugins/gles/DrawArrayVisitor new file mode 100644 index 000000000..332d21ced --- /dev/null +++ b/src/osgPlugins/gles/DrawArrayVisitor @@ -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(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(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 diff --git a/src/osgPlugins/gles/EdgeIndexFunctor b/src/osgPlugins/gles/EdgeIndexFunctor new file mode 100644 index 000000000..79b1293f0 --- /dev/null +++ b/src/osgPlugins/gles/EdgeIndexFunctor @@ -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 +#include + + +template +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 + 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(mode, count, indices); + } + + virtual void drawElements(GLenum mode, GLsizei count, const GLushort* indices) { + drawElements(mode, count, indices); + } + + virtual void drawElements(GLenum mode, GLsizei count, const GLuint* indices) { + drawElements(mode, count, indices); + } + + + GLenum _modeCache; + std::vector _indexCache; +}; + +#endif diff --git a/src/osgPlugins/gles/GeometryArray b/src/osgPlugins/gles/GeometryArray new file mode 100644 index 000000000..50d556a6f --- /dev/null +++ b/src/osgPlugins/gles/GeometryArray @@ -0,0 +1,339 @@ +/* -*-c++-*- */ +#ifndef GEOMETRY_ARRAY_UTILS_H +#define GEOMETRY_ARRAY_UTILS_H + +#include +#include +#include +#include + + +typedef std::vector 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 + inline void copy(T& array) + { + if (!_dst) { + osg::notify(osg::WARN) << "Can't append to array null" << std::endl; + return; + } + + T* dstArray = dynamic_cast(_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 bool arrayAppendElement(osg::Array* src, unsigned int index, osg::Array* dst) + { + T* array= dynamic_cast(src); + if (array) { + T* arrayDst = dynamic_cast(dst); + arrayDst->push_back((*array)[index]); + return true; + } + return false; + } + + void operator()(osg::Array* src, unsigned int index, osg::Array* dst) { + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + } + }; + + struct ArraySetNumElements { + + template bool arraySetNumElements(osg::Array* src, unsigned int numElements) + { + T* array= dynamic_cast(src); + if (array) { + array->resize(numElements); + return true; + } + return false; + } + + void operator()(osg::Array* array, unsigned int numElements) { + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + } + }; + + osg::ref_ptr _vertexes; + osg::ref_ptr _normals; + osg::ref_ptr _colors; + osg::ref_ptr _secondaryColors; + osg::ref_ptr _fogCoords; + std::vector > _texCoordArrays; + std::vector > _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;igetNumElements()) + _texCoordArrays[i] = geometry.getTexCoordArray(i); + + _attributesArrays.resize(geometry.getNumVertexAttribArrays()); + for(unsigned int i=0;igetNumElements()) + _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(_vertexes->cloneType()); + + if (_normals.valid()) + array._normals = dynamic_cast(_normals->cloneType()); + + if (_colors.valid()) + array._colors = dynamic_cast(_colors->cloneType()); + + if (_secondaryColors.valid()) + array._secondaryColors = dynamic_cast(_secondaryColors->cloneType()); + + if (_fogCoords.valid()) + array._fogCoords = dynamic_cast(_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(_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(_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 diff --git a/src/osgPlugins/gles/GeometrySplitterVisitor b/src/osgPlugins/gles/GeometrySplitterVisitor new file mode 100644 index 000000000..ba64d9692 --- /dev/null +++ b/src/osgPlugins/gles/GeometrySplitterVisitor @@ -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 +#include + +#include +#include +#include +#include +#include + +#include "GeometryArray" +#include "GeometryUniqueVisitor" +#include "glesUtil" + + +class GeometryIndexSplitter +{ +public: + typedef std::vector > 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 processing = osg::clone(&geometry, osg::CopyOp::DEEP_COPY_ARRAYS | + osg::CopyOp::DEEP_COPY_PRIMITIVES | + osg::CopyOp::DEEP_COPY_USERDATA); + osg::ref_ptr 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(geometry.getVertexArray())); + // uvs + for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) { + setBufferBoundingBox(dynamic_cast(geometry.getTexCoordArray(i))); + } + } + + template + 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 > primitivesToFix; + + osg::Geometry::PrimitiveSetList& primitives = geometry.getPrimitiveSetList(); + std::set validIndices; + osg::ref_ptr 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(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& 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& 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(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 small = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES); + osg::ref_ptr 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 > 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::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(node, GeometryList(list))); + } + + unsigned int _maxIndexValue; + std::map _split; + bool _disablePostTransform; +}; + +#endif diff --git a/src/osgPlugins/gles/GeometryUniqueVisitor b/src/osgPlugins/gles/GeometryUniqueVisitor new file mode 100644 index 000000000..c89b02bf1 --- /dev/null +++ b/src/osgPlugins/gles/GeometryUniqueVisitor @@ -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 +#include +#include + +#include +#include + +#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 _processed; + StatLogger _logger; +}; + +#endif diff --git a/src/osgPlugins/gles/IndexMeshVisitor b/src/osgPlugins/gles/IndexMeshVisitor new file mode 100644 index 000000000..8c4326e7a --- /dev/null +++ b/src/osgPlugins/gles/IndexMeshVisitor @@ -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 IndexList; + void addDrawElements(IndexList&, + osg::PrimitiveSet::Mode, + osg::Geometry::PrimitiveSetList&, + std::string userValue = std::string()); +}; + +#endif diff --git a/src/osgPlugins/gles/IndexMeshVisitor.cpp b/src/osgPlugins/gles/IndexMeshVisitor.cpp new file mode 100644 index 000000000..4c9d86198 --- /dev/null +++ b/src/osgPlugins/gles/IndexMeshVisitor.cpp @@ -0,0 +1,174 @@ +#include +#include // sort +#include // numeric_limits + +#include +#include +#include +#include + +#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 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); + } +} diff --git a/src/osgPlugins/gles/LineIndexFunctor b/src/osgPlugins/gles/LineIndexFunctor new file mode 100644 index 000000000..f4862d2fd --- /dev/null +++ b/src/osgPlugins/gles/LineIndexFunctor @@ -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 +#include + +#include +#include + + +class Line { +public: + Line(unsigned int a, unsigned int b) { + _a = std::min(a, b); + _b = std::max(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 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 + 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(mode, count, indices); + } + + virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) { + drawElements(mode, count, indices); + } + + virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) { + drawElements(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 _indexCache; + std::set _lineCache; +}; + +#endif diff --git a/src/osgPlugins/gles/OpenGLESGeometryOptimizer b/src/osgPlugins/gles/OpenGLESGeometryOptimizer new file mode 100644 index 000000000..e0361632a --- /dev/null +++ b/src/osgPlugins/gles/OpenGLESGeometryOptimizer @@ -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 +#include //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(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 diff --git a/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp b/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp new file mode 100644 index 000000000..04873b9aa --- /dev/null +++ b/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp @@ -0,0 +1,54 @@ +#include + +#include "OpenGLESGeometryOptimizer" +#include "glesUtil" + +const unsigned glesUtil::Remapper::invalidIndex = std::numeric_limits::max(); + + +osg::Node* OpenGLESGeometryOptimizer::optimize(osg::Node& node) { + osg::ref_ptr 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(); +} diff --git a/src/osgPlugins/gles/PointIndexFunctor b/src/osgPlugins/gles/PointIndexFunctor new file mode 100644 index 000000000..bfd244515 --- /dev/null +++ b/src/osgPlugins/gles/PointIndexFunctor @@ -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 +#include + + +template +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;ioperator()(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;iptroperator()(*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;iptroperator()(*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;iptroperator()(*iptr); + } + break; + default: + break; + } + } + + GLenum _modeCache; + std::vector _indexCache; +}; + +#endif diff --git a/src/osgPlugins/gles/PreTransformVisitor b/src/osgPlugins/gles/PreTransformVisitor new file mode 100644 index 000000000..f59ce70a1 --- /dev/null +++ b/src/osgPlugins/gles/PreTransformVisitor @@ -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 diff --git a/src/osgPlugins/gles/PrimitiveIndexors b/src/osgPlugins/gles/PrimitiveIndexors new file mode 100644 index 000000000..ea3eba706 --- /dev/null +++ b/src/osgPlugins/gles/PrimitiveIndexors @@ -0,0 +1,114 @@ +#ifndef PRIMITIVE_OPERATORS_H +#define PRIMITIVE_OPERATORS_H + +#include +#include +#include + +#include "TriangleLinePointIndexFunctor" +#include "EdgeIndexFunctor" +#include "LineIndexFunctor" +#include "PointIndexFunctor" + + +typedef std::vector 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 TriangleIndexor; +typedef EdgeIndexFunctor EdgeIndexor; +typedef LineIndexFunctor LineIndexor; // indexes only primitives in LINES, LINE_STRIP, LINE_LOOP mode +typedef PointIndexFunctor PointIndexor; + +#endif diff --git a/src/osgPlugins/gles/ReaderWriterGLES.cpp b/src/osgPlugins/gles/ReaderWriterGLES.cpp new file mode 100644 index 000000000..03a94bcdb --- /dev/null +++ b/src/osgPlugins/gles/ReaderWriterGLES.cpp @@ -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 +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#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=","Specify on wich texture unit normal map is"); + supportsOption("triStripCacheSize=","set the cache size when doing tristrip"); + supportsOption("triStripMinSize=","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=","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 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 optimizedNode = optimizeModel(node, _options); + + // forward writing to next plugin + ref_ptr 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 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) diff --git a/src/osgPlugins/gles/StatLogger b/src/osgPlugins/gles/StatLogger new file mode 100644 index 000000000..7bc9f2e3d --- /dev/null +++ b/src/osgPlugins/gles/StatLogger @@ -0,0 +1,32 @@ +#ifndef STAT_LOGGER +#define STAT_LOGGER + +#include +#include + + +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 diff --git a/src/osgPlugins/gles/TangentSpaceVisitor b/src/osgPlugins/gles/TangentSpaceVisitor new file mode 100644 index 000000000..778fa299b --- /dev/null +++ b/src/osgPlugins/gles/TangentSpaceVisitor @@ -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 +#include // {get,set}UserValue +#include + +#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 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(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 diff --git a/src/osgPlugins/gles/TriangleLinePointIndexFunctor b/src/osgPlugins/gles/TriangleLinePointIndexFunctor new file mode 100644 index 000000000..877e2a6ee --- /dev/null +++ b/src/osgPlugins/gles/TriangleLinePointIndexFunctor @@ -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 +#include + + +template +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 + 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(mode, count, indices); + } + + virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) { + drawElements(mode, count, indices); + } + + virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) { + drawElements(mode, count, indices); + } + + + GLenum _modeCache; + std::vector _indexCache; + std::vector _remap; +}; + +#endif diff --git a/src/osgPlugins/gles/TriangleStripVisitor b/src/osgPlugins/gles/TriangleStripVisitor new file mode 100644 index 000000000..11f0439b8 --- /dev/null +++ b/src/osgPlugins/gles/TriangleStripVisitor @@ -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 diff --git a/src/osgPlugins/gles/TriangleStripVisitor.cpp b/src/osgPlugins/gles/TriangleStripVisitor.cpp new file mode 100644 index 000000000..47e430f1b --- /dev/null +++ b/src/osgPlugins/gles/TriangleStripVisitor.cpp @@ -0,0 +1,94 @@ +#include +#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 (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); + } +} diff --git a/src/osgPlugins/gles/UnIndexMeshVisitor b/src/osgPlugins/gles/UnIndexMeshVisitor new file mode 100644 index 000000000..76f281211 --- /dev/null +++ b/src/osgPlugins/gles/UnIndexMeshVisitor @@ -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 diff --git a/src/osgPlugins/gles/UnIndexMeshVisitor.cpp b/src/osgPlugins/gles/UnIndexMeshVisitor.cpp new file mode 100644 index 000000000..64df5b9dc --- /dev/null +++ b/src/osgPlugins/gles/UnIndexMeshVisitor.cpp @@ -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 +#include + +#include "UnIndexMeshVisitor" +#include "GeometryArray" +#include "PrimitiveIndexors" + +typedef std::vector 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); +} diff --git a/src/osgPlugins/gles/WireframeVisitor b/src/osgPlugins/gles/WireframeVisitor new file mode 100644 index 000000000..8f8a68e6c --- /dev/null +++ b/src/osgPlugins/gles/WireframeVisitor @@ -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 + +#include +#include +#include + +#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 _processed; + bool _inlined; +}; + +#endif diff --git a/src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp b/src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp new file mode 100644 index 000000000..cee462228 --- /dev/null +++ b/src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp @@ -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 +#include +#include +#include +#include + +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(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 vertexDataList; + vertexDataList.resize(vertexCount); + + // compute face count per vertex + for (unsigned int i=0; i activeFaceList; + + const unsigned int kEvictedCacheIndex = std::numeric_limits::max(); + + { + // allocate face list per vertex + unsigned int curActiveFaceListPos = 0; + for (unsigned int i=0; i 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 bestScore) + { + bestScore = faceScore; + bestFace = face; + } + } + } + + std::swap(cache0, cache1); + entriesInCache0 = std::min(entriesInCache1, lruCacheSize); + } + } + +} // namespace Forsyth diff --git a/src/osgPlugins/gles/forsythtriangleorderoptimizer.h b/src/osgPlugins/gles/forsythtriangleorderoptimizer.h new file mode 100644 index 000000000..d0fee89de --- /dev/null +++ b/src/osgPlugins/gles/forsythtriangleorderoptimizer.h @@ -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__ diff --git a/src/osgPlugins/gles/glesUtil b/src/osgPlugins/gles/glesUtil new file mode 100644 index 000000000..c8f5909f4 --- /dev/null +++ b/src/osgPlugins/gles/glesUtil @@ -0,0 +1,574 @@ +#ifndef GLES_UTIL +#define GLES_UTIL + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "TriangleLinePointIndexFunctor" +#include "StatLogger" +#include "forsythtriangleorderoptimizer.h" + + +namespace glesUtil { + using namespace std; + using namespace osg; + typedef std::vector 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 VertexList; + + struct Triangle + { + unsigned verts[3]; + }; + typedef vector 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 + { + TriangleCounter(vector* 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 + { + 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 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 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& 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 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 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 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 + 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& remapping) + : _remapping(remapping), _newsize(0) + { + for (vector::const_iterator itr = _remapping.begin(), + end = _remapping.end(); + itr != end; + ++itr) + if (*itr != invalidIndex) + ++_newsize; + } + + const vector& _remapping; + size_t _newsize; + + template + inline void remap(T& array) + { + ref_ptr 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 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 + { + VertexReorder(unsigned numVerts) + { + remap.resize(numVerts, glesUtil::Remapper::invalidIndex); + } + }; + + + template + inline void reorderDrawElements(DE& drawElements, + const vector& reorder) + { + for (typename DE::iterator itr = drawElements.begin(), end = drawElements.end(); + itr != end; + ++itr) + { + *itr = static_cast(reorder[*itr]); + } + } + + + class VertexAccessOrderVisitor : osgUtil::VertexAccessOrderVisitor + { + struct OrderByPrimitiveMode + { + inline bool operator() (const osg::ref_ptr& prim1, const osg::ref_ptr& 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(ps), vr.remap); + break; + case PrimitiveSet::DrawElementsUShortPrimitiveType: + reorderDrawElements(*static_cast(ps), vr.remap); + break; + case PrimitiveSet::DrawElementsUIntPrimitiveType: + reorderDrawElements(*static_cast(ps), vr.remap); + break; + default: + break; + } + } + + // deduplicate UVs array that were only shared within the geometry + deduplicator.deduplicateUVs(geom); + + geom.dirtyDisplayList(); + } + }; +} // glesUtil namespace + +#endif diff --git a/src/osgPlugins/osgjs/Adaptator b/src/osgPlugins/osgjs/Adaptator new file mode 100644 index 000000000..18ff70324 --- /dev/null +++ b/src/osgPlugins/osgjs/Adaptator @@ -0,0 +1,117 @@ +/* -*-c++-*- + * Copyright (C) 2010 Cedric Pinson + * + * 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 + */ + +#include + + +struct DrawArrayLenghtAdaptator +{ + unsigned int _normalArraySize; + const Vec3* _normalArrayPtr; + + unsigned int _colorArraySize; + const Vec4* _colorArrayPtr; + + std::vector > _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 +{ + + 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; currentoperator()(current, current+1, current+2,_treatVertexDataAsTemporary); + + } + break; + } + case(GL_TRIANGLE_STRIP): + { + unsigned int current = first; + for(GLsizei i=2;ioperator()(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;ioperator()(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;ioperator()(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;ioperator()(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; + } + } +}; diff --git a/src/osgPlugins/osgjs/Animation b/src/osgPlugins/osgjs/Animation new file mode 100644 index 000000000..43bfedcb5 --- /dev/null +++ b/src/osgPlugins/osgjs/Animation @@ -0,0 +1,12 @@ +/* -*-c++-*- + * Copyright (C) 2011 Cedric Pinson + */ +#ifndef JSON_ANIMATION_H +#define JSON_ANIMATION_H +#include +#include + +JSONObject* createJSONAnimation(osgAnimation::Animation* anim); +JSONObject* createJSONUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform& acb); + +#endif diff --git a/src/osgPlugins/osgjs/Animation.cpp b/src/osgPlugins/osgjs/Animation.cpp new file mode 100644 index 000000000..f6e50a570 --- /dev/null +++ b/src/osgPlugins/osgjs/Animation.cpp @@ -0,0 +1,203 @@ +/* -*-c++-*- + * Copyright (C) 2011 Cedric Pinson + */ + +#include +#include +#include +#include +#include +#include +#include +#include "JSON_Objects" + +static bool addJSONChannelVec3(osgAnimation::Vec3LinearChannel* channel, JSONObject& anim) +{ + if (channel && channel->getSampler()) { + osg::ref_ptr json = new JSONObject; + json->getMaps()["Name"] = new JSONValue(channel->getName()); + json->getMaps()["TargetName"] = new JSONValue(channel->getTargetName()); + osgAnimation::Vec3KeyframeContainer* keys = channel->getSamplerTyped()->getKeyframeContainerTyped(); + JSONKeyframes* jsonKeys = new JSONKeyframes(); + //if (!keys->getName().empty()) { + // jsonKeys->getMaps()["Name"] = new JSONValue(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 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 json = new JSONObject; + json->getMaps()["Name"] = new JSONValue(channel->getName()); + json->getMaps()["TargetName"] = new JSONValue(channel->getTargetName()); + osgAnimation::FloatKeyframeContainer* keys = channel->getSamplerTyped()->getKeyframeContainerTyped(); + JSONKeyframes* jsonKeys = new JSONKeyframes(); + //if (!keys->getName().empty()) { + // jsonKeys->getMaps()["Name"] = new JSONValue(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 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 json = new JSONObject; + json->getMaps()["Name"] = new JSONValue(channel->getName()); + json->getMaps()["TargetName"] = new JSONValue(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 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(channel); + if (c) { + if (addJSONChannelVec3(c, anim)) + return; + } + } + + { + osgAnimation::FloatLinearChannel* c = dynamic_cast(channel); + if (c) { + if (addJSONChannelFloat(c, anim)) + return; + } + } + + { + osgAnimation::QuatSphericalLinearChannel* c = dynamic_cast(channel); + if (c) { + if (addJSONChannelQuaternion(c, anim)) + return; + } + } + +} + +JSONObject* createJSONAnimation(osgAnimation::Animation* anim) +{ + osg::ref_ptr json = new JSONObject; + json->getMaps()["Channels"] = new JSONArray(); + json->getMaps()["Name"] = new JSONValue(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 json = new JSONObject; + json->getMaps()["Name"] = new JSONValue(acb.getName()); + + osg::ref_ptr 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(st[i].get()); + if (element) { + osg::ref_ptr jsonElement = new JSONObject; + jsonElement->getMaps()["Name"] = new JSONValue(element->getName()); + jsonElement->getMaps()["Translate"] = new JSONVec3Array(element->getTranslate()); + + osg::ref_ptr jsonElementObject = new JSONObject; + jsonElementObject->getMaps()["osgAnimation.StackedTranslate"] = jsonElement; + jsonStackedArray->getArray().push_back(jsonElementObject); + continue; + } + } + + + { + osgAnimation::StackedQuaternionElement* element = dynamic_cast(st[i].get()); + if (element) { + osg::ref_ptr jsonElement = new JSONObject; + jsonElement->getMaps()["Name"] = new JSONValue(element->getName()); + jsonElement->getMaps()["Quaternion"] = new JSONVec4Array(element->getQuaternion().asVec4()); + + osg::ref_ptr jsonElementObject = new JSONObject; + jsonElementObject->getMaps()["osgAnimation.StackedQuaternion"] = jsonElement; + jsonStackedArray->getArray().push_back(jsonElementObject); + continue; + } + } + + + { + osgAnimation::StackedRotateAxisElement* element = dynamic_cast(st[i].get()); + if (element) { + osg::ref_ptr jsonElement = new JSONObject; + jsonElement->getMaps()["Name"] = new JSONValue(element->getName()); + jsonElement->getMaps()["Axis"] = new JSONVec3Array(element->getAxis()); + jsonElement->getMaps()["Angle"] = new JSONValue(element->getAngle()); + + osg::ref_ptr jsonElementObject = new JSONObject; + jsonElementObject->getMaps()["osgAnimation.StackedRotateAxis"] = jsonElement; + jsonStackedArray->getArray().push_back(jsonElementObject); + continue; + } + } + } + if (jsonStackedArray->getArray().empty()) { + return 0; + } + + return json.release(); +} diff --git a/src/osgPlugins/osgjs/Base64 b/src/osgPlugins/osgjs/Base64 new file mode 100644 index 000000000..6d1becc1c --- /dev/null +++ b/src/osgPlugins/osgjs/Base64 @@ -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(file)); + + // decode from file to a standard container + ifstream file("input.txt"); + vector dest; + base64::decode(istream_iterator(file), istream_iterator(), + back_inserter(dest) + ); +*/ + +#ifndef _BASE64_HPP +#define _BASE64_HPP + +#include + + +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 + 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(*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 + 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(*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((input[0] << 2) + (input[1] >> 4)); + ++out; + if (chars >= 3) + { + *out = static_cast((input[1] << 4) + (input[2] >> 2)); + ++out; + if (chars >= 4) + { + *out = static_cast((input[2] << 6) + input[3]); + ++out; + } + } + } + } while (chars == 4); + + } + +} // end namespace + +#endif diff --git a/src/osgPlugins/osgjs/Base64.cpp b/src/osgPlugins/osgjs/Base64.cpp new file mode 100644 index 000000000..227b6b1a3 --- /dev/null +++ b/src/osgPlugins/osgjs/Base64.cpp @@ -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; +} + diff --git a/src/osgPlugins/osgjs/CMakeLists.txt b/src/osgPlugins/osgjs/CMakeLists.txt new file mode 100644 index 000000000..ec2664882 --- /dev/null +++ b/src/osgPlugins/osgjs/CMakeLists.txt @@ -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) diff --git a/src/osgPlugins/osgjs/CompactBufferVisitor b/src/osgPlugins/osgjs/CompactBufferVisitor new file mode 100644 index 000000000..74145053b --- /dev/null +++ b/src/osgPlugins/osgjs/CompactBufferVisitor @@ -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 + + +////// taken from gles/GeometryUniqueVisitor + +#include +#include +#include + +#include + + +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(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(primitives[i].get()); + if(isProcessed(de)) { + geometry.setPrimitiveSet(i, dynamic_cast(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(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(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(de->index(i))); + } + return maximum; + } + + osg::Object* getProcessedBuffer(const osg::Object* buffer) { + std::map::iterator it = _processed.find(buffer); + if(it == _processed.end()) { + return 0; + } + else { + return it->second; + } + } + + std::map _processed; +}; + +#endif diff --git a/src/osgPlugins/osgjs/JSON_Objects b/src/osgPlugins/osgjs/JSON_Objects new file mode 100644 index 000000000..93584d33e --- /dev/null +++ b/src/osgPlugins/osgjs/JSON_Objects @@ -0,0 +1,372 @@ +/* -*-c++-*- + * Copyright (C) 2010 Cedric Pinson + */ + +#ifndef JSON_OBJECT +#define JSON_OBJECT + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "json_stream" + +#ifdef WIN32 + #include +#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 struct JSONValue; + +struct JSONArray; +struct JSONObject : public JSONObjectBase +{ + typedef std::map > JSONMap; + typedef std::vector 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 JSONValue* asValue() { + return dynamic_cast * > ( 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&) const; + template + void dumpVarintVector(std::vector&, T const*, bool) const; + template + void dumpVarintValue(std::vector&, T const*, bool) const; + std::vector 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 > 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 +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 : 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 : 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(matrix.ptr()[i])); + } + } + void write(json_stream& str, WriteVisitor& visitor); +}; + + + +struct JSONVertexArray : public JSONArray +{ + osg::ref_ptr _arrayData; + std::string _filename; + + std::pair 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(array->getDataPointer()); + myfile.write(b, array->getTotalDataSize()); + unsigned int fsize = myfile.tellp(); + myfile.close(); + return fsize; + } + + template 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 void writeInlineArray(json_stream& str, unsigned int size, const T* array) { + str << JSONObjectBase::indent() << "\"Elements\": [ " << static_cast(array[0]); + for (unsigned int i = 1; i < size; i++) { + str << ", " << static_cast(array[i]); + } + str << " ]," << std::endl; + } + + template 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(array->getDataSize()); + getMaps()["Type"] = new JSONValue("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 +struct BufferArray +{ typedef osg::UShortArray type; }; + +#define ADD_INDEX_BUFFER(T, B)\ +template<> \ +struct BufferArray \ +{ typedef B type; } + +ADD_INDEX_BUFFER(osg::DrawElementsUInt, osg::UIntArray); +ADD_INDEX_BUFFER(osg::DrawElementsUByte, osg::UByteArray); + + +template +struct JSONDrawElements : public JSONObject +{ + JSONDrawElements(T& array) { + typedef typename BufferArray::type array_type; + typedef typename array_type::ElementDataType element_type; + + JSONBufferArray* buf; + + if (array.getMode() == GL_QUADS) { + int size = array.getNumIndices(); + osg::ref_ptr buffer = new array_type(size); + unsigned int idx = 0; + for (int i = 0; i < size/4; ++i) { + (*buffer)[idx++] = static_cast(array.index(i*4 + 0)); + (*buffer)[idx++] = static_cast(array.index(i*4 + 1)); + (*buffer)[idx++] = static_cast(array.index(i*4 + 3)); + + (*buffer)[idx++] = static_cast(array.index(i*4 + 1)); + (*buffer)[idx++] = static_cast(array.index(i*4 + 2)); + (*buffer)[idx++] = static_cast(array.index(i*4 + 3)); + } + buf = new JSONBufferArray(buffer.get()); + getMaps()["Mode"] = getDrawMode(osg::PrimitiveSet::TRIANGLES); + } + else { + osg::ref_ptr buffer = new array_type(array.getNumIndices()); + for(unsigned int i = 0 ; i < array.getNumIndices() ; ++ i) + (*buffer)[i] = static_cast(array.index(i)); + buf = new JSONBufferArray(buffer.get()); + getMaps()["Mode"] = getDrawMode(array.getMode()); + } + + buf->getMaps()["Type"] = new JSONValue("ELEMENT_ARRAY_BUFFER"); + getMaps()["Indices"] = buf; + } + + void setBufferName(const std::string& bufferName) { + JSONObject::setBufferName(bufferName); + getMaps()["Indices"]->setBufferName(bufferName); + } +}; + + +#endif diff --git a/src/osgPlugins/osgjs/JSON_Objects.cpp b/src/osgPlugins/osgjs/JSON_Objects.cpp new file mode 100644 index 000000000..65a2ba4ef --- /dev/null +++ b/src/osgPlugins/osgjs/JSON_Objects.cpp @@ -0,0 +1,713 @@ +/* -*-c++-*- + * Copyright (C) 2010 Cedric Pinson + */ + +#include "JSON_Objects" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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(id); +} + +JSONObject::JSONObject() +{ + _uniqueID = -1; +} + +void JSONObject::addUniqueID() +{ + _uniqueID = JSONObject::uniqueID++; + _maps["UniqueID"] = new JSONValue(_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(array->getType())) + { + case osg::Array::IntArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::ShortArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + + case osg::Array::UIntArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::UShortArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + + case osg::Array::Vec2iArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::Vec3iArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::Vec4iArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + + case osg::Array::Vec2uiArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::Vec3uiArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::Vec4uiArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + + case osg::Array::Vec2sArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::Vec3sArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::Vec4sArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + + case osg::Array::Vec2usArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::Vec3usArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + case osg::Array::Vec4usArrayType: + isInteger = (dynamic_cast(array) != NULL); + break; + + default: + isInteger = false; + break; + } + return isInteger; +} + +void JSONObject::encodeArrayAsVarintBuffer(osg::Array const* array, std::vector& buffer) const +{ + switch(static_cast(array->getType())) + { + case osg::Array::IntArrayType: + dumpVarintValue(buffer, dynamic_cast(array), false); + break; + case osg::Array::ShortArrayType: + dumpVarintValue(buffer, dynamic_cast(array), false); + break; + + case osg::Array::UIntArrayType: + dumpVarintValue(buffer, dynamic_cast(array), true); + break; + case osg::Array::UShortArrayType: + dumpVarintValue(buffer, dynamic_cast(array), true); + break; + + case osg::Array::Vec2iArrayType: + dumpVarintVector(buffer, dynamic_cast(array), false); + break; + case osg::Array::Vec3iArrayType: + dumpVarintVector(buffer, dynamic_cast(array), false); + break; + case osg::Array::Vec4iArrayType: + dumpVarintVector(buffer, dynamic_cast(array), false); + break; + + case osg::Array::Vec2sArrayType: + dumpVarintVector(buffer, dynamic_cast(array), false); + break; + case osg::Array::Vec3sArrayType: + dumpVarintVector(buffer, dynamic_cast(array), false); + break; + case osg::Array::Vec4sArrayType: + dumpVarintVector(buffer, dynamic_cast(array), false); + break; + + + case osg::Array::Vec2uiArrayType: + dumpVarintVector(buffer, dynamic_cast(array), true); + break; + case osg::Array::Vec3uiArrayType: + dumpVarintVector(buffer, dynamic_cast(array), true); + break; + case osg::Array::Vec4uiArrayType: + dumpVarintVector(buffer, dynamic_cast(array), true); + break; + + case osg::Array::Vec2usArrayType: + dumpVarintVector(buffer, dynamic_cast(array), true); + break; + case osg::Array::Vec3usArrayType: + dumpVarintVector(buffer, dynamic_cast(array), true); + break; + case osg::Array::Vec4usArrayType: + dumpVarintVector(buffer, dynamic_cast(array), true); + break; + + default: + break; + } +} + +template +void JSONObject::dumpVarintVector(std::vector& 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 encoding = varintEncoding(value); + oss.insert(oss.end(), encoding.begin(), encoding.end()); + } + } +} + +template +void JSONObject::dumpVarintValue(std::vector& 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 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 JSONObject::varintEncoding(unsigned int value) const +{ + std::vector 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& 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 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 varintByteBuffer; + encodeArrayAsVarintBuffer(array, varintByteBuffer); + output.write((char*)&varintByteBuffer[0], varintByteBuffer.size() * sizeof(uint8_t)); + encoding = std::string("varint"); + } + else + { + const char* b = static_cast(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(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 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 converted = new osg::Vec4Array; + converted->reserve(array->getNumElements()); + + const osg::Vec4ubArray* a = dynamic_cast(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(array->getDataPointer()); + unsigned int size = array->getNumElements() * array->getDataSize(); + writeInlineArrayReal(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(array->getDataPointer()); + unsigned int size = array->getNumElements() * array->getDataSize(); + writeInlineArrayReal(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(array->getDataPointer()); + unsigned int size = array->getNumElements() * array->getDataSize(); + writeInlineArray(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(array->getDataPointer()); + unsigned int size = array->getNumElements() * array->getDataSize(); + writeInlineArray(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(array->getDataPointer()); + unsigned int size = array->getNumElements() * array->getDataSize(); + writeInlineArray(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(array->getDataPointer()); + unsigned int size = array->getNumElements() * array->getDataSize(); + writeInlineArray(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(array->getDataPointer()); + unsigned int size = array->getNumElements() * array->getDataSize(); + writeInlineArray(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(array->getDataPointer()); + unsigned int size = array->getNumElements() * array->getDataSize(); + writeInlineArray(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 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(v[i])); + } +} + +JSONVec5Array::JSONVec5Array(const Vec5& v) : JSONVec3Array() +{ + for (int i = 0; i < 5; ++i) { + _array.push_back(new JSONValue(v[i])); + } +} + +JSONVec2Array::JSONVec2Array(const osg::Vec2& v) : JSONVec3Array() +{ + for (int i = 0; i < 2; ++i) { + _array.push_back(new JSONValue(v[i])); + } +} + +JSONVec3Array::JSONVec3Array(const osg::Vec3& v) +{ + for (int i = 0; i < 3; ++i) { + _array.push_back(new JSONValue(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("POINTS"); + break; + case GL_LINES: + result = new JSONValue("LINES"); + break; + case GL_LINE_LOOP: + result = new JSONValue("LINE_LOOP"); + break; + case GL_LINE_STRIP: + result = new JSONValue("LINE_STRIP"); + break; + case GL_TRIANGLES: + result = new JSONValue("TRIANGLES"); + break; + case GL_POLYGON: + result = new JSONValue("TRIANGLE_FAN"); + break; + case GL_QUAD_STRIP: + case GL_TRIANGLE_STRIP: + result = new JSONValue("TRIANGLE_STRIP"); + break; + case GL_TRIANGLE_FAN: + result = new JSONValue("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(array.getFirst()); + getMaps()["Count"] = new JSONValue(array.getCount()); + getMaps()["Mode"] = getDrawMode(array.getMode()); +} + + +JSONDrawArrayLengths::JSONDrawArrayLengths(osg::DrawArrayLengths& array) +{ + getMaps()["First"] = new JSONValue(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(array[i])); + } + getMaps()["ArrayLengths"] = jsonArray; +} diff --git a/src/osgPlugins/osgjs/ReaderWriterJSON.cpp b/src/osgPlugins/osgjs/ReaderWriterJSON.cpp new file mode 100644 index 000000000..ad514c7a0 --- /dev/null +++ b/src/osgPlugins/osgjs/ReaderWriterJSON.cpp @@ -0,0 +1,243 @@ +// copyright: 'Cedric Pinson cedric@plopbyte.com' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#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 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=","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 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::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) diff --git a/src/osgPlugins/osgjs/WriteVisitor b/src/osgPlugins/osgjs/WriteVisitor new file mode 100644 index 000000000..4f62155d1 --- /dev/null +++ b/src/osgPlugins/osgjs/WriteVisitor @@ -0,0 +1,451 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 2012 Cedric Pinson */ +#ifndef WRITE_VISITOR_H +#define WRITE_VISITOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 +bool getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value); + + +class WriteVisitor : public osg::NodeVisitor +{ +public: + typedef std::vector > StateSetStack; + + std::map, osg::ref_ptr > _maps; + std::vector > _parents; + osg::ref_ptr _root; + StateSetStack _stateset; + std::string _baseName; + bool _useExternalBinaryArray; + bool _mergeAllBinaryFiles; + bool _inlineImages; + bool _varint; + int _maxTextureDimension; + std::vector _specificBuffers; + std::map _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::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::iterator buffer = _buffers.begin() ; + buffer != _buffers.end() ; ++ buffer) { + buffer->second->close(); + } + } + + unsigned int getBuffersSize() { + unsigned int size = 0; + for(std::map::iterator buffer = _buffers.begin() ; + buffer != _buffers.end() ; ++ buffer) { + size += buffer->second->tellp(); + } + return size; + } + + void write(json_stream& str) { + osg::ref_ptr o = new JSONObject(); + o->getMaps()["Version"] = new JSONValue(WRITER_VERSION); + o->getMaps()["Generator"] = new JSONValue("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::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(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 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(nc); + if (updateMT) { + osg::ref_ptr jsonCallback = createJSONUpdateMatrixTransform(*updateMT); + if (jsonCallback.valid()) { + osg::ref_ptr 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(&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 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 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 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 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 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 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 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 diff --git a/src/osgPlugins/osgjs/WriteVisitor.cpp b/src/osgPlugins/osgjs/WriteVisitor.cpp new file mode 100644 index 000000000..9f2b3c7fd --- /dev/null +++ b/src/osgPlugins/osgjs/WriteVisitor.cpp @@ -0,0 +1,829 @@ +#include "WriteVisitor" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(osg->getName()); + } + + osgSim::ShapeAttributeList* osgSim_userdata = dynamic_cast(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(attr.getName()); + osg::ref_ptr > value; + switch(attr.getType()) { + case osgSim::ShapeAttribute::INTEGER: + { + std::stringstream ss; + ss << attr.getInt(); + value = new JSONValue(ss.str()); + } + break; + case osgSim::ShapeAttribute::DOUBLE: + { + std::stringstream ss; + ss << attr.getDouble(); + value = new JSONValue(ss.str()); + } + break; + case osgSim::ShapeAttribute::STRING: + { + std::stringstream ss; + ss << attr.getString(); + value = new JSONValue(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(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(name); + jsonEntry->getMaps()["Value"] = new JSONValue(value); + jsonUDCArray->getArray().push_back(jsonEntry); + } + + } + json->getMaps()["UserDataContainer"] = jsonUDC; + } +} + + +void getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value) { + if(getStringifiedUserValue(o, name, value)) return; + if(getStringifiedUserValue(o, name, value)) return; + if(getStringifiedUserValue(o, name, value)) return; + if(getStringifiedUserValue(o, name, value)) return; + if(getStringifiedUserValue(o, name, value)) return; + if(getStringifiedUserValue(o, name, value)) return; + if(getStringifiedUserValue(o, name, value)) return; + if(getStringifiedUserValue(o, name, value)) return; + if(getStringifiedUserValue(o, name, value)) return; +} + + +template +bool getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value) { + osg::TemplateValueObject* vo = dynamic_cast< osg::TemplateValueObject* >(o); + if (vo) { + std::ostringstream oss; + oss << vo->getValue(); + name = vo->getName(); + value = oss.str(); + return true; + } + return false; +} + + +static JSONValue* getBlendFuncMode(GLenum mode) { + switch (mode) { + case osg::BlendFunc::DST_ALPHA: return new JSONValue("DST_ALPHA"); + case osg::BlendFunc::DST_COLOR: return new JSONValue("DST_COLOR"); + case osg::BlendFunc::ONE: return new JSONValue("ONE"); + case osg::BlendFunc::ONE_MINUS_DST_ALPHA: return new JSONValue("ONE_MINUS_DST_ALPHA"); + case osg::BlendFunc::ONE_MINUS_DST_COLOR: return new JSONValue("ONE_MINUS_DST_COLOR"); + case osg::BlendFunc::ONE_MINUS_SRC_ALPHA: return new JSONValue("ONE_MINUS_SRC_ALPHA"); + case osg::BlendFunc::ONE_MINUS_SRC_COLOR: return new JSONValue("ONE_MINUS_SRC_COLOR"); + case osg::BlendFunc::SRC_ALPHA: return new JSONValue("SRC_ALPHA"); + case osg::BlendFunc::SRC_ALPHA_SATURATE: return new JSONValue("SRC_ALPHA_SATURATE"); + case osg::BlendFunc::SRC_COLOR: return new JSONValue("SRC_COLOR"); + case osg::BlendFunc::CONSTANT_COLOR: return new JSONValue("CONSTANT_COLOR"); + case osg::BlendFunc::ONE_MINUS_CONSTANT_COLOR: return new JSONValue("ONE_MINUS_CONSTANT_COLOR"); + case osg::BlendFunc::CONSTANT_ALPHA: return new JSONValue("CONSTANT_ALPHA"); + case osg::BlendFunc::ONE_MINUS_CONSTANT_ALPHA: return new JSONValue("ONE_MINUS_CONSTANT_ALPHA"); + case osg::BlendFunc::ZERO: return new JSONValue("ZERO"); + default: + return new JSONValue("ONE"); + } + return new JSONValue("ONE"); +} + +static JSONValue* getJSONFilterMode(osg::Texture::FilterMode mode) +{ + switch(mode) { + case GL_LINEAR: + return new JSONValue("LINEAR"); + case GL_LINEAR_MIPMAP_LINEAR: + return new JSONValue("LINEAR_MIPMAP_LINEAR"); + case GL_LINEAR_MIPMAP_NEAREST: + return new JSONValue("LINEAR_MIPMAP_NEAREST"); + case GL_NEAREST: + return new JSONValue("NEAREST"); + case GL_NEAREST_MIPMAP_LINEAR: + return new JSONValue("NEAREST_MIPMAP_LINEAR"); + case GL_NEAREST_MIPMAP_NEAREST: + return new JSONValue("NEAREST_MIPMAP_NEAREST"); + default: + return 0; + } + return 0; +} + +static JSONValue* getJSONWrapMode(osg::Texture::WrapMode mode) +{ + switch(mode) { + case GL_CLAMP: + // clamp does not exist in opengles 2.0 + //return new JSONValue("CLAMP"); + return new JSONValue("CLAMP_TO_EDGE"); + case GL_CLAMP_TO_EDGE: + return new JSONValue("CLAMP_TO_EDGE"); + case GL_CLAMP_TO_BORDER_ARB: + return new JSONValue("CLAMP_TO_BORDER"); + case GL_REPEAT: + return new JSONValue("REPEAT"); + case GL_MIRRORED_REPEAT_IBM: + return new JSONValue("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("/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(in), + std::istreambuf_iterator(), + std::ostreambuf_iterator(out), false); + + return new JSONValue(out.str()); + + } + } + return new JSONValue(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 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* json = new JSONDrawElements(*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* json = new JSONDrawElements(*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* json = new JSONDrawElements(*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 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* json = new JSONDrawElements(*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 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 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 json = new JSONNode; + json->addUniqueID(); + _maps[geom] = json; + + if (geom->getStateSet()) + createJSONStateSet(json.get(), geom->getStateSet()); + + translateObject(json.get(), geom); + + osg::ref_ptr 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 primitives = new JSONArray(); + for (unsigned int i = 0; i < geom->getNumPrimitiveSets(); ++i) { + osg::ref_ptr obj = new JSONObject; + osg::PrimitiveSet* primitive = geom->getPrimitiveSet(i); + if(!primitive) continue; + + if (primitive->getType() == osg::PrimitiveSet::DrawArraysPrimitiveType) { + osg::DrawArrays* da = dynamic_cast((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((primitive)); + primitives->getArray().push_back(obj); + obj->getMaps()["DrawElementsUInt"] = createJSONDrawElementsUInt(da, geom); + + } else if (primitive->getType() == osg::PrimitiveSet::DrawElementsUShortPrimitiveType) { + osg::DrawElementsUShort* da = dynamic_cast((primitive)); + primitives->getArray().push_back(obj); + obj->getMaps()["DrawElementsUShort"] = createJSONDrawElementsUShort(da, geom); + + } else if (primitive->getType() == osg::PrimitiveSet::DrawElementsUBytePrimitiveType) { + osg::DrawElementsUByte* da = dynamic_cast((primitive)); + primitives->getArray().push_back(obj); + obj->getMaps()["DrawElementsUByte"] = createJSONDrawElementsUByte(da, geom); + + } else if (primitive->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType) { + osg::DrawArrayLengths* dal = dynamic_cast((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 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 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 json = new JSONObject; + json->addUniqueID(); + _maps[sa] = json; + + translateObject(json.get(), sa); + + osg::ref_ptr > mode = new JSONValue("BACK"); + if (sa->getMode() == osg::CullFace::FRONT) { + mode = new JSONValue("BACK"); + } + if (sa->getMode() == osg::CullFace::FRONT_AND_BACK) { + mode = new JSONValue("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 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(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 jsonLight = new JSONLight; + jsonLight->addUniqueID(); + _maps[light] = jsonLight; + + translateObject(jsonLight.get(), light); + + jsonLight->getMaps()["LightNum"] = new JSONValue(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(light->getConstantAttenuation()); + jsonLight->getMaps()["LinearAttenuation"] = new JSONValue(light->getLinearAttenuation()); + jsonLight->getMaps()["QuadraticAttenuation"] = new JSONValue(light->getQuadraticAttenuation()); + jsonLight->getMaps()["SpotExponent"] = new JSONValue(light->getSpotExponent()); + jsonLight->getMaps()["SpotCutoff"] = new JSONValue(light->getSpotCutoff()); + return jsonLight.release(); +} + +template JSONObject* createImageFromTexture(osg::Texture* texture, JSONObject* jsonTexture, bool inlineImages, + int maxTextureDimension, const std::string &baseName = "") +{ + T* text = dynamic_cast( 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 jsonPlod = new JSONNode; + jsonPlod->addUniqueID(); + _maps[plod] = jsonPlod; + + + // Center Mode + osg::ref_ptr > centerMode = new JSONValue("USE_BOUNDING_SPHERE_CENTER"); + if (plod->getCenterMode() == osg::LOD::USER_DEFINED_CENTER) { + centerMode = new JSONValue("USER_DEFINED_CENTER"); + } else if (plod->getCenterMode() == osg::LOD::UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED){ + centerMode = new JSONValue("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 > rangeMode = new JSONValue("DISTANCE_FROM_EYE_POINT"); + if (plod->getRangeMode() == osg::LOD::PIXEL_SIZE_ON_SCREEN) { + rangeMode = new JSONValue("PIXEL_SIZE_ON_SCREEN"); + } + jsonPlod->getMaps()["RangeMode"] = rangeMode; + // Range List + //osg::ref_ptr 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 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(filename); + } + else + fileObject->getMaps()[str] = new JSONValue(""); + } + 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 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(texture, jsonTexture.get(), this->_inlineImages, + this->_maxTextureDimension, this->_baseName); + if (obj) { + return obj; + } + } + + { + JSONObject* obj = createImageFromTexture(texture, jsonTexture.get(), this->_inlineImages, + this->_maxTextureDimension, this->_baseName); + if (obj) { + return obj; + } + } + + { + JSONObject* obj = createImageFromTexture(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 jsonStateSet = new JSONStateSet; + _maps[stateset] = jsonStateSet; + jsonStateSet->addUniqueID(); + + translateObject(jsonStateSet.get(), stateset); + + if (stateset->getRenderingHint() == osg::StateSet::TRANSPARENT_BIN) { + jsonStateSet->getMaps()["RenderingHint"] = new JSONValue("TRANSPARENT_BIN"); + } + + bool blendEnabled = false; + if (stateset->getMode(GL_BLEND) == osg::StateAttribute::ON) { + blendEnabled = true; + } + + osg::ref_ptr textureAttributeList = new JSONArray; + int lastTextureIndex = -1; + for (int i = 0; i < 32; ++i) { + osg::Texture* texture = dynamic_cast(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 attributeList = new JSONArray; + + osg::Material* material = dynamic_cast(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(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 defaultBlend = new osg::BlendFunc(); + obj->getMaps()["osg.BlendFunc"] = createJSONBlendFunc(defaultBlend.get()); + attributeList->getArray().push_back(obj); + } + + osg::ref_ptr cullFace = dynamic_cast(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 defaultCull = new osg::CullFace(); + cf = createJSONCullFace(defaultCull.get()); + cf->getMaps()["Mode"] = new JSONValue("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(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(); +} diff --git a/src/osgPlugins/osgjs/json_stream b/src/osgPlugins/osgjs/json_stream new file mode 100644 index 000000000..45109d960 --- /dev/null +++ b/src/osgPlugins/osgjs/json_stream @@ -0,0 +1,274 @@ +#ifndef JSON_STREAM +#define JSON_STREAM + +#include +#include +#include // control characters +#include +#include +#include +#include +#include + + +// 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 + json_stream& operator<<(const T& data) { + if (_stream.is_open()) { + _stream << sanitize(data); + } + return *this; + } + + template + const T sanitize(const T& t) { + return t; + } + + double sanitize(const float f) { + return sanitize(static_cast(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::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(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