diff --git a/src/osgPlugins/gles/AABBonBoneVisitor b/src/osgPlugins/gles/AABBonBoneVisitor index a8f3a7396..675fbb3df 100644 --- a/src/osgPlugins/gles/AABBonBoneVisitor +++ b/src/osgPlugins/gles/AABBonBoneVisitor @@ -35,234 +35,33 @@ public: typedef std::vector BoneList; typedef std::vector RigGeometryList; - ComputeAABBOnBoneVisitor(bool createGeometry): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _root(0), _createGeometry(createGeometry) {} - void apply(osg::Transform& node) { - if(!_root) - _root = dynamic_cast(&node); - - osgAnimation::Bone * b = dynamic_cast(&node); - if(b) apply(*b); - traverse(node); - } - - void apply(osg::Geometry& geometry) { - if(osgAnimation::RigGeometry *rigGeometry = dynamic_cast(&geometry)) { - apply(*rigGeometry); - } - } - - void apply(osgAnimation::Bone &bone) { - _bones.push_back(&bone); - } - - void apply(osgAnimation::RigGeometry &rig) { - _rigGeometries.push_back(&rig); - } - - void computeBoundingBoxOnBones() { - //Perform Updates - updateBones(); - updateRigGeometries(); - - //We have our T pose, we can compute an AABB for each bone - for (BoneList::iterator bone = _bones.begin(); bone != _bones.end(); ++ bone) { - osg::BoundingBox bb; - //For each geometry - for (RigGeometryList::iterator iterator = _rigGeometries.begin(); iterator != _rigGeometries.end(); ++ iterator) { - osgAnimation::RigGeometry* rigGeometry = *iterator; - if(!rigGeometry) continue; - - osg::Matrix mtxLocalToSkl = rigGeometry->getWorldMatrices(_root).at(0); - - //For each Vertex influence - osgAnimation::VertexInfluenceMap * infMap = rigGeometry->getInfluenceMap(); - osgAnimation::VertexInfluenceMap::iterator itMap = infMap->find((*bone)->getName()); - if(itMap == infMap->end()) continue; - - osg::Vec3Array *vertices = dynamic_cast(rigGeometry->getVertexArray()); - if(!vertices) continue; - - osgAnimation::VertexInfluence vxtInf = (*itMap).second; - - //Expand the boundingBox with each vertex - for(unsigned int j = 0; j < vxtInf.size(); j++) { - if(vxtInf.at(j).second < 10e-2) continue; //Skip vertex if low weight - osg::Vec3 vx = vertices->at(vxtInf.at(j).first); - vx = vx * mtxLocalToSkl; - bb.expandBy(vx); - } - - // Compare initial and actual boundingBox if (no change) => no box on bone - if(bb == osg::BoundingBox() || bb._min.x() == bb._max.x() || bb._min.y() == bb._max.y() || bb._min.z() == bb._max.z()) { - continue; - } - - osg::Matrix worldToBone = osg::Matrix::inverse((*bone)->getWorldMatrices(_root).at(0)); - - if(_createGeometry) { - osg::Geode *g = new osg::Geode; - g->setName("AABB_for_bone_" + (*bone)->getName()); - g->addDrawable(createBox(bb, worldToBone)); - (*bone)->addChild(g); - } - - serializeBoundingBox(bb, worldToBone, *(*bone)); - } - } - - //Clear geometries - for (RigGeometryList::iterator iterator = _rigGeometries.begin(); iterator != _rigGeometries.end(); ++ iterator) { - osgAnimation::RigGeometry* rigGeometry = *iterator; - if(rigGeometry) { - rigGeometry->copyFrom(*rigGeometry->getSourceGeometry()); - rigGeometry->setRigTransformImplementation(0); - } - } - } + void apply(osg::Transform&); + void apply(osg::Geometry&); + void apply(osgAnimation::Bone &); + void apply(osgAnimation::RigGeometry &); + void computeBoundingBoxOnBones(); protected: - osg::Geometry* createBox(const osg::BoundingBox &bb, const osg::Matrix &transform, - float ratio=1.0, osg::Vec4 color=osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)) { - osg::Geometry *cube = new osg::Geometry; + osg::Geometry* createBox(const osg::BoundingBox &, + const osg::Matrix &, + float /*ratio*/=1.0, + osg::Vec4 /*color*/=osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); - osg::Vec3 center = bb.center(); - double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50; - double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50; - double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50; + void serializeBoundingBox(const osg::BoundingBox &, + const osg::Matrix &, + osgAnimation::Bone &, + float /*ratio*/=1.0); - halfLenghtX *= ratio; - halfLenghtY *= ratio; - halfLenghtZ *= ratio; - - osg::Vec3Array *cubeVertices = new osg::Vec3Array; - cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform); - cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform); - cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform); - cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform); - - cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform); - cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform); - cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform); - cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform); - - cube->setVertexArray(cubeVertices); - - { - osg::DrawElementsUInt* up = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); - up->push_back(4); - up->push_back(5); - up->push_back(1); - up->push_back(0); - cube->addPrimitiveSet(up); - } - - { - osg::DrawElementsUInt* down = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); - down->push_back(2); - down->push_back(6); - down->push_back(7); - down->push_back(3); - cube->addPrimitiveSet(down); - } - - { - osg::DrawElementsUInt* left = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); - left->push_back(2); - left->push_back(3); - left->push_back(0); - left->push_back(1); - cube->addPrimitiveSet(left); - } - - { - osg::DrawElementsUInt* right = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); - right->push_back(7); - right->push_back(6); - right->push_back(5); - right->push_back(4); - cube->addPrimitiveSet(right); - } - - { - osg::DrawElementsUInt* front = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); - front->push_back(3); - front->push_back(7); - front->push_back(4); - front->push_back(0); - cube->addPrimitiveSet(front); - } - - { - osg::DrawElementsUInt* back = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); - back->push_back(6); - back->push_back(2); - back->push_back(1); - back->push_back(5); - cube->addPrimitiveSet(back); - } - - osg::Vec4Array* colors = new osg::Vec4Array; - colors->push_back(color); - colors->push_back(color); - colors->push_back(color); - colors->push_back(color); - colors->push_back(color); - colors->push_back(color); - colors->push_back(color); - colors->push_back(color); - - cube->setColorArray(colors); - cube->setColorBinding(osg::Geometry::BIND_PER_VERTEX); - - return cube; - } - - void serializeBoundingBox(const osg::BoundingBox &bb, const osg::Matrix &transform, osgAnimation::Bone &b, float ratio = 1.0) { - osg::Vec3 center = bb.center(); - double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50; - double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50; - double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50; - - halfLenghtX *= ratio; - halfLenghtY *= ratio; - halfLenghtZ *= ratio; - - osg::BoundingBox serializedBB; - - serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform); - serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform); - serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform); - serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform); - serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform); - serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform); - serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform); - serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform); - - b.setUserValue("AABBonBone_min", serializedBB._min); - b.setUserValue("AABBonBone_max", serializedBB._max); - } - - void updateBones() { - osgUtil::UpdateVisitor update; - _root->accept(update); - } - - void updateRigGeometries() { - for (unsigned int i = 0, size = _rigGeometries.size(); i < size; i++) { - osgAnimation::RigGeometry * rig = _rigGeometries.at(i); - osg::Drawable::UpdateCallback * callback = dynamic_cast(rig->getUpdateCallback()); - if(callback) { - callback->update(0, rig); - } - } - } + void updateBones(); + void updateRigGeometries(); +protected: BoneList _bones; RigGeometryList _rigGeometries; osgAnimation::Skeleton *_root; diff --git a/src/osgPlugins/gles/AABBonBoneVisitor.cpp b/src/osgPlugins/gles/AABBonBoneVisitor.cpp new file mode 100644 index 000000000..14ff98f77 --- /dev/null +++ b/src/osgPlugins/gles/AABBonBoneVisitor.cpp @@ -0,0 +1,235 @@ +#include "AABBonBoneVisitor" + + +void ComputeAABBOnBoneVisitor::apply(osg::Transform& node) { + if(!_root) + _root = dynamic_cast(&node); + + osgAnimation::Bone * b = dynamic_cast(&node); + if(b) apply(*b); + traverse(node); +} + + +void ComputeAABBOnBoneVisitor::apply(osg::Geometry& geometry) { + if(osgAnimation::RigGeometry *rigGeometry = dynamic_cast(&geometry)) { + apply(*rigGeometry); + } +} + + +void ComputeAABBOnBoneVisitor::apply(osgAnimation::Bone &bone) { + _bones.push_back(&bone); +} + + +void ComputeAABBOnBoneVisitor::apply(osgAnimation::RigGeometry &rig) { + _rigGeometries.push_back(&rig); +} + + +void ComputeAABBOnBoneVisitor::computeBoundingBoxOnBones() { + //Perform Updates + updateBones(); + updateRigGeometries(); + + //We have our T pose, we can compute an AABB for each bone + for (BoneList::iterator bone = _bones.begin(); bone != _bones.end(); ++ bone) { + osg::BoundingBox bb; + //For each geometry + for (RigGeometryList::iterator iterator = _rigGeometries.begin(); iterator != _rigGeometries.end(); ++ iterator) { + osgAnimation::RigGeometry* rigGeometry = *iterator; + if(!rigGeometry) continue; + + osg::Matrix mtxLocalToSkl = rigGeometry->getWorldMatrices(_root).at(0); + + //For each Vertex influence + osgAnimation::VertexInfluenceMap * infMap = rigGeometry->getInfluenceMap(); + osgAnimation::VertexInfluenceMap::iterator itMap = infMap->find((*bone)->getName()); + if(itMap == infMap->end()) continue; + + osg::Vec3Array *vertices = dynamic_cast(rigGeometry->getVertexArray()); + if(!vertices) continue; + + osgAnimation::VertexInfluence vxtInf = (*itMap).second; + + //Expand the boundingBox with each vertex + for(unsigned int j = 0; j < vxtInf.size(); j++) { + if(vxtInf.at(j).second < 10e-2) continue; //Skip vertex if low weight + osg::Vec3 vx = vertices->at(vxtInf.at(j).first); + vx = vx * mtxLocalToSkl; + bb.expandBy(vx); + } + + // Compare initial and actual boundingBox if (no change) => no box on bone + if(bb == osg::BoundingBox() || bb._min.x() == bb._max.x() || bb._min.y() == bb._max.y() || bb._min.z() == bb._max.z()) { + continue; + } + + osg::Matrix worldToBone = osg::Matrix::inverse((*bone)->getWorldMatrices(_root).at(0)); + + if(_createGeometry) { + osg::Geode *g = new osg::Geode; + g->setName("AABB_for_bone_" + (*bone)->getName()); + g->addDrawable(createBox(bb, worldToBone)); + (*bone)->addChild(g); + } + + serializeBoundingBox(bb, worldToBone, *(*bone)); + } + } + + //Clear geometries + for (RigGeometryList::iterator iterator = _rigGeometries.begin(); iterator != _rigGeometries.end(); ++ iterator) { + osgAnimation::RigGeometry* rigGeometry = *iterator; + if(rigGeometry) { + rigGeometry->copyFrom(*rigGeometry->getSourceGeometry()); + rigGeometry->setRigTransformImplementation(0); + } + } +} + + +osg::Geometry* ComputeAABBOnBoneVisitor::createBox(const osg::BoundingBox &bb, + const osg::Matrix &transform, + float ratio, + osg::Vec4 color) { + osg::Geometry *cube = new osg::Geometry; + + osg::Vec3 center = bb.center(); + double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50; + double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50; + double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50; + + halfLenghtX *= ratio; + halfLenghtY *= ratio; + halfLenghtZ *= ratio; + + osg::Vec3Array *cubeVertices = new osg::Vec3Array; + cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform); + cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform); + cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform); + cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform); + + cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform); + cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform); + cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform); + cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform); + + cube->setVertexArray(cubeVertices); + + { + osg::DrawElementsUInt* up = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); + up->push_back(4); + up->push_back(5); + up->push_back(1); + up->push_back(0); + cube->addPrimitiveSet(up); + } + + { + osg::DrawElementsUInt* down = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); + down->push_back(2); + down->push_back(6); + down->push_back(7); + down->push_back(3); + cube->addPrimitiveSet(down); + } + + { + osg::DrawElementsUInt* left = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); + left->push_back(2); + left->push_back(3); + left->push_back(0); + left->push_back(1); + cube->addPrimitiveSet(left); + } + + { + osg::DrawElementsUInt* right = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); + right->push_back(7); + right->push_back(6); + right->push_back(5); + right->push_back(4); + cube->addPrimitiveSet(right); + } + + { + osg::DrawElementsUInt* front = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); + front->push_back(3); + front->push_back(7); + front->push_back(4); + front->push_back(0); + cube->addPrimitiveSet(front); + } + + { + osg::DrawElementsUInt* back = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); + back->push_back(6); + back->push_back(2); + back->push_back(1); + back->push_back(5); + cube->addPrimitiveSet(back); + } + + osg::Vec4Array* colors = new osg::Vec4Array; + colors->push_back(color); + colors->push_back(color); + colors->push_back(color); + colors->push_back(color); + colors->push_back(color); + colors->push_back(color); + colors->push_back(color); + colors->push_back(color); + + cube->setColorArray(colors); + cube->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + + return cube; +} + + +void ComputeAABBOnBoneVisitor::serializeBoundingBox(const osg::BoundingBox &bb, + const osg::Matrix &transform, + osgAnimation::Bone &b, + float ratio) { + osg::Vec3 center = bb.center(); + double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50; + double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50; + double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50; + + halfLenghtX *= ratio; + halfLenghtY *= ratio; + halfLenghtZ *= ratio; + + osg::BoundingBox serializedBB; + + serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform); + serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform); + serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform); + serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform); + serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform); + serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform); + serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform); + serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform); + + b.setUserValue("AABBonBone_min", serializedBB._min); + b.setUserValue("AABBonBone_max", serializedBB._max); +} + + +void ComputeAABBOnBoneVisitor::updateBones() { + osgUtil::UpdateVisitor update; + _root->accept(update); +} + + +void ComputeAABBOnBoneVisitor::updateRigGeometries() { + for (unsigned int i = 0, size = _rigGeometries.size(); i < size; i++) { + osgAnimation::RigGeometry * rig = _rigGeometries.at(i); + osg::Drawable::UpdateCallback * callback = dynamic_cast(rig->getUpdateCallback()); + if(callback) { + callback->update(0, rig); + } + } +} diff --git a/src/osgPlugins/gles/AnimationCleanerVisitor b/src/osgPlugins/gles/AnimationCleanerVisitor index 312f37755..84fb17f58 100644 --- a/src/osgPlugins/gles/AnimationCleanerVisitor +++ b/src/osgPlugins/gles/AnimationCleanerVisitor @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -30,32 +29,7 @@ #include #include "StatLogger" - - -inline bool hasPositiveWeights(const osg::Geometry* geometry) { - const osg::Vec4Array* weights = 0; - - for(unsigned int i = 0 ; i < geometry->getNumVertexAttribArrays() ; ++ i) { - const osg::Array* attribute = geometry->getVertexAttribArray(i); - bool isWeights = false; - if(attribute && attribute->getUserValue("weights", isWeights) && isWeights) { - weights = dynamic_cast(attribute); - break; - } - } - - if(weights) { - for(osg::Vec4Array::const_iterator weight = weights->begin() ; weight != weights->end() ; ++ weight) { - const osg::Vec4& value = *weight; - // weights are sorted in decreasing order - if(value[0] != 0.f) { - return true; - } - } - } - - return false; -} +#include "glesUtil" class HasGeometryVisitor : public osg::NodeVisitor { @@ -93,559 +67,54 @@ public: {} - void apply(osg::Node& node) { - osgAnimation::BasicAnimationManager* manager = getCallbackType(node.getUpdateCallback()); - if(manager) { - _managers[manager] = osg::ref_ptr(&node); - collectAnimationChannels(*manager); - } + void apply(osg::Node&); + void apply(osg::MatrixTransform&); + void apply(osg::Geometry&); - collectUpdateCallback(node); + void collectUpdateCallback(osg::Node&); + void collectAnimationChannels(osgAnimation::BasicAnimationManager&); - traverse(node); - } + virtual void clean(); + void removeAnimation(); - void apply(osg::MatrixTransform& transform) { - HasGeometryVisitor hasData; - transform.traverse(hasData); + void cleanInvalidMorphGeometries(); + void cleanInvalidRigGeometries(); + void cleanUnusedMorphTarget(); - if(!hasData.geometry) { - // if animation transforms have no child geometry they are cleanable - osgAnimation::Skeleton* skeleton = dynamic_cast(&transform); - osgAnimation::Bone* bone = dynamic_cast(&transform); - if(skeleton) { - _transforms.push_back(osg::ref_ptr(skeleton)); - } - if(bone) { - _transforms.push_back(osg::ref_ptr(bone)); - } - } - - osgAnimation::UpdateMatrixTransform* update = getCallbackType(transform.getUpdateCallback()); - if(update) { - _updates[update] = osg::ref_ptr(&transform); - } - - traverse(transform); - } - - - void apply(osg::Geometry& geometry) { - osgAnimation::MorphGeometry* morphGeometry = 0; - osgAnimation::RigGeometry* rigGeometry = dynamic_cast(&geometry); - - if(rigGeometry) { - if(std::find(_rigGeometries.begin(), _rigGeometries.end(), rigGeometry) == _rigGeometries.end()) { - _rigGeometries.push_back(rigGeometry); - } - morphGeometry = dynamic_cast(rigGeometry->getSourceGeometry()); - if(morphGeometry) { - _morphGeometries[morphGeometry] = rigGeometry; - } - } - else { - morphGeometry = dynamic_cast(&geometry); - if(morphGeometry && _morphGeometries.count(morphGeometry) == 0) { - _morphGeometries[morphGeometry] = 0; - } - } - - if(morphGeometry) { - typedef osgAnimation::MorphGeometry::MorphTargetList MorphTargetList; - MorphTargetList morphTargetList = morphGeometry->getMorphTargetList(); - for(MorphTargetList::iterator morphTarget = morphTargetList.begin(); morphTarget != morphTargetList.end(); ++morphTarget) { - osgAnimation::MorphGeometry::MorphTarget& target = *morphTarget; - _morphTargets[target.getGeometry()->getName()] = morphGeometry; - } - } - } - - // end of visitor::apply - - - void collectUpdateCallback(osg::Node& node) { - osg::Callback *callBack = node.getUpdateCallback(); - while(callBack) { - BaseAnimationUpdateCallback* update = getCallbackType(callBack); - if(update) { - _updates[update] = osg::ref_ptr(&node); - } - callBack = callBack->getNestedCallback(); - } - } - - void collectAnimationChannels(osgAnimation::BasicAnimationManager& manager) { - osgAnimation::AnimationList& animations = manager.getAnimationList(); - for(osgAnimation::AnimationList::iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) { - if(animation->valid()) { - osgAnimation::ChannelList& channels = (*animation)->getChannels(); - for(osgAnimation::ChannelList::iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) { - if(channel->valid()) { - _channels.push_back(std::pair((*channel)->getTargetName(), channel->get())); - } - } - } - } - } - - virtual void clean() { - // 1. clean scene graph to only keep 'valid' objects (top to bottom): - // * BasicAnimationManager - // * Animation - // * Target - // * deduplicate successive identical KeyFrames - // 2. check if there is still valid animation data in the scene graph. - // If no valid BasicAnimationManager is left, then clean all collected animation data. - if(_managers.size() == 0) { - OSG_WARN << "Monitor: animation.no_animation_manager" << std::endl; - } - else if(_managers.size() > 1) { - OSG_WARN << "Monitor: animation.multiple_animation_manager" << std::endl; - } - else { - OSG_WARN << "Monitor: animation.single_animation_manager" << std::endl; - } - - // only keep animations if we have a single animation manager - bool keepAnimations = _managers.size() == 1; - - cleanUnusedMorphTarget(); - cleanInvalidUpdateMorph(); - - for(BasicAnimationManagerMap::iterator manager = _managers.begin() ; keepAnimations && manager != _managers.end() ; ++ manager) { - cleanAnimations(*manager->first); - if(!isValidAnimationManager(*manager->first)) { - if(manager->second.valid()) { - manager->second->removeUpdateCallback(manager->first.get()); - } - keepAnimations = false; - OSG_WARN << "No valid animation data found. Removing all animation objects" << std::endl; - OSG_WARN << "Monitor: animation.disable_animation" << std::endl; - } - } - - if(!keepAnimations) { - removeAnimation(); - } - else - { - cleanInvalidMorphGeometries(); - cleanInvalidRigGeometries(); - } - } - - void removeAnimation() { - // * bake rig - // * replace animation geometries by static geometries - // * remove animation callbacks - // * remove animation transforms - bakeRigInitialPose(); - removeAnimatedGeometries(); - removeAnimationUpdateCallbacks(); - removeAnimationTransforms(); - } - - void cleanInvalidMorphGeometries() { - // Replace morph geometries by static geometries if: - // * empty morph target list - for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ) { - if(morphGeometry->first.valid()) { - if(morphGeometry->first.get()->getMorphTargetList().size() == 0) { - OSG_WARN << "Monitor: animation.invalid_morphgeometry" << std::endl; - replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second); - _morphGeometries.erase(morphGeometry ++); - } - else { - ++ morphGeometry; - } - } - } - } - - void cleanInvalidRigGeometries() { - // Replace rig geometries by static geometries if: - // * empty or inexistant vertex influence map - // * no *strictly* positive influence coefficient - for(RigGeometryList::iterator iterator = _rigGeometries.begin() ; iterator != _rigGeometries.end() ; ) { - osg::ref_ptr rigGeometry = *iterator; - if(rigGeometry.valid() && !hasPositiveWeights(rigGeometry->getSourceGeometry())) { - OSG_WARN << "Monitor: animation.invalid_riggeometry" << std::endl; - replaceRigGeometryBySource(*rigGeometry.get()); - iterator = _rigGeometries.erase(iterator); - } - else { - ++ iterator; - } - } - } - - void cleanUnusedMorphTarget() { - // Removes MorphGeometry targets not updated by any channel - std::set kept, removed; - - for(NameMorphMap::iterator targetMorph = _morphTargets.begin() ; targetMorph != _morphTargets.end() ; ) { - const std::string& target = targetMorph->first; - unsigned int count = 0; - for(TargetChannelList::const_iterator targetChannel = _channels.begin() ; targetChannel != _channels.end() ; ++ targetChannel) { - if(targetChannel->first == target) { - ++ count; - } - } - - if(count == 0) { - removed.insert(target); - targetMorph->second->removeMorphTarget(target); - _morphTargets.erase(targetMorph ++); - } - else { - kept.insert(target); - ++ targetMorph; - } - } - - if(!removed.empty()) { - OSG_WARN << "Monitor: animation.unused_morphtarget" << std::endl; - for(TargetChannelList::iterator targetChannel = _channels.begin() ; targetChannel != _channels.end() ; ) { - std::string target = targetChannel->first; - - if(removed.find(target) != removed.end()) { - _channels.erase(targetChannel ++); - } - else { - if(kept.find(target) != kept.end()) { - // update target channel names with the (possibly) new target index in MophTargetList - osgAnimation::MorphGeometry& morphGeometry = *_morphTargets[target]; - const osgAnimation::MorphGeometry::MorphTargetList& targets = morphGeometry.getMorphTargetList(); - for(unsigned int i = 0 ; i < targets.size() ; ++ i) { - if(targets[i].getGeometry()->getName() == target) { - std::ostringstream oss; - oss << i; - targetChannel->second->setName(oss.str()); - } - } - } - - ++ targetChannel; - } - } - } - } - - void cleanInvalidUpdateMorph() { - // Removes unused UpdateMorph targets (i.e. name does not match any MorphGeometry target) - for(AnimationUpdateCallBackMap::iterator update = _updates.begin() ; update != _updates.end() ; ++ update) { - osgAnimation::UpdateMorph *updateMorph = dynamic_cast(update->first.get()); - if(!updateMorph) continue; - - NameSet toRemove; - for(unsigned int i = 0, numTarget = updateMorph->getNumTarget(); i < numTarget; ++i) { - const std::string& name = updateMorph->getTargetName(i); - if(_morphTargets.count(name) == 0) { - toRemove.insert(name); - } - } - - for(NameSet::iterator targetName = toRemove.begin(); targetName != toRemove.end(); ++targetName) { - updateMorph->removeTarget(*targetName); - } - } - - // Removes empty UpdateMorphCallback - for(AnimationUpdateCallBackMap::iterator update = _updates.begin() ; update != _updates.end() ; ) { - osgAnimation::UpdateMorph *updateMorph = dynamic_cast(update->first.get()); - if(!updateMorph || updateMorph->getNumTarget() != 0) { - ++ update; - } - else { - osg::Callback *callBack = update->second.get()->getUpdateCallback(); - if(callBack) { - if(callBack == updateMorph) - update->second.get()->setUpdateCallback(callBack->getNestedCallback()); - else - callBack->removeNestedCallback(updateMorph); - } - _updates.erase(update ++); - } - } - } + void cleanInvalidUpdateMorph(); protected: - void warn(const std::string& method, const std::string& label, const osgAnimation::Channel& channel, const std::string& message) const { - OSG_WARN << std::flush << "Warning: " << "[" << method << "] " << "[[" << label << "]] " - << "Channel '" << channel.getName() << "' with target '" << channel.getTargetName() - << " '" << message << std::endl; - } + void warn(const std::string&, const std::string&, const osgAnimation::Channel&, const std::string&) const; template - T* getCallbackType(osg::Callback* callback) { - if(!callback) return 0; + T* getCallbackType(osg::Callback*); - T* callback_type = dynamic_cast(callback); - if(callback_type) { - return callback_type; - } + void cleanAnimations(osgAnimation::BasicAnimationManager&); + void cleanAnimation(osgAnimation::Animation&); + void cleanChannel(osgAnimation::Channel&); + bool isValidAnimationManager(const osgAnimation::BasicAnimationManager&) const; + bool isValidAnimation(const osgAnimation::Animation&) const; + bool isValidChannel(const osgAnimation::Channel&) const; - return getCallbackType(callback->getNestedCallback()); - } - - void cleanAnimations(osgAnimation::BasicAnimationManager& manager) { - // remove manager's invalid animations - osgAnimation::AnimationList& animations = manager.getAnimationList(); - - std::vector invalids; - for(osgAnimation::AnimationList::iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) { - if(animation->valid()) { - cleanAnimation(*animation->get()); - } - - if(!(animation->valid()) || !isValidAnimation(*animation->get())) { - invalids.push_back(animation->get()); - } - } - - for(std::vector::iterator invalid = invalids.begin() ; invalid != invalids.end() ; ++ invalid) { - manager.unregisterAnimation(*invalid); - } - } - - void cleanAnimation(osgAnimation::Animation& animation) { - // remove animation's invalid channels - osgAnimation::ChannelList& channels = animation.getChannels(); - osgAnimation::ChannelList invalids; - - for(osgAnimation::ChannelList::iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) { - if(channel->valid()) { - cleanChannel(*channel->get()); - } - - if(!channel->valid() || !isValidChannel(*channel->get())) { - invalids.push_back(channel->get()); - } - } - - for(osgAnimation::ChannelList::iterator invalid = invalids.begin() ; invalid != invalids.end() ; ++ invalid) { - animation.removeChannel(invalid->get()); - } - } - - void cleanChannel(osgAnimation::Channel& channel) { - // deduplicate successive KeyFrames that are identical - osgAnimation::Sampler* sampler = channel.getSampler(); - if(sampler) { - osgAnimation::KeyframeContainer* container = sampler->getKeyframeContainer(); - if(container && container->size()) { - unsigned int deduplicated = container->linearInterpolationDeduplicate(); - if(deduplicated) { - OSG_WARN << "Deduplicated " << deduplicated << " keyframes on channel " << channel.getName() << std::endl; - } - } - } - } - - bool isValidAnimationManager(const osgAnimation::BasicAnimationManager& manager) const { - // a valid manager has at least one animation and all animations must be valid - const osgAnimation::AnimationList& animations = manager.getAnimationList(); - - for(osgAnimation::AnimationList::const_iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) { - if(!animation->valid() || !isValidAnimation(*animation->get())) { - return false; - } - } - return manager.getAnimationList().size() > 0; - } - - bool isValidAnimation(const osgAnimation::Animation& animation) const { - // a valid animation has at least one channel and all channels must be valid - const osgAnimation::ChannelList& channels = animation.getChannels(); - - for(osgAnimation::ChannelList::const_iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) { - if(!channel->valid() || !isValidChannel(*channel->get())) { - return false; - } - } - - return channels.size() > 0; - } - - bool isValidChannel(const osgAnimation::Channel& channel) const { - // a valid channel has valid target i.e. - // 1. there exists some UpdateMatrixTransform with channel's target name - // 2. the channel does not simply mimick the UpdateMatrixTransform content - std::string target = channel.getTargetName(); - for(AnimationUpdateCallBackMap::const_iterator update = _updates.begin() ; update != _updates.end() ; ++ update) { - osgAnimation::UpdateMorph *updateMorph = dynamic_cast(update->first.get()); - if(updateMorph) { - for(int i = 0, num = updateMorph->getNumTarget(); i < num; ++i) { - if(updateMorph->getTargetName(i) == target) { - return true; - } - } - } - else { - if(update->first->getName() == target) { - // check if channel contains necessary data or just mimick the UpdateCallback's StackedTransform - bool channelMimickingTransform = isChannelEqualToStackedTransform(channel, - dynamic_cast(update->first.get())); - if(channelMimickingTransform) { - warn("isChannelEqualToStackedTransform", "animation", channel, "seems redundant with stacked transform and has been removed."); - } - return !channelMimickingTransform; - } - } - } - return false; - } - - const osgAnimation::StackedTransformElement* getStackedElement(const osgAnimation::StackedTransform& transforms, const std::string& name) const { - for(osgAnimation::StackedTransform::const_iterator transform = transforms.begin() ; transform != transforms.end() ; ++ transform) { - if(transform->valid() && transform->get()->getName() == name) { - return transform->get(); - } - } - return 0; - } - - bool isChannelEqualToStackedTransform(const osgAnimation::Channel& channel, const osgAnimation::UpdateMatrixTransform* matrixTransform) const { - // if the channel has no 'StackedTransform' we compare the KeyFrame with the 'no-op' transform - const osgAnimation::StackedTransformElement* element = getStackedElement(matrixTransform->getStackedTransforms(), channel.getName()); - - if(channel.getName() == "translate") { - const osgAnimation::StackedTranslateElement* translation = dynamic_cast(element); - osg::Vec3 value(0., 0., 0.); - if(translation) { - value = translation->getTranslate(); - } - return isChannelEqualToStackedTransform(dynamic_cast(&channel), value); - } - else if(channel.getName() == "scale") { - const osgAnimation::StackedScaleElement* scale = dynamic_cast(element); - osg::Vec3 value(1., 1., 1.); - if(scale) { - value = scale->getScale(); - } - return isChannelEqualToStackedTransform(dynamic_cast(&channel), value); - } - else if(channel.getName() == "rotate") { - const osgAnimation::StackedQuaternionElement* rotation = dynamic_cast(element); - osg::Quat value(0., 0., 0., 1.); - if(rotation) { - value = rotation->getQuaternion(); - } - return isChannelEqualToStackedTransform(dynamic_cast(&channel), value); - } - return false; - } + const osgAnimation::StackedTransformElement* getStackedElement(const osgAnimation::StackedTransform&, const std::string&) const; + bool isChannelEqualToStackedTransform(const osgAnimation::Channel&, const osgAnimation::UpdateMatrixTransform*) const; template - bool isChannelEqualToStackedTransform(const T* channel, const V& value) const { - if(!channel) { - return false; - } - - const typename T::KeyframeContainerType* keys = channel->getSamplerTyped()->getKeyframeContainerTyped(); - if(keys->size() == 0) { - // channel with no keyframe is equal to anything - return true; - } - if(keys->size() == 1) { - return (*keys)[0].getValue() == value; - } - return false; - } - - void removeAnimationUpdateCallbacks() { - removeUpdateCallbacksTemplate(_updates); - removeUpdateCallbacksTemplate(_managers); - } + bool isChannelEqualToStackedTransform(const T*, const V&) const; + void removeAnimationUpdateCallbacks(); template - void removeUpdateCallbacksTemplate(C& callbackNodes) { - for(typename C::iterator callbackNode = callbackNodes.begin() ; callbackNode != callbackNodes.end() ; ++ callbackNode) { - if(callbackNode->first && callbackNode->second.valid()) { - osg::Callback* callback = callbackNode->first.get(); - osg::Node* node = callbackNode->second.get(); - do { - node->removeUpdateCallback(callback); - callback = getCallbackType(node->getUpdateCallback()); - } - while(callback); - } - } - } + void removeUpdateCallbacksTemplate(C&); - void removeAnimationTransforms() { - for(MatrixTransformList::iterator transform = _transforms.begin() ; transform != _transforms.end() ; ++ transform) { - if(transform->valid()) { - removeFromParents(transform->get()); - } - } - } + void removeAnimationTransforms(); + void removeAnimatedGeometries(); + void removeFromParents(osg::Node*); - void removeAnimatedGeometries() { - for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ++ morphGeometry) { - if(morphGeometry->first.valid()) { - replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second); - } - } + void replaceRigGeometryBySource(osgAnimation::RigGeometry&) const; + void replaceMorphGeometryByGeometry(osgAnimation::MorphGeometry&, osgAnimation::RigGeometry* /*rigGeometry*/=0) const; + void replaceAnimatedGeometryByStaticGeometry(osg::Geometry*, osg::Geometry*) const; - for(RigGeometryList::iterator rigGeometry = _rigGeometries.begin() ; rigGeometry != _rigGeometries.end() ; ++ rigGeometry) { - if(rigGeometry->valid()) { - replaceRigGeometryBySource(*(rigGeometry->get())); - } - } - } - - void removeFromParents(osg::Node* node) { - osg::Node::ParentList parents = node->getParents(); - for(osg::Node::ParentList::iterator parent = parents.begin() ; parent != parents.end() ; ++ parent) { - if(*parent) { - (*parent)->removeChild(node); - } - } - } - - void replaceRigGeometryBySource(osgAnimation::RigGeometry& rigGeometry) const { - if(osgAnimation::MorphGeometry* source = dynamic_cast(rigGeometry.getSourceGeometry())) { - osgAnimation::MorphGeometry* morph = new osgAnimation::MorphGeometry(*source); - replaceAnimatedGeometryByStaticGeometry(&rigGeometry, morph); - } - else { - replaceAnimatedGeometryByStaticGeometry(&rigGeometry, - new osg::Geometry(*rigGeometry.getSourceGeometry())); - } - } - - void replaceMorphGeometryByGeometry(osgAnimation::MorphGeometry& morphGeometry, osgAnimation::RigGeometry* rigGeometry=0) const { - osg::Geometry* geometry = new osg::Geometry(morphGeometry); - if(!rigGeometry) { - replaceAnimatedGeometryByStaticGeometry(&morphGeometry, geometry); - } - else { - rigGeometry->setSourceGeometry(geometry); - } - } - - void replaceAnimatedGeometryByStaticGeometry(osg::Geometry* animatedGeometry, osg::Geometry* staticGeometry) const { - for(unsigned int i = 0 ; i < animatedGeometry->getNumParents() ; ++ i) { - osg::Geode* parent = (animatedGeometry->getParent(i) ? animatedGeometry->getParent(i)->asGeode() : 0); - if(parent) { - parent->addDrawable(staticGeometry); - parent->removeDrawable(animatedGeometry); - } - } - } - - void bakeRigInitialPose() { - // use RigTransformSoftware to compute T-pose and replace rig source by computed geometry - for(RigGeometryList::iterator rigiterator = _rigGeometries.begin() ; rigiterator != _rigGeometries.end() ; ++ rigiterator) { - osgAnimation::RigGeometry* rigGeometry = (*rigiterator).get(); - rigGeometry->setRigTransformImplementation(new osgAnimation::RigTransformSoftware); - rigGeometry->update(); - - osg::Geometry* baked = static_cast(rigGeometry->clone(osg::CopyOp::DEEP_COPY_ALL)); - rigGeometry->setSourceGeometry(baked); - } - } + void bakeRigInitialPose(); protected: BasicAnimationManagerMap _managers; diff --git a/src/osgPlugins/gles/AnimationCleanerVisitor.cpp b/src/osgPlugins/gles/AnimationCleanerVisitor.cpp new file mode 100644 index 000000000..cb692675d --- /dev/null +++ b/src/osgPlugins/gles/AnimationCleanerVisitor.cpp @@ -0,0 +1,589 @@ +#include "AnimationCleanerVisitor" + + + +void AnimationCleanerVisitor::apply(osg::Node& node) { + osgAnimation::BasicAnimationManager* manager = getCallbackType(node.getUpdateCallback()); + if(manager) { + _managers[manager] = osg::ref_ptr(&node); + collectAnimationChannels(*manager); + } + + collectUpdateCallback(node); + + traverse(node); +} + + +void AnimationCleanerVisitor::apply(osg::MatrixTransform& transform) { + HasGeometryVisitor hasData; + transform.traverse(hasData); + + if(!hasData.geometry) { + // if animation transforms have no child geometry they are cleanable + osgAnimation::Skeleton* skeleton = dynamic_cast(&transform); + osgAnimation::Bone* bone = dynamic_cast(&transform); + if(skeleton) { + _transforms.push_back(osg::ref_ptr(skeleton)); + } + if(bone) { + _transforms.push_back(osg::ref_ptr(bone)); + } + } + + osgAnimation::UpdateMatrixTransform* update = getCallbackType(transform.getUpdateCallback()); + if(update) { + _updates[update] = osg::ref_ptr(&transform); + } + + traverse(transform); +} + + +void AnimationCleanerVisitor::apply(osg::Geometry& geometry) { + osgAnimation::MorphGeometry* morphGeometry = 0; + osgAnimation::RigGeometry* rigGeometry = dynamic_cast(&geometry); + + if(rigGeometry) { + if(std::find(_rigGeometries.begin(), _rigGeometries.end(), rigGeometry) == _rigGeometries.end()) { + _rigGeometries.push_back(rigGeometry); + } + morphGeometry = dynamic_cast(rigGeometry->getSourceGeometry()); + if(morphGeometry) { + _morphGeometries[morphGeometry] = rigGeometry; + } + } + else { + morphGeometry = dynamic_cast(&geometry); + if(morphGeometry && _morphGeometries.count(morphGeometry) == 0) { + _morphGeometries[morphGeometry] = 0; + } + } + + if(morphGeometry) { + typedef osgAnimation::MorphGeometry::MorphTargetList MorphTargetList; + MorphTargetList morphTargetList = morphGeometry->getMorphTargetList(); + for(MorphTargetList::iterator morphTarget = morphTargetList.begin(); morphTarget != morphTargetList.end(); ++morphTarget) { + osgAnimation::MorphGeometry::MorphTarget& target = *morphTarget; + _morphTargets[target.getGeometry()->getName()] = morphGeometry; + } + } +} + + +void AnimationCleanerVisitor::collectUpdateCallback(osg::Node& node) { + osg::Callback *callBack = node.getUpdateCallback(); + while(callBack) { + BaseAnimationUpdateCallback* update = getCallbackType(callBack); + if(update) { + _updates[update] = osg::ref_ptr(&node); + } + callBack = callBack->getNestedCallback(); + } +} + + +void AnimationCleanerVisitor::collectAnimationChannels(osgAnimation::BasicAnimationManager& manager) { + osgAnimation::AnimationList& animations = manager.getAnimationList(); + for(osgAnimation::AnimationList::iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) { + if(animation->valid()) { + osgAnimation::ChannelList& channels = (*animation)->getChannels(); + for(osgAnimation::ChannelList::iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) { + if(channel->valid()) { + _channels.push_back(std::pair((*channel)->getTargetName(), channel->get())); + } + } + } + } +} + + +void AnimationCleanerVisitor::clean() { + // 1. clean scene graph to only keep 'valid' objects (top to bottom): + // * BasicAnimationManager + // * Animation + // * Target + // * deduplicate successive identical KeyFrames + // 2. check if there is still valid animation data in the scene graph. + // If no valid BasicAnimationManager is left, then clean all collected animation data. + if(_managers.size() == 0) { + OSG_WARN << "Monitor: animation.no_animation_manager" << std::endl; + } + else if(_managers.size() > 1) { + OSG_WARN << "Monitor: animation.multiple_animation_manager" << std::endl; + } + else { + OSG_WARN << "Monitor: animation.single_animation_manager" << std::endl; + } + + // only keep animations if we have a single animation manager + bool keepAnimations = _managers.size() == 1; + + cleanUnusedMorphTarget(); + cleanInvalidUpdateMorph(); + + for(BasicAnimationManagerMap::iterator manager = _managers.begin() ; keepAnimations && manager != _managers.end() ; ++ manager) { + cleanAnimations(*manager->first); + if(!isValidAnimationManager(*manager->first)) { + if(manager->second.valid()) { + manager->second->removeUpdateCallback(manager->first.get()); + } + keepAnimations = false; + OSG_WARN << "No valid animation data found. Removing all animation objects" << std::endl; + OSG_WARN << "Monitor: animation.disable_animation" << std::endl; + } + } + + if(!keepAnimations) { + removeAnimation(); + } + else + { + cleanInvalidMorphGeometries(); + cleanInvalidRigGeometries(); + } +} + + +void AnimationCleanerVisitor::removeAnimation() { + // * bake rig + // * replace animation geometries by static geometries + // * remove animation callbacks + // * remove animation transforms + bakeRigInitialPose(); + removeAnimatedGeometries(); + removeAnimationUpdateCallbacks(); + removeAnimationTransforms(); +} + + +void AnimationCleanerVisitor::cleanInvalidMorphGeometries() { + // Replace morph geometries by static geometries if: + // * empty morph target list + for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ) { + if(morphGeometry->first.valid()) { + if(morphGeometry->first.get()->getMorphTargetList().size() == 0) { + OSG_WARN << "Monitor: animation.invalid_morphgeometry" << std::endl; + replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second); + _morphGeometries.erase(morphGeometry ++); + } + else { + ++ morphGeometry; + } + } + } +} + + +void AnimationCleanerVisitor::cleanInvalidRigGeometries() { + // Replace rig geometries by static geometries if: + // * empty or inexistant vertex influence map + // * no *strictly* positive influence coefficient + for(RigGeometryList::iterator iterator = _rigGeometries.begin() ; iterator != _rigGeometries.end() ; ) { + osg::ref_ptr rigGeometry = *iterator; + if(rigGeometry.valid() && !glesUtil::hasPositiveWeights(rigGeometry->getSourceGeometry())) { + OSG_WARN << "Monitor: animation.invalid_riggeometry" << std::endl; + replaceRigGeometryBySource(*rigGeometry.get()); + iterator = _rigGeometries.erase(iterator); + } + else { + ++ iterator; + } + } +} + + +void AnimationCleanerVisitor::cleanUnusedMorphTarget() { + // Removes MorphGeometry targets not updated by any channel + std::set kept, removed; + + for(NameMorphMap::iterator targetMorph = _morphTargets.begin() ; targetMorph != _morphTargets.end() ; ) { + const std::string& target = targetMorph->first; + unsigned int count = 0; + for(TargetChannelList::const_iterator targetChannel = _channels.begin() ; targetChannel != _channels.end() ; ++ targetChannel) { + if(targetChannel->first == target) { + ++ count; + } + } + + if(count == 0) { + removed.insert(target); + targetMorph->second->removeMorphTarget(target); + _morphTargets.erase(targetMorph ++); + } + else { + kept.insert(target); + ++ targetMorph; + } + } + + if(!removed.empty()) { + OSG_WARN << "Monitor: animation.unused_morphtarget" << std::endl; + for(TargetChannelList::iterator targetChannel = _channels.begin() ; targetChannel != _channels.end() ; ) { + std::string target = targetChannel->first; + + if(removed.find(target) != removed.end()) { + _channels.erase(targetChannel ++); + } + else { + if(kept.find(target) != kept.end()) { + // update target channel names with the (possibly) new target index in MophTargetList + osgAnimation::MorphGeometry& morphGeometry = *_morphTargets[target]; + const osgAnimation::MorphGeometry::MorphTargetList& targets = morphGeometry.getMorphTargetList(); + for(unsigned int i = 0 ; i < targets.size() ; ++ i) { + if(targets[i].getGeometry()->getName() == target) { + std::ostringstream oss; + oss << i; + targetChannel->second->setName(oss.str()); + } + } + } + + ++ targetChannel; + } + } + } +} + + +void AnimationCleanerVisitor::cleanInvalidUpdateMorph() { + // Removes unused UpdateMorph targets (i.e. name does not match any MorphGeometry target) + for(AnimationUpdateCallBackMap::iterator update = _updates.begin() ; update != _updates.end() ; ++ update) { + osgAnimation::UpdateMorph *updateMorph = dynamic_cast(update->first.get()); + if(!updateMorph) continue; + + NameSet toRemove; + for(unsigned int i = 0, numTarget = updateMorph->getNumTarget(); i < numTarget; ++i) { + const std::string& name = updateMorph->getTargetName(i); + if(_morphTargets.count(name) == 0) { + toRemove.insert(name); + } + } + + for(NameSet::iterator targetName = toRemove.begin(); targetName != toRemove.end(); ++targetName) { + updateMorph->removeTarget(*targetName); + } + } + + // Removes empty UpdateMorphCallback + for(AnimationUpdateCallBackMap::iterator update = _updates.begin() ; update != _updates.end() ; ) { + osgAnimation::UpdateMorph *updateMorph = dynamic_cast(update->first.get()); + if(!updateMorph || updateMorph->getNumTarget() != 0) { + ++ update; + } + else { + osg::Callback *callBack = update->second.get()->getUpdateCallback(); + if(callBack) { + if(callBack == updateMorph) + update->second.get()->setUpdateCallback(callBack->getNestedCallback()); + else + callBack->removeNestedCallback(updateMorph); + } + _updates.erase(update ++); + } + } +} + + +void AnimationCleanerVisitor::warn(const std::string& method, + const std::string& label, + const osgAnimation::Channel& channel, + const std::string& message) const { + OSG_WARN << std::flush << "Warning: " << "[" << method << "] " << "[[" << label << "]] " + << "Channel '" << channel.getName() << "' with target '" << channel.getTargetName() + << " '" << message << std::endl; +} + + +template +T* AnimationCleanerVisitor::getCallbackType(osg::Callback* callback) { + if(!callback) return 0; + + T* callback_type = dynamic_cast(callback); + if(callback_type) { + return callback_type; + } + + return getCallbackType(callback->getNestedCallback()); +} + + +void AnimationCleanerVisitor::cleanAnimations(osgAnimation::BasicAnimationManager& manager) { + // remove manager's invalid animations + osgAnimation::AnimationList& animations = manager.getAnimationList(); + + std::vector invalids; + for(osgAnimation::AnimationList::iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) { + if(animation->valid()) { + cleanAnimation(*animation->get()); + } + + if(!(animation->valid()) || !isValidAnimation(*animation->get())) { + invalids.push_back(animation->get()); + } + } + + for(std::vector::iterator invalid = invalids.begin() ; invalid != invalids.end() ; ++ invalid) { + manager.unregisterAnimation(*invalid); + } +} + + +void AnimationCleanerVisitor::cleanAnimation(osgAnimation::Animation& animation) { + // remove animation's invalid channels + osgAnimation::ChannelList& channels = animation.getChannels(); + osgAnimation::ChannelList invalids; + + for(osgAnimation::ChannelList::iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) { + if(channel->valid()) { + cleanChannel(*channel->get()); + } + + if(!channel->valid() || !isValidChannel(*channel->get())) { + invalids.push_back(channel->get()); + } + } + + for(osgAnimation::ChannelList::iterator invalid = invalids.begin() ; invalid != invalids.end() ; ++ invalid) { + animation.removeChannel(invalid->get()); + } +} + + +void AnimationCleanerVisitor::cleanChannel(osgAnimation::Channel& channel) { + // deduplicate successive KeyFrames that are identical + osgAnimation::Sampler* sampler = channel.getSampler(); + if(sampler) { + osgAnimation::KeyframeContainer* container = sampler->getKeyframeContainer(); + if(container && container->size()) { + unsigned int deduplicated = container->linearInterpolationDeduplicate(); + if(deduplicated) { + OSG_WARN << "Deduplicated " << deduplicated << " keyframes on channel " << channel.getName() << std::endl; + } + } + } +} + + +bool AnimationCleanerVisitor::isValidAnimationManager(const osgAnimation::BasicAnimationManager& manager) const { + // a valid manager has at least one animation and all animations must be valid + const osgAnimation::AnimationList& animations = manager.getAnimationList(); + + for(osgAnimation::AnimationList::const_iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) { + if(!animation->valid() || !isValidAnimation(*animation->get())) { + return false; + } + } + return manager.getAnimationList().size() > 0; +} + + +bool AnimationCleanerVisitor::isValidAnimation(const osgAnimation::Animation& animation) const { + // a valid animation has at least one channel and all channels must be valid + const osgAnimation::ChannelList& channels = animation.getChannels(); + + for(osgAnimation::ChannelList::const_iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) { + if(!channel->valid() || !isValidChannel(*channel->get())) { + return false; + } + } + + return channels.size() > 0; +} + + +bool AnimationCleanerVisitor::isValidChannel(const osgAnimation::Channel& channel) const { + // a valid channel has valid target i.e. + // 1. there exists some UpdateMatrixTransform with channel's target name + // 2. the channel does not simply mimick the UpdateMatrixTransform content + std::string target = channel.getTargetName(); + for(AnimationUpdateCallBackMap::const_iterator update = _updates.begin() ; update != _updates.end() ; ++ update) { + osgAnimation::UpdateMorph *updateMorph = dynamic_cast(update->first.get()); + if(updateMorph) { + for(int i = 0, num = updateMorph->getNumTarget(); i < num; ++i) { + if(updateMorph->getTargetName(i) == target) { + return true; + } + } + } + else { + if(update->first->getName() == target) { + // check if channel contains necessary data or just mimick the UpdateCallback's StackedTransform + bool channelMimickingTransform = isChannelEqualToStackedTransform(channel, + dynamic_cast(update->first.get())); + if(channelMimickingTransform) { + warn("isChannelEqualToStackedTransform", "animation", channel, "seems redundant with stacked transform and has been removed."); + } + return !channelMimickingTransform; + } + } + } + return false; +} + + +const osgAnimation::StackedTransformElement* AnimationCleanerVisitor::getStackedElement(const osgAnimation::StackedTransform& transforms, + const std::string& name) const { + for(osgAnimation::StackedTransform::const_iterator transform = transforms.begin() ; transform != transforms.end() ; ++ transform) { + if(transform->valid() && transform->get()->getName() == name) { + return transform->get(); + } + } + return 0; +} + + +bool AnimationCleanerVisitor::isChannelEqualToStackedTransform(const osgAnimation::Channel& channel, + const osgAnimation::UpdateMatrixTransform* matrixTransform) const { + // if the channel has no 'StackedTransform' we compare the KeyFrame with the 'no-op' transform + const osgAnimation::StackedTransformElement* element = getStackedElement(matrixTransform->getStackedTransforms(), channel.getName()); + + if(channel.getName() == "translate") { + const osgAnimation::StackedTranslateElement* translation = dynamic_cast(element); + osg::Vec3 value(0., 0., 0.); + if(translation) { + value = translation->getTranslate(); + } + return isChannelEqualToStackedTransform(dynamic_cast(&channel), value); + } + else if(channel.getName() == "scale") { + const osgAnimation::StackedScaleElement* scale = dynamic_cast(element); + osg::Vec3 value(1., 1., 1.); + if(scale) { + value = scale->getScale(); + } + return isChannelEqualToStackedTransform(dynamic_cast(&channel), value); + } + else if(channel.getName() == "rotate") { + const osgAnimation::StackedQuaternionElement* rotation = dynamic_cast(element); + osg::Quat value(0., 0., 0., 1.); + if(rotation) { + value = rotation->getQuaternion(); + } + return isChannelEqualToStackedTransform(dynamic_cast(&channel), value); + } + return false; +} + + +template +bool AnimationCleanerVisitor::isChannelEqualToStackedTransform(const T* channel, const V& value) const { + if(!channel) { + return false; + } + + const typename T::KeyframeContainerType* keys = channel->getSamplerTyped()->getKeyframeContainerTyped(); + if(keys->size() == 0) { + // channel with no keyframe is equal to anything + return true; + } + if(keys->size() == 1) { + return (*keys)[0].getValue() == value; + } + return false; +} + + +void AnimationCleanerVisitor::removeAnimationUpdateCallbacks() { + removeUpdateCallbacksTemplate(_updates); + removeUpdateCallbacksTemplate(_managers); +} + + +template +void AnimationCleanerVisitor::removeUpdateCallbacksTemplate(C& callbackNodes) { + for(typename C::iterator callbackNode = callbackNodes.begin() ; callbackNode != callbackNodes.end() ; ++ callbackNode) { + if(callbackNode->first && callbackNode->second.valid()) { + osg::Callback* callback = callbackNode->first.get(); + osg::Node* node = callbackNode->second.get(); + do { + node->removeUpdateCallback(callback); + callback = getCallbackType(node->getUpdateCallback()); + } + while(callback); + } + } +} + + +void AnimationCleanerVisitor::removeAnimationTransforms() { + for(MatrixTransformList::iterator transform = _transforms.begin() ; transform != _transforms.end() ; ++ transform) { + if(transform->valid()) { + removeFromParents(transform->get()); + } + } +} + + +void AnimationCleanerVisitor::removeAnimatedGeometries() { + for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ++ morphGeometry) { + if(morphGeometry->first.valid()) { + replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second); + } + } + + for(RigGeometryList::iterator rigGeometry = _rigGeometries.begin() ; rigGeometry != _rigGeometries.end() ; ++ rigGeometry) { + if(rigGeometry->valid()) { + replaceRigGeometryBySource(*(rigGeometry->get())); + } + } +} + + +void AnimationCleanerVisitor::removeFromParents(osg::Node* node) { + osg::Node::ParentList parents = node->getParents(); + for(osg::Node::ParentList::iterator parent = parents.begin() ; parent != parents.end() ; ++ parent) { + if(*parent) { + (*parent)->removeChild(node); + } + } +} + + +void AnimationCleanerVisitor::replaceRigGeometryBySource(osgAnimation::RigGeometry& rigGeometry) const { + if(osgAnimation::MorphGeometry* source = dynamic_cast(rigGeometry.getSourceGeometry())) { + osgAnimation::MorphGeometry* morph = new osgAnimation::MorphGeometry(*source); + replaceAnimatedGeometryByStaticGeometry(&rigGeometry, morph); + } + else { + replaceAnimatedGeometryByStaticGeometry(&rigGeometry, + new osg::Geometry(*rigGeometry.getSourceGeometry())); + } +} + + +void AnimationCleanerVisitor::replaceMorphGeometryByGeometry(osgAnimation::MorphGeometry& morphGeometry, + osgAnimation::RigGeometry* rigGeometry) const { + osg::Geometry* geometry = new osg::Geometry(morphGeometry); + if(!rigGeometry) { + replaceAnimatedGeometryByStaticGeometry(&morphGeometry, geometry); + } + else { + rigGeometry->setSourceGeometry(geometry); + } +} + + +void AnimationCleanerVisitor::replaceAnimatedGeometryByStaticGeometry(osg::Geometry* animatedGeometry, + osg::Geometry* staticGeometry) const { + for(unsigned int i = 0 ; i < animatedGeometry->getNumParents() ; ++ i) { + osg::Geode* parent = (animatedGeometry->getParent(i) ? animatedGeometry->getParent(i)->asGeode() : 0); + if(parent) { + parent->addDrawable(staticGeometry); + parent->removeDrawable(animatedGeometry); + } + } +} + + +void AnimationCleanerVisitor::bakeRigInitialPose() { + // use RigTransformSoftware to compute T-pose and replace rig source by computed geometry + for(RigGeometryList::iterator rigiterator = _rigGeometries.begin() ; rigiterator != _rigGeometries.end() ; ++ rigiterator) { + osgAnimation::RigGeometry* rigGeometry = (*rigiterator).get(); + rigGeometry->setRigTransformImplementation(new osgAnimation::RigTransformSoftware); + rigGeometry->update(); + + osg::Geometry* baked = static_cast(rigGeometry->clone(osg::CopyOp::DEEP_COPY_ALL)); + rigGeometry->setSourceGeometry(baked); + } +} diff --git a/src/osgPlugins/gles/BindPerVertexVisitor b/src/osgPlugins/gles/BindPerVertexVisitor index 04926e260..0a5d82aa3 100644 --- a/src/osgPlugins/gles/BindPerVertexVisitor +++ b/src/osgPlugins/gles/BindPerVertexVisitor @@ -19,289 +19,20 @@ // TODO: deprecated class BindPerVertexVisitor : public GeometryUniqueVisitor { public: - BindPerVertexVisitor(): GeometryUniqueVisitor("BindPerVertexVisitor") + BindPerVertexVisitor(): + GeometryUniqueVisitor("BindPerVertexVisitor") {} - void process(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); - } - }; + void process(osg::Geometry&); protected: - void bindPerVertex(osg::Array* src, - osg::Geometry::AttributeBinding fromBinding, - osg::Geometry::PrimitiveSetList& primitives) { - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - if (doConvert(src, fromBinding, primitives)) - return; - if (doConvert(src, fromBinding, primitives)) - return; - - } + void bindPerVertex(osg::Array*, osg::Geometry::AttributeBinding, osg::Geometry::PrimitiveSetList&); template - bool doConvert(osg::Array* src, - osg::Geometry::AttributeBinding fromBinding, - osg::Geometry::PrimitiveSetList& primitives) { - T* array= dynamic_cast(src); - if (array) { - convert(*array, fromBinding, primitives); - return true; - } - return false; - } + bool doConvert(osg::Array*, osg::Geometry::AttributeBinding, osg::Geometry::PrimitiveSetList&); template - void convert(T& array, - osg::Geometry::AttributeBinding fromBinding, - osg::Geometry::PrimitiveSetList& primitives) - { - osg::ref_ptr result = new T(); - for (unsigned int p = 0; p < primitives.size(); p++) { - switch ( primitives[p]->getMode() ) { - case osg::PrimitiveSet::POINTS: - osg::notify(osg::WARN) << "ConvertToBindPerVertex not supported for POINTS" << std::endl; - break; - - case osg::PrimitiveSet::LINE_STRIP: - switch(fromBinding) { - case osg::Geometry::BIND_OFF: - case osg::Geometry::BIND_PER_VERTEX: - break; - case osg::Geometry::BIND_OVERALL: - { - for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) - result->push_back(array[0]); - } - break; - case osg::Geometry::BIND_PER_PRIMITIVE_SET: - { - unsigned int nb = primitives[p]->getNumIndices(); - for (unsigned int i = 0; i < nb; i++) - result->push_back(array[p]); - } - break; - } - break; - - case osg::PrimitiveSet::LINES: - switch(fromBinding) { - case osg::Geometry::BIND_OFF: - case osg::Geometry::BIND_PER_VERTEX: - break; - case osg::Geometry::BIND_OVERALL: - { - for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) - result->push_back(array[0]); - } - break; - case osg::Geometry::BIND_PER_PRIMITIVE_SET: - { - unsigned int nb = primitives[p]->getNumIndices(); - for (unsigned int i = 0; i < nb; i++) - result->push_back(array[p]); - } - break; - } - break; - - case osg::PrimitiveSet::TRIANGLES: - switch(fromBinding) { - case osg::Geometry::BIND_OFF: - case osg::Geometry::BIND_PER_VERTEX: - break; - case osg::Geometry::BIND_OVERALL: - { - for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) - result->push_back(array[0]); - } - break; - case osg::Geometry::BIND_PER_PRIMITIVE_SET: - { - unsigned int nb = primitives[p]->getNumIndices(); - for (unsigned int i = 0; i < nb; i++) - result->push_back(array[p]); - } - break; - } - break; - - case osg::PrimitiveSet::TRIANGLE_STRIP: - switch(fromBinding) { - case osg::Geometry::BIND_OFF: - case osg::Geometry::BIND_PER_VERTEX: - break; - case osg::Geometry::BIND_OVERALL: - { - for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) - result->push_back(array[0]); - } - break; - case osg::Geometry::BIND_PER_PRIMITIVE_SET: - { - osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_STRIP" << std::endl; - } - break; - } - break; - - case osg::PrimitiveSet::TRIANGLE_FAN: - switch(fromBinding) { - case osg::Geometry::BIND_OFF: - case osg::Geometry::BIND_PER_VERTEX: - break; - case osg::Geometry::BIND_OVERALL: - { - for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) - result->push_back(array[0]); - } - break; - case osg::Geometry::BIND_PER_PRIMITIVE_SET: - { - osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_FAN" << std::endl; - } - break; - } - break; - - case osg::PrimitiveSet::QUADS: - switch(fromBinding) { - case osg::Geometry::BIND_OFF: - case osg::Geometry::BIND_PER_VERTEX: - break; - case osg::Geometry::BIND_OVERALL: - { - for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) - result->push_back(array[0]); - } - break; - case osg::Geometry::BIND_PER_PRIMITIVE_SET: - { - osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUADS" << std::endl; - } - break; - } - break; - - case osg::PrimitiveSet::QUAD_STRIP: - switch(fromBinding) { - case osg::Geometry::BIND_OFF: - case osg::Geometry::BIND_PER_VERTEX: - break; - case osg::Geometry::BIND_OVERALL: - { - for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) - result->push_back(array[0]); - } - break; - case osg::Geometry::BIND_PER_PRIMITIVE_SET: - { - osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUAD_STRIP" << std::endl; - } - break; - } - break; - } - } - array = *result; - } + void convert(T&, osg::Geometry::AttributeBinding, osg::Geometry::PrimitiveSetList&); }; #endif diff --git a/src/osgPlugins/gles/BindPerVertexVisitor.cpp b/src/osgPlugins/gles/BindPerVertexVisitor.cpp new file mode 100644 index 000000000..c65c294af --- /dev/null +++ b/src/osgPlugins/gles/BindPerVertexVisitor.cpp @@ -0,0 +1,283 @@ +#include "BindPerVertexVisitor" + + +void BindPerVertexVisitor::process(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); + } +} + + +void BindPerVertexVisitor::bindPerVertex(osg::Array* src, + osg::Geometry::AttributeBinding fromBinding, + osg::Geometry::PrimitiveSetList& primitives) { + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; + + if (doConvert(src, fromBinding, primitives)) + return; + if (doConvert(src, fromBinding, primitives)) + return; +} + + +template +bool BindPerVertexVisitor::doConvert(osg::Array* src, + osg::Geometry::AttributeBinding fromBinding, + osg::Geometry::PrimitiveSetList& primitives) { + T* array= dynamic_cast(src); + if (array) { + convert(*array, fromBinding, primitives); + return true; + } + return false; +} + + +template +void BindPerVertexVisitor::convert(T& array, + osg::Geometry::AttributeBinding fromBinding, + osg::Geometry::PrimitiveSetList& primitives) { + osg::ref_ptr result = new T(); + for (unsigned int p = 0; p < primitives.size(); p++) { + switch ( primitives[p]->getMode() ) { + case osg::PrimitiveSet::POINTS: + osg::notify(osg::WARN) << "ConvertToBindPerVertex not supported for POINTS" << std::endl; + break; + + case osg::PrimitiveSet::LINE_STRIP: + switch(fromBinding) { + case osg::Geometry::BIND_OFF: + case osg::Geometry::BIND_PER_VERTEX: + break; + case osg::Geometry::BIND_OVERALL: + { + for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) + result->push_back(array[0]); + } + break; + case osg::Geometry::BIND_PER_PRIMITIVE_SET: + { + unsigned int nb = primitives[p]->getNumIndices(); + for (unsigned int i = 0; i < nb; i++) + result->push_back(array[p]); + } + break; + } + break; + + case osg::PrimitiveSet::LINES: + switch(fromBinding) { + case osg::Geometry::BIND_OFF: + case osg::Geometry::BIND_PER_VERTEX: + break; + case osg::Geometry::BIND_OVERALL: + { + for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) + result->push_back(array[0]); + } + break; + case osg::Geometry::BIND_PER_PRIMITIVE_SET: + { + unsigned int nb = primitives[p]->getNumIndices(); + for (unsigned int i = 0; i < nb; i++) + result->push_back(array[p]); + } + break; + } + break; + + case osg::PrimitiveSet::TRIANGLES: + switch(fromBinding) { + case osg::Geometry::BIND_OFF: + case osg::Geometry::BIND_PER_VERTEX: + break; + case osg::Geometry::BIND_OVERALL: + { + for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) + result->push_back(array[0]); + } + break; + case osg::Geometry::BIND_PER_PRIMITIVE_SET: + { + unsigned int nb = primitives[p]->getNumIndices(); + for (unsigned int i = 0; i < nb; i++) + result->push_back(array[p]); + } + break; + } + break; + + case osg::PrimitiveSet::TRIANGLE_STRIP: + switch(fromBinding) { + case osg::Geometry::BIND_OFF: + case osg::Geometry::BIND_PER_VERTEX: + break; + case osg::Geometry::BIND_OVERALL: + { + for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) + result->push_back(array[0]); + } + break; + case osg::Geometry::BIND_PER_PRIMITIVE_SET: + { + osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_STRIP" << std::endl; + } + break; + } + break; + + case osg::PrimitiveSet::TRIANGLE_FAN: + switch(fromBinding) { + case osg::Geometry::BIND_OFF: + case osg::Geometry::BIND_PER_VERTEX: + break; + case osg::Geometry::BIND_OVERALL: + { + for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) + result->push_back(array[0]); + } + break; + case osg::Geometry::BIND_PER_PRIMITIVE_SET: + { + osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_FAN" << std::endl; + } + break; + } + break; + + case osg::PrimitiveSet::QUADS: + switch(fromBinding) { + case osg::Geometry::BIND_OFF: + case osg::Geometry::BIND_PER_VERTEX: + break; + case osg::Geometry::BIND_OVERALL: + { + for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) + result->push_back(array[0]); + } + break; + case osg::Geometry::BIND_PER_PRIMITIVE_SET: + { + osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUADS" << std::endl; + } + break; + } + break; + + case osg::PrimitiveSet::QUAD_STRIP: + switch(fromBinding) { + case osg::Geometry::BIND_OFF: + case osg::Geometry::BIND_PER_VERTEX: + break; + case osg::Geometry::BIND_OVERALL: + { + for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++) + result->push_back(array[0]); + } + break; + case osg::Geometry::BIND_PER_PRIMITIVE_SET: + { + osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUAD_STRIP" << std::endl; + } + break; + } + break; + } + } + array = *result; +} diff --git a/src/osgPlugins/gles/CMakeLists.txt b/src/osgPlugins/gles/CMakeLists.txt index 2c11cc3a6..d1096e644 100644 --- a/src/osgPlugins/gles/CMakeLists.txt +++ b/src/osgPlugins/gles/CMakeLists.txt @@ -1,6 +1,17 @@ SET(TARGET_SRC ReaderWriterGLES.cpp + AABBonBoneVisitor.cpp + AnimationCleanerVisitor.cpp + BindPerVertexVisitor.cpp + DetachPrimitiveVisitor.cpp + GeometryIndexSplitter.cpp + GeometrySplitterVisitor.cpp + SubGeometry.cpp OpenGLESGeometryOptimizer.cpp + RigAnimationVisitor.cpp + RigAttributesVisitor.cpp + TriangleMeshSmoother.cpp + TangentSpaceVisitor.cpp TriangleStripVisitor.cpp IndexMeshVisitor.cpp UnIndexMeshVisitor.cpp) @@ -15,6 +26,7 @@ SET(TARGET_H DrawArrayVisitor EdgeIndexFunctor GeometryArray + GeometryIndexSplitter GeometryInspector GeometrySplitterVisitor GeometryUniqueVisitor @@ -35,6 +47,7 @@ SET(TARGET_H SubGeometry TangentSpaceVisitor TriangleMeshGraph + TriangleMeshSmoother TriangleStripVisitor UnIndexMeshVisitor WireframeVisitor diff --git a/src/osgPlugins/gles/DetachPrimitiveVisitor b/src/osgPlugins/gles/DetachPrimitiveVisitor index 61720d763..4a02b0235 100644 --- a/src/osgPlugins/gles/DetachPrimitiveVisitor +++ b/src/osgPlugins/gles/DetachPrimitiveVisitor @@ -23,140 +23,23 @@ public: _userValue(userValue), _keepGeometryAttributes(keepGeometryAttributes), _inlined(inlined) {} - void reparentDuplicatedGeometry(osg::Geometry& geometry, osg::Geometry& duplicated) - { - unsigned int nbParents = geometry.getNumParents(); - for(unsigned int i = 0 ; i < nbParents ; ++ i) { - osg::Node* parent = geometry.getParent(i); - if(parent && parent->asGeode()) { - osg::Geode* geode = parent->asGeode(); - geode->addDrawable(&duplicated); - - if(!_inlined) { - geode->removeDrawable(&duplicated); - } - } - } - } - - void process(osg::Geometry& geometry) { - if(shouldDetach(geometry)) { - osg::Geometry* detached = detachGeometry(geometry); - reparentDuplicatedGeometry(geometry, *detached); - setProcessed(detached); - } - } - - void process(osgAnimation::RigGeometry& rigGeometry) { - return process(static_cast(rigGeometry)); - } + void reparentDuplicatedGeometry(osg::Geometry&, osg::Geometry&); + void process(osg::Geometry&); + void process(osgAnimation::RigGeometry&); protected: - bool shouldDetach(const osg::Geometry& geometry) const { - if(const osgAnimation::RigGeometry* rigGeometry = dynamic_cast(&geometry)) { - return shouldDetach(*rigGeometry->getSourceGeometry()); - } + bool shouldDetach(const osg::Geometry&) const; - bool detach = false; - for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { - const osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i); - if(primitive && primitive->getUserValue(_userValue, detach) && detach) { - return true; - } - } - return false; - } + osg::Geometry* detachGeometry(osg::Geometry&); + osg::Geometry* makeDetachedGeometry(osg::Geometry&); + osg::Geometry* createDetachedGeometry(osg::Geometry&); - osg::Geometry* detachGeometry(osg::Geometry& source) { - // filter vertex buffers depending on geometry type - osg::Geometry* detached = makeDetachedGeometry(source); - detached->setUserValue(_userValue, true); - return detached; - } + osg::Geometry::PrimitiveSetList createDetachedPrimitives(osg::Geometry&); + osgAnimation::MorphGeometry* createDetachedGeometry(osgAnimation::MorphGeometry&); + osg::Geometry* createDetachedGeometry(osgAnimation::RigGeometry&); - osg::Geometry* makeDetachedGeometry(osg::Geometry& geometry) { - if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast(&geometry)) { - return createDetachedGeometry(*rigGeometry); - } - if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast(&geometry)) { - return createDetachedGeometry(*morphGeometry); - } - return createDetachedGeometry(geometry); - } - - 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); - } - - detached->setPrimitiveSetList(createDetachedPrimitives(source)); - return detached; - } - - osg::Geometry::PrimitiveSetList createDetachedPrimitives(osg::Geometry& source) { - // 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); - } - } - return detachedPrimitives; - } - - osgAnimation::MorphGeometry* createDetachedGeometry(osgAnimation::MorphGeometry& source) { - osgAnimation::MorphGeometry* detached = new osgAnimation::MorphGeometry(*createDetachedGeometry(static_cast(source))); - detached->setVertexArray(source.getVertexArray()); - - osgAnimation::MorphGeometry::MorphTargetList& targets = source.getMorphTargetList(); - for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) { - detached->addMorphTarget(target->getGeometry(), target->getWeight()); - } - return detached; - } - - osg::Geometry* createDetachedGeometry(osgAnimation::RigGeometry& source) { - osgAnimation::RigGeometry* detached; - if(!_keepGeometryAttributes) { - detached = new osgAnimation::RigGeometry(); - detached->setSourceGeometry(makeDetachedGeometry(*source.getSourceGeometry())); - - // Only keep vertexes and Bones/Weights attrib arrays - detached->setVertexArray(source.getVertexArray()); - for(unsigned int i = 0 ; i < source.getVertexAttribArrayList().size() ; ++ i) { - osg::Array* attribute = source.getVertexAttribArray(i); - if(attribute) { - bool isBones = false; - bool isWeights = false; - attribute->getUserValue("bones", isBones); - attribute->getUserValue("weights", isWeights); - if (isBones || isWeights) { - detached->setVertexAttribArray(i, source.getVertexAttribArray(i)); - } - } - } - } - else { - detached = new osgAnimation::RigGeometry(source, osg::CopyOp::SHALLOW_COPY); - } - - return detached; - } +protected: std::string _userValue; bool _keepGeometryAttributes; bool _inlined; diff --git a/src/osgPlugins/gles/DetachPrimitiveVisitor.cpp b/src/osgPlugins/gles/DetachPrimitiveVisitor.cpp new file mode 100644 index 000000000..b7b8e18bb --- /dev/null +++ b/src/osgPlugins/gles/DetachPrimitiveVisitor.cpp @@ -0,0 +1,142 @@ +#include "DetachPrimitiveVisitor" + +void DetachPrimitiveVisitor::reparentDuplicatedGeometry(osg::Geometry& geometry, osg::Geometry& duplicated) { + unsigned int nbParents = geometry.getNumParents(); + for(unsigned int i = 0 ; i < nbParents ; ++ i) { + osg::Node* parent = geometry.getParent(i); + if(parent && parent->asGeode()) { + osg::Geode* geode = parent->asGeode(); + geode->addDrawable(&duplicated); + + if(!_inlined) { + geode->removeDrawable(&duplicated); + } + } + } +} + + +void DetachPrimitiveVisitor::process(osg::Geometry& geometry) { + if(shouldDetach(geometry)) { + osg::Geometry* detached = detachGeometry(geometry); + reparentDuplicatedGeometry(geometry, *detached); + setProcessed(detached); + } +} + + +void DetachPrimitiveVisitor::process(osgAnimation::RigGeometry& rigGeometry) { + return process(static_cast(rigGeometry)); +} + + +bool DetachPrimitiveVisitor::shouldDetach(const osg::Geometry& geometry) const { + if(const osgAnimation::RigGeometry* rigGeometry = dynamic_cast(&geometry)) { + return shouldDetach(*rigGeometry->getSourceGeometry()); + } + + bool detach = false; + for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { + const osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i); + if(primitive && primitive->getUserValue(_userValue, detach) && detach) { + return true; + } + } + return false; +} + + +osg::Geometry* DetachPrimitiveVisitor::detachGeometry(osg::Geometry& source) { + // filter vertex buffers depending on geometry type + osg::Geometry* detached = makeDetachedGeometry(source); + detached->setUserValue(_userValue, true); + return detached; +} + + +osg::Geometry* DetachPrimitiveVisitor::makeDetachedGeometry(osg::Geometry& geometry) { + if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast(&geometry)) { + return createDetachedGeometry(*rigGeometry); + } + if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast(&geometry)) { + return createDetachedGeometry(*morphGeometry); + } + return createDetachedGeometry(geometry); +} + + +osg::Geometry* DetachPrimitiveVisitor::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); + } + + detached->setPrimitiveSetList(createDetachedPrimitives(source)); + return detached; +} + + +osg::Geometry::PrimitiveSetList DetachPrimitiveVisitor::createDetachedPrimitives(osg::Geometry& source) { + // 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); + } + } + return detachedPrimitives; +} + + +osgAnimation::MorphGeometry* DetachPrimitiveVisitor::createDetachedGeometry(osgAnimation::MorphGeometry& source) { + osgAnimation::MorphGeometry* detached = new osgAnimation::MorphGeometry(*createDetachedGeometry(static_cast(source))); + detached->setVertexArray(source.getVertexArray()); + + osgAnimation::MorphGeometry::MorphTargetList& targets = source.getMorphTargetList(); + for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) { + detached->addMorphTarget(target->getGeometry(), target->getWeight()); + } + return detached; +} + + +osg::Geometry* DetachPrimitiveVisitor::createDetachedGeometry(osgAnimation::RigGeometry& source) { + osgAnimation::RigGeometry* detached; + if(!_keepGeometryAttributes) { + detached = new osgAnimation::RigGeometry(); + detached->setSourceGeometry(makeDetachedGeometry(*source.getSourceGeometry())); + + // Only keep vertexes and Bones/Weights attrib arrays + detached->setVertexArray(source.getVertexArray()); + for(unsigned int i = 0 ; i < source.getVertexAttribArrayList().size() ; ++ i) { + osg::Array* attribute = source.getVertexAttribArray(i); + if(attribute) { + bool isBones = false; + bool isWeights = false; + attribute->getUserValue("bones", isBones); + attribute->getUserValue("weights", isWeights); + if (isBones || isWeights) { + detached->setVertexAttribArray(i, source.getVertexAttribArray(i)); + } + } + } + } + else { + detached = new osgAnimation::RigGeometry(source, osg::CopyOp::SHALLOW_COPY); + } + + return detached; +} diff --git a/src/osgPlugins/gles/GeometryIndexSplitter b/src/osgPlugins/gles/GeometryIndexSplitter new file mode 100644 index 000000000..0ce5d77a0 --- /dev/null +++ b/src/osgPlugins/gles/GeometryIndexSplitter @@ -0,0 +1,78 @@ +/* -*-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_INDEX_SPLITTER +#define GEOMETRY_INDEX_SPLITTER + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "glesUtil" +#include "GeometryArray" +#include "TriangleMeshGraph" +#include "SubGeometry" +#include "Line" + + +class GeometryIndexSplitter +{ +protected: + class IndexCache : public IndexDeque { + public: + IndexCache(unsigned int size=64) : _size(size) + {} + + void push_back(unsigned int value) { + IndexDeque::push_back(value); + if(size() > _size) pop_front(); + } + protected: + unsigned int _size; + }; + +public: + typedef std::vector< osg::ref_ptr > GeometryList; + + GeometryIndexSplitter(unsigned int maxAllowedIndex): + _maxAllowedIndex(maxAllowedIndex) + {} + + bool split(osg::Geometry&); + + unsigned int findCandidate(const IndexVector&); + unsigned int findCandidate(const IndexVector&, const IndexVector&); + void setTriangleCluster(const TriangleMeshGraph&, unsigned int, unsigned int, IndexVector&, IndexSet&, unsigned int&); + void extract_primitives(const IndexSet&, const osg::DrawElements*, IndexVector&, unsigned int); + +protected: + bool needToSplit(const osg::Geometry&) const; + bool needToSplit(const osg::DrawElements&) const; + void attachBufferBoundingBox(osg::Geometry&) const; + + template + void setBufferBoundingBox(T*) const; + void setValidIndices(std::set&, const osg::DrawElements*) const; + +public: + const unsigned int _maxAllowedIndex; + GeometryList _geometryList; +}; + +#endif diff --git a/src/osgPlugins/gles/GeometryIndexSplitter.cpp b/src/osgPlugins/gles/GeometryIndexSplitter.cpp new file mode 100644 index 000000000..5c3cecd1d --- /dev/null +++ b/src/osgPlugins/gles/GeometryIndexSplitter.cpp @@ -0,0 +1,285 @@ +#include "GeometryIndexSplitter" + + +bool GeometryIndexSplitter::split(osg::Geometry& geometry) { + if(!needToSplit(geometry)) { + _geometryList.push_back(&geometry); + return false; + } + + // keep bounding box data as user value if needed in subsequent processing + attachBufferBoundingBox(geometry); + + osg::DrawElements *wire_primitive = 0, + *line_primitive = 0, + *point_primitive = 0; + for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { + osg::DrawElements* primitive = (geometry.getPrimitiveSet(i) ? geometry.getPrimitiveSet(i)->getDrawElements() : 0); + if(primitive) { + if(primitive->getMode() == osg::PrimitiveSet::LINES) { + bool isWireframe = false; + if(primitive->getUserValue("wireframe", isWireframe) && isWireframe) { + wire_primitive = primitive; + } + else { + line_primitive = primitive; + } + } + else if(primitive->getMode() == osg::PrimitiveSet::POINTS) { + point_primitive = primitive; + } + } + } + + // only wireframe can be processed directly as they simply "duplicate" triangle or edge data; + // lines/points may reference points not used for triangles so we keep a set of primitives + // that remain to process + LineSet source_lines; + IndexSet source_points; + + if(line_primitive) { + for(unsigned int i = 0 ; i < line_primitive->getNumIndices() ; i += 2) { + source_lines.insert(Line(line_primitive->index(i), line_primitive->index(i + 1))); + } + } + if(point_primitive) { + for(unsigned int i = 0 ; i < point_primitive->getNumIndices() ; ++ i) { + source_points.insert(point_primitive->index(i)); + } + } + + TriangleMeshGraph graph(geometry, false); + unsigned int remaining_triangles = graph.getNumTriangles(), + cluster = 0; + IndexVector clusters(remaining_triangles, 0); + IndexCache cache; + + // assign a cluster id for each triangle + // 1. bootstrap cluster by selecting first remaining triangle + // 2. while cluster size is < max cluster size + // 2.1 look for a triangle that is a neighbor of one cluster triangle (considering N last cluster triangles from cache) + // 2.2 if 2.1 was not successful and there still is room for 3 new vertices (i.e. a full new triangle) then + // add any (might share an edge/vertex/nothing with existing cluster) triangle + // 2.3 if we still have lines/points, add anything we can 'naively' + // 3. insert wireframe edges corresponding to selected triangles + // 4. extract subgeometry + + while(remaining_triangles || !source_lines.empty() || !source_points.empty()) { + IndexVector subtriangles, subwireframe, sublines, subpoints; + IndexSet cluster_vertices; + + ++ cluster; + + if(remaining_triangles) { + // find first unmarked triangle (as remaining_triangles > 0 there *must* be at least one) + cache.push_back(findCandidate(clusters)); + setTriangleCluster(graph, cache.back(), cluster, clusters, cluster_vertices, remaining_triangles); + + while(remaining_triangles && cluster_vertices.size() < _maxAllowedIndex) { + unsigned int candidate = std::numeric_limits::max(); + + for(IndexCache::const_reverse_iterator cached = cache.rbegin() ; cached != cache.rend() ; ++ cached) { + candidate = findCandidate(graph.triangleNeighbors(*cached), clusters); + if(candidate != std::numeric_limits::max()) break; + } + + if(candidate == std::numeric_limits::max()) { + // do we have room for a triangle having all vertices not in the cluster? + if(!(cluster_vertices.size() + 2 < _maxAllowedIndex)) { + break; + } + + candidate = findCandidate(clusters); + } + + cache.push_back(candidate); + setTriangleCluster(graph, candidate, cluster, clusters, cluster_vertices, remaining_triangles); + } + + // build list of cluster triangles + for(unsigned int triangle = 0 ; triangle < clusters.size() ; ++ triangle) { + if(clusters[triangle] == cluster) { + const Triangle& t = graph.triangle(triangle); + subtriangles.push_back(t.v1()); + subtriangles.push_back(t.v2()); + subtriangles.push_back(t.v3()); + } + } + + // update lines/points: if all vertices referenced by a point/line primitive are + // already extracted, let's insert it in the subgeometry and update the set of + // primitives still remaining Lines may e.g. reference one vertex in cluster A and + // the other in cluster B hence need specific care + if(line_primitive) { + extract_primitives(cluster_vertices, line_primitive, sublines, 2); + for(unsigned int i = 0 ; i < sublines.size() / 2 ; i += 2) { + source_lines.erase(Line(sublines[i], sublines[i + 1])); + } + } + + if(point_primitive) { + extract_primitives(cluster_vertices, point_primitive, subpoints, 1); + for(unsigned int i = 0 ; i < subpoints.size() ; ++ i) { + source_points.erase(subpoints[i]); + } + } + } + + // let's consider that every new lines adds 2 vertices for simplicity + while(!source_lines.empty() && cluster_vertices.size() - 1 < _maxAllowedIndex) { + Line line = *source_lines.begin(); + source_lines.erase(source_lines.begin()); + cluster_vertices.insert(line._a); + cluster_vertices.insert(line._b); + sublines.push_back(line._a); + sublines.push_back(line._b); + } + + while(!source_points.empty() && cluster_vertices.size() < _maxAllowedIndex) { + unsigned int point = *source_points.begin(); + source_points.erase(source_points.begin()); + cluster_vertices.insert(point); + subpoints.push_back(point); + } + + // finally extract wireframe (may originate from triangles or lines but necessarily have + // to reference vertices that are *all* in the geometry) + if(wire_primitive) { + extract_primitives(cluster_vertices, wire_primitive, subwireframe, 2); + } + + _geometryList.push_back(SubGeometry(geometry, + subtriangles, + sublines, + subwireframe, + subpoints).geometry()); + } + + osg::notify(osg::NOTICE) << "geometry " << &geometry << " " << geometry.getName() + << " vertexes (" << geometry.getVertexArray()->getNumElements() + << ") has DrawElements index > " << _maxAllowedIndex << ", splitted to " + << _geometryList.size() << " geometry" << std::endl; + + return true; +} + + +unsigned int GeometryIndexSplitter::findCandidate(const IndexVector& clusters) { + for(unsigned int i = 0 ; i < clusters.size() ; ++ i) { + if(!clusters[i]) { + return i; + } + } + return std::numeric_limits::max(); +} + + +unsigned int GeometryIndexSplitter::findCandidate(const IndexVector& candidates, const IndexVector& clusters) { + for(IndexVector::const_iterator candidate = candidates.begin() ; candidate != candidates.end() ; ++ candidate) { + if(!clusters[*candidate]) { + return *candidate; + } + } + return std::numeric_limits::max(); +} + + +void GeometryIndexSplitter::setTriangleCluster(const TriangleMeshGraph& graph, + unsigned int triangle, + unsigned int cluster, + IndexVector& clusters, + IndexSet& cluster_vertices, + unsigned int& remaining) { + clusters[triangle] = cluster; + const Triangle& t = graph.triangle(triangle); + cluster_vertices.insert(t.v1()); + cluster_vertices.insert(t.v2()); + cluster_vertices.insert(t.v3()); + remaining --; +} + + +void GeometryIndexSplitter::extract_primitives(const IndexSet& vertices, + const osg::DrawElements* elements, + IndexVector& indices, + unsigned int primitive_size) { + for(unsigned int i = 0 ; i < elements->getNumIndices() ; i += primitive_size) { + bool is_included = true; + for(unsigned int j = 0 ; j < primitive_size ; ++ j) { + if(!vertices.count(elements->index(i + j))) { + is_included = false; + break; + } + } + + if(is_included) { + for(unsigned int j = 0 ; j < primitive_size ; ++ j) { + indices.push_back(elements->index(i + j)); + } + } + } +} + + +bool GeometryIndexSplitter::needToSplit(const osg::Geometry& geometry) const { + for(unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) { + const osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements(); + if (primitive && needToSplit(*primitive)) { + return true; + } + } + return false; +} + + +bool GeometryIndexSplitter::needToSplit(const osg::DrawElements& primitive) const { + for(unsigned int j = 0; j < primitive.getNumIndices(); j++) { + if (primitive.index(j) > _maxAllowedIndex){ + return true; + } + } + return false; +} + + +void GeometryIndexSplitter::attachBufferBoundingBox(osg::Geometry& geometry) const { + // positions + setBufferBoundingBox(dynamic_cast(geometry.getVertexArray())); + // uvs + for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) { + setBufferBoundingBox(dynamic_cast(geometry.getTexCoordArray(i))); + } +} + + +template +void GeometryIndexSplitter::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); + } +} + + +void GeometryIndexSplitter::setValidIndices(std::set& indices, const osg::DrawElements* primitive) const { + for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) { + indices.insert(primitive->index(j)); + } +} diff --git a/src/osgPlugins/gles/GeometrySplitterVisitor b/src/osgPlugins/gles/GeometrySplitterVisitor index 5de2118ca..51596cfcf 100644 --- a/src/osgPlugins/gles/GeometrySplitterVisitor +++ b/src/osgPlugins/gles/GeometrySplitterVisitor @@ -13,328 +13,13 @@ #ifndef GEOMETRY_SPLITTER_VISITOR #define GEOMETRY_SPLITTER_VISITOR -#include -#include -#include +#include #include #include -#include -#include -#include - #include -#include "glesUtil" -#include "GeometryArray" #include "GeometryUniqueVisitor" -#include "AnimationCleanerVisitor" -#include "TriangleMeshGraph" -#include "SubGeometry" -#include "Line" - - - -class GeometryIndexSplitter -{ -protected: - class IndexCache : public IndexDeque { - public: - IndexCache(unsigned int size=64) : _size(size) - {} - - void push_back(unsigned int value) { - IndexDeque::push_back(value); - if(size() > _size) pop_front(); - } - protected: - unsigned int _size; - }; - -public: - typedef std::vector< osg::ref_ptr > GeometryList; - - GeometryIndexSplitter(unsigned int maxAllowedIndex): - _maxAllowedIndex(maxAllowedIndex) - {} - - bool split(osg::Geometry& geometry) { - if(!needToSplit(geometry)) { - _geometryList.push_back(&geometry); - return false; - } - - // keep bounding box data as user value if needed in subsequent processing - attachBufferBoundingBox(geometry); - - osg::DrawElements *wire_primitive = 0, - *line_primitive = 0, - *point_primitive = 0; - for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { - osg::DrawElements* primitive = (geometry.getPrimitiveSet(i) ? geometry.getPrimitiveSet(i)->getDrawElements() : 0); - if(primitive) { - if(primitive->getMode() == osg::PrimitiveSet::LINES) { - bool isWireframe = false; - if(primitive->getUserValue("wireframe", isWireframe) && isWireframe) { - wire_primitive = primitive; - } - else { - line_primitive = primitive; - } - } - else if(primitive->getMode() == osg::PrimitiveSet::POINTS) { - point_primitive = primitive; - } - } - } - - // only wireframe can be processed directly as they simply "duplicate" triangle or edge data; - // lines/points may reference points not used for triangles so we keep a set of primitives - // that remain to process - LineSet source_lines; - IndexSet source_points; - - if(line_primitive) { - for(unsigned int i = 0 ; i < line_primitive->getNumIndices() ; i += 2) { - source_lines.insert(Line(line_primitive->index(i), line_primitive->index(i + 1))); - } - } - if(point_primitive) { - for(unsigned int i = 0 ; i < point_primitive->getNumIndices() ; ++ i) { - source_points.insert(point_primitive->index(i)); - } - } - - TriangleMeshGraph graph(geometry, false); - unsigned int remaining_triangles = graph.getNumTriangles(), - cluster = 0; - IndexVector clusters(remaining_triangles, 0); - IndexCache cache; - - // assign a cluster id for each triangle - // 1. bootstrap cluster by selecting first remaining triangle - // 2. while cluster size is < max cluster size - // 2.1 look for a triangle that is a neighbor of one cluster triangle (considering N last cluster triangles from cache) - // 2.2 if 2.1 was not successful and there still is room for 3 new vertices (i.e. a full new triangle) then - // add any (might share an edge/vertex/nothing with existing cluster) triangle - // 2.3 if we still have lines/points, add anything we can 'naively' - // 3. insert wireframe edges corresponding to selected triangles - // 4. extract subgeometry - - while(remaining_triangles || !source_lines.empty() || !source_points.empty()) { - IndexVector subtriangles, subwireframe, sublines, subpoints; - IndexSet cluster_vertices; - - ++ cluster; - - if(remaining_triangles) { - // find first unmarked triangle (as remaining_triangles > 0 there *must* be at least one) - cache.push_back(findCandidate(clusters)); - setTriangleCluster(graph, cache.back(), cluster, clusters, cluster_vertices, remaining_triangles); - - while(remaining_triangles && cluster_vertices.size() < _maxAllowedIndex) { - unsigned int candidate = std::numeric_limits::max(); - - for(IndexCache::const_reverse_iterator cached = cache.rbegin() ; cached != cache.rend() ; ++ cached) { - candidate = findCandidate(graph.triangleNeighbors(*cached), clusters); - if(candidate != std::numeric_limits::max()) break; - } - - if(candidate == std::numeric_limits::max()) { - // do we have room for a triangle having all vertices not in the cluster? - if(!(cluster_vertices.size() + 2 < _maxAllowedIndex)) { - break; - } - - candidate = findCandidate(clusters); - } - - cache.push_back(candidate); - setTriangleCluster(graph, candidate, cluster, clusters, cluster_vertices, remaining_triangles); - } - - // build list of cluster triangles - for(unsigned int triangle = 0 ; triangle < clusters.size() ; ++ triangle) { - if(clusters[triangle] == cluster) { - const Triangle& t = graph.triangle(triangle); - subtriangles.push_back(t.v1()); - subtriangles.push_back(t.v2()); - subtriangles.push_back(t.v3()); - } - } - - // update lines/points: if all vertices referenced by a point/line primitive are - // already extracted, let's insert it in the subgeometry and update the set of - // primitives still remaining Lines may e.g. reference one vertex in cluster A and - // the other in cluster B hence need specific care - if(line_primitive) { - extract_primitives(cluster_vertices, line_primitive, sublines, 2); - for(unsigned int i = 0 ; i < sublines.size() / 2 ; i += 2) { - source_lines.erase(Line(sublines[i], sublines[i + 1])); - } - } - - if(point_primitive) { - extract_primitives(cluster_vertices, point_primitive, subpoints, 1); - for(unsigned int i = 0 ; i < subpoints.size() ; ++ i) { - source_points.erase(subpoints[i]); - } - } - } - - // let's consider that every new lines adds 2 vertices for simplicity - while(!source_lines.empty() && cluster_vertices.size() - 1 < _maxAllowedIndex) { - Line line = *source_lines.begin(); - source_lines.erase(source_lines.begin()); - cluster_vertices.insert(line._a); - cluster_vertices.insert(line._b); - sublines.push_back(line._a); - sublines.push_back(line._b); - } - - while(!source_points.empty() && cluster_vertices.size() < _maxAllowedIndex) { - unsigned int point = *source_points.begin(); - source_points.erase(source_points.begin()); - cluster_vertices.insert(point); - subpoints.push_back(point); - } - - // finally extract wireframe (may originate from triangles or lines but necessarily have - // to reference vertices that are *all* in the geometry) - if(wire_primitive) { - extract_primitives(cluster_vertices, wire_primitive, subwireframe, 2); - } - - _geometryList.push_back(SubGeometry(geometry, - subtriangles, - sublines, - subwireframe, - subpoints).geometry()); - } - - osg::notify(osg::NOTICE) << "geometry " << &geometry << " " << geometry.getName() - << " vertexes (" << geometry.getVertexArray()->getNumElements() - << ") has DrawElements index > " << _maxAllowedIndex << ", splitted to " - << _geometryList.size() << " geometry" << std::endl; - - return true; - } - - unsigned int findCandidate(const IndexVector& clusters) { - for(unsigned int i = 0 ; i < clusters.size() ; ++ i) { - if(!clusters[i]) { - return i; - } - } - return std::numeric_limits::max(); - } - - unsigned int findCandidate(const IndexVector& candidates, const IndexVector& clusters) { - for(IndexVector::const_iterator candidate = candidates.begin() ; candidate != candidates.end() ; ++ candidate) { - if(!clusters[*candidate]) { - return *candidate; - } - } - return std::numeric_limits::max(); - } - - void setTriangleCluster(const TriangleMeshGraph& graph, - unsigned int triangle, - unsigned int cluster, - IndexVector& clusters, - IndexSet& cluster_vertices, - unsigned int& remaining) { - clusters[triangle] = cluster; - const Triangle& t = graph.triangle(triangle); - cluster_vertices.insert(t.v1()); - cluster_vertices.insert(t.v2()); - cluster_vertices.insert(t.v3()); - remaining --; - } - - void extract_primitives(const IndexSet& vertices, const osg::DrawElements* elements, IndexVector& indices, unsigned int primitive_size) { - for(unsigned int i = 0 ; i < elements->getNumIndices() ; i += primitive_size) { - bool is_included = true; - for(unsigned int j = 0 ; j < primitive_size ; ++ j) { - if(!vertices.count(elements->index(i + j))) { - is_included = false; - break; - } - } - - if(is_included) { - for(unsigned int j = 0 ; j < primitive_size ; ++ j) { - indices.push_back(elements->index(i + j)); - } - } - } - } - -protected: - 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 (primitive && 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) > _maxAllowedIndex){ - return true; - } - } - return false; - } - - void attachBufferBoundingBox(osg::Geometry& geometry) const { - // positions - setBufferBoundingBox(dynamic_cast(geometry.getVertexArray())); - // uvs - for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) { - setBufferBoundingBox(dynamic_cast(geometry.getTexCoordArray(i))); - } - } - - template - void setBufferBoundingBox(T* buffer) const { - if(!buffer) return; - - typename T::ElementDataType bbl; - typename T::ElementDataType ufr; - const unsigned int dimension = buffer->getDataSize(); - - if(buffer->getNumElements()) { - for(unsigned int i = 0 ; i < dimension ; ++i) { - bbl[i] = ufr[i] = (*buffer->begin())[i]; - } - - for(typename T::const_iterator it = buffer->begin() + 1 ; it != buffer->end() ; ++ it) { - for(unsigned int i = 0 ; i < dimension ; ++ i) { - bbl[i] = std::min(bbl[i], (*it)[i]); - ufr[i] = std::max(ufr[i], (*it)[i]); - } - } - - buffer->setUserValue("bbl", bbl); - buffer->setUserValue("ufr", ufr); - } - } - - void setValidIndices(std::set& indices, const osg::DrawElements* primitive) const { - for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) { - indices.insert(primitive->index(j)); - } - } - -public: - const unsigned int _maxAllowedIndex; - GeometryList _geometryList; -}; - class GeometrySplitterVisitor : public GeometryUniqueVisitor { @@ -349,70 +34,14 @@ public: _exportNonGeometryDrawables(exportNonGeometryDrawables) {} - void apply(osg::Geode& geode) { - GeometryUniqueVisitor::apply(geode); - GeometryList remappedGeometries; - DrawableList nonGeometryDrawables; - - for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) { - osg::Geometry* geometry = geode.getDrawable(i)->asGeometry(); - if(geometry) { - if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast(geometry)) { - SplitMap::iterator lookup = _split.find(rigGeometry->getSourceGeometry()); - if(lookup != _split.end() && !lookup->second.empty()) { - for(GeometryList::iterator splittedSource = lookup->second.begin() ; splittedSource != lookup->second.end() ; ++ splittedSource) { - if(hasPositiveWeights(splittedSource->get())) { - osgAnimation::RigGeometry* splittedRig = new osgAnimation::RigGeometry(*rigGeometry); - splittedRig->setSourceGeometry(splittedSource->get()); - remappedGeometries.push_back(splittedRig); - } - else { - remappedGeometries.push_back(splittedSource->get()); - } - } - } - } - else { - SplitMap::iterator lookup = _split.find(geometry); - if(lookup != _split.end() && !lookup->second.empty()) { - remappedGeometries.insert(remappedGeometries.end(), lookup->second.begin(), lookup->second.end()); - } - } - } - else { - nonGeometryDrawables.push_back(geode.getDrawable(i)); - } - } - - // remove all drawables - geode.removeDrawables(0, geode.getNumDrawables()); - // insert splitted geometries - for(unsigned int i = 0 ; i < remappedGeometries.size() ; ++ i) { - geode.addDrawable(remappedGeometries[i].get()); - } - - if(_exportNonGeometryDrawables) { - // insert other drawables (e.g. osgText) - for(unsigned int i = 0 ; i < nonGeometryDrawables.size() ; ++ i) { - geode.addDrawable(nonGeometryDrawables[i].get()); - } - } - } - - void process(osg::Geometry& geometry) { - GeometryIndexSplitter splitter(_maxAllowedIndex); - splitter.split(geometry); - setProcessed(&geometry, splitter._geometryList); - } + void apply(osg::Geode&); + void process(osg::Geometry&); protected: - bool isProcessed(osg::Geometry* node) { - return _split.find(node) != _split.end(); - } + bool isProcessed(osg::Geometry*); + void setProcessed(osg::Geometry*, const GeometryList&); - void setProcessed(osg::Geometry* node, const GeometryList& list) { - _split.insert(std::pair(node, GeometryList(list))); - } +protected: unsigned int _maxAllowedIndex; std::map _split; bool _exportNonGeometryDrawables; diff --git a/src/osgPlugins/gles/GeometrySplitterVisitor.cpp b/src/osgPlugins/gles/GeometrySplitterVisitor.cpp new file mode 100644 index 000000000..dd787df68 --- /dev/null +++ b/src/osgPlugins/gles/GeometrySplitterVisitor.cpp @@ -0,0 +1,70 @@ +#include "GeometrySplitterVisitor" +#include "GeometryIndexSplitter" + + +void GeometrySplitterVisitor::apply(osg::Geode& geode) { + GeometryUniqueVisitor::apply(geode); + GeometryList remappedGeometries; + DrawableList nonGeometryDrawables; + + for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) { + osg::Geometry* geometry = geode.getDrawable(i)->asGeometry(); + if(geometry) { + if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast(geometry)) { + SplitMap::iterator lookup = _split.find(rigGeometry->getSourceGeometry()); + if(lookup != _split.end() && !lookup->second.empty()) { + for(GeometryList::iterator splittedSource = lookup->second.begin() ; splittedSource != lookup->second.end() ; ++ splittedSource) { + if(glesUtil::hasPositiveWeights(splittedSource->get())) { + osgAnimation::RigGeometry* splittedRig = new osgAnimation::RigGeometry(*rigGeometry); + splittedRig->setSourceGeometry(splittedSource->get()); + remappedGeometries.push_back(splittedRig); + } + else { + remappedGeometries.push_back(splittedSource->get()); + } + } + } + } + else { + SplitMap::iterator lookup = _split.find(geometry); + if(lookup != _split.end() && !lookup->second.empty()) { + remappedGeometries.insert(remappedGeometries.end(), lookup->second.begin(), lookup->second.end()); + } + } + } + else { + nonGeometryDrawables.push_back(geode.getDrawable(i)); + } + } + + // remove all drawables + geode.removeDrawables(0, geode.getNumDrawables()); + // insert splitted geometries + for(unsigned int i = 0 ; i < remappedGeometries.size() ; ++ i) { + geode.addDrawable(remappedGeometries[i].get()); + } + + if(_exportNonGeometryDrawables) { + // insert other drawables (e.g. osgText) + for(unsigned int i = 0 ; i < nonGeometryDrawables.size() ; ++ i) { + geode.addDrawable(nonGeometryDrawables[i].get()); + } + } +} + + +void GeometrySplitterVisitor::process(osg::Geometry& geometry) { + GeometryIndexSplitter splitter(_maxAllowedIndex); + splitter.split(geometry); + setProcessed(&geometry, splitter._geometryList); +} + + +bool GeometrySplitterVisitor::isProcessed(osg::Geometry* node) { + return _split.find(node) != _split.end(); +} + + +void GeometrySplitterVisitor::setProcessed(osg::Geometry* node, const GeometryList& list) { + _split.insert(std::pair(node, GeometryList(list))); +} diff --git a/src/osgPlugins/gles/RigAnimationVisitor b/src/osgPlugins/gles/RigAnimationVisitor index 3d1e1feb5..ea8492e12 100644 --- a/src/osgPlugins/gles/RigAnimationVisitor +++ b/src/osgPlugins/gles/RigAnimationVisitor @@ -30,19 +30,6 @@ #include "StatLogger" -struct sort_weights { - bool operator()(const std::pair &left, const std::pair &right) { - // in case weights are equal, order elements by ascending bone ids - if(left.second == right.second) { - return left.first < right.first; - } - else { - return left.second > right.second; - } - } -}; - - // the idea is to create true Geometry if skeleton with RigGeometry class RigAnimationVisitor : public osgUtil::UpdateVisitor { @@ -55,146 +42,28 @@ public: setFrameStamp(new osg::FrameStamp()); } - void apply(osg::Drawable& drawable) { - // skip drawables already processed - if (isProcessed(drawable)) { - return; - } - - apply(drawable.asGeometry()); - - setProcessed(drawable); - } - - void apply(osg::Geometry* geometry) { - osgAnimation::RigGeometry* rig = dynamic_cast(geometry); - if(rig) { - apply(*rig); - } - } + void apply(osg::Drawable&); + void apply(osg::Geometry*); + void apply(osgAnimation::RigGeometry&); +protected: inline void normalizeWeight(osg::Vec4f &v) const { // do not consider positive weights only float sum = std::abs(v[0]) + std::abs(v[1]) + std::abs(v[2]) + std::abs(v[3]); if (sum > 0) v /= sum; } - void apply(osgAnimation::RigGeometry& rigGeometry) { - // find skeleton - osgAnimation::UpdateRigGeometry rigUpdater; - osg::Geometry* source = rigGeometry.getSourceGeometry(); + boneIndices remapGeometryBones(const osg::Vec4usArray&); + void applyBoneIndicesRemap(osg::Vec4usArray&, const boneIndices&); - if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast(source)) { - // skip normals blending when rigging a morph as targets may not have normals yet - morphGeometry->setMorphNormals(false); - } + void serializeBonesUserValues(osg::Vec4usArray&, + const std::map&, + const osgAnimation::RigTransformHardware::BoneNamePaletteIndex&); - rigUpdater.update(0, &rigGeometry); - - osgAnimation::RigTransformHardware rth; - rth(rigGeometry); - std::vector< std::vector< std::pair > > vertexBoneWeights(rigGeometry.getVertexArray()->getNumElements()); - - // collect all bone/weight pairs for *all* vertices - for(unsigned int i = 0 ; i < static_cast(rth.getNumVertexAttrib()) ; ++ i) { - osg::Vec4Array* weights = dynamic_cast(rth.getVertexAttrib(i)); - for(unsigned int k = 0 ; k < weights->getNumElements() ; ++ k) { - vertexBoneWeights[k].push_back(std::pair((*weights)[k][0], (*weights)[k][1])); - vertexBoneWeights[k].push_back(std::pair((*weights)[k][2], (*weights)[k][3])); - } - } - - osg::ref_ptr bones = new osg::Vec4usArray; - osg::ref_ptr weights = new osg::Vec4Array; - - // for each vertex a partial sort to keep only n max weights (hardcoded to 4) - for(unsigned int i = 0 ; i < vertexBoneWeights.size() ; ++ i) { - std::vector< std::pair > maxVertexBoneWeight(4); - std::partial_sort_copy(vertexBoneWeights[i].begin(), vertexBoneWeights[i].end(), - maxVertexBoneWeight.begin(), maxVertexBoneWeight.end(), - sort_weights()); - - osg::Vec4 vertexWeights; - osg::Vec4us vertexBones; - for(unsigned int j = 0 ; j < 4 ; ++ j) { - vertexBones[j] = maxVertexBoneWeight[j].first; - vertexWeights[j] = maxVertexBoneWeight[j].second; - } - - normalizeWeight(vertexWeights); - - bones->push_back(vertexBones); - weights->push_back(vertexWeights); - } - - boneIndices geometryBoneIndices = remapGeometryBones(*bones); - applyBoneIndicesRemap(*bones, geometryBoneIndices); - serializeBonesUserValues(*bones, geometryBoneIndices, rth.getBoneNameToPalette()); - - bones->setUserValue("bones", true); - weights->setUserValue("weights", true); - - // attach bones & weights to source geometry during scene graph processing - source->setVertexAttribArray(source->getNumVertexAttribArrays(), bones.get(), osg::Array::BIND_PER_VERTEX); - source->setVertexAttribArray(source->getNumVertexAttribArrays(), weights.get(), osg::Array::BIND_PER_VERTEX); - - rigGeometry.setRigTransformImplementation(0); //Remove current implementation to force implementation re-init - } + bool isProcessed(osg::Drawable&); + void setProcessed(osg::Drawable&); protected: - boneIndices remapGeometryBones(const osg::Vec4usArray& bones) { - boneIndices remap; - for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) { - for(unsigned int j = 0 ; j < 4 ; ++ j) { - if(remap.find(bones[i][j]) == remap.end()) { - remap[bones[i][j]] = static_cast(remap.size() - 1); - } - } - } - return remap; - } - - void applyBoneIndicesRemap(osg::Vec4usArray& bones, const boneIndices& remap) { - for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) { - boneIndices::const_iterator x = remap.find(bones[i][0]), - y = remap.find(bones[i][1]), - z = remap.find(bones[i][2]), - w = remap.find(bones[i][3]); - bones[i] = osg::Vec4us(x->second, - y->second, - z->second, - w->second); - } - } - - - void serializeBonesUserValues(osg::Vec4usArray& bones, const std::map& oldIndexToNewIndex, - const osgAnimation::RigTransformHardware::BoneNamePaletteIndex& boneNamePaletteIndex) { - - // map 'global' palette index to bone name - std::map oldIndexToBoneName; - for(osgAnimation::RigTransformHardware::BoneNamePaletteIndex::const_iterator it = boneNamePaletteIndex.begin() ; - it != boneNamePaletteIndex.end() ; ++ it) { - oldIndexToBoneName[static_cast(it->second)] = it->first; - } - - // serialize geometry 'palette index' => 'bone name' with user value using animationBone_ as - // name prefix - for(std::map::const_iterator it = oldIndexToNewIndex.begin() ; it != oldIndexToNewIndex.end() ; ++ it) { - std::ostringstream oss; - oss << "animationBone_" << it->second; - bones.setUserValue(oss.str(), oldIndexToBoneName[it->first]); - } - } - - bool isProcessed(osg::Drawable& node) { - return _processed.find(&node) != _processed.end(); - } - - void setProcessed(osg::Drawable& node) { - _processed.insert(&node); - } - std::set _processed; StatLogger _logger; }; diff --git a/src/osgPlugins/gles/RigAnimationVisitor.cpp b/src/osgPlugins/gles/RigAnimationVisitor.cpp new file mode 100644 index 000000000..5cb8f4bf8 --- /dev/null +++ b/src/osgPlugins/gles/RigAnimationVisitor.cpp @@ -0,0 +1,155 @@ +#include "RigAnimationVisitor" + + +struct sort_weights { + bool operator()(const std::pair &left, const std::pair &right) { + // in case weights are equal, order elements by ascending bone ids + if(left.second == right.second) { + return left.first < right.first; + } + else { + return left.second > right.second; + } + } +}; + + +void RigAnimationVisitor::apply(osg::Drawable& drawable) { + // skip drawables already processed + if (isProcessed(drawable)) { + return; + } + + apply(drawable.asGeometry()); + + setProcessed(drawable); +} + + +void RigAnimationVisitor::apply(osg::Geometry* geometry) { + osgAnimation::RigGeometry* rig = dynamic_cast(geometry); + if(rig) { + apply(*rig); + } +} + + +void RigAnimationVisitor::apply(osgAnimation::RigGeometry& rigGeometry) { + // find skeleton + osgAnimation::UpdateRigGeometry rigUpdater; + osg::Geometry* source = rigGeometry.getSourceGeometry(); + + if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast(source)) { + // skip normals blending when rigging a morph as targets may not have normals yet + morphGeometry->setMorphNormals(false); + } + + rigUpdater.update(0, &rigGeometry); + + osgAnimation::RigTransformHardware rth; + rth(rigGeometry); + std::vector< std::vector< std::pair > > vertexBoneWeights(rigGeometry.getVertexArray()->getNumElements()); + + // collect all bone/weight pairs for *all* vertices + for(unsigned int i = 0 ; i < static_cast(rth.getNumVertexAttrib()) ; ++ i) { + osg::Vec4Array* weights = dynamic_cast(rth.getVertexAttrib(i)); + for(unsigned int k = 0 ; k < weights->getNumElements() ; ++ k) { + vertexBoneWeights[k].push_back(std::pair((*weights)[k][0], (*weights)[k][1])); + vertexBoneWeights[k].push_back(std::pair((*weights)[k][2], (*weights)[k][3])); + } + } + + osg::ref_ptr bones = new osg::Vec4usArray; + osg::ref_ptr weights = new osg::Vec4Array; + + // for each vertex a partial sort to keep only n max weights (hardcoded to 4) + for(unsigned int i = 0 ; i < vertexBoneWeights.size() ; ++ i) { + std::vector< std::pair > maxVertexBoneWeight(4); + std::partial_sort_copy(vertexBoneWeights[i].begin(), vertexBoneWeights[i].end(), + maxVertexBoneWeight.begin(), maxVertexBoneWeight.end(), + sort_weights()); + + osg::Vec4 vertexWeights; + osg::Vec4us vertexBones; + for(unsigned int j = 0 ; j < 4 ; ++ j) { + vertexBones[j] = maxVertexBoneWeight[j].first; + vertexWeights[j] = maxVertexBoneWeight[j].second; + } + + normalizeWeight(vertexWeights); + + bones->push_back(vertexBones); + weights->push_back(vertexWeights); + } + + RigAnimationVisitor::boneIndices geometryBoneIndices = remapGeometryBones(*bones); + applyBoneIndicesRemap(*bones, geometryBoneIndices); + serializeBonesUserValues(*bones, geometryBoneIndices, rth.getBoneNameToPalette()); + + bones->setUserValue("bones", true); + weights->setUserValue("weights", true); + + // attach bones & weights to source geometry during scene graph processing + source->setVertexAttribArray(source->getNumVertexAttribArrays(), bones.get(), osg::Array::BIND_PER_VERTEX); + source->setVertexAttribArray(source->getNumVertexAttribArrays(), weights.get(), osg::Array::BIND_PER_VERTEX); + + rigGeometry.setRigTransformImplementation(0); //Remove current implementation to force implementation re-init +} + + +RigAnimationVisitor::boneIndices RigAnimationVisitor::remapGeometryBones(const osg::Vec4usArray& bones) { + RigAnimationVisitor::boneIndices remap; + for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) { + for(unsigned int j = 0 ; j < 4 ; ++ j) { + if(remap.find(bones[i][j]) == remap.end()) { + remap[bones[i][j]] = static_cast(remap.size() - 1); + } + } + } + return remap; +} + + +void RigAnimationVisitor::applyBoneIndicesRemap(osg::Vec4usArray& bones, const RigAnimationVisitor::boneIndices& remap) { + for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) { + RigAnimationVisitor::boneIndices::const_iterator x = remap.find(bones[i][0]), + y = remap.find(bones[i][1]), + z = remap.find(bones[i][2]), + w = remap.find(bones[i][3]); + bones[i] = osg::Vec4us(x->second, + y->second, + z->second, + w->second); + } +} + + +void RigAnimationVisitor::serializeBonesUserValues(osg::Vec4usArray& bones, + const std::map& oldIndexToNewIndex, + const osgAnimation::RigTransformHardware::BoneNamePaletteIndex& boneNamePaletteIndex) { + + // map 'global' palette index to bone name + std::map oldIndexToBoneName; + for(osgAnimation::RigTransformHardware::BoneNamePaletteIndex::const_iterator it = boneNamePaletteIndex.begin() ; + it != boneNamePaletteIndex.end() ; ++ it) { + oldIndexToBoneName[static_cast(it->second)] = it->first; + } + + // serialize geometry 'palette index' => 'bone name' with user value using animationBone_ as + // name prefix + for(std::map::const_iterator it = oldIndexToNewIndex.begin() ; it != oldIndexToNewIndex.end() ; ++ it) { + std::ostringstream oss; + oss << "animationBone_" << it->second; + bones.setUserValue(oss.str(), oldIndexToBoneName[it->first]); + } +} + + +bool RigAnimationVisitor::isProcessed(osg::Drawable& node) { + return _processed.find(&node) != _processed.end(); +} + + +void RigAnimationVisitor::setProcessed(osg::Drawable& node) { + _processed.insert(&node); +} diff --git a/src/osgPlugins/gles/RigAttributesVisitor b/src/osgPlugins/gles/RigAttributesVisitor index 13cd47fe6..5095d3245 100644 --- a/src/osgPlugins/gles/RigAttributesVisitor +++ b/src/osgPlugins/gles/RigAttributesVisitor @@ -12,44 +12,13 @@ public: GeometryUniqueVisitor("RigAttributesVisitor") {} - void process(osgAnimation::RigGeometry& rigGeometry) { - osg::Geometry* source = rigGeometry.getSourceGeometry(); - if(source) { - int sourceBones = getPropertyIndex(*source, std::string("bones")); - int rigBones = getPropertyIndex(rigGeometry, std::string("bones")); - if(sourceBones >= 0) { - rigBones = (rigBones >= 0 ? rigBones : rigGeometry.getNumVertexAttribArrays()); - rigGeometry.setVertexAttribArray(rigBones, source->getVertexAttribArray(sourceBones)); - source->setVertexAttribArray(sourceBones, 0); - } - - int sourceWeights = getPropertyIndex(*source, std::string("weights")); - int rigWeights = getPropertyIndex(rigGeometry, std::string("weights")); - if(sourceWeights >= 0) { - rigWeights = (rigWeights >= 0 ? rigWeights : rigGeometry.getNumVertexAttribArrays()); - rigGeometry.setVertexAttribArray(rigWeights, source->getVertexAttribArray(sourceWeights)); - source->setVertexAttribArray(sourceWeights, 0); - } - } - } - + void process(osgAnimation::RigGeometry& rigGeometry); void process(osg::Geometry& /*geometry*/) { return; } protected: - int getPropertyIndex(const osg::Geometry& geometry, const std::string& property) { - for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) { - const osg::Array* attribute = geometry.getVertexAttribArray(i); - bool isProperty = false; - if(attribute && attribute->getUserValue(property, isProperty) && isProperty) { - return i; - } - } - return -1; - } + int getPropertyIndex(const osg::Geometry& geometry, const std::string& property); }; - - #endif diff --git a/src/osgPlugins/gles/RigAttributesVisitor.cpp b/src/osgPlugins/gles/RigAttributesVisitor.cpp new file mode 100644 index 000000000..e2dea8afb --- /dev/null +++ b/src/osgPlugins/gles/RigAttributesVisitor.cpp @@ -0,0 +1,35 @@ +#include "RigAttributesVisitor" + + +void RigAttributesVisitor::process(osgAnimation::RigGeometry& rigGeometry) { + osg::Geometry* source = rigGeometry.getSourceGeometry(); + if(source) { + int sourceBones = getPropertyIndex(*source, std::string("bones")); + int rigBones = getPropertyIndex(rigGeometry, std::string("bones")); + if(sourceBones >= 0) { + rigBones = (rigBones >= 0 ? rigBones : rigGeometry.getNumVertexAttribArrays()); + rigGeometry.setVertexAttribArray(rigBones, source->getVertexAttribArray(sourceBones)); + source->setVertexAttribArray(sourceBones, 0); + } + + int sourceWeights = getPropertyIndex(*source, std::string("weights")); + int rigWeights = getPropertyIndex(rigGeometry, std::string("weights")); + if(sourceWeights >= 0) { + rigWeights = (rigWeights >= 0 ? rigWeights : rigGeometry.getNumVertexAttribArrays()); + rigGeometry.setVertexAttribArray(rigWeights, source->getVertexAttribArray(sourceWeights)); + source->setVertexAttribArray(sourceWeights, 0); + } + } +} + + +int RigAttributesVisitor::getPropertyIndex(const osg::Geometry& geometry, const std::string& property) { + for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) { + const osg::Array* attribute = geometry.getVertexAttribArray(i); + bool isProperty = false; + if(attribute && attribute->getUserValue(property, isProperty) && isProperty) { + return i; + } + } + return -1; +} diff --git a/src/osgPlugins/gles/SmoothNormalVisitor b/src/osgPlugins/gles/SmoothNormalVisitor index 62ba9ae75..086c28239 100644 --- a/src/osgPlugins/gles/SmoothNormalVisitor +++ b/src/osgPlugins/gles/SmoothNormalVisitor @@ -1,372 +1,10 @@ /* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */ -#include -#include -#include -#include -#include -#include -#include - #include -#include -#include #include -#include #include "GeometryUniqueVisitor" -#include "TriangleMeshGraph" - - -// Smoothing steps: -// -// 1. compute the vertex/triangles graph -// 2. compute triangle normals (vertexTriangles::addTriangle) -// 3. determine *piecewise* one-ring for each *unique* vertex (TriangleMeshGraph::vertexOneRing) -// Each piece of the one-ring contains triangles that are neighbors and do not share a sharp edge -// 4. for each one-ring piece sum the triangle normals (TriangleMeshSmoother::computeVertexNormals) -// 5. if the vertex has been processed already: duplicate and update triangles -// otherwise set the normal -// -// **triangle normals are normalized but weighted by their area when cumulated over the ring** - -class TriangleMeshSmoother { -public: - enum SmoothingMode { - recompute = 1 << 0, - diagnose = 1 << 1, - smooth_flipped = 1 << 2, - smooth_all = 1 << 3 - }; - - - class DuplicateVertex : public osg::ArrayVisitor { - public: - unsigned int _i; - unsigned int _end; - - DuplicateVertex(unsigned int i): _i(i), _end(i) - {} - - template - void apply_imp(ARRAY& array) { - _end = array.size(); - array.push_back(array[_i]); - } - - virtual void apply(osg::ByteArray& array) { apply_imp(array); } - virtual void apply(osg::ShortArray& array) { apply_imp(array); } - virtual void apply(osg::IntArray& array) { apply_imp(array); } - virtual void apply(osg::UByteArray& array) { apply_imp(array); } - virtual void apply(osg::UShortArray& array) { apply_imp(array); } - virtual void apply(osg::UIntArray& array) { apply_imp(array); } - virtual void apply(osg::FloatArray& array) { apply_imp(array); } - virtual void apply(osg::DoubleArray& array) { apply_imp(array); } - - virtual void apply(osg::Vec2Array& array) { apply_imp(array); } - virtual void apply(osg::Vec3Array& array) { apply_imp(array); } - virtual void apply(osg::Vec4Array& array) { apply_imp(array); } - - virtual void apply(osg::Vec2bArray& array) { apply_imp(array); } - virtual void apply(osg::Vec3bArray& array) { apply_imp(array); } - virtual void apply(osg::Vec4bArray& array) { apply_imp(array); } - - virtual void apply(osg::Vec2sArray& array) { apply_imp(array); } - virtual void apply(osg::Vec3sArray& array) { apply_imp(array); } - virtual void apply(osg::Vec4sArray& array) { apply_imp(array); } - - virtual void apply(osg::Vec2iArray& array) { apply_imp(array); } - virtual void apply(osg::Vec3iArray& array) { apply_imp(array); } - virtual void apply(osg::Vec4iArray& array) { apply_imp(array); } - - virtual void apply(osg::Vec2dArray& array) { apply_imp(array); } - virtual void apply(osg::Vec3dArray& array) { apply_imp(array); } - virtual void apply(osg::Vec4dArray& array) { apply_imp(array); } - - virtual void apply(osg::Vec2ubArray& array) { apply_imp(array); } - virtual void apply(osg::Vec3ubArray& array) { apply_imp(array); } - virtual void apply(osg::Vec4ubArray& array) { apply_imp(array); } - - virtual void apply(osg::Vec2usArray& array) { apply_imp(array); } - virtual void apply(osg::Vec3usArray& array) { apply_imp(array); } - virtual void apply(osg::Vec4usArray& array) { apply_imp(array); } - - virtual void apply(osg::Vec2uiArray& array) { apply_imp(array); } - virtual void apply(osg::Vec3uiArray& array) { apply_imp(array); } - virtual void apply(osg::Vec4uiArray& array) { apply_imp(array); } - - virtual void apply(osg::MatrixfArray& array) { apply_imp(array); } - virtual void apply(osg::MatrixdArray& array) { apply_imp(array); } - }; - -public: - TriangleMeshSmoother(osg::Geometry& geometry, float creaseAngle, bool comparePosition=false, int mode=diagnose): - _geometry(geometry), - _creaseAngle(creaseAngle), - _graph(0), - _mode(mode) - { - if(!_geometry.getVertexArray() || !_geometry.getVertexArray()->getNumElements()) { - return; - } - - osgUtil::SharedArrayOptimizer deduplicator; - deduplicator.findDuplicatedUVs(geometry); - - // duplicate shared arrays as it isn't safe to duplicate vertices when arrays are shared. - if (geometry.containsSharedArrays()) { - geometry.duplicateSharedArrays(); - } - - if(!_geometry.getNormalArray() || _geometry.getNormalArray()->getNumElements() != _geometry.getVertexArray()->getNumElements()) { - _geometry.setNormalArray(new osg::Vec3Array(_geometry.getVertexArray()->getNumElements()), osg::Array::BIND_PER_VERTEX); - } - - // build a unifier to consider deduplicated vertex indices - _graph = new TriangleMeshGraph(_geometry, comparePosition); - - unsigned int nbTriangles = 0; - for(unsigned int i = 0 ; i < _geometry.getNumPrimitiveSets() ; ++ i) { - osg::PrimitiveSet* primitive = _geometry.getPrimitiveSet(i); - - if(!primitive || !primitive->getNumIndices()) { - continue; - } - else if(primitive->getMode() > osg::PrimitiveSet::TRIANGLES) { - OSG_INFO << "[smoother] Cannot smooth geometry '" << _geometry.getName() - << "' due to not tessellated primitives" << std::endl; - return; - } - else if(primitive->getMode() == osg::PrimitiveSet::TRIANGLES) { - nbTriangles += primitive->getNumIndices() / 3; - } - } - _triangles.reserve(nbTriangles); - - // collect all buffers that are BIND_PER_VERTEX for eventual vertex duplication - addArray(_geometry.getVertexArray()); - addArray(_geometry.getColorArray()); - addArray(_geometry.getSecondaryColorArray()); - addArray(_geometry.getFogCoordArray()); - for(unsigned int i = 0; i < _geometry.getNumTexCoordArrays(); ++ i) { - addArray(_geometry.getTexCoordArray(i)); - } - for(unsigned int i = 0; i < _geometry.getNumVertexAttribArrays(); ++ i) { - addArray(_geometry.getVertexAttribArray(i)); - } - - switch(_mode) { - case recompute: - computeVertexNormals(); - break; - case smooth_all: - smoothVertexNormals(true, true); - break; - case smooth_flipped: - smoothVertexNormals(true, false); - break; - case diagnose: - smoothVertexNormals(false, false); - break; - }; - - // deduplicate UVs array that were only shared within the geometry - deduplicator.deduplicateUVs(geometry); - } - - ~TriangleMeshSmoother() { - if(_graph) { - delete _graph; - } - } - -protected: - unsigned int duplicateVertex(unsigned int index) { - DuplicateVertex duplicate(index); - for(ArrayVector::iterator array = _vertexArrays.begin(); array != _vertexArrays.end(); ++ array) { - (*array)->accept(duplicate); - } -#if 0 - OSG_INFO << "[normals] [[TriangleMeshSmoother]] vertex " << index - << " duplicated => " << duplicate._end << std::endl; -#endif - _graph->add(duplicate._end, index); - return duplicate._end; - } - - void smoothVertexNormals(bool fix=true, bool force=false) { - _vertexArrays.clear(); // make sure we do not change vertex count - bool flipped = false; - - osg::Vec3Array* normals = dynamic_cast(_geometry.getNormalArray()); - osg::Vec3Array* positions = dynamic_cast(_geometry.getVertexArray()); - - if(!positions || !normals || normals->getNumElements() != positions->getNumElements()) { - OSG_WARN << std::endl - << "Warning: [smoothVertexNormals] [[normals]] Geometry '" << _geometry.getName() - << "' has invalid positions/normals"; - return; - } - - for(unsigned int index = 0 ; index < positions->getNumElements() ; ++ index) { - std::vector oneRing = _graph->vertexOneRing(_graph->unify(index), _creaseAngle); - osg::Vec3f smoothedNormal(0.f, 0.f, 0.f); - - // sum normals for each cluster in the one-ring - for(std::vector::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) { - smoothedNormal += cumulateTriangleNormals(*cluster); - } - - float length = smoothedNormal.normalize(); - if(length > 0.) { - if(force || smoothedNormal * normals->at(index) < 1.e-6) { - flipped = true; - if(fix) { - (*normals)[index] = smoothedNormal; - } - } - } - } - - if(flipped) { - OSG_WARN << std::endl << "Warning: [smoothVertexNormals] [[normals]] Geometry '" << _geometry.getName() << "' "; - switch(_mode) { - case diagnose: - OSG_WARN << "has some flipped normals; please check that the shading is correct" << std::endl; - OSG_WARN << "Monitor: normal.invalid" << std::endl; - break; - case smooth_flipped: - OSG_WARN << "has some flipped normals that have been fixed" << std::endl; - OSG_WARN << "Monitor: normal.smooth_flipped" << std::endl; - break; - case smooth_all: - OSG_WARN << "normals have all been smoothed" << std::endl; - OSG_WARN << "Monitor: normal.smooth_all" << std::endl; - break; - } - } - } - - void computeVertexNormals() { - osg::Vec3Array* normals = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX, - _geometry.getVertexArray()->getNumElements()); - addArray(normals); - - for(unsigned int i = 0 ; i < normals->getNumElements() ; ++ i) { - (*normals)[i].set(0.f, 0.f, 0.f); - } - - for(VertexIterator uniqueIndex = _graph->begin() ; uniqueIndex != _graph->end() ; ++ uniqueIndex) { - unsigned int index = uniqueIndex->_index; - std::set processed; - - std::vector oneRing = _graph->vertexOneRing(index, _creaseAngle); - for(std::vector::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) { - osg::Vec3f clusterNormal = cumulateTriangleNormals(*cluster); - clusterNormal.normalize(); - - std::set duplicates; - for(IndexVector::const_iterator tri = cluster->begin() ; tri != cluster->end() ; ++ tri) { - const Triangle& triangle = _graph->triangle(*tri); - - if(_graph->unify(triangle.v1()) == index) { - duplicates.insert(triangle.v1()); - } - else if(_graph->unify(triangle.v2()) == index) { - duplicates.insert(triangle.v2()); - } - else if(_graph->unify(triangle.v3()) == index) { - duplicates.insert(triangle.v3()); - } - } - - for(std::set::iterator vertex = duplicates.begin() ; vertex != duplicates.end() ; ++ vertex) { - if(processed.find(*vertex) == processed.end()) { - // vertex not yet processed - (*normals)[*vertex] = clusterNormal; - processed.insert(*vertex); - } - else { - // vertex already processed in a previous cluster: need to duplicate - unsigned int duplicate = duplicateVertex(*vertex); - replaceVertexIndexInTriangles(*cluster, *vertex, duplicate); - (*normals)[duplicate] = clusterNormal; - - processed.insert(duplicate); - } - } - } - } - - _geometry.setNormalArray(normals, osg::Array::BIND_PER_VERTEX); - updateGeometryPrimitives(); - - OSG_WARN << std::endl <<"Warning: [computeVertexNormals] [[normals]] Geometry '" << _geometry.getName() - << "' normals have been recomputed" << std::endl; - OSG_WARN << "Monitor: normal.recompute" << std::endl; - } - - osg::Vec3f cumulateTriangleNormals(const IndexVector& triangles) const { - osg::Vec3f normal; - normal.set(0.f, 0.f, 0.f); - for(IndexVector::const_iterator triangle = triangles.begin() ; triangle != triangles.end() ; ++ triangle) { - const Triangle& t = _graph->triangle(*triangle); - normal += (t._normal * t._area); - } - return normal; - } - - void replaceVertexIndexInTriangles(const IndexVector& triangles, unsigned int oldIndex, unsigned int newIndex) { - for(IndexVector::const_iterator tri = triangles.begin() ; tri != triangles.end() ; ++ tri) { - Triangle& triangle = _graph->triangle(*tri); - if(triangle.v1() == oldIndex) { - triangle.v1() = newIndex; - } - else if(triangle.v2() == oldIndex) { - triangle.v2() = newIndex; - } - else if(triangle.v3() == oldIndex) { - triangle.v3() = newIndex; - } - } - } - - void addArray(osg::Array* array) { - if (array && array->getBinding() == osg::Array::BIND_PER_VERTEX) { - _vertexArrays.push_back(array); - } - } - - void updateGeometryPrimitives() { - osg::Geometry::PrimitiveSetList primitives; - for(unsigned int i = 0 ; i < _geometry.getNumPrimitiveSets() ; ++ i) { - osg::PrimitiveSet* primitive = _geometry.getPrimitiveSet(i); - if(primitive && primitive->getMode() < osg::PrimitiveSet::TRIANGLES) { - primitives.push_back(primitive); - } - } - - osg::DrawElementsUInt* triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES); - for(unsigned int i = 0 ; i < _graph->getNumTriangles() ; ++ i) { - const Triangle& triangle = _graph->triangle(i); - triangles->push_back(triangle.v1()); - triangles->push_back(triangle.v2()); - triangles->push_back(triangle.v3()); - } - primitives.push_back(triangles); - - _geometry.setPrimitiveSetList(primitives); - } - - - osg::Geometry& _geometry; - float _creaseAngle; - TriangleMeshGraph* _graph; - TriangleVector _triangles; - ArrayVector _vertexArrays; - int _mode; // smooth or recompute normals -}; - +#include "TriangleMeshSmoother" class SmoothNormalVisitor : public GeometryUniqueVisitor { diff --git a/src/osgPlugins/gles/StatLogger b/src/osgPlugins/gles/StatLogger index a69f8006d..c6f164e27 100644 --- a/src/osgPlugins/gles/StatLogger +++ b/src/osgPlugins/gles/StatLogger @@ -10,15 +10,19 @@ class StatLogger { public: - StatLogger(const std::string& label): _label(label) + StatLogger(const std::string& label): + _label(label) { _start = _stop = getTick(); } + + ~StatLogger() { _stop = getTick(); - OSG_INFO << std::flush + + OSG_INFO << std::endl << "Info: " << _label << " timing: " << getElapsedSeconds() << "s" - << std::endl << std::flush; + << std::endl; } protected: diff --git a/src/osgPlugins/gles/SubGeometry b/src/osgPlugins/gles/SubGeometry index 378ab94fc..aa69f8770 100644 --- a/src/osgPlugins/gles/SubGeometry +++ b/src/osgPlugins/gles/SubGeometry @@ -19,259 +19,36 @@ public: typedef std::map::iterator BufferIterator; typedef std::map IndexMapping; - SubGeometry(const osg::Geometry& source, - const std::vector& triangles, - const std::vector& lines, - const std::vector& wireframe, - const std::vector& points) - { - // Create new geometry as we will modify vertex arrays and primitives (the process is - // equivalent to deep cloning). - // As UserValues might be changed on a per-geometry basis afterwards, we deep clone userdata - // We do *not* want to clone statesets as they reference a UniqueID that should be unique - // (see #83056464). - if(dynamic_cast(&source)) { - _geometry = new osgAnimation::MorphGeometry; - } - else { - _geometry = new osg::Geometry; - } - - if(source.getUserDataContainer()) { - _geometry->setUserDataContainer(osg::clone(source.getUserDataContainer(), osg::CopyOp::DEEP_COPY_ALL)); - } - - if(source.getStateSet()) { - _geometry->setStateSet(const_cast(source.getStateSet())); - } - - addSourceBuffers(_geometry.get(), source); - - // process morph targets if needed - if(const osgAnimation::MorphGeometry* morphSource = dynamic_cast(&source)) { - osgAnimation::MorphGeometry* morph = dynamic_cast(_geometry.get()); - if (morph) - { - const osgAnimation::MorphGeometry::MorphTargetList& morphTargetList = morphSource->getMorphTargetList(); - - osgAnimation::MorphGeometry::MorphTargetList::const_iterator targetSource; - for(targetSource = morphTargetList.begin() ; targetSource != morphTargetList.end() ; ++ targetSource) { - if(targetSource->getGeometry()) { - osg::Geometry* target = new osg::Geometry; - addSourceBuffers(target, *targetSource->getGeometry()); - morph->addMorphTarget(target, targetSource->getWeight()); - } - } - } - } - - // remap primitives indices by decreasing ordering (triangles > lines > wireframe > points) - for(unsigned int i = 0 ; i < triangles.size() ; i += 3) { - copyTriangle(triangles[i], triangles[i + 1], triangles[i + 2]); - } - - for(unsigned int i = 0 ; i < lines.size() ; i += 2) { - copyEdge(lines[i], lines[i + 1], false); - } - - for(unsigned int i = 0 ; i < wireframe.size() ; i += 2) { - copyEdge(wireframe[i], wireframe[i + 1], true); - } - - for(unsigned int i = 0 ; i < points.size() ; ++ i) { - copyPoint(points[i]); - } - - // remap vertex buffers accordingly to primitives - for(BufferIterator it = _bufferMap.begin() ; it != _bufferMap.end() ; ++ it) { - if(it->first) { - copyFrom(*(it->second), *(it->first)); - } - } - } + SubGeometry(const osg::Geometry&, + const std::vector&, + const std::vector&, + const std::vector&, + const std::vector&); osg::Geometry* geometry() const { return _geometry.get(); } protected: - void addSourceBuffers(osg::Geometry* geometry, const osg::Geometry& source) { - // create necessary vertex containers - const osg::Array* array = 0; + osg::Array* makeVertexBuffer(const osg::Array*, bool /*copyUserData*/=true); - geometry->setName(source.getName()); - - // collect all buffers that are BIND_PER_VERTEX for eventual vertex duplication - if( (array = vertexArray(source.getVertexArray())) ) { - geometry->setVertexArray(makeVertexBuffer(array)); - } - - if( (array = vertexArray(source.getNormalArray())) ){ - geometry->setNormalArray(makeVertexBuffer(array)); - } - - if( (array = vertexArray(source.getColorArray())) ){ - geometry->setColorArray(makeVertexBuffer(array)); - } - - if( (array = vertexArray(source.getSecondaryColorArray())) ){ - geometry->setSecondaryColorArray(makeVertexBuffer(array)); - } - - if( (array = vertexArray(source.getFogCoordArray())) ){ - geometry->setFogCoordArray(makeVertexBuffer(array)); - } - - for(unsigned int i = 0; i < source.getNumVertexAttribArrays(); ++ i) { - if( (array = vertexArray(source.getVertexAttribArray(i))) ){ - geometry->setVertexAttribArray(i, makeVertexBuffer(array)); - } - } - - for(unsigned int i = 0; i < source.getNumTexCoordArrays(); ++ i) { - if( (array = vertexArray(source.getTexCoordArray(i))) ){ - geometry->setTexCoordArray(i, makeVertexBuffer(array)); - } - } - } - - void copyTriangle(unsigned int v1, unsigned int v2, unsigned int v3) { - osg::DrawElements* triangles = getOrCreateTriangles(); - triangles->addElement(mapVertex(v1)); - triangles->addElement(mapVertex(v2)); - triangles->addElement(mapVertex(v3)); - } - - void copyEdge(unsigned int v1, unsigned int v2, bool wireframe) { - osg::DrawElements* edges = getOrCreateLines(wireframe); - edges->addElement(mapVertex(v1)); - edges->addElement(mapVertex(v2)); - } - - void copyPoint(unsigned int v1) { - osg::DrawElements* points = getOrCreatePoints(); - points->addElement(mapVertex(v1)); - } - - const osg::Array* vertexArray(const osg::Array* array) { - // filter invalid vertex buffers - if (array && array->getNumElements() && array->getBinding() == osg::Array::BIND_PER_VERTEX) { - return array; - } - else { - return 0; - } - } - - inline osg::Array* makeVertexBuffer(const osg::Array* array, bool copyUserData=true) { - osg::Array* buffer = array ? osg::cloneType(array) : 0; - if(buffer) { - buffer->setBinding(osg::Array::BIND_PER_VERTEX); - if(copyUserData && array->getUserDataContainer()) { - buffer->setUserDataContainer(osg::clone(array->getUserDataContainer(), osg::CopyOp::DEEP_COPY_ALL)); - } - _bufferMap[buffer] = array; - } - return buffer; - } + void addSourceBuffers(osg::Geometry*, const osg::Geometry&); + const osg::Array* vertexArray(const osg::Array* array); + unsigned int mapVertex(unsigned int); + void copyTriangle(unsigned int, unsigned int, unsigned int); + void copyEdge(unsigned int, unsigned int, bool); + void copyPoint(unsigned int); + void copyFrom(const osg::Array&, osg::Array&); template - void copyValues(const C& src, C& dst) { - dst.resize(_indexMap.size()); - for(IndexMapping::const_iterator remapper = _indexMap.begin() ; remapper != _indexMap.end() ; ++ remapper) { - dst[remapper->second] = src[remapper->first]; - } - } + void copyValues(const C& src, C& dst); -#define COPY_TEMPLATE(T) \ - if (dynamic_cast(&src)) \ - { return copyValues(dynamic_cast(src), dynamic_cast(dst)); } - - void copyFrom(const osg::Array& src, osg::Array& dst) { - COPY_TEMPLATE(osg::Vec2Array); - COPY_TEMPLATE(osg::Vec3Array); - COPY_TEMPLATE(osg::Vec4Array); - - COPY_TEMPLATE(osg::Vec2dArray); - COPY_TEMPLATE(osg::Vec3dArray); - COPY_TEMPLATE(osg::Vec4dArray); - - COPY_TEMPLATE(osg::ByteArray); - COPY_TEMPLATE(osg::ShortArray); - COPY_TEMPLATE(osg::IntArray); - COPY_TEMPLATE(osg::UByteArray); - COPY_TEMPLATE(osg::UShortArray); - COPY_TEMPLATE(osg::UIntArray); - COPY_TEMPLATE(osg::FloatArray); - COPY_TEMPLATE(osg::DoubleArray); - - COPY_TEMPLATE(osg::Vec2iArray); - COPY_TEMPLATE(osg::Vec3iArray); - COPY_TEMPLATE(osg::Vec4iArray); - - COPY_TEMPLATE(osg::Vec2uiArray); - COPY_TEMPLATE(osg::Vec3uiArray); - COPY_TEMPLATE(osg::Vec4uiArray); - - COPY_TEMPLATE(osg::Vec2sArray); - COPY_TEMPLATE(osg::Vec3sArray); - COPY_TEMPLATE(osg::Vec4sArray); - - COPY_TEMPLATE(osg::Vec2usArray); - COPY_TEMPLATE(osg::Vec3usArray); - COPY_TEMPLATE(osg::Vec4usArray); - - COPY_TEMPLATE(osg::Vec2bArray); - COPY_TEMPLATE(osg::Vec3bArray); - COPY_TEMPLATE(osg::Vec4bArray); - - COPY_TEMPLATE(osg::Vec2ubArray); - COPY_TEMPLATE(osg::Vec3ubArray); - COPY_TEMPLATE(osg::Vec4ubArray); - - COPY_TEMPLATE(osg::MatrixfArray); - - COPY_TEMPLATE(osg::QuatArray); - } - - unsigned int mapVertex(unsigned int i) { - if(_indexMap.find(i) == _indexMap.end()) { - unsigned int index = _indexMap.size(); - _indexMap[i] = index; - } - return _indexMap[i]; - } - - osg::DrawElements* getOrCreateTriangles() { - if(_primitives.find("triangles") == _primitives.end()) { - _primitives["triangles"] = new osg::DrawElementsUInt(GL_TRIANGLES); - _geometry->addPrimitiveSet(_primitives["triangles"]); - } - return _primitives["triangles"]; - } - - osg::DrawElements* getOrCreateLines(bool wireframe) { - std::string label = wireframe ? "wireframe" : "lines"; - - if(_primitives.find(label) == _primitives.end()) { - _primitives[label] = new osg::DrawElementsUInt(GL_LINES); - if(wireframe) { - _primitives[label]->setUserValue("wireframe", true); - } - _geometry->addPrimitiveSet(_primitives[label]); - } - return _primitives[label]; - } - - osg::DrawElements* getOrCreatePoints() { - if(_primitives.find("points") == _primitives.end()) { - _primitives["points"] = new osg::DrawElementsUInt(GL_POINTS); - _geometry->addPrimitiveSet(_primitives["points"]); - } - return _primitives["points"]; - } + osg::DrawElements* getOrCreateTriangles(); + osg::DrawElements* getOrCreateLines(bool); + osg::DrawElements* getOrCreatePoints(); +protected: osg::ref_ptr _geometry; std::map _bufferMap; std::map _indexMap; diff --git a/src/osgPlugins/gles/SubGeometry.cpp b/src/osgPlugins/gles/SubGeometry.cpp new file mode 100644 index 000000000..1b86c85cd --- /dev/null +++ b/src/osgPlugins/gles/SubGeometry.cpp @@ -0,0 +1,261 @@ +#include "SubGeometry" + + +SubGeometry::SubGeometry(const osg::Geometry& source, + const std::vector& triangles, + const std::vector& lines, + const std::vector& wireframe, + const std::vector& points) +{ + // Create new geometry as we will modify vertex arrays and primitives (the process is + // equivalent to deep cloning). + // As UserValues might be changed on a per-geometry basis afterwards, we deep clone userdata + // We do *not* want to clone statesets as they reference a UniqueID that should be unique + // (see #83056464). + if(dynamic_cast(&source)) { + _geometry = new osgAnimation::MorphGeometry; + } + else { + _geometry = new osg::Geometry; + } + + if(source.getUserDataContainer()) { + _geometry->setUserDataContainer(osg::clone(source.getUserDataContainer(), osg::CopyOp::DEEP_COPY_ALL)); + } + + if(source.getStateSet()) { + _geometry->setStateSet(const_cast(source.getStateSet())); + } + + addSourceBuffers(_geometry.get(), source); + + // process morph targets if needed + if(const osgAnimation::MorphGeometry* morphSource = dynamic_cast(&source)) { + osgAnimation::MorphGeometry* morph = dynamic_cast(_geometry.get()); + if (morph) + { + const osgAnimation::MorphGeometry::MorphTargetList& morphTargetList = morphSource->getMorphTargetList(); + + osgAnimation::MorphGeometry::MorphTargetList::const_iterator targetSource; + for(targetSource = morphTargetList.begin() ; targetSource != morphTargetList.end() ; ++ targetSource) { + if(targetSource->getGeometry()) { + osg::Geometry* target = new osg::Geometry; + addSourceBuffers(target, *targetSource->getGeometry()); + morph->addMorphTarget(target, targetSource->getWeight()); + } + } + } + } + + // remap primitives indices by decreasing ordering (triangles > lines > wireframe > points) + for(unsigned int i = 0 ; i < triangles.size() ; i += 3) { + copyTriangle(triangles[i], triangles[i + 1], triangles[i + 2]); + } + + for(unsigned int i = 0 ; i < lines.size() ; i += 2) { + copyEdge(lines[i], lines[i + 1], false); + } + + for(unsigned int i = 0 ; i < wireframe.size() ; i += 2) { + copyEdge(wireframe[i], wireframe[i + 1], true); + } + + for(unsigned int i = 0 ; i < points.size() ; ++ i) { + copyPoint(points[i]); + } + + // remap vertex buffers accordingly to primitives + for(BufferIterator it = _bufferMap.begin() ; it != _bufferMap.end() ; ++ it) { + if(it->first) { + copyFrom(*(it->second), *(it->first)); + } + } +} + + +void SubGeometry::addSourceBuffers(osg::Geometry* geometry, const osg::Geometry& source) { + // create necessary vertex containers + const osg::Array* array = 0; + + geometry->setName(source.getName()); + + // collect all buffers that are BIND_PER_VERTEX for eventual vertex duplication + if( (array = vertexArray(source.getVertexArray())) ) { + geometry->setVertexArray(makeVertexBuffer(array)); + } + + if( (array = vertexArray(source.getNormalArray())) ){ + geometry->setNormalArray(makeVertexBuffer(array)); + } + + if( (array = vertexArray(source.getColorArray())) ){ + geometry->setColorArray(makeVertexBuffer(array)); + } + + if( (array = vertexArray(source.getSecondaryColorArray())) ){ + geometry->setSecondaryColorArray(makeVertexBuffer(array)); + } + + if( (array = vertexArray(source.getFogCoordArray())) ){ + geometry->setFogCoordArray(makeVertexBuffer(array)); + } + + for(unsigned int i = 0; i < source.getNumVertexAttribArrays(); ++ i) { + if( (array = vertexArray(source.getVertexAttribArray(i))) ){ + geometry->setVertexAttribArray(i, makeVertexBuffer(array)); + } + } + + for(unsigned int i = 0; i < source.getNumTexCoordArrays(); ++ i) { + if( (array = vertexArray(source.getTexCoordArray(i))) ){ + geometry->setTexCoordArray(i, makeVertexBuffer(array)); + } + } +} + + +void SubGeometry::copyTriangle(unsigned int v1, unsigned int v2, unsigned int v3) { + osg::DrawElements* triangles = getOrCreateTriangles(); + triangles->addElement(mapVertex(v1)); + triangles->addElement(mapVertex(v2)); + triangles->addElement(mapVertex(v3)); +} + + +void SubGeometry::copyEdge(unsigned int v1, unsigned int v2, bool wireframe) { + osg::DrawElements* edges = getOrCreateLines(wireframe); + edges->addElement(mapVertex(v1)); + edges->addElement(mapVertex(v2)); +} + + +void SubGeometry::copyPoint(unsigned int v1) { + osg::DrawElements* points = getOrCreatePoints(); + points->addElement(mapVertex(v1)); +} + + +const osg::Array* SubGeometry::vertexArray(const osg::Array* array) { + // filter invalid vertex buffers + if (array && array->getNumElements() && array->getBinding() == osg::Array::BIND_PER_VERTEX) { + return array; + } + else { + return 0; + } +} + + +osg::Array* SubGeometry::makeVertexBuffer(const osg::Array* array, bool copyUserData) { + osg::Array* buffer = array ? osg::cloneType(array) : 0; + if(buffer) { + buffer->setBinding(osg::Array::BIND_PER_VERTEX); + if(copyUserData && array->getUserDataContainer()) { + buffer->setUserDataContainer(osg::clone(array->getUserDataContainer(), osg::CopyOp::DEEP_COPY_ALL)); + } + _bufferMap[buffer] = array; + } + return buffer; +} + + +template +void SubGeometry::copyValues(const C& src, C& dst) { + dst.resize(_indexMap.size()); + for(IndexMapping::const_iterator remapper = _indexMap.begin() ; remapper != _indexMap.end() ; ++ remapper) { + dst[remapper->second] = src[remapper->first]; + } +} + + +#define COPY_TEMPLATE(T) \ +if (dynamic_cast(&src)) \ +{ copyValues(dynamic_cast(src), dynamic_cast(dst)); } + +void SubGeometry::copyFrom(const osg::Array& src, osg::Array& dst) { + COPY_TEMPLATE(osg::Vec2Array); + COPY_TEMPLATE(osg::Vec3Array); + COPY_TEMPLATE(osg::Vec4Array); + + COPY_TEMPLATE(osg::Vec2dArray); + COPY_TEMPLATE(osg::Vec3dArray); + COPY_TEMPLATE(osg::Vec4dArray); + + COPY_TEMPLATE(osg::ByteArray); + COPY_TEMPLATE(osg::ShortArray); + COPY_TEMPLATE(osg::IntArray); + COPY_TEMPLATE(osg::UByteArray); + COPY_TEMPLATE(osg::UShortArray); + COPY_TEMPLATE(osg::UIntArray); + COPY_TEMPLATE(osg::FloatArray); + COPY_TEMPLATE(osg::DoubleArray); + + COPY_TEMPLATE(osg::Vec2iArray); + COPY_TEMPLATE(osg::Vec3iArray); + COPY_TEMPLATE(osg::Vec4iArray); + + COPY_TEMPLATE(osg::Vec2uiArray); + COPY_TEMPLATE(osg::Vec3uiArray); + COPY_TEMPLATE(osg::Vec4uiArray); + + COPY_TEMPLATE(osg::Vec2sArray); + COPY_TEMPLATE(osg::Vec3sArray); + COPY_TEMPLATE(osg::Vec4sArray); + + COPY_TEMPLATE(osg::Vec2usArray); + COPY_TEMPLATE(osg::Vec3usArray); + COPY_TEMPLATE(osg::Vec4usArray); + + COPY_TEMPLATE(osg::Vec2bArray); + COPY_TEMPLATE(osg::Vec3bArray); + COPY_TEMPLATE(osg::Vec4bArray); + + COPY_TEMPLATE(osg::Vec2ubArray); + COPY_TEMPLATE(osg::Vec3ubArray); + COPY_TEMPLATE(osg::Vec4ubArray); + + COPY_TEMPLATE(osg::MatrixfArray); + + COPY_TEMPLATE(osg::QuatArray); +} + + +unsigned int SubGeometry::mapVertex(unsigned int i) { + if(_indexMap.find(i) == _indexMap.end()) { + unsigned int index = _indexMap.size(); + _indexMap[i] = index; + } + return _indexMap[i]; +} + + +osg::DrawElements* SubGeometry::getOrCreateTriangles() { + if(_primitives.find("triangles") == _primitives.end()) { + _primitives["triangles"] = new osg::DrawElementsUInt(GL_TRIANGLES); + _geometry->addPrimitiveSet(_primitives["triangles"]); + } + return _primitives["triangles"]; +} + + +osg::DrawElements* SubGeometry::getOrCreateLines(bool wireframe) { + std::string label = wireframe ? "wireframe" : "lines"; + + if(_primitives.find(label) == _primitives.end()) { + _primitives[label] = new osg::DrawElementsUInt(GL_LINES); + if(wireframe) { + _primitives[label]->setUserValue("wireframe", true); + } + _geometry->addPrimitiveSet(_primitives[label]); + } + return _primitives[label]; +} + + +osg::DrawElements* SubGeometry::getOrCreatePoints() { + if(_primitives.find("points") == _primitives.end()) { + _primitives["points"] = new osg::DrawElementsUInt(GL_POINTS); + _geometry->addPrimitiveSet(_primitives["points"]); + } + return _primitives["points"]; +} diff --git a/src/osgPlugins/gles/TangentSpaceVisitor b/src/osgPlugins/gles/TangentSpaceVisitor index 0b6211041..07c9bbfda 100644 --- a/src/osgPlugins/gles/TangentSpaceVisitor +++ b/src/osgPlugins/gles/TangentSpaceVisitor @@ -28,110 +28,13 @@ class TangentSpaceVisitor : public GeometryUniqueVisitor { public: - TangentSpaceVisitor(int textureUnit = 0): + TangentSpaceVisitor(int textureUnit=0): GeometryUniqueVisitor("TangentSpaceVisitor"), _textureUnit(textureUnit) {} - void process(osgAnimation::MorphGeometry& morphGeometry) { - process(static_cast(morphGeometry)); - - osgAnimation::MorphGeometry::MorphTargetList& targets = morphGeometry.getMorphTargetList(); - for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) { - osg::Geometry* geometry = target->getGeometry(); - bool useParentMorphTexCoord = geometry->getTexCoordArrayList().empty(); - - if(useParentMorphTexCoord) { - // tangent space require tex coords; in case a target has no tex coords, we try to - // bind the parent geometry tex coords - geometry->setTexCoordArrayList(morphGeometry.getTexCoordArrayList()); - } - - process(*geometry); - - if(useParentMorphTexCoord) { - // drop parent tex coords after tangent space computation - geometry->setTexCoordArrayList(osg::Geometry::ArrayList()); - } - } - } - - void process(osg::Geometry& geom) { - // We don't have to recompute the tangent space if we already have the data - int tangentIndex = -1; - if (geom.getUserValue(std::string("tangent"), tangentIndex) && tangentIndex != -1) - { - if(geom.getVertexAttribArray(tangentIndex)) { - OSG_INFO << "[TangentSpaceVisitor::apply] Geometry '" << geom.getName() - << "' The tangent space is not recomputed as it was given within the original file" << std::endl; - geom.getVertexAttribArray(tangentIndex)->setUserValue("tangent", true); - return; - } - } - - if (!geom.getTexCoordArray(_textureUnit)){ - int texUnit = 0; - bool found = false; - while(texUnit < 32){ - if (_textureUnit != texUnit && geom.getTexCoordArray(texUnit)){ - _textureUnit = texUnit; - found = true; - break; - } - texUnit++; - } - if (!found) - return; - } - - osg::ref_ptr generator = new osgUtil::TangentSpaceGenerator; - generator->generate(&geom, _textureUnit); - - // keep original normal array - if (!geom.getNormalArray()) { - if (generator->getNormalArray()) { - osg::Vec3Array* vec3Normals = new osg::Vec3Array(); - osg::Vec4Array* vec4Normals = generator->getNormalArray(); - for (unsigned int i = 0; i < vec4Normals->size(); i++) { - osg::Vec3 n = osg::Vec3((*vec4Normals)[i][0], - (*vec4Normals)[i][1], - (*vec4Normals)[i][2]); - vec3Normals->push_back(n); - } - geom.setNormalArray(vec3Normals, osg::Array::BIND_PER_VERTEX); - } - } - - if (generator->getTangentArray()) { - osg::Vec4Array* normal = generator->getNormalArray(); - osg::Vec4Array* tangent = generator->getTangentArray(); - osg::Vec4Array* tangent2 = generator->getBinormalArray(); - osg::Vec4Array* finalTangent = osg::clone(generator->getTangentArray(), 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); - tangentIndex = (tangentIndex >= 0 ? tangentIndex : geom.getNumVertexAttribArrays()) ; - geom.setVertexAttribArray(tangentIndex, finalTangent, osg::Array::BIND_PER_VERTEX); - } - } + void process(osgAnimation::MorphGeometry&); + void process(osg::Geometry&); protected: int _textureUnit; diff --git a/src/osgPlugins/gles/TangentSpaceVisitor.cpp b/src/osgPlugins/gles/TangentSpaceVisitor.cpp new file mode 100644 index 000000000..874d85657 --- /dev/null +++ b/src/osgPlugins/gles/TangentSpaceVisitor.cpp @@ -0,0 +1,103 @@ +#include "TangentSpaceVisitor" + + +void TangentSpaceVisitor::process(osgAnimation::MorphGeometry& morphGeometry) { + process(static_cast(morphGeometry)); + + osgAnimation::MorphGeometry::MorphTargetList& targets = morphGeometry.getMorphTargetList(); + for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) { + osg::Geometry* geometry = target->getGeometry(); + bool useParentMorphTexCoord = geometry->getTexCoordArrayList().empty(); + + if(useParentMorphTexCoord) { + // tangent space require tex coords; in case a target has no tex coords, we try to + // bind the parent geometry tex coords + geometry->setTexCoordArrayList(morphGeometry.getTexCoordArrayList()); + } + + process(*geometry); + + if(useParentMorphTexCoord) { + // drop parent tex coords after tangent space computation + geometry->setTexCoordArrayList(osg::Geometry::ArrayList()); + } + } +} + + +void TangentSpaceVisitor::process(osg::Geometry& geometry) { + // We don't have to recompute the tangent space if we already have the data + int tangentIndex = -1; + if (geometry.getUserValue(std::string("tangent"), tangentIndex) && tangentIndex != -1) + { + if(geometry.getVertexAttribArray(tangentIndex)) { + OSG_INFO << "[TangentSpaceVisitor::apply] Geometry '" << geometry.getName() + << "' The tangent space is not recomputed as it was given within the original file" << std::endl; + geometry.getVertexAttribArray(tangentIndex)->setUserValue("tangent", true); + return; + } + } + + if (!geometry.getTexCoordArray(_textureUnit)){ + int texUnit = 0; + bool found = false; + while(texUnit < 32){ + if (_textureUnit != texUnit && geometry.getTexCoordArray(texUnit)){ + _textureUnit = texUnit; + found = true; + break; + } + texUnit++; + } + if (!found) + return; + } + + osg::ref_ptr generator = new osgUtil::TangentSpaceGenerator; + generator->generate(&geometry, _textureUnit); + + // keep original normal array + if (!geometry.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); + } + geometry.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 = osg::clone(generator->getTangentArray(), 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); + tangentIndex = (tangentIndex >= 0 ? tangentIndex : geometry.getNumVertexAttribArrays()) ; + geometry.setVertexAttribArray(tangentIndex, finalTangent, osg::Array::BIND_PER_VERTEX); + } +} diff --git a/src/osgPlugins/gles/TriangleMeshSmoother b/src/osgPlugins/gles/TriangleMeshSmoother new file mode 100644 index 000000000..2172386d2 --- /dev/null +++ b/src/osgPlugins/gles/TriangleMeshSmoother @@ -0,0 +1,134 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "GeometryUniqueVisitor" +#include "TriangleMeshGraph" + + +// Smoothing steps: +// +// 1. compute the vertex/triangles graph +// 2. compute triangle normals (vertexTriangles::addTriangle) +// 3. determine *piecewise* one-ring for each *unique* vertex (TriangleMeshGraph::vertexOneRing) +// Each piece of the one-ring contains triangles that are neighbors and do not share a sharp edge +// 4. for each one-ring piece sum the triangle normals (TriangleMeshSmoother::computeVertexNormals) +// 5. if the vertex has been processed already: duplicate and update triangles +// otherwise set the normal +// +// **triangle normals are normalized but weighted by their area when cumulated over the ring** + +class TriangleMeshSmoother { +public: + enum SmoothingMode { + recompute = 1 << 0, + diagnose = 1 << 1, + smooth_flipped = 1 << 2, + smooth_all = 1 << 3 + }; + + + class DuplicateVertex : public osg::ArrayVisitor { + public: + unsigned int _i; + unsigned int _end; + + DuplicateVertex(unsigned int i): _i(i), _end(i) + {} + + template + void apply_imp(ARRAY& array) { + _end = array.size(); + array.push_back(array[_i]); + } + + virtual void apply(osg::ByteArray& array) { apply_imp(array); } + virtual void apply(osg::ShortArray& array) { apply_imp(array); } + virtual void apply(osg::IntArray& array) { apply_imp(array); } + virtual void apply(osg::UByteArray& array) { apply_imp(array); } + virtual void apply(osg::UShortArray& array) { apply_imp(array); } + virtual void apply(osg::UIntArray& array) { apply_imp(array); } + virtual void apply(osg::FloatArray& array) { apply_imp(array); } + virtual void apply(osg::DoubleArray& array) { apply_imp(array); } + + virtual void apply(osg::Vec2Array& array) { apply_imp(array); } + virtual void apply(osg::Vec3Array& array) { apply_imp(array); } + virtual void apply(osg::Vec4Array& array) { apply_imp(array); } + + virtual void apply(osg::Vec2bArray& array) { apply_imp(array); } + virtual void apply(osg::Vec3bArray& array) { apply_imp(array); } + virtual void apply(osg::Vec4bArray& array) { apply_imp(array); } + + virtual void apply(osg::Vec2sArray& array) { apply_imp(array); } + virtual void apply(osg::Vec3sArray& array) { apply_imp(array); } + virtual void apply(osg::Vec4sArray& array) { apply_imp(array); } + + virtual void apply(osg::Vec2iArray& array) { apply_imp(array); } + virtual void apply(osg::Vec3iArray& array) { apply_imp(array); } + virtual void apply(osg::Vec4iArray& array) { apply_imp(array); } + + virtual void apply(osg::Vec2dArray& array) { apply_imp(array); } + virtual void apply(osg::Vec3dArray& array) { apply_imp(array); } + virtual void apply(osg::Vec4dArray& array) { apply_imp(array); } + + virtual void apply(osg::Vec2ubArray& array) { apply_imp(array); } + virtual void apply(osg::Vec3ubArray& array) { apply_imp(array); } + virtual void apply(osg::Vec4ubArray& array) { apply_imp(array); } + + virtual void apply(osg::Vec2usArray& array) { apply_imp(array); } + virtual void apply(osg::Vec3usArray& array) { apply_imp(array); } + virtual void apply(osg::Vec4usArray& array) { apply_imp(array); } + + virtual void apply(osg::Vec2uiArray& array) { apply_imp(array); } + virtual void apply(osg::Vec3uiArray& array) { apply_imp(array); } + virtual void apply(osg::Vec4uiArray& array) { apply_imp(array); } + + virtual void apply(osg::MatrixfArray& array) { apply_imp(array); } + virtual void apply(osg::MatrixdArray& array) { apply_imp(array); } + }; + +public: + TriangleMeshSmoother(osg::Geometry& geometry, float creaseAngle, bool /*comparePosition*/=false, int /*mode*/=diagnose); + + ~TriangleMeshSmoother() { + if(_graph) { + delete _graph; + } + } + +protected: + unsigned int duplicateVertex(unsigned int); + + void smoothVertexNormals(bool /*fix*/=true, bool /*force*/=false); + + void computeVertexNormals(); + + osg::Vec3f cumulateTriangleNormals(const IndexVector&) const; + + void replaceVertexIndexInTriangles(const IndexVector&, unsigned int, unsigned int); + + void addArray(osg::Array*); + + void updateGeometryPrimitives(); + + +protected: + osg::Geometry& _geometry; + float _creaseAngle; + TriangleMeshGraph* _graph; + TriangleVector _triangles; + ArrayVector _vertexArrays; + int _mode; // smooth or recompute normals +}; diff --git a/src/osgPlugins/gles/TriangleMeshSmoother.cpp b/src/osgPlugins/gles/TriangleMeshSmoother.cpp new file mode 100644 index 000000000..7215544f7 --- /dev/null +++ b/src/osgPlugins/gles/TriangleMeshSmoother.cpp @@ -0,0 +1,257 @@ +#include "TriangleMeshSmoother" + + +TriangleMeshSmoother::TriangleMeshSmoother(osg::Geometry& geometry, float creaseAngle, bool comparePosition, int mode): + _geometry(geometry), + _creaseAngle(creaseAngle), + _graph(0), + _mode(mode) +{ + if(!_geometry.getVertexArray() || !_geometry.getVertexArray()->getNumElements()) { + return; + } + + osgUtil::SharedArrayOptimizer deduplicator; + deduplicator.findDuplicatedUVs(geometry); + + // duplicate shared arrays as it isn't safe to duplicate vertices when arrays are shared. + if (geometry.containsSharedArrays()) { + geometry.duplicateSharedArrays(); + } + + if(!_geometry.getNormalArray() || _geometry.getNormalArray()->getNumElements() != _geometry.getVertexArray()->getNumElements()) { + _geometry.setNormalArray(new osg::Vec3Array(_geometry.getVertexArray()->getNumElements()), osg::Array::BIND_PER_VERTEX); + } + + // build a unifier to consider deduplicated vertex indices + _graph = new TriangleMeshGraph(_geometry, comparePosition); + + unsigned int nbTriangles = 0; + for(unsigned int i = 0 ; i < _geometry.getNumPrimitiveSets() ; ++ i) { + osg::PrimitiveSet* primitive = _geometry.getPrimitiveSet(i); + + if(!primitive || !primitive->getNumIndices()) { + continue; + } + else if(primitive->getMode() > osg::PrimitiveSet::TRIANGLES) { + OSG_INFO << "[smoother] Cannot smooth geometry '" << _geometry.getName() + << "' due to not tessellated primitives" << std::endl; + return; + } + else if(primitive->getMode() == osg::PrimitiveSet::TRIANGLES) { + nbTriangles += primitive->getNumIndices() / 3; + } + } + _triangles.reserve(nbTriangles); + + // collect all buffers that are BIND_PER_VERTEX for eventual vertex duplication + addArray(_geometry.getVertexArray()); + addArray(_geometry.getColorArray()); + addArray(_geometry.getSecondaryColorArray()); + addArray(_geometry.getFogCoordArray()); + for(unsigned int i = 0; i < _geometry.getNumTexCoordArrays(); ++ i) { + addArray(_geometry.getTexCoordArray(i)); + } + for(unsigned int i = 0; i < _geometry.getNumVertexAttribArrays(); ++ i) { + addArray(_geometry.getVertexAttribArray(i)); + } + + switch(_mode) { + case recompute: + computeVertexNormals(); + break; + case smooth_all: + smoothVertexNormals(true, true); + break; + case smooth_flipped: + smoothVertexNormals(true, false); + break; + case diagnose: + smoothVertexNormals(false, false); + break; + }; + + // deduplicate UVs array that were only shared within the geometry + deduplicator.deduplicateUVs(geometry); +} + + +unsigned int TriangleMeshSmoother::duplicateVertex(unsigned int index) { + DuplicateVertex duplicate(index); + for(ArrayVector::iterator array = _vertexArrays.begin(); array != _vertexArrays.end(); ++ array) { + (*array)->accept(duplicate); + } + + _graph->add(duplicate._end, index); + return duplicate._end; +} + + +void TriangleMeshSmoother::smoothVertexNormals(bool fix, bool force) { + _vertexArrays.clear(); // make sure we do not change vertex count + bool flipped = false; + + osg::Vec3Array* normals = dynamic_cast(_geometry.getNormalArray()); + osg::Vec3Array* positions = dynamic_cast(_geometry.getVertexArray()); + + if(!positions || !normals || normals->getNumElements() != positions->getNumElements()) { + OSG_WARN << std::endl + << "Warning: [smoothVertexNormals] [[normals]] Geometry '" << _geometry.getName() + << "' has invalid positions/normals"; + return; + } + + for(unsigned int index = 0 ; index < positions->getNumElements() ; ++ index) { + std::vector oneRing = _graph->vertexOneRing(_graph->unify(index), _creaseAngle); + osg::Vec3f smoothedNormal(0.f, 0.f, 0.f); + + // sum normals for each cluster in the one-ring + for(std::vector::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) { + smoothedNormal += cumulateTriangleNormals(*cluster); + } + + float length = smoothedNormal.normalize(); + if(length > 0.) { + if(force || smoothedNormal * normals->at(index) < 1.e-6) { + flipped = true; + if(fix) { + (*normals)[index] = smoothedNormal; + } + } + } + } + + if(flipped) { + OSG_WARN << std::endl << "Warning: [smoothVertexNormals] [[normals]] Geometry '" << _geometry.getName() << "' "; + switch(_mode) { + case diagnose: + OSG_WARN << "has some flipped normals; please check that the shading is correct" << std::endl; + OSG_WARN << "Monitor: normal.invalid" << std::endl; + break; + case smooth_flipped: + OSG_WARN << "has some flipped normals that have been fixed" << std::endl; + OSG_WARN << "Monitor: normal.smooth_flipped" << std::endl; + break; + case smooth_all: + OSG_WARN << "normals have all been smoothed" << std::endl; + OSG_WARN << "Monitor: normal.smooth_all" << std::endl; + break; + } + } +} + + +void TriangleMeshSmoother::computeVertexNormals() { + osg::Vec3Array* normals = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX, + _geometry.getVertexArray()->getNumElements()); + addArray(normals); + + for(unsigned int i = 0 ; i < normals->getNumElements() ; ++ i) { + (*normals)[i].set(0.f, 0.f, 0.f); + } + + for(VertexIterator uniqueIndex = _graph->begin() ; uniqueIndex != _graph->end() ; ++ uniqueIndex) { + unsigned int index = uniqueIndex->_index; + std::set processed; + + std::vector oneRing = _graph->vertexOneRing(index, _creaseAngle); + for(std::vector::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) { + osg::Vec3f clusterNormal = cumulateTriangleNormals(*cluster); + clusterNormal.normalize(); + + std::set duplicates; + for(IndexVector::const_iterator tri = cluster->begin() ; tri != cluster->end() ; ++ tri) { + const Triangle& triangle = _graph->triangle(*tri); + + if(_graph->unify(triangle.v1()) == index) { + duplicates.insert(triangle.v1()); + } + else if(_graph->unify(triangle.v2()) == index) { + duplicates.insert(triangle.v2()); + } + else if(_graph->unify(triangle.v3()) == index) { + duplicates.insert(triangle.v3()); + } + } + + for(std::set::iterator vertex = duplicates.begin() ; vertex != duplicates.end() ; ++ vertex) { + if(processed.find(*vertex) == processed.end()) { + // vertex not yet processed + (*normals)[*vertex] = clusterNormal; + processed.insert(*vertex); + } + else { + // vertex already processed in a previous cluster: need to duplicate + unsigned int duplicate = duplicateVertex(*vertex); + replaceVertexIndexInTriangles(*cluster, *vertex, duplicate); + (*normals)[duplicate] = clusterNormal; + + processed.insert(duplicate); + } + } + } + } + + _geometry.setNormalArray(normals, osg::Array::BIND_PER_VERTEX); + updateGeometryPrimitives(); + + OSG_WARN << std::endl <<"Warning: [computeVertexNormals] [[normals]] Geometry '" << _geometry.getName() + << "' normals have been recomputed" << std::endl; + OSG_WARN << "Monitor: normal.recompute" << std::endl; +} + + +osg::Vec3f TriangleMeshSmoother::cumulateTriangleNormals(const IndexVector& triangles) const { + osg::Vec3f normal; + normal.set(0.f, 0.f, 0.f); + for(IndexVector::const_iterator triangle = triangles.begin() ; triangle != triangles.end() ; ++ triangle) { + const Triangle& t = _graph->triangle(*triangle); + normal += (t._normal * t._area); + } + return normal; +} + + +void TriangleMeshSmoother::replaceVertexIndexInTriangles(const IndexVector& triangles, unsigned int oldIndex, unsigned int newIndex) { + for(IndexVector::const_iterator tri = triangles.begin() ; tri != triangles.end() ; ++ tri) { + Triangle& triangle = _graph->triangle(*tri); + if(triangle.v1() == oldIndex) { + triangle.v1() = newIndex; + } + else if(triangle.v2() == oldIndex) { + triangle.v2() = newIndex; + } + else if(triangle.v3() == oldIndex) { + triangle.v3() = newIndex; + } + } +} + + +void TriangleMeshSmoother::addArray(osg::Array* array) { + if (array && array->getBinding() == osg::Array::BIND_PER_VERTEX) { + _vertexArrays.push_back(array); + } +} + + +void TriangleMeshSmoother::updateGeometryPrimitives() { + osg::Geometry::PrimitiveSetList primitives; + for(unsigned int i = 0 ; i < _geometry.getNumPrimitiveSets() ; ++ i) { + osg::PrimitiveSet* primitive = _geometry.getPrimitiveSet(i); + if(primitive && primitive->getMode() < osg::PrimitiveSet::TRIANGLES) { + primitives.push_back(primitive); + } + } + + osg::DrawElementsUInt* triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES); + for(unsigned int i = 0 ; i < _graph->getNumTriangles() ; ++ i) { + const Triangle& triangle = _graph->triangle(i); + triangles->push_back(triangle.v1()); + triangles->push_back(triangle.v2()); + triangles->push_back(triangle.v3()); + } + primitives.push_back(triangles); + + _geometry.setPrimitiveSetList(primitives); +} diff --git a/src/osgPlugins/gles/glesUtil b/src/osgPlugins/gles/glesUtil index f9db652bd..bf6faffef 100644 --- a/src/osgPlugins/gles/glesUtil +++ b/src/osgPlugins/gles/glesUtil @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,33 @@ namespace glesUtil { using namespace osg; typedef std::vector IndexList; + + inline bool hasPositiveWeights(const osg::Geometry* geometry) { + const osg::Vec4Array* weights = 0; + + for(unsigned int i = 0 ; i < geometry->getNumVertexAttribArrays() ; ++ i) { + const osg::Array* attribute = geometry->getVertexAttribArray(i); + bool isWeights = false; + if(attribute && attribute->getUserValue("weights", isWeights) && isWeights) { + weights = dynamic_cast(attribute); + break; + } + } + + if(weights) { + for(osg::Vec4Array::const_iterator weight = weights->begin() ; weight != weights->end() ; ++ weight) { + const osg::Vec4& value = *weight; + // weights are sorted in decreasing order + if(value[0] != 0.f) { + return true; + } + } + } + + return false; + } + + // 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.