From 9fbdaaae65e1a9d9aad5a74dded56f89f332c440 Mon Sep 17 00:00:00 2001 From: Marc Helbling Date: Fri, 1 Jul 2016 17:28:01 +0200 Subject: [PATCH] Updates gles plugin --- src/osgPlugins/gles/AABBonBoneVisitor | 261 +++++++ src/osgPlugins/gles/AnimationCleanerVisitor | 663 ++++++++++++++++++ src/osgPlugins/gles/AnimationVisitor | 31 - src/osgPlugins/gles/BindPerVertexVisitor | 75 +- src/osgPlugins/gles/CMakeLists.txt | 40 +- src/osgPlugins/gles/DetachPrimitiveVisitor | 104 ++- src/osgPlugins/gles/DisableAnimationVisitor | 47 ++ src/osgPlugins/gles/DrawArrayVisitor | 3 +- src/osgPlugins/gles/GeometryArray | 308 +++++++- src/osgPlugins/gles/GeometryInspector | 65 ++ src/osgPlugins/gles/GeometrySplitterVisitor | 480 +++++++------ src/osgPlugins/gles/GeometryUniqueVisitor | 47 +- src/osgPlugins/gles/IndexMeshVisitor | 2 +- src/osgPlugins/gles/IndexMeshVisitor.cpp | 50 +- src/osgPlugins/gles/LimitMorphTargetCount | 31 + src/osgPlugins/gles/Line | 29 + src/osgPlugins/gles/LineIndexFunctor | 26 +- .../gles/MostInfluencedGeometryByBone | 229 ++++++ src/osgPlugins/gles/OpenGLESGeometryOptimizer | 110 ++- .../gles/OpenGLESGeometryOptimizer.cpp | 80 ++- src/osgPlugins/gles/PreTransformVisitor | 4 +- src/osgPlugins/gles/PrimitiveIndexors | 2 +- src/osgPlugins/gles/ReaderWriterGLES.cpp | 58 +- src/osgPlugins/gles/RigAnimationVisitor | 202 ++++++ src/osgPlugins/gles/RigAttributesVisitor | 53 ++ src/osgPlugins/gles/SmoothNormalVisitor | 395 +++++++++++ src/osgPlugins/gles/SubGeometry | 275 ++++++++ src/osgPlugins/gles/TangentSpaceVisitor | 41 +- .../gles/TriangleLinePointIndexFunctor | 257 ------- src/osgPlugins/gles/TriangleMeshGraph | 340 +++++++++ src/osgPlugins/gles/TriangleStripVisitor | 2 +- src/osgPlugins/gles/TriangleStripVisitor.cpp | 3 +- src/osgPlugins/gles/UnIndexMeshVisitor | 2 +- src/osgPlugins/gles/UnIndexMeshVisitor.cpp | 3 +- src/osgPlugins/gles/WireframeVisitor | 43 +- src/osgPlugins/gles/debug | 46 ++ .../gles/forsythtriangleorderoptimizer.cpp | 350 --------- .../gles/forsythtriangleorderoptimizer.h | 46 -- src/osgPlugins/gles/glesUtil | 295 ++------ 39 files changed, 3750 insertions(+), 1348 deletions(-) create mode 100644 src/osgPlugins/gles/AABBonBoneVisitor create mode 100644 src/osgPlugins/gles/AnimationCleanerVisitor delete mode 100644 src/osgPlugins/gles/AnimationVisitor create mode 100644 src/osgPlugins/gles/DisableAnimationVisitor create mode 100644 src/osgPlugins/gles/GeometryInspector create mode 100644 src/osgPlugins/gles/LimitMorphTargetCount create mode 100644 src/osgPlugins/gles/Line create mode 100644 src/osgPlugins/gles/MostInfluencedGeometryByBone create mode 100644 src/osgPlugins/gles/RigAnimationVisitor create mode 100644 src/osgPlugins/gles/RigAttributesVisitor create mode 100644 src/osgPlugins/gles/SmoothNormalVisitor create mode 100644 src/osgPlugins/gles/SubGeometry delete mode 100644 src/osgPlugins/gles/TriangleLinePointIndexFunctor create mode 100644 src/osgPlugins/gles/TriangleMeshGraph create mode 100644 src/osgPlugins/gles/debug delete mode 100644 src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp delete mode 100644 src/osgPlugins/gles/forsythtriangleorderoptimizer.h diff --git a/src/osgPlugins/gles/AABBonBoneVisitor b/src/osgPlugins/gles/AABBonBoneVisitor new file mode 100644 index 000000000..dcb9f01aa --- /dev/null +++ b/src/osgPlugins/gles/AABBonBoneVisitor @@ -0,0 +1,261 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson + * + * This application is open source and may be redistributed and/or modified + * freely and without restriction, both in commercial and non commercial + * applications, as long as this copyright notice is maintained. + * + * This application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef AABBONEVISITOR_H +#define AABBONEVISITOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// Here the idea is to compute AABB by bone for a skelton +// You just need to call this visitor on a skeleton and after call computeBoundingBoxOnBones +// If you have more than one skeleton you should crete a visitor by skeleton +class ComputeAABBOnBoneVisitor : public osg::NodeVisitor { +public: + typedef std::vector BoneList; + typedef std::vector RigGeometryList; + + + 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::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; + } + + 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 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 computeBoundingBoxOnBones() { + //Perform Updates + + //Update Bones + osgUtil::UpdateVisitor up; + _root->accept(up); + + //Update rigGeometries + for (unsigned int i = 0, size = _rigGeometries.size(); i < size; i++) { + osgAnimation::RigGeometry * rig = _rigGeometries.at(i); + osg::Drawable::UpdateCallback * up = dynamic_cast(rig->getUpdateCallback()); + if(up) up->update(0, rig); + } + + //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 rigGeometry = _rigGeometries.begin(); rigGeometry != _rigGeometries.end(); ++ rigGeometry) { + 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; + + osgAnimation::VertexInfluence vxtInf = (*itMap).second; + osg::Vec3Array *vertices = dynamic_cast((*rigGeometry)->getVertexArray()); + + //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 rigGeometry = _rigGeometries.begin(); rigGeometry != _rigGeometries.end(); ++ rigGeometry) { + (*rigGeometry)->copyFrom(*(*rigGeometry)->getSourceGeometry()); + (*rigGeometry)->setRigTransformImplementation(0); + } + } + + std::vector _bones; + std::vector _rigGeometries; + osgAnimation::Skeleton *_root; + bool _createGeometry; +}; + + +struct FindSkeletons : public osg::NodeVisitor +{ + FindSkeletons(): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + {} + + void apply(osg::Transform& node) { + osgAnimation::Skeleton *skl = dynamic_cast(&node); + if(skl) _skls.push_back(skl); + traverse(node); + } + + std::vector _skls; +}; + +#endif // AABBONEVISITOR_H diff --git a/src/osgPlugins/gles/AnimationCleanerVisitor b/src/osgPlugins/gles/AnimationCleanerVisitor new file mode 100644 index 000000000..38e217621 --- /dev/null +++ b/src/osgPlugins/gles/AnimationCleanerVisitor @@ -0,0 +1,663 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */ + +#ifndef ANIMATION_CLEANER_VISITOR +#define ANIMATION_CLEANER_VISITOR + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} + + +class HasGeometryVisitor : public osg::NodeVisitor { +public: + HasGeometryVisitor(): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + geometry(false) + {} + + void apply(osg::Geometry& object) { + geometry = true; + } + + bool geometry; +}; + + +class AnimationCleanerVisitor : public osg::NodeVisitor +{ +public: + typedef std::map< osg::ref_ptr, osg::ref_ptr > BasicAnimationManagerMap; + typedef osgAnimation::AnimationUpdateCallback BaseAnimationUpdateCallback; + typedef std::map< osg::ref_ptr, osg::ref_ptr > AnimationUpdateCallBackMap; + typedef std::vector< osg::ref_ptr > MatrixTransformList; + typedef std::vector< osg::ref_ptr > RigGeometryList; + typedef std::map< osg::ref_ptr, osgAnimation::RigGeometry* > MorphGeometryMap; + typedef std::map< std::string, osgAnimation::MorphGeometry* > NameMorphMap; + typedef std::set< std::string > NameSet; + typedef std::vector< std::pair > TargetChannelList; + + + AnimationCleanerVisitor(std::string name="AnimationCleanerVisitor"): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _logger(name + "::apply(..)") + {} + + + void 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 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 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 + + RigGeometryList::iterator rigGeometry = _rigGeometries.begin(); + while(rigGeometry != _rigGeometries.end()) { + if(rigGeometry->valid()) { + if(!hasPositiveWeights((*rigGeometry)->getSourceGeometry())) { + OSG_WARN << "Monitor: animation.invalid_riggeometry" << std::endl; + replaceRigGeometryBySource(*(rigGeometry->get())); + _rigGeometries.erase(rigGeometry); + continue; // skip iterator increment + } + } + ++ rigGeometry; + } + } + + 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 ++); + } + } + } + +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; + } + + template + T* 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 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; + } + + 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); + } + + 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 removeAnimationTransforms() { + for(MatrixTransformList::iterator transform = _transforms.begin() ; transform != _transforms.end() ; ++ transform) { + if(transform->valid()) { + removeFromParents(transform->get()); + } + } + } + + void 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 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); + } + } + +protected: + BasicAnimationManagerMap _managers; + AnimationUpdateCallBackMap _updates; + MatrixTransformList _transforms; + RigGeometryList _rigGeometries; + MorphGeometryMap _morphGeometries; + NameMorphMap _morphTargets; + TargetChannelList _channels; + StatLogger _logger; +}; + +#endif diff --git a/src/osgPlugins/gles/AnimationVisitor b/src/osgPlugins/gles/AnimationVisitor deleted file mode 100644 index 24a053b89..000000000 --- a/src/osgPlugins/gles/AnimationVisitor +++ /dev/null @@ -1,31 +0,0 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson - * - * This application is open source and may be redistributed and/or modified - * freely and without restriction, both in commercial and non commercial - * applications, as long as this copyright notice is maintained. - * - * This application is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - */ - -#ifndef ANIMATION_VISITOR -#define ANIMATION_VISITOR - -#include -#include -#include -#include - - -// the idea is to create true Geometry if skeleton with RigGeometry -class AnimationVisitor : public osgUtil::UpdateVisitor -{ -public: - AnimationVisitor() { - setFrameStamp(new osg::FrameStamp()); - } -}; - -#endif diff --git a/src/osgPlugins/gles/BindPerVertexVisitor b/src/osgPlugins/gles/BindPerVertexVisitor index e60a23a96..04926e260 100644 --- a/src/osgPlugins/gles/BindPerVertexVisitor +++ b/src/osgPlugins/gles/BindPerVertexVisitor @@ -22,7 +22,7 @@ public: BindPerVertexVisitor(): GeometryUniqueVisitor("BindPerVertexVisitor") {} - void apply(osg::Geometry& geometry) { + void process(osg::Geometry& geometry) { if (geometry.getNormalArray() && geometry.getNormalBinding() != osg::Geometry::BIND_PER_VERTEX) { bindPerVertex(geometry.getNormalArray(), geometry.getNormalBinding(), @@ -50,25 +50,90 @@ public: geometry.getPrimitiveSetList()); geometry.setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX); } - - setProcessed(&geometry); }; protected: void bindPerVertex(osg::Array* src, osg::Geometry::AttributeBinding fromBinding, osg::Geometry::PrimitiveSetList& primitives) { - if (doConvert(src, fromBinding, primitives)) + 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 diff --git a/src/osgPlugins/gles/CMakeLists.txt b/src/osgPlugins/gles/CMakeLists.txt index 51aefc4b2..2c11cc3a6 100644 --- a/src/osgPlugins/gles/CMakeLists.txt +++ b/src/osgPlugins/gles/CMakeLists.txt @@ -3,10 +3,44 @@ SET(TARGET_SRC OpenGLESGeometryOptimizer.cpp TriangleStripVisitor.cpp IndexMeshVisitor.cpp - UnIndexMeshVisitor.cpp - forsythtriangleorderoptimizer.cpp) + UnIndexMeshVisitor.cpp) + +SET(TARGET_H + AABBonBoneVisitor + AnimationCleanerVisitor + BindPerVertexVisitor + debug + DetachPrimitiveVisitor + DisableAnimationVisitor + DrawArrayVisitor + EdgeIndexFunctor + GeometryArray + GeometryInspector + GeometrySplitterVisitor + GeometryUniqueVisitor + glesUtil + IndexMeshVisitor + LimitMorphTargetCount + Line + LineIndexFunctor + MostInfluencedGeometryByBone + OpenGLESGeometryOptimizer + PointIndexFunctor + PreTransformVisitor + PrimitiveIndexors + RigAnimationVisitor + RigAttributesVisitor + SmoothNormalVisitor + StatLogger + SubGeometry + TangentSpaceVisitor + TriangleMeshGraph + TriangleStripVisitor + UnIndexMeshVisitor + WireframeVisitor +) #### end var setup ### SET(TARGET_ADDED_LIBRARIES - osgUtil) + osgUtil osgAnimation) SETUP_PLUGIN(gles) diff --git a/src/osgPlugins/gles/DetachPrimitiveVisitor b/src/osgPlugins/gles/DetachPrimitiveVisitor index c7649701f..61720d763 100644 --- a/src/osgPlugins/gles/DetachPrimitiveVisitor +++ b/src/osgPlugins/gles/DetachPrimitiveVisitor @@ -23,32 +23,43 @@ public: _userValue(userValue), _keepGeometryAttributes(keepGeometryAttributes), _inlined(inlined) {} - void apply(osg::Geometry& geometry) { - if(shouldDetach(geometry)) { - osg::Geometry* detached = createDetachedGeometry(geometry); + 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); - unsigned int nbParents = geometry.getNumParents(); - for(unsigned int i = 0 ; i < nbParents ; ++ i) { - osg::Node* parent = geometry.getParent(i); - // TODO: Geode will be soon deprecated - if(parent && parent->asGeode()) { - osg::Geode* geode = parent->asGeode(); - geode->addDrawable(detached); - if(!_inlined) { - geode->removeDrawable(&geometry); - } + if(!_inlined) { + geode->removeDrawable(&duplicated); } } + } + } + + void process(osg::Geometry& geometry) { + if(shouldDetach(geometry)) { + osg::Geometry* detached = detachGeometry(geometry); + reparentDuplicatedGeometry(geometry, *detached); setProcessed(detached); } - setProcessed(&geometry); + } + + void process(osgAnimation::RigGeometry& rigGeometry) { + return process(static_cast(rigGeometry)); } protected: - bool shouldDetach(osg::Geometry& geometry) { + bool 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) { - osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i); + const osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i); if(primitive && primitive->getUserValue(_userValue, detach) && detach) { return true; } @@ -56,6 +67,23 @@ protected: return false; } + 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* 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) { @@ -72,6 +100,11 @@ protected: 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) { @@ -82,12 +115,47 @@ protected: source.removePrimitiveSet(i); } } + return detachedPrimitives; + } - detached->setPrimitiveSetList(detachedPrimitives); - detached->setUserValue(_userValue, true); + 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; + } std::string _userValue; bool _keepGeometryAttributes; diff --git a/src/osgPlugins/gles/DisableAnimationVisitor b/src/osgPlugins/gles/DisableAnimationVisitor new file mode 100644 index 000000000..8130e2275 --- /dev/null +++ b/src/osgPlugins/gles/DisableAnimationVisitor @@ -0,0 +1,47 @@ +/* -*-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 DISABLE_ANIMATION_VISITOR +#define DISABLE_ANIMATION_VISITOR + +#include "AnimationCleanerVisitor" + + +// Derive from AnimationCleanerVisitor to only allow animation data removal +class DisableAnimationVisitor : public AnimationCleanerVisitor +{ +public: + DisableAnimationVisitor(): + AnimationCleanerVisitor("DisableAnimationVisitor"), + _removeDone(false) + {} + + ~DisableAnimationVisitor() { + // make sure removal has been done + remove(); + } + + void clean() + {} + + void remove() { + if(!_removeDone) { + AnimationCleanerVisitor::removeAnimation(); + } + _removeDone = true; + } + +protected: + bool _removeDone; +}; + +#endif diff --git a/src/osgPlugins/gles/DrawArrayVisitor b/src/osgPlugins/gles/DrawArrayVisitor index 435e08d4e..344cfb424 100644 --- a/src/osgPlugins/gles/DrawArrayVisitor +++ b/src/osgPlugins/gles/DrawArrayVisitor @@ -22,7 +22,7 @@ public: DrawArrayVisitor(): GeometryUniqueVisitor("DrawArrayVisitor") {} - void apply(osg::Geometry& geometry) { + void process(osg::Geometry& geometry) { GeometryArrayList srcArrays(geometry); // clone but clear content @@ -87,7 +87,6 @@ public: dst.setToGeometry(geometry); geometry.setPrimitiveSetList(newGeometry->getPrimitiveSetList()); - setProcessed(&geometry); } }; diff --git a/src/osgPlugins/gles/GeometryArray b/src/osgPlugins/gles/GeometryArray index 549b7417b..eaf97e227 100644 --- a/src/osgPlugins/gles/GeometryArray +++ b/src/osgPlugins/gles/GeometryArray @@ -57,21 +57,36 @@ struct GeometryArrayList { virtual void apply(osg::Vec3Array& array) { copy(array); } virtual void apply(osg::Vec4Array& array) { copy(array); } - virtual void apply(osg::Vec4ubArray& array) { copy(array); } - virtual void apply(osg::Vec2bArray& array) { copy(array); } virtual void apply(osg::Vec3bArray& array) { copy(array); } virtual void apply(osg::Vec4bArray& array) { copy(array); } + virtual void apply(osg::Vec2ubArray& array) { copy(array); } + virtual void apply(osg::Vec3ubArray& array) { copy(array); } + virtual void apply(osg::Vec4ubArray& array) { copy(array); } + + virtual void apply(osg::Vec2iArray& array) { copy(array); } + virtual void apply(osg::Vec3iArray& array) { copy(array); } + virtual void apply(osg::Vec4iArray& array) { copy(array); } + + virtual void apply(osg::Vec2uiArray& array) { copy(array); } + virtual void apply(osg::Vec3uiArray& array) { copy(array); } + virtual void apply(osg::Vec4uiArray& array) { copy(array); } + virtual void apply(osg::Vec2sArray& array) { copy(array); } virtual void apply(osg::Vec3sArray& array) { copy(array); } virtual void apply(osg::Vec4sArray& array) { copy(array); } + virtual void apply(osg::Vec2usArray& array) { copy(array); } + virtual void apply(osg::Vec3usArray& array) { copy(array); } + virtual void apply(osg::Vec4usArray& array) { copy(array); } + virtual void apply(osg::Vec2dArray& array) { copy(array); } virtual void apply(osg::Vec3dArray& array) { copy(array); } virtual void apply(osg::Vec4dArray& array) { copy(array); } virtual void apply(osg::MatrixfArray& array) { copy(array); } + virtual void apply(osg::MatrixdArray& array) { copy(array); } protected: ArrayIndexAppendVisitor& operator = (const ArrayIndexAppendVisitor&) { return *this; } @@ -82,10 +97,9 @@ struct GeometryArrayList { template bool arrayAppendElement(osg::Array* src, unsigned int index, osg::Array* dst) { - T* array= dynamic_cast(src); - T* arrayDst = dynamic_cast(dst); - if (array && arrayDst) - { + T* array = dynamic_cast(src); + if (array) { + T* arrayDst = dynamic_cast(dst); arrayDst->push_back((*array)[index]); return true; } @@ -93,10 +107,30 @@ struct GeometryArrayList { } void operator()(osg::Array* src, unsigned int index, osg::Array* dst) { + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; if (arrayAppendElement(src, index, dst)) return; + if (arrayAppendElement(src, index, dst)) + return; + if (arrayAppendElement(src, index, dst)) return; @@ -106,8 +140,83 @@ struct GeometryArrayList { if (arrayAppendElement(src, index, dst)) return; + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + if (arrayAppendElement(src, index, dst)) return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; + + if (arrayAppendElement(src, index, dst)) + return; } }; @@ -124,10 +233,30 @@ struct GeometryArrayList { } void operator()(osg::Array* array, unsigned int numElements) { + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; if (arraySetNumElements(array, numElements)) return; + if (arraySetNumElements(array, numElements)) + return; + if (arraySetNumElements(array, numElements)) return; @@ -137,8 +266,83 @@ struct GeometryArrayList { if (arraySetNumElements(array, numElements)) return; + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + if (arraySetNumElements(array, numElements)) return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; + + if (arraySetNumElements(array, numElements)) + return; } }; @@ -154,84 +358,110 @@ struct GeometryArrayList { GeometryArrayList(osg::Geometry& geometry) { _vertexes = geometry.getVertexArray(); unsigned int nbvertexes = _vertexes->getNumElements(); - if (geometry.getNormalArray() && nbvertexes == geometry.getNormalArray()->getNumElements()) + + if (geometry.getNormalArray() && nbvertexes == geometry.getNormalArray()->getNumElements()) { _normals = geometry.getNormalArray(); + } - if (geometry.getColorArray() && nbvertexes == geometry.getColorArray()->getNumElements()) + if (geometry.getColorArray() && nbvertexes == geometry.getColorArray()->getNumElements()) { _colors = geometry.getColorArray(); + } - if (geometry.getSecondaryColorArray() && nbvertexes == geometry.getSecondaryColorArray()->getNumElements()) + if (geometry.getSecondaryColorArray() && nbvertexes == geometry.getSecondaryColorArray()->getNumElements()) { _secondaryColors = geometry.getSecondaryColorArray(); + } - if (geometry.getFogCoordArray() && nbvertexes == geometry.getFogCoordArray()->getNumElements()) + if (geometry.getFogCoordArray() && nbvertexes == geometry.getFogCoordArray()->getNumElements()) { _fogCoords = geometry.getFogCoordArray(); + } _texCoordArrays.resize(geometry.getNumTexCoordArrays()); - for(unsigned int i=0;igetNumElements()) + for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) { + if (geometry.getTexCoordArray(i) && nbvertexes == geometry.getTexCoordArray(i)->getNumElements()) { _texCoordArrays[i] = geometry.getTexCoordArray(i); + } + } _attributesArrays.resize(geometry.getNumVertexAttribArrays()); - for(unsigned int i=0;igetNumElements()) + for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) { + if (geometry.getVertexAttribArrayList()[i] && nbvertexes == geometry.getVertexAttribArrayList()[i]->getNumElements()) { _attributesArrays[i] = geometry.getVertexAttribArrayList()[i]; + } + } } void setNumElements(unsigned int nbElements) { - if (_vertexes.valid()) + if (_vertexes.valid()) { ArraySetNumElements()(_vertexes.get(), nbElements); + } - if (_normals.valid()) + if (_normals.valid()) { ArraySetNumElements()(_normals.get(), nbElements); + } - if (_colors.valid()) + if (_colors.valid()) { ArraySetNumElements()(_colors.get(), nbElements); + } - if (_secondaryColors.valid()) + if (_secondaryColors.valid()) { ArraySetNumElements()(_secondaryColors.get(), nbElements); + } - if (_fogCoords.valid()) + if (_fogCoords.valid()) { ArraySetNumElements()(_fogCoords.get(), nbElements); + } - for (unsigned int i = 0; i < _texCoordArrays.size(); i++) - if (_texCoordArrays[i].valid()) + for (unsigned int i = 0; i < _texCoordArrays.size(); i++) { + if (_texCoordArrays[i].valid()) { ArraySetNumElements()(_texCoordArrays[i].get(), nbElements); + } + } - for (unsigned int i = 0; i < _attributesArrays.size(); i++) - if (_attributesArrays[i].valid()) + for (unsigned int i = 0; i < _attributesArrays.size(); i++) { + if (_attributesArrays[i].valid()) { ArraySetNumElements()(_attributesArrays[i].get(), nbElements); + } + } } unsigned int append(unsigned int index, GeometryArrayList& dst) { - if (_vertexes.valid()) + if (_vertexes.valid()) { ArrayAppendElement()(_vertexes.get(), index, dst._vertexes.get()); + } - if (_normals.valid()) + if (_normals.valid()) { ArrayAppendElement()(_normals.get(), index, dst._normals.get()); + } - if (_colors.valid()) + if (_colors.valid()) { ArrayAppendElement()(_colors.get(), index, dst._colors.get()); + } - if (_secondaryColors.valid()) + if (_secondaryColors.valid()) { ArrayAppendElement()(_secondaryColors.get(), index, dst._secondaryColors.get()); + } - if (_fogCoords.valid()) + if (_fogCoords.valid()) { ArrayAppendElement()(_fogCoords.get(), index, dst._fogCoords.get()); + } - for (unsigned int i = 0; i < _texCoordArrays.size(); i++) - if (_texCoordArrays[i].valid()) + for (unsigned int i = 0; i < _texCoordArrays.size(); i++) { + if (_texCoordArrays[i].valid()) { ArrayAppendElement()(_texCoordArrays[i].get(), index, dst._texCoordArrays[i].get()); + } + } - for (unsigned int i = 0; i < _attributesArrays.size(); i++) - if (_attributesArrays[i].valid()) + for (unsigned int i = 0; i < _attributesArrays.size(); i++) { + if (_attributesArrays[i].valid()) { ArrayAppendElement()(_attributesArrays[i].get(), index, dst._attributesArrays[i].get()); + } + } return dst._vertexes->getNumElements()-1; } unsigned int append(const IndexList& indexes, GeometryArrayList& dst) { - if (_vertexes.valid()) { ArrayIndexAppendVisitor append(indexes, dst._vertexes.get()); _vertexes->accept(append); @@ -257,17 +487,19 @@ struct GeometryArrayList { _fogCoords->accept(append); } - for (unsigned int i = 0; i < _texCoordArrays.size(); i++) + for (unsigned int i = 0; i < _texCoordArrays.size(); i++) { if (_texCoordArrays[i].valid()) { ArrayIndexAppendVisitor append(indexes, dst._texCoordArrays[i].get()); _texCoordArrays[i]->accept(append); } + } - for (unsigned int i = 0; i < _attributesArrays.size(); i++) + for (unsigned int i = 0; i < _attributesArrays.size(); ++ i) { if (_attributesArrays[i].valid()) { ArrayIndexAppendVisitor append(indexes, dst._attributesArrays[i].get()); _attributesArrays[i]->accept(append); } + } return dst._vertexes->getNumElements()-1; } @@ -291,14 +523,16 @@ struct GeometryArrayList { array._texCoordArrays.resize(_texCoordArrays.size()); for (unsigned int i = 0; i < _texCoordArrays.size(); i++) { - if (_texCoordArrays[i].valid()) + if (_texCoordArrays[i].valid()) { array._texCoordArrays[i] = dynamic_cast(_texCoordArrays[i]->cloneType()); + } } array._attributesArrays.resize(_attributesArrays.size()); for (unsigned int i = 0; i < _attributesArrays.size(); i++) { - if (_attributesArrays[i].valid()) + if (_attributesArrays[i].valid()) { array._attributesArrays[i] = dynamic_cast(_attributesArrays[i]->cloneType()); + } } return array; diff --git a/src/osgPlugins/gles/GeometryInspector b/src/osgPlugins/gles/GeometryInspector new file mode 100644 index 000000000..2e8f822c4 --- /dev/null +++ b/src/osgPlugins/gles/GeometryInspector @@ -0,0 +1,65 @@ +#ifndef GEOMETRY_INSPECTOR +#define GEOMETRY_INSPECTOR + +#include +#include + +#include "GeometryUniqueVisitor" + +#include +#include +#include +#include +#include +#include +#include + + +class GeometryInspector : public GeometryUniqueVisitor { +public: + void process(osg::Geometry& geometry) {} + void process(osgAnimation::RigGeometry& rigGeometry) { + osgAnimation::MorphGeometry* morph = dynamic_cast(rigGeometry.getSourceGeometry()); + if(morph) { + process(*morph); + } + else { + process(*rigGeometry.getSourceGeometry()); + } + } + void process(osgAnimation::MorphGeometry& morphGeometry) { + osgAnimation::MorphGeometry::MorphTargetList targets = morphGeometry.getMorphTargetList(); + unsigned int count = 0; + for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target, ++ count) { + osg::ref_ptr geometry = new osg::Geometry(*target->getGeometry()); + geometry->setPrimitiveSetList(morphGeometry.getPrimitiveSetList()); + std::ostringstream oss; + if(geometry->getName().empty()) { + oss << "/tmp/noname_" << _processed.size(); + } + else { + oss << "/tmp/" << geometry->getName(); + } + oss << "_" << count << ".osgt"; + dump(*geometry, oss.str()); + } + } + +protected: + void dump(osg::Geometry& geometry, const std::string& path) { + osg::ref_ptr geode = new osg::Geode; + geode->addDrawable(&geometry); + + osg::ref_ptr registry = osgDB::Registry::instance(); + std::string ext = osgDB::getLowerCaseFileExtension(path); + osgDB::ReaderWriter* writer = registry->getReaderWriterForExtension(ext); + + OSG_WARN << "extension: " << ext << std::flush; + OSG_WARN << "writer: " << writer << std::flush; + if(writer) { + writer->writeNode(*geode, path.c_str()); + } + } +}; + +#endif diff --git a/src/osgPlugins/gles/GeometrySplitterVisitor b/src/osgPlugins/gles/GeometrySplitterVisitor index 321252435..e238ec836 100644 --- a/src/osgPlugins/gles/GeometrySplitterVisitor +++ b/src/osgPlugins/gles/GeometrySplitterVisitor @@ -14,6 +14,7 @@ #define GEOMETRY_SPLITTER_VISITOR #include +#include #include #include @@ -22,121 +23,257 @@ #include #include +#include + +#include "glesUtil" #include "GeometryArray" #include "GeometryUniqueVisitor" -#include "glesUtil" +#include "AnimationCleanerVisitor" +#include "TriangleMeshGraph" +#include "SubGeometry" +#include "Line" + class GeometryIndexSplitter { -public: - typedef std::vector > GeometryList; +protected: + class IndexCache : public IndexDeque { + public: + IndexCache(unsigned int size=64) : _size(size) + {} - GeometryIndexSplitter(unsigned int maxIndex, bool disablePostTransform): - _maxIndexToSplit(maxIndex), _disablePostTransform(disablePostTransform) + 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(!hasValidPrimitives(geometry) || !needToSplit(geometry)) { - if(!_disablePostTransform) { - // optimize cache for rendering - glesUtil::VertexCacheVisitor posttransform; - posttransform.optimizeVertices(geometry); - } - _geometryList.push_back(&geometry); // remap geometry to itself + if(!needToSplit(geometry)) { + _geometryList.push_back(&geometry); return false; } // keep bounding box data as user value if needed in subsequent processing attachBufferBoundingBox(geometry); - // first optimize primitives indexing - { - if(!_disablePostTransform) { - // post-transform for better locality - glesUtil::VertexCacheVisitor posttransform; - posttransform.optimizeVertices(geometry); + 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; + } } - // pre-transform to reindex correctly - glesUtil::VertexAccessOrderVisitor pretransform; - pretransform.optimizeOrder(geometry); } - // Clone geometry as we will modify vertex arrays and primitives - // To avoid issues with shared buffers, arrays & primitives should be - // deep cloned. - // As UserValues might be changed on a per-geometry basis afterwards, we - // also deep clone userdata - // We do *not* want to clone statesets as they reference a UniqueID that - // should be unique (see #83056464). - osg::ref_ptr processing = osg::clone(&geometry, osg::CopyOp::DEEP_COPY_ARRAYS | - osg::CopyOp::DEEP_COPY_PRIMITIVES | - osg::CopyOp::DEEP_COPY_USERDATA); - osg::ref_ptr reported; - while (true) { - reported = doSplit(*processing); + // 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; - // reduce vertex array if needed - if(processing && processing->getNumPrimitiveSets()) { - GeometryArrayList arrayList(*processing); - arrayList.setNumElements(osg::minimum(arrayList.size(), _maxIndexToSplit + 1)); - _geometryList.push_back(processing); + if(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; + 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]); + } + } } - if (!reported.valid()) { - break; + // 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); } - else { - processing = reported; - reported = 0; - // re order index elements - glesUtil::VertexAccessOrderVisitor preTransform; - preTransform.optimizeOrder(*processing); + 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 > " << _maxIndexToSplit << ", splitted to " + << ") has DrawElements index > " << _maxAllowedIndex << ", splitted to " << _geometryList.size() << " geometry" << std::endl; return true; } -protected: - bool hasValidPrimitives(osg::Geometry& geometry) const { - for (unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) { - osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i); - if (primitive) { - if (!primitive->getDrawElements()) { - osg::notify(osg::INFO) << "can't split Geometry " << geometry.getName() - << " (" << &geometry << ") contains non indexed primitives" - << std::endl; - return false; - } + 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(); + } - switch (primitive->getMode()) { - case osg::PrimitiveSet::TRIANGLES: - case osg::PrimitiveSet::LINES: - case osg::PrimitiveSet::POINTS: - break; - default: - osg::notify(osg::FATAL) << "can't split Geometry " << geometry.getName() - << " (" << &geometry << ") contains non point/line/triangle primitives" - << std::endl; - return false; - break; + 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)); } } } - return true; } +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 (needToSplit(*primitive)) { + if (primitive && needToSplit(*primitive)) { return true; } } @@ -145,7 +282,7 @@ protected: bool needToSplit(const osg::DrawElements& primitive) const { for(unsigned int j = 0; j < primitive.getNumIndices(); j++) { - if (primitive.index(j) > _maxIndexToSplit){ + if (primitive.index(j) > _maxAllowedIndex){ return true; } } @@ -186,149 +323,14 @@ protected: } } - osg::Geometry* doSplit(osg::Geometry& geometry) const { - osg::Geometry::PrimitiveSetList geomPrimitives; - osg::Geometry::PrimitiveSetList wirePrimitives; - osg::Geometry::PrimitiveSetList reportedPrimitives; - std::vector< osg::ref_ptr > primitivesToFix; - - osg::Geometry::PrimitiveSetList& primitives = geometry.getPrimitiveSetList(); - std::set validIndices; - osg::ref_ptr reportGeometry; - - for (unsigned int i = 0; i < primitives.size(); i++) { - osg::DrawElements *primitive = primitives[i]->getDrawElements(); - - bool isWireframe = false; - if(primitive->getUserValue("wireframe", isWireframe)) { - wirePrimitives.push_back(primitive); - } - else if (needToSplit(*primitive)) { - primitivesToFix.push_back(primitive); - } - else { - geomPrimitives.push_back(primitive); - setValidIndices(validIndices, primitive); - } - } - - if (!primitivesToFix.empty()) { - // filter all indices > _maxIndexValue in primitives - for (unsigned int i = 0; i < primitivesToFix.size(); i++) { - osg::DrawElementsUInt* source = dynamic_cast(primitivesToFix[i].get()); - if (source) - { - osg::DrawElements* large = removeLargeIndices(source); - reportedPrimitives.push_back(large); - geomPrimitives.push_back(source); - setValidIndices(validIndices, source); - } - } - } - - // keep wireframe data associated to the current solid geometry - extractWireframePrimitive(wirePrimitives, validIndices, geomPrimitives, reportedPrimitives); - - geometry.setPrimitiveSetList(geomPrimitives); - if (!reportedPrimitives.empty()) { - reportGeometry = osg::clone(&geometry, osg::CopyOp::DEEP_COPY_ARRAYS | - osg::CopyOp::DEEP_COPY_USERDATA); - reportGeometry->setPrimitiveSetList(reportedPrimitives); - return reportGeometry.release(); - } - return 0; - } - void setValidIndices(std::set& indices, const osg::DrawElements* primitive) const { for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) { indices.insert(primitive->index(j)); } } - osg::DrawElements* removeLargeIndices(osg::DrawElementsUInt* source) const { - osg::DrawElementsUInt* large = new osg::DrawElementsUInt(source->getMode()); - unsigned int primitive_size = 0; - switch(source->getMode()) { - case osg::PrimitiveSet::POINTS: - primitive_size = 1; - break; - case osg::PrimitiveSet::LINES: - primitive_size = 2; - break; - case osg::PrimitiveSet::TRIANGLES: - primitive_size = 3; - break; - } - - for (int id = source->getNumPrimitives() - 1; id >= 0; -- id) { - const unsigned int arrayIndex = id * primitive_size; - for(unsigned int i = 0 ; i < primitive_size ; ++ i) { - if(source->index(arrayIndex + i) > _maxIndexToSplit) { - // add primitive in the large DrawElements - for(unsigned int j = 0 ; j < primitive_size ; ++ j) { - large->addElement(source->index(arrayIndex + j)); - } - // remove primitive from source DrawElements - for(int j = primitive_size - 1 ; j >= 0 ; -- j) { - source->erase(source->begin() + arrayIndex + j); - } - break; // skip to next primitive - } - } - } - return large; - } - - // keep wireframe data associated to the solid geometry - void extractWireframePrimitive(osg::Geometry::PrimitiveSetList& lines, - const std::set& indices, - osg::Geometry::PrimitiveSetList& primitives, - osg::Geometry::PrimitiveSetList& reported) const { - if(indices.empty()) { - return; - } - - for(unsigned int i = 0 ; i < lines.size() ; ++ i) - { - const osg::DrawElementsUInt* line = dynamic_cast(lines[i].get()); - if(!line || line->getMode() != osg::PrimitiveSet::LINES) - { - osg::notify(osg::INFO) << "Primitive with bad mode flagged as wireframe. Skipping."<< std::endl; - continue; - } - osg::ref_ptr small = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES); - osg::ref_ptr large = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES); - - for (unsigned int id = 0 ; id < line->getNumPrimitives() ; ++ id) { - unsigned int arrayIndex = id * 2; - unsigned int a = line->index(arrayIndex), - b = line->index(arrayIndex + 1); - - if(indices.find(a) != indices.end() && indices.find(b) != indices.end()) { - small->addElement(a); - small->addElement(b); - } - else { - large->addElement(a); - large->addElement(b); - } - } - - if(small->size()) { - small->setUserValue("wireframe", true); - primitives.push_back(small); - } - - if(large->size()) { - large->setUserValue("wireframe", true); - reported.push_back(large); - } - } - } - public: - const unsigned int _maxIndexToSplit; - bool _disablePostTransform; + const unsigned int _maxAllowedIndex; GeometryList _geometryList; }; @@ -337,34 +339,67 @@ public: class GeometrySplitterVisitor : public GeometryUniqueVisitor { public: typedef std::vector< osg::ref_ptr > GeometryList; + typedef std::vector< osg::ref_ptr > DrawableList; + typedef std::map SplitMap; - GeometrySplitterVisitor(unsigned int maxIndexValue = 65535, bool disablePostTransform=false): + GeometrySplitterVisitor(unsigned int maxAllowedIndex=65535, bool exportNonGeometryDrawables=false): GeometryUniqueVisitor("GeometrySplitterVisitor"), - _maxIndexValue(maxIndexValue), - _disablePostTransform(disablePostTransform) + _maxAllowedIndex(maxAllowedIndex), + _exportNonGeometryDrawables(exportNonGeometryDrawables) {} void apply(osg::Geode& geode) { GeometryUniqueVisitor::apply(geode); - GeometryList remapped; + GeometryList remappedGeometries; + DrawableList nonGeometryDrawables; + for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) { osg::Geometry* geometry = geode.getDrawable(i)->asGeometry(); if(geometry) { - std::map::iterator lookup = _split.find(geometry); - if(lookup != _split.end() && !lookup->second.empty()) { - remapped.insert(remapped.end(), lookup->second.begin(), lookup->second.end()); + 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()); - for(unsigned int i = 0 ; i < remapped.size() ; ++ i) { - geode.addDrawable(remapped[i].get()); + // 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 apply(osg::Geometry& geometry) { - GeometryIndexSplitter splitter(_maxIndexValue, _disablePostTransform); + void process(osg::Geometry& geometry) { + GeometryIndexSplitter splitter(_maxAllowedIndex); splitter.split(geometry); setProcessed(&geometry, splitter._geometryList); } @@ -377,10 +412,9 @@ protected: void setProcessed(osg::Geometry* node, const GeometryList& list) { _split.insert(std::pair(node, GeometryList(list))); } - - unsigned int _maxIndexValue; + unsigned int _maxAllowedIndex; std::map _split; - bool _disablePostTransform; + bool _exportNonGeometryDrawables; }; #endif diff --git a/src/osgPlugins/gles/GeometryUniqueVisitor b/src/osgPlugins/gles/GeometryUniqueVisitor index 8edbd15ae..4179ef611 100644 --- a/src/osgPlugins/gles/GeometryUniqueVisitor +++ b/src/osgPlugins/gles/GeometryUniqueVisitor @@ -17,6 +17,9 @@ #include #include +#include +#include + #include #include @@ -37,14 +40,48 @@ public: } virtual void apply(osg::Drawable& drawable){ - osg::Geometry* geometry = drawable.asGeometry(); - if (!geometry || isProcessed(geometry)) { - return; + if (osg::Geometry* geometry = drawable.asGeometry()) { + // call overloaded geometry processing + apply(*geometry); } - apply(*geometry); } - virtual void apply(osg::Geometry& /*geometry*/) {} // to be implemented by actual visitors + virtual void apply(osg::Geometry& geometry) { + // skip already processed geometry + if (isProcessed(&geometry)) { + return; + } + + if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast(&geometry)) { + process(*rigGeometry); + } + else if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast(&geometry)) { + process(*morphGeometry); + } + else { + process(geometry); + } + + // flag geometry as processed + setProcessed(&geometry); + } + + virtual void process(osg::Geometry& node) = 0; + + virtual void process(osgAnimation::MorphGeometry& node) { + // by default process a MorphGeometry like a regular geometry. If a visitor updates + // vertices, it *has* to override this apply otherwise there might be a mismatch between the + // 'master' morph geometry and morph targets. + process(static_cast(node)); + } + + virtual void process(osgAnimation::RigGeometry& rigGeometry) { + // by default, consider RigGeometry is just considered as a "shell" and visitor + // processing should be performed on the rig source + if(rigGeometry.getSourceGeometry()) { + apply(*rigGeometry.getSourceGeometry()); + } + } protected: bool isProcessed(osg::Geometry* node) { diff --git a/src/osgPlugins/gles/IndexMeshVisitor b/src/osgPlugins/gles/IndexMeshVisitor index 8c4326e7a..c41f006b4 100644 --- a/src/osgPlugins/gles/IndexMeshVisitor +++ b/src/osgPlugins/gles/IndexMeshVisitor @@ -21,7 +21,7 @@ public: IndexMeshVisitor(): GeometryUniqueVisitor("IndexMeshVisitor") {} - void apply(osg::Geometry& geom); + void process(osg::Geometry& geom); protected: typedef std::vector IndexList; diff --git a/src/osgPlugins/gles/IndexMeshVisitor.cpp b/src/osgPlugins/gles/IndexMeshVisitor.cpp index 4c9d86198..617a86221 100644 --- a/src/osgPlugins/gles/IndexMeshVisitor.cpp +++ b/src/osgPlugins/gles/IndexMeshVisitor.cpp @@ -3,6 +3,7 @@ #include // numeric_limits #include +#include #include #include #include @@ -14,7 +15,21 @@ using namespace glesUtil; -void IndexMeshVisitor::apply(osg::Geometry& geom) { +void remapGeometryVertices(RemapArray ra, osg::Geometry& geom) +{ + osgAnimation::MorphGeometry *morphGeometry = dynamic_cast(&geom); + if(morphGeometry) { + osgAnimation::MorphGeometry::MorphTargetList targetList = morphGeometry->getMorphTargetList(); + for (osgAnimation::MorphGeometry::MorphTargetList::iterator ti = targetList.begin(); ti != targetList.end(); ++ti) { + osgAnimation::MorphGeometry::MorphTarget *morphTarget = &(*ti); + osg::Geometry *target = morphTarget->getGeometry(); + VertexAttribComparitor arrayComparitor(*target); + arrayComparitor.accept(ra); + } + } +} + +void IndexMeshVisitor::process(osg::Geometry& geom) { // TODO: this is deprecated if (geom.getNormalBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return; if (geom.getColorBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return; @@ -22,7 +37,7 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) { if (geom.getFogCoordBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return; // no point optimizing if we don't have enough vertices. - if (!geom.getVertexArray() || geom.getVertexArray()->getNumElements() < 3) return; + if (!geom.getVertexArray() || geom.getVertexArray()->getNumElements() < 2) return; osgUtil::SharedArrayOptimizer deduplicator; @@ -105,6 +120,9 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) { RemapArray ra(copyMapping); arrayComparitor.accept(ra); + //Remap morphGeometry target + remapGeometryVertices(ra, geom); + // triangulate faces { TriangleIndexor ti; @@ -112,7 +130,10 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) { ti._remapping = finalMapping; for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) { - (*itr)->accept(ti); + osg::PrimitiveSet* primitive = (*itr).get(); + if(primitive && primitive->getMode() >= osg::PrimitiveSet::TRIANGLES) { + primitive->accept(ti); + } } addDrawElements(ti._indices, osg::PrimitiveSet::TRIANGLES, new_primitives); @@ -127,12 +148,15 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) { wi._remapping = finalMapping; for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) { - bool isWireframe = false; - if((*itr)->getUserValue("wireframe", isWireframe) && isWireframe) { - (*itr)->accept(wi); - } - else { - (*itr)->accept(li); + osg::PrimitiveSet* primitive = (*itr).get(); + if(primitive && primitive->getMode() >= osg::PrimitiveSet::LINES && primitive->getMode() <= osg::PrimitiveSet::LINE_LOOP) { + bool isWireframe = false; + if(primitive->getUserValue("wireframe", isWireframe) && isWireframe) { + primitive->accept(wi); + } + else { + primitive->accept(li); + } } } addDrawElements(li._indices, osg::PrimitiveSet::LINES, new_primitives); @@ -143,9 +167,10 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) { { IndexList points; for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) { - if((*itr) && (*itr)->getMode() == osg::PrimitiveSet::POINTS) { - for(unsigned int k = 0 ; k < (*itr)->getNumIndices() ; ++ k) { - points.push_back(finalMapping[(*itr)->index(k)]); + osg::PrimitiveSet* primitive = (*itr).get(); + if(primitive && primitive->getMode() == osg::PrimitiveSet::POINTS) { + for(unsigned int k = 0 ; k < primitive->getNumIndices() ; ++ k) { + points.push_back(finalMapping[primitive->index(k)]); } } } @@ -154,7 +179,6 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) { geom.setPrimitiveSetList(new_primitives); deduplicator.deduplicateUVs(geom); - setProcessed(&geom); } diff --git a/src/osgPlugins/gles/LimitMorphTargetCount b/src/osgPlugins/gles/LimitMorphTargetCount new file mode 100644 index 000000000..6e0a10da4 --- /dev/null +++ b/src/osgPlugins/gles/LimitMorphTargetCount @@ -0,0 +1,31 @@ +#ifndef LIMIT_MORPH_TARGET_COUNT_VISITOR +#define LIMIT_MORPH_TARGET_COUNT_VISITOR + +/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */ + +#include "GeometryUniqueVisitor" + + +class LimitMorphTargetCount : public GeometryUniqueVisitor +{ +public: + LimitMorphTargetCount(unsigned int maxMorphTarget=0) : _maxMorphTarget(maxMorphTarget) {} + + void process(osgAnimation::MorphGeometry& node) + { + if(_maxMorphTarget) { + osgAnimation::MorphGeometry::MorphTargetList& morphTargetList = node.getMorphTargetList(); + while(morphTargetList.size() > _maxMorphTarget) { + morphTargetList.pop_back(); + } + } + } + + void process(osg::Geometry& ) {} + +protected: + unsigned int _maxMorphTarget; +}; + + +#endif // LIMIT_MORPH_TARGET_COUNT_VISITOR diff --git a/src/osgPlugins/gles/Line b/src/osgPlugins/gles/Line new file mode 100644 index 000000000..b27fd4b3a --- /dev/null +++ b/src/osgPlugins/gles/Line @@ -0,0 +1,29 @@ +#ifndef GLES_LINE +#define GLES_LINE + + +class Line { +public: + Line(unsigned int a, unsigned int b) { + _a = std::min(a, b); + _b = std::max(a, b); + } + + bool operator<(const Line& e) { + return _a < e._a || (_a == e._a && _b < e._b); + } + + unsigned int _a, _b; +}; + + +struct LineCompare { + bool operator()(const Line& lhs, const Line& rhs) const { + return lhs._a < rhs._a || (lhs._a == rhs._a && lhs._b < rhs._b); + } +}; + + +typedef std::set LineSet; + +#endif diff --git a/src/osgPlugins/gles/LineIndexFunctor b/src/osgPlugins/gles/LineIndexFunctor index 8f47cb499..044161e64 100644 --- a/src/osgPlugins/gles/LineIndexFunctor +++ b/src/osgPlugins/gles/LineIndexFunctor @@ -16,29 +16,7 @@ #include #include -#include -#include - - -class Line { -public: - Line(unsigned int a, unsigned int b) { - _a = std::min(a, b); - _b = std::max(a, b); - } - - bool operator<(const Line& e) { - return _a < e._a || (_a == e._a && _b < e._b); - } - - unsigned int _a, _b; -}; - -struct LineCompare { - bool operator()(const Line& lhs, const Line& rhs) const { - return lhs._a < rhs._a || (lhs._a == rhs._a && lhs._b < rhs._b); - } -}; +#include "Line" template @@ -172,7 +150,7 @@ public: GLenum _modeCache; std::vector _indexCache; - std::set _lineCache; + LineSet _lineCache; }; #endif diff --git a/src/osgPlugins/gles/MostInfluencedGeometryByBone b/src/osgPlugins/gles/MostInfluencedGeometryByBone new file mode 100644 index 000000000..024d87ddb --- /dev/null +++ b/src/osgPlugins/gles/MostInfluencedGeometryByBone @@ -0,0 +1,229 @@ +/* -*-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 MOST_INFLUENCED_GEOMETRY_BY_BONE_H +#define MOST_INFLUENCED_GEOMETRY_BY_BONE_H + +#include + +#include +#include +#include + +#include +#include + +#include "StatLogger" + + +class InfluenceAttribute; + +//{ +// "Bone001": { +// Geom1: { +// numVertexInfluenced: (int), +// gloabalWeight: (float) +// }, +// Geom2: { +// numVertexInfluenced: (int), +// gloabalWeight: (float) +// }, +// ... +// }, +// "Bone002": { +// Geom1: { +// numVertexInfluenced: (int), +// gloabalWeight: (float) +// }, +// Geom4: { +// numVertexInfluenced: (int), +// gloabalWeight: (float) +// }, +// ... +// }, +// ... +//} +// +//Here we store influences by bone, we will sort it and take the biggest one +typedef std::map< osgAnimation::Bone*, std::map< osgAnimation::RigGeometry*, InfluenceAttribute > > RigGeometryInfluenceByBoneMap; + +typedef std::map< osgAnimation::RigGeometry*, InfluenceAttribute > BoneInfluenceMap; +typedef std::pair< osgAnimation::RigGeometry*, InfluenceAttribute > BoneInfluence; +typedef std::vector< BoneInfluence > BoneInfluences; + + +typedef std::set< osgAnimation::RigGeometry* > RigGeometrySet; +typedef std::set< osgAnimation::Bone* > BoneSet; + +//Here we simply collect all bones and all rigGeometries +class CollectBonesAndRigGeometriesVisitor: public osg::NodeVisitor { + +public: + CollectBonesAndRigGeometriesVisitor(): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + {} + + void apply(osg::Geometry &geometry) { + osgAnimation::RigGeometry *rigGeometry = dynamic_cast(&geometry); + if(rigGeometry) { + _rigGeometrySet.insert(rigGeometry); + } + traverse(geometry); + } + + void apply(osg::MatrixTransform &node) { + osgAnimation::Bone *bone = dynamic_cast(&node); + if(bone) { + _boneSet.insert(bone); + } + + traverse(node); + } + + RigGeometrySet& getRigGeometrySet() { + return _rigGeometrySet; + } + + BoneSet& getBoneSet() { + return _boneSet; + } + +protected: + RigGeometrySet _rigGeometrySet; + BoneSet _boneSet; +}; + + +//Store and compute influence attributes i.e number of influenced vertex and accumulate weight +class InfluenceAttribute { +public: + InfluenceAttribute(): + _accumulatedWeight(0), + _weightCount(0) + {} + + void addWeight(float weight) { + _accumulatedWeight += weight; + _weightCount++; + } + + unsigned int getNumInfluencedVertex() { + return _weightCount; + } + + unsigned int getNumInfluencedVertex() const { + return _weightCount; + } + + float getNormalizedWeight() const { + if(_weightCount == 0) return 0; + return _accumulatedWeight / _weightCount; + } + +protected: + float _accumulatedWeight; + unsigned int _weightCount; +}; + +typedef std::pair< std::string, osgAnimation::Bone* > StringBonePair; +typedef std::pair< osgAnimation::RigGeometry*, unsigned int > RigGeometryIntPair; + +class BoneNameBoneMap : public std::map { + +public: + BoneNameBoneMap(const BoneSet& bones) { + for(BoneSet::iterator bone = bones.begin(); bone != bones.end(); ++bone) { + insert(StringBonePair((*bone)->getName(), *bone)); + } + } +}; + + +class RigGeometryIndexMap : public std::map { + +public: + RigGeometryIndexMap(const RigGeometrySet& rigGeometrySet) { + unsigned int index = 0; + for(RigGeometrySet::iterator rigGeometry = rigGeometrySet.begin(); rigGeometry != rigGeometrySet.end(); ++rigGeometry, ++index) { + insert(RigGeometryIntPair(*rigGeometry, index)); + } + } +}; + + +class ComputeMostInfluencedGeometryByBone { + +public: + ComputeMostInfluencedGeometryByBone(RigGeometrySet &rigGeometrySet, BoneSet &boneSet): + _rigGeometrySet(rigGeometrySet), + _boneSet(boneSet), + _logger("ComputeMostInfluencedGeometryByBone::compute(...)") + {} + + void compute() { + RigGeometryIndexMap rigGeometryIndexMap(_rigGeometrySet); + + RigGeometryInfluenceByBoneMap ribbm; + computeInfluences(_boneSet, _rigGeometrySet, ribbm); + for(RigGeometryInfluenceByBoneMap::iterator boneInfluencePair = ribbm.begin(); boneInfluencePair != ribbm.end(); ++boneInfluencePair) { + osg::ref_ptr bone = boneInfluencePair->first; + BoneInfluenceMap boneInfluenceMap = boneInfluencePair->second; + BoneInfluences influences(boneInfluenceMap.begin(), boneInfluenceMap.end()); + + std::sort(influences.begin(), influences.end(), sort_influences()); + bone->setUserValue("rigIndex", rigGeometryIndexMap [ influences.front().first ]); + } + + RigGeometrySet &rigGeometrySet(_rigGeometrySet); + for(RigGeometrySet::iterator rigGeometry = rigGeometrySet.begin(); rigGeometry != rigGeometrySet.end(); ++rigGeometry) { + (*rigGeometry)->setUserValue("rigIndex", rigGeometryIndexMap[ *rigGeometry ]); + } + } + +protected: + void computeInfluences(const BoneSet& bones, const RigGeometrySet& rigGeometries, RigGeometryInfluenceByBoneMap& rigGeometryInfluenceByBoneMap) { + BoneNameBoneMap boneMap(bones); + + for(RigGeometrySet::const_iterator rigGeometry = rigGeometries.begin(); rigGeometry != rigGeometries.end(); ++rigGeometry) { + osg::ref_ptr vertexInfluenceMap = (*rigGeometry)->getInfluenceMap(); + + for(osgAnimation::VertexInfluenceMap::iterator vertexInfluencePair = vertexInfluenceMap->begin(); vertexInfluencePair != vertexInfluenceMap->end(); ++vertexInfluencePair) { + BoneNameBoneMap::iterator bone_it = boneMap.find(vertexInfluencePair->first); + if(bone_it == boneMap.end()) continue; + osg::ref_ptr bone = bone_it->second; + const osgAnimation::VertexInfluence& vertexInfluence = (*vertexInfluencePair).second; + + for(osgAnimation::VertexInfluence::const_iterator vertexIndexWeight = vertexInfluence.begin(); vertexIndexWeight != vertexInfluence.end(); ++vertexIndexWeight) { + rigGeometryInfluenceByBoneMap[bone.get()][*rigGeometry].addWeight((*vertexIndexWeight).second); + } + } + } + } + + struct sort_influences { + //We sort influences by number of influenced vertex first and then by normalized weight (number_of_vertex_influence / accumulated_weight) + //i.e we choose to keep geometries with many vertex insted of geometries with high normalized weight, it makes more sense for geometry + //selection via bone influence box @see AABBonBoneVisitor class + bool operator()(const BoneInfluence &a, const BoneInfluence &b) { + return (a.second.getNumInfluencedVertex() > b.second.getNumInfluencedVertex()) || + (a.second.getNumInfluencedVertex() == b.second.getNumInfluencedVertex() && \ + a.second.getNormalizedWeight() > b.second.getNormalizedWeight()); + } + }; + + RigGeometrySet &_rigGeometrySet; + BoneSet &_boneSet; + StatLogger _logger; +}; + + +#endif // MOST_INFLUENCED_GEOMETRY_BY_BONE_H diff --git a/src/osgPlugins/gles/OpenGLESGeometryOptimizer b/src/osgPlugins/gles/OpenGLESGeometryOptimizer index e0361632a..9eb36c74b 100644 --- a/src/osgPlugins/gles/OpenGLESGeometryOptimizer +++ b/src/osgPlugins/gles/OpenGLESGeometryOptimizer @@ -16,49 +16,66 @@ #include #include //std::max -#include "AnimationVisitor" +#include "AnimationCleanerVisitor" +#include "RigAnimationVisitor" +#include "AABBonBoneVisitor" +#include "DisableAnimationVisitor" #include "BindPerVertexVisitor" #include "DetachPrimitiveVisitor" #include "DrawArrayVisitor" #include "GeometrySplitterVisitor" #include "IndexMeshVisitor" #include "PreTransformVisitor" +#include "SmoothNormalVisitor" #include "TangentSpaceVisitor" #include "TriangleStripVisitor" #include "UnIndexMeshVisitor" #include "WireframeVisitor" +#include "AABBonBoneVisitor" +#include "AnimationCleanerVisitor" +#include "MostInfluencedGeometryByBone" +#include "LimitMorphTargetCount" +#include "RigAttributesVisitor" +#include "GeometryInspector" class OpenGLESGeometryOptimizer { public: OpenGLESGeometryOptimizer() : + _mode("all"), _useDrawArray(false), _disableTriStrip(false), _disableMergeTriStrip(false), _disablePreTransform(false), - _disablePostTransform(false), + _disableAnimation(false), + _enableAABBonBone(false), _triStripCacheSize(16), _triStripMinSize(2), _generateTangentSpace(false), _tangentUnit(0), _maxIndexValue(65535), - _wireframe("") + _wireframe(""), + _maxMorphTarget(0), + _exportNonGeometryDrawables(false) {} // run the optimizer osg::Node* optimize(osg::Node& node); // handle options + void setMode(const std::string& mode) { _mode = mode; } void setUseDrawArray(bool s) { _useDrawArray = s; } void setDisableTriStrip(bool s) { _disableTriStrip = s; } void setDisableMergeTriStrip(bool s) { _disableMergeTriStrip = s; } void setDisablePreTransform(bool s) { _disablePreTransform = s; } - void setDisablePostTransform(bool s) { _disablePostTransform = s; } + void setDisableAnimation(bool s) { _disableAnimation = s; } + void setDisableAnimationCleaning(bool s) { _disableAnimationCleaning = s; } + void setEnableAABBonBone(bool s) { _enableAABBonBone = s; } void setTripStripCacheSize(unsigned int size) { _triStripCacheSize = size; } void setTripStripMinSize(unsigned int size) { _triStripMinSize = std::max(size, 2); } - + void setExportNonGeometryDrawables(bool value) { _exportNonGeometryDrawables = value; } void setTexCoordChannelForTangentSpace(int uv) { _tangentUnit = uv; _generateTangentSpace = true; @@ -72,13 +89,66 @@ public: setDisableTriStrip(true); } } + void setMaxMorphTarget(unsigned int maxMorphTarget) { + _maxMorphTarget = maxMorphTarget; + } protected: void makeAnimation(osg::Node* node) { - AnimationVisitor anim; + makeRigAnimation(node); + if(_disableAnimation) { + makeDisableAnimation(node); + } + else { + if(!_disableAnimationCleaning) { + makeCleanAnimation(node); + } + makeLimitMorphTargetCount(node); + makeAABBonBone(node, _enableAABBonBone); + makeMostInfluencedGeometryByBone(node); + } + } + + void makeDisableAnimation(osg::Node* node) { + DisableAnimationVisitor disabler; + node->accept(disabler); + } + + void makeCleanAnimation(osg::Node* node) { + AnimationCleanerVisitor cleaner; + node->accept(cleaner); + cleaner.clean(); + } + + void makeRigAnimation(osg::Node* node) { + RigAnimationVisitor anim; node->accept(anim); } + void makeAABBonBone(osg::Node* node, bool enableAABBonBone) { + FindSkeletons fs; + node->accept(fs); + + for(unsigned int i = 0; i < fs._skls.size(); i++) { + osgAnimation::Skeleton * skl = fs._skls[i]; + ComputeAABBOnBoneVisitor cabv(enableAABBonBone); + skl->accept(cabv); + cabv.computeBoundingBoxOnBones(); + } + } + + void makeMostInfluencedGeometryByBone(osg::Node *node) { + CollectBonesAndRigGeometriesVisitor collector; + node->accept(collector); + ComputeMostInfluencedGeometryByBone computor(collector.getRigGeometrySet(), collector.getBoneSet()); + computor.compute(); + } + + void makeLimitMorphTargetCount(osg::Node* node) { + LimitMorphTargetCount limit(_maxMorphTarget); + node->accept(limit); + } + void makeWireframe(osg::Node* node) { WireframeVisitor wireframe(_wireframe == std::string("inline")); node->accept(wireframe); @@ -94,13 +164,18 @@ protected: node->accept(indexer); } + void makeSmoothNormal(osg::Node* node) { + SmoothNormalVisitor smoother(osg::PI / 4.f, true); + node->accept(smoother); + } + void makeTangentSpace(osg::Node* node) { TangentSpaceVisitor tangent(_tangentUnit); node->accept(tangent); } void makeSplit(osg::Node* node) { - GeometrySplitterVisitor splitter(_maxIndexValue, _disablePostTransform); + GeometrySplitterVisitor splitter(_maxIndexValue, _exportNonGeometryDrawables ); node->accept(splitter); } @@ -124,12 +199,25 @@ protected: node->accept(detacher); } - bool _useDrawArray; + void makeBonesAndWeightOnRigGeometry(osg::Node* node) { + RigAttributesVisitor rigger; + node->accept(rigger); + } + void makeInspectGeometry(osg::Node* node) { + GeometryInspector inspector; + node->accept(inspector); + } + + + std::string _mode; + bool _useDrawArray; bool _disableTriStrip; bool _disableMergeTriStrip; bool _disablePreTransform; - bool _disablePostTransform; + bool _disableAnimation; + bool _disableAnimationCleaning; + bool _enableAABBonBone; unsigned int _triStripCacheSize; unsigned int _triStripMinSize; @@ -138,6 +226,10 @@ protected: unsigned int _maxIndexValue; std::string _wireframe; + + unsigned int _maxMorphTarget; + + bool _exportNonGeometryDrawables; }; #endif diff --git a/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp b/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp index 04873b9aa..5c10f69fb 100644 --- a/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp +++ b/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp @@ -9,46 +9,56 @@ const unsigned glesUtil::Remapper::invalidIndex = std::numeric_limits: osg::Node* OpenGLESGeometryOptimizer::optimize(osg::Node& node) { osg::ref_ptr model = osg::clone(&node); - // animation: create regular Geometry if RigGeometry - makeAnimation(model.get()); - - // wireframe - if (!_wireframe.empty()) { - makeWireframe(model.get()); + if(_mode == "all" || _mode == "animation") { + // animation: process bones/weights or remove all animation data if disabled + makeAnimation(model.get()); } - // bind per vertex - makeBindPerVertex(model.get()); + if(_mode == "all" || _mode == "geometry") { + // wireframe + if (!_wireframe.empty()) { + makeWireframe(model.get()); + } - // index (merge exact duplicates + uses simple triangles & lines i.e. no strip/fan/loop) - makeIndexMesh(model.get()); + // bind per vertex + makeBindPerVertex(model.get()); - // tangent space - if (_generateTangentSpace) { - makeTangentSpace(model.get()); + // index (merge exact duplicates + uses simple triangles & lines i.e. no strip/fan/loop) + makeIndexMesh(model.get()); + + // smooth vertex normals (if geometry has no normal compute smooth normals) + makeSmoothNormal(model.get()); + + // tangent space + if (_generateTangentSpace) { + makeTangentSpace(model.get()); + } + + if(!_useDrawArray) { + // split geometries having some primitive index > _maxIndexValue + makeSplit(model.get()); + } + + // strip + if(!_disableTriStrip) { + makeTriStrip(model.get()); + } + + if(_useDrawArray) { + // drawelements to drawarrays + makeDrawArray(model.get()); + } + else if(!_disablePreTransform) { + // pre-transform + makePreTransform(model.get()); + } + + // unbind bones/weights from source and bind on RigGeometry + makeBonesAndWeightOnRigGeometry(model.get()); + + // detach wireframe + makeDetach(model.get()); } - if(!_useDrawArray) { - // split geometries having some primitive index > _maxIndexValue - makeSplit(model.get()); - } - - // strip - if(!_disableTriStrip) { - makeTriStrip(model.get()); - } - - if(_useDrawArray) { - // drawelements to drawarrays - makeDrawArray(model.get()); - } - else if(!_disablePreTransform) { - // pre-transform - makePreTransform(model.get()); - } - - // detach wireframe - makeDetach(model.get()); - return model.release(); } diff --git a/src/osgPlugins/gles/PreTransformVisitor b/src/osgPlugins/gles/PreTransformVisitor index f59ce70a1..45e9aeb2d 100644 --- a/src/osgPlugins/gles/PreTransformVisitor +++ b/src/osgPlugins/gles/PreTransformVisitor @@ -23,11 +23,9 @@ public: PreTransformVisitor(): GeometryUniqueVisitor("PreTransformVisitor") {} - void apply(osg::Geometry& geometry) { + void process(osg::Geometry& geometry) { glesUtil::VertexAccessOrderVisitor optimizer; optimizer.optimizeOrder(geometry); - - setProcessed(&geometry); } }; diff --git a/src/osgPlugins/gles/PrimitiveIndexors b/src/osgPlugins/gles/PrimitiveIndexors index ea3eba706..7e83e6d6b 100644 --- a/src/osgPlugins/gles/PrimitiveIndexors +++ b/src/osgPlugins/gles/PrimitiveIndexors @@ -3,9 +3,9 @@ #include #include +#include #include -#include "TriangleLinePointIndexFunctor" #include "EdgeIndexFunctor" #include "LineIndexFunctor" #include "PointIndexFunctor" diff --git a/src/osgPlugins/gles/ReaderWriterGLES.cpp b/src/osgPlugins/gles/ReaderWriterGLES.cpp index 2c7133382..799601409 100644 --- a/src/osgPlugins/gles/ReaderWriterGLES.cpp +++ b/src/osgPlugins/gles/ReaderWriterGLES.cpp @@ -36,32 +36,42 @@ class ReaderWriterGLES : public osgDB::ReaderWriter public: struct OptionsStruct { + std::string glesMode; std::string enableWireframe; bool generateTangentSpace; int tangentSpaceTextureUnit; bool disableTriStrip; bool disableMergeTriStrip; bool disablePreTransform; - bool disablePostTransform; + bool disableAnimation; + bool disableAnimationCleaning; + bool enableAABBonBone; unsigned int triStripCacheSize; unsigned int triStripMinSize; bool useDrawArray; bool disableIndex; unsigned int maxIndexValue; + unsigned int maxMorphTarget; + bool exportNonGeometryDrawables; OptionsStruct() { + glesMode = "all"; enableWireframe = ""; generateTangentSpace = false; tangentSpaceTextureUnit = 0; disableTriStrip = false; disableMergeTriStrip = false; disablePreTransform = false; - disablePostTransform = false; + disableAnimation = false; + disableAnimationCleaning = false; + enableAABBonBone = false; triStripCacheSize = 16; triStripMinSize = 2; useDrawArray = false; disableIndex = false; maxIndexValue = 0; + maxMorphTarget = 0; + exportNonGeometryDrawables = false; } }; @@ -70,6 +80,7 @@ public: { supportsExtension("gles","OpenGL ES optimized format"); + supportsOption("glesMode[=all|animation|geometry]","run all optimizations (default) or simply animation/geometry."); supportsOption("enableWireframe[=inline]","create a wireframe geometry for each triangles geometry. The wire geometry will be stored along the solid geometry if 'inline' is specified."); supportsOption("generateTangentSpace","Build tangent space to each geometry"); supportsOption("tangentSpaceTextureUnit=","Specify on which texture unit normal map is"); @@ -78,17 +89,21 @@ public: supportsOption("disableMergeTriStrip","disable the merge of all tristrip into one"); supportsOption("disableTriStrip","disable generation of tristrip"); supportsOption("disablePreTransform","disable pre-transform of geometries after split"); - supportsOption("disablePostTransform","disable post-transform of geometries (called during geometry splitting)"); + supportsOption("disableAnimation","disable animation support"); + supportsOption("disableAnimationCleaning","disable animations/channels cleaning"); + supportsOption("enableAABBonBone","Create AABB on bone for rigGeometry (Adds a Geometry in the graph)"); supportsOption("useDrawArray","prefer drawArray instead of drawelement with split of geometry"); supportsOption("disableIndex","Do not index the geometry"); supportsOption("maxIndexValue=","set the maximum index value (first index is 0)"); + supportsOption("maxMorphTarget=", "set the maximum morph target in morph geometry (no limit by default)"); + supportsOption("exportNonGeometryDrawables", "export non geometry drawables, right now only text 2D supported" ); } virtual const char* className() const { return "GLES Optimizer"; } virtual osg::Node* optimizeModel(const Node& node, const OptionsStruct& options) const { - osg::Node* model = osg::clone(&node); + osg::ref_ptr model = osg::clone(&node); if (options.disableIndex) { UnIndexMeshVisitor unindex; @@ -97,24 +112,29 @@ public: else { OpenGLESGeometryOptimizer optimizer; + optimizer.setMode(options.glesMode); optimizer.setUseDrawArray(options.useDrawArray); optimizer.setTripStripCacheSize(options.triStripCacheSize); optimizer.setTripStripMinSize(options.triStripMinSize); optimizer.setDisableTriStrip(options.disableTriStrip); optimizer.setDisableMergeTriStrip(options.disableMergeTriStrip); optimizer.setDisablePreTransform(options.disablePreTransform); - optimizer.setDisablePostTransform(options.disablePostTransform); + optimizer.setDisableAnimation(options.disableAnimation); + optimizer.setDisableAnimationCleaning(options.disableAnimationCleaning); + optimizer.setEnableAABBonBone(options.enableAABBonBone); optimizer.setWireframe(options.enableWireframe); + optimizer.setExportNonGeometryDrawables(options.exportNonGeometryDrawables); if (options.generateTangentSpace) { optimizer.setTexCoordChannelForTangentSpace(options.tangentSpaceTextureUnit); } if(options.maxIndexValue) { optimizer.setMaxIndexValue(options.maxIndexValue); } + optimizer.setMaxMorphTarget(options.maxMorphTarget); model = optimizer.optimize(*model); } - return model; + return model.release(); } @@ -201,6 +221,12 @@ public: pre_equals = opt; } + if (pre_equals == "glesMode") + { + if(post_equals == "animation" || post_equals == "geometry") { + localOptions.glesMode = post_equals; + } + } if (pre_equals == "enableWireframe") { if(post_equals == "inline") { @@ -222,9 +248,17 @@ public: { localOptions.disablePreTransform = true; } - if (pre_equals == "disablePostTransform") + if (pre_equals == "disableAnimation") { - localOptions.disablePostTransform = true; + localOptions.disableAnimation = true; + } + if (pre_equals == "disableAnimationCleaning") + { + localOptions.disableAnimationCleaning = true; + } + if (pre_equals == "enableAABBonBone") + { + localOptions.enableAABBonBone = true; } if (pre_equals == "disableTriStrip") { @@ -238,7 +272,10 @@ public: { localOptions.disableIndex = true; } - + if (pre_equals == "exportNonGeometryDrawables") + { + localOptions.exportNonGeometryDrawables = true; + } if (post_equals.length() > 0) { if (pre_equals == "tangentSpaceTextureUnit") { localOptions.tangentSpaceTextureUnit = atoi(post_equals.c_str()); @@ -252,6 +289,9 @@ public: if (pre_equals == "maxIndexValue") { localOptions.maxIndexValue = atoi(post_equals.c_str()); } + if(pre_equals == "maxMorphTarget") { + localOptions.maxMorphTarget = atoi(post_equals.c_str()); + } } } } diff --git a/src/osgPlugins/gles/RigAnimationVisitor b/src/osgPlugins/gles/RigAnimationVisitor new file mode 100644 index 000000000..f069f5b43 --- /dev/null +++ b/src/osgPlugins/gles/RigAnimationVisitor @@ -0,0 +1,202 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson + * + * This application is open source and may be redistributed and/or modified + * freely and without restriction, both in commercial and non commercial + * applications, as long as this copyright notice is maintained. + * + * This application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef RIG_ANIMATION_VISITOR +#define RIG_ANIMATION_VISITOR + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#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 +{ +public: + typedef std::map boneIndices; + + RigAnimationVisitor(): + _logger("RigAnimationVisitor::apply(..)") + { + 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); + } + } + + 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(); + + 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); + } + + 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 + } + +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; +}; + +#endif diff --git a/src/osgPlugins/gles/RigAttributesVisitor b/src/osgPlugins/gles/RigAttributesVisitor new file mode 100644 index 000000000..6674fb9d1 --- /dev/null +++ b/src/osgPlugins/gles/RigAttributesVisitor @@ -0,0 +1,53 @@ +#ifndef RIG_ATTRIBUTES_VISITOR +#define RIG_ATTRIBUTES_VISITOR + +#include "GeometryUniqueVisitor" + + +class RigAttributesVisitor : public GeometryUniqueVisitor { +public: + RigAttributesVisitor(): + 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(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; + } +}; + + + +#endif diff --git a/src/osgPlugins/gles/SmoothNormalVisitor b/src/osgPlugins/gles/SmoothNormalVisitor new file mode 100644 index 000000000..2f72275a4 --- /dev/null +++ b/src/osgPlugins/gles/SmoothNormalVisitor @@ -0,0 +1,395 @@ +#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()); + for(unsigned int index = 0 ; index < _geometry.getVertexArray()->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 +}; + + + +class SmoothNormalVisitor : public GeometryUniqueVisitor { +public: + SmoothNormalVisitor(float creaseAngle, bool comparePosition=false): + GeometryUniqueVisitor("SmoothNormalVisitor"), + _creaseAngle(creaseAngle), + _comparePosition(comparePosition) + {} + + void process(osg::Geometry& geometry) { + if(!geometry.getNormalArray()) { + TriangleMeshSmoother(geometry, _creaseAngle, _comparePosition, TriangleMeshSmoother::recompute); + } + else { + TriangleMeshSmoother(geometry, _creaseAngle, _comparePosition, TriangleMeshSmoother::diagnose); + } + } + + void process(osgAnimation::MorphGeometry& morphGeometry) { + TriangleMeshSmoother(morphGeometry, 0, true, TriangleMeshSmoother::smooth_all); + osgAnimation::MorphGeometry::MorphTargetList targets = morphGeometry.getMorphTargetList(); + for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) { + // check normal orientation using the same primitives as parent geometry + osg::Geometry::PrimitiveSetList& primitives = target->getGeometry()->getPrimitiveSetList(); + target->getGeometry()->setPrimitiveSetList(morphGeometry.getPrimitiveSetList()); + + TriangleMeshSmoother(*target->getGeometry(), 0, true, TriangleMeshSmoother::smooth_all); + + target->getGeometry()->setPrimitiveSetList(primitives); + } + } + +protected: + float _creaseAngle; + bool _comparePosition; +}; diff --git a/src/osgPlugins/gles/SubGeometry b/src/osgPlugins/gles/SubGeometry new file mode 100644 index 000000000..b9c81a7f7 --- /dev/null +++ b/src/osgPlugins/gles/SubGeometry @@ -0,0 +1,275 @@ +#ifndef SUB_GEOMETRY +#define SUB_GEOMETRY + +#include + +#include +#include +#include +#include +#include + +#include + + +class SubGeometry { +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()); + const osgAnimation::MorphGeometry::MorphTargetList& morphTargetList = morphSource->getMorphTargetList(); + for(osgAnimation::MorphGeometry::MorphTargetList::const_iterator targetSource = morphTargetList.begin() ; + targetSource != morphTargetList.end() ; ++ targetSource) { + 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)); + } + } + } + + 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; + + 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 = 0; + if(array) { + buffer = dynamic_cast(array->cloneType()); + 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 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)) \ + { 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::ref_ptr _geometry; + std::map _bufferMap; + std::map _indexMap; + std::map _primitives; +}; + + +#endif diff --git a/src/osgPlugins/gles/TangentSpaceVisitor b/src/osgPlugins/gles/TangentSpaceVisitor index e3b7f2fb7..0b6211041 100644 --- a/src/osgPlugins/gles/TangentSpaceVisitor +++ b/src/osgPlugins/gles/TangentSpaceVisitor @@ -33,7 +33,42 @@ public: _textureUnit(textureUnit) {} - void apply(osg::Geometry& geom) { + 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; @@ -93,9 +128,9 @@ public: // The bitangent vector B is then given by B = (N × T) · Tw } finalTangent->setUserValue("tangent", true); - geom.setVertexAttribArray(geom.getNumVertexAttribArrays(), finalTangent, osg::Array::BIND_PER_VERTEX); + tangentIndex = (tangentIndex >= 0 ? tangentIndex : geom.getNumVertexAttribArrays()) ; + geom.setVertexAttribArray(tangentIndex, finalTangent, osg::Array::BIND_PER_VERTEX); } - setProcessed(&geom); } protected: diff --git a/src/osgPlugins/gles/TriangleLinePointIndexFunctor b/src/osgPlugins/gles/TriangleLinePointIndexFunctor deleted file mode 100644 index 877e2a6ee..000000000 --- a/src/osgPlugins/gles/TriangleLinePointIndexFunctor +++ /dev/null @@ -1,257 +0,0 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab - * - * This application is open source and may be redistributed and/or modified - * freely and without restriction, both in commercial and non commercial - * applications, as long as this copyright notice is maintained. - * - * This application is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - */ - -#ifndef TRIANGLE_LINE_POINT_INDEX_FUNCTOR -#define TRIANGLE_LINE_POINT_INDEX_FUNCTOR - -#include -#include - - -template -class TriangleLinePointIndexFunctor : public osg::PrimitiveIndexFunctor, public T -{ -public: - virtual void setVertexArray(unsigned int,const osg::Vec2*) - {} - - virtual void setVertexArray(unsigned int ,const osg::Vec3* ) - {} - - virtual void setVertexArray(unsigned int,const osg::Vec4* ) - {} - - virtual void setVertexArray(unsigned int,const osg::Vec2d*) - {} - - virtual void setVertexArray(unsigned int ,const osg::Vec3d* ) - {} - - virtual void setVertexArray(unsigned int,const osg::Vec4d* ) - {} - - virtual void begin(GLenum mode) { - _modeCache = mode; - _indexCache.clear(); - } - - virtual void vertex(unsigned int vert) { - _indexCache.push_back(vert); - } - - virtual void end() { - if (!_indexCache.empty()) { - drawElements(_modeCache,_indexCache.size(),&_indexCache.front()); - } - } - - virtual void drawArrays(GLenum mode, GLint first, GLsizei count) { - switch(mode) - { - case(GL_TRIANGLES): - { - unsigned int pos=first; - for(GLsizei i = 2 ; i < count ; i += 3, pos += 3) { - this->operator()(pos, pos + 1, pos + 2); - } - break; - } - case(GL_TRIANGLE_STRIP): - { - unsigned int pos = first; - for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) { - if ((i%2)) this->operator()(pos, pos + 2, pos + 1); - else this->operator()(pos, pos + 1, pos + 2); - } - break; - } - case(GL_QUADS): - { - unsigned int pos = first; - for(GLsizei i = 3 ; i < count ; i += 4, pos += 4) { - this->operator()(pos,pos + 1, pos + 2); - this->operator()(pos,pos + 2, pos + 3); - } - break; - } - case(GL_QUAD_STRIP): - { - unsigned int pos = first; - for(GLsizei i = 3 ; i < count ; i += 2, pos += 2) { - this->operator()(pos, pos + 1,pos + 2); - this->operator()(pos + 1,pos + 3,pos + 2); - } - break; - } - case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN - case(GL_TRIANGLE_FAN): - { - unsigned int pos = first + 1; - for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) { - this->operator()(first, pos, pos + 1); - } - break; - } - case(GL_LINES): - { - unsigned int pos = first; - for(GLsizei i = 0 ; i < count ; i += 2, pos += 2) { - this->operator()(pos, pos + 1); - } - break; - } - case(GL_LINE_STRIP): - { - unsigned int pos = first; - for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) { - this->operator()(pos, pos + 1); - } - break; - } - case(GL_LINE_LOOP): - { - unsigned int pos = first; - for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) { - this->operator()(pos, pos + 1); - } - this->operator()(pos, first); - break; - } - case(GL_POINTS): - { - unsigned int pos=first; - for(GLsizei i = 0 ; i < count ; ++ i) { - this->operator()(pos + i); - } - break; - } - default: - break; - } - } - - template - void drawElements(GLenum mode, GLsizei count, const I* indices) - { - typedef I Index; - typedef const I* IndexPointer; - - if (indices == 0 || count == 0) { - return; - } - - switch(mode) - { - case(GL_TRIANGLES): - { - IndexPointer ilast = &indices[count]; - for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 3) { - this->operator()(*iptr, *(iptr + 1), *(iptr + 2)); - } - break; - } - case(GL_TRIANGLE_STRIP): - { - IndexPointer iptr = indices; - for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) { - if ((i%2)) this->operator()(*(iptr), *(iptr + 2), *(iptr + 1)); - else this->operator()(*(iptr), *(iptr + 1), *(iptr + 2)); - } - break; - } - case(GL_QUADS): - { - IndexPointer iptr = indices; - for(GLsizei i = 3 ; i < count ; i += 4, iptr += 4) { - this->operator()(*(iptr), *(iptr + 1), *(iptr + 2)); - this->operator()(*(iptr), *(iptr + 2), *(iptr + 3)); - } - break; - } - case(GL_QUAD_STRIP): - { - IndexPointer iptr = indices; - for(GLsizei i = 3 ; i < count ; i += 2, iptr += 2) { - this->operator()(*(iptr), *(iptr + 1), *(iptr + 2)); - this->operator()(*(iptr + 1), *(iptr + 3), *(iptr + 2)); - } - break; - } - case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN - case(GL_TRIANGLE_FAN): - { - IndexPointer iptr = indices; - Index first = *iptr; - ++iptr; - for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) { - this->operator()(first, *(iptr), *(iptr + 1)); - } - break; - } - case(GL_LINES): - { - const I* iptr = indices; - for(GLsizei i = 0 ; i < count ; i += 2, iptr += 2) { - this->operator()(*iptr, *(iptr + 1)); - } - break; - } - case(GL_LINE_STRIP): - { - const I* iptr = indices; - for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) { - this->operator()(*iptr, *(iptr + 1)); - } - break; - } - case(GL_LINE_LOOP): - { - const I* iptr = indices; - I first = *iptr; - for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) { - this->operator()(*iptr, *(iptr + 1)); - } - this->operator()(*iptr, first); - break; - } - case GL_POINTS: - { - IndexPointer ilast = &indices[count]; - for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 1) { - this->operator()(*iptr); - } - break; - } - default: - break; - } - } - - virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) { - drawElements(mode, count, indices); - } - - virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) { - drawElements(mode, count, indices); - } - - virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) { - drawElements(mode, count, indices); - } - - - GLenum _modeCache; - std::vector _indexCache; - std::vector _remap; -}; - -#endif diff --git a/src/osgPlugins/gles/TriangleMeshGraph b/src/osgPlugins/gles/TriangleMeshGraph new file mode 100644 index 000000000..052bce56c --- /dev/null +++ b/src/osgPlugins/gles/TriangleMeshGraph @@ -0,0 +1,340 @@ +#ifndef MESH_GRAPH +#define MESH_GRAPH + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +class Vertex; +class Triangle; +typedef std::vector IndexVector; +typedef std::deque IndexDeque; +typedef std::set IndexSet; +typedef std::set::const_iterator VertexIterator; +typedef std::vector TriangleVector; +typedef std::vector< osg::ref_ptr > ArrayVector; + + +inline float clamp(float value, float minValue, float maxValue) { + return std::min(maxValue, std::max(minValue, value)); +} + + +class Triangle { +public: + unsigned int _v[3]; + osg::Vec3 _normal; + float _area; + + Triangle(unsigned int v1, unsigned int v2, unsigned int v3, const osg::Vec3& normal) + { + _v[0] = v1; _v[1] = v2; _v[2] = v3; + _area = normal.length(); + _normal = normal / _area; + } + + Triangle unique(const std::vector& toUnique) const { + return Triangle(toUnique[v1()], toUnique[v2()], toUnique[v3()], _normal); + } + + bool operator==(const Triangle& other) const { + return v1() == other.v1() && + v2() == other.v2() && + v3() == other.v3(); + } + + inline unsigned int& v1() { + return _v[0]; + } + + inline unsigned int v1() const { + return _v[0]; + } + + inline unsigned int& v2() { + return _v[1]; + } + + inline unsigned int v2() const { + return _v[1]; + } + + inline unsigned int& v3() { + return _v[2]; + } + + inline unsigned int v3() const { + return _v[2]; + } + + inline unsigned int operator[](unsigned int i) const { + return _v[i]; + } + + bool hasEdge(unsigned int e1, unsigned int e2) const { + return hasVertex(e1) && hasVertex(e2); + } + + inline bool hasVertex(unsigned int vertex) const { + return v1() == vertex || v2() == vertex || v3() == vertex; + } + + bool intersect(const Triangle& other) const { + return other.hasEdge(v1(), v2()) || + other.hasEdge(v1(), v3()) || + other.hasEdge(v2(), v3()); + } + + inline float angleCosine(const Triangle& other) const { + // Triangle._normal is normalized so no need to divide by lengthes + return (_normal * other._normal); + } + + inline float angle(const Triangle& other) const { + return acos(clamp(angleCosine(other), -1.f, 1.f)); + } +}; + + +class Edge { +public: + unsigned int _v[2]; + + Edge(unsigned int e1, unsigned int e2) { + _v[0] = e1; + _v[1] = e2; + } + + bool operator==(const Edge& other) const { + return v1() == other.v1() && v2() == other.v2(); + } + + unsigned int v1() const + { return _v[0]; } + + unsigned int& v1() + { return _v[0]; } + + unsigned int v2() const + { return _v[1]; } + + unsigned int& v2() + { return _v[1]; } +}; + + +class Vertex { +public: + Vertex(const osg::Vec3 position): _position(position), + _index(std::numeric_limits::max()) + {} + + bool operator<(const Vertex& other) const { + return _position < other._position; + } + + const osg::Vec3 _position; + mutable unsigned int _index; // not used in operator< +}; + + +class TriangleMeshGraph { +protected: + class TriangleRegistror { + public: + void operator() (unsigned int p1, unsigned int p2, unsigned int p3) { + if (p1 == p2 || p2 == p3 || p1 == p3) { + return; + } + _graph->addTriangle(p1, p2, p3); + } + + void setGraph(TriangleMeshGraph* graph) { + _graph = graph; + } + + protected: + TriangleMeshGraph* _graph; + }; + + +public: + TriangleMeshGraph(const osg::Geometry& geometry, bool comparePosition=true): + _geometry(geometry), + _positions(dynamic_cast(geometry.getVertexArray())), + _comparePosition(comparePosition) + { + unsigned int nbVertex = _positions->getNumElements(); + _unique.resize(nbVertex, std::numeric_limits::max()); + _vertexTriangles.resize(nbVertex, IndexVector()); + build(); + } + + VertexIterator begin() const { + return _vertices.begin(); + } + + VertexIterator end() const { + return _vertices.end(); + } + + void setComparePosition(bool use) { + _comparePosition = use; + } + + unsigned int getNumTriangles() const { + return _triangles.size(); + } + + const Triangle& triangle(unsigned int index) const { + return _triangles[index]; + } + + Triangle& triangle(unsigned int index) { + return _triangles[index]; + } + + void addTriangle(unsigned int v1, unsigned int v2, unsigned int v3) { + osg::Vec3f p1 = (*_positions)[v1], + p2 = (*_positions)[v2], + p3 = (*_positions)[v3]; + + osg::Vec3f cross = (p2 - p1) ^ (p3 - p1); + if(cross.length()) { + registerTriangleForVertex(_triangles.size(), v1, unify(v1)); + registerTriangleForVertex(_triangles.size(), v2, unify(v2)); + registerTriangleForVertex(_triangles.size(), v3, unify(v3)); + _triangles.push_back(Triangle(v1, v2, v3, cross)); + } + } + + unsigned int unify(unsigned int i) { + if(_unique[i] == std::numeric_limits::max()) { + if(_comparePosition) { + std::pair::iterator, bool> result = _vertices.insert(Vertex((*_positions)[i])); + if(result.second) { // new element + result.first->_index = i; + } + _unique[i] = result.first->_index; + } + else { + _unique[i] = i; + } + } + return _unique[i]; + } + + void add(unsigned int newIndex, unsigned int oldIndex) { + if(newIndex >= _unique.size()) { + _unique.resize(newIndex + 1); + } + _unique[newIndex] = _unique[oldIndex]; + } + + const IndexVector& triangles(unsigned int index) const { + return _vertexTriangles[index]; + } + + std::vector vertexOneRing(unsigned int index, const float creaseAngle) const { + std::vector oneRing; + + IndexDeque triangles(_vertexTriangles[index].begin(), _vertexTriangles[index].end()); + + while(!triangles.empty()) { + IndexDeque cluster; + cluster.push_front(triangles.front()); + triangles.pop_front(); + + IndexDeque::iterator neighbor; + // expand from front + while(!triangles.empty()) { + neighbor = findNeighbor(triangles, cluster.front(), creaseAngle); + if(neighbor == triangles.end()) { + break; + } + cluster.push_front(*neighbor); + triangles.erase(neighbor); + } + + // expand from back + while(!triangles.empty()) { + neighbor = findNeighbor(triangles, cluster.back(), creaseAngle); + if(neighbor == triangles.end()) { + break; + } + cluster.push_back(*neighbor); + triangles.erase(neighbor); + } + oneRing.push_back(IndexVector(cluster.begin(), cluster.end())); + } + return oneRing; + } + + IndexVector triangleNeighbors(unsigned int index) const { + IndexVector neighbors; + const Triangle& t = _triangles[index]; + + for(unsigned int i = 0 ; i < 3 ; ++ i) { + const IndexVector& others = triangles(t[i]); + for(IndexVector::const_iterator other = others.begin() ; other != others.end() ; ++ other) { + if(*other == index) { + continue; + } + else if (t.intersect(_triangles[*other])){ + neighbors.push_back(*other); + } + } + } + + return neighbors; + } + +protected: + void build() { + osg::TriangleIndexFunctor functor; + functor.setGraph(this); + _geometry.accept(functor); + } + + inline void registerTriangleForVertex(unsigned int triangle, unsigned int vertex, unsigned int deduplicate) { + _vertexTriangles[vertex].push_back(triangle); + if(vertex != deduplicate) { + _vertexTriangles[deduplicate].push_back(triangle); + } + } + + IndexDeque::iterator findNeighbor(IndexDeque& candidates, const unsigned int index, const float creaseAngle) const { + Triangle triangle = _triangles[index].unique(_unique); + + for(IndexDeque::iterator candidate = candidates.begin() ; candidate != candidates.end() ; ++ candidate) { + Triangle other = _triangles[*candidate].unique(_unique); + + if(triangle.intersect(other) && isSmoothEdge(triangle, other, creaseAngle)) { + return candidate; + } + } + return candidates.end(); + } + + inline bool isSmoothEdge(const Triangle& triangle1, const Triangle& triangle2, const float creaseAngle) const { + return (creaseAngle == 0.f ? true : triangle1.angle(triangle2) < creaseAngle); + } + + const osg::Geometry& _geometry; + const osg::Vec3Array* _positions; + bool _comparePosition; + std::set _vertices; + IndexVector _unique; + std::vector _vertexTriangles; + TriangleVector _triangles; +}; + + +#endif diff --git a/src/osgPlugins/gles/TriangleStripVisitor b/src/osgPlugins/gles/TriangleStripVisitor index 11f0439b8..e7ac4c315 100644 --- a/src/osgPlugins/gles/TriangleStripVisitor +++ b/src/osgPlugins/gles/TriangleStripVisitor @@ -22,7 +22,7 @@ public: _cacheSize(cacheSize), _minSize(minSize), _merge(merge) {} - void apply(osg::Geometry& geometry); + void process(osg::Geometry& geometry); protected: void mergeTrianglesStrip(osg::Geometry& geometry); diff --git a/src/osgPlugins/gles/TriangleStripVisitor.cpp b/src/osgPlugins/gles/TriangleStripVisitor.cpp index 0b8efd94e..eb9202b80 100644 --- a/src/osgPlugins/gles/TriangleStripVisitor.cpp +++ b/src/osgPlugins/gles/TriangleStripVisitor.cpp @@ -2,10 +2,11 @@ #include "TriangleStripVisitor" -void TriangleStripVisitor::apply(osg::Geometry& geometry) { +void TriangleStripVisitor::process(osg::Geometry& geometry) { osgUtil::TriStripVisitor tristrip; tristrip.setCacheSize(_cacheSize); tristrip.setMinStripSize(_minSize); + tristrip.setIndexMesh(false); tristrip.stripify(geometry); // merge stritrip to one using degenerated triangles as glue diff --git a/src/osgPlugins/gles/UnIndexMeshVisitor b/src/osgPlugins/gles/UnIndexMeshVisitor index 76f281211..f45726bd9 100644 --- a/src/osgPlugins/gles/UnIndexMeshVisitor +++ b/src/osgPlugins/gles/UnIndexMeshVisitor @@ -22,7 +22,7 @@ public: UnIndexMeshVisitor(): GeometryUniqueVisitor("UnIndexMeshVisitor") {} - void apply(osg::Geometry& geom); + void process(osg::Geometry& geom); }; #endif diff --git a/src/osgPlugins/gles/UnIndexMeshVisitor.cpp b/src/osgPlugins/gles/UnIndexMeshVisitor.cpp index 08ed09cdc..b08e22ede 100644 --- a/src/osgPlugins/gles/UnIndexMeshVisitor.cpp +++ b/src/osgPlugins/gles/UnIndexMeshVisitor.cpp @@ -21,7 +21,7 @@ typedef std::vector IndexList; // this help works only for indexed primitive to unindex it -void UnIndexMeshVisitor::apply(osg::Geometry& geom) +void UnIndexMeshVisitor::process(osg::Geometry& geom) { // no point optimizing if we don't have enough vertices. if (!geom.getVertexArray()) return; @@ -124,5 +124,4 @@ void UnIndexMeshVisitor::apply(osg::Geometry& geom) arrayList.setToGeometry(geom); geom.setPrimitiveSetList(newPrimitives); - setProcessed(&geom); } diff --git a/src/osgPlugins/gles/WireframeVisitor b/src/osgPlugins/gles/WireframeVisitor index 8f8a68e6c..563bef26c 100644 --- a/src/osgPlugins/gles/WireframeVisitor +++ b/src/osgPlugins/gles/WireframeVisitor @@ -13,8 +13,6 @@ #ifndef WIREFRAME_VISITOR #define WIREFRAME_VISITOR -#include - #include #include #include @@ -38,36 +36,22 @@ public: void apply(osg::Geode& geode) { handleStateSet(geode); - for (unsigned int i = 0; i < geode.getNumDrawables(); i++) { - apply(*geode.getDrawable(i)); - } + GeometryUniqueVisitor::apply(geode); } - void apply(osg::Drawable& drawable) { - osg::Geometry* geometry = drawable.asGeometry(); - if (!geometry) { - return; - } - apply(*geometry); - } - - void apply(osg::Geometry& geometry) { - if(_processed.find(&geometry) == _processed.end()) { - const unsigned int nbSourcePrimitives = geometry.getNumPrimitiveSets(); - for(unsigned int i = 0 ; i < nbSourcePrimitives ; ++ i) { - osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i); - EdgeIndexor edges; - primitive->accept(edges); - if(!edges._indices.empty()) { - osg::DrawElementsUInt* wireframe = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES, - edges._indices.begin(), - edges._indices.end()); - wireframe->setUserValue("wireframe", true); - geometry.getPrimitiveSetList().push_back(wireframe); - } + void process(osg::Geometry& geometry) { + const unsigned int nbSourcePrimitives = geometry.getNumPrimitiveSets(); + for(unsigned int i = 0 ; i < nbSourcePrimitives ; ++ i) { + osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i); + EdgeIndexor edges; + primitive->accept(edges); + if(!edges._indices.empty()) { + osg::DrawElementsUInt* wireframe = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES, + edges._indices.begin(), + edges._indices.end()); + wireframe->setUserValue("wireframe", true); + geometry.getPrimitiveSetList().push_back(wireframe); } - - _processed.insert(&geometry); } } @@ -77,7 +61,6 @@ public: } } - std::set _processed; bool _inlined; }; diff --git a/src/osgPlugins/gles/debug b/src/osgPlugins/gles/debug new file mode 100644 index 000000000..f5b8e8d6c --- /dev/null +++ b/src/osgPlugins/gles/debug @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + + +inline void save_debug(osg::Node& node, const std::string& path) { + osg::ref_ptr registry = osgDB::Registry::instance(); + std::string ext = osgDB::getLowerCaseFileExtension(path); + osgDB::ReaderWriter* writer = registry->getReaderWriterForExtension(ext); + if(writer) { + writer->writeNode(node, path.c_str()); + } +} + +inline void save_debug(osg::Geometry& geometry, const std::string& path) { + osg::ref_ptr geode = new osg::Geode; + geode->addDrawable(&geometry); + save_debug(*geode, path); +} + +template +std::string dump(const VV& v) { + std::ostringstream oss; + oss << "(" << v[0]; + for(int i = 1 ; i < VV::num_components ; ++ i) { + oss << ", " << v[i]; + } + oss << ")"; + return oss.str(); +} + +template +float length(const VV& v) { + float l = 0; + for(int i = 0 ; i < VV::num_components ; ++ i) { + l += v[i] * v[i]; + } + return std::sqrt(l); +} + diff --git a/src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp b/src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp deleted file mode 100644 index f184c5ba9..000000000 --- a/src/osgPlugins/gles/forsythtriangleorderoptimizer.cpp +++ /dev/null @@ -1,350 +0,0 @@ -//----------------------------------------------------------------------------- -// This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache -// Optimization" algorithm as described here: -// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html -// -// This code was authored and released into the public domain by -// Adrian Stone (stone@gameangst.com). -// -// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER -// LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -namespace Forsyth -{ - //----------------------------------------------------------------------------- - // OptimizeFaces - //----------------------------------------------------------------------------- - // Parameters: - // indexList - // input index list - // indexCount - // the number of indices in the list - // vertexCount - // the largest index value in indexList - // newIndexList - // a pointer to a preallocated buffer the same size as indexList to - // hold the optimized index list - // lruCacheSize - // the size of the simulated post-transform cache (max:64) - //----------------------------------------------------------------------------- - void OptimizeFaces(const unsigned int* indexList, unsigned int indexCount, unsigned int vertexCount, unsigned int* newIndexList, unsigned int lruCacheSize); - - namespace - { -#if 0 - // code for computing vertex score was taken, as much as possible - // directly from the original publication. - float ComputeVertexCacheScore(int cachePosition, int vertexCacheSize) - { - const float FindVertexScore_CacheDecayPower = 1.5f; - const float FindVertexScore_LastTriScore = 0.75f; - - float score = 0.0f; - if ( cachePosition < 0 ) - { - // Vertex is not in FIFO cache - no score. - } - else - { - if ( cachePosition < 3 ) - { - // This vertex was used in the last triangle, - // so it has a fixed score, whichever of the three - // it's in. Otherwise, you can get very different - // answers depending on whether you add - // the triangle 1,2,3 or 3,1,2 - which is silly. - score = FindVertexScore_LastTriScore; - } - else - { - assert ( cachePosition < vertexCacheSize ); - // Points for being high in the cache. - const float scaler = 1.0f / ( vertexCacheSize - 3 ); - score = 1.0f - ( cachePosition - 3 ) * scaler; - score = powf ( score, FindVertexScore_CacheDecayPower ); - } - } - - return score; - } -#endif - - float ComputeVertexValenceScore(unsigned int numActiveFaces) - { - const float FindVertexScore_ValenceBoostScale = 2.0f; - const float FindVertexScore_ValenceBoostPower = 0.5f; - - float score = 0.f; - - // Bonus points for having a low number of tris still to - // use the vert, so we get rid of lone verts quickly. - float valenceBoost = powf ( static_cast(numActiveFaces), - -FindVertexScore_ValenceBoostPower ); - score += FindVertexScore_ValenceBoostScale * valenceBoost; - - return score; - } - - - const int kMaxVertexCacheSize = 64; - const unsigned int kMaxPrecomputedVertexValenceScores = 64; - float s_vertexCacheScores[kMaxVertexCacheSize+1][kMaxVertexCacheSize]; - float s_vertexValenceScores[kMaxPrecomputedVertexValenceScores]; - -#if 0 - bool ComputeVertexScores() - { - for (int cacheSize=0; cacheSize<=kMaxVertexCacheSize; ++cacheSize) - { - for (int cachePos=0; cachePos vertexDataList; - vertexDataList.resize(vertexCount); - - // compute face count per vertex - for (unsigned int i=0; i activeFaceList; - - const unsigned int kEvictedCacheIndex = std::numeric_limits::max(); - - { - // allocate face list per vertex - unsigned int curActiveFaceListPos = 0; - for (unsigned int i=0; i processedFaceList; - processedFaceList.resize(indexCount); - - unsigned int vertexCacheBuffer[(kMaxVertexCacheSize+3)*2]; - unsigned int* cache0 = vertexCacheBuffer; - unsigned int* cache1 = vertexCacheBuffer+(kMaxVertexCacheSize+3); - unsigned int entriesInCache0 = 0; - - unsigned int bestFace = 0; - float bestScore = -1.f; - - const float maxValenceScore = FindVertexScore(1, kEvictedCacheIndex, lruCacheSize) * 3.f; - - for (unsigned int i = 0; i < indexCount; i += 3) - { - if (bestScore < 0.f) - { - // no verts in the cache are used by any unprocessed faces so - // search all unprocessed faces for a new starting point - for (unsigned int j = 0; j < indexCount; j += 3) - { - if (processedFaceList[j] == 0) - { - unsigned int face = j; - float faceScore = 0.f; - for (unsigned int k=0; k<3; ++k) - { - unsigned int index = indexList[face+k]; - OptimizeVertexData& vertexData = vertexDataList[index]; - assert(vertexData.activeFaceListSize > 0); - assert(vertexData.cachePos0 >= lruCacheSize); - faceScore += vertexData.score; - } - - if (faceScore > bestScore) - { - bestScore = faceScore; - bestFace = face; - - assert(bestScore <= maxValenceScore); - if (bestScore >= maxValenceScore) - { - break; - } - } - } - } - assert(bestScore >= 0.f); - } - - processedFaceList[bestFace] = 1; - unsigned int entriesInCache1 = 0; - - // add bestFace to LRU cache and to newIndexList - for (unsigned int v = 0; v < 3; ++v) - { - unsigned int index = indexList[bestFace+v]; - newIndexList[i+v] = index; - - OptimizeVertexData& vertexData = vertexDataList[index]; - - if (vertexData.cachePos1 >= entriesInCache1) - { - vertexData.cachePos1 = entriesInCache1; - cache1[entriesInCache1++] = index; - - if (vertexData.activeFaceListSize == 1) - { - --vertexData.activeFaceListSize; - continue; - } - } - - assert(vertexData.activeFaceListSize > 0); - unsigned int* begin = &activeFaceList[vertexData.activeFaceListStart]; - unsigned int* end = &activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize]; - unsigned int* it = std::find(begin, end, bestFace); - assert(it != end); - std::swap(*it, *(end-1)); - --vertexData.activeFaceListSize; - vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize); - - } - - // move the rest of the old verts in the cache down and compute their new scores - for (unsigned int c0 = 0; c0 < entriesInCache0; ++c0) - { - unsigned int index = cache0[c0]; - OptimizeVertexData& vertexData = vertexDataList[index]; - - if (vertexData.cachePos1 >= entriesInCache1) - { - vertexData.cachePos1 = entriesInCache1; - cache1[entriesInCache1++] = index; - vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize); - } - } - - // find the best scoring triangle in the current cache (including up to 3 that were just evicted) - bestScore = -1.f; - for (unsigned int c1 = 0; c1 < entriesInCache1; ++c1) - { - unsigned int index = cache1[c1]; - OptimizeVertexData& vertexData = vertexDataList[index]; - vertexData.cachePos0 = vertexData.cachePos1; - vertexData.cachePos1 = kEvictedCacheIndex; - for (unsigned int j=0; j bestScore) - { - bestScore = faceScore; - bestFace = face; - } - } - } - - std::swap(cache0, cache1); - entriesInCache0 = std::min(entriesInCache1, lruCacheSize); - } - } - -} // namespace Forsyth diff --git a/src/osgPlugins/gles/forsythtriangleorderoptimizer.h b/src/osgPlugins/gles/forsythtriangleorderoptimizer.h deleted file mode 100644 index d0fee89de..000000000 --- a/src/osgPlugins/gles/forsythtriangleorderoptimizer.h +++ /dev/null @@ -1,46 +0,0 @@ -//----------------------------------------------------------------------------- -// This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache -// Optimization" algorithm as described here: -// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html -// -// This code was authored and released into the public domain by -// Adrian Stone (stone@gameangst.com). -// -// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER -// LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#ifndef __FORSYTH_TRIANGLE_REORDER__ -#define __FORSYTH_TRIANGLE_REORDER__ - -namespace Forsyth -{ - //----------------------------------------------------------------------------- - // OptimizeFaces - //----------------------------------------------------------------------------- - // Parameters: - // indexList - // input index list - // indexCount - // the number of indices in the list - // vertexCount - // the largest index value in indexList - // newIndexList - // a pointer to a preallocated buffer the same size as indexList to - // hold the optimized index list - // lruCacheSize - // the size of the simulated post-transform cache (max:64) - //----------------------------------------------------------------------------- - void OptimizeFaces(const unsigned int* indexList, - unsigned int indexCount, - unsigned int vertexCount, - unsigned int* newIndexList, - unsigned int lruCacheSize); - -} // namespace Forsyth - -#endif // __FORSYTH_TRIANGLE_REORDER__ diff --git a/src/osgPlugins/gles/glesUtil b/src/osgPlugins/gles/glesUtil index e313b4f56..1e3b0b388 100644 --- a/src/osgPlugins/gles/glesUtil +++ b/src/osgPlugins/gles/glesUtil @@ -10,10 +10,12 @@ #include #include #include +#include + +#include +#include -#include "TriangleLinePointIndexFunctor" #include "StatLogger" -#include "forsythtriangleorderoptimizer.h" namespace glesUtil { @@ -21,212 +23,6 @@ namespace glesUtil { using namespace osg; typedef std::vector IndexList; - /////////// Post-transform - - // A list of triangles that use a vertex is associated with the Vertex - // structure in another array. - struct Vertex - { - int trisUsing; - size_t triList; // index to start of triangle storage - Vertex() - : trisUsing(0), triList(0) - { - } - }; - typedef vector VertexList; - - struct Triangle - { - unsigned verts[3]; - }; - typedef vector TriangleList; - - struct TriangleCounterOperator - { - VertexList* vertices; - int triangleCount; - TriangleCounterOperator() : vertices(0), triangleCount(0) {} - - void doVertex(unsigned p) - { - if (vertices->size() <= p) - vertices->resize(p + 1); - (*vertices)[p].trisUsing++; - } - - void operator() (unsigned int p1, unsigned int p2, unsigned int p3) - { - if (p1 == p2 || p2 == p3 || p1 == p3) - return; - doVertex(p1); - doVertex(p2); - doVertex(p3); - triangleCount++; - } - }; - - struct TriangleCounter : public TriangleIndexFunctor - { - TriangleCounter(vector* vertices_) - { - vertices = vertices_; - } - }; - - // Initialize the vertex triangle lists and the triangle data structures - struct TriangleAddOperator - { - VertexList* vertices; - TriangleList* triangles; - int triIdx; - TriangleAddOperator() : vertices(0), triangles(0), triIdx(0) {} - - void operator() (unsigned int p1, unsigned int p2, unsigned int p3) - { - if (p1 == p2 || p2 == p3 || p1 == p3) - return; - (*triangles)[triIdx].verts[0] = p1; - (*triangles)[triIdx].verts[1] = p2; - (*triangles)[triIdx].verts[2] = p3; - triIdx++; - } - }; - - struct TriangleAdder : public TriangleIndexFunctor - { - TriangleAdder(VertexList* vertices_, TriangleList* triangles_) - { - vertices = vertices_; - triangles = triangles_; - } - }; - - struct is_not_soup - { - is_not_soup(const VertexList& vertices) : _vertices(vertices) {} - bool operator()(const Triangle &t) - { - return _vertices[t.verts[0]].trisUsing > 1 || - _vertices[t.verts[1]].trisUsing > 1 || - _vertices[t.verts[2]].trisUsing > 1; - } - - VertexList _vertices; - }; - - - class VertexCacheVisitor : osgUtil::VertexCacheVisitor - { - public: - - void optimizeVertices(Geometry& geom) - { - StatLogger logger("glesUtil::VertexCacheVisitor::optimizeVertices(" + geom.getName() + ")"); - - Array* vertArray = geom.getVertexArray(); - if (!vertArray) - return; - unsigned vertArraySize = vertArray->getNumElements(); - // If all the vertices fit in the cache, there's no point in - // doing this optimization. - if (vertArraySize <= 16) - return; - - osg::ref_ptr dummy = new osg::Geometry; - osg::Geometry::PrimitiveSetList newPrims; - - for (int ii = geom.getNumPrimitiveSets() - 1 ; ii >= 0 ; -- ii) { - osg::PrimitiveSet* primitive = geom.getPrimitiveSet(ii); - if(!primitive || !primitive->getNumIndices()) { - continue; - } - - // Collect all 'surface' primitives in the dummy geometry - if(primitive->getMode() >= PrimitiveSet::TRIANGLES && primitive->getDrawElements()) { - dummy->addPrimitiveSet(primitive); - } - else { - newPrims.push_back(primitive); - } - } - - if(!dummy->getNumPrimitiveSets()) { - return; - } - - vector newVertList; - doVertexOptimization(*dummy, newVertList); - - osg::DrawElementsUInt* elements = new DrawElementsUInt(GL_TRIANGLES, newVertList.begin(), newVertList.end()); - if (geom.getUseVertexBufferObjects()) { - elements->setElementBufferObject(new ElementBufferObject); - } - newPrims.insert(newPrims.begin(), elements); - - geom.setPrimitiveSetList(newPrims); - - geom.dirtyDisplayList(); - } - - void doVertexOptimization(Geometry& geom, vector& vertDrawList) - { - Geometry::PrimitiveSetList& primSets = geom.getPrimitiveSetList(); - // lists for all the vertices and triangles - VertexList vertices; - TriangleList triangles; - TriangleCounter triCounter(&vertices); - for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(), - end = primSets.end(); - itr != end; - ++itr) - (*itr)->accept(triCounter); - triangles.resize(triCounter.triangleCount); - // Get total of triangles used by all the vertices - size_t vertTrisSize = 0; - for (VertexList::iterator itr = vertices.begin(), end = vertices.end(); - itr != end; - ++itr) - { - itr->triList = vertTrisSize; - vertTrisSize += itr->trisUsing; - } - // Store for lists of triangles (indices) used by the vertices - vector vertTriListStore(vertTrisSize); - TriangleAdder triAdder(&vertices, &triangles); - for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(), - end = primSets.end(); - itr != end; - ++itr) - (*itr)->accept(triAdder); - - // discard triangle soup as it cannot be cache-optimized - TriangleList::iterator soupIterator = std::partition(triangles.begin(), triangles.end(), - is_not_soup(vertices)); - TriangleList soup(soupIterator, triangles.end()); - triangles.erase(soupIterator, triangles.end()); - OSG_INFO << "Info: glesUtil::VertexCacheVisitor::doVertexOptimization(..) found " - << soup.size() << " soup triangles" << std::endl << std::flush; - - std::vector indices; - for(TriangleList::const_iterator it_tri = triangles.begin() ; it_tri != triangles.end() ; ++ it_tri) { - indices.push_back(it_tri->verts[0]); - indices.push_back(it_tri->verts[1]); - indices.push_back(it_tri->verts[2]); - } - - // call bgfx forsyth-algorithm implementation - vertDrawList.resize(indices.size()); - Forsyth::OptimizeFaces(&indices[0], indices.size(), vertices.size(), &vertDrawList[0], 16); - for(TriangleList::iterator itr = soup.begin() ; itr != soup.end() ; ++ itr) { - vertDrawList.push_back(itr->verts[0]); - vertDrawList.push_back(itr->verts[1]); - vertDrawList.push_back(itr->verts[2]); - } - } - - }; // Post-transform - // A helper class that gathers up all the attribute arrays of an // osg::Geometry object that are BIND_PER_VERTEX and runs an // ArrayVisitor on them. @@ -389,11 +185,13 @@ namespace glesUtil { { ref_ptr newarray = new T(_newsize); T* newptr = newarray.get(); - for (size_t i = 0; i < array.size(); ++i) - if (_remapping[i] != invalidIndex) - (*newptr)[_remapping[i]] = array[i]; - array.swap(*newptr); + for (size_t i = 0; i < _remapping.size(); ++i) { + if (_remapping[i] != invalidIndex) { + (*newptr)[_remapping[i]] = array[i]; + } + } + array.swap(*newptr); } virtual void apply(osg::Array&) {} @@ -410,8 +208,6 @@ namespace glesUtil { virtual void apply(osg::Vec3Array& array) { remap(array); } virtual void apply(osg::Vec4Array& array) { remap(array); } - virtual void apply(osg::Vec4ubArray& array) { remap(array); } - virtual void apply(osg::Vec2bArray& array) { remap(array); } virtual void apply(osg::Vec3bArray& array) { remap(array); } virtual void apply(osg::Vec4bArray& array) { remap(array); } @@ -420,11 +216,28 @@ namespace glesUtil { virtual void apply(osg::Vec3sArray& array) { remap(array); } virtual void apply(osg::Vec4sArray& array) { remap(array); } + virtual void apply(osg::Vec2iArray& array) { remap(array); } + virtual void apply(osg::Vec3iArray& array) { remap(array); } + virtual void apply(osg::Vec4iArray& array) { remap(array); } + virtual void apply(osg::Vec2dArray& array) { remap(array); } virtual void apply(osg::Vec3dArray& array) { remap(array); } virtual void apply(osg::Vec4dArray& array) { remap(array); } + virtual void apply(osg::Vec2ubArray& array) { remap(array); } + virtual void apply(osg::Vec3ubArray& array) { remap(array); } + virtual void apply(osg::Vec4ubArray& array) { remap(array); } + + virtual void apply(osg::Vec2usArray& array) { remap(array); } + virtual void apply(osg::Vec3usArray& array) { remap(array); } + virtual void apply(osg::Vec4usArray& array) { remap(array); } + + virtual void apply(osg::Vec2uiArray& array) { remap(array); } + virtual void apply(osg::Vec3uiArray& array) { remap(array); } + virtual void apply(osg::Vec4uiArray& array) { remap(array); } + virtual void apply(osg::MatrixfArray& array) { remap(array); } + virtual void apply(osg::MatrixdArray& array) { remap(array); } }; @@ -474,16 +287,15 @@ namespace glesUtil { }; - template - inline void reorderDrawElements(DE& drawElements, - const vector& reorder) + inline osg::DrawElementsUInt* reorderDrawElements(PrimitiveSet* primitive, + const vector& reorder) { - for (typename DE::iterator itr = drawElements.begin(), end = drawElements.end(); - itr != end; - ++itr) - { - *itr = static_cast(reorder[*itr]); + osg::DrawElementsUInt* newElements = new osg::DrawElementsUInt(primitive->getMode()); + for (unsigned int i = 0 ; i < primitive->getNumIndices() ; ++ i) { + newElements->addElement(static_cast(reorder[primitive->index(i)])); } + newElements->setUserDataContainer(primitive->getUserDataContainer()); + return newElements; } @@ -491,10 +303,10 @@ namespace glesUtil { { struct OrderByPrimitiveMode { - inline bool operator() (const osg::ref_ptr& prim1, const osg::ref_ptr& prim2) + inline bool operator() (const osg::ref_ptr prim1, const osg::ref_ptr prim2) { if(prim1.get() && prim2.get()) { - return prim1->getMode() >= prim2->getMode(); + return prim1->getMode() > prim2->getMode(); } else if(prim1.get()) { return true; @@ -504,6 +316,19 @@ namespace glesUtil { } order_by_primitive_mode; public: + void remapTargetVertices(Remapper remapper, Geometry& geom) + { + if(osgAnimation::MorphGeometry *morphGeometry = dynamic_cast(&geom)) { + osgAnimation::MorphGeometry::MorphTargetList targetList = morphGeometry->getMorphTargetList(); + for (osgAnimation::MorphGeometry::MorphTargetList::iterator ti = targetList.begin(); ti != targetList.end(); ++ti) { + osgAnimation::MorphGeometry::MorphTarget *morphTarget = &(*ti); + osg::Geometry *target = morphTarget->getGeometry(); + GeometryArrayGatherer gatherer(*target); + gatherer.accept(remapper); + } + } + } + void optimizeOrder(Geometry& geom) { StatLogger logger("glesUtil::VertexAccessOrderVisitor::optimizeOrder(" + geom.getName() + ")"); @@ -541,27 +366,21 @@ namespace glesUtil { Remapper remapper(vr.remap); gatherer.accept(remapper); + + //Remap morphGeometry target + remapTargetVertices(remapper, geom); + + Geometry::PrimitiveSetList newPrimitives; for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(), end = primSets.end(); itr != end; ++itr) { PrimitiveSet* ps = itr->get(); - switch (ps->getType()) - { - case PrimitiveSet::DrawElementsUBytePrimitiveType: - reorderDrawElements(*static_cast(ps), vr.remap); - break; - case PrimitiveSet::DrawElementsUShortPrimitiveType: - reorderDrawElements(*static_cast(ps), vr.remap); - break; - case PrimitiveSet::DrawElementsUIntPrimitiveType: - reorderDrawElements(*static_cast(ps), vr.remap); - break; - default: - break; - } + newPrimitives.push_back(reorderDrawElements(ps, vr.remap)); } + geom.setPrimitiveSetList(newPrimitives); + // deduplicate UVs array that were only shared within the geometry deduplicator.deduplicateUVs(geom);