From Cedric Pinson, gles and osgjs plugins that support conversion of OSG loaded models into a form that can be used with osgjs JavaScript library
git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14770 16af8721-9629-0410-8352-f15c8da7e697
This commit is contained in:
@@ -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)
|
||||
|
||||
31
src/osgPlugins/gles/AnimationVisitor
Normal file
31
src/osgPlugins/gles/AnimationVisitor
Normal file
@@ -0,0 +1,31 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ANIMATION_VISITOR
|
||||
#define ANIMATION_VISITOR
|
||||
|
||||
#include <osgUtil/UpdateVisitor>
|
||||
#include <osgAnimation/UpdateMatrixTransform>
|
||||
#include <osgAnimation/AnimationManagerBase>
|
||||
#include <osgAnimation/BasicAnimationManager>
|
||||
|
||||
|
||||
// the idea is to create true Geometry if skeleton with RigGeometry
|
||||
class AnimationVisitor : public osgUtil::UpdateVisitor
|
||||
{
|
||||
public:
|
||||
AnimationVisitor() {
|
||||
setFrameStamp(new osg::FrameStamp());
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
242
src/osgPlugins/gles/BindPerVertexVisitor
Normal file
242
src/osgPlugins/gles/BindPerVertexVisitor
Normal file
@@ -0,0 +1,242 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BIND_PER_VERTEX_VISITOR
|
||||
#define BIND_PER_VERTEX_VISITOR
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
|
||||
|
||||
// TODO: deprecated
|
||||
class BindPerVertexVisitor : public GeometryUniqueVisitor {
|
||||
public:
|
||||
BindPerVertexVisitor(): GeometryUniqueVisitor("BindPerVertexVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
if (geometry.getNormalArray() && geometry.getNormalBinding() != osg::Geometry::BIND_PER_VERTEX) {
|
||||
bindPerVertex(geometry.getNormalArray(),
|
||||
geometry.getNormalBinding(),
|
||||
geometry.getPrimitiveSetList());
|
||||
geometry.setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (geometry.getColorArray() && geometry.getColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
|
||||
bindPerVertex(geometry.getColorArray(),
|
||||
geometry.getColorBinding(),
|
||||
geometry.getPrimitiveSetList());
|
||||
geometry.setColorBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (geometry.getSecondaryColorArray() && geometry.getSecondaryColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
|
||||
bindPerVertex(geometry.getSecondaryColorArray(),
|
||||
geometry.getSecondaryColorBinding(),
|
||||
geometry.getPrimitiveSetList());
|
||||
geometry.setSecondaryColorBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (geometry.getFogCoordArray() && geometry.getFogCoordBinding() != osg::Geometry::BIND_PER_VERTEX) {
|
||||
bindPerVertex(geometry.getFogCoordArray(),
|
||||
geometry.getFogCoordBinding(),
|
||||
geometry.getPrimitiveSetList());
|
||||
geometry.setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
setProcessed(&geometry);
|
||||
};
|
||||
|
||||
protected:
|
||||
void bindPerVertex(osg::Array* src,
|
||||
osg::Geometry::AttributeBinding fromBinding,
|
||||
osg::Geometry::PrimitiveSetList& primitives) {
|
||||
if (doConvert<osg::Vec3Array>(src, fromBinding, primitives))
|
||||
return;
|
||||
|
||||
if (doConvert<osg::Vec2Array>(src, fromBinding, primitives))
|
||||
return;
|
||||
|
||||
if (doConvert<osg::Vec4Array>(src, fromBinding, primitives))
|
||||
return;
|
||||
|
||||
if (doConvert<osg::Vec4ubArray>(src, fromBinding, primitives))
|
||||
return;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool doConvert(osg::Array* src,
|
||||
osg::Geometry::AttributeBinding fromBinding,
|
||||
osg::Geometry::PrimitiveSetList& primitives) {
|
||||
T* array= dynamic_cast<T*>(src);
|
||||
if (array) {
|
||||
convert(*array, fromBinding, primitives);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void convert(T& array,
|
||||
osg::Geometry::AttributeBinding fromBinding,
|
||||
osg::Geometry::PrimitiveSetList& primitives)
|
||||
{
|
||||
osg::ref_ptr<T> result = new T();
|
||||
for (unsigned int p = 0; p < primitives.size(); p++) {
|
||||
switch ( primitives[p]->getMode() ) {
|
||||
case osg::PrimitiveSet::POINTS:
|
||||
osg::notify(osg::WARN) << "ConvertToBindPerVertex not supported for POINTS" << std::endl;
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::LINE_STRIP:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
unsigned int nb = primitives[p]->getNumIndices();
|
||||
for (unsigned int i = 0; i < nb; i++)
|
||||
result->push_back(array[p]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::LINES:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
unsigned int nb = primitives[p]->getNumIndices();
|
||||
for (unsigned int i = 0; i < nb; i++)
|
||||
result->push_back(array[p]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::TRIANGLES:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
unsigned int nb = primitives[p]->getNumIndices();
|
||||
for (unsigned int i = 0; i < nb; i++)
|
||||
result->push_back(array[p]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::TRIANGLE_STRIP:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_STRIP" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::TRIANGLE_FAN:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_FAN" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::QUADS:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUADS" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::QUAD_STRIP:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUAD_STRIP" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
array = *result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
12
src/osgPlugins/gles/CMakeLists.txt
Normal file
12
src/osgPlugins/gles/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
SET(TARGET_SRC
|
||||
ReaderWriterGLES.cpp
|
||||
OpenGLESGeometryOptimizer.cpp
|
||||
TriangleStripVisitor.cpp
|
||||
IndexMeshVisitor.cpp
|
||||
UnIndexMeshVisitor.cpp
|
||||
forsythtriangleorderoptimizer.cpp)
|
||||
|
||||
#### end var setup ###
|
||||
SET(TARGET_ADDED_LIBRARIES
|
||||
osgUtil)
|
||||
SETUP_PLUGIN(gles)
|
||||
97
src/osgPlugins/gles/DetachPrimitiveVisitor
Normal file
97
src/osgPlugins/gles/DetachPrimitiveVisitor
Normal file
@@ -0,0 +1,97 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DETACH_PRIMITIVE_VISITOR
|
||||
#define DETACH_PRIMITIVE_VISITOR
|
||||
|
||||
#include <osg/ValueObject>
|
||||
#include "GeometryUniqueVisitor"
|
||||
|
||||
class DetachPrimitiveVisitor : public GeometryUniqueVisitor {
|
||||
public:
|
||||
DetachPrimitiveVisitor(std::string const& userValue, bool keepGeometryAttributes=false, bool inlined=true):
|
||||
GeometryUniqueVisitor("DetachPrimitiveVisitor"),
|
||||
_userValue(userValue), _keepGeometryAttributes(keepGeometryAttributes), _inlined(inlined)
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
if(shouldDetach(geometry)) {
|
||||
osg::Geometry* detached = createDetachedGeometry(geometry);
|
||||
|
||||
unsigned int nbParents = geometry.getNumParents();
|
||||
for(unsigned int i = 0 ; i < nbParents ; ++ i) {
|
||||
osg::Node* parent = geometry.getParent(i);
|
||||
// TODO: Geode will be soon deprecated
|
||||
if(parent && parent->asGeode()) {
|
||||
osg::Geode* geode = parent->asGeode();
|
||||
geode->addDrawable(detached);
|
||||
if(!_inlined) {
|
||||
geode->removeDrawable(&geometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
setProcessed(detached);
|
||||
}
|
||||
setProcessed(&geometry);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool shouldDetach(osg::Geometry& geometry) {
|
||||
bool detach = false;
|
||||
for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) {
|
||||
osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
|
||||
if(primitive && primitive->getUserValue(_userValue, detach) && detach) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
osg::Geometry* createDetachedGeometry(osg::Geometry& source) {
|
||||
osg::Geometry* detached = new osg::Geometry(source, osg::CopyOp::SHALLOW_COPY);
|
||||
if(!_keepGeometryAttributes) {
|
||||
// we keep only vertexes and clean all other attributes and values
|
||||
detached->setNormalArray(0);
|
||||
detached->setColorArray(0);
|
||||
detached->setSecondaryColorArray(0);
|
||||
detached->setFogCoordArray(0);
|
||||
for (unsigned int i = 0 ; i < source.getNumTexCoordArrays(); ++ i) {
|
||||
detached->setTexCoordArray(i, 0);
|
||||
}
|
||||
detached->getVertexAttribArrayList().clear();
|
||||
detached->setStateSet(0);
|
||||
detached->setUserDataContainer(0);
|
||||
}
|
||||
|
||||
// filter primitivesets
|
||||
osg::Geometry::PrimitiveSetList detachedPrimitives;
|
||||
for(int i = source.getNumPrimitiveSets() - 1 ; i >= 0 ; -- i) {
|
||||
osg::PrimitiveSet* primitive = source.getPrimitiveSet(i);
|
||||
bool isTrue = false;
|
||||
if(primitive && primitive->getUserValue(_userValue, isTrue) && isTrue) {
|
||||
detachedPrimitives.push_back(primitive);
|
||||
source.removePrimitiveSet(i);
|
||||
}
|
||||
}
|
||||
|
||||
detached->setPrimitiveSetList(detachedPrimitives);
|
||||
detached->setUserValue(_userValue, true);
|
||||
return detached;
|
||||
}
|
||||
|
||||
|
||||
std::string _userValue;
|
||||
bool _keepGeometryAttributes;
|
||||
bool _inlined;
|
||||
};
|
||||
|
||||
#endif
|
||||
88
src/osgPlugins/gles/DrawArrayVisitor
Normal file
88
src/osgPlugins/gles/DrawArrayVisitor
Normal file
@@ -0,0 +1,88 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DRAW_ARRAY_VISITOR
|
||||
#define DRAW_ARRAY_VISITOR
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
#include "GeometryArray"
|
||||
|
||||
|
||||
class DrawArrayVisitor : public GeometryUniqueVisitor {
|
||||
public:
|
||||
DrawArrayVisitor(): GeometryUniqueVisitor("DrawArrayVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
GeometryArrayList srcArrays(geometry);
|
||||
|
||||
// clone but clear content
|
||||
osg::Geometry* newGeometry = new osg::Geometry;
|
||||
GeometryArrayList dst = srcArrays.cloneType();
|
||||
|
||||
for (unsigned int i = 0; i < geometry.getNumPrimitiveSets(); i++) {
|
||||
osg::PrimitiveSet* ps = geometry.getPrimitiveSet(i);
|
||||
switch (ps->getType()) {
|
||||
case osg::PrimitiveSet::DrawArraysPrimitiveType:
|
||||
{
|
||||
osg::DrawArrays* dw = dynamic_cast<osg::DrawArrays*>(ps);
|
||||
unsigned int start = dst.size();
|
||||
osg::DrawArrays* ndw = new osg::DrawArrays(dw->getMode(), start, dw->getNumIndices());
|
||||
newGeometry->getPrimitiveSetList().push_back(ndw);
|
||||
for ( unsigned int j = 0; j < dw->getNumIndices(); j++) {
|
||||
srcArrays.append(dw->getFirst()+j, dst);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case osg::PrimitiveSet::DrawElementsUBytePrimitiveType:
|
||||
case osg::PrimitiveSet::DrawElementsUShortPrimitiveType:
|
||||
case osg::PrimitiveSet::DrawElementsUIntPrimitiveType:
|
||||
{
|
||||
osg::DrawElements* de = ps->getDrawElements();
|
||||
unsigned int start = dst.size();
|
||||
osg::DrawArrays* ndw = new osg::DrawArrays(de->getMode(), start, de->getNumIndices());
|
||||
newGeometry->getPrimitiveSetList().push_back(ndw);
|
||||
for (unsigned int j = 0; j < de->getNumIndices(); j++) {
|
||||
unsigned int idx = de->getElement(j);
|
||||
srcArrays.append(idx, dst);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case osg::PrimitiveSet::DrawArrayLengthsPrimitiveType:
|
||||
{
|
||||
osg::DrawArrayLengths* dal = dynamic_cast<osg::DrawArrayLengths*>(ps);
|
||||
unsigned int start = dst.size();
|
||||
unsigned int offset = dal->getFirst();
|
||||
unsigned int totalDrawArraysVertexes = 0;
|
||||
for (unsigned int j = 0; j < dal->size(); j++) {
|
||||
totalDrawArraysVertexes += (*dal)[j];
|
||||
}
|
||||
osg::DrawArrays* ndw = new osg::DrawArrays(dal->getMode(), start, totalDrawArraysVertexes);
|
||||
newGeometry->getPrimitiveSetList().push_back(ndw);
|
||||
|
||||
for (unsigned int v = 0; v < totalDrawArraysVertexes; v++) {
|
||||
srcArrays.append(offset + v, dst);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dst.setToGeometry(geometry);
|
||||
geometry.setPrimitiveSetList(newGeometry->getPrimitiveSetList());
|
||||
setProcessed(&geometry);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
277
src/osgPlugins/gles/EdgeIndexFunctor
Normal file
277
src/osgPlugins/gles/EdgeIndexFunctor
Normal file
@@ -0,0 +1,277 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EDGE_INDEX_FUNCTOR_H
|
||||
#define EDGE_INDEX_FUNCTOR_H
|
||||
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/Array>
|
||||
|
||||
|
||||
template<class T>
|
||||
class EdgeIndexFunctor : public osg::PrimitiveIndexFunctor, public T
|
||||
{
|
||||
public:
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2*)
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2d*)
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3d* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4d* )
|
||||
{}
|
||||
|
||||
virtual void begin(GLenum mode) {
|
||||
_modeCache = mode;
|
||||
_indexCache.clear();
|
||||
}
|
||||
|
||||
virtual void vertex(unsigned int vert) {
|
||||
_indexCache.push_back(vert);
|
||||
}
|
||||
|
||||
virtual void end() {
|
||||
if (!_indexCache.empty()) {
|
||||
drawElements(_modeCache, _indexCache.size(), &_indexCache.front());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_TRIANGLES):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 2 ; i < count ; i += 3, pos += 3)
|
||||
{
|
||||
this->operator()(pos, pos+1);
|
||||
this->operator()(pos+1, pos+2);
|
||||
this->operator()(pos+2, pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_TRIANGLE_STRIP):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos)
|
||||
{
|
||||
if ((i % 2)) {
|
||||
this->operator()(pos, pos+2);
|
||||
this->operator()(pos+2, pos+1);
|
||||
this->operator()(pos+1, pos);
|
||||
} else {
|
||||
this->operator()(pos, pos+1);
|
||||
this->operator()(pos+1, pos+2);
|
||||
this->operator()(pos, pos+2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUADS):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 3 ; i < count ; i += 4, pos += 4)
|
||||
{
|
||||
this->operator()(pos, pos+1);
|
||||
this->operator()(pos+1, pos+2);
|
||||
this->operator()(pos+2, pos+3);
|
||||
this->operator()(pos+3, pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUAD_STRIP):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 3 ; i < count ; i += 2, pos += 2)
|
||||
{
|
||||
this->operator()(pos, pos+1);
|
||||
this->operator()(pos+1, pos+3);
|
||||
this->operator()(pos+2, pos+3);
|
||||
this->operator()(pos+2, pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
|
||||
case(GL_TRIANGLE_FAN):
|
||||
{
|
||||
unsigned int pos=first+1;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) {
|
||||
this->operator()(pos, pos+1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINES):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 0 ; i < count ; i += 2, pos += 2) {
|
||||
this->operator()(pos, pos+1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case(GL_LINE_STRIP):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
|
||||
this->operator()(pos, pos+1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case(GL_LINE_LOOP):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 0 ; i < count - 1; i += 1, pos += 1) {
|
||||
this->operator()(pos, pos+1);
|
||||
}
|
||||
this->operator()(pos, first);
|
||||
}
|
||||
break;
|
||||
case(GL_POINTS):
|
||||
break;
|
||||
default:
|
||||
// can't be converted into to edges.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
void drawElements(GLenum mode, GLsizei count, const I* indices) {
|
||||
if (indices == 0 || count == 0) return;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_TRIANGLES):
|
||||
{
|
||||
const I* ilast = &indices[count];
|
||||
for(const I* iptr = indices ; iptr < ilast ; iptr += 3) {
|
||||
this->operator()(*iptr, *(iptr+1));
|
||||
this->operator()(*(iptr+1), *(iptr+2));
|
||||
this->operator()(*iptr, *(iptr+2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_TRIANGLE_STRIP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
|
||||
I v0 = *(iptr),
|
||||
v1 = *(iptr + 1),
|
||||
v2 = *(iptr + 2);
|
||||
|
||||
// when merging strips we create degenerate triangles and add
|
||||
// non existing edges that should be filtered
|
||||
if(v0 == v1 || v0 == v2 || v1 == v2) continue;
|
||||
|
||||
if ((i % 2)) {
|
||||
this->operator()(v0, v2);
|
||||
this->operator()(v2, v1);
|
||||
this->operator()(v0, v1);
|
||||
} else {
|
||||
this->operator()(v0, v1);
|
||||
this->operator()(v1, v2);
|
||||
this->operator()(v0, v2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUADS):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 3 ; i < count ; i += 4, iptr += 4) {
|
||||
this->operator()(*(iptr), *(iptr+1));
|
||||
this->operator()(*(iptr+1), *(iptr+2));
|
||||
this->operator()(*(iptr+2), *(iptr+3) );
|
||||
this->operator()(*(iptr), *(iptr+3) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUAD_STRIP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 3 ; i < count ; i += 2, iptr += 2) {
|
||||
this->operator()(*(iptr), *(iptr+1));
|
||||
this->operator()(*(iptr+3), *(iptr+1));
|
||||
this->operator()(*(iptr+2), *(iptr+3));
|
||||
this->operator()(*(iptr), *(iptr+2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
|
||||
case(GL_TRIANGLE_FAN):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
++iptr;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
|
||||
this->operator()(*(iptr), *(iptr+1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINES):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 2, iptr += 2) {
|
||||
this->operator()(*iptr, *(iptr+1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case(GL_LINE_STRIP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
|
||||
this->operator()(*iptr, *(iptr+1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case(GL_LINE_LOOP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
I first = *iptr;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
|
||||
this->operator()(*iptr, *(iptr+1));
|
||||
}
|
||||
this->operator()(*iptr, first);
|
||||
}
|
||||
break;
|
||||
case(GL_POINTS):
|
||||
break;
|
||||
default:
|
||||
// can't be converted into to edge lines
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) {
|
||||
drawElements<GLubyte>(mode, count, indices);
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode, GLsizei count, const GLushort* indices) {
|
||||
drawElements<GLushort>(mode, count, indices);
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode, GLsizei count, const GLuint* indices) {
|
||||
drawElements<GLuint>(mode, count, indices);
|
||||
}
|
||||
|
||||
|
||||
GLenum _modeCache;
|
||||
std::vector<GLuint> _indexCache;
|
||||
};
|
||||
|
||||
#endif
|
||||
339
src/osgPlugins/gles/GeometryArray
Normal file
339
src/osgPlugins/gles/GeometryArray
Normal file
@@ -0,0 +1,339 @@
|
||||
/* -*-c++-*- */
|
||||
#ifndef GEOMETRY_ARRAY_UTILS_H
|
||||
#define GEOMETRY_ARRAY_UTILS_H
|
||||
|
||||
#include <osg/Array>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Notify>
|
||||
#include <osg/Geometry>
|
||||
|
||||
|
||||
typedef std::vector<unsigned int> IndexList;
|
||||
|
||||
struct GeometryArrayList {
|
||||
|
||||
class ArrayIndexAppendVisitor : public osg::ArrayVisitor
|
||||
{
|
||||
public:
|
||||
ArrayIndexAppendVisitor(const IndexList& indexes, osg::Array* dst): _indexes(indexes), _dst(dst)
|
||||
{
|
||||
}
|
||||
|
||||
const IndexList& _indexes;
|
||||
osg::Array* _dst;
|
||||
|
||||
template<class T>
|
||||
inline void copy(T& array)
|
||||
{
|
||||
if (!_dst) {
|
||||
osg::notify(osg::WARN) << "Can't append to array null" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
T* dstArray = dynamic_cast<T*>(_dst);
|
||||
for(IndexList::const_iterator it = _indexes.begin(); it != _indexes.end(); ++it)
|
||||
{
|
||||
unsigned int idx = *it;
|
||||
dstArray->push_back(array[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void apply(osg::Array&) {}
|
||||
virtual void apply(osg::ByteArray& array) { copy(array); }
|
||||
virtual void apply(osg::ShortArray& array) { copy(array); }
|
||||
virtual void apply(osg::IntArray& array) { copy(array); }
|
||||
virtual void apply(osg::UByteArray& array) { copy(array); }
|
||||
virtual void apply(osg::UShortArray& array) { copy(array); }
|
||||
virtual void apply(osg::UIntArray& array) { copy(array); }
|
||||
virtual void apply(osg::FloatArray& array) { copy(array); }
|
||||
virtual void apply(osg::DoubleArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2Array& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3Array& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4Array& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec4ubArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2bArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3bArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4bArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2sArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3sArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4sArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2dArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3dArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4dArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::MatrixfArray& array) { copy(array); }
|
||||
protected:
|
||||
|
||||
ArrayIndexAppendVisitor& operator = (const ArrayIndexAppendVisitor&) { return *this; }
|
||||
};
|
||||
|
||||
|
||||
struct ArrayAppendElement {
|
||||
|
||||
template <class T> bool arrayAppendElement(osg::Array* src, unsigned int index, osg::Array* dst)
|
||||
{
|
||||
T* array= dynamic_cast<T*>(src);
|
||||
if (array) {
|
||||
T* arrayDst = dynamic_cast<T*>(dst);
|
||||
arrayDst->push_back((*array)[index]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void operator()(osg::Array* src, unsigned int index, osg::Array* dst) {
|
||||
|
||||
if (arrayAppendElement<osg::FloatArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2Array>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec3Array>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4Array>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4ubArray>(src, index, dst))
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
struct ArraySetNumElements {
|
||||
|
||||
template <class T> bool arraySetNumElements(osg::Array* src, unsigned int numElements)
|
||||
{
|
||||
T* array= dynamic_cast<T*>(src);
|
||||
if (array) {
|
||||
array->resize(numElements);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void operator()(osg::Array* array, unsigned int numElements) {
|
||||
|
||||
if (arraySetNumElements<osg::FloatArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2Array>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec3Array>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4Array>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4ubArray>(array, numElements))
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
osg::ref_ptr<osg::Array> _vertexes;
|
||||
osg::ref_ptr<osg::Array> _normals;
|
||||
osg::ref_ptr<osg::Array> _colors;
|
||||
osg::ref_ptr<osg::Array> _secondaryColors;
|
||||
osg::ref_ptr<osg::Array> _fogCoords;
|
||||
std::vector<osg::ref_ptr<osg::Array> > _texCoordArrays;
|
||||
std::vector<osg::ref_ptr<osg::Array> > _attributesArrays;
|
||||
|
||||
GeometryArrayList() {}
|
||||
GeometryArrayList(osg::Geometry& geometry) {
|
||||
_vertexes = geometry.getVertexArray();
|
||||
unsigned int nbvertexes = _vertexes->getNumElements();
|
||||
if (geometry.getNormalArray() && nbvertexes == geometry.getNormalArray()->getNumElements())
|
||||
_normals = geometry.getNormalArray();
|
||||
|
||||
if (geometry.getColorArray() && nbvertexes == geometry.getColorArray()->getNumElements())
|
||||
_colors = geometry.getColorArray();
|
||||
|
||||
if (geometry.getSecondaryColorArray() && nbvertexes == geometry.getSecondaryColorArray()->getNumElements())
|
||||
_secondaryColors = geometry.getSecondaryColorArray();
|
||||
|
||||
if (geometry.getFogCoordArray() && nbvertexes == geometry.getFogCoordArray()->getNumElements())
|
||||
_fogCoords = geometry.getFogCoordArray();
|
||||
|
||||
_texCoordArrays.resize(geometry.getNumTexCoordArrays());
|
||||
for(unsigned int i=0;i<geometry.getNumTexCoordArrays();++i)
|
||||
if (geometry.getTexCoordArray(i) && nbvertexes == geometry.getTexCoordArray(i)->getNumElements())
|
||||
_texCoordArrays[i] = geometry.getTexCoordArray(i);
|
||||
|
||||
_attributesArrays.resize(geometry.getNumVertexAttribArrays());
|
||||
for(unsigned int i=0;i<geometry.getNumVertexAttribArrays();++i)
|
||||
if (geometry.getVertexAttribArrayList()[i] && nbvertexes == geometry.getVertexAttribArrayList()[i]->getNumElements())
|
||||
_attributesArrays[i] = geometry.getVertexAttribArrayList()[i];
|
||||
}
|
||||
|
||||
void setNumElements(unsigned int nbElements) {
|
||||
if (_vertexes.valid())
|
||||
ArraySetNumElements()(_vertexes.get(), nbElements);
|
||||
|
||||
if (_normals.valid())
|
||||
ArraySetNumElements()(_normals.get(), nbElements);
|
||||
|
||||
if (_colors.valid())
|
||||
ArraySetNumElements()(_colors.get(), nbElements);
|
||||
|
||||
if (_secondaryColors.valid())
|
||||
ArraySetNumElements()(_secondaryColors.get(), nbElements);
|
||||
|
||||
if (_fogCoords.valid())
|
||||
ArraySetNumElements()(_fogCoords.get(), nbElements);
|
||||
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++)
|
||||
if (_texCoordArrays[i].valid())
|
||||
ArraySetNumElements()(_texCoordArrays[i].get(), nbElements);
|
||||
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++)
|
||||
if (_attributesArrays[i].valid())
|
||||
ArraySetNumElements()(_attributesArrays[i].get(), nbElements);
|
||||
}
|
||||
|
||||
unsigned int append(unsigned int index, GeometryArrayList& dst) {
|
||||
if (_vertexes.valid())
|
||||
ArrayAppendElement()(_vertexes.get(), index, dst._vertexes.get());
|
||||
|
||||
if (_normals.valid())
|
||||
ArrayAppendElement()(_normals.get(), index, dst._normals.get());
|
||||
|
||||
if (_colors.valid())
|
||||
ArrayAppendElement()(_colors.get(), index, dst._colors.get());
|
||||
|
||||
if (_secondaryColors.valid())
|
||||
ArrayAppendElement()(_secondaryColors.get(), index, dst._secondaryColors.get());
|
||||
|
||||
if (_fogCoords.valid())
|
||||
ArrayAppendElement()(_fogCoords.get(), index, dst._fogCoords.get());
|
||||
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++)
|
||||
if (_texCoordArrays[i].valid())
|
||||
ArrayAppendElement()(_texCoordArrays[i].get(), index, dst._texCoordArrays[i].get());
|
||||
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++)
|
||||
if (_attributesArrays[i].valid())
|
||||
ArrayAppendElement()(_attributesArrays[i].get(), index, dst._attributesArrays[i].get());
|
||||
|
||||
return dst._vertexes->getNumElements()-1;
|
||||
}
|
||||
|
||||
|
||||
unsigned int append(const IndexList& indexes, GeometryArrayList& dst) {
|
||||
|
||||
if (_vertexes.valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._vertexes.get());
|
||||
_vertexes->accept(append);
|
||||
}
|
||||
|
||||
if (_normals.valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._normals.get());
|
||||
_normals->accept(append);
|
||||
}
|
||||
|
||||
if (_colors.valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._colors.get());
|
||||
_colors->accept(append);
|
||||
}
|
||||
|
||||
if (_secondaryColors.valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._secondaryColors.get());
|
||||
_secondaryColors->accept(append);
|
||||
}
|
||||
|
||||
if (_fogCoords.valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._fogCoords.get());
|
||||
_fogCoords->accept(append);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++)
|
||||
if (_texCoordArrays[i].valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._texCoordArrays[i].get());
|
||||
_texCoordArrays[i]->accept(append);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++)
|
||||
if (_attributesArrays[i].valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._attributesArrays[i].get());
|
||||
_attributesArrays[i]->accept(append);
|
||||
}
|
||||
|
||||
return dst._vertexes->getNumElements()-1;
|
||||
}
|
||||
|
||||
GeometryArrayList cloneType() const {
|
||||
GeometryArrayList array;
|
||||
if (_vertexes.valid())
|
||||
array._vertexes = dynamic_cast<osg::Array*>(_vertexes->cloneType());
|
||||
|
||||
if (_normals.valid())
|
||||
array._normals = dynamic_cast<osg::Array*>(_normals->cloneType());
|
||||
|
||||
if (_colors.valid())
|
||||
array._colors = dynamic_cast<osg::Array*>(_colors->cloneType());
|
||||
|
||||
if (_secondaryColors.valid())
|
||||
array._secondaryColors = dynamic_cast<osg::Array*>(_secondaryColors->cloneType());
|
||||
|
||||
if (_fogCoords.valid())
|
||||
array._fogCoords = dynamic_cast<osg::Array*>(_fogCoords->cloneType());
|
||||
|
||||
array._texCoordArrays.resize(_texCoordArrays.size());
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++) {
|
||||
if (_texCoordArrays[i].valid())
|
||||
array._texCoordArrays[i] = dynamic_cast<osg::Array*>(_texCoordArrays[i]->cloneType());
|
||||
}
|
||||
|
||||
array._attributesArrays.resize(_attributesArrays.size());
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++) {
|
||||
if (_attributesArrays[i].valid())
|
||||
array._attributesArrays[i] = dynamic_cast<osg::Array*>(_attributesArrays[i]->cloneType());
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
unsigned int size() const {
|
||||
return _vertexes->getNumElements();
|
||||
}
|
||||
|
||||
void setToGeometry(osg::Geometry& geom) {
|
||||
if (_vertexes.valid())
|
||||
geom.setVertexArray(_vertexes.get());
|
||||
|
||||
if (_normals.valid()) {
|
||||
geom.setNormalArray(_normals.get(), osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (_colors.valid()) {
|
||||
geom.setColorArray(_colors.get(), osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (_secondaryColors.valid()) {
|
||||
geom.setSecondaryColorArray(_secondaryColors.get(), osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (_fogCoords.valid()) {
|
||||
geom.setFogCoordArray(_fogCoords.get(), osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); ++i) {
|
||||
if (_texCoordArrays[i].valid()) {
|
||||
geom.setTexCoordArray(i, _texCoordArrays[i].get(), osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); ++i) {
|
||||
if (_attributesArrays[i].valid()) {
|
||||
geom.setVertexAttribArray(i, _attributesArrays[i].get(), osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
381
src/osgPlugins/gles/GeometrySplitterVisitor
Normal file
381
src/osgPlugins/gles/GeometrySplitterVisitor
Normal file
@@ -0,0 +1,381 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GEOMETRY_SPLITTER_VISITOR
|
||||
#define GEOMETRY_SPLITTER_VISITOR
|
||||
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/ValueObject>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
|
||||
#include "GeometryArray"
|
||||
#include "GeometryUniqueVisitor"
|
||||
#include "glesUtil"
|
||||
|
||||
|
||||
class GeometryIndexSplitter
|
||||
{
|
||||
public:
|
||||
typedef std::vector<osg::ref_ptr<osg::Geometry> > GeometryList;
|
||||
|
||||
GeometryIndexSplitter(unsigned int maxIndex, bool disablePostTransform):
|
||||
_maxIndexToSplit(maxIndex), _disablePostTransform(disablePostTransform)
|
||||
{}
|
||||
|
||||
bool split(osg::Geometry& geometry) {
|
||||
if(!hasValidPrimitives(geometry) || !needToSplit(geometry)) {
|
||||
if(!_disablePostTransform) {
|
||||
// optimize cache for rendering
|
||||
glesUtil::VertexCacheVisitor posttransform;
|
||||
posttransform.optimizeVertices(geometry);
|
||||
}
|
||||
_geometryList.push_back(&geometry); // remap geometry to itself
|
||||
return false;
|
||||
}
|
||||
|
||||
// keep bounding box data as user value if needed in subsequent processing
|
||||
attachBufferBoundingBox(geometry);
|
||||
|
||||
// first optimize primitives indexing
|
||||
{
|
||||
if(!_disablePostTransform) {
|
||||
// post-transform for better locality
|
||||
glesUtil::VertexCacheVisitor posttransform;
|
||||
posttransform.optimizeVertices(geometry);
|
||||
}
|
||||
// pre-transform to reindex correctly
|
||||
glesUtil::VertexAccessOrderVisitor pretransform;
|
||||
pretransform.optimizeOrder(geometry);
|
||||
}
|
||||
|
||||
// Clone geometry as we will modify vertex arrays and primitives
|
||||
// To avoid issues with shared buffers, arrays & primitives should be
|
||||
// deep cloned.
|
||||
// As UserValues might be changed on a per-geometry basis afterwards, we
|
||||
// also deep clone userdata
|
||||
// We do *not* want to clone statesets as they reference a UniqueID that
|
||||
// should be unique (see #83056464).
|
||||
osg::ref_ptr<osg::Geometry> processing = osg::clone(&geometry, osg::CopyOp::DEEP_COPY_ARRAYS |
|
||||
osg::CopyOp::DEEP_COPY_PRIMITIVES |
|
||||
osg::CopyOp::DEEP_COPY_USERDATA);
|
||||
osg::ref_ptr<osg::Geometry> reported;
|
||||
while (true) {
|
||||
reported = doSplit(*processing);
|
||||
|
||||
// reduce vertex array if needed
|
||||
if(processing && processing->getNumPrimitiveSets()) {
|
||||
GeometryArrayList arrayList(*processing);
|
||||
arrayList.setNumElements(osg::minimum(arrayList.size(), _maxIndexToSplit + 1));
|
||||
_geometryList.push_back(processing);
|
||||
}
|
||||
|
||||
if (!reported.valid()) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
processing = reported;
|
||||
reported = 0;
|
||||
|
||||
// re order index elements
|
||||
glesUtil::VertexAccessOrderVisitor preTransform;
|
||||
preTransform.optimizeOrder(*processing);
|
||||
}
|
||||
}
|
||||
|
||||
osg::notify(osg::NOTICE) << "geometry " << &geometry << " " << geometry.getName()
|
||||
<< " vertexes (" << geometry.getVertexArray()->getNumElements()
|
||||
<< ") has DrawElements index > " << _maxIndexToSplit << ", splitted to "
|
||||
<< _geometryList.size() << " geometry" << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool hasValidPrimitives(osg::Geometry& geometry) const {
|
||||
for (unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) {
|
||||
osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
|
||||
if (primitive) {
|
||||
if (!primitive->getDrawElements()) {
|
||||
osg::notify(osg::INFO) << "can't split Geometry " << geometry.getName()
|
||||
<< " (" << &geometry << ") contains non indexed primitives"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (primitive->getMode()) {
|
||||
case osg::PrimitiveSet::TRIANGLES:
|
||||
case osg::PrimitiveSet::LINES:
|
||||
case osg::PrimitiveSet::POINTS:
|
||||
break;
|
||||
default:
|
||||
osg::notify(osg::FATAL) << "can't split Geometry " << geometry.getName()
|
||||
<< " (" << &geometry << ") contains non point/line/triangle primitives"
|
||||
<< std::endl;
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool needToSplit(const osg::Geometry& geometry) const {
|
||||
for(unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) {
|
||||
const osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements();
|
||||
if (needToSplit(*primitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool needToSplit(const osg::DrawElements& primitive) const {
|
||||
for(unsigned int j = 0; j < primitive.getNumIndices(); j++) {
|
||||
if (primitive.index(j) > _maxIndexToSplit){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void attachBufferBoundingBox(osg::Geometry& geometry) const {
|
||||
// positions
|
||||
setBufferBoundingBox(dynamic_cast<osg::Vec3Array*>(geometry.getVertexArray()));
|
||||
// uvs
|
||||
for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) {
|
||||
setBufferBoundingBox(dynamic_cast<osg::Vec2Array*>(geometry.getTexCoordArray(i)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void setBufferBoundingBox(T* buffer) const {
|
||||
if(!buffer) return;
|
||||
|
||||
typename T::ElementDataType bbl;
|
||||
typename T::ElementDataType ufr;
|
||||
const unsigned int dimension = buffer->getDataSize();
|
||||
|
||||
if(buffer->getNumElements()) {
|
||||
for(unsigned int i = 0 ; i < dimension ; ++i) {
|
||||
bbl[i] = ufr[i] = (*buffer->begin())[i];
|
||||
}
|
||||
|
||||
for(typename T::const_iterator it = buffer->begin() + 1 ; it != buffer->end() ; ++ it) {
|
||||
for(unsigned int i = 0 ; i < dimension ; ++ i) {
|
||||
bbl[i] = std::min(bbl[i], (*it)[i]);
|
||||
ufr[i] = std::max(ufr[i], (*it)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
buffer->setUserValue("bbl", bbl);
|
||||
buffer->setUserValue("ufr", ufr);
|
||||
}
|
||||
}
|
||||
|
||||
osg::Geometry* doSplit(osg::Geometry& geometry) const {
|
||||
osg::Geometry::PrimitiveSetList geomPrimitives;
|
||||
osg::Geometry::PrimitiveSetList wirePrimitives;
|
||||
osg::Geometry::PrimitiveSetList reportedPrimitives;
|
||||
std::vector< osg::ref_ptr<osg::DrawElements> > primitivesToFix;
|
||||
|
||||
osg::Geometry::PrimitiveSetList& primitives = geometry.getPrimitiveSetList();
|
||||
std::set<unsigned int> validIndices;
|
||||
osg::ref_ptr<osg::Geometry> reportGeometry;
|
||||
|
||||
for (unsigned int i = 0; i < primitives.size(); i++) {
|
||||
osg::DrawElements *primitive = primitives[i]->getDrawElements();
|
||||
|
||||
bool isWireframe = false;
|
||||
if(primitive->getUserValue("wireframe", isWireframe)) {
|
||||
wirePrimitives.push_back(primitive);
|
||||
}
|
||||
else if (needToSplit(*primitive)) {
|
||||
primitivesToFix.push_back(primitive);
|
||||
}
|
||||
else {
|
||||
geomPrimitives.push_back(primitive);
|
||||
setValidIndices(validIndices, primitive);
|
||||
}
|
||||
}
|
||||
|
||||
if (!primitivesToFix.empty()) {
|
||||
// filter all indices > _maxIndexValue in primitives
|
||||
for (unsigned int i = 0; i < primitivesToFix.size(); i++) {
|
||||
osg::DrawElements* source = primitivesToFix[i].get();
|
||||
osg::DrawElements* large = removeLargeIndices(dynamic_cast<osg::DrawElementsUInt*>(source));
|
||||
reportedPrimitives.push_back(large);
|
||||
geomPrimitives.push_back(source);
|
||||
setValidIndices(validIndices, source);
|
||||
}
|
||||
}
|
||||
|
||||
// keep wireframe data associated to the current solid geometry
|
||||
extractWireframePrimitive(wirePrimitives, validIndices, geomPrimitives, reportedPrimitives);
|
||||
|
||||
geometry.setPrimitiveSetList(geomPrimitives);
|
||||
if (!reportedPrimitives.empty()) {
|
||||
reportGeometry = osg::clone(&geometry, osg::CopyOp::DEEP_COPY_ARRAYS |
|
||||
osg::CopyOp::DEEP_COPY_USERDATA);
|
||||
reportGeometry->setPrimitiveSetList(reportedPrimitives);
|
||||
return reportGeometry.release();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setValidIndices(std::set<unsigned int>& indices, const osg::DrawElements* primitive) const {
|
||||
for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) {
|
||||
indices.insert(primitive->index(j));
|
||||
}
|
||||
}
|
||||
|
||||
osg::DrawElements* removeLargeIndices(osg::DrawElementsUInt* source) const {
|
||||
osg::DrawElementsUInt* large = new osg::DrawElementsUInt(source->getMode());
|
||||
unsigned int primitive_size = 0;
|
||||
switch(source->getMode()) {
|
||||
case osg::PrimitiveSet::POINTS:
|
||||
primitive_size = 1;
|
||||
break;
|
||||
case osg::PrimitiveSet::LINES:
|
||||
primitive_size = 2;
|
||||
break;
|
||||
case osg::PrimitiveSet::TRIANGLES:
|
||||
primitive_size = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int id = source->getNumPrimitives() - 1; id >= 0; -- id) {
|
||||
const unsigned int arrayIndex = id * primitive_size;
|
||||
for(unsigned int i = 0 ; i < primitive_size ; ++ i) {
|
||||
if(source->index(arrayIndex + i) > _maxIndexToSplit) {
|
||||
// add primitive in the large DrawElements
|
||||
for(unsigned int j = 0 ; j < primitive_size ; ++ j) {
|
||||
large->addElement(source->index(arrayIndex + j));
|
||||
}
|
||||
// remove primitive from source DrawElements
|
||||
for(int j = primitive_size - 1 ; j >= 0 ; -- j) {
|
||||
source->erase(source->begin() + arrayIndex + j);
|
||||
}
|
||||
break; // skip to next primitive
|
||||
}
|
||||
}
|
||||
}
|
||||
return large;
|
||||
}
|
||||
|
||||
// keep wireframe data associated to the solid geometry
|
||||
void extractWireframePrimitive(osg::Geometry::PrimitiveSetList& lines,
|
||||
const std::set<unsigned int>& indices,
|
||||
osg::Geometry::PrimitiveSetList& primitives,
|
||||
osg::Geometry::PrimitiveSetList& reported) const {
|
||||
if(indices.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0 ; i < lines.size() ; ++ i) {
|
||||
const osg::DrawElementsUInt* line = dynamic_cast<osg::DrawElementsUInt*>(lines[i].get());
|
||||
if(!line || line->getMode() != osg::PrimitiveSet::LINES) {
|
||||
osg::notify(osg::INFO) << "Primitive with bad mode flagged as wireframe. Skipping."
|
||||
<< std::endl;
|
||||
}
|
||||
osg::ref_ptr<osg::DrawElementsUInt> small = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES);
|
||||
osg::ref_ptr<osg::DrawElementsUInt> large = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES);
|
||||
|
||||
for (unsigned int id = 0 ; id < line->getNumPrimitives() ; ++ id) {
|
||||
unsigned int arrayIndex = id * 2;
|
||||
unsigned int a = line->index(arrayIndex),
|
||||
b = line->index(arrayIndex + 1);
|
||||
|
||||
if(indices.find(a) != indices.end() && indices.find(b) != indices.end()) {
|
||||
small->addElement(a);
|
||||
small->addElement(b);
|
||||
}
|
||||
else {
|
||||
large->addElement(a);
|
||||
large->addElement(b);
|
||||
}
|
||||
}
|
||||
|
||||
if(small->size()) {
|
||||
small->setUserValue("wireframe", true);
|
||||
primitives.push_back(small);
|
||||
}
|
||||
|
||||
if(large->size()) {
|
||||
large->setUserValue("wireframe", true);
|
||||
reported.push_back(large);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
const unsigned int _maxIndexToSplit;
|
||||
bool _disablePostTransform;
|
||||
GeometryList _geometryList;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class GeometrySplitterVisitor : public GeometryUniqueVisitor {
|
||||
public:
|
||||
typedef std::vector< osg::ref_ptr<osg::Geometry> > GeometryList;
|
||||
|
||||
GeometrySplitterVisitor(unsigned int maxIndexValue = 65535, bool disablePostTransform=false):
|
||||
GeometryUniqueVisitor("GeometrySplitterVisitor"),
|
||||
_maxIndexValue(maxIndexValue),
|
||||
_disablePostTransform(disablePostTransform)
|
||||
{}
|
||||
|
||||
void apply(osg::Geode& geode) {
|
||||
GeometryUniqueVisitor::apply(geode);
|
||||
GeometryList remapped;
|
||||
for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) {
|
||||
osg::Geometry* geometry = geode.getDrawable(i)->asGeometry();
|
||||
if(geometry) {
|
||||
std::map<osg::Geometry*, GeometryList>::iterator lookup = _split.find(geometry);
|
||||
if(lookup != _split.end() && !lookup->second.empty()) {
|
||||
remapped.insert(remapped.end(), lookup->second.begin(), lookup->second.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove all drawables
|
||||
geode.removeDrawables(0, geode.getNumDrawables());
|
||||
for(unsigned int i = 0 ; i < remapped.size() ; ++ i) {
|
||||
geode.addDrawable(remapped[i].get());
|
||||
}
|
||||
}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
GeometryIndexSplitter splitter(_maxIndexValue, _disablePostTransform);
|
||||
splitter.split(geometry);
|
||||
setProcessed(&geometry, splitter._geometryList);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool isProcessed(osg::Geometry* node) {
|
||||
return _split.find(node) != _split.end();
|
||||
}
|
||||
|
||||
void setProcessed(osg::Geometry* node, const GeometryList& list) {
|
||||
_split.insert(std::pair<osg::Geometry*, GeometryList>(node, GeometryList(list)));
|
||||
}
|
||||
|
||||
unsigned int _maxIndexValue;
|
||||
std::map<osg::Geometry*, GeometryList> _split;
|
||||
bool _disablePostTransform;
|
||||
};
|
||||
|
||||
#endif
|
||||
66
src/osgPlugins/gles/GeometryUniqueVisitor
Normal file
66
src/osgPlugins/gles/GeometryUniqueVisitor
Normal file
@@ -0,0 +1,66 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GEOMETRY_UNIQUE_VISITOR_H
|
||||
#define GEOMETRY_UNIQUE_VISITOR_H
|
||||
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Geode>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "StatLogger"
|
||||
|
||||
|
||||
class GeometryUniqueVisitor : public osg::NodeVisitor {
|
||||
public:
|
||||
GeometryUniqueVisitor(const std::string label=std::string("GeometryUniqueVisitor")):
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
||||
_logger(formatStatLabel(label))
|
||||
{}
|
||||
|
||||
virtual void apply(osg::Geode& geode){
|
||||
for (unsigned int i = 0; i < geode.getNumDrawables(); i++) {
|
||||
apply(*geode.getDrawable(i));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void apply(osg::Drawable& drawable){
|
||||
osg::Geometry* geometry = drawable.asGeometry();
|
||||
if (!geometry || isProcessed(geometry)) {
|
||||
return;
|
||||
}
|
||||
apply(*geometry);
|
||||
}
|
||||
|
||||
virtual void apply(osg::Geometry& geometry) {} // to be implemented by actual visitors
|
||||
|
||||
protected:
|
||||
bool isProcessed(osg::Geometry* node) {
|
||||
return _processed.find(node) != _processed.end();
|
||||
}
|
||||
|
||||
void setProcessed(osg::Geometry* node) {
|
||||
_processed.insert(node);
|
||||
}
|
||||
|
||||
std::string formatStatLabel(const std::string& label) const {
|
||||
return label + "::apply(..)";
|
||||
}
|
||||
|
||||
std::set<osg::Geometry*> _processed;
|
||||
StatLogger _logger;
|
||||
};
|
||||
|
||||
#endif
|
||||
34
src/osgPlugins/gles/IndexMeshVisitor
Normal file
34
src/osgPlugins/gles/IndexMeshVisitor
Normal file
@@ -0,0 +1,34 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INDEX_MESH_VISITOR
|
||||
#define INDEX_MESH_VISITOR
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
|
||||
class IndexMeshVisitor : public GeometryUniqueVisitor
|
||||
{
|
||||
public:
|
||||
IndexMeshVisitor(): GeometryUniqueVisitor("IndexMeshVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geom);
|
||||
|
||||
protected:
|
||||
typedef std::vector<unsigned int> IndexList;
|
||||
void addDrawElements(IndexList&,
|
||||
osg::PrimitiveSet::Mode,
|
||||
osg::Geometry::PrimitiveSetList&,
|
||||
std::string userValue = std::string());
|
||||
};
|
||||
|
||||
#endif
|
||||
174
src/osgPlugins/gles/IndexMeshVisitor.cpp
Normal file
174
src/osgPlugins/gles/IndexMeshVisitor.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include <vector>
|
||||
#include <algorithm> // sort
|
||||
#include <limits> // numeric_limits
|
||||
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/ValueObject>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
|
||||
#include "glesUtil"
|
||||
#include "IndexMeshVisitor"
|
||||
#include "PrimitiveIndexors"
|
||||
|
||||
|
||||
using namespace glesUtil;
|
||||
|
||||
void IndexMeshVisitor::apply(osg::Geometry& geom) {
|
||||
// TODO: this is deprecated
|
||||
if (geom.getNormalBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
|
||||
if (geom.getColorBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
|
||||
if (geom.getSecondaryColorBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
|
||||
if (geom.getFogCoordBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
|
||||
|
||||
// no point optimizing if we don't have enough vertices.
|
||||
if (!geom.getVertexArray() || geom.getVertexArray()->getNumElements() < 3) return;
|
||||
|
||||
|
||||
osgUtil::SharedArrayOptimizer deduplicator;
|
||||
deduplicator.findDuplicatedUVs(geom);
|
||||
|
||||
// duplicate shared arrays as it isn't safe to rearrange vertices when arrays are shared.
|
||||
if (geom.containsSharedArrays()) {
|
||||
geom.duplicateSharedArrays();
|
||||
}
|
||||
|
||||
osg::Geometry::PrimitiveSetList& primitives = geom.getPrimitiveSetList();
|
||||
osg::Geometry::PrimitiveSetList::iterator itr;
|
||||
|
||||
osg::Geometry::PrimitiveSetList new_primitives;
|
||||
new_primitives.reserve(primitives.size());
|
||||
|
||||
// compute duplicate vertices
|
||||
typedef std::vector<unsigned int> IndexList;
|
||||
unsigned int numVertices = geom.getVertexArray()->getNumElements();
|
||||
IndexList indices(numVertices);
|
||||
unsigned int i, j;
|
||||
for(i = 0 ; i < numVertices ; ++ i) {
|
||||
indices[i] = i;
|
||||
}
|
||||
|
||||
VertexAttribComparitor arrayComparitor(geom);
|
||||
std::sort(indices.begin(), indices.end(), arrayComparitor);
|
||||
|
||||
unsigned int lastUnique = 0;
|
||||
unsigned int numUnique = 1;
|
||||
for(i = 1 ; i < numVertices ; ++ i) {
|
||||
if (arrayComparitor.compare(indices[lastUnique], indices[i]) != 0) {
|
||||
lastUnique = i;
|
||||
++ numUnique;
|
||||
}
|
||||
}
|
||||
|
||||
IndexList remapDuplicatesToOrignals(numVertices);
|
||||
lastUnique = 0;
|
||||
for(i = 1 ; i < numVertices ; ++ i) {
|
||||
if (arrayComparitor.compare(indices[lastUnique],indices[i]) != 0) {
|
||||
// found a new vertex entry, so previous run of duplicates needs
|
||||
// to be put together.
|
||||
unsigned int min_index = indices[lastUnique];
|
||||
for(j = lastUnique + 1 ; j < i ; ++ j) {
|
||||
min_index = osg::minimum(min_index, indices[j]);
|
||||
}
|
||||
for(j = lastUnique ; j < i ; ++ j) {
|
||||
remapDuplicatesToOrignals[indices[j]] = min_index;
|
||||
}
|
||||
lastUnique = i;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int min_index = indices[lastUnique];
|
||||
for(j = lastUnique + 1 ; j < i ; ++ j) {
|
||||
min_index = osg::minimum(min_index, indices[j]);
|
||||
}
|
||||
for(j = lastUnique ; j < i ; ++ j) {
|
||||
remapDuplicatesToOrignals[indices[j]] = min_index;
|
||||
}
|
||||
|
||||
// copy the arrays.
|
||||
IndexList finalMapping(numVertices);
|
||||
IndexList copyMapping;
|
||||
copyMapping.reserve(numUnique);
|
||||
unsigned int currentIndex = 0;
|
||||
for(i = 0 ; i < numVertices ; ++ i) {
|
||||
if (remapDuplicatesToOrignals[i] == i) {
|
||||
finalMapping[i] = currentIndex;
|
||||
copyMapping.push_back(i);
|
||||
currentIndex++;
|
||||
}
|
||||
else {
|
||||
finalMapping[i] = finalMapping[remapDuplicatesToOrignals[i]];
|
||||
}
|
||||
}
|
||||
|
||||
// remap any shared vertex attributes
|
||||
RemapArray ra(copyMapping);
|
||||
arrayComparitor.accept(ra);
|
||||
|
||||
// triangulate faces
|
||||
{
|
||||
TriangleIndexor ti;
|
||||
ti._maxIndex = numVertices;
|
||||
ti._remapping = finalMapping;
|
||||
|
||||
for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) {
|
||||
(*itr)->accept(ti);
|
||||
}
|
||||
|
||||
addDrawElements(ti._indices, osg::PrimitiveSet::TRIANGLES, new_primitives);
|
||||
}
|
||||
|
||||
// line-ify line-type primitives
|
||||
{
|
||||
LineIndexor li, wi; // lines and wireframes
|
||||
li._maxIndex = numVertices;
|
||||
wi._maxIndex = numVertices;
|
||||
li._remapping = finalMapping;
|
||||
wi._remapping = finalMapping;
|
||||
|
||||
for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) {
|
||||
bool isWireframe = false;
|
||||
if((*itr)->getUserValue("wireframe", isWireframe) && isWireframe) {
|
||||
(*itr)->accept(wi);
|
||||
}
|
||||
else {
|
||||
(*itr)->accept(li);
|
||||
}
|
||||
}
|
||||
addDrawElements(li._indices, osg::PrimitiveSet::LINES, new_primitives);
|
||||
addDrawElements(wi._indices, osg::PrimitiveSet::LINES, new_primitives, "wireframe");
|
||||
}
|
||||
|
||||
// adds points primitives
|
||||
{
|
||||
IndexList points;
|
||||
for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) {
|
||||
if((*itr) && (*itr)->getMode() == osg::PrimitiveSet::POINTS) {
|
||||
for(unsigned int k = 0 ; k < (*itr)->getNumIndices() ; ++ k) {
|
||||
points.push_back(finalMapping[(*itr)->index(k)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
addDrawElements(points, osg::PrimitiveSet::POINTS, new_primitives);
|
||||
}
|
||||
|
||||
geom.setPrimitiveSetList(new_primitives);
|
||||
deduplicator.deduplicateUVs(geom);
|
||||
setProcessed(&geom);
|
||||
}
|
||||
|
||||
|
||||
void IndexMeshVisitor::addDrawElements(IndexList& data,
|
||||
osg::PrimitiveSet::Mode mode,
|
||||
osg::Geometry::PrimitiveSetList& primitives,
|
||||
std::string userValue) {
|
||||
if(!data.empty()) {
|
||||
osg::DrawElementsUInt* elements = new osg::DrawElementsUInt(mode,
|
||||
data.begin(),
|
||||
data.end());
|
||||
if(!userValue.empty()) {
|
||||
elements->setUserValue(userValue, true);
|
||||
}
|
||||
primitives.push_back(elements);
|
||||
}
|
||||
}
|
||||
178
src/osgPlugins/gles/LineIndexFunctor
Normal file
178
src/osgPlugins/gles/LineIndexFunctor
Normal file
@@ -0,0 +1,178 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINE_INDEX_FUNCTOR_H
|
||||
#define LINE_INDEX_FUNCTOR_H
|
||||
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/Array>
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
|
||||
class Line {
|
||||
public:
|
||||
Line(unsigned int a, unsigned int b) {
|
||||
_a = std::min<unsigned int>(a, b);
|
||||
_b = std::max<unsigned int>(a, b);
|
||||
}
|
||||
|
||||
bool operator<(const Line& e) {
|
||||
return _a < e._a || (_a == e._a && _b < e._b);
|
||||
}
|
||||
|
||||
unsigned int _a, _b;
|
||||
};
|
||||
|
||||
struct LineCompare {
|
||||
bool operator()(const Line& lhs, const Line& rhs) {
|
||||
return lhs._a < rhs._a || (lhs._a == rhs._a && lhs._b < rhs._b);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class T>
|
||||
class LineIndexFunctor : public osg::PrimitiveIndexFunctor, public T
|
||||
{
|
||||
public:
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2*)
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2d*)
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3d* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4d* )
|
||||
{}
|
||||
|
||||
virtual void begin(GLenum mode) {
|
||||
_modeCache = mode;
|
||||
_indexCache.clear();
|
||||
}
|
||||
|
||||
virtual void vertex(unsigned int vert) {
|
||||
_indexCache.push_back(vert);
|
||||
}
|
||||
|
||||
virtual void end() {
|
||||
if (!_indexCache.empty()) {
|
||||
drawElements(_modeCache, _indexCache.size(), &_indexCache.front());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_LINES):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 0 ; i < count ; i += 2, pos += 2) {
|
||||
line(pos, pos + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case(GL_LINE_STRIP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
|
||||
line(pos, pos + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case(GL_LINE_LOOP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
|
||||
line(pos, pos + 1);
|
||||
}
|
||||
line(pos, first);
|
||||
}
|
||||
break;
|
||||
default: // not a line
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
void drawElements(GLenum mode, GLsizei count, const I* indices)
|
||||
{
|
||||
if (indices == 0 || count == 0) return;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_LINES):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 0 ; i < count ; i += 2, iptr += 2) {
|
||||
line(*iptr, *(iptr+1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case(GL_LINE_STRIP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
|
||||
line(*iptr, *(iptr+1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case(GL_LINE_LOOP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
I first = *iptr;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
|
||||
line(*iptr, *(iptr+1));
|
||||
}
|
||||
line(*iptr, first);
|
||||
}
|
||||
break;
|
||||
default: // not a line
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) {
|
||||
drawElements<GLubyte>(mode, count, indices);
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) {
|
||||
drawElements<GLushort>(mode, count, indices);
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) {
|
||||
drawElements<GLuint>(mode, count, indices);
|
||||
}
|
||||
|
||||
void line(unsigned int a, unsigned int b) {
|
||||
Line e(T::index(a), T::index(b)); // use remapped indices to deduplicate lines
|
||||
|
||||
if(_lineCache.find(e) == _lineCache.end()) {
|
||||
this->operator()(a, b);
|
||||
_lineCache.insert(e);
|
||||
}
|
||||
}
|
||||
|
||||
GLenum _modeCache;
|
||||
std::vector<GLuint> _indexCache;
|
||||
std::set<Line, LineCompare> _lineCache;
|
||||
};
|
||||
|
||||
#endif
|
||||
143
src/osgPlugins/gles/OpenGLESGeometryOptimizer
Normal file
143
src/osgPlugins/gles/OpenGLESGeometryOptimizer
Normal file
@@ -0,0 +1,143 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OPENGLES_GEOMETRY_OPTIMIZER
|
||||
#define OPENGLES_GEOMETRY_OPTIMIZER
|
||||
|
||||
#include <osg/Node>
|
||||
#include <algorithm> //std::max
|
||||
|
||||
#include "AnimationVisitor"
|
||||
#include "BindPerVertexVisitor"
|
||||
#include "DetachPrimitiveVisitor"
|
||||
#include "DrawArrayVisitor"
|
||||
#include "GeometrySplitterVisitor"
|
||||
#include "IndexMeshVisitor"
|
||||
#include "PreTransformVisitor"
|
||||
#include "TangentSpaceVisitor"
|
||||
#include "TriangleStripVisitor"
|
||||
#include "UnIndexMeshVisitor"
|
||||
#include "WireframeVisitor"
|
||||
|
||||
|
||||
class OpenGLESGeometryOptimizer
|
||||
{
|
||||
public:
|
||||
OpenGLESGeometryOptimizer() :
|
||||
_useDrawArray(false),
|
||||
_disableTriStrip(false),
|
||||
_disableMergeTriStrip(false),
|
||||
_disablePreTransform(false),
|
||||
_disablePostTransform(false),
|
||||
_triStripCacheSize(16),
|
||||
_triStripMinSize(2),
|
||||
_generateTangentSpace(false),
|
||||
_tangentUnit(0),
|
||||
_maxIndexValue(65535),
|
||||
_wireframe("")
|
||||
{}
|
||||
|
||||
// run the optimizer
|
||||
osg::Node* optimize(osg::Node& node);
|
||||
|
||||
// handle options
|
||||
void setUseDrawArray(bool s) { _useDrawArray = s; }
|
||||
|
||||
void setDisableTriStrip(bool s) { _disableTriStrip = s; }
|
||||
void setDisableMergeTriStrip(bool s) { _disableMergeTriStrip = s; }
|
||||
void setDisablePreTransform(bool s) { _disablePreTransform = s; }
|
||||
void setDisablePostTransform(bool s) { _disablePostTransform = s; }
|
||||
void setTripStripCacheSize(unsigned int size) { _triStripCacheSize = size; }
|
||||
void setTripStripMinSize(unsigned int size) { _triStripMinSize = std::max<unsigned int>(size, 2); }
|
||||
|
||||
void setTexCoordChannelForTangentSpace(int uv) {
|
||||
_tangentUnit = uv;
|
||||
_generateTangentSpace = true;
|
||||
}
|
||||
|
||||
void setMaxIndexValue(unsigned int s) { _maxIndexValue = s; }
|
||||
void setWireframe(const std::string& s) {
|
||||
_wireframe = s;
|
||||
if(_wireframe == std::string("outline")) {
|
||||
// no use to build strip if we only want wireframe
|
||||
setDisableTriStrip(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void makeAnimation(osg::Node* node) {
|
||||
AnimationVisitor anim;
|
||||
node->accept(anim);
|
||||
}
|
||||
|
||||
void makeWireframe(osg::Node* node) {
|
||||
WireframeVisitor wireframe(_wireframe == std::string("inline"));
|
||||
node->accept(wireframe);
|
||||
}
|
||||
|
||||
void makeBindPerVertex(osg::Node* node) {
|
||||
BindPerVertexVisitor bindpervertex;
|
||||
node->accept(bindpervertex);
|
||||
}
|
||||
|
||||
void makeIndexMesh(osg::Node* node) {
|
||||
IndexMeshVisitor indexer;
|
||||
node->accept(indexer);
|
||||
}
|
||||
|
||||
void makeTangentSpace(osg::Node* node) {
|
||||
TangentSpaceVisitor tangent(_tangentUnit);
|
||||
node->accept(tangent);
|
||||
}
|
||||
|
||||
void makeSplit(osg::Node* node) {
|
||||
GeometrySplitterVisitor splitter(_maxIndexValue, _disablePostTransform);
|
||||
node->accept(splitter);
|
||||
}
|
||||
|
||||
void makeTriStrip(osg::Node* node) {
|
||||
TriangleStripVisitor strip(_triStripCacheSize, _triStripMinSize, !_disableMergeTriStrip);
|
||||
node->accept(strip);
|
||||
}
|
||||
|
||||
void makeDrawArray(osg::Node* node) {
|
||||
DrawArrayVisitor drawarray;
|
||||
node->accept(drawarray);
|
||||
}
|
||||
|
||||
void makePreTransform(osg::Node* node) {
|
||||
PreTransformVisitor preTransform;
|
||||
node->accept(preTransform);
|
||||
}
|
||||
|
||||
void makeDetach(osg::Node* node) {
|
||||
DetachPrimitiveVisitor detacher("wireframe", false, _wireframe == std::string("inline"));
|
||||
node->accept(detacher);
|
||||
}
|
||||
|
||||
bool _useDrawArray;
|
||||
|
||||
bool _disableTriStrip;
|
||||
bool _disableMergeTriStrip;
|
||||
bool _disablePreTransform;
|
||||
bool _disablePostTransform;
|
||||
unsigned int _triStripCacheSize;
|
||||
unsigned int _triStripMinSize;
|
||||
|
||||
bool _generateTangentSpace;
|
||||
int _tangentUnit;
|
||||
|
||||
unsigned int _maxIndexValue;
|
||||
std::string _wireframe;
|
||||
};
|
||||
|
||||
#endif
|
||||
54
src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp
Normal file
54
src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <limits>
|
||||
|
||||
#include "OpenGLESGeometryOptimizer"
|
||||
#include "glesUtil"
|
||||
|
||||
const unsigned glesUtil::Remapper::invalidIndex = std::numeric_limits<unsigned>::max();
|
||||
|
||||
|
||||
osg::Node* OpenGLESGeometryOptimizer::optimize(osg::Node& node) {
|
||||
osg::ref_ptr<osg::Node> model = osg::clone(&node);
|
||||
|
||||
// animation: create regular Geometry if RigGeometry
|
||||
makeAnimation(model.get());
|
||||
|
||||
// wireframe
|
||||
if (!_wireframe.empty()) {
|
||||
makeWireframe(model.get());
|
||||
}
|
||||
|
||||
// bind per vertex
|
||||
makeBindPerVertex(model.get());
|
||||
|
||||
// index (merge exact duplicates + uses simple triangles & lines i.e. no strip/fan/loop)
|
||||
makeIndexMesh(model.get());
|
||||
|
||||
// tangent space
|
||||
if (_generateTangentSpace) {
|
||||
makeTangentSpace(model.get());
|
||||
}
|
||||
|
||||
if(!_useDrawArray) {
|
||||
// split geometries having some primitive index > _maxIndexValue
|
||||
makeSplit(model.get());
|
||||
}
|
||||
|
||||
// strip
|
||||
if(!_disableTriStrip) {
|
||||
makeTriStrip(model.get());
|
||||
}
|
||||
|
||||
if(_useDrawArray) {
|
||||
// drawelements to drawarrays
|
||||
makeDrawArray(model.get());
|
||||
}
|
||||
else if(!_disablePreTransform) {
|
||||
// pre-transform
|
||||
makePreTransform(model.get());
|
||||
}
|
||||
|
||||
// detach wireframe
|
||||
makeDetach(model.get());
|
||||
|
||||
return model.release();
|
||||
}
|
||||
153
src/osgPlugins/gles/PointIndexFunctor
Normal file
153
src/osgPlugins/gles/PointIndexFunctor
Normal file
@@ -0,0 +1,153 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef POINT_INDEX_FUNCTOR_H
|
||||
#define POINT_INDEX_FUNCTOR_H
|
||||
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/Array>
|
||||
|
||||
|
||||
template<class T>
|
||||
class PointIndexFunctor : public osg::PrimitiveIndexFunctor, public T
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2*)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3* )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4* )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2d*)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3d* )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4d* )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void begin(GLenum mode)
|
||||
{
|
||||
_modeCache = mode;
|
||||
_indexCache.clear();
|
||||
}
|
||||
|
||||
virtual void vertex(unsigned int vert)
|
||||
{
|
||||
_indexCache.push_back(vert);
|
||||
}
|
||||
|
||||
virtual void end()
|
||||
{
|
||||
if (!_indexCache.empty())
|
||||
{
|
||||
drawElements(_modeCache,_indexCache.size(),&_indexCache.front());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawArrays(GLenum mode,GLint first,GLsizei count)
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_POINTS):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i=0;i<count;++i)
|
||||
this->operator()(pos+i);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// can't be converted into to triangles.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices)
|
||||
{
|
||||
if (indices==0 || count==0) return;
|
||||
|
||||
typedef GLubyte Index;
|
||||
typedef const Index* IndexPointer;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case (GL_POINTS):
|
||||
{
|
||||
IndexPointer ilast = &indices[count];
|
||||
for(IndexPointer iptr=indices;iptr<ilast;iptr+=1)
|
||||
this->operator()(*iptr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices)
|
||||
{
|
||||
if (indices==0 || count==0) return;
|
||||
|
||||
typedef GLushort Index;
|
||||
typedef const Index* IndexPointer;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case (GL_POINTS):
|
||||
{
|
||||
IndexPointer ilast = &indices[count];
|
||||
for(IndexPointer iptr=indices;iptr<ilast;iptr+=1)
|
||||
this->operator()(*iptr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices)
|
||||
{
|
||||
if (indices==0 || count==0) return;
|
||||
|
||||
typedef GLuint Index;
|
||||
typedef const Index* IndexPointer;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case (GL_POINTS):
|
||||
{
|
||||
IndexPointer ilast = &indices[count];
|
||||
for(IndexPointer iptr=indices;iptr<ilast;iptr+=1)
|
||||
this->operator()(*iptr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum _modeCache;
|
||||
std::vector<GLuint> _indexCache;
|
||||
};
|
||||
|
||||
#endif
|
||||
35
src/osgPlugins/gles/PreTransformVisitor
Normal file
35
src/osgPlugins/gles/PreTransformVisitor
Normal file
@@ -0,0 +1,35 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PRE_TRANSFORM_VISITOR
|
||||
#define PRE_TRANSFORM_VISITOR
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
#include "glesUtil"
|
||||
|
||||
|
||||
class PreTransformVisitor : public GeometryUniqueVisitor
|
||||
{
|
||||
public:
|
||||
PreTransformVisitor(): GeometryUniqueVisitor("PreTransformVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
glesUtil::VertexAccessOrderVisitor optimizer;
|
||||
optimizer.optimizeOrder(geometry);
|
||||
|
||||
setProcessed(&geometry);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
114
src/osgPlugins/gles/PrimitiveIndexors
Normal file
114
src/osgPlugins/gles/PrimitiveIndexors
Normal file
@@ -0,0 +1,114 @@
|
||||
#ifndef PRIMITIVE_OPERATORS_H
|
||||
#define PRIMITIVE_OPERATORS_H
|
||||
|
||||
#include <vector>
|
||||
#include <osg/TriangleIndexFunctor>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
|
||||
#include "TriangleLinePointIndexFunctor"
|
||||
#include "EdgeIndexFunctor"
|
||||
#include "LineIndexFunctor"
|
||||
#include "PointIndexFunctor"
|
||||
|
||||
|
||||
typedef std::vector<unsigned int> IndexList;
|
||||
|
||||
|
||||
// Construct an index list of triangles for DrawElements for any input
|
||||
// primitives.
|
||||
struct IndexOperator {
|
||||
unsigned int _maxIndex;
|
||||
IndexList _remapping;
|
||||
IndexList _indices;
|
||||
|
||||
IndexOperator() : _maxIndex(0)
|
||||
{}
|
||||
|
||||
inline unsigned int index(unsigned int v) {
|
||||
if(_remapping.empty()) {
|
||||
return v;
|
||||
}
|
||||
else {
|
||||
return _remapping[v];
|
||||
}
|
||||
}
|
||||
|
||||
// triangles
|
||||
inline void operator()(unsigned int p1, unsigned int p2, unsigned int p3) {
|
||||
if(!valid(p1, p2, p3)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_remapping.empty()) {
|
||||
_indices.push_back(p1);
|
||||
_indices.push_back(p2);
|
||||
_indices.push_back(p3);
|
||||
}
|
||||
else {
|
||||
_indices.push_back(_remapping[p1]);
|
||||
_indices.push_back(_remapping[p2]);
|
||||
_indices.push_back(_remapping[p3]);
|
||||
}
|
||||
}
|
||||
|
||||
// edges
|
||||
inline void operator()(unsigned int p1, unsigned int p2) {
|
||||
if(!valid(p1, p2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_remapping.empty()) {
|
||||
_indices.push_back(p1);
|
||||
_indices.push_back(p2);
|
||||
}
|
||||
else {
|
||||
_indices.push_back(_remapping[p1]);
|
||||
_indices.push_back(_remapping[p2]);
|
||||
}
|
||||
}
|
||||
|
||||
// points
|
||||
inline void operator()(unsigned int p1) {
|
||||
if(!valid(p1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_remapping.empty()) {
|
||||
_indices.push_back(p1);
|
||||
}
|
||||
else {
|
||||
_indices.push_back(_remapping[p1]);
|
||||
}
|
||||
}
|
||||
|
||||
// filter primitives referencing bad indices
|
||||
inline bool valid(unsigned int v1, unsigned int v2, unsigned int v3) {
|
||||
if(_maxIndex) {
|
||||
return (v1 < _maxIndex && v2 < _maxIndex && v3 < _maxIndex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool valid(unsigned int v1, unsigned int v2) {
|
||||
if(_maxIndex) {
|
||||
return (v1 < _maxIndex && v2 < _maxIndex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool valid(unsigned int v1) {
|
||||
if(_maxIndex) {
|
||||
return (v1 < _maxIndex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef osg::TriangleIndexFunctor<IndexOperator> TriangleIndexor;
|
||||
typedef EdgeIndexFunctor<IndexOperator> EdgeIndexor;
|
||||
typedef LineIndexFunctor<IndexOperator> LineIndexor; // indexes only primitives in LINES, LINE_STRIP, LINE_LOOP mode
|
||||
typedef PointIndexFunctor<IndexOperator> PointIndexor;
|
||||
|
||||
#endif
|
||||
272
src/osgPlugins/gles/ReaderWriterGLES.cpp
Normal file
272
src/osgPlugins/gles/ReaderWriterGLES.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Version>
|
||||
#include <osg/Endian>
|
||||
|
||||
#include <osgDB/ReaderWriter>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/WriteFile>
|
||||
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "UnIndexMeshVisitor"
|
||||
#include "OpenGLESGeometryOptimizer"
|
||||
|
||||
using namespace osg;
|
||||
|
||||
|
||||
class ReaderWriterGLES : public osgDB::ReaderWriter
|
||||
{
|
||||
public:
|
||||
|
||||
struct OptionsStruct {
|
||||
std::string enableWireframe;
|
||||
bool generateTangentSpace;
|
||||
int tangentSpaceTextureUnit;
|
||||
bool disableTriStrip;
|
||||
bool disableMergeTriStrip;
|
||||
bool disablePreTransform;
|
||||
bool disablePostTransform;
|
||||
unsigned int triStripCacheSize;
|
||||
unsigned int triStripMinSize;
|
||||
bool useDrawArray;
|
||||
bool disableIndex;
|
||||
unsigned int maxIndexValue;
|
||||
|
||||
OptionsStruct() {
|
||||
enableWireframe = "";
|
||||
generateTangentSpace = false;
|
||||
tangentSpaceTextureUnit = 0;
|
||||
disableTriStrip = false;
|
||||
disableMergeTriStrip = false;
|
||||
disablePreTransform = false;
|
||||
disablePostTransform = false;
|
||||
triStripCacheSize = 16;
|
||||
triStripMinSize = 2;
|
||||
useDrawArray = false;
|
||||
disableIndex = false;
|
||||
maxIndexValue = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
ReaderWriterGLES()
|
||||
{
|
||||
supportsExtension("gles","OpenGL ES optimized format");
|
||||
|
||||
supportsOption("enableWireframe[=inline]","create a wireframe geometry for each triangles geometry. The wire geometry will be stored along the solid geometry if 'inline' is specified.");
|
||||
supportsOption("generateTangentSpace","Build tangent space to each geometry");
|
||||
supportsOption("tangentSpaceTextureUnit=<unit>","Specify on wich texture unit normal map is");
|
||||
supportsOption("triStripCacheSize=<int>","set the cache size when doing tristrip");
|
||||
supportsOption("triStripMinSize=<int>","set the minimum accepted length for a strip");
|
||||
supportsOption("disableMergeTriStrip","disable the merge of all tristrip into one");
|
||||
supportsOption("disableTriStrip","disable generation of tristrip");
|
||||
supportsOption("disablePreTransform","disable pre-transform of geometries after split");
|
||||
supportsOption("disablePostTransform","disable post-transform of geometries (called during geometry splitting)");
|
||||
supportsOption("useDrawArray","prefer drawArray instead of drawelement with split of geometry");
|
||||
supportsOption("disableIndex","Do not index the geometry");
|
||||
supportsOption("maxIndexValue=<int>","set the maximum index value (first index is 0)");
|
||||
}
|
||||
|
||||
virtual const char* className() const { return "GLES Optimizer"; }
|
||||
|
||||
virtual osg::Node* optimizeModel(const Node& node, const OptionsStruct& options) const
|
||||
{
|
||||
osg::Node* model = osg::clone(&node);
|
||||
|
||||
if (options.disableIndex) {
|
||||
UnIndexMeshVisitor unindex;
|
||||
model->accept(unindex);
|
||||
}
|
||||
else {
|
||||
OpenGLESGeometryOptimizer optimizer;
|
||||
|
||||
optimizer.setUseDrawArray(options.useDrawArray);
|
||||
optimizer.setTripStripCacheSize(options.triStripCacheSize);
|
||||
optimizer.setTripStripMinSize(options.triStripMinSize);
|
||||
optimizer.setDisableTriStrip(options.disableTriStrip);
|
||||
optimizer.setDisableMergeTriStrip(options.disableMergeTriStrip);
|
||||
optimizer.setDisablePreTransform(options.disablePreTransform);
|
||||
optimizer.setDisablePostTransform(options.disablePostTransform);
|
||||
optimizer.setWireframe(options.enableWireframe);
|
||||
if (options.generateTangentSpace) {
|
||||
optimizer.setTexCoordChannelForTangentSpace(options.tangentSpaceTextureUnit);
|
||||
}
|
||||
if(options.maxIndexValue) {
|
||||
optimizer.setMaxIndexValue(options.maxIndexValue);
|
||||
}
|
||||
|
||||
model = optimizer.optimize(*model);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
std::string ext = osgDB::getLowerCaseFileExtension(fileName);
|
||||
if( !acceptsExtension(ext) )
|
||||
return ReadResult::FILE_NOT_HANDLED;
|
||||
|
||||
OSG_INFO << "ReaderWriterGLES( \"" << fileName << "\" )" << std::endl;
|
||||
|
||||
// strip the pseudo-loader extension
|
||||
std::string realName = osgDB::getNameLessExtension( fileName );
|
||||
|
||||
if (realName.empty())
|
||||
return ReadResult::FILE_NOT_HANDLED;
|
||||
|
||||
// recursively load the subfile.
|
||||
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile( realName, options );
|
||||
if( !node )
|
||||
{
|
||||
// propagate the read failure upwards
|
||||
OSG_WARN << "Subfile \"" << realName << "\" could not be loaded" << std::endl;
|
||||
return ReadResult::FILE_NOT_HANDLED;
|
||||
}
|
||||
|
||||
OptionsStruct _options;
|
||||
_options = parseOptions(options);
|
||||
node = optimizeModel(*node, _options);
|
||||
|
||||
return node.release();
|
||||
}
|
||||
|
||||
virtual osgDB::ReaderWriter::WriteResult writeNode(const Node& node,
|
||||
const std::string& fileName,
|
||||
const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
std::string ext = osgDB::getLowerCaseFileExtension(fileName);
|
||||
if (!acceptsExtension(ext))
|
||||
return WriteResult::FILE_NOT_HANDLED;
|
||||
|
||||
std::string realFileName = osgDB::getNameLessExtension(fileName);
|
||||
if(realFileName.empty())
|
||||
return WriteResult::FILE_NOT_HANDLED;
|
||||
|
||||
// gles optimization
|
||||
OptionsStruct _options;
|
||||
_options = parseOptions(options);
|
||||
ref_ptr<Node> optimizedNode = optimizeModel(node, _options);
|
||||
|
||||
// forward writing to next plugin
|
||||
ref_ptr<ReaderWriter> rw = getReaderWriter(realFileName);
|
||||
if(rw) {
|
||||
return rw->writeNode(*optimizedNode, realFileName, options);
|
||||
}
|
||||
else {
|
||||
return WriteResult::ERROR_IN_WRITING_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
ReaderWriterGLES::OptionsStruct parseOptions(const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
OptionsStruct localOptions;
|
||||
|
||||
if (options)
|
||||
{
|
||||
osg::notify(NOTICE) << "options " << options->getOptionString() << std::endl;
|
||||
std::istringstream iss(options->getOptionString());
|
||||
std::string opt;
|
||||
while (iss >> opt)
|
||||
{
|
||||
// split opt into pre= and post=
|
||||
std::string pre_equals;
|
||||
std::string post_equals;
|
||||
|
||||
size_t found = opt.find("=");
|
||||
if(found!=std::string::npos)
|
||||
{
|
||||
pre_equals = opt.substr(0,found);
|
||||
post_equals = opt.substr(found+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pre_equals = opt;
|
||||
}
|
||||
|
||||
if (pre_equals == "enableWireframe")
|
||||
{
|
||||
if(post_equals == "inline") {
|
||||
localOptions.enableWireframe = "inline";
|
||||
}
|
||||
else {
|
||||
localOptions.enableWireframe = "outline";
|
||||
}
|
||||
}
|
||||
if (pre_equals == "useDrawArray")
|
||||
{
|
||||
localOptions.useDrawArray = true;
|
||||
}
|
||||
if (pre_equals == "disableMergeTriStrip")
|
||||
{
|
||||
localOptions.disableMergeTriStrip = true;
|
||||
}
|
||||
if (pre_equals == "disablePreTransform")
|
||||
{
|
||||
localOptions.disablePreTransform = true;
|
||||
}
|
||||
if (pre_equals == "disablePostTransform")
|
||||
{
|
||||
localOptions.disablePostTransform = true;
|
||||
}
|
||||
if (pre_equals == "disableTriStrip")
|
||||
{
|
||||
localOptions.disableTriStrip = true;
|
||||
}
|
||||
if (pre_equals == "generateTangentSpace")
|
||||
{
|
||||
localOptions.generateTangentSpace = true;
|
||||
}
|
||||
if (pre_equals == "disableIndex")
|
||||
{
|
||||
localOptions.disableIndex = true;
|
||||
}
|
||||
|
||||
if (post_equals.length() > 0) {
|
||||
if (pre_equals == "tangentSpaceTextureUnit") {
|
||||
localOptions.tangentSpaceTextureUnit = atoi(post_equals.c_str());
|
||||
}
|
||||
if (pre_equals == "triStripCacheSize") {
|
||||
localOptions.triStripCacheSize = atoi(post_equals.c_str());
|
||||
}
|
||||
if (pre_equals == "triStripMinSize") {
|
||||
localOptions.triStripMinSize = atoi(post_equals.c_str());
|
||||
}
|
||||
if (pre_equals == "maxIndexValue") {
|
||||
localOptions.maxIndexValue = atoi(post_equals.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return localOptions;
|
||||
}
|
||||
|
||||
protected:
|
||||
ReaderWriter* getReaderWriter(const std::string& fileName) const
|
||||
{
|
||||
ref_ptr<osgDB::Registry> registry = osgDB::Registry::instance();
|
||||
std::string ext = osgDB::getLowerCaseFileExtension(fileName);
|
||||
return registry->getReaderWriterForExtension(ext);
|
||||
}
|
||||
};
|
||||
|
||||
// now register with Registry to instantiate the above
|
||||
// reader/writer.
|
||||
REGISTER_OSGPLUGIN(gles, ReaderWriterGLES)
|
||||
32
src/osgPlugins/gles/StatLogger
Normal file
32
src/osgPlugins/gles/StatLogger
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef STAT_LOGGER
|
||||
#define STAT_LOGGER
|
||||
|
||||
#include <osg/Timer>
|
||||
#include <osg/Notify>
|
||||
|
||||
|
||||
class StatLogger
|
||||
{
|
||||
public:
|
||||
StatLogger(const std::string& label): _start(getTick()), _label(label) {}
|
||||
~StatLogger() {
|
||||
_stop = getTick();
|
||||
OSG_INFO << std::flush
|
||||
<< "Info: " << _label << " timing: " << getElapsedSeconds() << "s"
|
||||
<< std::endl << std::flush;
|
||||
}
|
||||
|
||||
protected:
|
||||
osg::Timer_t _start, _stop;
|
||||
std::string _label;
|
||||
|
||||
inline osg::Timer_t getTick() const {
|
||||
return osg::Timer::instance()->tick();
|
||||
}
|
||||
|
||||
inline double getElapsedSeconds() const {
|
||||
return osg::Timer::instance()->delta_s(_start, _stop);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
106
src/osgPlugins/gles/TangentSpaceVisitor
Normal file
106
src/osgPlugins/gles/TangentSpaceVisitor
Normal file
@@ -0,0 +1,106 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TANGENT_SPACE_VISITOR
|
||||
#define TANGENT_SPACE_VISITOR
|
||||
|
||||
#define TANGENT_ATTRIBUTE_INDEX 20
|
||||
|
||||
#include <osgUtil/TangentSpaceGenerator>
|
||||
#include <osg/ValueObject> // {get,set}UserValue
|
||||
#include <osg/Array>
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
|
||||
|
||||
// we will store only tangent and rebuilt tangent2 in the vertex shader
|
||||
// http://www.terathon.com/code/tangent.html
|
||||
|
||||
class TangentSpaceVisitor : public GeometryUniqueVisitor
|
||||
{
|
||||
public:
|
||||
TangentSpaceVisitor(int textureUnit = 0):
|
||||
GeometryUniqueVisitor("TangentSpaceVisitor"),
|
||||
_textureUnit(textureUnit)
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geom) {
|
||||
if (!geom.getTexCoordArray(_textureUnit)){
|
||||
int texUnit = 0;
|
||||
bool found = false;
|
||||
while(texUnit < 32){
|
||||
if (_textureUnit != texUnit && geom.getTexCoordArray(texUnit)){
|
||||
_textureUnit = texUnit;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
texUnit++;
|
||||
}
|
||||
if (!found)
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osgUtil::TangentSpaceGenerator> generator = new osgUtil::TangentSpaceGenerator;
|
||||
generator->generate(&geom, _textureUnit);
|
||||
|
||||
// keep original normal array
|
||||
if (!geom.getNormalArray()) {
|
||||
if (generator->getNormalArray()) {
|
||||
osg::Vec3Array* vec3Normals = new osg::Vec3Array();
|
||||
osg::Vec4Array* vec4Normals = generator->getNormalArray();
|
||||
for (unsigned int i = 0; i < vec4Normals->size(); i++) {
|
||||
osg::Vec3 n = osg::Vec3((*vec4Normals)[i][0],
|
||||
(*vec4Normals)[i][1],
|
||||
(*vec4Normals)[i][2]);
|
||||
vec3Normals->push_back(n);
|
||||
}
|
||||
geom.setNormalArray(vec3Normals, osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
}
|
||||
|
||||
if (generator->getTangentArray()) {
|
||||
osg::Vec4Array* normal = generator->getNormalArray();
|
||||
osg::Vec4Array* tangent = generator->getTangentArray();
|
||||
osg::Vec4Array* tangent2 = generator->getBinormalArray();
|
||||
osg::Vec4Array* finalTangent = dynamic_cast<osg::Vec4Array*>(generator->getTangentArray()
|
||||
->clone(osg::CopyOp::DEEP_COPY_ALL));
|
||||
for (unsigned int i = 0; i < tangent->size(); i++) {
|
||||
osg::Vec3 n = osg::Vec3((*normal)[i][0],
|
||||
(*normal)[i][1],
|
||||
(*normal)[i][2]);
|
||||
osg::Vec3 t = osg::Vec3((*tangent)[i][0],
|
||||
(*tangent)[i][1],
|
||||
(*tangent)[i][2]);
|
||||
osg::Vec3 t2 = osg::Vec3((*tangent2)[i][0],
|
||||
(*tangent2)[i][1],
|
||||
(*tangent2)[i][2]);
|
||||
|
||||
// Gram-Schmidt orthogonalize
|
||||
osg::Vec3 t3 = (t - n * (n * t));
|
||||
t3.normalize();
|
||||
(*finalTangent)[i] = osg::Vec4(t3, 0.0);
|
||||
|
||||
// Calculate handedness
|
||||
(*finalTangent)[i][3] = (((n ^ t) * t2) < 0.0) ? -1.0 : 1.0;
|
||||
// The bitangent vector B is then given by B = (N × T) · Tw
|
||||
}
|
||||
finalTangent->setUserValue("tangent", true);
|
||||
geom.setVertexAttribArray(geom.getNumVertexAttribArrays(), finalTangent, osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
setProcessed(&geom);
|
||||
}
|
||||
|
||||
protected:
|
||||
int _textureUnit;
|
||||
};
|
||||
|
||||
#endif
|
||||
257
src/osgPlugins/gles/TriangleLinePointIndexFunctor
Normal file
257
src/osgPlugins/gles/TriangleLinePointIndexFunctor
Normal file
@@ -0,0 +1,257 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TRIANGLE_LINE_POINT_INDEX_FUNCTOR
|
||||
#define TRIANGLE_LINE_POINT_INDEX_FUNCTOR
|
||||
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/Array>
|
||||
|
||||
|
||||
template<class T>
|
||||
class TriangleLinePointIndexFunctor : public osg::PrimitiveIndexFunctor, public T
|
||||
{
|
||||
public:
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2*)
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2d*)
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3d* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4d* )
|
||||
{}
|
||||
|
||||
virtual void begin(GLenum mode) {
|
||||
_modeCache = mode;
|
||||
_indexCache.clear();
|
||||
}
|
||||
|
||||
virtual void vertex(unsigned int vert) {
|
||||
_indexCache.push_back(vert);
|
||||
}
|
||||
|
||||
virtual void end() {
|
||||
if (!_indexCache.empty()) {
|
||||
drawElements(_modeCache,_indexCache.size(),&_indexCache.front());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_TRIANGLES):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 2 ; i < count ; i += 3, pos += 3) {
|
||||
this->operator()(pos, pos + 1, pos + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_TRIANGLE_STRIP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) {
|
||||
if ((i%2)) this->operator()(pos, pos + 2, pos + 1);
|
||||
else this->operator()(pos, pos + 1, pos + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUADS):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 3 ; i < count ; i += 4, pos += 4) {
|
||||
this->operator()(pos,pos + 1, pos + 2);
|
||||
this->operator()(pos,pos + 2, pos + 3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUAD_STRIP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 3 ; i < count ; i += 2, pos += 2) {
|
||||
this->operator()(pos, pos + 1,pos + 2);
|
||||
this->operator()(pos + 1,pos + 3,pos + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
|
||||
case(GL_TRIANGLE_FAN):
|
||||
{
|
||||
unsigned int pos = first + 1;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) {
|
||||
this->operator()(first, pos, pos + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINES):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 0 ; i < count ; i += 2, pos += 2) {
|
||||
this->operator()(pos, pos + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINE_STRIP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
|
||||
this->operator()(pos, pos + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINE_LOOP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
|
||||
this->operator()(pos, pos + 1);
|
||||
}
|
||||
this->operator()(pos, first);
|
||||
break;
|
||||
}
|
||||
case(GL_POINTS):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 0 ; i < count ; ++ i) {
|
||||
this->operator()(pos + i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
void drawElements(GLenum mode, GLsizei count, const I* indices)
|
||||
{
|
||||
typedef I Index;
|
||||
typedef const I* IndexPointer;
|
||||
|
||||
if (indices == 0 || count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_TRIANGLES):
|
||||
{
|
||||
IndexPointer ilast = &indices[count];
|
||||
for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 3) {
|
||||
this->operator()(*iptr, *(iptr + 1), *(iptr + 2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_TRIANGLE_STRIP):
|
||||
{
|
||||
IndexPointer iptr = indices;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
|
||||
if ((i%2)) this->operator()(*(iptr), *(iptr + 2), *(iptr + 1));
|
||||
else this->operator()(*(iptr), *(iptr + 1), *(iptr + 2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUADS):
|
||||
{
|
||||
IndexPointer iptr = indices;
|
||||
for(GLsizei i = 3 ; i < count ; i += 4, iptr += 4) {
|
||||
this->operator()(*(iptr), *(iptr + 1), *(iptr + 2));
|
||||
this->operator()(*(iptr), *(iptr + 2), *(iptr + 3));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUAD_STRIP):
|
||||
{
|
||||
IndexPointer iptr = indices;
|
||||
for(GLsizei i = 3 ; i < count ; i += 2, iptr += 2) {
|
||||
this->operator()(*(iptr), *(iptr + 1), *(iptr + 2));
|
||||
this->operator()(*(iptr + 1), *(iptr + 3), *(iptr + 2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
|
||||
case(GL_TRIANGLE_FAN):
|
||||
{
|
||||
IndexPointer iptr = indices;
|
||||
Index first = *iptr;
|
||||
++iptr;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
|
||||
this->operator()(first, *(iptr), *(iptr + 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINES):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 0 ; i < count ; i += 2, iptr += 2) {
|
||||
this->operator()(*iptr, *(iptr + 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINE_STRIP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
|
||||
this->operator()(*iptr, *(iptr + 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINE_LOOP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
I first = *iptr;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
|
||||
this->operator()(*iptr, *(iptr + 1));
|
||||
}
|
||||
this->operator()(*iptr, first);
|
||||
break;
|
||||
}
|
||||
case GL_POINTS:
|
||||
{
|
||||
IndexPointer ilast = &indices[count];
|
||||
for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 1) {
|
||||
this->operator()(*iptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) {
|
||||
drawElements<GLubyte>(mode, count, indices);
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) {
|
||||
drawElements<GLushort>(mode, count, indices);
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) {
|
||||
drawElements<GLuint>(mode, count, indices);
|
||||
}
|
||||
|
||||
|
||||
GLenum _modeCache;
|
||||
std::vector<GLuint> _indexCache;
|
||||
std::vector<unsigned int> _remap;
|
||||
};
|
||||
|
||||
#endif
|
||||
35
src/osgPlugins/gles/TriangleStripVisitor
Normal file
35
src/osgPlugins/gles/TriangleStripVisitor
Normal file
@@ -0,0 +1,35 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TRIANGLE_STRIP_VISITOR_H
|
||||
#define TRIANGLE_STRIP_VISITOR_H
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
|
||||
class TriangleStripVisitor : public GeometryUniqueVisitor {
|
||||
public:
|
||||
TriangleStripVisitor(unsigned int cacheSize, unsigned int minSize, bool merge):
|
||||
GeometryUniqueVisitor("TriangleStripVisitor"),
|
||||
_cacheSize(cacheSize), _minSize(minSize), _merge(merge)
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry);
|
||||
|
||||
protected:
|
||||
void mergeTrianglesStrip(osg::Geometry& geometry);
|
||||
|
||||
unsigned int _cacheSize;
|
||||
unsigned int _minSize;
|
||||
bool _merge;
|
||||
};
|
||||
|
||||
#endif
|
||||
94
src/osgPlugins/gles/TriangleStripVisitor.cpp
Normal file
94
src/osgPlugins/gles/TriangleStripVisitor.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <osgUtil/TriStripVisitor>
|
||||
#include "TriangleStripVisitor"
|
||||
|
||||
|
||||
void TriangleStripVisitor::apply(osg::Geometry& geometry) {
|
||||
osgUtil::TriStripVisitor tristrip;
|
||||
tristrip.setCacheSize(_cacheSize);
|
||||
tristrip.setMinStripSize(_minSize);
|
||||
tristrip.stripify(geometry);
|
||||
|
||||
// merge stritrip to one using degenerated triangles as glue
|
||||
if (_merge) {
|
||||
mergeTrianglesStrip(geometry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TriangleStripVisitor::mergeTrianglesStrip(osg::Geometry& geometry)
|
||||
{
|
||||
int nbtristrip = 0;
|
||||
int nbtristripVertexes = 0;
|
||||
|
||||
for (unsigned int i = 0; i < geometry.getNumPrimitiveSets(); i++) {
|
||||
osg::PrimitiveSet* ps = geometry.getPrimitiveSet(i);
|
||||
osg::DrawElements* de = ps->getDrawElements();
|
||||
if (de && de->getMode() == osg::PrimitiveSet::TRIANGLE_STRIP) {
|
||||
nbtristrip++;
|
||||
nbtristripVertexes += de->getNumIndices();
|
||||
}
|
||||
}
|
||||
|
||||
if (nbtristrip > 0) {
|
||||
osg::notify(osg::NOTICE) << "found " << nbtristrip << " tristrip, "
|
||||
<< "total vertexes " << nbtristripVertexes
|
||||
<< " should result to " << nbtristripVertexes + nbtristrip*2
|
||||
<< " after connection" << std::endl;
|
||||
|
||||
osg::DrawElementsUShort* ndw = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP);
|
||||
for (unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) {
|
||||
osg::PrimitiveSet* ps = geometry.getPrimitiveSet(i);
|
||||
if (ps && ps->getMode() == osg::PrimitiveSet::TRIANGLE_STRIP) {
|
||||
osg::DrawElements* de = ps->getDrawElements();
|
||||
if (de) {
|
||||
// if connection needed insert degenerate triangles
|
||||
if (ndw->getNumIndices() != 0 && ndw->back() != de->getElement(0)) {
|
||||
// duplicate last vertex
|
||||
ndw->addElement(ndw->back());
|
||||
// insert first vertex of next strip
|
||||
ndw->addElement(de->getElement(0));
|
||||
}
|
||||
|
||||
if (ndw->getNumIndices() % 2 != 0 ) {
|
||||
// add a dummy vertex to reverse the strip
|
||||
ndw->addElement(de->getElement(0));
|
||||
}
|
||||
|
||||
for (unsigned int j = 0; j < de->getNumIndices(); j++) {
|
||||
ndw->addElement(de->getElement(j));
|
||||
}
|
||||
}
|
||||
else if (ps->getType() == osg::PrimitiveSet::DrawArraysPrimitiveType) {
|
||||
// trip strip can generate drawarray of 5 elements we want to merge them too
|
||||
osg::DrawArrays* da = dynamic_cast<osg::DrawArrays*> (ps);
|
||||
// if connection needed insert degenerate triangles
|
||||
if (ndw->getNumIndices() != 0 && ndw->back() != da->getFirst()) {
|
||||
// duplicate last vertex
|
||||
ndw->addElement(ndw->back());
|
||||
// insert first vertex of next strip
|
||||
ndw->addElement(da->getFirst());
|
||||
}
|
||||
|
||||
if (ndw->getNumIndices() % 2 != 0 ) {
|
||||
// add a dummy vertex to reverse the strip
|
||||
ndw->addElement(da->getFirst());
|
||||
}
|
||||
|
||||
for (unsigned int j = 0; j < da->getNumIndices(); j++) {
|
||||
ndw->addElement(da->getFirst() + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = geometry.getNumPrimitiveSets() - 1 ; i >= 0 ; -- i) {
|
||||
osg::PrimitiveSet* ps = geometry.getPrimitiveSet(i);
|
||||
// remove null primitive sets and all primitives that have been merged
|
||||
// (i.e. all TRIANGLE_STRIP DrawElements and DrawArrays)
|
||||
if (!ps || (ps && ps->getMode() == osg::PrimitiveSet::TRIANGLE_STRIP)) {
|
||||
geometry.getPrimitiveSetList().erase(geometry.getPrimitiveSetList().begin() + i);
|
||||
}
|
||||
}
|
||||
geometry.getPrimitiveSetList().insert(geometry.getPrimitiveSetList().begin(), ndw);
|
||||
}
|
||||
}
|
||||
28
src/osgPlugins/gles/UnIndexMeshVisitor
Normal file
28
src/osgPlugins/gles/UnIndexMeshVisitor
Normal file
@@ -0,0 +1,28 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef UNINDEX_MESH_VISITOR
|
||||
#define UNINDEX_MESH_VISITOR
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
|
||||
|
||||
class UnIndexMeshVisitor : public GeometryUniqueVisitor
|
||||
{
|
||||
public:
|
||||
UnIndexMeshVisitor(): GeometryUniqueVisitor("UnIndexMeshVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geom);
|
||||
};
|
||||
|
||||
#endif
|
||||
128
src/osgPlugins/gles/UnIndexMeshVisitor.cpp
Normal file
128
src/osgPlugins/gles/UnIndexMeshVisitor.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
|
||||
#include "UnIndexMeshVisitor"
|
||||
#include "GeometryArray"
|
||||
#include "PrimitiveIndexors"
|
||||
|
||||
typedef std::vector<unsigned int> IndexList;
|
||||
// this help works only for indexed primitive to unindex it
|
||||
|
||||
|
||||
void UnIndexMeshVisitor::apply(osg::Geometry& geom)
|
||||
{
|
||||
// no point optimizing if we don't have enough vertices.
|
||||
if (!geom.getVertexArray()) return;
|
||||
|
||||
// check for the existence of surface primitives
|
||||
unsigned int numIndexedPrimitives = 0;
|
||||
osg::Geometry::PrimitiveSetList& primitives = geom.getPrimitiveSetList();
|
||||
osg::Geometry::PrimitiveSetList::iterator itr;
|
||||
for(itr=primitives.begin();
|
||||
itr!=primitives.end();
|
||||
++itr)
|
||||
{
|
||||
osg::PrimitiveSet::Type type = (*itr)->getType();
|
||||
if ((type == osg::PrimitiveSet::DrawElementsUBytePrimitiveType
|
||||
|| type == osg::PrimitiveSet::DrawElementsUShortPrimitiveType
|
||||
|| type == osg::PrimitiveSet::DrawElementsUIntPrimitiveType))
|
||||
numIndexedPrimitives++;
|
||||
}
|
||||
|
||||
// no polygons or no indexed primitive, nothing to do
|
||||
if (!numIndexedPrimitives) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we dont manage lines
|
||||
|
||||
GeometryArrayList arraySrc(geom);
|
||||
GeometryArrayList arrayList = arraySrc.cloneType();
|
||||
|
||||
osg::Geometry::PrimitiveSetList newPrimitives;
|
||||
|
||||
for(itr=primitives.begin();
|
||||
itr!=primitives.end();
|
||||
++itr)
|
||||
{
|
||||
osg::PrimitiveSet::Mode mode = (osg::PrimitiveSet::Mode)(*itr)->getMode();
|
||||
|
||||
switch(mode) {
|
||||
|
||||
// manage triangles
|
||||
case(osg::PrimitiveSet::TRIANGLES):
|
||||
case(osg::PrimitiveSet::TRIANGLE_STRIP):
|
||||
case(osg::PrimitiveSet::TRIANGLE_FAN):
|
||||
case(osg::PrimitiveSet::QUADS):
|
||||
case(osg::PrimitiveSet::QUAD_STRIP):
|
||||
case(osg::PrimitiveSet::POLYGON):
|
||||
{
|
||||
// for each geometry list indexes of vertexes
|
||||
// to makes triangles
|
||||
TriangleIndexor triangleIndexList;
|
||||
(*itr)->accept(triangleIndexList);
|
||||
|
||||
unsigned int index = arrayList.size();
|
||||
|
||||
// now copy each vertex to new array, like a draw array
|
||||
arraySrc.append(triangleIndexList._indices, arrayList);
|
||||
|
||||
newPrimitives.push_back(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
|
||||
index,
|
||||
triangleIndexList._indices.size()));
|
||||
}
|
||||
break;
|
||||
|
||||
// manage lines
|
||||
case(osg::PrimitiveSet::LINES):
|
||||
case(osg::PrimitiveSet::LINE_STRIP):
|
||||
case(osg::PrimitiveSet::LINE_LOOP):
|
||||
{
|
||||
EdgeIndexor edgesIndexList;
|
||||
(*itr)->accept(edgesIndexList);
|
||||
|
||||
unsigned int index = arrayList.size();
|
||||
|
||||
// now copy each vertex to new array, like a draw array
|
||||
arraySrc.append(edgesIndexList._indices, arrayList);
|
||||
|
||||
newPrimitives.push_back(new osg::DrawArrays(osg::PrimitiveSet::LINES,
|
||||
index,
|
||||
edgesIndexList._indices.size()));
|
||||
}
|
||||
break;
|
||||
case(osg::PrimitiveSet::POINTS):
|
||||
{
|
||||
PointIndexor pointsIndexList;
|
||||
(*itr)->accept(pointsIndexList);
|
||||
|
||||
unsigned int index = arrayList.size();
|
||||
|
||||
// now copy each vertex to new array, like a draw array
|
||||
arraySrc.append(pointsIndexList._indices, arrayList);
|
||||
newPrimitives.push_back(new osg::DrawArrays(osg::PrimitiveSet::POINTS,
|
||||
index,
|
||||
pointsIndexList._indices.size()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
arrayList.setToGeometry(geom);
|
||||
geom.setPrimitiveSetList(newPrimitives);
|
||||
setProcessed(&geom);
|
||||
}
|
||||
84
src/osgPlugins/gles/WireframeVisitor
Normal file
84
src/osgPlugins/gles/WireframeVisitor
Normal file
@@ -0,0 +1,84 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WIREFRAME_VISITOR
|
||||
#define WIREFRAME_VISITOR
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <osg/ValueObject>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
#include "PrimitiveIndexors"
|
||||
|
||||
|
||||
class WireframeVisitor : public GeometryUniqueVisitor
|
||||
{
|
||||
public:
|
||||
WireframeVisitor(bool inlined=false):
|
||||
GeometryUniqueVisitor("WireframeVisitor"),
|
||||
_inlined(inlined)
|
||||
{}
|
||||
|
||||
void apply(osg::Node& node) {
|
||||
handleStateSet(node);
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
void apply(osg::Geode& geode) {
|
||||
handleStateSet(geode);
|
||||
for (unsigned int i = 0; i < geode.getNumDrawables(); i++) {
|
||||
apply(*geode.getDrawable(i));
|
||||
}
|
||||
}
|
||||
|
||||
void apply(osg::Drawable& drawable) {
|
||||
osg::Geometry* geometry = drawable.asGeometry();
|
||||
if (!geometry) {
|
||||
return;
|
||||
}
|
||||
apply(*geometry);
|
||||
}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
if(_processed.find(&geometry) == _processed.end()) {
|
||||
const unsigned int nbSourcePrimitives = geometry.getNumPrimitiveSets();
|
||||
for(unsigned int i = 0 ; i < nbSourcePrimitives ; ++ i) {
|
||||
osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
|
||||
EdgeIndexor edges;
|
||||
primitive->accept(edges);
|
||||
if(!edges._indices.empty()) {
|
||||
osg::DrawElementsUInt* wireframe = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES,
|
||||
edges._indices.begin(),
|
||||
edges._indices.end());
|
||||
wireframe->setUserValue("wireframe", true);
|
||||
geometry.getPrimitiveSetList().push_back(wireframe);
|
||||
}
|
||||
}
|
||||
|
||||
_processed.insert(&geometry);
|
||||
}
|
||||
}
|
||||
|
||||
void handleStateSet(osg::Node& node) {
|
||||
if(!_inlined) {
|
||||
node.setStateSet(0);
|
||||
}
|
||||
}
|
||||
|
||||
std::set<osg::Geometry*> _processed;
|
||||
bool _inlined;
|
||||
};
|
||||
|
||||
#endif
|
||||
346
src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp
Normal file
346
src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
|
||||
// Optimization" algorithm as described here:
|
||||
// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
|
||||
//
|
||||
// This code was authored and released into the public domain by
|
||||
// Adrian Stone (stone@gameangst.com).
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
// SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Forsyth
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
// OptimizeFaces
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parameters:
|
||||
// indexList
|
||||
// input index list
|
||||
// indexCount
|
||||
// the number of indices in the list
|
||||
// vertexCount
|
||||
// the largest index value in indexList
|
||||
// newIndexList
|
||||
// a pointer to a preallocated buffer the same size as indexList to
|
||||
// hold the optimized index list
|
||||
// lruCacheSize
|
||||
// the size of the simulated post-transform cache (max:64)
|
||||
//-----------------------------------------------------------------------------
|
||||
void OptimizeFaces(const unsigned int* indexList, unsigned int indexCount, unsigned int vertexCount, unsigned int* newIndexList, unsigned int lruCacheSize);
|
||||
|
||||
namespace
|
||||
{
|
||||
// code for computing vertex score was taken, as much as possible
|
||||
// directly from the original publication.
|
||||
float ComputeVertexCacheScore(int cachePosition, int vertexCacheSize)
|
||||
{
|
||||
const float FindVertexScore_CacheDecayPower = 1.5f;
|
||||
const float FindVertexScore_LastTriScore = 0.75f;
|
||||
|
||||
float score = 0.0f;
|
||||
if ( cachePosition < 0 )
|
||||
{
|
||||
// Vertex is not in FIFO cache - no score.
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( cachePosition < 3 )
|
||||
{
|
||||
// This vertex was used in the last triangle,
|
||||
// so it has a fixed score, whichever of the three
|
||||
// it's in. Otherwise, you can get very different
|
||||
// answers depending on whether you add
|
||||
// the triangle 1,2,3 or 3,1,2 - which is silly.
|
||||
score = FindVertexScore_LastTriScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert ( cachePosition < vertexCacheSize );
|
||||
// Points for being high in the cache.
|
||||
const float scaler = 1.0f / ( vertexCacheSize - 3 );
|
||||
score = 1.0f - ( cachePosition - 3 ) * scaler;
|
||||
score = powf ( score, FindVertexScore_CacheDecayPower );
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
float ComputeVertexValenceScore(unsigned int numActiveFaces)
|
||||
{
|
||||
const float FindVertexScore_ValenceBoostScale = 2.0f;
|
||||
const float FindVertexScore_ValenceBoostPower = 0.5f;
|
||||
|
||||
float score = 0.f;
|
||||
|
||||
// Bonus points for having a low number of tris still to
|
||||
// use the vert, so we get rid of lone verts quickly.
|
||||
float valenceBoost = powf ( static_cast<float>(numActiveFaces),
|
||||
-FindVertexScore_ValenceBoostPower );
|
||||
score += FindVertexScore_ValenceBoostScale * valenceBoost;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
const int kMaxVertexCacheSize = 64;
|
||||
const unsigned int kMaxPrecomputedVertexValenceScores = 64;
|
||||
float s_vertexCacheScores[kMaxVertexCacheSize+1][kMaxVertexCacheSize];
|
||||
float s_vertexValenceScores[kMaxPrecomputedVertexValenceScores];
|
||||
|
||||
bool ComputeVertexScores()
|
||||
{
|
||||
for (int cacheSize=0; cacheSize<=kMaxVertexCacheSize; ++cacheSize)
|
||||
{
|
||||
for (int cachePos=0; cachePos<cacheSize; ++cachePos)
|
||||
{
|
||||
s_vertexCacheScores[cacheSize][cachePos] = ComputeVertexCacheScore(cachePos, cacheSize);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int valence=0; valence<kMaxPrecomputedVertexValenceScores; ++valence)
|
||||
{
|
||||
s_vertexValenceScores[valence] = ComputeVertexValenceScore(valence);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool s_vertexScoresComputed = ComputeVertexScores();
|
||||
|
||||
// inline float FindVertexCacheScore(unsigned int cachePosition, unsigned int maxSizeVertexCache)
|
||||
// {
|
||||
// return s_vertexCacheScores[maxSizeVertexCache][cachePosition];
|
||||
// }
|
||||
|
||||
// inline float FindVertexValenceScore(unsigned int numActiveTris)
|
||||
// {
|
||||
// return s_vertexValenceScores[numActiveTris];
|
||||
// }
|
||||
|
||||
float FindVertexScore(unsigned int numActiveFaces, unsigned int cachePosition, unsigned int vertexCacheSize)
|
||||
{
|
||||
assert(s_vertexScoresComputed);
|
||||
|
||||
if ( numActiveFaces == 0 )
|
||||
{
|
||||
// No tri needs this vertex!
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
float score = 0.f;
|
||||
if (cachePosition < vertexCacheSize)
|
||||
{
|
||||
score += s_vertexCacheScores[vertexCacheSize][cachePosition];
|
||||
}
|
||||
|
||||
if (numActiveFaces < kMaxPrecomputedVertexValenceScores)
|
||||
{
|
||||
score += s_vertexValenceScores[numActiveFaces];
|
||||
}
|
||||
else
|
||||
{
|
||||
score += ComputeVertexValenceScore(numActiveFaces);
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
struct OptimizeVertexData
|
||||
{
|
||||
float score;
|
||||
unsigned int activeFaceListStart;
|
||||
unsigned int activeFaceListSize;
|
||||
unsigned int cachePos0;
|
||||
unsigned int cachePos1;
|
||||
OptimizeVertexData() : score(0.f), activeFaceListStart(0), activeFaceListSize(0), cachePos0(0), cachePos1(0) { }
|
||||
};
|
||||
}
|
||||
|
||||
void OptimizeFaces(const unsigned int* indexList, unsigned int indexCount, unsigned int vertexCount, unsigned int* newIndexList, unsigned int lruCacheSize)
|
||||
{
|
||||
std::vector<OptimizeVertexData> vertexDataList;
|
||||
vertexDataList.resize(vertexCount);
|
||||
|
||||
// compute face count per vertex
|
||||
for (unsigned int i=0; i<indexCount; ++i)
|
||||
{
|
||||
unsigned int index = indexList[i];
|
||||
assert(index < vertexCount);
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
vertexData.activeFaceListSize++;
|
||||
}
|
||||
|
||||
std::vector<unsigned int> activeFaceList;
|
||||
|
||||
const unsigned int kEvictedCacheIndex = std::numeric_limits<unsigned int>::max();
|
||||
|
||||
{
|
||||
// allocate face list per vertex
|
||||
unsigned int curActiveFaceListPos = 0;
|
||||
for (unsigned int i=0; i<vertexCount; ++i)
|
||||
{
|
||||
OptimizeVertexData& vertexData = vertexDataList[i];
|
||||
vertexData.cachePos0 = kEvictedCacheIndex;
|
||||
vertexData.cachePos1 = kEvictedCacheIndex;
|
||||
vertexData.activeFaceListStart = curActiveFaceListPos;
|
||||
curActiveFaceListPos += vertexData.activeFaceListSize;
|
||||
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos0, lruCacheSize);
|
||||
vertexData.activeFaceListSize = 0;
|
||||
}
|
||||
activeFaceList.resize(curActiveFaceListPos);
|
||||
}
|
||||
|
||||
// fill out face list per vertex
|
||||
for (unsigned int i=0; i<indexCount; i+=3)
|
||||
{
|
||||
for (unsigned int j=0; j<3; ++j)
|
||||
{
|
||||
unsigned int index = indexList[i+j];
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize] = i;
|
||||
vertexData.activeFaceListSize++;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<unsigned int> processedFaceList;
|
||||
processedFaceList.resize(indexCount);
|
||||
|
||||
unsigned int vertexCacheBuffer[(kMaxVertexCacheSize+3)*2];
|
||||
unsigned int* cache0 = vertexCacheBuffer;
|
||||
unsigned int* cache1 = vertexCacheBuffer+(kMaxVertexCacheSize+3);
|
||||
unsigned int entriesInCache0 = 0;
|
||||
|
||||
unsigned int bestFace = 0;
|
||||
float bestScore = -1.f;
|
||||
|
||||
const float maxValenceScore = FindVertexScore(1, kEvictedCacheIndex, lruCacheSize) * 3.f;
|
||||
|
||||
for (unsigned int i = 0; i < indexCount; i += 3)
|
||||
{
|
||||
if (bestScore < 0.f)
|
||||
{
|
||||
// no verts in the cache are used by any unprocessed faces so
|
||||
// search all unprocessed faces for a new starting point
|
||||
for (unsigned int j = 0; j < indexCount; j += 3)
|
||||
{
|
||||
if (processedFaceList[j] == 0)
|
||||
{
|
||||
unsigned int face = j;
|
||||
float faceScore = 0.f;
|
||||
for (unsigned int k=0; k<3; ++k)
|
||||
{
|
||||
unsigned int index = indexList[face+k];
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
assert(vertexData.activeFaceListSize > 0);
|
||||
assert(vertexData.cachePos0 >= lruCacheSize);
|
||||
faceScore += vertexData.score;
|
||||
}
|
||||
|
||||
if (faceScore > bestScore)
|
||||
{
|
||||
bestScore = faceScore;
|
||||
bestFace = face;
|
||||
|
||||
assert(bestScore <= maxValenceScore);
|
||||
if (bestScore >= maxValenceScore)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(bestScore >= 0.f);
|
||||
}
|
||||
|
||||
processedFaceList[bestFace] = 1;
|
||||
unsigned int entriesInCache1 = 0;
|
||||
|
||||
// add bestFace to LRU cache and to newIndexList
|
||||
for (unsigned int v = 0; v < 3; ++v)
|
||||
{
|
||||
unsigned int index = indexList[bestFace+v];
|
||||
newIndexList[i+v] = index;
|
||||
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
|
||||
if (vertexData.cachePos1 >= entriesInCache1)
|
||||
{
|
||||
vertexData.cachePos1 = entriesInCache1;
|
||||
cache1[entriesInCache1++] = index;
|
||||
|
||||
if (vertexData.activeFaceListSize == 1)
|
||||
{
|
||||
--vertexData.activeFaceListSize;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
assert(vertexData.activeFaceListSize > 0);
|
||||
unsigned int* begin = &activeFaceList[vertexData.activeFaceListStart];
|
||||
unsigned int* end = &activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize];
|
||||
unsigned int* it = std::find(begin, end, bestFace);
|
||||
assert(it != end);
|
||||
std::swap(*it, *(end-1));
|
||||
--vertexData.activeFaceListSize;
|
||||
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
|
||||
|
||||
}
|
||||
|
||||
// move the rest of the old verts in the cache down and compute their new scores
|
||||
for (unsigned int c0 = 0; c0 < entriesInCache0; ++c0)
|
||||
{
|
||||
unsigned int index = cache0[c0];
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
|
||||
if (vertexData.cachePos1 >= entriesInCache1)
|
||||
{
|
||||
vertexData.cachePos1 = entriesInCache1;
|
||||
cache1[entriesInCache1++] = index;
|
||||
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
|
||||
}
|
||||
}
|
||||
|
||||
// find the best scoring triangle in the current cache (including up to 3 that were just evicted)
|
||||
bestScore = -1.f;
|
||||
for (unsigned int c1 = 0; c1 < entriesInCache1; ++c1)
|
||||
{
|
||||
unsigned int index = cache1[c1];
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
vertexData.cachePos0 = vertexData.cachePos1;
|
||||
vertexData.cachePos1 = kEvictedCacheIndex;
|
||||
for (unsigned int j=0; j<vertexData.activeFaceListSize; ++j)
|
||||
{
|
||||
unsigned int face = activeFaceList[vertexData.activeFaceListStart+j];
|
||||
float faceScore = 0.f;
|
||||
for (unsigned int v=0; v<3; v++)
|
||||
{
|
||||
unsigned int faceIndex = indexList[face+v];
|
||||
OptimizeVertexData& faceVertexData = vertexDataList[faceIndex];
|
||||
faceScore += faceVertexData.score;
|
||||
}
|
||||
if (faceScore > bestScore)
|
||||
{
|
||||
bestScore = faceScore;
|
||||
bestFace = face;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(cache0, cache1);
|
||||
entriesInCache0 = std::min(entriesInCache1, lruCacheSize);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Forsyth
|
||||
46
src/osgPlugins/gles/forsythtriangleorderoptimizer.h
Normal file
46
src/osgPlugins/gles/forsythtriangleorderoptimizer.h
Normal file
@@ -0,0 +1,46 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
|
||||
// Optimization" algorithm as described here:
|
||||
// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
|
||||
//
|
||||
// This code was authored and released into the public domain by
|
||||
// Adrian Stone (stone@gameangst.com).
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
// SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef __FORSYTH_TRIANGLE_REORDER__
|
||||
#define __FORSYTH_TRIANGLE_REORDER__
|
||||
|
||||
namespace Forsyth
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
// OptimizeFaces
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parameters:
|
||||
// indexList
|
||||
// input index list
|
||||
// indexCount
|
||||
// the number of indices in the list
|
||||
// vertexCount
|
||||
// the largest index value in indexList
|
||||
// newIndexList
|
||||
// a pointer to a preallocated buffer the same size as indexList to
|
||||
// hold the optimized index list
|
||||
// lruCacheSize
|
||||
// the size of the simulated post-transform cache (max:64)
|
||||
//-----------------------------------------------------------------------------
|
||||
void OptimizeFaces(const unsigned int* indexList,
|
||||
unsigned int indexCount,
|
||||
unsigned int vertexCount,
|
||||
unsigned int* newIndexList,
|
||||
unsigned int lruCacheSize);
|
||||
|
||||
} // namespace Forsyth
|
||||
|
||||
#endif // __FORSYTH_TRIANGLE_REORDER__
|
||||
574
src/osgPlugins/gles/glesUtil
Normal file
574
src/osgPlugins/gles/glesUtil
Normal file
@@ -0,0 +1,574 @@
|
||||
#ifndef GLES_UTIL
|
||||
#define GLES_UTIL
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <osg/Array>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
#include <osg/TriangleIndexFunctor>
|
||||
|
||||
#include "TriangleLinePointIndexFunctor"
|
||||
#include "StatLogger"
|
||||
#include "forsythtriangleorderoptimizer.h"
|
||||
|
||||
|
||||
namespace glesUtil {
|
||||
using namespace std;
|
||||
using namespace osg;
|
||||
typedef std::vector<unsigned int> IndexList;
|
||||
|
||||
/////////// Post-transform
|
||||
|
||||
// A list of triangles that use a vertex is associated with the Vertex
|
||||
// structure in another array.
|
||||
struct Vertex
|
||||
{
|
||||
int trisUsing;
|
||||
size_t triList; // index to start of triangle storage
|
||||
Vertex()
|
||||
: trisUsing(0), triList(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
typedef vector<Vertex> VertexList;
|
||||
|
||||
struct Triangle
|
||||
{
|
||||
unsigned verts[3];
|
||||
};
|
||||
typedef vector<Triangle> TriangleList;
|
||||
|
||||
struct TriangleCounterOperator
|
||||
{
|
||||
VertexList* vertices;
|
||||
int triangleCount;
|
||||
TriangleCounterOperator() : vertices(0), triangleCount(0) {}
|
||||
|
||||
void doVertex(unsigned p)
|
||||
{
|
||||
if (vertices->size() <= p)
|
||||
vertices->resize(p + 1);
|
||||
(*vertices)[p].trisUsing++;
|
||||
}
|
||||
|
||||
void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
|
||||
{
|
||||
if (p1 == p2 || p2 == p3 || p1 == p3)
|
||||
return;
|
||||
doVertex(p1);
|
||||
doVertex(p2);
|
||||
doVertex(p3);
|
||||
triangleCount++;
|
||||
}
|
||||
};
|
||||
|
||||
struct TriangleCounter : public TriangleIndexFunctor<TriangleCounterOperator>
|
||||
{
|
||||
TriangleCounter(vector<Vertex>* vertices_)
|
||||
{
|
||||
vertices = vertices_;
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize the vertex triangle lists and the triangle data structures
|
||||
struct TriangleAddOperator
|
||||
{
|
||||
VertexList* vertices;
|
||||
TriangleList* triangles;
|
||||
int triIdx;
|
||||
TriangleAddOperator() : vertices(0), triIdx(0) {}
|
||||
|
||||
void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
|
||||
{
|
||||
if (p1 == p2 || p2 == p3 || p1 == p3)
|
||||
return;
|
||||
(*triangles)[triIdx].verts[0] = p1;
|
||||
(*triangles)[triIdx].verts[1] = p2;
|
||||
(*triangles)[triIdx].verts[2] = p3;
|
||||
triIdx++;
|
||||
}
|
||||
};
|
||||
|
||||
struct TriangleAdder : public TriangleIndexFunctor<TriangleAddOperator>
|
||||
{
|
||||
TriangleAdder(VertexList* vertices_, TriangleList* triangles_)
|
||||
{
|
||||
vertices = vertices_;
|
||||
triangles = triangles_;
|
||||
}
|
||||
};
|
||||
|
||||
struct is_not_soup
|
||||
{
|
||||
is_not_soup(const VertexList& vertices) : _vertices(vertices) {}
|
||||
bool operator()(const Triangle &t)
|
||||
{
|
||||
return _vertices[t.verts[0]].trisUsing > 1 ||
|
||||
_vertices[t.verts[1]].trisUsing > 1 ||
|
||||
_vertices[t.verts[2]].trisUsing > 1;
|
||||
}
|
||||
|
||||
VertexList _vertices;
|
||||
};
|
||||
|
||||
|
||||
class VertexCacheVisitor : osgUtil::VertexCacheVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
void optimizeVertices(Geometry& geom)
|
||||
{
|
||||
StatLogger logger("glesUtil::VertexCacheVisitor::optimizeVertices(" + geom.getName() + ")");
|
||||
|
||||
Array* vertArray = geom.getVertexArray();
|
||||
if (!vertArray)
|
||||
return;
|
||||
unsigned vertArraySize = vertArray->getNumElements();
|
||||
// If all the vertices fit in the cache, there's no point in
|
||||
// doing this optimization.
|
||||
if (vertArraySize <= 16)
|
||||
return;
|
||||
|
||||
osg::ref_ptr<osg::Geometry> dummy = new osg::Geometry;
|
||||
osg::Geometry::PrimitiveSetList newPrims;
|
||||
|
||||
for (int ii = geom.getNumPrimitiveSets() - 1 ; ii >= 0 ; -- ii) {
|
||||
osg::PrimitiveSet* primitive = geom.getPrimitiveSet(ii);
|
||||
if(!primitive || !primitive->getNumIndices()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect all 'surface' primitives in the dummy geometry
|
||||
if(primitive->getMode() >= PrimitiveSet::TRIANGLES && primitive->getDrawElements()) {
|
||||
dummy->addPrimitiveSet(primitive);
|
||||
}
|
||||
else {
|
||||
newPrims.push_back(primitive);
|
||||
}
|
||||
}
|
||||
|
||||
if(!dummy->getNumPrimitiveSets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector<unsigned> newVertList;
|
||||
doVertexOptimization(*dummy, newVertList);
|
||||
|
||||
osg::DrawElementsUInt* elements = new DrawElementsUInt(GL_TRIANGLES, newVertList.begin(), newVertList.end());
|
||||
if (geom.getUseVertexBufferObjects()) {
|
||||
elements->setElementBufferObject(new ElementBufferObject);
|
||||
}
|
||||
newPrims.insert(newPrims.begin(), elements);
|
||||
|
||||
geom.setPrimitiveSetList(newPrims);
|
||||
|
||||
geom.dirtyDisplayList();
|
||||
}
|
||||
|
||||
void doVertexOptimization(Geometry& geom, vector<unsigned>& vertDrawList)
|
||||
{
|
||||
Geometry::PrimitiveSetList& primSets = geom.getPrimitiveSetList();
|
||||
// lists for all the vertices and triangles
|
||||
VertexList vertices;
|
||||
TriangleList triangles;
|
||||
TriangleCounter triCounter(&vertices);
|
||||
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
|
||||
end = primSets.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
(*itr)->accept(triCounter);
|
||||
triangles.resize(triCounter.triangleCount);
|
||||
// Get total of triangles used by all the vertices
|
||||
size_t vertTrisSize = 0;
|
||||
for (VertexList::iterator itr = vertices.begin(), end = vertices.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
{
|
||||
itr->triList = vertTrisSize;
|
||||
vertTrisSize += itr->trisUsing;
|
||||
}
|
||||
// Store for lists of triangles (indices) used by the vertices
|
||||
vector<unsigned> vertTriListStore(vertTrisSize);
|
||||
TriangleAdder triAdder(&vertices, &triangles);
|
||||
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
|
||||
end = primSets.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
(*itr)->accept(triAdder);
|
||||
|
||||
// discard triangle soup as it cannot be cache-optimized
|
||||
TriangleList::iterator soupIterator = std::partition(triangles.begin(), triangles.end(),
|
||||
is_not_soup(vertices));
|
||||
TriangleList soup(soupIterator, triangles.end());
|
||||
triangles.erase(soupIterator, triangles.end());
|
||||
OSG_INFO << "Info: glesUtil::VertexCacheVisitor::doVertexOptimization(..) found "
|
||||
<< soup.size() << " soup triangles" << std::endl << std::flush;
|
||||
|
||||
std::vector<unsigned int> indices;
|
||||
for(TriangleList::const_iterator it_tri = triangles.begin() ; it_tri != triangles.end() ; ++ it_tri) {
|
||||
indices.push_back(it_tri->verts[0]);
|
||||
indices.push_back(it_tri->verts[1]);
|
||||
indices.push_back(it_tri->verts[2]);
|
||||
}
|
||||
|
||||
// call bgfx forsyth-algorithm implementation
|
||||
vertDrawList.resize(indices.size());
|
||||
Forsyth::OptimizeFaces(&indices[0], indices.size(), vertices.size(), &vertDrawList[0], 16);
|
||||
for(TriangleList::iterator itr = soup.begin() ; itr != soup.end() ; ++ itr) {
|
||||
vertDrawList.push_back(itr->verts[0]);
|
||||
vertDrawList.push_back(itr->verts[1]);
|
||||
vertDrawList.push_back(itr->verts[2]);
|
||||
}
|
||||
}
|
||||
|
||||
}; // Post-transform
|
||||
|
||||
// A helper class that gathers up all the attribute arrays of an
|
||||
// osg::Geometry object that are BIND_PER_VERTEX and runs an
|
||||
// ArrayVisitor on them.
|
||||
struct GeometryArrayGatherer
|
||||
{
|
||||
typedef std::vector<osg::Array*> ArrayList;
|
||||
|
||||
GeometryArrayGatherer(osg::Geometry& geometry) {
|
||||
add(geometry.getVertexArray());
|
||||
add(geometry.getNormalArray());
|
||||
add(geometry.getColorArray());
|
||||
add(geometry.getSecondaryColorArray());
|
||||
add(geometry.getFogCoordArray());
|
||||
for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) {
|
||||
add(geometry.getTexCoordArray(i));
|
||||
}
|
||||
for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) {
|
||||
add(geometry.getVertexAttribArray(i));
|
||||
}
|
||||
}
|
||||
|
||||
void add(osg::Array* array) {
|
||||
if (array) {
|
||||
_arrayList.push_back(array);
|
||||
}
|
||||
}
|
||||
|
||||
void accept(osg::ArrayVisitor& av) {
|
||||
for(ArrayList::iterator itr = _arrayList.begin() ; itr != _arrayList.end(); ++ itr) {
|
||||
(*itr)->accept(av);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList _arrayList;
|
||||
};
|
||||
|
||||
|
||||
// Compact the vertex attribute arrays. Also stolen from TriStripVisitor
|
||||
class RemapArray : public osg::ArrayVisitor
|
||||
{
|
||||
public:
|
||||
RemapArray(const IndexList& remapping) : _remapping(remapping)
|
||||
{}
|
||||
|
||||
const IndexList& _remapping;
|
||||
|
||||
template<class T>
|
||||
inline void remap(T& array) {
|
||||
for(unsigned int i = 0 ; i < _remapping.size() ; ++ i) {
|
||||
if(i != _remapping[i]) {
|
||||
array[i] = array[_remapping[i]];
|
||||
}
|
||||
}
|
||||
array.erase(array.begin() + _remapping.size(),
|
||||
array.end());
|
||||
}
|
||||
|
||||
virtual void apply(osg::Array&) {}
|
||||
virtual void apply(osg::ByteArray& array) { remap(array); }
|
||||
virtual void apply(osg::ShortArray& array) { remap(array); }
|
||||
virtual void apply(osg::IntArray& array) { remap(array); }
|
||||
virtual void apply(osg::UByteArray& array) { remap(array); }
|
||||
virtual void apply(osg::UShortArray& array) { remap(array); }
|
||||
virtual void apply(osg::UIntArray& array) { remap(array); }
|
||||
virtual void apply(osg::FloatArray& array) { remap(array); }
|
||||
virtual void apply(osg::DoubleArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2dArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3dArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4dArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2Array& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3Array& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4Array& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2iArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3iArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4iArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2uiArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3uiArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4uiArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2sArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3sArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4sArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2usArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3usArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4usArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2bArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3bArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4bArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec4ubArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3ubArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec2ubArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::MatrixfArray& array) { remap(array); }
|
||||
|
||||
protected:
|
||||
RemapArray& operator= (const RemapArray&) { return *this; }
|
||||
};
|
||||
|
||||
|
||||
// Compare vertices in a mesh using all their attributes. The vertices
|
||||
// are identified by their index. Extracted from TriStripVisitor.cpp
|
||||
struct VertexAttribComparitor : public GeometryArrayGatherer
|
||||
{
|
||||
VertexAttribComparitor(osg::Geometry& geometry) : GeometryArrayGatherer(geometry)
|
||||
{}
|
||||
|
||||
bool operator() (unsigned int lhs, unsigned int rhs) const {
|
||||
for(ArrayList::const_iterator itr = _arrayList.begin(); itr != _arrayList.end(); ++ itr) {
|
||||
int compare = (*itr)->compare(lhs, rhs);
|
||||
if (compare == -1) { return true; }
|
||||
if (compare == 1) { return false; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int compare(unsigned int lhs, unsigned int rhs) {
|
||||
for(ArrayList::iterator itr = _arrayList.begin(); itr != _arrayList.end(); ++ itr) {
|
||||
int compare = (*itr)->compare(lhs, rhs);
|
||||
if (compare == -1) { return -1; }
|
||||
if (compare == 1) { return 1; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
VertexAttribComparitor& operator= (const VertexAttribComparitor&) { return *this; }
|
||||
};
|
||||
|
||||
// Move the values in an array to new positions, based on the
|
||||
// remapping table. remapping[i] contains element i's new position, if
|
||||
// any. Unlike RemapArray in TriStripVisitor, this code doesn't
|
||||
// assume that elements only move downward in the array.
|
||||
class Remapper : public osg::ArrayVisitor
|
||||
{
|
||||
public:
|
||||
static const unsigned invalidIndex;
|
||||
Remapper(const vector<unsigned>& remapping)
|
||||
: _remapping(remapping), _newsize(0)
|
||||
{
|
||||
for (vector<unsigned>::const_iterator itr = _remapping.begin(),
|
||||
end = _remapping.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
if (*itr != invalidIndex)
|
||||
++_newsize;
|
||||
}
|
||||
|
||||
const vector<unsigned>& _remapping;
|
||||
size_t _newsize;
|
||||
|
||||
template<class T>
|
||||
inline void remap(T& array)
|
||||
{
|
||||
ref_ptr<T> newarray = new T(_newsize);
|
||||
T* newptr = newarray.get();
|
||||
for (size_t i = 0; i < array.size(); ++i)
|
||||
if (_remapping[i] != invalidIndex)
|
||||
(*newptr)[_remapping[i]] = array[i];
|
||||
array.swap(*newptr);
|
||||
|
||||
}
|
||||
|
||||
virtual void apply(osg::Array&) {}
|
||||
virtual void apply(osg::ByteArray& array) { remap(array); }
|
||||
virtual void apply(osg::ShortArray& array) { remap(array); }
|
||||
virtual void apply(osg::IntArray& array) { remap(array); }
|
||||
virtual void apply(osg::UByteArray& array) { remap(array); }
|
||||
virtual void apply(osg::UShortArray& array) { remap(array); }
|
||||
virtual void apply(osg::UIntArray& array) { remap(array); }
|
||||
virtual void apply(osg::FloatArray& array) { remap(array); }
|
||||
virtual void apply(osg::DoubleArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2Array& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3Array& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4Array& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec4ubArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2bArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3bArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4bArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2sArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3sArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4sArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2dArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3dArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4dArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::MatrixfArray& array) { remap(array); }
|
||||
};
|
||||
|
||||
|
||||
// Record the order in which vertices in a Geometry are used.
|
||||
struct VertexReorderOperator
|
||||
{
|
||||
unsigned seq;
|
||||
std::vector<unsigned int> remap;
|
||||
|
||||
VertexReorderOperator() : seq(0)
|
||||
{
|
||||
}
|
||||
|
||||
void inline doVertex(unsigned v)
|
||||
{
|
||||
if (remap[v] == glesUtil::Remapper::invalidIndex) {
|
||||
remap[v] = seq ++;
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(unsigned p1, unsigned p2, unsigned p3)
|
||||
{
|
||||
doVertex(p1);
|
||||
doVertex(p2);
|
||||
doVertex(p3);
|
||||
}
|
||||
|
||||
void operator()(unsigned p1, unsigned p2)
|
||||
{
|
||||
doVertex(p1);
|
||||
doVertex(p2);
|
||||
}
|
||||
|
||||
void operator()(unsigned p1)
|
||||
{
|
||||
doVertex(p1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct VertexReorder : public TriangleLinePointIndexFunctor<glesUtil::VertexReorderOperator>
|
||||
{
|
||||
VertexReorder(unsigned numVerts)
|
||||
{
|
||||
remap.resize(numVerts, glesUtil::Remapper::invalidIndex);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename DE>
|
||||
inline void reorderDrawElements(DE& drawElements,
|
||||
const vector<unsigned>& reorder)
|
||||
{
|
||||
for (typename DE::iterator itr = drawElements.begin(), end = drawElements.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
{
|
||||
*itr = static_cast<typename DE::value_type>(reorder[*itr]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VertexAccessOrderVisitor : osgUtil::VertexAccessOrderVisitor
|
||||
{
|
||||
struct OrderByPrimitiveMode
|
||||
{
|
||||
inline bool operator() (const osg::ref_ptr<osg::PrimitiveSet>& prim1, const osg::ref_ptr<osg::PrimitiveSet>& prim2)
|
||||
{
|
||||
if(prim1.get() && prim2.get()) {
|
||||
return prim1->getMode() >= prim2->getMode();
|
||||
}
|
||||
else if(prim1.get()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} order_by_primitive_mode;
|
||||
|
||||
public:
|
||||
void optimizeOrder(Geometry& geom)
|
||||
{
|
||||
StatLogger logger("glesUtil::VertexAccessOrderVisitor::optimizeOrder(" + geom.getName() + ")");
|
||||
|
||||
Array* vertArray = geom.getVertexArray();
|
||||
if (!vertArray || !vertArray->getNumElements())
|
||||
return;
|
||||
Geometry::PrimitiveSetList& primSets = geom.getPrimitiveSetList();
|
||||
|
||||
// sort primitives: first triangles, then lines and finally points
|
||||
std::sort(primSets.begin(), primSets.end(), order_by_primitive_mode);
|
||||
|
||||
glesUtil::VertexReorder vr(vertArray->getNumElements());
|
||||
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
|
||||
end = primSets.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
{
|
||||
PrimitiveSet* ps = itr->get();
|
||||
PrimitiveSet::Type type = ps->getType();
|
||||
if (type != PrimitiveSet::DrawElementsUBytePrimitiveType
|
||||
&& type != PrimitiveSet::DrawElementsUShortPrimitiveType
|
||||
&& type != PrimitiveSet::DrawElementsUIntPrimitiveType)
|
||||
return;
|
||||
ps->accept(vr);
|
||||
}
|
||||
|
||||
// search for UVs array shared only within the geometry
|
||||
osgUtil::SharedArrayOptimizer deduplicator;
|
||||
deduplicator.findDuplicatedUVs(geom);
|
||||
|
||||
// duplicate shared arrays as it isn't safe to rearrange vertices when arrays are shared.
|
||||
if (geom.containsSharedArrays()) geom.duplicateSharedArrays();
|
||||
GeometryArrayGatherer gatherer(geom);
|
||||
|
||||
Remapper remapper(vr.remap);
|
||||
gatherer.accept(remapper);
|
||||
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
|
||||
end = primSets.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
{
|
||||
PrimitiveSet* ps = itr->get();
|
||||
switch (ps->getType())
|
||||
{
|
||||
case PrimitiveSet::DrawElementsUBytePrimitiveType:
|
||||
reorderDrawElements(*static_cast<DrawElementsUByte*>(ps), vr.remap);
|
||||
break;
|
||||
case PrimitiveSet::DrawElementsUShortPrimitiveType:
|
||||
reorderDrawElements(*static_cast<DrawElementsUShort*>(ps), vr.remap);
|
||||
break;
|
||||
case PrimitiveSet::DrawElementsUIntPrimitiveType:
|
||||
reorderDrawElements(*static_cast<DrawElementsUInt*>(ps), vr.remap);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// deduplicate UVs array that were only shared within the geometry
|
||||
deduplicator.deduplicateUVs(geom);
|
||||
|
||||
geom.dirtyDisplayList();
|
||||
}
|
||||
};
|
||||
} // glesUtil namespace
|
||||
|
||||
#endif
|
||||
117
src/osgPlugins/osgjs/Adaptator
Normal file
117
src/osgPlugins/osgjs/Adaptator
Normal file
@@ -0,0 +1,117 @@
|
||||
/* -*-c++-*-
|
||||
* Copyright (C) 2010 Cedric Pinson <cedric.pinson@plopbyte.net>
|
||||
*
|
||||
* This library is open source and may be redistributed and/or modified under
|
||||
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
|
||||
* (at your option) any later version. The full license is in LICENSE file
|
||||
* included with this distribution, and on the openscenegraph.org website.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* OpenSceneGraph Public License for more details.
|
||||
*
|
||||
* Authors:
|
||||
* Cedric Pinson <cedric.pinson@plopbyte.net>
|
||||
*/
|
||||
|
||||
#include <osg/TriangleFunctor>
|
||||
|
||||
|
||||
struct DrawArrayLenghtAdaptator
|
||||
{
|
||||
unsigned int _normalArraySize;
|
||||
const Vec3* _normalArrayPtr;
|
||||
|
||||
unsigned int _colorArraySize;
|
||||
const Vec4* _colorArrayPtr;
|
||||
|
||||
std::vector<std::pair<unsigned int, const osg::Vec2*> > _uvs;
|
||||
|
||||
virtual void setNormalArray(unsigned int count,const Vec3* vertices) {
|
||||
_normalArraySize = count;
|
||||
_normalArrayPtr = vertices;
|
||||
}
|
||||
|
||||
virtual void setNormalArray(unsigned int count,const Vec4* colors) {
|
||||
_colorArraySize = count;
|
||||
_colorArrayPtr = colors;
|
||||
}
|
||||
|
||||
virtual void setTexCoordArray(int unit, unsigned int count,const Vec2* uvs) {
|
||||
if (_uvs.size() <= unit)
|
||||
_uvs.resize(unit+1);
|
||||
_uvs[i].first = count;
|
||||
_uvs[i].second = uvs;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct AdaptDraw : public osg::TriangleFunctor<DrawArrayLenghtAdaptator>
|
||||
{
|
||||
|
||||
virtual void drawArrays(GLenum mode,GLint first,GLsizei count)
|
||||
{
|
||||
if (_vertexArrayPtr==0 || count==0) return;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_TRIANGLES):
|
||||
{
|
||||
unsigned int last = first+count;
|
||||
for(unsigned int current = first; current<last ; current+=3) {
|
||||
this->operator()(current, current+1, current+2,_treatVertexDataAsTemporary);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_TRIANGLE_STRIP):
|
||||
{
|
||||
unsigned int current = first;
|
||||
for(GLsizei i=2;i<count;++i, current+=1)
|
||||
{
|
||||
if ((i%2)) this->operator()(current, current+2, current+1,_treatVertexDataAsTemporary);
|
||||
else this->operator()(current, current+1, current+2,_treatVertexDataAsTemporary);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUADS):
|
||||
{
|
||||
unsigned int current = first;
|
||||
for(GLsizei i=3;i<count;i+=4,current+=4)
|
||||
{
|
||||
this->operator()(current, current+1, current+2,_treatVertexDataAsTemporary);
|
||||
this->operator()(current, current+2, current+3,_treatVertexDataAsTemporary);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUAD_STRIP):
|
||||
{
|
||||
unsigned int current = first;
|
||||
for(GLsizei i=3;i<count;i+=2,current+=2)
|
||||
{
|
||||
this->operator()(current, current+1, current+2,_treatVertexDataAsTemporary);
|
||||
this->operator()(current+1, current+3, current+2,_treatVertexDataAsTemporary);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
|
||||
case(GL_TRIANGLE_FAN):
|
||||
{
|
||||
unsigned int current = first + 1;
|
||||
for(GLsizei i=2;i<count;++i,++current)
|
||||
{
|
||||
this->operator()(first),current, current+1,_treatVertexDataAsTemporary);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_POINTS):
|
||||
case(GL_LINES):
|
||||
case(GL_LINE_STRIP):
|
||||
case(GL_LINE_LOOP):
|
||||
default:
|
||||
// can't be converted into to triangles.
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
12
src/osgPlugins/osgjs/Animation
Normal file
12
src/osgPlugins/osgjs/Animation
Normal file
@@ -0,0 +1,12 @@
|
||||
/* -*-c++-*-
|
||||
* Copyright (C) 2011 Cedric Pinson <cedric.pinson@plopbyte.com>
|
||||
*/
|
||||
#ifndef JSON_ANIMATION_H
|
||||
#define JSON_ANIMATION_H
|
||||
#include <osgAnimation/Animation>
|
||||
#include <osgAnimation/UpdateMatrixTransform>
|
||||
|
||||
JSONObject* createJSONAnimation(osgAnimation::Animation* anim);
|
||||
JSONObject* createJSONUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform& acb);
|
||||
|
||||
#endif
|
||||
203
src/osgPlugins/osgjs/Animation.cpp
Normal file
203
src/osgPlugins/osgjs/Animation.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/* -*-c++-*-
|
||||
* Copyright (C) 2011 Cedric Pinson <cedric.pinson@plopbyte.com>
|
||||
*/
|
||||
|
||||
#include <osgAnimation/Animation>
|
||||
#include <osgAnimation/Channel>
|
||||
#include <osgAnimation/Sampler>
|
||||
#include <osgAnimation/UpdateMatrixTransform>
|
||||
#include <osgAnimation/StackedTranslateElement>
|
||||
#include <osgAnimation/StackedQuaternionElement>
|
||||
#include <osgAnimation/StackedRotateAxisElement>
|
||||
#include "JSON_Objects"
|
||||
|
||||
static bool addJSONChannelVec3(osgAnimation::Vec3LinearChannel* channel, JSONObject& anim)
|
||||
{
|
||||
if (channel && channel->getSampler()) {
|
||||
osg::ref_ptr<JSONObject> json = new JSONObject;
|
||||
json->getMaps()["Name"] = new JSONValue<std::string>(channel->getName());
|
||||
json->getMaps()["TargetName"] = new JSONValue<std::string>(channel->getTargetName());
|
||||
osgAnimation::Vec3KeyframeContainer* keys = channel->getSamplerTyped()->getKeyframeContainerTyped();
|
||||
JSONKeyframes* jsonKeys = new JSONKeyframes();
|
||||
//if (!keys->getName().empty()) {
|
||||
// jsonKeys->getMaps()["Name"] = new JSONValue<std::string>(keys->getName());
|
||||
//}
|
||||
|
||||
for (unsigned int i = 0; i < keys->size(); i++) {
|
||||
JSONVec4Array* kf = new JSONVec4Array(osg::Vec4((*keys)[i].getTime(),
|
||||
(*keys)[i].getValue()[0],
|
||||
(*keys)[i].getValue()[1],
|
||||
(*keys)[i].getValue()[2]));
|
||||
jsonKeys->getArray().push_back(kf);
|
||||
}
|
||||
json->getMaps()["KeyFrames"] = jsonKeys;
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonChannel = new JSONObject();
|
||||
jsonChannel->getMaps()["osgAnimation.Vec3LerpChannel"] = json;
|
||||
anim.getMaps()["Channels"]->asArray()->getArray().push_back(jsonChannel);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool addJSONChannelFloat(osgAnimation::FloatLinearChannel* channel, JSONObject& anim)
|
||||
{
|
||||
if (channel->getSampler()) {
|
||||
osg::ref_ptr<JSONObject> json = new JSONObject;
|
||||
json->getMaps()["Name"] = new JSONValue<std::string>(channel->getName());
|
||||
json->getMaps()["TargetName"] = new JSONValue<std::string>(channel->getTargetName());
|
||||
osgAnimation::FloatKeyframeContainer* keys = channel->getSamplerTyped()->getKeyframeContainerTyped();
|
||||
JSONKeyframes* jsonKeys = new JSONKeyframes();
|
||||
//if (!keys->getName().empty()) {
|
||||
// jsonKeys->getMaps()["Name"] = new JSONValue<std::string>(keys->getName());
|
||||
//}
|
||||
|
||||
for (unsigned int i = 0; i < keys->size(); i++) {
|
||||
JSONVec2Array* kf = new JSONVec2Array(osg::Vec2((*keys)[i].getTime(),
|
||||
(*keys)[i].getValue()));
|
||||
jsonKeys->getArray().push_back(kf);
|
||||
}
|
||||
json->getMaps()["KeyFrames"] = jsonKeys;
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonChannel = new JSONObject();
|
||||
jsonChannel->getMaps()["osgAnimation.FloatLerpChannel"] = json;
|
||||
anim.getMaps()["Channels"]->asArray()->getArray().push_back(jsonChannel);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool addJSONChannelQuaternion(osgAnimation::QuatSphericalLinearChannel* channel, JSONObject& anim)
|
||||
{
|
||||
if (channel->getSampler()) {
|
||||
osg::ref_ptr<JSONObject> json = new JSONObject;
|
||||
json->getMaps()["Name"] = new JSONValue<std::string>(channel->getName());
|
||||
json->getMaps()["TargetName"] = new JSONValue<std::string>(channel->getTargetName());
|
||||
osgAnimation::QuatKeyframeContainer* keys = channel->getSamplerTyped()->getKeyframeContainerTyped();
|
||||
JSONKeyframes* jsonKeys = new JSONKeyframes();
|
||||
|
||||
for (unsigned int i = 0; i < keys->size(); i++) {
|
||||
JSONVec5Array* kf = new JSONVec5Array(Vec5((*keys)[i].getTime(),
|
||||
(*keys)[i].getValue()[0],
|
||||
(*keys)[i].getValue()[1],
|
||||
(*keys)[i].getValue()[2],
|
||||
(*keys)[i].getValue()[3]));
|
||||
jsonKeys->getArray().push_back(kf);
|
||||
}
|
||||
json->getMaps()["KeyFrames"] = jsonKeys;
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonChannel = new JSONObject();
|
||||
jsonChannel->getMaps()["osgAnimation.QuatSlerpChannel"] = json;
|
||||
anim.getMaps()["Channels"]->asArray()->getArray().push_back(jsonChannel);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void addJSONChannel(osgAnimation::Channel* channel, JSONObject& anim)
|
||||
{
|
||||
{
|
||||
osgAnimation::Vec3LinearChannel* c = dynamic_cast<osgAnimation::Vec3LinearChannel*>(channel);
|
||||
if (c) {
|
||||
if (addJSONChannelVec3(c, anim))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
osgAnimation::FloatLinearChannel* c = dynamic_cast<osgAnimation::FloatLinearChannel*>(channel);
|
||||
if (c) {
|
||||
if (addJSONChannelFloat(c, anim))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
osgAnimation::QuatSphericalLinearChannel* c = dynamic_cast<osgAnimation::QuatSphericalLinearChannel*>(channel);
|
||||
if (c) {
|
||||
if (addJSONChannelQuaternion(c, anim))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JSONObject* createJSONAnimation(osgAnimation::Animation* anim)
|
||||
{
|
||||
osg::ref_ptr<JSONObject> json = new JSONObject;
|
||||
json->getMaps()["Channels"] = new JSONArray();
|
||||
json->getMaps()["Name"] = new JSONValue<std::string>(anim->getName());
|
||||
|
||||
for (unsigned int i = 0; i < anim->getChannels().size(); i++) {
|
||||
addJSONChannel(anim->getChannels()[i].get(), *json);
|
||||
}
|
||||
return json.release();
|
||||
}
|
||||
|
||||
|
||||
|
||||
JSONObject* createJSONUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform& acb)
|
||||
{
|
||||
std::string name = acb.getName();
|
||||
osg::ref_ptr<JSONObject> json = new JSONObject;
|
||||
json->getMaps()["Name"] = new JSONValue<std::string>(acb.getName());
|
||||
|
||||
osg::ref_ptr<JSONArray> jsonStackedArray = new JSONArray();
|
||||
json->getMaps()["StackedTransforms"] = jsonStackedArray;
|
||||
|
||||
osgAnimation::StackedTransform& st = acb.getStackedTransforms();
|
||||
for (unsigned int i = 0; i < st.size(); i++) {
|
||||
{
|
||||
osgAnimation::StackedTranslateElement* element = dynamic_cast<osgAnimation::StackedTranslateElement*>(st[i].get());
|
||||
if (element) {
|
||||
osg::ref_ptr<JSONObject> jsonElement = new JSONObject;
|
||||
jsonElement->getMaps()["Name"] = new JSONValue<std::string>(element->getName());
|
||||
jsonElement->getMaps()["Translate"] = new JSONVec3Array(element->getTranslate());
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonElementObject = new JSONObject;
|
||||
jsonElementObject->getMaps()["osgAnimation.StackedTranslate"] = jsonElement;
|
||||
jsonStackedArray->getArray().push_back(jsonElementObject);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
osgAnimation::StackedQuaternionElement* element = dynamic_cast<osgAnimation::StackedQuaternionElement*>(st[i].get());
|
||||
if (element) {
|
||||
osg::ref_ptr<JSONObject> jsonElement = new JSONObject;
|
||||
jsonElement->getMaps()["Name"] = new JSONValue<std::string>(element->getName());
|
||||
jsonElement->getMaps()["Quaternion"] = new JSONVec4Array(element->getQuaternion().asVec4());
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonElementObject = new JSONObject;
|
||||
jsonElementObject->getMaps()["osgAnimation.StackedQuaternion"] = jsonElement;
|
||||
jsonStackedArray->getArray().push_back(jsonElementObject);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
osgAnimation::StackedRotateAxisElement* element = dynamic_cast<osgAnimation::StackedRotateAxisElement*>(st[i].get());
|
||||
if (element) {
|
||||
osg::ref_ptr<JSONObject> jsonElement = new JSONObject;
|
||||
jsonElement->getMaps()["Name"] = new JSONValue<std::string>(element->getName());
|
||||
jsonElement->getMaps()["Axis"] = new JSONVec3Array(element->getAxis());
|
||||
jsonElement->getMaps()["Angle"] = new JSONValue<double>(element->getAngle());
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonElementObject = new JSONObject;
|
||||
jsonElementObject->getMaps()["osgAnimation.StackedRotateAxis"] = jsonElement;
|
||||
jsonStackedArray->getArray().push_back(jsonElementObject);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jsonStackedArray->getArray().empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return json.release();
|
||||
}
|
||||
170
src/osgPlugins/osgjs/Base64
Normal file
170
src/osgPlugins/osgjs/Base64
Normal file
@@ -0,0 +1,170 @@
|
||||
/* base64.hpp - base64 encoder/decoder implementing section 6.8 of RFC2045
|
||||
|
||||
Copyright (C) 2002 Ryan Petrie (ryanpetrie@netscape.net)
|
||||
and released under the zlib license:
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
base64::encoder and base64::decoder are intended to be identical in usage
|
||||
as the STL's std::copy and similar algorithms. They require as parameters
|
||||
an input iterator range and an output iterator, and just as std::copy, the
|
||||
iterators can be bare pointers, container iterators, or even adaptors such
|
||||
as ostream_iterator.
|
||||
|
||||
Examples:
|
||||
|
||||
// encode/decode with in-place memory buffers
|
||||
char source[size], dest[size*2];
|
||||
base64::encode(source, source+size, dest);
|
||||
base64::decode(dest, dest+size*2, source);
|
||||
|
||||
// encode from memory to a file
|
||||
char source[size];
|
||||
ofstream file("output.txt");
|
||||
base64::encode((char*)source, source+size, ostream_iterator<char>(file));
|
||||
|
||||
// decode from file to a standard container
|
||||
ifstream file("input.txt");
|
||||
vector<char> dest;
|
||||
base64::decode(istream_iterator<char>(file), istream_iterator<char>(),
|
||||
back_inserter(dest)
|
||||
);
|
||||
*/
|
||||
|
||||
#ifndef _BASE64_HPP
|
||||
#define _BASE64_HPP
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace base64
|
||||
{
|
||||
typedef unsigned uint32;
|
||||
typedef unsigned char uint8;
|
||||
|
||||
extern const char* to_table;
|
||||
extern const char* to_table_end;
|
||||
|
||||
extern const char* from_table;
|
||||
|
||||
|
||||
template <class InputIterator, class OutputIterator>
|
||||
void encode(const InputIterator& begin,
|
||||
const InputIterator& end,
|
||||
OutputIterator out, bool wrap)
|
||||
{
|
||||
InputIterator it = begin;
|
||||
int lineSize = 0;
|
||||
|
||||
int bytes;
|
||||
do
|
||||
{
|
||||
uint32 input = 0;
|
||||
|
||||
// get the next three bytes into "in" (and count how many we actually get)
|
||||
bytes = 0;
|
||||
for(; (bytes < 3) && (it != end); ++bytes, ++it)
|
||||
{
|
||||
input <<= 8;
|
||||
input += static_cast<uint8>(*it);
|
||||
}
|
||||
|
||||
// convert to base64
|
||||
int bits = bytes*8;
|
||||
while (bits > 0)
|
||||
{
|
||||
bits -= 6;
|
||||
const uint8 index = ((bits < 0) ? input << -bits : input >> bits) & 0x3F;
|
||||
*out = to_table[index];
|
||||
++out;
|
||||
++lineSize;
|
||||
}
|
||||
|
||||
if (lineSize >= 76 && wrap) // ensure proper line length
|
||||
{
|
||||
*out = 13;
|
||||
++out;
|
||||
*out = 10;
|
||||
++out;
|
||||
lineSize = 0;
|
||||
}
|
||||
|
||||
} while (bytes == 3);
|
||||
|
||||
|
||||
// add pad characters if necessary
|
||||
if (bytes > 0)
|
||||
for(int i=bytes; i < 3; ++i)
|
||||
{
|
||||
*out = '=';
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class InputIterator, class OutputIterator>
|
||||
void decode(const InputIterator& begin,
|
||||
const InputIterator& end,
|
||||
OutputIterator out)
|
||||
{
|
||||
InputIterator it = begin;
|
||||
int chars;
|
||||
|
||||
do
|
||||
{
|
||||
uint8 input[4] = {0, 0, 0, 0};
|
||||
|
||||
// get four characters
|
||||
chars=0;
|
||||
while((chars<4) && (it != end))
|
||||
{
|
||||
uint8 c = static_cast<char>(*it);
|
||||
if (c == '=') break; // pad character marks the end of the stream
|
||||
++it;
|
||||
|
||||
if (std::find(to_table, to_table_end, c) != to_table_end)
|
||||
{
|
||||
input[chars] = from_table[c];
|
||||
chars++;
|
||||
}
|
||||
}
|
||||
|
||||
// output the binary data
|
||||
if (chars >= 2)
|
||||
{
|
||||
*out = static_cast<uint8>((input[0] << 2) + (input[1] >> 4));
|
||||
++out;
|
||||
if (chars >= 3)
|
||||
{
|
||||
*out = static_cast<uint8>((input[1] << 4) + (input[2] >> 2));
|
||||
++out;
|
||||
if (chars >= 4)
|
||||
{
|
||||
*out = static_cast<uint8>((input[2] << 6) + input[3]);
|
||||
++out;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (chars == 4);
|
||||
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
#endif
|
||||
42
src/osgPlugins/osgjs/Base64.cpp
Normal file
42
src/osgPlugins/osgjs/Base64.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "Base64"
|
||||
|
||||
namespace base64
|
||||
{
|
||||
const char _to_table[64] =
|
||||
{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
const char* to_table = _to_table;
|
||||
|
||||
|
||||
const char* to_table_end =
|
||||
_to_table + sizeof(_to_table);
|
||||
|
||||
const char _from_table[128] =
|
||||
{
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, // 0
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, // 8
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, // 16
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, // 24
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, // 32
|
||||
-1, -1, -1, 62, -1, -1, -1, 63, // 40
|
||||
52, 53, 54, 55, 56, 57, 58, 59, // 48
|
||||
60, 61, -1, -1, -1, 0, -1, -1, // 56
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, // 64
|
||||
7, 8, 9, 10, 11, 12, 13, 14, // 72
|
||||
15, 16, 17, 18, 19, 20, 21, 22, // 80
|
||||
23, 24, 25, -1, -1, -1, -1, -1, // 88
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, // 96
|
||||
33, 34, 35, 36, 37, 38, 39, 40, // 104
|
||||
41, 42, 43, 44, 45, 46, 47, 48, // 112
|
||||
49, 50, 51, -1, -1, -1, -1, -1 // 120
|
||||
};
|
||||
const char* from_table = _from_table;
|
||||
}
|
||||
|
||||
24
src/osgPlugins/osgjs/CMakeLists.txt
Normal file
24
src/osgPlugins/osgjs/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
SET(TARGET_SRC
|
||||
Animation.cpp
|
||||
Base64.cpp
|
||||
JSON_Objects.cpp
|
||||
ReaderWriterJSON.cpp
|
||||
WriteVisitor.cpp)
|
||||
|
||||
SET(TARGET_H
|
||||
Adaptator
|
||||
Animation
|
||||
Base64
|
||||
CompactBufferVisitor
|
||||
JSON_Objects
|
||||
json_stream
|
||||
WriteVisitor
|
||||
)
|
||||
|
||||
#### end var setup ###
|
||||
SET(TARGET_ADDED_LIBRARIES
|
||||
osgAnimation
|
||||
osgSim
|
||||
osgUtil)
|
||||
|
||||
SETUP_PLUGIN(osgjs)
|
||||
115
src/osgPlugins/osgjs/CompactBufferVisitor
Normal file
115
src/osgPlugins/osgjs/CompactBufferVisitor
Normal file
@@ -0,0 +1,115 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMPACT_BUFFER_VISITOR
|
||||
#define COMPACT_BUFFER_VISITOR
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
////// taken from gles/GeometryUniqueVisitor
|
||||
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Geode>
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
class CompactBufferVisitor : public osg::NodeVisitor {
|
||||
public:
|
||||
CompactBufferVisitor():
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
||||
{}
|
||||
|
||||
void apply(osg::Geode& geode){
|
||||
for (unsigned int i = 0; i < geode.getNumDrawables(); i++) {
|
||||
apply(*geode.getDrawable(i));
|
||||
}
|
||||
}
|
||||
|
||||
void apply(osg::Drawable& drawable){
|
||||
osg::Geometry* geometry = drawable.asGeometry();
|
||||
if (!geometry || isProcessed(geometry)) {
|
||||
return;
|
||||
}
|
||||
apply(*geometry);
|
||||
}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
compactPrimitiveSets(geometry);
|
||||
setProcessed(&geometry);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
bool isProcessed(const osg::Object* buffer)
|
||||
{ return (_processed.find(buffer) != _processed.end()); }
|
||||
|
||||
void setProcessed(const osg::Object* source, osg::Object* processed=0)
|
||||
{ _processed.insert(std::pair<const osg::Object*, osg::Object*>(source, processed)); }
|
||||
|
||||
void compactPrimitiveSets(osg::Geometry& geometry) {
|
||||
osg::Geometry::PrimitiveSetList& primitives = geometry.getPrimitiveSetList();
|
||||
|
||||
for (unsigned int i = 0 ; i < primitives.size() ; i++) {
|
||||
osg::DrawElementsUInt* de = dynamic_cast<osg::DrawElementsUInt*>(primitives[i].get());
|
||||
if(isProcessed(de)) {
|
||||
geometry.setPrimitiveSet(i, dynamic_cast<osg::DrawElements*>(getProcessedBuffer(de)));
|
||||
}
|
||||
else {
|
||||
if(de && de->getNumIndices()) {
|
||||
unsigned int maximum = maxIndex(de);
|
||||
|
||||
if(maximum < 256) {
|
||||
osg::DrawElementsUByte* elements = new osg::DrawElementsUByte(de->getMode());
|
||||
for (unsigned int j = 0 ; j < de->getNumIndices() ; ++ j) {
|
||||
elements->push_back(static_cast<GLubyte>(de->index(j)));
|
||||
}
|
||||
geometry.setPrimitiveSet(i, elements);
|
||||
setProcessed(de, elements);
|
||||
}
|
||||
else if (maximum < 65536) {
|
||||
osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(de->getMode());
|
||||
for (unsigned int j = 0 ; j < de->getNumIndices() ; ++ j) {
|
||||
elements->push_back(static_cast<GLushort>(de->index(j)));
|
||||
}
|
||||
geometry.setPrimitiveSet(i, elements);
|
||||
setProcessed(de, elements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int maxIndex(osg::DrawElements* de) {
|
||||
unsigned int maximum = de->index(0);
|
||||
for(unsigned int i = 1 ; i < de->getNumIndices() ; ++ i){
|
||||
maximum = std::max(maximum, static_cast<unsigned int>(de->index(i)));
|
||||
}
|
||||
return maximum;
|
||||
}
|
||||
|
||||
osg::Object* getProcessedBuffer(const osg::Object* buffer) {
|
||||
std::map<const osg::Object*, osg::Object*>::iterator it = _processed.find(buffer);
|
||||
if(it == _processed.end()) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::map<const osg::Object*, osg::Object*> _processed;
|
||||
};
|
||||
|
||||
#endif
|
||||
372
src/osgPlugins/osgjs/JSON_Objects
Normal file
372
src/osgPlugins/osgjs/JSON_Objects
Normal file
@@ -0,0 +1,372 @@
|
||||
/* -*-c++-*-
|
||||
* Copyright (C) 2010 Cedric Pinson <cedric.pinson@plopbyte.net>
|
||||
*/
|
||||
|
||||
#ifndef JSON_OBJECT
|
||||
#define JSON_OBJECT
|
||||
|
||||
#include <osg/Matrix>
|
||||
#include <osg/Array>
|
||||
#include <osg/Light>
|
||||
#include <osg/Notify>
|
||||
#include <osg/PrimitiveSet>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "json_stream"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <cstdint>
|
||||
#endif
|
||||
|
||||
class WriteVisitor;
|
||||
|
||||
struct Vec5
|
||||
{
|
||||
float _v[5];
|
||||
Vec5() {}
|
||||
Vec5(float a0, float a1, float a2, float a3, float a4) {
|
||||
_v[0] = a0;
|
||||
_v[1] = a1;
|
||||
_v[2] = a2;
|
||||
_v[3] = a3;
|
||||
_v[4] = a4;
|
||||
};
|
||||
inline float& operator [] (int i) { return _v[i]; }
|
||||
inline float operator [] (int i) const { return _v[i]; }
|
||||
};
|
||||
|
||||
struct JSONObjectBase : public osg::Referenced
|
||||
{
|
||||
static int level;
|
||||
static std::string indent();
|
||||
virtual void write(json_stream& str, WriteVisitor& visitor) {}
|
||||
};
|
||||
|
||||
template <class T> struct JSONValue;
|
||||
|
||||
struct JSONArray;
|
||||
struct JSONObject : public JSONObjectBase
|
||||
{
|
||||
typedef std::map<std::string, osg::ref_ptr<JSONObject> > JSONMap;
|
||||
typedef std::vector<std::string> OrderList;
|
||||
JSONMap _maps;
|
||||
JSONMap& getMaps() { return _maps; }
|
||||
void writeOrder(json_stream& str, const OrderList& order, WriteVisitor& visitor);
|
||||
virtual void write(json_stream& str, WriteVisitor& visitor);
|
||||
void addChild(const std::string& type, JSONObject* child);
|
||||
virtual JSONArray* asArray() { return 0; }
|
||||
template<class T> JSONValue<T>* asValue() {
|
||||
return dynamic_cast<JSONValue<T> * > ( this);
|
||||
}
|
||||
|
||||
JSONObject(const unsigned int id, const std::string& bufferName = "");
|
||||
JSONObject();
|
||||
void addUniqueID();
|
||||
unsigned int getUniqueID() const { return _uniqueID; }
|
||||
JSONObject* getShadowObject() { return new JSONObject(_uniqueID, _bufferName); }
|
||||
|
||||
unsigned int _uniqueID;
|
||||
static unsigned int uniqueID;
|
||||
|
||||
std::string _bufferName;
|
||||
virtual void setBufferName(const std::string& name) { _bufferName = name; }
|
||||
std::string getBufferName() { return _bufferName; }
|
||||
bool isVarintableIntegerBuffer(osg::Array const*) const;
|
||||
void encodeArrayAsVarintBuffer(osg::Array const*, std::vector<uint8_t>&) const;
|
||||
template<typename T>
|
||||
void dumpVarintVector(std::vector<uint8_t>&, T const*, bool) const;
|
||||
template<typename T>
|
||||
void dumpVarintValue(std::vector<uint8_t>&, T const*, bool) const;
|
||||
std::vector<uint8_t> varintEncoding(unsigned int value) const;
|
||||
|
||||
// see https://developers.google.com/protocol-buffers/docs/encoding?hl=fr&csw=1#types
|
||||
inline unsigned int toVarintUnsigned(int const v) const
|
||||
{ return (v << 1) ^ (v >> ((sizeof(v) << 3) - 1)); }
|
||||
|
||||
};
|
||||
|
||||
JSONObject* createLight(osg::Light* light);
|
||||
|
||||
struct JSONNode : public JSONObject
|
||||
{
|
||||
void write(json_stream& str, WriteVisitor& visitor);
|
||||
};
|
||||
|
||||
typedef JSONObject JSONStateSet;
|
||||
typedef JSONObject JSONMaterial;
|
||||
typedef JSONObject JSONLight;
|
||||
|
||||
|
||||
struct JSONArray : public JSONObject
|
||||
{
|
||||
typedef std::vector<osg::ref_ptr<JSONObject> > JSONList;
|
||||
JSONList _array;
|
||||
JSONArray() {}
|
||||
virtual void write(json_stream& str, WriteVisitor& visitor);
|
||||
JSONList& getArray() { return _array; }
|
||||
JSONArray* asArray() { return this; }
|
||||
};
|
||||
|
||||
struct JSONKeyframes : public JSONArray
|
||||
{
|
||||
virtual void write(json_stream& str, WriteVisitor& visitor);
|
||||
};
|
||||
|
||||
|
||||
struct JSONVec3Array : public JSONArray
|
||||
{
|
||||
JSONVec3Array() {}
|
||||
JSONVec3Array(const osg::Vec3&);
|
||||
virtual void write(json_stream& str, WriteVisitor& visitor);
|
||||
};
|
||||
|
||||
struct JSONVec4Array : public JSONVec3Array
|
||||
{
|
||||
JSONVec4Array(const osg::Vec4&);
|
||||
};
|
||||
|
||||
struct JSONVec5Array : public JSONVec3Array
|
||||
{
|
||||
JSONVec5Array(const Vec5&);
|
||||
};
|
||||
|
||||
struct JSONVec2Array : public JSONVec3Array
|
||||
{
|
||||
JSONVec2Array(const osg::Vec2&);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct JSONValue : public JSONObject
|
||||
{
|
||||
T _value;
|
||||
JSONValue(const T& v) {
|
||||
_value = v;
|
||||
}
|
||||
T& getValue() { return _value; }
|
||||
virtual void write(json_stream& str, WriteVisitor& visitor) {
|
||||
str << _value ;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
struct JSONValue<double> : public JSONObject
|
||||
{
|
||||
double _value;
|
||||
JSONValue(const double& v) {
|
||||
_value = v;
|
||||
}
|
||||
void write(json_stream& str, WriteVisitor& visitor) {
|
||||
if (osg::isNaN(_value)) {
|
||||
_value = 0.0;
|
||||
}
|
||||
str << _value;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct JSONValue<std::string> : public JSONObject
|
||||
{
|
||||
std::string _value;
|
||||
|
||||
JSONValue(const std::string& v) {
|
||||
_value = jsonEscape(v);
|
||||
}
|
||||
|
||||
void write(json_stream& str, WriteVisitor& visitor) {
|
||||
str << '"' << _value << '"';
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string jsonEscape(const std::string& input) {
|
||||
std::string value = input;
|
||||
replace(value, std::string("\\"), std::string("\\\\"));
|
||||
replace(value, std::string("\""), std::string("\\\""));
|
||||
replace(value, std::string("\b"), std::string("\\b"));
|
||||
replace(value, std::string("\f"), std::string("\\f"));
|
||||
replace(value, std::string("\n"), std::string("\\n"));
|
||||
replace(value, std::string("\r"), std::string("\\r"));
|
||||
replace(value, std::string("\t"), std::string("\\t"));
|
||||
return value;
|
||||
}
|
||||
|
||||
void replace(std::string& str, const std::string& from, const std::string& to) {
|
||||
if(from.empty())
|
||||
return;
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct JSONMatrix : public JSONArray
|
||||
{
|
||||
JSONMatrix(const osg::Matrix& matrix) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
_array.push_back(new JSONValue<double>(matrix.ptr()[i]));
|
||||
}
|
||||
}
|
||||
void write(json_stream& str, WriteVisitor& visitor);
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct JSONVertexArray : public JSONArray
|
||||
{
|
||||
osg::ref_ptr<const osg::Array> _arrayData;
|
||||
std::string _filename;
|
||||
|
||||
std::pair<unsigned int, unsigned int> writeMergeData(const osg::Array* array,
|
||||
WriteVisitor &visitor,
|
||||
const std::string& filename,
|
||||
std::string& encoding);
|
||||
|
||||
unsigned int writeData(const osg::Array* array, const std::string& filename)
|
||||
{
|
||||
std::ofstream myfile;
|
||||
myfile.open(filename.c_str(), std::ios::binary );
|
||||
const char* b = static_cast<const char*>(array->getDataPointer());
|
||||
myfile.write(b, array->getTotalDataSize());
|
||||
unsigned int fsize = myfile.tellp();
|
||||
myfile.close();
|
||||
return fsize;
|
||||
}
|
||||
|
||||
template <class T> void writeInlineArray(json_stream& str, unsigned int size, const T* array) {
|
||||
str << JSONObjectBase::indent() << "\"Elements\": [ " << array[0];
|
||||
for (unsigned int i = 1; i < size; i++) {
|
||||
T v = array[i];
|
||||
str << ", " << v;
|
||||
}
|
||||
str << " ]," << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename U> void writeInlineArray(json_stream& str, unsigned int size, const T* array) {
|
||||
str << JSONObjectBase::indent() << "\"Elements\": [ " << static_cast<U>(array[0]);
|
||||
for (unsigned int i = 1; i < size; i++) {
|
||||
str << ", " << static_cast<U>(array[i]);
|
||||
}
|
||||
str << " ]," << std::endl;
|
||||
}
|
||||
|
||||
template <class T> void writeInlineArrayReal(json_stream& str, unsigned int size, const T* array) {
|
||||
str << JSONObjectBase::indent() << "\"Elements\": [ " << array[0];
|
||||
for (unsigned int i = 1; i < size; i++) {
|
||||
float v = array[i];
|
||||
if (osg::isNaN(v))
|
||||
v = 0;
|
||||
str << ", " << v;
|
||||
}
|
||||
str << " ]," << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void write(json_stream& str, WriteVisitor& visitor);
|
||||
|
||||
JSONVertexArray() {}
|
||||
JSONVertexArray(const osg::Array* array) {
|
||||
_arrayData = array;
|
||||
}
|
||||
};
|
||||
|
||||
struct JSONBufferArray : public JSONObject
|
||||
{
|
||||
JSONBufferArray() {}
|
||||
JSONBufferArray(const osg::Array* array)
|
||||
{
|
||||
JSONVertexArray* b = new JSONVertexArray(array);
|
||||
getMaps()["Array"] = b;
|
||||
getMaps()["ItemSize"] = new JSONValue<int>(array->getDataSize());
|
||||
getMaps()["Type"] = new JSONValue<std::string>("ARRAY_BUFFER"); //0x8892);
|
||||
}
|
||||
|
||||
void setBufferName(const std::string& bufferName) {
|
||||
JSONObject::setBufferName(bufferName);
|
||||
getMaps()["Array"]->setBufferName(bufferName);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
JSONObject* getDrawMode(GLenum mode);
|
||||
|
||||
struct JSONDrawArray : public JSONObject
|
||||
{
|
||||
JSONDrawArray(osg::DrawArrays& array);
|
||||
};
|
||||
|
||||
struct JSONDrawArrayLengths : public JSONObject
|
||||
{
|
||||
JSONDrawArrayLengths(osg::DrawArrayLengths& array);
|
||||
|
||||
void setBufferName(const std::string& bufferName) {
|
||||
JSONObject::setBufferName(bufferName);
|
||||
getMaps()["ArrayLengths"]->setBufferName(bufferName);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct BufferArray
|
||||
{ typedef osg::UShortArray type; };
|
||||
|
||||
#define ADD_INDEX_BUFFER(T, B)\
|
||||
template<> \
|
||||
struct BufferArray<T> \
|
||||
{ typedef B type; }
|
||||
|
||||
ADD_INDEX_BUFFER(osg::DrawElementsUInt, osg::UIntArray);
|
||||
ADD_INDEX_BUFFER(osg::DrawElementsUByte, osg::UByteArray);
|
||||
|
||||
|
||||
template <class T>
|
||||
struct JSONDrawElements : public JSONObject
|
||||
{
|
||||
JSONDrawElements(T& array) {
|
||||
typedef typename BufferArray<T>::type array_type;
|
||||
typedef typename array_type::ElementDataType element_type;
|
||||
|
||||
JSONBufferArray* buf;
|
||||
|
||||
if (array.getMode() == GL_QUADS) {
|
||||
int size = array.getNumIndices();
|
||||
osg::ref_ptr<array_type> buffer = new array_type(size);
|
||||
unsigned int idx = 0;
|
||||
for (int i = 0; i < size/4; ++i) {
|
||||
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 0));
|
||||
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 1));
|
||||
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 3));
|
||||
|
||||
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 1));
|
||||
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 2));
|
||||
(*buffer)[idx++] = static_cast<element_type>(array.index(i*4 + 3));
|
||||
}
|
||||
buf = new JSONBufferArray(buffer.get());
|
||||
getMaps()["Mode"] = getDrawMode(osg::PrimitiveSet::TRIANGLES);
|
||||
}
|
||||
else {
|
||||
osg::ref_ptr<array_type> buffer = new array_type(array.getNumIndices());
|
||||
for(unsigned int i = 0 ; i < array.getNumIndices() ; ++ i)
|
||||
(*buffer)[i] = static_cast<element_type>(array.index(i));
|
||||
buf = new JSONBufferArray(buffer.get());
|
||||
getMaps()["Mode"] = getDrawMode(array.getMode());
|
||||
}
|
||||
|
||||
buf->getMaps()["Type"] = new JSONValue<std::string>("ELEMENT_ARRAY_BUFFER");
|
||||
getMaps()["Indices"] = buf;
|
||||
}
|
||||
|
||||
void setBufferName(const std::string& bufferName) {
|
||||
JSONObject::setBufferName(bufferName);
|
||||
getMaps()["Indices"]->setBufferName(bufferName);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
713
src/osgPlugins/osgjs/JSON_Objects.cpp
Normal file
713
src/osgPlugins/osgjs/JSON_Objects.cpp
Normal file
@@ -0,0 +1,713 @@
|
||||
/* -*-c++-*-
|
||||
* Copyright (C) 2010 Cedric Pinson <cedric.pinson@plopbyte.net>
|
||||
*/
|
||||
|
||||
#include "JSON_Objects"
|
||||
#include <osgDB/WriteFile>
|
||||
#include <osgDB/FileNameUtils>
|
||||
#include <osg/Material>
|
||||
#include <osg/BlendFunc>
|
||||
#include <osg/BlendColor>
|
||||
#include <osg/Texture>
|
||||
#include <osg/CullFace>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture1D>
|
||||
#include <osg/Image>
|
||||
#include <sstream>
|
||||
#include "WriteVisitor"
|
||||
|
||||
int JSONObjectBase::level = 0;
|
||||
unsigned int JSONObject::uniqueID = 0;
|
||||
|
||||
std::string JSONObjectBase::indent()
|
||||
{
|
||||
std::string str;
|
||||
for (int i = 0; i < JSONObjectBase::level; ++i) {
|
||||
str += " ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
void JSONMatrix::write(json_stream& str, WriteVisitor& visitor)
|
||||
{
|
||||
str << "[ ";
|
||||
for (unsigned int i = 0; i < _array.size(); i++) {
|
||||
_array[i]->write(str, visitor);
|
||||
if (i != _array.size() -1)
|
||||
str << ", ";
|
||||
}
|
||||
str << " ]";
|
||||
}
|
||||
|
||||
|
||||
void JSONNode::write(json_stream& str, WriteVisitor& visitor)
|
||||
{
|
||||
std::vector<std::string> order;
|
||||
order.push_back("UniqueID");
|
||||
order.push_back("Name");
|
||||
order.push_back("TargetName");
|
||||
order.push_back("Matrix");
|
||||
order.push_back("UpdateCallbacks");
|
||||
order.push_back("StateSet");
|
||||
writeOrder(str, order, visitor);
|
||||
}
|
||||
|
||||
JSONObject::JSONObject(const unsigned int id, const std::string& bufferName)
|
||||
{
|
||||
_uniqueID = id;
|
||||
_bufferName = bufferName;
|
||||
_maps["UniqueID"] = new JSONValue<unsigned int>(id);
|
||||
}
|
||||
|
||||
JSONObject::JSONObject()
|
||||
{
|
||||
_uniqueID = -1;
|
||||
}
|
||||
|
||||
void JSONObject::addUniqueID()
|
||||
{
|
||||
_uniqueID = JSONObject::uniqueID++;
|
||||
_maps["UniqueID"] = new JSONValue<unsigned int>(_uniqueID);
|
||||
}
|
||||
|
||||
void JSONObject::addChild(const std::string& type, JSONObject* child)
|
||||
{
|
||||
if (!getMaps()["Children"])
|
||||
getMaps()["Children"] = new JSONArray;
|
||||
|
||||
JSONObject* jsonObject = new JSONObject();
|
||||
jsonObject->getMaps()[type] = child;
|
||||
getMaps()["Children"]->asArray()->getArray().push_back(jsonObject);
|
||||
}
|
||||
|
||||
bool JSONObject::isVarintableIntegerBuffer(osg::Array const* array) const
|
||||
{
|
||||
// Return true for buffers representing integer values and that therefore
|
||||
// can be binary encoded compactly using the varint protocol.
|
||||
// Note: as Byte/UByte array are already compact we do not consider
|
||||
// them as compactable
|
||||
bool isInteger = false;
|
||||
switch(static_cast<int>(array->getType()))
|
||||
{
|
||||
case osg::Array::IntArrayType:
|
||||
isInteger = (dynamic_cast<osg::IntArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::ShortArrayType:
|
||||
isInteger = (dynamic_cast<osg::ShortArray const*>(array) != NULL);
|
||||
break;
|
||||
|
||||
case osg::Array::UIntArrayType:
|
||||
isInteger = (dynamic_cast<osg::UIntArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::UShortArrayType:
|
||||
isInteger = (dynamic_cast<osg::UShortArray const*>(array) != NULL);
|
||||
break;
|
||||
|
||||
case osg::Array::Vec2iArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec2iArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::Vec3iArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec3iArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::Vec4iArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec4iArray const*>(array) != NULL);
|
||||
break;
|
||||
|
||||
case osg::Array::Vec2uiArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec2uiArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::Vec3uiArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec3uiArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::Vec4uiArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec4uiArray const*>(array) != NULL);
|
||||
break;
|
||||
|
||||
case osg::Array::Vec2sArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec2sArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::Vec3sArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec3sArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::Vec4sArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec4sArray const*>(array) != NULL);
|
||||
break;
|
||||
|
||||
case osg::Array::Vec2usArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec2usArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::Vec3usArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec3usArray const*>(array) != NULL);
|
||||
break;
|
||||
case osg::Array::Vec4usArrayType:
|
||||
isInteger = (dynamic_cast<osg::Vec4usArray const*>(array) != NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
isInteger = false;
|
||||
break;
|
||||
}
|
||||
return isInteger;
|
||||
}
|
||||
|
||||
void JSONObject::encodeArrayAsVarintBuffer(osg::Array const* array, std::vector<uint8_t>& buffer) const
|
||||
{
|
||||
switch(static_cast<int>(array->getType()))
|
||||
{
|
||||
case osg::Array::IntArrayType:
|
||||
dumpVarintValue<osg::IntArray>(buffer, dynamic_cast<osg::IntArray const*>(array), false);
|
||||
break;
|
||||
case osg::Array::ShortArrayType:
|
||||
dumpVarintValue<osg::ShortArray>(buffer, dynamic_cast<osg::ShortArray const*>(array), false);
|
||||
break;
|
||||
|
||||
case osg::Array::UIntArrayType:
|
||||
dumpVarintValue<osg::UIntArray>(buffer, dynamic_cast<osg::UIntArray const*>(array), true);
|
||||
break;
|
||||
case osg::Array::UShortArrayType:
|
||||
dumpVarintValue<osg::UShortArray>(buffer, dynamic_cast<osg::UShortArray const*>(array), true);
|
||||
break;
|
||||
|
||||
case osg::Array::Vec2iArrayType:
|
||||
dumpVarintVector<osg::Vec2iArray>(buffer, dynamic_cast<osg::Vec2iArray const*>(array), false);
|
||||
break;
|
||||
case osg::Array::Vec3iArrayType:
|
||||
dumpVarintVector<osg::Vec3iArray>(buffer, dynamic_cast<osg::Vec3iArray const*>(array), false);
|
||||
break;
|
||||
case osg::Array::Vec4iArrayType:
|
||||
dumpVarintVector<osg::Vec4iArray>(buffer, dynamic_cast<osg::Vec4iArray const*>(array), false);
|
||||
break;
|
||||
|
||||
case osg::Array::Vec2sArrayType:
|
||||
dumpVarintVector<osg::Vec2sArray>(buffer, dynamic_cast<osg::Vec2sArray const*>(array), false);
|
||||
break;
|
||||
case osg::Array::Vec3sArrayType:
|
||||
dumpVarintVector<osg::Vec3sArray>(buffer, dynamic_cast<osg::Vec3sArray const*>(array), false);
|
||||
break;
|
||||
case osg::Array::Vec4sArrayType:
|
||||
dumpVarintVector<osg::Vec4sArray>(buffer, dynamic_cast<osg::Vec4sArray const*>(array), false);
|
||||
break;
|
||||
|
||||
|
||||
case osg::Array::Vec2uiArrayType:
|
||||
dumpVarintVector<osg::Vec2uiArray>(buffer, dynamic_cast<osg::Vec2uiArray const*>(array), true);
|
||||
break;
|
||||
case osg::Array::Vec3uiArrayType:
|
||||
dumpVarintVector<osg::Vec3uiArray>(buffer, dynamic_cast<osg::Vec3uiArray const*>(array), true);
|
||||
break;
|
||||
case osg::Array::Vec4uiArrayType:
|
||||
dumpVarintVector<osg::Vec4uiArray>(buffer, dynamic_cast<osg::Vec4uiArray const*>(array), true);
|
||||
break;
|
||||
|
||||
case osg::Array::Vec2usArrayType:
|
||||
dumpVarintVector<osg::Vec2usArray>(buffer, dynamic_cast<osg::Vec2usArray const*>(array), true);
|
||||
break;
|
||||
case osg::Array::Vec3usArrayType:
|
||||
dumpVarintVector<osg::Vec3usArray>(buffer, dynamic_cast<osg::Vec3usArray const*>(array), true);
|
||||
break;
|
||||
case osg::Array::Vec4usArrayType:
|
||||
dumpVarintVector<osg::Vec4usArray>(buffer, dynamic_cast<osg::Vec4usArray const*>(array), true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void JSONObject::dumpVarintVector(std::vector<uint8_t>& oss, T const* buffer, bool isUnsigned) const {
|
||||
unsigned int n = buffer->getDataSize();
|
||||
for(typename T::const_iterator it = buffer->begin() ; it != buffer->end() ; ++ it) {
|
||||
for(unsigned int i = 0 ; i < n ; ++ i) {
|
||||
unsigned int value = isUnsigned ? (*it)[i] : JSONObject::toVarintUnsigned((*it)[i]);
|
||||
|
||||
std::vector<uint8_t> encoding = varintEncoding(value);
|
||||
oss.insert(oss.end(), encoding.begin(), encoding.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void JSONObject::dumpVarintValue(std::vector<uint8_t>& oss, T const* buffer, bool isUnsigned) const {
|
||||
for(typename T::const_iterator it = buffer->begin() ; it != buffer->end() ; ++ it) {
|
||||
unsigned int value = isUnsigned ? (*it) : JSONObject::toVarintUnsigned(*it);
|
||||
|
||||
std::vector<uint8_t> encoding = varintEncoding(value);
|
||||
oss.insert(oss.end(), encoding.begin(), encoding.end());
|
||||
}
|
||||
}
|
||||
|
||||
// varint encoding adapted from http://stackoverflow.com/questions/19758270/read-varint-from-linux-sockets
|
||||
std::vector<uint8_t> JSONObject::varintEncoding(unsigned int value) const
|
||||
{
|
||||
std::vector<uint8_t> buffer;
|
||||
|
||||
do {
|
||||
uint8_t next_byte = value & 0x7F;
|
||||
value >>= 7;
|
||||
if (value) {
|
||||
next_byte |= 0x80;
|
||||
}
|
||||
buffer.push_back(next_byte);
|
||||
}
|
||||
while (value);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void writeEntry(json_stream& str, const std::string& key, JSONObject::JSONMap& map, WriteVisitor& visitor)
|
||||
{
|
||||
if (key.empty())
|
||||
return;
|
||||
|
||||
if ( map.find(key) != map.end() &&
|
||||
map[ key ].valid() ) {
|
||||
|
||||
str << JSONObjectBase::indent() << '"' << key << '"' << ": ";
|
||||
map[ key ]->write(str, visitor);
|
||||
map.erase(key);
|
||||
|
||||
if (!map.empty()) {
|
||||
str << ", ";
|
||||
str << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JSONObject::writeOrder(json_stream& str, const std::vector<std::string>& order, WriteVisitor& visitor)
|
||||
{
|
||||
str << "{" << std::endl;
|
||||
JSONObjectBase::level++;
|
||||
for (unsigned int i = 0; i < order.size(); i++) {
|
||||
writeEntry(str, order[i], _maps, visitor);
|
||||
}
|
||||
|
||||
while(!_maps.empty()) {
|
||||
std::string key = _maps.begin()->first;
|
||||
writeEntry(str, key, _maps, visitor);
|
||||
}
|
||||
|
||||
JSONObjectBase::level--;
|
||||
str << std::endl << JSONObjectBase::indent() << "}";
|
||||
}
|
||||
|
||||
void JSONObject::write(json_stream& str, WriteVisitor& visitor)
|
||||
{
|
||||
OrderList defaultOrder;
|
||||
defaultOrder.push_back("UniqueID");
|
||||
defaultOrder.push_back("Name");
|
||||
defaultOrder.push_back("TargetName");
|
||||
writeOrder(str, defaultOrder, visitor);
|
||||
}
|
||||
|
||||
|
||||
std::pair<unsigned int,unsigned int> JSONVertexArray::writeMergeData(const osg::Array* array,
|
||||
WriteVisitor &visitor,
|
||||
const std::string& filename,
|
||||
std::string& encoding)
|
||||
{
|
||||
std::ofstream& output = visitor.getBufferFile(filename);
|
||||
unsigned int offset = output.tellp();
|
||||
|
||||
if(visitor._varint && isVarintableIntegerBuffer(array))
|
||||
{
|
||||
std::vector<uint8_t> varintByteBuffer;
|
||||
encodeArrayAsVarintBuffer(array, varintByteBuffer);
|
||||
output.write((char*)&varintByteBuffer[0], varintByteBuffer.size() * sizeof(uint8_t));
|
||||
encoding = std::string("varint");
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* b = static_cast<const char*>(array->getDataPointer());
|
||||
size_t totalDataSize = array->getTotalDataSize();
|
||||
output.write(b, totalDataSize);
|
||||
}
|
||||
|
||||
unsigned int fsize = output.tellp();
|
||||
|
||||
// pad to 4 bytes
|
||||
unsigned int remainder = fsize % 4;
|
||||
if (remainder) {
|
||||
unsigned int null = 0;
|
||||
output.write((char*) (&null), 4 - remainder);
|
||||
fsize = output.tellp();
|
||||
}
|
||||
return std::pair<unsigned int, unsigned int>(offset, fsize - offset);
|
||||
}
|
||||
|
||||
void JSONVertexArray::write(json_stream& str, WriteVisitor& visitor)
|
||||
{
|
||||
bool _useExternalBinaryArray = visitor._useExternalBinaryArray;
|
||||
bool _mergeAllBinaryFiles = visitor._mergeAllBinaryFiles;
|
||||
std::string basename = visitor._baseName;
|
||||
|
||||
addUniqueID();
|
||||
|
||||
std::stringstream url;
|
||||
if (visitor._useExternalBinaryArray) {
|
||||
std::string bufferName = getBufferName();
|
||||
if(bufferName.empty())
|
||||
bufferName = visitor.getBinaryFilename();
|
||||
|
||||
if (visitor._mergeAllBinaryFiles)
|
||||
url << bufferName;
|
||||
else
|
||||
url << basename << "_" << _uniqueID << ".bin";
|
||||
}
|
||||
|
||||
std::string type;
|
||||
|
||||
osg::ref_ptr<const osg::Array> array = _arrayData;
|
||||
|
||||
switch (array->getType()) {
|
||||
case osg::Array::FloatArrayType:
|
||||
case osg::Array::Vec2ArrayType:
|
||||
case osg::Array::Vec3ArrayType:
|
||||
case osg::Array::Vec4ArrayType:
|
||||
type = "Float32Array";
|
||||
break;
|
||||
case osg::Array::Vec4ubArrayType:
|
||||
{
|
||||
osg::ref_ptr<osg::Vec4Array> converted = new osg::Vec4Array;
|
||||
converted->reserve(array->getNumElements());
|
||||
|
||||
const osg::Vec4ubArray* a = dynamic_cast<const osg::Vec4ubArray*>(array.get());
|
||||
for (unsigned int i = 0; i < a->getNumElements(); ++i) {
|
||||
converted->push_back(osg::Vec4( (*a)[i][0]/255.0,
|
||||
(*a)[i][1]/255.0,
|
||||
(*a)[i][2]/255.0,
|
||||
(*a)[i][3]/255.0));
|
||||
}
|
||||
array = converted;
|
||||
type = "Float32Array";
|
||||
}
|
||||
break;
|
||||
case osg::Array::UByteArrayType:
|
||||
case osg::Array::Vec2ubArrayType:
|
||||
case osg::Array::Vec3ubArrayType:
|
||||
type = "Uint8Array";
|
||||
break;
|
||||
case osg::Array::UShortArrayType:
|
||||
case osg::Array::Vec2usArrayType:
|
||||
case osg::Array::Vec3usArrayType:
|
||||
case osg::Array::Vec4usArrayType:
|
||||
type = "Uint16Array";
|
||||
break;
|
||||
case osg::Array::UIntArrayType:
|
||||
case osg::Array::Vec2uiArrayType:
|
||||
case osg::Array::Vec3uiArrayType:
|
||||
case osg::Array::Vec4uiArrayType:
|
||||
type = "Uint32Array";
|
||||
break;
|
||||
case osg::Array::ByteArrayType:
|
||||
case osg::Array::Vec2bArrayType:
|
||||
case osg::Array::Vec3bArrayType:
|
||||
case osg::Array::Vec4bArrayType:
|
||||
type = "Int8Array";
|
||||
break;
|
||||
case osg::Array::ShortArrayType:
|
||||
case osg::Array::Vec2sArrayType:
|
||||
case osg::Array::Vec3sArrayType:
|
||||
case osg::Array::Vec4sArrayType:
|
||||
type = "Int16Array";
|
||||
break;
|
||||
case osg::Array::IntArrayType:
|
||||
case osg::Array::Vec2iArrayType:
|
||||
case osg::Array::Vec3iArrayType:
|
||||
case osg::Array::Vec4iArrayType:
|
||||
type = "Int32Array";
|
||||
break;
|
||||
default:
|
||||
osg::notify(osg::WARN) << "Array of type " << array->getType() << " not supported" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
str << "{ " << std::endl;
|
||||
JSONObjectBase::level++;
|
||||
str << JSONObjectBase::indent() << "\"" << type << "\"" << ": { " << std::endl;
|
||||
JSONObjectBase::level++;
|
||||
if (_useExternalBinaryArray) {
|
||||
str << JSONObjectBase::indent() << "\"File\": \"" << osgDB::getSimpleFileName(url.str()) << "\","<< std::endl;
|
||||
} else {
|
||||
if (array->getNumElements() == 0) {
|
||||
str << JSONObjectBase::indent() << "\"Elements\": [ ],";
|
||||
|
||||
} else {
|
||||
|
||||
switch (array->getType()) {
|
||||
case osg::Array::FloatArrayType:
|
||||
case osg::Array::Vec2ArrayType:
|
||||
case osg::Array::Vec3ArrayType:
|
||||
case osg::Array::Vec4ArrayType:
|
||||
{
|
||||
const float* a = static_cast<const float*>(array->getDataPointer());
|
||||
unsigned int size = array->getNumElements() * array->getDataSize();
|
||||
writeInlineArrayReal<float>(str, size, a);
|
||||
}
|
||||
break;
|
||||
case osg::Array::DoubleArrayType:
|
||||
case osg::Array::Vec2dArrayType:
|
||||
case osg::Array::Vec3dArrayType:
|
||||
case osg::Array::Vec4dArrayType:
|
||||
{
|
||||
const double* a = static_cast<const double*>(array->getDataPointer());
|
||||
unsigned int size = array->getNumElements() * array->getDataSize();
|
||||
writeInlineArrayReal<double>(str, size, a);
|
||||
}
|
||||
break;
|
||||
case osg::Array::ByteArrayType:
|
||||
case osg::Array::Vec2bArrayType:
|
||||
case osg::Array::Vec3bArrayType:
|
||||
case osg::Array::Vec4bArrayType:
|
||||
{
|
||||
const char* a = static_cast<const char*>(array->getDataPointer());
|
||||
unsigned int size = array->getNumElements() * array->getDataSize();
|
||||
writeInlineArray<char, short>(str, size, a); // using short to write readable numbers and not `char`s
|
||||
}
|
||||
break;
|
||||
case osg::Array::UByteArrayType:
|
||||
case osg::Array::Vec2ubArrayType:
|
||||
case osg::Array::Vec3ubArrayType:
|
||||
case osg::Array::Vec4ubArrayType:
|
||||
{
|
||||
const unsigned char* a = static_cast<const unsigned char*>(array->getDataPointer());
|
||||
unsigned int size = array->getNumElements() * array->getDataSize();
|
||||
writeInlineArray<unsigned char, unsigned short>(str, size, a); // using short to write readable numbers and not `char`s
|
||||
}
|
||||
break;
|
||||
case osg::Array::ShortArrayType:
|
||||
case osg::Array::Vec2sArrayType:
|
||||
case osg::Array::Vec3sArrayType:
|
||||
case osg::Array::Vec4sArrayType:
|
||||
{
|
||||
const short* a = static_cast<const short*>(array->getDataPointer());
|
||||
unsigned int size = array->getNumElements() * array->getDataSize();
|
||||
writeInlineArray<short>(str, size, a);
|
||||
}
|
||||
break;
|
||||
case osg::Array::UShortArrayType:
|
||||
case osg::Array::Vec2usArrayType:
|
||||
case osg::Array::Vec3usArrayType:
|
||||
case osg::Array::Vec4usArrayType:
|
||||
{
|
||||
const unsigned short* a = static_cast<const unsigned short*>(array->getDataPointer());
|
||||
unsigned int size = array->getNumElements() * array->getDataSize();
|
||||
writeInlineArray<unsigned short>(str, size, a);
|
||||
}
|
||||
break;
|
||||
case osg::Array::IntArrayType:
|
||||
case osg::Array::Vec2iArrayType:
|
||||
case osg::Array::Vec3iArrayType:
|
||||
case osg::Array::Vec4iArrayType:
|
||||
{
|
||||
const int* a = static_cast<const int*>(array->getDataPointer());
|
||||
unsigned int size = array->getNumElements() * array->getDataSize();
|
||||
writeInlineArray<int>(str, size, a);
|
||||
}
|
||||
break;
|
||||
case osg::Array::UIntArrayType:
|
||||
case osg::Array::Vec2uiArrayType:
|
||||
case osg::Array::Vec3uiArrayType:
|
||||
case osg::Array::Vec4uiArrayType:
|
||||
{
|
||||
const unsigned int* a = static_cast<const unsigned int*>(array->getDataPointer());
|
||||
unsigned int size = array->getNumElements() * array->getDataSize();
|
||||
writeInlineArray<unsigned int>(str, size, a);
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::Array::MatrixdArrayType:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
str << JSONObjectBase::indent() << "\"Size\": " << array->getNumElements();
|
||||
if (_useExternalBinaryArray) {
|
||||
str << "," << std::endl;
|
||||
} else {
|
||||
str << std::endl;
|
||||
}
|
||||
|
||||
if (_useExternalBinaryArray) {
|
||||
unsigned int size;
|
||||
if (_mergeAllBinaryFiles) {
|
||||
std::pair<unsigned int, unsigned int> result;
|
||||
std::string encoding;
|
||||
result = writeMergeData(array.get(), visitor, url.str(), encoding);
|
||||
unsigned int offset = result.first;
|
||||
size = result.second;
|
||||
if(!encoding.empty()) {
|
||||
str << JSONObjectBase::indent() << "\"Offset\": " << offset << "," << std::endl;
|
||||
str << JSONObjectBase::indent() << "\"Encoding\": \"" << encoding << "\"" << std::endl;
|
||||
}
|
||||
else {
|
||||
str << JSONObjectBase::indent() << "\"Offset\": " << offset << std::endl;
|
||||
}
|
||||
} else {
|
||||
size = writeData(array.get(), url.str());
|
||||
str << JSONObjectBase::indent() << "\"Offset\": " << 0 << std::endl;
|
||||
}
|
||||
std::stringstream b;
|
||||
osg::notify(osg::NOTICE) << "TypedArray " << type << " " << url.str() << " ";
|
||||
if (size/1024.0 < 1.0) {
|
||||
osg::notify(osg::NOTICE) << size << " bytes" << std::endl;
|
||||
} else if (size/(1024.0*1024.0) < 1.0) {
|
||||
osg::notify(osg::NOTICE) << size/1024.0 << " kb" << std::endl;
|
||||
} else {
|
||||
osg::notify(osg::NOTICE) << size/(1024.0*1024.0) << " mb" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JSONObjectBase::level--;
|
||||
str << JSONObjectBase::indent() << "}" << std::endl;
|
||||
JSONObjectBase::level--;
|
||||
|
||||
str << JSONObjectBase::indent() << "}";
|
||||
}
|
||||
|
||||
|
||||
JSONVec4Array::JSONVec4Array(const osg::Vec4& v) : JSONVec3Array()
|
||||
{
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
_array.push_back(new JSONValue<float>(v[i]));
|
||||
}
|
||||
}
|
||||
|
||||
JSONVec5Array::JSONVec5Array(const Vec5& v) : JSONVec3Array()
|
||||
{
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
_array.push_back(new JSONValue<float>(v[i]));
|
||||
}
|
||||
}
|
||||
|
||||
JSONVec2Array::JSONVec2Array(const osg::Vec2& v) : JSONVec3Array()
|
||||
{
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
_array.push_back(new JSONValue<float>(v[i]));
|
||||
}
|
||||
}
|
||||
|
||||
JSONVec3Array::JSONVec3Array(const osg::Vec3& v)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
_array.push_back(new JSONValue<float>(v[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void JSONVec3Array::write(json_stream& str,WriteVisitor& visitor)
|
||||
{
|
||||
str << "[ ";
|
||||
for (unsigned int i = 0; i < _array.size(); i++) {
|
||||
if (_array[i].valid()) {
|
||||
_array[i]->write(str, visitor);
|
||||
} else {
|
||||
str << "undefined";
|
||||
}
|
||||
if (i != _array.size()-1)
|
||||
str << ", ";
|
||||
}
|
||||
str << "]";
|
||||
}
|
||||
|
||||
void JSONKeyframes::write(json_stream& str,WriteVisitor& visitor)
|
||||
{
|
||||
JSONObjectBase::level++;
|
||||
str << "[" << std::endl << JSONObjectBase::indent();
|
||||
for (unsigned int i = 0; i < _array.size(); i++) {
|
||||
if (_array[i].valid()) {
|
||||
_array[i]->write(str, visitor);
|
||||
} else {
|
||||
str << "undefined";
|
||||
}
|
||||
if (i != _array.size() -1) {
|
||||
str << ",";
|
||||
str << "\n" << JSONObjectBase::indent();
|
||||
}
|
||||
}
|
||||
str << " ]";
|
||||
JSONObjectBase::level--;
|
||||
}
|
||||
|
||||
|
||||
void JSONArray::write(json_stream& str,WriteVisitor& visitor)
|
||||
{
|
||||
str << "[ ";
|
||||
for (unsigned int i = 0; i < _array.size(); i++) {
|
||||
if (_array[i].valid()) {
|
||||
_array[i]->write(str, visitor);
|
||||
} else {
|
||||
str << "undefined";
|
||||
}
|
||||
if (i != _array.size() -1) {
|
||||
str << ",";
|
||||
str << "\n" << JSONObjectBase::indent();
|
||||
}
|
||||
}
|
||||
//str << std::endl << JSONObjectBase::indent() << "]";
|
||||
str << " ]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
JSONObject* getDrawMode(GLenum mode)
|
||||
{
|
||||
JSONObject* result = 0;
|
||||
switch (mode) {
|
||||
case GL_POINTS:
|
||||
result = new JSONValue<std::string>("POINTS");
|
||||
break;
|
||||
case GL_LINES:
|
||||
result = new JSONValue<std::string>("LINES");
|
||||
break;
|
||||
case GL_LINE_LOOP:
|
||||
result = new JSONValue<std::string>("LINE_LOOP");
|
||||
break;
|
||||
case GL_LINE_STRIP:
|
||||
result = new JSONValue<std::string>("LINE_STRIP");
|
||||
break;
|
||||
case GL_TRIANGLES:
|
||||
result = new JSONValue<std::string>("TRIANGLES");
|
||||
break;
|
||||
case GL_POLYGON:
|
||||
result = new JSONValue<std::string>("TRIANGLE_FAN");
|
||||
break;
|
||||
case GL_QUAD_STRIP:
|
||||
case GL_TRIANGLE_STRIP:
|
||||
result = new JSONValue<std::string>("TRIANGLE_STRIP");
|
||||
break;
|
||||
case GL_TRIANGLE_FAN:
|
||||
result = new JSONValue<std::string>("TRIANGLE_FAN");
|
||||
break;
|
||||
case GL_QUADS:
|
||||
osg::notify(osg::WARN) << "exporting quads will not be able to work on opengl es" << std::endl;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JSONDrawArray::JSONDrawArray(osg::DrawArrays& array)
|
||||
{
|
||||
getMaps()["First"] = new JSONValue<int>(array.getFirst());
|
||||
getMaps()["Count"] = new JSONValue<int>(array.getCount());
|
||||
getMaps()["Mode"] = getDrawMode(array.getMode());
|
||||
}
|
||||
|
||||
|
||||
JSONDrawArrayLengths::JSONDrawArrayLengths(osg::DrawArrayLengths& array)
|
||||
{
|
||||
getMaps()["First"] = new JSONValue<int>(array.getFirst());
|
||||
getMaps()["Mode"] = getDrawMode(array.getMode());
|
||||
|
||||
JSONArray* jsonArray = new JSONArray;
|
||||
for (unsigned int i = 0; i < array.size(); i++) {
|
||||
jsonArray->getArray().push_back(new JSONValue<int>(array[i]));
|
||||
}
|
||||
getMaps()["ArrayLengths"] = jsonArray;
|
||||
}
|
||||
243
src/osgPlugins/osgjs/ReaderWriterJSON.cpp
Normal file
243
src/osgPlugins/osgjs/ReaderWriterJSON.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
// copyright: 'Cedric Pinson cedric@plopbyte.com'
|
||||
#include <osg/Image>
|
||||
#include <osg/Notify>
|
||||
#include <osg/Geode>
|
||||
#include <osg/GL>
|
||||
#include <osg/Version>
|
||||
#include <osg/Endian>
|
||||
#include <osg/Projection>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
|
||||
#include <osgUtil/UpdateVisitor>
|
||||
#include <osgDB/ReaderWriter>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/WriteFile>
|
||||
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
#include <osgAnimation/UpdateMatrixTransform>
|
||||
#include <osgAnimation/AnimationManagerBase>
|
||||
#include <osgAnimation/BasicAnimationManager>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "json_stream"
|
||||
#include "JSON_Objects"
|
||||
#include "Animation"
|
||||
#include "CompactBufferVisitor"
|
||||
#include "WriteVisitor"
|
||||
|
||||
|
||||
|
||||
using namespace osg;
|
||||
|
||||
|
||||
class ReaderWriterJSON : public osgDB::ReaderWriter
|
||||
{
|
||||
public:
|
||||
|
||||
struct OptionsStruct {
|
||||
int resizeTextureUpToPowerOf2;
|
||||
bool useExternalBinaryArray;
|
||||
bool mergeAllBinaryFiles;
|
||||
bool disableCompactBuffer;
|
||||
bool inlineImages;
|
||||
bool varint;
|
||||
bool strictJson;
|
||||
std::vector<std::string> useSpecificBuffer;
|
||||
|
||||
OptionsStruct() {
|
||||
resizeTextureUpToPowerOf2 = 0;
|
||||
useExternalBinaryArray = false;
|
||||
mergeAllBinaryFiles = false;
|
||||
disableCompactBuffer = false;
|
||||
inlineImages = false;
|
||||
varint = false;
|
||||
strictJson = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
ReaderWriterJSON()
|
||||
{
|
||||
supportsExtension("osgjs","OpenSceneGraph Javascript implementation format");
|
||||
supportsOption("resizeTextureUpToPowerOf2=<int>","Specify the maximum power of 2 allowed dimension for texture. Using 0 will disable the functionality and no image resizing will occur.");
|
||||
supportsOption("useExternalBinaryArray","create binary files for vertex arrays");
|
||||
supportsOption("mergeAllBinaryFiles","merge all binary files into one to avoid multi request on a server");
|
||||
supportsOption("inlineImages","insert base64 encoded images instead of referring to them");
|
||||
supportsOption("varint","Use varint encoding to serialize integer buffers");
|
||||
supportsOption("useSpecificBuffer=uservalue1,uservalue2","uses specific buffers for unshared buffers attached to geometries having a specified user value");
|
||||
supportsOption("disableCompactBuffer","keep source types and do not try to optimize buffers size");
|
||||
supportsOption("disableStrictJson","do not clean string (to utf8) or floating point (should be finite) values");
|
||||
}
|
||||
|
||||
virtual const char* className() const { return "OSGJS json Writer"; }
|
||||
|
||||
virtual ReadResult readNode(const std::string& fileName, const Options* options) const;
|
||||
|
||||
virtual WriteResult writeNode(const Node& node,
|
||||
const std::string& fileName,
|
||||
const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
std::string ext = osgDB::getFileExtension(fileName);
|
||||
if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
|
||||
|
||||
OptionsStruct _options = parseOptions(options);
|
||||
json_stream fout(fileName, _options.strictJson);
|
||||
|
||||
if(fout) {
|
||||
WriteResult res = writeNodeModel(node, fout, osgDB::getNameLessExtension(fileName), _options);
|
||||
return res;
|
||||
}
|
||||
return WriteResult("Unable to open file for output");
|
||||
}
|
||||
|
||||
virtual WriteResult writeNode(const Node& node,
|
||||
json_stream& fout,
|
||||
const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
if (!fout) {
|
||||
return WriteResult("Unable to write to output stream");
|
||||
}
|
||||
|
||||
OptionsStruct _options;
|
||||
_options = parseOptions(options);
|
||||
return writeNodeModel(node, fout, "stream", _options);
|
||||
}
|
||||
|
||||
virtual WriteResult writeNodeModel(const Node& node, json_stream& fout, const std::string& basename, const OptionsStruct& options) const
|
||||
{
|
||||
// process regular model
|
||||
osg::ref_ptr<osg::Node> model = osg::clone(&node);
|
||||
|
||||
if(!options.disableCompactBuffer) {
|
||||
CompactBufferVisitor compact;
|
||||
model->accept(compact);
|
||||
}
|
||||
|
||||
WriteVisitor writer;
|
||||
try {
|
||||
//osgDB::writeNodeFile(*model, "/tmp/debug_osgjs.osg");
|
||||
writer.setBaseName(basename);
|
||||
writer.useExternalBinaryArray(options.useExternalBinaryArray);
|
||||
writer.mergeAllBinaryFiles(options.mergeAllBinaryFiles);
|
||||
writer.inlineImages(options.inlineImages);
|
||||
writer.setMaxTextureDimension(options.resizeTextureUpToPowerOf2);
|
||||
writer.setVarint(options.varint);
|
||||
for(std::vector<std::string>::const_iterator specificBuffer = options.useSpecificBuffer.begin() ;
|
||||
specificBuffer != options.useSpecificBuffer.end() ; ++ specificBuffer) {
|
||||
writer.addSpecificBuffer(*specificBuffer);
|
||||
}
|
||||
model->accept(writer);
|
||||
if (writer._root.valid()) {
|
||||
writer.write(fout);
|
||||
return WriteResult::FILE_SAVED;
|
||||
}
|
||||
} catch (...) {
|
||||
osg::notify(osg::FATAL) << "can't save osgjs file" << std::endl;
|
||||
return WriteResult("Unable to write to output stream");
|
||||
}
|
||||
return WriteResult("Unable to write to output stream");
|
||||
}
|
||||
|
||||
ReaderWriterJSON::OptionsStruct parseOptions(const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
OptionsStruct localOptions;
|
||||
|
||||
if (options)
|
||||
{
|
||||
osg::notify(NOTICE) << "options " << options->getOptionString() << std::endl;
|
||||
std::istringstream iss(options->getOptionString());
|
||||
std::string opt;
|
||||
while (iss >> opt)
|
||||
{
|
||||
// split opt into pre= and post=
|
||||
std::string pre_equals;
|
||||
std::string post_equals;
|
||||
|
||||
size_t found = opt.find("=");
|
||||
if(found!=std::string::npos)
|
||||
{
|
||||
pre_equals = opt.substr(0,found);
|
||||
post_equals = opt.substr(found+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pre_equals = opt;
|
||||
}
|
||||
|
||||
if (pre_equals == "useExternalBinaryArray")
|
||||
{
|
||||
localOptions.useExternalBinaryArray = true;
|
||||
}
|
||||
if (pre_equals == "mergeAllBinaryFiles")
|
||||
{
|
||||
localOptions.mergeAllBinaryFiles = true;
|
||||
}
|
||||
if (pre_equals == "disableCompactBuffer")
|
||||
{
|
||||
localOptions.disableCompactBuffer = true;
|
||||
}
|
||||
if (pre_equals == "disableStrictJson")
|
||||
{
|
||||
localOptions.strictJson = false;
|
||||
}
|
||||
|
||||
|
||||
if (pre_equals == "inlineImages")
|
||||
{
|
||||
localOptions.inlineImages = true;
|
||||
}
|
||||
if (pre_equals == "varint")
|
||||
{
|
||||
localOptions.varint = true;
|
||||
}
|
||||
|
||||
if (pre_equals == "resizeTextureUpToPowerOf2" && post_equals.length() > 0)
|
||||
{
|
||||
int value = atoi(post_equals.c_str());
|
||||
localOptions.resizeTextureUpToPowerOf2 = osg::Image::computeNearestPowerOfTwo(value);
|
||||
}
|
||||
|
||||
if (pre_equals == "useSpecificBuffer" && !post_equals.empty())
|
||||
{
|
||||
size_t stop_pos = 0, start_pos = 0;
|
||||
while((stop_pos = post_equals.find(",", start_pos)) != std::string::npos) {
|
||||
localOptions.useSpecificBuffer.push_back(post_equals.substr(start_pos,
|
||||
stop_pos - start_pos));
|
||||
start_pos = stop_pos + 1;
|
||||
++ stop_pos;
|
||||
}
|
||||
localOptions.useSpecificBuffer.push_back(post_equals.substr(start_pos,
|
||||
post_equals.length() - start_pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
return localOptions;
|
||||
}
|
||||
};
|
||||
|
||||
osgDB::ReaderWriter::ReadResult ReaderWriterJSON::readNode(const std::string& file, const Options* options) const
|
||||
{
|
||||
std::string ext = osgDB::getLowerCaseFileExtension(file);
|
||||
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
|
||||
|
||||
// strip the pseudo-loader extension
|
||||
std::string fileName = osgDB::getNameLessExtension( file );
|
||||
|
||||
fileName = osgDB::findDataFile( fileName, options );
|
||||
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
|
||||
|
||||
osg::Node *node = osgDB::readNodeFile( fileName, options );
|
||||
if (!node)
|
||||
return ReadResult::FILE_NOT_HANDLED;
|
||||
|
||||
return ReadResult::FILE_NOT_HANDLED;
|
||||
}
|
||||
|
||||
// now register with Registry to instantiate the above
|
||||
// reader/writer.
|
||||
REGISTER_OSGPLUGIN(osgjs, ReaderWriterJSON)
|
||||
451
src/osgPlugins/osgjs/WriteVisitor
Normal file
451
src/osgPlugins/osgjs/WriteVisitor
Normal file
@@ -0,0 +1,451 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) 2012 Cedric Pinson */
|
||||
#ifndef WRITE_VISITOR_H
|
||||
#define WRITE_VISITOR_H
|
||||
|
||||
#include <osg/Image>
|
||||
#include <osg/Notify>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/GL>
|
||||
#include <osg/Version>
|
||||
#include <osg/Endian>
|
||||
#include <osg/Projection>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/PagedLOD>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osgAnimation/BasicAnimationManager>
|
||||
#include <osg/LightSource>
|
||||
#include <osg/CullFace>
|
||||
#include <osg/Material>
|
||||
#include <osg/BlendColor>
|
||||
#include <osg/BlendFunc>
|
||||
#include <osg/ValueObject>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "JSON_Objects"
|
||||
#include "Animation"
|
||||
#include "json_stream"
|
||||
|
||||
|
||||
#define WRITER_VERSION 7
|
||||
|
||||
|
||||
osg::Array* getTangentSpaceArray(osg::Geometry& geometry);
|
||||
void translateObject(JSONObject* json, osg::Object* osg);
|
||||
void getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value);
|
||||
template<typename T>
|
||||
bool getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value);
|
||||
|
||||
|
||||
class WriteVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
typedef std::vector<osg::ref_ptr<osg::StateSet> > StateSetStack;
|
||||
|
||||
std::map<osg::ref_ptr<osg::Object>, osg::ref_ptr<JSONObject> > _maps;
|
||||
std::vector<osg::ref_ptr<JSONObject> > _parents;
|
||||
osg::ref_ptr<JSONObject> _root;
|
||||
StateSetStack _stateset;
|
||||
std::string _baseName;
|
||||
bool _useExternalBinaryArray;
|
||||
bool _mergeAllBinaryFiles;
|
||||
bool _inlineImages;
|
||||
bool _varint;
|
||||
int _maxTextureDimension;
|
||||
std::vector<std::string> _specificBuffers;
|
||||
std::map<std::string, std::ofstream*> _buffers;
|
||||
|
||||
std::ofstream& getBufferFile(const std::string& name) {
|
||||
if(_buffers.find(name) == _buffers.end()) {
|
||||
_buffers[name] = new std::ofstream(name.c_str(), std::ios::binary);
|
||||
}
|
||||
return *_buffers[name];
|
||||
}
|
||||
|
||||
WriteVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {
|
||||
_mergeAllBinaryFiles = false;
|
||||
_useExternalBinaryArray = false;
|
||||
_inlineImages = false;
|
||||
_maxTextureDimension = 0;
|
||||
_varint = false;
|
||||
}
|
||||
|
||||
~WriteVisitor() {
|
||||
for(std::map<std::string, std::ofstream*>::iterator buffer = _buffers.begin() ;
|
||||
buffer != _buffers.end() ; ++ buffer) {
|
||||
delete buffer->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::string getBinaryFilename(const std::string& buffer = "") {
|
||||
std::string suffix;
|
||||
if(!buffer.empty()) {
|
||||
suffix = "_" + buffer;
|
||||
}
|
||||
return std::string(_baseName) + suffix + ".bin";
|
||||
}
|
||||
|
||||
void closeBuffers() {
|
||||
for(std::map<std::string, std::ofstream*>::iterator buffer = _buffers.begin() ;
|
||||
buffer != _buffers.end() ; ++ buffer) {
|
||||
buffer->second->close();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getBuffersSize() {
|
||||
unsigned int size = 0;
|
||||
for(std::map<std::string, std::ofstream*>::iterator buffer = _buffers.begin() ;
|
||||
buffer != _buffers.end() ; ++ buffer) {
|
||||
size += buffer->second->tellp();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void write(json_stream& str) {
|
||||
osg::ref_ptr<JSONObject> o = new JSONObject();
|
||||
o->getMaps()["Version"] = new JSONValue<int>(WRITER_VERSION);
|
||||
o->getMaps()["Generator"] = new JSONValue<std::string>("OpenSceneGraph " + std::string(osgGetVersion()) );
|
||||
o->getMaps()["osg.Node"] = _root.get();
|
||||
o->write(str, *this);
|
||||
if (_mergeAllBinaryFiles) {
|
||||
closeBuffers();
|
||||
unsigned int size = getBuffersSize();
|
||||
osg::notify(osg::NOTICE) << "Use a merged binary file ";
|
||||
if (size/1024.0 < 1.0) {
|
||||
osg::notify(osg::NOTICE) << size << " bytes" << std::endl;
|
||||
} else if (size/(1024.0*1024.0) < 1.0) {
|
||||
osg::notify(osg::NOTICE) << size/1024.0 << " kb" << std::endl;
|
||||
} else {
|
||||
osg::notify(osg::NOTICE) << size/(1024.0*1024.0) << " mb" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void error() {
|
||||
throw "Error occur";
|
||||
}
|
||||
|
||||
void setBufferName(JSONObject *json, osg::Geometry* geometry) {
|
||||
if(!_mergeAllBinaryFiles || _specificBuffers.empty())
|
||||
return;
|
||||
std::string bufferName = getBufferName(geometry);
|
||||
std::string defaultBufferName = getBinaryFilename();
|
||||
std::string jsonBufferName = json->getBufferName();
|
||||
// if the buffer is shared we will always favor dumping it in the default
|
||||
// buffer and otherwise we keep the first buffer name set.
|
||||
if(!jsonBufferName.empty()) {
|
||||
if(jsonBufferName != defaultBufferName && bufferName == defaultBufferName) {
|
||||
json->setBufferName(defaultBufferName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
json->setBufferName(bufferName);
|
||||
}
|
||||
}
|
||||
|
||||
std::string getBufferName(osg::Geometry* geometry) {
|
||||
std::string name("");
|
||||
bool isSpecific = false;
|
||||
for(std::vector<std::string>::iterator it_flag = _specificBuffers.begin() ;
|
||||
it_flag != _specificBuffers.end() ; ++ it_flag) {
|
||||
if(geometry->getUserValue(*it_flag, isSpecific) && isSpecific) {
|
||||
name = *it_flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return getBinaryFilename(name);
|
||||
}
|
||||
|
||||
JSONObject* createJSONPagedLOD(osg::PagedLOD* plod);
|
||||
JSONObject* createJSONStateSet(osg::StateSet* ss);
|
||||
JSONObject* createJSONTexture(osg::Texture* sa);
|
||||
JSONObject* createJSONMaterial(osg::Material* sa);
|
||||
JSONObject* createJSONLight(osg::Light* sa);
|
||||
JSONObject* createJSONCullFace(osg::CullFace* sa);
|
||||
JSONObject* createJSONBlendColor(osg::BlendColor* sa);
|
||||
JSONObject* createJSONBlendFunc(osg::BlendFunc* sa);
|
||||
|
||||
JSONObject* createJSONBufferArray(osg::Array* array, osg::Geometry* geom = 0);
|
||||
JSONObject* createJSONDrawElements(osg::DrawArrays* drawArray, osg::Geometry* geom = 0);
|
||||
|
||||
JSONObject* createJSONDrawElementsUInt(osg::DrawElementsUInt* de, osg::Geometry* geom = 0);
|
||||
JSONObject* createJSONDrawElementsUShort(osg::DrawElementsUShort* de, osg::Geometry* geom = 0);
|
||||
JSONObject* createJSONDrawElementsUByte(osg::DrawElementsUByte* de, osg::Geometry* geom = 0);
|
||||
|
||||
JSONObject* createJSONDrawArray(osg::DrawArrays* drawArray, osg::Geometry* geom = 0);
|
||||
JSONObject* createJSONDrawArrayLengths(osg::DrawArrayLengths* drawArray, osg::Geometry* geom = 0);
|
||||
|
||||
JSONObject* createJSONGeometry(osg::Geometry* geom);
|
||||
|
||||
JSONObject* getParent() {
|
||||
if (_parents.empty()) {
|
||||
_root = new JSONObject;
|
||||
_parents.push_back(_root.get());
|
||||
}
|
||||
return _parents.back().get();
|
||||
}
|
||||
|
||||
void initJsonObjectFromNode(osg::Node& node, JSONObject& json) {
|
||||
translateObject(&json, &node);
|
||||
}
|
||||
|
||||
|
||||
void createJSONStateSet(JSONObject* json, osg::StateSet* ss) {
|
||||
JSONObject* json_stateset = createJSONStateSet(ss);
|
||||
if (json_stateset) {
|
||||
JSONObject* obj = new JSONObject;
|
||||
obj->getMaps()["osg.StateSet"] = json_stateset;
|
||||
json->getMaps()["StateSet"] = obj;
|
||||
|
||||
}
|
||||
}
|
||||
void createJSONStateSet(osg::Node& node, JSONObject* json) {
|
||||
if (node.getStateSet()) {
|
||||
createJSONStateSet(json, node.getStateSet());
|
||||
}
|
||||
}
|
||||
|
||||
void applyCallback(osg::Node& node, JSONObject* json) {
|
||||
JSONArray* updateCallbacks = new JSONArray;
|
||||
osg::Callback* nc = node.getUpdateCallback();
|
||||
while (nc) {
|
||||
osgAnimation::BasicAnimationManager* am = dynamic_cast<osgAnimation::BasicAnimationManager*>(nc);
|
||||
if (am) {
|
||||
JSONArray* array = new JSONArray;
|
||||
JSONObject* bam = new JSONObject;
|
||||
bam->getMaps()["Animations"] = array;
|
||||
|
||||
|
||||
JSONObject* nodeCallbackObject = new JSONObject;
|
||||
nodeCallbackObject->getMaps()["osgAnimation.BasicAnimationManager"] = bam;
|
||||
updateCallbacks->getArray().push_back(nodeCallbackObject);
|
||||
|
||||
for ( unsigned int i = 0; i < am->getAnimationList().size(); i++) {
|
||||
osg::ref_ptr<JSONObject> jsonAnim = createJSONAnimation(am->getAnimationList()[i].get());
|
||||
if (jsonAnim) {
|
||||
JSONObject* obj = new JSONObject;
|
||||
obj->getMaps()["osgAnimation.Animation"] = jsonAnim;
|
||||
array->getArray().push_back(obj);
|
||||
//std::stringstream ss;
|
||||
//jsonAnim->write(ss);
|
||||
//std::cout << ss.str() << std::endl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
osgAnimation::UpdateMatrixTransform* updateMT = dynamic_cast<osgAnimation::UpdateMatrixTransform*>(nc);
|
||||
if (updateMT) {
|
||||
osg::ref_ptr<JSONObject> jsonCallback = createJSONUpdateMatrixTransform(*updateMT);
|
||||
if (jsonCallback.valid()) {
|
||||
osg::ref_ptr<JSONObject> jsonObject = new JSONObject;
|
||||
jsonObject->getMaps()["osgAnimation.UpdateMatrixTransform"] = jsonCallback;
|
||||
updateCallbacks->getArray().push_back(jsonObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
nc = nc->getNestedCallback();
|
||||
}
|
||||
|
||||
if (!updateCallbacks->getArray().empty()) {
|
||||
json->getMaps()["UpdateCallbacks"] = updateCallbacks;
|
||||
}
|
||||
}
|
||||
|
||||
void apply(osg::Drawable& drw) {
|
||||
osg::Geometry* geom = dynamic_cast<osg::Geometry*>(&drw);
|
||||
if (geom) {
|
||||
JSONObject* json = createJSONGeometry(geom);
|
||||
JSONObject* parent = getParent();
|
||||
parent->addChild("osg.Geometry", json);
|
||||
}
|
||||
}
|
||||
|
||||
void apply(osg::Geode& node) {
|
||||
|
||||
JSONObject* parent = getParent();
|
||||
if (_maps.find(&node) != _maps.end()) {
|
||||
parent->addChild("osg.Node", _maps[&node]->getShadowObject());
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONNode;
|
||||
json->addUniqueID();
|
||||
_maps[&node] = json;
|
||||
|
||||
applyCallback(node, json.get());
|
||||
createJSONStateSet(node, json.get());
|
||||
|
||||
parent->addChild("osg.Node", json.get());
|
||||
initJsonObjectFromNode(node, *json);
|
||||
_parents.push_back(json);
|
||||
for (unsigned int i = 0; i < node.getNumDrawables(); ++i) {
|
||||
if (node.getDrawable(i))
|
||||
apply(*node.getDrawable(i));
|
||||
}
|
||||
_parents.pop_back();
|
||||
}
|
||||
|
||||
void apply(osg::Group& node) {
|
||||
|
||||
JSONObject* parent = getParent();
|
||||
if (_maps.find(&node) != _maps.end()) {
|
||||
parent->addChild("osg.Node", _maps[&node]->getShadowObject());
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONNode;
|
||||
json->addUniqueID();
|
||||
_maps[&node] = json;
|
||||
parent->addChild("osg.Node", json.get());
|
||||
|
||||
applyCallback(node, json.get());
|
||||
createJSONStateSet(node, json.get());
|
||||
|
||||
initJsonObjectFromNode(node, *json);
|
||||
|
||||
_parents.push_back(json);
|
||||
traverse(node);
|
||||
_parents.pop_back();
|
||||
}
|
||||
|
||||
void apply(osg::PagedLOD& node)
|
||||
{
|
||||
JSONObject* parent = getParent();
|
||||
if (_maps.find(&node) != _maps.end()) {
|
||||
parent->addChild("osg.PagedLOD", _maps[&node]->getShadowObject());
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> json = createJSONPagedLOD(&node);
|
||||
json->addUniqueID();
|
||||
_maps[&node] = json;
|
||||
parent->addChild("osg.PagedLOD", json.get());
|
||||
|
||||
|
||||
applyCallback(node, json.get());
|
||||
|
||||
|
||||
createJSONStateSet(node, json.get());
|
||||
|
||||
initJsonObjectFromNode(node, *json);
|
||||
_parents.push_back(json);
|
||||
traverse(node);
|
||||
_parents.pop_back();
|
||||
}
|
||||
|
||||
void apply(osg::LightSource& node) {
|
||||
|
||||
JSONObject* parent = getParent();
|
||||
if (_maps.find(&node) != _maps.end()) {
|
||||
parent->addChild("osg.LightSource", _maps[&node]->getShadowObject());
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONNode;
|
||||
json->addUniqueID();
|
||||
_maps[&node] = json;
|
||||
|
||||
applyCallback(node, json.get());
|
||||
createJSONStateSet(node, json.get());
|
||||
|
||||
parent->addChild("osg.LightSource", json.get());
|
||||
|
||||
initJsonObjectFromNode(node, *json);
|
||||
|
||||
if (node.getLight()) {
|
||||
JSONObject* obj = new JSONObject;
|
||||
obj->getMaps()["osg.Light"] = createJSONLight(node.getLight());
|
||||
json->getMaps()["Light"] = obj;
|
||||
}
|
||||
|
||||
_parents.push_back(json);
|
||||
traverse(node);
|
||||
_parents.pop_back();
|
||||
}
|
||||
|
||||
void apply(osg::Projection& node) {
|
||||
JSONObject* parent = getParent();
|
||||
if (_maps.find(&node) != _maps.end()) {
|
||||
parent->addChild("osg.Projection", _maps[&node]->getShadowObject());
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONNode;
|
||||
json->addUniqueID();
|
||||
_maps[&node] = json;
|
||||
|
||||
applyCallback(node, json.get());
|
||||
createJSONStateSet(node, json.get());
|
||||
|
||||
parent->addChild("osg.Projection", json.get());
|
||||
|
||||
initJsonObjectFromNode(node, *json);
|
||||
json->getMaps()["Matrix"] = new JSONMatrix(node.getMatrix());
|
||||
_parents.push_back(json);
|
||||
traverse(node);
|
||||
_parents.pop_back();
|
||||
}
|
||||
|
||||
void apply(osg::MatrixTransform& node) {
|
||||
JSONObject* parent = getParent();
|
||||
if (_maps.find(&node) != _maps.end()) {
|
||||
parent->addChild("osg.MatrixTransform", _maps[&node]->getShadowObject());
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONNode;
|
||||
json->addUniqueID();
|
||||
_maps[&node] = json;
|
||||
|
||||
applyCallback(node, json.get());
|
||||
createJSONStateSet(node, json.get());
|
||||
|
||||
parent->addChild("osg.MatrixTransform", json.get());
|
||||
|
||||
initJsonObjectFromNode(node, *json);
|
||||
json->getMaps()["Matrix"] = new JSONMatrix(node.getMatrix());
|
||||
_parents.push_back(json);
|
||||
traverse(node);
|
||||
_parents.pop_back();
|
||||
}
|
||||
|
||||
void apply(osg::PositionAttitudeTransform& node)
|
||||
{
|
||||
JSONObject* parent = getParent();
|
||||
if (_maps.find(&node) != _maps.end()) {
|
||||
parent->addChild("osg.MatrixTransform", _maps[&node]->getShadowObject());
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONNode;
|
||||
json->addUniqueID();
|
||||
_maps[&node] = json;
|
||||
|
||||
applyCallback(node, json.get());
|
||||
createJSONStateSet(node, json.get());
|
||||
|
||||
parent->addChild("osg.MatrixTransform", json.get());
|
||||
|
||||
initJsonObjectFromNode(node, *json);
|
||||
osg::Matrix matrix = osg::Matrix::identity();
|
||||
node.computeLocalToWorldMatrix(matrix,0);
|
||||
json->getMaps()["Matrix"] = new JSONMatrix(matrix);
|
||||
_parents.push_back(json);
|
||||
traverse(node);
|
||||
_parents.pop_back();
|
||||
}
|
||||
|
||||
void setBaseName(const std::string& basename) { _baseName = basename; }
|
||||
void useExternalBinaryArray(bool use) { _useExternalBinaryArray = use; }
|
||||
void mergeAllBinaryFiles(bool use) { _mergeAllBinaryFiles = use; }
|
||||
void inlineImages(bool use) { _inlineImages = use; }
|
||||
void setVarint(bool use) { _varint = use; }
|
||||
void setMaxTextureDimension(int use) { _maxTextureDimension = use; }
|
||||
void addSpecificBuffer(const std::string& bufferFlag) { _specificBuffers.push_back(bufferFlag); }
|
||||
};
|
||||
|
||||
#endif
|
||||
829
src/osgPlugins/osgjs/WriteVisitor.cpp
Normal file
829
src/osgPlugins/osgjs/WriteVisitor.cpp
Normal file
@@ -0,0 +1,829 @@
|
||||
#include "WriteVisitor"
|
||||
#include <osgDB/WriteFile>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/FileNameUtils>
|
||||
#include <osg/UserDataContainer>
|
||||
#include <osg/TextureRectangle>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture1D>
|
||||
#include <osg/Material>
|
||||
#include <osg/BlendFunc>
|
||||
#include <osgSim/ShapeAttribute>
|
||||
|
||||
#include "Base64"
|
||||
|
||||
|
||||
|
||||
osg::Array* getTangentSpaceArray(osg::Geometry& geometry) {
|
||||
for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) {
|
||||
osg::Array* attribute = geometry.getVertexAttribArray(i);
|
||||
bool isTangentArray = false;
|
||||
if(attribute && attribute->getUserValue("tangent", isTangentArray) && isTangentArray) {
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void translateObject(JSONObject* json, osg::Object* osg)
|
||||
{
|
||||
if (!osg->getName().empty()) {
|
||||
json->getMaps()["Name"] = new JSONValue<std::string>(osg->getName());
|
||||
}
|
||||
|
||||
osgSim::ShapeAttributeList* osgSim_userdata = dynamic_cast<osgSim::ShapeAttributeList* >(osg->getUserData());
|
||||
if (osgSim_userdata) {
|
||||
JSONObject* jsonUDC = new JSONObject();
|
||||
jsonUDC->addUniqueID();
|
||||
|
||||
JSONArray* jsonUDCArray = new JSONArray();
|
||||
jsonUDC->getMaps()["Values"] = jsonUDCArray;
|
||||
for (unsigned int i = 0; i < osgSim_userdata->size(); i++) {
|
||||
const osgSim::ShapeAttribute& attr = (*osgSim_userdata)[i];
|
||||
JSONObject* jsonEntry = new JSONObject();
|
||||
jsonEntry->getMaps()["Name"] = new JSONValue<std::string>(attr.getName());
|
||||
osg::ref_ptr<JSONValue<std::string> > value;
|
||||
switch(attr.getType()) {
|
||||
case osgSim::ShapeAttribute::INTEGER:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << attr.getInt();
|
||||
value = new JSONValue<std::string>(ss.str());
|
||||
}
|
||||
break;
|
||||
case osgSim::ShapeAttribute::DOUBLE:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << attr.getDouble();
|
||||
value = new JSONValue<std::string>(ss.str());
|
||||
}
|
||||
break;
|
||||
case osgSim::ShapeAttribute::STRING:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << attr.getString();
|
||||
value = new JSONValue<std::string>(ss.str());
|
||||
}
|
||||
break;
|
||||
case osgSim::ShapeAttribute::UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
jsonEntry->getMaps()["Value"] = value;
|
||||
jsonUDCArray->getArray().push_back(jsonEntry);
|
||||
}
|
||||
json->getMaps()["UserDataContainer"] = jsonUDC;
|
||||
|
||||
} else if (osg->getUserDataContainer()) {
|
||||
JSONObject* jsonUDC = new JSONObject();
|
||||
jsonUDC->addUniqueID();
|
||||
|
||||
if (!osg->getUserDataContainer()->getName().empty()) {
|
||||
jsonUDC->getMaps()["Name"] = new JSONValue<std::string>(osg->getUserDataContainer()->getName());
|
||||
}
|
||||
JSONArray* jsonUDCArray = new JSONArray();
|
||||
jsonUDC->getMaps()["Values"] = jsonUDCArray;
|
||||
for (unsigned int i = 0; i < osg->getUserDataContainer()->getNumUserObjects(); i++) {
|
||||
osg::Object* o = osg->getUserDataContainer()->getUserObject(i);
|
||||
std::string name, value;
|
||||
getStringifiedUserValue(o, name, value);
|
||||
if(!name.empty() && !value.empty())
|
||||
{
|
||||
JSONObject* jsonEntry = new JSONObject();
|
||||
jsonEntry->getMaps()["Name"] = new JSONValue<std::string>(name);
|
||||
jsonEntry->getMaps()["Value"] = new JSONValue<std::string>(value);
|
||||
jsonUDCArray->getArray().push_back(jsonEntry);
|
||||
}
|
||||
|
||||
}
|
||||
json->getMaps()["UserDataContainer"] = jsonUDC;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value) {
|
||||
if(getStringifiedUserValue<std::string>(o, name, value)) return;
|
||||
if(getStringifiedUserValue<char>(o, name, value)) return;
|
||||
if(getStringifiedUserValue<bool>(o, name, value)) return;
|
||||
if(getStringifiedUserValue<short>(o, name, value)) return;
|
||||
if(getStringifiedUserValue<unsigned short>(o, name, value)) return;
|
||||
if(getStringifiedUserValue<int>(o, name, value)) return;
|
||||
if(getStringifiedUserValue<unsigned int>(o, name, value)) return;
|
||||
if(getStringifiedUserValue<float>(o, name, value)) return;
|
||||
if(getStringifiedUserValue<double>(o, name, value)) return;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
bool getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value) {
|
||||
osg::TemplateValueObject<T>* vo = dynamic_cast< osg::TemplateValueObject<T>* >(o);
|
||||
if (vo) {
|
||||
std::ostringstream oss;
|
||||
oss << vo->getValue();
|
||||
name = vo->getName();
|
||||
value = oss.str();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static JSONValue<std::string>* getBlendFuncMode(GLenum mode) {
|
||||
switch (mode) {
|
||||
case osg::BlendFunc::DST_ALPHA: return new JSONValue<std::string>("DST_ALPHA");
|
||||
case osg::BlendFunc::DST_COLOR: return new JSONValue<std::string>("DST_COLOR");
|
||||
case osg::BlendFunc::ONE: return new JSONValue<std::string>("ONE");
|
||||
case osg::BlendFunc::ONE_MINUS_DST_ALPHA: return new JSONValue<std::string>("ONE_MINUS_DST_ALPHA");
|
||||
case osg::BlendFunc::ONE_MINUS_DST_COLOR: return new JSONValue<std::string>("ONE_MINUS_DST_COLOR");
|
||||
case osg::BlendFunc::ONE_MINUS_SRC_ALPHA: return new JSONValue<std::string>("ONE_MINUS_SRC_ALPHA");
|
||||
case osg::BlendFunc::ONE_MINUS_SRC_COLOR: return new JSONValue<std::string>("ONE_MINUS_SRC_COLOR");
|
||||
case osg::BlendFunc::SRC_ALPHA: return new JSONValue<std::string>("SRC_ALPHA");
|
||||
case osg::BlendFunc::SRC_ALPHA_SATURATE: return new JSONValue<std::string>("SRC_ALPHA_SATURATE");
|
||||
case osg::BlendFunc::SRC_COLOR: return new JSONValue<std::string>("SRC_COLOR");
|
||||
case osg::BlendFunc::CONSTANT_COLOR: return new JSONValue<std::string>("CONSTANT_COLOR");
|
||||
case osg::BlendFunc::ONE_MINUS_CONSTANT_COLOR: return new JSONValue<std::string>("ONE_MINUS_CONSTANT_COLOR");
|
||||
case osg::BlendFunc::CONSTANT_ALPHA: return new JSONValue<std::string>("CONSTANT_ALPHA");
|
||||
case osg::BlendFunc::ONE_MINUS_CONSTANT_ALPHA: return new JSONValue<std::string>("ONE_MINUS_CONSTANT_ALPHA");
|
||||
case osg::BlendFunc::ZERO: return new JSONValue<std::string>("ZERO");
|
||||
default:
|
||||
return new JSONValue<std::string>("ONE");
|
||||
}
|
||||
return new JSONValue<std::string>("ONE");
|
||||
}
|
||||
|
||||
static JSONValue<std::string>* getJSONFilterMode(osg::Texture::FilterMode mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case GL_LINEAR:
|
||||
return new JSONValue<std::string>("LINEAR");
|
||||
case GL_LINEAR_MIPMAP_LINEAR:
|
||||
return new JSONValue<std::string>("LINEAR_MIPMAP_LINEAR");
|
||||
case GL_LINEAR_MIPMAP_NEAREST:
|
||||
return new JSONValue<std::string>("LINEAR_MIPMAP_NEAREST");
|
||||
case GL_NEAREST:
|
||||
return new JSONValue<std::string>("NEAREST");
|
||||
case GL_NEAREST_MIPMAP_LINEAR:
|
||||
return new JSONValue<std::string>("NEAREST_MIPMAP_LINEAR");
|
||||
case GL_NEAREST_MIPMAP_NEAREST:
|
||||
return new JSONValue<std::string>("NEAREST_MIPMAP_NEAREST");
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JSONValue<std::string>* getJSONWrapMode(osg::Texture::WrapMode mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case GL_CLAMP:
|
||||
// clamp does not exist in opengles 2.0
|
||||
//return new JSONValue<std::string>("CLAMP");
|
||||
return new JSONValue<std::string>("CLAMP_TO_EDGE");
|
||||
case GL_CLAMP_TO_EDGE:
|
||||
return new JSONValue<std::string>("CLAMP_TO_EDGE");
|
||||
case GL_CLAMP_TO_BORDER_ARB:
|
||||
return new JSONValue<std::string>("CLAMP_TO_BORDER");
|
||||
case GL_REPEAT:
|
||||
return new JSONValue<std::string>("REPEAT");
|
||||
case GL_MIRRORED_REPEAT_IBM:
|
||||
return new JSONValue<std::string>("MIRROR");
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
JSONObject* createImage(osg::Image* image, bool inlineImages, int maxTextureDimension, const std::string &baseName)
|
||||
{
|
||||
if (!image) {
|
||||
osg::notify(osg::WARN) << "unknown image from texture2d " << std::endl;
|
||||
return new JSONValue<std::string>("/unknown.png");
|
||||
} else {
|
||||
std::string modelDir = osgDB::getFilePath(baseName);
|
||||
if (!image->getFileName().empty() && image->getWriteHint() != osg::Image::STORE_INLINE) {
|
||||
if(maxTextureDimension) {
|
||||
int new_s = osg::Image::computeNearestPowerOfTwo(image->s());
|
||||
int new_t = osg::Image::computeNearestPowerOfTwo(image->t());
|
||||
|
||||
bool notValidPowerOf2 = false;
|
||||
if (new_s != image->s() || image->s() > maxTextureDimension) notValidPowerOf2 = true;
|
||||
if (new_t != image->t() || image->t() > maxTextureDimension) notValidPowerOf2 = true;
|
||||
|
||||
if (notValidPowerOf2) {
|
||||
image->ensureValidSizeForTexturing(maxTextureDimension);
|
||||
if(osgDB::isAbsolutePath(image->getFileName()))
|
||||
osgDB::writeImageFile(*image, image->getFileName());
|
||||
else
|
||||
osgDB::writeImageFile(*image,
|
||||
osgDB::concatPaths(modelDir,
|
||||
image->getFileName()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no image file so use this inline name image and create a file
|
||||
std::stringstream ss;
|
||||
ss << osgDB::getFilePath(baseName) << osgDB::getNativePathSeparator();
|
||||
ss << (long int)image << ".inline_conv_generated.png"; // write the pointer location
|
||||
std::string filename = ss.str();
|
||||
if (osgDB::writeImageFile(*image, filename)) {
|
||||
image->setFileName(filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (!image->getFileName().empty()) { // means that everything went ok
|
||||
if (inlineImages) {
|
||||
|
||||
std::ifstream in(osgDB::findDataFile(image->getFileName()).c_str());
|
||||
if (in.is_open())
|
||||
{
|
||||
std::stringstream out;
|
||||
out << "data:image/" << osgDB::getLowerCaseFileExtension(image->getFileName()) << ";base64,";
|
||||
base64::encode(std::istreambuf_iterator<char>(in),
|
||||
std::istreambuf_iterator<char>(),
|
||||
std::ostreambuf_iterator<char>(out), false);
|
||||
|
||||
return new JSONValue<std::string>(out.str());
|
||||
|
||||
}
|
||||
}
|
||||
return new JSONValue<std::string>(image->getFileName());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
JSONObject* WriteVisitor::createJSONBufferArray(osg::Array* array, osg::Geometry* geom)
|
||||
{
|
||||
if (_maps.find(array) != _maps.end())
|
||||
return _maps[array]->getShadowObject();
|
||||
|
||||
osg::ref_ptr<JSONBufferArray> json = new JSONBufferArray(array);
|
||||
json->addUniqueID();
|
||||
_maps[array] = json;
|
||||
if(geom && _mergeAllBinaryFiles) {
|
||||
setBufferName(json.get(), geom);
|
||||
}
|
||||
return json.get();
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONDrawElementsUInt(osg::DrawElementsUInt* de, osg::Geometry* geom)
|
||||
{
|
||||
if (_maps.find(de) != _maps.end())
|
||||
return _maps[de]->getShadowObject();
|
||||
|
||||
JSONDrawElements<osg::DrawElementsUInt>* json = new JSONDrawElements<osg::DrawElementsUInt>(*de);
|
||||
json->addUniqueID();
|
||||
_maps[de] = json;
|
||||
if(geom && _mergeAllBinaryFiles) {
|
||||
setBufferName(json, geom);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONDrawElementsUShort(osg::DrawElementsUShort* de, osg::Geometry* geom)
|
||||
{
|
||||
if (_maps.find(de) != _maps.end())
|
||||
return _maps[de]->getShadowObject();
|
||||
|
||||
JSONDrawElements<osg::DrawElementsUShort>* json = new JSONDrawElements<osg::DrawElementsUShort>(*de);
|
||||
json->addUniqueID();
|
||||
_maps[de] = json;
|
||||
if(geom && _mergeAllBinaryFiles) {
|
||||
setBufferName(json, geom);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONDrawElementsUByte(osg::DrawElementsUByte* de, osg::Geometry* geom)
|
||||
{
|
||||
if (_maps.find(de) != _maps.end())
|
||||
return _maps[de]->getShadowObject();
|
||||
|
||||
JSONDrawElements<osg::DrawElementsUByte>* json = new JSONDrawElements<osg::DrawElementsUByte>(*de);
|
||||
json->addUniqueID();
|
||||
_maps[de] = json;
|
||||
if(geom && _mergeAllBinaryFiles) {
|
||||
setBufferName(json, geom);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
// use to convert draw array quads to draw elements triangles
|
||||
JSONObject* WriteVisitor::createJSONDrawElements(osg::DrawArrays* drawArray, osg::Geometry* geom)
|
||||
{
|
||||
if (_maps.find(drawArray) != _maps.end())
|
||||
return _maps[drawArray]->getShadowObject();
|
||||
|
||||
if (drawArray->getMode() != GL_QUADS) {
|
||||
osg::notify(osg::WARN) << "" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::DrawElementsUShort> de = new osg::DrawElementsUShort(GL_TRIANGLES);
|
||||
|
||||
for (int i = 0; i < drawArray->getCount()/4; ++i) {
|
||||
int base = drawArray->getFirst() + i*4;
|
||||
de->push_back(base + 0);
|
||||
de->push_back(base + 1);
|
||||
de->push_back(base + 3);
|
||||
|
||||
de->push_back(base + 1);
|
||||
de->push_back(base + 2);
|
||||
de->push_back(base + 3);
|
||||
}
|
||||
JSONDrawElements<osg::DrawElementsUShort>* json = new JSONDrawElements<osg::DrawElementsUShort>(*de);
|
||||
json->addUniqueID();
|
||||
_maps[drawArray] = json;
|
||||
if(geom && _mergeAllBinaryFiles) {
|
||||
setBufferName(json, geom);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONDrawArray(osg::DrawArrays* da, osg::Geometry* geom)
|
||||
{
|
||||
if (_maps.find(da) != _maps.end())
|
||||
return _maps[da]->getShadowObject();
|
||||
|
||||
osg::ref_ptr<JSONDrawArray> json = new JSONDrawArray(*da);
|
||||
json->addUniqueID();
|
||||
_maps[da] = json;
|
||||
if(geom && _mergeAllBinaryFiles) {
|
||||
setBufferName(json.get(), geom);
|
||||
}
|
||||
return json.get();
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONDrawArrayLengths(osg::DrawArrayLengths* da, osg::Geometry* geom)
|
||||
{
|
||||
if (_maps.find(da) != _maps.end())
|
||||
return _maps[da]->getShadowObject();
|
||||
|
||||
osg::ref_ptr<JSONDrawArrayLengths> json = new JSONDrawArrayLengths(*da);
|
||||
json->addUniqueID();
|
||||
_maps[da] = json;
|
||||
if(geom && _mergeAllBinaryFiles) {
|
||||
setBufferName(json.get(), geom);
|
||||
}
|
||||
return json.get();
|
||||
}
|
||||
|
||||
|
||||
JSONObject* WriteVisitor::createJSONGeometry(osg::Geometry* geom)
|
||||
{
|
||||
if (_maps.find(geom) != _maps.end())
|
||||
return _maps[geom]->getShadowObject();
|
||||
|
||||
//if (needToSplit(*geom))
|
||||
// error();
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONNode;
|
||||
json->addUniqueID();
|
||||
_maps[geom] = json;
|
||||
|
||||
if (geom->getStateSet())
|
||||
createJSONStateSet(json.get(), geom->getStateSet());
|
||||
|
||||
translateObject(json.get(), geom);
|
||||
|
||||
osg::ref_ptr<JSONObject> attributes = new JSONObject;
|
||||
|
||||
int nbVertexes = 0;
|
||||
|
||||
if (geom->getVertexArray()) {
|
||||
nbVertexes = geom->getVertexArray()->getNumElements();
|
||||
attributes->getMaps()["Vertex"] = createJSONBufferArray(geom->getVertexArray(), geom);
|
||||
}
|
||||
if (geom->getNormalArray()) {
|
||||
attributes->getMaps()["Normal"] = createJSONBufferArray(geom->getNormalArray(), geom);
|
||||
int nb = geom->getNormalArray()->getNumElements();
|
||||
if (nbVertexes != nb) {
|
||||
osg::notify(osg::FATAL) << "Fatal nb normals " << nb << " != " << nbVertexes << std::endl;
|
||||
error();
|
||||
}
|
||||
}
|
||||
if (geom->getColorArray()) {
|
||||
attributes->getMaps()["Color"] = createJSONBufferArray(geom->getColorArray(), geom);
|
||||
int nb = geom->getColorArray()->getNumElements();
|
||||
if (nbVertexes != nb) {
|
||||
osg::notify(osg::FATAL) << "Fatal nb colors " << nb << " != " << nbVertexes << std::endl;
|
||||
error();
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
for ( int i = 0; i < 32; i++) {
|
||||
ss.str("");
|
||||
ss << "TexCoord" << i;
|
||||
//osg::notify(osg::NOTICE) << ss.str() << std::endl;
|
||||
if (geom->getTexCoordArray(i)) {
|
||||
attributes->getMaps()[ss.str()] = createJSONBufferArray(geom->getTexCoordArray(i), geom);
|
||||
int nb = geom->getTexCoordArray(i)->getNumElements();
|
||||
if (nbVertexes != nb) {
|
||||
osg::notify(osg::FATAL) << "Fatal nb tex coord " << i << " " << nb << " != " << nbVertexes << std::endl;
|
||||
error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osg::Array* tangents = getTangentSpaceArray(*geom);
|
||||
if (tangents) {
|
||||
attributes->getMaps()["Tangent"] = createJSONBufferArray(tangents, geom);
|
||||
int nb = tangents->getNumElements();
|
||||
if (nbVertexes != nb) {
|
||||
osg::notify(osg::FATAL) << "Fatal nb tangent " << nb << " != " << nbVertexes << std::endl;
|
||||
error();
|
||||
}
|
||||
}
|
||||
|
||||
json->getMaps()["VertexAttributeList"] = attributes;
|
||||
|
||||
if (!geom->getPrimitiveSetList().empty()) {
|
||||
osg::ref_ptr<JSONArray> primitives = new JSONArray();
|
||||
for (unsigned int i = 0; i < geom->getNumPrimitiveSets(); ++i) {
|
||||
osg::ref_ptr<JSONObject> obj = new JSONObject;
|
||||
osg::PrimitiveSet* primitive = geom->getPrimitiveSet(i);
|
||||
if(!primitive) continue;
|
||||
|
||||
if (primitive->getType() == osg::PrimitiveSet::DrawArraysPrimitiveType) {
|
||||
osg::DrawArrays* da = dynamic_cast<osg::DrawArrays*>((primitive));
|
||||
primitives->getArray().push_back(obj);
|
||||
if (da->getMode() == GL_QUADS) {
|
||||
obj->getMaps()["DrawElementsUShort"] = createJSONDrawElements(da, geom);
|
||||
} else {
|
||||
obj->getMaps()["DrawArrays"] = createJSONDrawArray(da, geom);
|
||||
}
|
||||
} else if (primitive->getType() == osg::PrimitiveSet::DrawElementsUIntPrimitiveType) {
|
||||
osg::DrawElementsUInt* da = dynamic_cast<osg::DrawElementsUInt*>((primitive));
|
||||
primitives->getArray().push_back(obj);
|
||||
obj->getMaps()["DrawElementsUInt"] = createJSONDrawElementsUInt(da, geom);
|
||||
|
||||
} else if (primitive->getType() == osg::PrimitiveSet::DrawElementsUShortPrimitiveType) {
|
||||
osg::DrawElementsUShort* da = dynamic_cast<osg::DrawElementsUShort*>((primitive));
|
||||
primitives->getArray().push_back(obj);
|
||||
obj->getMaps()["DrawElementsUShort"] = createJSONDrawElementsUShort(da, geom);
|
||||
|
||||
} else if (primitive->getType() == osg::PrimitiveSet::DrawElementsUBytePrimitiveType) {
|
||||
osg::DrawElementsUByte* da = dynamic_cast<osg::DrawElementsUByte*>((primitive));
|
||||
primitives->getArray().push_back(obj);
|
||||
obj->getMaps()["DrawElementsUByte"] = createJSONDrawElementsUByte(da, geom);
|
||||
|
||||
} else if (primitive->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType) {
|
||||
osg::DrawArrayLengths* dal = dynamic_cast<osg::DrawArrayLengths*>((primitive));
|
||||
primitives->getArray().push_back(obj);
|
||||
obj->getMaps()["DrawArrayLengths"] = createJSONDrawArrayLengths(dal, geom);
|
||||
} else {
|
||||
osg::notify(osg::WARN) << "Primitive Type " << geom->getPrimitiveSetList()[i]->getType() << " not supported, skipping" << std::endl;
|
||||
}
|
||||
}
|
||||
json->getMaps()["PrimitiveSetList"] = primitives;
|
||||
}
|
||||
return json.get();
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONBlendFunc(osg::BlendFunc* sa)
|
||||
{
|
||||
if (_maps.find(sa) != _maps.end())
|
||||
return _maps[sa]->getShadowObject();
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONObject;
|
||||
json->addUniqueID();
|
||||
_maps[sa] = json;
|
||||
|
||||
translateObject(json.get(), sa);
|
||||
|
||||
json->getMaps()["SourceRGB"] = getBlendFuncMode(sa->getSource());
|
||||
json->getMaps()["DestinationRGB"] = getBlendFuncMode(sa->getDestination());
|
||||
json->getMaps()["SourceAlpha"] = getBlendFuncMode(sa->getSourceAlpha());
|
||||
json->getMaps()["DestinationAlpha"] = getBlendFuncMode(sa->getDestinationAlpha());
|
||||
return json.release();
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONBlendColor(osg::BlendColor* sa)
|
||||
{
|
||||
if (_maps.find(sa) != _maps.end())
|
||||
return _maps[sa]->getShadowObject();
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONObject;
|
||||
json->addUniqueID();
|
||||
_maps[sa] = json;
|
||||
|
||||
translateObject(json.get(), sa);
|
||||
|
||||
json->getMaps()["ConstantColor"] = new JSONVec4Array(sa->getConstantColor());
|
||||
return json.release();
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONCullFace(osg::CullFace* sa)
|
||||
{
|
||||
if (_maps.find(sa) != _maps.end())
|
||||
return _maps[sa]->getShadowObject();
|
||||
|
||||
osg::ref_ptr<JSONObject> json = new JSONObject;
|
||||
json->addUniqueID();
|
||||
_maps[sa] = json;
|
||||
|
||||
translateObject(json.get(), sa);
|
||||
|
||||
osg::ref_ptr<JSONValue<std::string> > mode = new JSONValue<std::string>("BACK");
|
||||
if (sa->getMode() == osg::CullFace::FRONT) {
|
||||
mode = new JSONValue<std::string>("BACK");
|
||||
}
|
||||
if (sa->getMode() == osg::CullFace::FRONT_AND_BACK) {
|
||||
mode = new JSONValue<std::string>("FRONT_AND_BACK");
|
||||
}
|
||||
json->getMaps()["Mode"] = mode;
|
||||
return json.release();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
JSONObject* WriteVisitor::createJSONMaterial(osg::Material* material)
|
||||
{
|
||||
if (_maps.find(material) != _maps.end())
|
||||
return _maps[material]->getShadowObject();
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonMaterial = new JSONMaterial;
|
||||
jsonMaterial->addUniqueID();
|
||||
_maps[material] = jsonMaterial;
|
||||
|
||||
translateObject(jsonMaterial.get(), material);
|
||||
|
||||
jsonMaterial->getMaps()["Ambient"] = new JSONVec4Array(material->getAmbient(osg::Material::FRONT));
|
||||
jsonMaterial->getMaps()["Diffuse"] = new JSONVec4Array(material->getDiffuse(osg::Material::FRONT));
|
||||
jsonMaterial->getMaps()["Specular"] = new JSONVec4Array(material->getSpecular(osg::Material::FRONT));
|
||||
jsonMaterial->getMaps()["Emission"] = new JSONVec4Array(material->getEmission(osg::Material::FRONT));
|
||||
jsonMaterial->getMaps()["Shininess"] = new JSONValue<float>(material->getShininess(osg::Material::FRONT));
|
||||
|
||||
return jsonMaterial.release();
|
||||
}
|
||||
|
||||
|
||||
JSONObject* WriteVisitor::createJSONLight(osg::Light* light)
|
||||
{
|
||||
if (_maps.find(light) != _maps.end())
|
||||
return _maps[light]->getShadowObject();
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonLight = new JSONLight;
|
||||
jsonLight->addUniqueID();
|
||||
_maps[light] = jsonLight;
|
||||
|
||||
translateObject(jsonLight.get(), light);
|
||||
|
||||
jsonLight->getMaps()["LightNum"] = new JSONValue<int>(light->getLightNum());
|
||||
jsonLight->getMaps()["Ambient"] = new JSONVec4Array(light->getAmbient());
|
||||
jsonLight->getMaps()["Diffuse"] = new JSONVec4Array(light->getDiffuse());
|
||||
jsonLight->getMaps()["Specular"] = new JSONVec4Array(light->getSpecular());
|
||||
jsonLight->getMaps()["Position"] = new JSONVec4Array(light->getPosition());
|
||||
jsonLight->getMaps()["Direction"] = new JSONVec3Array(light->getDirection());
|
||||
|
||||
jsonLight->getMaps()["ConstantAttenuation"] = new JSONValue<float>(light->getConstantAttenuation());
|
||||
jsonLight->getMaps()["LinearAttenuation"] = new JSONValue<float>(light->getLinearAttenuation());
|
||||
jsonLight->getMaps()["QuadraticAttenuation"] = new JSONValue<float>(light->getQuadraticAttenuation());
|
||||
jsonLight->getMaps()["SpotExponent"] = new JSONValue<float>(light->getSpotExponent());
|
||||
jsonLight->getMaps()["SpotCutoff"] = new JSONValue<float>(light->getSpotCutoff());
|
||||
return jsonLight.release();
|
||||
}
|
||||
|
||||
template <class T> JSONObject* createImageFromTexture(osg::Texture* texture, JSONObject* jsonTexture, bool inlineImages,
|
||||
int maxTextureDimension, const std::string &baseName = "")
|
||||
{
|
||||
T* text = dynamic_cast<T*>( texture);
|
||||
if (text) {
|
||||
translateObject(jsonTexture,text);
|
||||
JSONObject* image = createImage(text->getImage(), inlineImages, maxTextureDimension, baseName);
|
||||
if (image)
|
||||
jsonTexture->getMaps()["File"] = image;
|
||||
return jsonTexture;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONPagedLOD(osg::PagedLOD *plod)
|
||||
{
|
||||
if (!plod) { return 0; }
|
||||
|
||||
if (_maps.find(plod) != _maps.end()) {
|
||||
return _maps[plod]->getShadowObject();
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonPlod = new JSONNode;
|
||||
jsonPlod->addUniqueID();
|
||||
_maps[plod] = jsonPlod;
|
||||
|
||||
|
||||
// Center Mode
|
||||
osg::ref_ptr<JSONValue<std::string> > centerMode = new JSONValue<std::string>("USE_BOUNDING_SPHERE_CENTER");
|
||||
if (plod->getCenterMode() == osg::LOD::USER_DEFINED_CENTER) {
|
||||
centerMode = new JSONValue<std::string>("USER_DEFINED_CENTER");
|
||||
} else if (plod->getCenterMode() == osg::LOD::UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED){
|
||||
centerMode = new JSONValue<std::string>("UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED");
|
||||
}
|
||||
jsonPlod->getMaps()["CenterMode"] = centerMode;
|
||||
// User defined center and radius
|
||||
jsonPlod->getMaps()["UserCenter"] = new JSONVec4Array(osg::Vec4(plod->getCenter().x(), plod->getCenter().y(),plod->getCenter().z(), plod->getRadius()));
|
||||
|
||||
|
||||
// Range Mode
|
||||
osg::ref_ptr<JSONValue<std::string> > rangeMode = new JSONValue<std::string>("DISTANCE_FROM_EYE_POINT");
|
||||
if (plod->getRangeMode() == osg::LOD::PIXEL_SIZE_ON_SCREEN) {
|
||||
rangeMode = new JSONValue<std::string>("PIXEL_SIZE_ON_SCREEN");
|
||||
}
|
||||
jsonPlod->getMaps()["RangeMode"] = rangeMode;
|
||||
// Range List
|
||||
//osg::ref_ptr<JSONArray> rangeList = new JSONArray;
|
||||
JSONObject* rangeObject = new JSONObject;
|
||||
for (unsigned int i =0; i< plod->getRangeList().size(); i++)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Range ";
|
||||
ss << i;
|
||||
std::string str = ss.str();
|
||||
rangeObject->getMaps()[str] = new JSONVec2Array(osg::Vec2(plod->getRangeList()[i].first, plod->getRangeList()[i].second));
|
||||
}
|
||||
jsonPlod->getMaps()["RangeList"] = rangeObject;
|
||||
// File List
|
||||
|
||||
JSONObject* fileObject = new JSONObject;
|
||||
for (unsigned int i =0; i< plod->getNumFileNames(); i++)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "File ";
|
||||
ss << i;
|
||||
std::string str = ss.str();
|
||||
// We need to convert first from osg format to osgjs format.
|
||||
osg::ref_ptr<osg::Node> n = osgDB::readNodeFile(plod->getFileName(i)+".gles");
|
||||
if (n)
|
||||
{
|
||||
std::string filename(osgDB::getStrippedName(plod->getFileName(i))+".osgjs");
|
||||
osgDB::writeNodeFile(*n,filename);
|
||||
fileObject->getMaps()[str] = new JSONValue<std::string>(filename);
|
||||
}
|
||||
else
|
||||
fileObject->getMaps()[str] = new JSONValue<std::string>("");
|
||||
}
|
||||
jsonPlod->getMaps()["RangeDataList"] = fileObject;
|
||||
|
||||
return jsonPlod.get();
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONTexture(osg::Texture* texture)
|
||||
{
|
||||
if (!texture) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_maps.find(texture) != _maps.end()) {
|
||||
return _maps[texture]->getShadowObject();
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonTexture = new JSONObject;
|
||||
jsonTexture->addUniqueID();
|
||||
_maps[texture] = jsonTexture;
|
||||
|
||||
jsonTexture->getMaps()["MagFilter"] = getJSONFilterMode(texture->getFilter(osg::Texture::MAG_FILTER));
|
||||
jsonTexture->getMaps()["MinFilter"] = getJSONFilterMode(texture->getFilter(osg::Texture::MIN_FILTER));
|
||||
|
||||
jsonTexture->getMaps()["WrapS"] = getJSONWrapMode(texture->getWrap(osg::Texture::WRAP_S));
|
||||
jsonTexture->getMaps()["WrapT"] = getJSONWrapMode(texture->getWrap(osg::Texture::WRAP_T));
|
||||
|
||||
|
||||
{
|
||||
JSONObject* obj = createImageFromTexture<osg::Texture1D>(texture, jsonTexture.get(), this->_inlineImages,
|
||||
this->_maxTextureDimension, this->_baseName);
|
||||
if (obj) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
JSONObject* obj = createImageFromTexture<osg::Texture2D>(texture, jsonTexture.get(), this->_inlineImages,
|
||||
this->_maxTextureDimension, this->_baseName);
|
||||
if (obj) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
JSONObject* obj = createImageFromTexture<osg::TextureRectangle>(texture, jsonTexture.get(), this->_inlineImages,
|
||||
this->_maxTextureDimension, this->_baseName);
|
||||
if (obj) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSONObject* WriteVisitor::createJSONStateSet(osg::StateSet* stateset)
|
||||
{
|
||||
|
||||
if (_maps.find(stateset) != _maps.end()) {
|
||||
return _maps[stateset]->getShadowObject();
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONObject> jsonStateSet = new JSONStateSet;
|
||||
_maps[stateset] = jsonStateSet;
|
||||
jsonStateSet->addUniqueID();
|
||||
|
||||
translateObject(jsonStateSet.get(), stateset);
|
||||
|
||||
if (stateset->getRenderingHint() == osg::StateSet::TRANSPARENT_BIN) {
|
||||
jsonStateSet->getMaps()["RenderingHint"] = new JSONValue<std::string>("TRANSPARENT_BIN");
|
||||
}
|
||||
|
||||
bool blendEnabled = false;
|
||||
if (stateset->getMode(GL_BLEND) == osg::StateAttribute::ON) {
|
||||
blendEnabled = true;
|
||||
}
|
||||
|
||||
osg::ref_ptr<JSONArray> textureAttributeList = new JSONArray;
|
||||
int lastTextureIndex = -1;
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
osg::Texture* texture = dynamic_cast<osg::Texture*>(stateset->getTextureAttribute(i,osg::StateAttribute::TEXTURE));
|
||||
|
||||
JSONArray* textureUnit = new JSONArray;
|
||||
JSONObject* jsonTexture = createJSONTexture(texture);
|
||||
textureAttributeList->getArray().push_back(textureUnit);
|
||||
|
||||
if (jsonTexture) {
|
||||
JSONObject* textureObject = new JSONObject;
|
||||
textureObject->getMaps()["osg.Texture"] = jsonTexture;
|
||||
textureUnit->getArray().push_back(textureObject);
|
||||
lastTextureIndex = i;
|
||||
}
|
||||
}
|
||||
if (lastTextureIndex > -1) {
|
||||
textureAttributeList->getArray().resize(lastTextureIndex+1);
|
||||
jsonStateSet->getMaps()["TextureAttributeList"] = textureAttributeList;
|
||||
}
|
||||
|
||||
|
||||
osg::ref_ptr<JSONArray> attributeList = new JSONArray;
|
||||
|
||||
osg::Material* material = dynamic_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
|
||||
if (material) {
|
||||
JSONObject* obj = new JSONObject;
|
||||
obj->getMaps()["osg.Material"] = createJSONMaterial(material);
|
||||
attributeList->getArray().push_back(obj);
|
||||
}
|
||||
|
||||
osg::BlendFunc* blendFunc = dynamic_cast<osg::BlendFunc*>(stateset->getAttribute(osg::StateAttribute::BLENDFUNC));
|
||||
if (blendFunc) {
|
||||
JSONObject* obj = new JSONObject;
|
||||
obj->getMaps()["osg.BlendFunc"] = createJSONBlendFunc(blendFunc);
|
||||
attributeList->getArray().push_back(obj);
|
||||
} else if (blendEnabled == true) {
|
||||
JSONObject* obj = new JSONObject;
|
||||
osg::ref_ptr<osg::BlendFunc> defaultBlend = new osg::BlendFunc();
|
||||
obj->getMaps()["osg.BlendFunc"] = createJSONBlendFunc(defaultBlend.get());
|
||||
attributeList->getArray().push_back(obj);
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::CullFace> cullFace = dynamic_cast<osg::CullFace*>(stateset->getAttribute(osg::StateAttribute::CULLFACE));
|
||||
osg::StateAttribute::GLModeValue cullMode = stateset->getMode(GL_CULL_FACE);
|
||||
if (cullFace || cullMode != osg::StateAttribute::INHERIT) {
|
||||
JSONObject* obj = new JSONObject;
|
||||
JSONObject* cf = 0;
|
||||
if (cullMode == osg::StateAttribute::OFF) {
|
||||
osg::ref_ptr<osg::CullFace> defaultCull = new osg::CullFace();
|
||||
cf = createJSONCullFace(defaultCull.get());
|
||||
cf->getMaps()["Mode"] = new JSONValue<std::string>("DISABLE");
|
||||
obj->getMaps()["osg.CullFace"] = cf;
|
||||
attributeList->getArray().push_back(obj);
|
||||
} else {
|
||||
if (!cullFace) {
|
||||
cullFace = new osg::CullFace();
|
||||
}
|
||||
cf = createJSONCullFace(cullFace.get());
|
||||
}
|
||||
obj->getMaps()["osg.CullFace"] = cf;
|
||||
attributeList->getArray().push_back(obj);
|
||||
}
|
||||
|
||||
osg::BlendColor* blendColor = dynamic_cast<osg::BlendColor*>(stateset->getAttribute(osg::StateAttribute::BLENDCOLOR));
|
||||
if (blendColor) {
|
||||
JSONObject* obj = new JSONObject;
|
||||
obj->getMaps()["osg.BlendColor"] = createJSONBlendColor(blendColor);
|
||||
attributeList->getArray().push_back(obj);
|
||||
}
|
||||
|
||||
|
||||
if (!attributeList->getArray().empty()) {
|
||||
jsonStateSet->getMaps()["AttributeList"] = attributeList;
|
||||
}
|
||||
|
||||
|
||||
osg::StateSet::ModeList modeList = stateset->getModeList();
|
||||
for (unsigned int i = 0; i < modeList.size(); ++i) {
|
||||
// add modes
|
||||
}
|
||||
|
||||
if (jsonStateSet->getMaps().empty())
|
||||
return 0;
|
||||
return jsonStateSet.release();
|
||||
}
|
||||
274
src/osgPlugins/osgjs/json_stream
Normal file
274
src/osgPlugins/osgjs/json_stream
Normal file
@@ -0,0 +1,274 @@
|
||||
#ifndef JSON_STREAM
|
||||
#define JSON_STREAM
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cctype> // control characters
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
|
||||
// A simple class wrapping ofstream calls to enable generic cleaning of json data.
|
||||
// Especially 'standard' json should:
|
||||
// * have utf-8 encoded string
|
||||
// * disallow some control characters
|
||||
// * does not support inf or nan values
|
||||
|
||||
class json_stream : public std::ofstream {
|
||||
public:
|
||||
json_stream(const std::string& filename, bool strict=true) :
|
||||
_stream(filename.c_str()),
|
||||
_strict(strict)
|
||||
{}
|
||||
|
||||
~json_stream() {
|
||||
_stream.close();
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return _stream.is_open();
|
||||
}
|
||||
|
||||
// forward std::endl
|
||||
typedef std::ostream& (*ostream_manipulator)(std::ostream&);
|
||||
json_stream& operator<<(ostream_manipulator pf) {
|
||||
if (_stream.is_open()) {
|
||||
_stream << pf;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
json_stream& operator<<(const T& data) {
|
||||
if (_stream.is_open()) {
|
||||
_stream << sanitize(data);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T sanitize(const T& t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
double sanitize(const float f) {
|
||||
return sanitize(static_cast<double>(f));
|
||||
}
|
||||
|
||||
double sanitize(const double d) {
|
||||
if(_strict) {
|
||||
return to_valid_float(d);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
double to_valid_float(const double d) {
|
||||
if(std::isfinite(d)) {
|
||||
return d;
|
||||
}
|
||||
else {
|
||||
if(std::isinf(d)) {
|
||||
return std::numeric_limits<double>::max();
|
||||
}
|
||||
// no much way to do better than replace invalid float NaN by 0
|
||||
return 0.;
|
||||
}
|
||||
}
|
||||
|
||||
std::string sanitize(const std::string& s) {
|
||||
if(_strict) {
|
||||
return to_json_utf8(s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string sanitize(const char* s) {
|
||||
return sanitize(std::string(s));
|
||||
}
|
||||
|
||||
std::string to_json_utf8(const std::string& s) {
|
||||
// TODO: try to decode latin1 if string is not valid utf8
|
||||
// before actually fixing bad 'chars'
|
||||
return clean_invalid_utf8(s);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
std::ofstream _stream;
|
||||
bool _strict;
|
||||
|
||||
std::string json_encode_control_char(int ctrl) {
|
||||
// see http://json.org
|
||||
std::ostringstream oss;
|
||||
|
||||
if(ctrl == 8 || // \b
|
||||
ctrl == 9 || // \t
|
||||
ctrl == 10 || // \n
|
||||
ctrl == 12 || // \f
|
||||
ctrl == 13 || // \r
|
||||
ctrl == 27 || //
|
||||
ctrl == 34 || // \"
|
||||
ctrl == 47 // \/
|
||||
) {
|
||||
oss << static_cast<char>(ctrl);
|
||||
}
|
||||
else {
|
||||
oss.fill('0');
|
||||
oss << "\\u" << std::setw(4) << std::hex << ctrl;
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
inline bool is_valid_continuation_byte(unsigned int byte) {
|
||||
return ((byte & 0xC0) == 0x80);
|
||||
}
|
||||
|
||||
inline int get_next_byte(std::string::const_iterator& iterator, std::string::const_iterator end_iterator) {
|
||||
if(iterator != end_iterator) {
|
||||
return *(++ iterator);
|
||||
}
|
||||
else {
|
||||
return 0; // invalid continuation byte
|
||||
}
|
||||
}
|
||||
|
||||
// from http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
|
||||
std::string utf8_encode_codepoint(unsigned code_point)
|
||||
{
|
||||
std::string output;
|
||||
|
||||
if(code_point > 0x590 && code_point < 0x5F4) {
|
||||
return output;
|
||||
}
|
||||
|
||||
// out of range
|
||||
if(code_point > 1114112) {
|
||||
return utf8_encode_codepoint(0xfffd);
|
||||
}
|
||||
|
||||
if (code_point < 0x80) {
|
||||
output.push_back(code_point);
|
||||
}
|
||||
else if (code_point <= 0x7FF) {
|
||||
output.push_back((code_point >> 6) + 0xC0);
|
||||
output.push_back((code_point & 0x3F) + 0x80);
|
||||
}
|
||||
else if (code_point <= 0xFFFF) {
|
||||
output.push_back((code_point >> 12) + 0xE0);
|
||||
output.push_back(((code_point >> 6) & 0x3F) + 0x80);
|
||||
output.push_back((code_point & 0x3F) + 0x80);
|
||||
}
|
||||
else if (code_point <= 0x10FFFF) {
|
||||
output.push_back((code_point >> 18) + 0xF0);
|
||||
output.push_back(((code_point >> 12) & 0x3F) + 0x80);
|
||||
output.push_back(((code_point >> 6) & 0x3F) + 0x80);
|
||||
output.push_back((code_point & 0x3F) + 0x80);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
// from http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
|
||||
std::string clean_invalid_utf8(const std::string& input,
|
||||
const int replacement_codepoint=0xfffd) {
|
||||
int code_unit1, code_unit2, code_unit3, code_unit4;
|
||||
std::string output, replacement = utf8_encode_codepoint(replacement_codepoint);
|
||||
|
||||
for(std::string::const_iterator iterator = input.begin() ; iterator != input.end() ; ++ iterator) {
|
||||
code_unit1 = *iterator;
|
||||
if (code_unit1 < 0x80) {
|
||||
if(std::iscntrl(code_unit1)) {
|
||||
output += json_encode_control_char(code_unit1);
|
||||
}
|
||||
else {
|
||||
output.push_back(code_unit1);
|
||||
}
|
||||
}
|
||||
else if (code_unit1 < 0xC2) {
|
||||
// continuation or overlong 2-byte sequence
|
||||
output += replacement;
|
||||
}
|
||||
else if (code_unit1 < 0xE0) {
|
||||
// 2-byte sequence
|
||||
code_unit2 = get_next_byte(iterator, input.end());
|
||||
|
||||
if (!is_valid_continuation_byte(code_unit2)) {
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
}
|
||||
else {
|
||||
output += utf8_encode_codepoint((code_unit1 << 6) + code_unit2 - 0x3080);
|
||||
}
|
||||
}
|
||||
else if (code_unit1 < 0xF0) {
|
||||
// 3-byte sequence
|
||||
code_unit2 = get_next_byte(iterator, input.end());
|
||||
|
||||
if (!is_valid_continuation_byte(code_unit2) ||
|
||||
(code_unit1 == 0xE0 && code_unit2 < 0xA0)) /* overlong */ {
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
}
|
||||
else {
|
||||
code_unit3 = get_next_byte(iterator, input.end());
|
||||
|
||||
if (!is_valid_continuation_byte(code_unit3)) {
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
}
|
||||
else {
|
||||
output += utf8_encode_codepoint((code_unit1 << 12) +
|
||||
(code_unit2 << 6) +
|
||||
code_unit3 - 0xE2080);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (code_unit1 < 0xF5) {
|
||||
// 4-byte sequence
|
||||
code_unit2 = get_next_byte(iterator, input.end());
|
||||
if(!is_valid_continuation_byte(code_unit2) ||
|
||||
(code_unit1 == 0xF0 && code_unit2 < 0x90) || /* overlong */
|
||||
(code_unit1 == 0xF4 && code_unit2 >= 0x90)) { /* > U+10FFFF */
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
}
|
||||
else {
|
||||
code_unit3 = get_next_byte(iterator, input.end());
|
||||
if(!is_valid_continuation_byte(code_unit3)) {
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
}
|
||||
else {
|
||||
code_unit4 = get_next_byte(iterator, input.end());
|
||||
if(!is_valid_continuation_byte(code_unit4)) {
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
output += replacement;
|
||||
}
|
||||
else {
|
||||
output += utf8_encode_codepoint((code_unit1 << 18) +
|
||||
(code_unit2 << 12) +
|
||||
(code_unit3 << 6) +
|
||||
code_unit4 - 0x3C82080);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* > U+10FFFF */
|
||||
output += replacement;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user