Merge pull request #105 from marchelbling/compiler-friendly-gles

Moves gles visitors implementation in cpp files
This commit is contained in:
OpenSceneGraph git repository 2016-07-13 09:46:19 +01:00 committed by GitHub
commit 0faebbef33
26 changed files with 2781 additions and 2442 deletions

View File

@ -35,234 +35,33 @@ public:
typedef std::vector<osgAnimation::Bone*> BoneList; typedef std::vector<osgAnimation::Bone*> BoneList;
typedef std::vector<osgAnimation::RigGeometry*> RigGeometryList; typedef std::vector<osgAnimation::RigGeometry*> RigGeometryList;
ComputeAABBOnBoneVisitor(bool createGeometry): ComputeAABBOnBoneVisitor(bool createGeometry):
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_root(0), _root(0),
_createGeometry(createGeometry) _createGeometry(createGeometry)
{} {}
void apply(osg::Transform& node) { void apply(osg::Transform&);
if(!_root) void apply(osg::Geometry&);
_root = dynamic_cast<osgAnimation::Skeleton*>(&node); void apply(osgAnimation::Bone &);
void apply(osgAnimation::RigGeometry &);
osgAnimation::Bone * b = dynamic_cast<osgAnimation::Bone*>(&node); void computeBoundingBoxOnBones();
if(b) apply(*b);
traverse(node);
}
void apply(osg::Geometry& geometry) {
if(osgAnimation::RigGeometry *rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry)) {
apply(*rigGeometry);
}
}
void apply(osgAnimation::Bone &bone) {
_bones.push_back(&bone);
}
void apply(osgAnimation::RigGeometry &rig) {
_rigGeometries.push_back(&rig);
}
void computeBoundingBoxOnBones() {
//Perform Updates
updateBones();
updateRigGeometries();
//We have our T pose, we can compute an AABB for each bone
for (BoneList::iterator bone = _bones.begin(); bone != _bones.end(); ++ bone) {
osg::BoundingBox bb;
//For each geometry
for (RigGeometryList::iterator iterator = _rigGeometries.begin(); iterator != _rigGeometries.end(); ++ iterator) {
osgAnimation::RigGeometry* rigGeometry = *iterator;
if(!rigGeometry) continue;
osg::Matrix mtxLocalToSkl = rigGeometry->getWorldMatrices(_root).at(0);
//For each Vertex influence
osgAnimation::VertexInfluenceMap * infMap = rigGeometry->getInfluenceMap();
osgAnimation::VertexInfluenceMap::iterator itMap = infMap->find((*bone)->getName());
if(itMap == infMap->end()) continue;
osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array*>(rigGeometry->getVertexArray());
if(!vertices) continue;
osgAnimation::VertexInfluence vxtInf = (*itMap).second;
//Expand the boundingBox with each vertex
for(unsigned int j = 0; j < vxtInf.size(); j++) {
if(vxtInf.at(j).second < 10e-2) continue; //Skip vertex if low weight
osg::Vec3 vx = vertices->at(vxtInf.at(j).first);
vx = vx * mtxLocalToSkl;
bb.expandBy(vx);
}
// Compare initial and actual boundingBox if (no change) => no box on bone
if(bb == osg::BoundingBox() || bb._min.x() == bb._max.x() || bb._min.y() == bb._max.y() || bb._min.z() == bb._max.z()) {
continue;
}
osg::Matrix worldToBone = osg::Matrix::inverse((*bone)->getWorldMatrices(_root).at(0));
if(_createGeometry) {
osg::Geode *g = new osg::Geode;
g->setName("AABB_for_bone_" + (*bone)->getName());
g->addDrawable(createBox(bb, worldToBone));
(*bone)->addChild(g);
}
serializeBoundingBox(bb, worldToBone, *(*bone));
}
}
//Clear geometries
for (RigGeometryList::iterator iterator = _rigGeometries.begin(); iterator != _rigGeometries.end(); ++ iterator) {
osgAnimation::RigGeometry* rigGeometry = *iterator;
if(rigGeometry) {
rigGeometry->copyFrom(*rigGeometry->getSourceGeometry());
rigGeometry->setRigTransformImplementation(0);
}
}
}
protected: protected:
osg::Geometry* createBox(const osg::BoundingBox &bb, const osg::Matrix &transform, osg::Geometry* createBox(const osg::BoundingBox &,
float ratio=1.0, osg::Vec4 color=osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)) { const osg::Matrix &,
osg::Geometry *cube = new osg::Geometry; float /*ratio*/=1.0,
osg::Vec4 /*color*/=osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));
osg::Vec3 center = bb.center(); void serializeBoundingBox(const osg::BoundingBox &,
double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50; const osg::Matrix &,
double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50; osgAnimation::Bone &,
double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50; float /*ratio*/=1.0);
halfLenghtX *= ratio; void updateBones();
halfLenghtY *= ratio; void updateRigGeometries();
halfLenghtZ *= ratio;
osg::Vec3Array *cubeVertices = new osg::Vec3Array;
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
cube->setVertexArray(cubeVertices);
{
osg::DrawElementsUInt* up = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
up->push_back(4);
up->push_back(5);
up->push_back(1);
up->push_back(0);
cube->addPrimitiveSet(up);
}
{
osg::DrawElementsUInt* down = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
down->push_back(2);
down->push_back(6);
down->push_back(7);
down->push_back(3);
cube->addPrimitiveSet(down);
}
{
osg::DrawElementsUInt* left = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
left->push_back(2);
left->push_back(3);
left->push_back(0);
left->push_back(1);
cube->addPrimitiveSet(left);
}
{
osg::DrawElementsUInt* right = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
right->push_back(7);
right->push_back(6);
right->push_back(5);
right->push_back(4);
cube->addPrimitiveSet(right);
}
{
osg::DrawElementsUInt* front = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
front->push_back(3);
front->push_back(7);
front->push_back(4);
front->push_back(0);
cube->addPrimitiveSet(front);
}
{
osg::DrawElementsUInt* back = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
back->push_back(6);
back->push_back(2);
back->push_back(1);
back->push_back(5);
cube->addPrimitiveSet(back);
}
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
cube->setColorArray(colors);
cube->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
return cube;
}
void serializeBoundingBox(const osg::BoundingBox &bb, const osg::Matrix &transform, osgAnimation::Bone &b, float ratio = 1.0) {
osg::Vec3 center = bb.center();
double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50;
double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50;
double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50;
halfLenghtX *= ratio;
halfLenghtY *= ratio;
halfLenghtZ *= ratio;
osg::BoundingBox serializedBB;
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
b.setUserValue("AABBonBone_min", serializedBB._min);
b.setUserValue("AABBonBone_max", serializedBB._max);
}
void updateBones() {
osgUtil::UpdateVisitor update;
_root->accept(update);
}
void updateRigGeometries() {
for (unsigned int i = 0, size = _rigGeometries.size(); i < size; i++) {
osgAnimation::RigGeometry * rig = _rigGeometries.at(i);
osg::Drawable::UpdateCallback * callback = dynamic_cast<osg::Drawable::UpdateCallback*>(rig->getUpdateCallback());
if(callback) {
callback->update(0, rig);
}
}
}
protected:
BoneList _bones; BoneList _bones;
RigGeometryList _rigGeometries; RigGeometryList _rigGeometries;
osgAnimation::Skeleton *_root; osgAnimation::Skeleton *_root;

View File

@ -0,0 +1,235 @@
#include "AABBonBoneVisitor"
void ComputeAABBOnBoneVisitor::apply(osg::Transform& node) {
if(!_root)
_root = dynamic_cast<osgAnimation::Skeleton*>(&node);
osgAnimation::Bone * b = dynamic_cast<osgAnimation::Bone*>(&node);
if(b) apply(*b);
traverse(node);
}
void ComputeAABBOnBoneVisitor::apply(osg::Geometry& geometry) {
if(osgAnimation::RigGeometry *rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry)) {
apply(*rigGeometry);
}
}
void ComputeAABBOnBoneVisitor::apply(osgAnimation::Bone &bone) {
_bones.push_back(&bone);
}
void ComputeAABBOnBoneVisitor::apply(osgAnimation::RigGeometry &rig) {
_rigGeometries.push_back(&rig);
}
void ComputeAABBOnBoneVisitor::computeBoundingBoxOnBones() {
//Perform Updates
updateBones();
updateRigGeometries();
//We have our T pose, we can compute an AABB for each bone
for (BoneList::iterator bone = _bones.begin(); bone != _bones.end(); ++ bone) {
osg::BoundingBox bb;
//For each geometry
for (RigGeometryList::iterator iterator = _rigGeometries.begin(); iterator != _rigGeometries.end(); ++ iterator) {
osgAnimation::RigGeometry* rigGeometry = *iterator;
if(!rigGeometry) continue;
osg::Matrix mtxLocalToSkl = rigGeometry->getWorldMatrices(_root).at(0);
//For each Vertex influence
osgAnimation::VertexInfluenceMap * infMap = rigGeometry->getInfluenceMap();
osgAnimation::VertexInfluenceMap::iterator itMap = infMap->find((*bone)->getName());
if(itMap == infMap->end()) continue;
osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array*>(rigGeometry->getVertexArray());
if(!vertices) continue;
osgAnimation::VertexInfluence vxtInf = (*itMap).second;
//Expand the boundingBox with each vertex
for(unsigned int j = 0; j < vxtInf.size(); j++) {
if(vxtInf.at(j).second < 10e-2) continue; //Skip vertex if low weight
osg::Vec3 vx = vertices->at(vxtInf.at(j).first);
vx = vx * mtxLocalToSkl;
bb.expandBy(vx);
}
// Compare initial and actual boundingBox if (no change) => no box on bone
if(bb == osg::BoundingBox() || bb._min.x() == bb._max.x() || bb._min.y() == bb._max.y() || bb._min.z() == bb._max.z()) {
continue;
}
osg::Matrix worldToBone = osg::Matrix::inverse((*bone)->getWorldMatrices(_root).at(0));
if(_createGeometry) {
osg::Geode *g = new osg::Geode;
g->setName("AABB_for_bone_" + (*bone)->getName());
g->addDrawable(createBox(bb, worldToBone));
(*bone)->addChild(g);
}
serializeBoundingBox(bb, worldToBone, *(*bone));
}
}
//Clear geometries
for (RigGeometryList::iterator iterator = _rigGeometries.begin(); iterator != _rigGeometries.end(); ++ iterator) {
osgAnimation::RigGeometry* rigGeometry = *iterator;
if(rigGeometry) {
rigGeometry->copyFrom(*rigGeometry->getSourceGeometry());
rigGeometry->setRigTransformImplementation(0);
}
}
}
osg::Geometry* ComputeAABBOnBoneVisitor::createBox(const osg::BoundingBox &bb,
const osg::Matrix &transform,
float ratio,
osg::Vec4 color) {
osg::Geometry *cube = new osg::Geometry;
osg::Vec3 center = bb.center();
double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50;
double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50;
double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50;
halfLenghtX *= ratio;
halfLenghtY *= ratio;
halfLenghtZ *= ratio;
osg::Vec3Array *cubeVertices = new osg::Vec3Array;
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
cube->setVertexArray(cubeVertices);
{
osg::DrawElementsUInt* up = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
up->push_back(4);
up->push_back(5);
up->push_back(1);
up->push_back(0);
cube->addPrimitiveSet(up);
}
{
osg::DrawElementsUInt* down = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
down->push_back(2);
down->push_back(6);
down->push_back(7);
down->push_back(3);
cube->addPrimitiveSet(down);
}
{
osg::DrawElementsUInt* left = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
left->push_back(2);
left->push_back(3);
left->push_back(0);
left->push_back(1);
cube->addPrimitiveSet(left);
}
{
osg::DrawElementsUInt* right = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
right->push_back(7);
right->push_back(6);
right->push_back(5);
right->push_back(4);
cube->addPrimitiveSet(right);
}
{
osg::DrawElementsUInt* front = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
front->push_back(3);
front->push_back(7);
front->push_back(4);
front->push_back(0);
cube->addPrimitiveSet(front);
}
{
osg::DrawElementsUInt* back = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
back->push_back(6);
back->push_back(2);
back->push_back(1);
back->push_back(5);
cube->addPrimitiveSet(back);
}
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
colors->push_back(color);
cube->setColorArray(colors);
cube->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
return cube;
}
void ComputeAABBOnBoneVisitor::serializeBoundingBox(const osg::BoundingBox &bb,
const osg::Matrix &transform,
osgAnimation::Bone &b,
float ratio) {
osg::Vec3 center = bb.center();
double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50;
double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50;
double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50;
halfLenghtX *= ratio;
halfLenghtY *= ratio;
halfLenghtZ *= ratio;
osg::BoundingBox serializedBB;
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
b.setUserValue("AABBonBone_min", serializedBB._min);
b.setUserValue("AABBonBone_max", serializedBB._max);
}
void ComputeAABBOnBoneVisitor::updateBones() {
osgUtil::UpdateVisitor update;
_root->accept(update);
}
void ComputeAABBOnBoneVisitor::updateRigGeometries() {
for (unsigned int i = 0, size = _rigGeometries.size(); i < size; i++) {
osgAnimation::RigGeometry * rig = _rigGeometries.at(i);
osg::Drawable::UpdateCallback * callback = dynamic_cast<osg::Drawable::UpdateCallback*>(rig->getUpdateCallback());
if(callback) {
callback->update(0, rig);
}
}
}

View File

@ -13,7 +13,6 @@
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osg/NodeVisitor> #include <osg/NodeVisitor>
#include <osg/Geode> #include <osg/Geode>
#include <osg/ValueObject>
#include <osgAnimation/UpdateMatrixTransform> #include <osgAnimation/UpdateMatrixTransform>
#include <osgAnimation/BasicAnimationManager> #include <osgAnimation/BasicAnimationManager>
@ -30,32 +29,7 @@
#include <osgAnimation/RigTransformSoftware> #include <osgAnimation/RigTransformSoftware>
#include "StatLogger" #include "StatLogger"
#include "glesUtil"
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<const osg::Vec4Array*>(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 { class HasGeometryVisitor : public osg::NodeVisitor {
@ -93,559 +67,54 @@ public:
{} {}
void apply(osg::Node& node) { void apply(osg::Node&);
osgAnimation::BasicAnimationManager* manager = getCallbackType<osgAnimation::BasicAnimationManager>(node.getUpdateCallback()); void apply(osg::MatrixTransform&);
if(manager) { void apply(osg::Geometry&);
_managers[manager] = osg::ref_ptr<osg::Node>(&node);
collectAnimationChannels(*manager);
}
collectUpdateCallback(node); void collectUpdateCallback(osg::Node&);
void collectAnimationChannels(osgAnimation::BasicAnimationManager&);
traverse(node); virtual void clean();
} void removeAnimation();
void apply(osg::MatrixTransform& transform) { void cleanInvalidMorphGeometries();
HasGeometryVisitor hasData; void cleanInvalidRigGeometries();
transform.traverse(hasData); void cleanUnusedMorphTarget();
if(!hasData.geometry) { void cleanInvalidUpdateMorph();
// if animation transforms have no child geometry they are cleanable
osgAnimation::Skeleton* skeleton = dynamic_cast<osgAnimation::Skeleton*>(&transform);
osgAnimation::Bone* bone = dynamic_cast<osgAnimation::Bone*>(&transform);
if(skeleton) {
_transforms.push_back(osg::ref_ptr<osgAnimation::Skeleton>(skeleton));
}
if(bone) {
_transforms.push_back(osg::ref_ptr<osgAnimation::Bone>(bone));
}
}
osgAnimation::UpdateMatrixTransform* update = getCallbackType<osgAnimation::UpdateMatrixTransform>(transform.getUpdateCallback());
if(update) {
_updates[update] = osg::ref_ptr<osg::Node>(&transform);
}
traverse(transform);
}
void apply(osg::Geometry& geometry) {
osgAnimation::MorphGeometry* morphGeometry = 0;
osgAnimation::RigGeometry* rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry);
if(rigGeometry) {
if(std::find(_rigGeometries.begin(), _rigGeometries.end(), rigGeometry) == _rigGeometries.end()) {
_rigGeometries.push_back(rigGeometry);
}
morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(rigGeometry->getSourceGeometry());
if(morphGeometry) {
_morphGeometries[morphGeometry] = rigGeometry;
}
}
else {
morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(&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<BaseAnimationUpdateCallback>(callBack);
if(update) {
_updates[update] = osg::ref_ptr<osg::Node>(&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<std::string, osgAnimation::Channel*>((*channel)->getTargetName(), channel->get()));
}
}
}
}
}
virtual void clean() {
// 1. clean scene graph to only keep 'valid' objects (top to bottom):
// * BasicAnimationManager
// * Animation
// * Target
// * deduplicate successive identical KeyFrames
// 2. check if there is still valid animation data in the scene graph.
// If no valid BasicAnimationManager is left, then clean all collected animation data.
if(_managers.size() == 0) {
OSG_WARN << "Monitor: animation.no_animation_manager" << std::endl;
}
else if(_managers.size() > 1) {
OSG_WARN << "Monitor: animation.multiple_animation_manager" << std::endl;
}
else {
OSG_WARN << "Monitor: animation.single_animation_manager" << std::endl;
}
// only keep animations if we have a single animation manager
bool keepAnimations = _managers.size() == 1;
cleanUnusedMorphTarget();
cleanInvalidUpdateMorph();
for(BasicAnimationManagerMap::iterator manager = _managers.begin() ; keepAnimations && manager != _managers.end() ; ++ manager) {
cleanAnimations(*manager->first);
if(!isValidAnimationManager(*manager->first)) {
if(manager->second.valid()) {
manager->second->removeUpdateCallback(manager->first.get());
}
keepAnimations = false;
OSG_WARN << "No valid animation data found. Removing all animation objects" << std::endl;
OSG_WARN << "Monitor: animation.disable_animation" << std::endl;
}
}
if(!keepAnimations) {
removeAnimation();
}
else
{
cleanInvalidMorphGeometries();
cleanInvalidRigGeometries();
}
}
void removeAnimation() {
// * bake rig
// * replace animation geometries by static geometries
// * remove animation callbacks
// * remove animation transforms
bakeRigInitialPose();
removeAnimatedGeometries();
removeAnimationUpdateCallbacks();
removeAnimationTransforms();
}
void cleanInvalidMorphGeometries() {
// Replace morph geometries by static geometries if:
// * empty morph target list
for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ) {
if(morphGeometry->first.valid()) {
if(morphGeometry->first.get()->getMorphTargetList().size() == 0) {
OSG_WARN << "Monitor: animation.invalid_morphgeometry" << std::endl;
replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second);
_morphGeometries.erase(morphGeometry ++);
}
else {
++ morphGeometry;
}
}
}
}
void cleanInvalidRigGeometries() {
// Replace rig geometries by static geometries if:
// * empty or inexistant vertex influence map
// * no *strictly* positive influence coefficient
for(RigGeometryList::iterator iterator = _rigGeometries.begin() ; iterator != _rigGeometries.end() ; ) {
osg::ref_ptr<osgAnimation::RigGeometry> rigGeometry = *iterator;
if(rigGeometry.valid() && !hasPositiveWeights(rigGeometry->getSourceGeometry())) {
OSG_WARN << "Monitor: animation.invalid_riggeometry" << std::endl;
replaceRigGeometryBySource(*rigGeometry.get());
iterator = _rigGeometries.erase(iterator);
}
else {
++ iterator;
}
}
}
void cleanUnusedMorphTarget() {
// Removes MorphGeometry targets not updated by any channel
std::set<std::string> 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<osgAnimation::UpdateMorph*>(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<osgAnimation::UpdateMorph*>(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: protected:
void warn(const std::string& method, const std::string& label, const osgAnimation::Channel& channel, const std::string& message) const { void warn(const std::string&, const std::string&, const osgAnimation::Channel&, const std::string&) const;
OSG_WARN << std::flush << "Warning: " << "[" << method << "] " << "[[" << label << "]] "
<< "Channel '" << channel.getName() << "' with target '" << channel.getTargetName()
<< " '" << message << std::endl;
}
template<typename T> template<typename T>
T* getCallbackType(osg::Callback* callback) { T* getCallbackType(osg::Callback*);
if(!callback) return 0;
T* callback_type = dynamic_cast<T*>(callback); void cleanAnimations(osgAnimation::BasicAnimationManager&);
if(callback_type) { void cleanAnimation(osgAnimation::Animation&);
return callback_type; void cleanChannel(osgAnimation::Channel&);
} bool isValidAnimationManager(const osgAnimation::BasicAnimationManager&) const;
bool isValidAnimation(const osgAnimation::Animation&) const;
bool isValidChannel(const osgAnimation::Channel&) const;
return getCallbackType<T>(callback->getNestedCallback()); const osgAnimation::StackedTransformElement* getStackedElement(const osgAnimation::StackedTransform&, const std::string&) const;
} bool isChannelEqualToStackedTransform(const osgAnimation::Channel&, const osgAnimation::UpdateMatrixTransform*) const;
void cleanAnimations(osgAnimation::BasicAnimationManager& manager) {
// remove manager's invalid animations
osgAnimation::AnimationList& animations = manager.getAnimationList();
std::vector<osgAnimation::Animation*> 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<osgAnimation::Animation*>::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<osgAnimation::UpdateMorph*>(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<osgAnimation::UpdateMatrixTransform*>(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<const osgAnimation::StackedTranslateElement*>(element);
osg::Vec3 value(0., 0., 0.);
if(translation) {
value = translation->getTranslate();
}
return isChannelEqualToStackedTransform(dynamic_cast<const osgAnimation::Vec3LinearChannel*>(&channel), value);
}
else if(channel.getName() == "scale") {
const osgAnimation::StackedScaleElement* scale = dynamic_cast<const osgAnimation::StackedScaleElement*>(element);
osg::Vec3 value(1., 1., 1.);
if(scale) {
value = scale->getScale();
}
return isChannelEqualToStackedTransform(dynamic_cast<const osgAnimation::Vec3LinearChannel*>(&channel), value);
}
else if(channel.getName() == "rotate") {
const osgAnimation::StackedQuaternionElement* rotation = dynamic_cast<const osgAnimation::StackedQuaternionElement*>(element);
osg::Quat value(0., 0., 0., 1.);
if(rotation) {
value = rotation->getQuaternion();
}
return isChannelEqualToStackedTransform(dynamic_cast<const osgAnimation::QuatSphericalLinearChannel*>(&channel), value);
}
return false;
}
template<typename T, typename V> template<typename T, typename V>
bool isChannelEqualToStackedTransform(const T* channel, const V& value) const { bool isChannelEqualToStackedTransform(const T*, const V&) 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<AnimationUpdateCallBackMap, osg::NodeCallback>(_updates);
removeUpdateCallbacksTemplate<BasicAnimationManagerMap, osgAnimation::BasicAnimationManager>(_managers);
}
void removeAnimationUpdateCallbacks();
template<typename C, typename T> template<typename C, typename T>
void removeUpdateCallbacksTemplate(C& callbackNodes) { void removeUpdateCallbacksTemplate(C&);
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<T>(node->getUpdateCallback());
}
while(callback);
}
}
}
void removeAnimationTransforms() { void removeAnimationTransforms();
for(MatrixTransformList::iterator transform = _transforms.begin() ; transform != _transforms.end() ; ++ transform) { void removeAnimatedGeometries();
if(transform->valid()) { void removeFromParents(osg::Node*);
removeFromParents(transform->get());
}
}
}
void removeAnimatedGeometries() { void replaceRigGeometryBySource(osgAnimation::RigGeometry&) const;
for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ++ morphGeometry) { void replaceMorphGeometryByGeometry(osgAnimation::MorphGeometry&, osgAnimation::RigGeometry* /*rigGeometry*/=0) const;
if(morphGeometry->first.valid()) { void replaceAnimatedGeometryByStaticGeometry(osg::Geometry*, osg::Geometry*) const;
replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second);
}
}
for(RigGeometryList::iterator rigGeometry = _rigGeometries.begin() ; rigGeometry != _rigGeometries.end() ; ++ rigGeometry) { void bakeRigInitialPose();
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<osgAnimation::MorphGeometry*>(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<osg::Geometry*>(rigGeometry->clone(osg::CopyOp::DEEP_COPY_ALL));
rigGeometry->setSourceGeometry(baked);
}
}
protected: protected:
BasicAnimationManagerMap _managers; BasicAnimationManagerMap _managers;

View File

@ -0,0 +1,589 @@
#include "AnimationCleanerVisitor"
void AnimationCleanerVisitor::apply(osg::Node& node) {
osgAnimation::BasicAnimationManager* manager = getCallbackType<osgAnimation::BasicAnimationManager>(node.getUpdateCallback());
if(manager) {
_managers[manager] = osg::ref_ptr<osg::Node>(&node);
collectAnimationChannels(*manager);
}
collectUpdateCallback(node);
traverse(node);
}
void AnimationCleanerVisitor::apply(osg::MatrixTransform& transform) {
HasGeometryVisitor hasData;
transform.traverse(hasData);
if(!hasData.geometry) {
// if animation transforms have no child geometry they are cleanable
osgAnimation::Skeleton* skeleton = dynamic_cast<osgAnimation::Skeleton*>(&transform);
osgAnimation::Bone* bone = dynamic_cast<osgAnimation::Bone*>(&transform);
if(skeleton) {
_transforms.push_back(osg::ref_ptr<osgAnimation::Skeleton>(skeleton));
}
if(bone) {
_transforms.push_back(osg::ref_ptr<osgAnimation::Bone>(bone));
}
}
osgAnimation::UpdateMatrixTransform* update = getCallbackType<osgAnimation::UpdateMatrixTransform>(transform.getUpdateCallback());
if(update) {
_updates[update] = osg::ref_ptr<osg::Node>(&transform);
}
traverse(transform);
}
void AnimationCleanerVisitor::apply(osg::Geometry& geometry) {
osgAnimation::MorphGeometry* morphGeometry = 0;
osgAnimation::RigGeometry* rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry);
if(rigGeometry) {
if(std::find(_rigGeometries.begin(), _rigGeometries.end(), rigGeometry) == _rigGeometries.end()) {
_rigGeometries.push_back(rigGeometry);
}
morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(rigGeometry->getSourceGeometry());
if(morphGeometry) {
_morphGeometries[morphGeometry] = rigGeometry;
}
}
else {
morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(&geometry);
if(morphGeometry && _morphGeometries.count(morphGeometry) == 0) {
_morphGeometries[morphGeometry] = 0;
}
}
if(morphGeometry) {
typedef osgAnimation::MorphGeometry::MorphTargetList MorphTargetList;
MorphTargetList morphTargetList = morphGeometry->getMorphTargetList();
for(MorphTargetList::iterator morphTarget = morphTargetList.begin(); morphTarget != morphTargetList.end(); ++morphTarget) {
osgAnimation::MorphGeometry::MorphTarget& target = *morphTarget;
_morphTargets[target.getGeometry()->getName()] = morphGeometry;
}
}
}
void AnimationCleanerVisitor::collectUpdateCallback(osg::Node& node) {
osg::Callback *callBack = node.getUpdateCallback();
while(callBack) {
BaseAnimationUpdateCallback* update = getCallbackType<BaseAnimationUpdateCallback>(callBack);
if(update) {
_updates[update] = osg::ref_ptr<osg::Node>(&node);
}
callBack = callBack->getNestedCallback();
}
}
void AnimationCleanerVisitor::collectAnimationChannels(osgAnimation::BasicAnimationManager& manager) {
osgAnimation::AnimationList& animations = manager.getAnimationList();
for(osgAnimation::AnimationList::iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) {
if(animation->valid()) {
osgAnimation::ChannelList& channels = (*animation)->getChannels();
for(osgAnimation::ChannelList::iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) {
if(channel->valid()) {
_channels.push_back(std::pair<std::string, osgAnimation::Channel*>((*channel)->getTargetName(), channel->get()));
}
}
}
}
}
void AnimationCleanerVisitor::clean() {
// 1. clean scene graph to only keep 'valid' objects (top to bottom):
// * BasicAnimationManager
// * Animation
// * Target
// * deduplicate successive identical KeyFrames
// 2. check if there is still valid animation data in the scene graph.
// If no valid BasicAnimationManager is left, then clean all collected animation data.
if(_managers.size() == 0) {
OSG_WARN << "Monitor: animation.no_animation_manager" << std::endl;
}
else if(_managers.size() > 1) {
OSG_WARN << "Monitor: animation.multiple_animation_manager" << std::endl;
}
else {
OSG_WARN << "Monitor: animation.single_animation_manager" << std::endl;
}
// only keep animations if we have a single animation manager
bool keepAnimations = _managers.size() == 1;
cleanUnusedMorphTarget();
cleanInvalidUpdateMorph();
for(BasicAnimationManagerMap::iterator manager = _managers.begin() ; keepAnimations && manager != _managers.end() ; ++ manager) {
cleanAnimations(*manager->first);
if(!isValidAnimationManager(*manager->first)) {
if(manager->second.valid()) {
manager->second->removeUpdateCallback(manager->first.get());
}
keepAnimations = false;
OSG_WARN << "No valid animation data found. Removing all animation objects" << std::endl;
OSG_WARN << "Monitor: animation.disable_animation" << std::endl;
}
}
if(!keepAnimations) {
removeAnimation();
}
else
{
cleanInvalidMorphGeometries();
cleanInvalidRigGeometries();
}
}
void AnimationCleanerVisitor::removeAnimation() {
// * bake rig
// * replace animation geometries by static geometries
// * remove animation callbacks
// * remove animation transforms
bakeRigInitialPose();
removeAnimatedGeometries();
removeAnimationUpdateCallbacks();
removeAnimationTransforms();
}
void AnimationCleanerVisitor::cleanInvalidMorphGeometries() {
// Replace morph geometries by static geometries if:
// * empty morph target list
for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ) {
if(morphGeometry->first.valid()) {
if(morphGeometry->first.get()->getMorphTargetList().size() == 0) {
OSG_WARN << "Monitor: animation.invalid_morphgeometry" << std::endl;
replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second);
_morphGeometries.erase(morphGeometry ++);
}
else {
++ morphGeometry;
}
}
}
}
void AnimationCleanerVisitor::cleanInvalidRigGeometries() {
// Replace rig geometries by static geometries if:
// * empty or inexistant vertex influence map
// * no *strictly* positive influence coefficient
for(RigGeometryList::iterator iterator = _rigGeometries.begin() ; iterator != _rigGeometries.end() ; ) {
osg::ref_ptr<osgAnimation::RigGeometry> rigGeometry = *iterator;
if(rigGeometry.valid() && !glesUtil::hasPositiveWeights(rigGeometry->getSourceGeometry())) {
OSG_WARN << "Monitor: animation.invalid_riggeometry" << std::endl;
replaceRigGeometryBySource(*rigGeometry.get());
iterator = _rigGeometries.erase(iterator);
}
else {
++ iterator;
}
}
}
void AnimationCleanerVisitor::cleanUnusedMorphTarget() {
// Removes MorphGeometry targets not updated by any channel
std::set<std::string> kept, removed;
for(NameMorphMap::iterator targetMorph = _morphTargets.begin() ; targetMorph != _morphTargets.end() ; ) {
const std::string& target = targetMorph->first;
unsigned int count = 0;
for(TargetChannelList::const_iterator targetChannel = _channels.begin() ; targetChannel != _channels.end() ; ++ targetChannel) {
if(targetChannel->first == target) {
++ count;
}
}
if(count == 0) {
removed.insert(target);
targetMorph->second->removeMorphTarget(target);
_morphTargets.erase(targetMorph ++);
}
else {
kept.insert(target);
++ targetMorph;
}
}
if(!removed.empty()) {
OSG_WARN << "Monitor: animation.unused_morphtarget" << std::endl;
for(TargetChannelList::iterator targetChannel = _channels.begin() ; targetChannel != _channels.end() ; ) {
std::string target = targetChannel->first;
if(removed.find(target) != removed.end()) {
_channels.erase(targetChannel ++);
}
else {
if(kept.find(target) != kept.end()) {
// update target channel names with the (possibly) new target index in MophTargetList
osgAnimation::MorphGeometry& morphGeometry = *_morphTargets[target];
const osgAnimation::MorphGeometry::MorphTargetList& targets = morphGeometry.getMorphTargetList();
for(unsigned int i = 0 ; i < targets.size() ; ++ i) {
if(targets[i].getGeometry()->getName() == target) {
std::ostringstream oss;
oss << i;
targetChannel->second->setName(oss.str());
}
}
}
++ targetChannel;
}
}
}
}
void AnimationCleanerVisitor::cleanInvalidUpdateMorph() {
// Removes unused UpdateMorph targets (i.e. name does not match any MorphGeometry target)
for(AnimationUpdateCallBackMap::iterator update = _updates.begin() ; update != _updates.end() ; ++ update) {
osgAnimation::UpdateMorph *updateMorph = dynamic_cast<osgAnimation::UpdateMorph*>(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<osgAnimation::UpdateMorph*>(update->first.get());
if(!updateMorph || updateMorph->getNumTarget() != 0) {
++ update;
}
else {
osg::Callback *callBack = update->second.get()->getUpdateCallback();
if(callBack) {
if(callBack == updateMorph)
update->second.get()->setUpdateCallback(callBack->getNestedCallback());
else
callBack->removeNestedCallback(updateMorph);
}
_updates.erase(update ++);
}
}
}
void AnimationCleanerVisitor::warn(const std::string& method,
const std::string& label,
const osgAnimation::Channel& channel,
const std::string& message) const {
OSG_WARN << std::flush << "Warning: " << "[" << method << "] " << "[[" << label << "]] "
<< "Channel '" << channel.getName() << "' with target '" << channel.getTargetName()
<< " '" << message << std::endl;
}
template<typename T>
T* AnimationCleanerVisitor::getCallbackType(osg::Callback* callback) {
if(!callback) return 0;
T* callback_type = dynamic_cast<T*>(callback);
if(callback_type) {
return callback_type;
}
return getCallbackType<T>(callback->getNestedCallback());
}
void AnimationCleanerVisitor::cleanAnimations(osgAnimation::BasicAnimationManager& manager) {
// remove manager's invalid animations
osgAnimation::AnimationList& animations = manager.getAnimationList();
std::vector<osgAnimation::Animation*> 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<osgAnimation::Animation*>::iterator invalid = invalids.begin() ; invalid != invalids.end() ; ++ invalid) {
manager.unregisterAnimation(*invalid);
}
}
void AnimationCleanerVisitor::cleanAnimation(osgAnimation::Animation& animation) {
// remove animation's invalid channels
osgAnimation::ChannelList& channels = animation.getChannels();
osgAnimation::ChannelList invalids;
for(osgAnimation::ChannelList::iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) {
if(channel->valid()) {
cleanChannel(*channel->get());
}
if(!channel->valid() || !isValidChannel(*channel->get())) {
invalids.push_back(channel->get());
}
}
for(osgAnimation::ChannelList::iterator invalid = invalids.begin() ; invalid != invalids.end() ; ++ invalid) {
animation.removeChannel(invalid->get());
}
}
void AnimationCleanerVisitor::cleanChannel(osgAnimation::Channel& channel) {
// deduplicate successive KeyFrames that are identical
osgAnimation::Sampler* sampler = channel.getSampler();
if(sampler) {
osgAnimation::KeyframeContainer* container = sampler->getKeyframeContainer();
if(container && container->size()) {
unsigned int deduplicated = container->linearInterpolationDeduplicate();
if(deduplicated) {
OSG_WARN << "Deduplicated " << deduplicated << " keyframes on channel " << channel.getName() << std::endl;
}
}
}
}
bool AnimationCleanerVisitor::isValidAnimationManager(const osgAnimation::BasicAnimationManager& manager) const {
// a valid manager has at least one animation and all animations must be valid
const osgAnimation::AnimationList& animations = manager.getAnimationList();
for(osgAnimation::AnimationList::const_iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) {
if(!animation->valid() || !isValidAnimation(*animation->get())) {
return false;
}
}
return manager.getAnimationList().size() > 0;
}
bool AnimationCleanerVisitor::isValidAnimation(const osgAnimation::Animation& animation) const {
// a valid animation has at least one channel and all channels must be valid
const osgAnimation::ChannelList& channels = animation.getChannels();
for(osgAnimation::ChannelList::const_iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) {
if(!channel->valid() || !isValidChannel(*channel->get())) {
return false;
}
}
return channels.size() > 0;
}
bool AnimationCleanerVisitor::isValidChannel(const osgAnimation::Channel& channel) const {
// a valid channel has valid target i.e.
// 1. there exists some UpdateMatrixTransform with channel's target name
// 2. the channel does not simply mimick the UpdateMatrixTransform content
std::string target = channel.getTargetName();
for(AnimationUpdateCallBackMap::const_iterator update = _updates.begin() ; update != _updates.end() ; ++ update) {
osgAnimation::UpdateMorph *updateMorph = dynamic_cast<osgAnimation::UpdateMorph*>(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<osgAnimation::UpdateMatrixTransform*>(update->first.get()));
if(channelMimickingTransform) {
warn("isChannelEqualToStackedTransform", "animation", channel, "seems redundant with stacked transform and has been removed.");
}
return !channelMimickingTransform;
}
}
}
return false;
}
const osgAnimation::StackedTransformElement* AnimationCleanerVisitor::getStackedElement(const osgAnimation::StackedTransform& transforms,
const std::string& name) const {
for(osgAnimation::StackedTransform::const_iterator transform = transforms.begin() ; transform != transforms.end() ; ++ transform) {
if(transform->valid() && transform->get()->getName() == name) {
return transform->get();
}
}
return 0;
}
bool AnimationCleanerVisitor::isChannelEqualToStackedTransform(const osgAnimation::Channel& channel,
const osgAnimation::UpdateMatrixTransform* matrixTransform) const {
// if the channel has no 'StackedTransform' we compare the KeyFrame with the 'no-op' transform
const osgAnimation::StackedTransformElement* element = getStackedElement(matrixTransform->getStackedTransforms(), channel.getName());
if(channel.getName() == "translate") {
const osgAnimation::StackedTranslateElement* translation = dynamic_cast<const osgAnimation::StackedTranslateElement*>(element);
osg::Vec3 value(0., 0., 0.);
if(translation) {
value = translation->getTranslate();
}
return isChannelEqualToStackedTransform(dynamic_cast<const osgAnimation::Vec3LinearChannel*>(&channel), value);
}
else if(channel.getName() == "scale") {
const osgAnimation::StackedScaleElement* scale = dynamic_cast<const osgAnimation::StackedScaleElement*>(element);
osg::Vec3 value(1., 1., 1.);
if(scale) {
value = scale->getScale();
}
return isChannelEqualToStackedTransform(dynamic_cast<const osgAnimation::Vec3LinearChannel*>(&channel), value);
}
else if(channel.getName() == "rotate") {
const osgAnimation::StackedQuaternionElement* rotation = dynamic_cast<const osgAnimation::StackedQuaternionElement*>(element);
osg::Quat value(0., 0., 0., 1.);
if(rotation) {
value = rotation->getQuaternion();
}
return isChannelEqualToStackedTransform(dynamic_cast<const osgAnimation::QuatSphericalLinearChannel*>(&channel), value);
}
return false;
}
template<typename T, typename V>
bool AnimationCleanerVisitor::isChannelEqualToStackedTransform(const T* channel, const V& value) const {
if(!channel) {
return false;
}
const typename T::KeyframeContainerType* keys = channel->getSamplerTyped()->getKeyframeContainerTyped();
if(keys->size() == 0) {
// channel with no keyframe is equal to anything
return true;
}
if(keys->size() == 1) {
return (*keys)[0].getValue() == value;
}
return false;
}
void AnimationCleanerVisitor::removeAnimationUpdateCallbacks() {
removeUpdateCallbacksTemplate<AnimationUpdateCallBackMap, osg::NodeCallback>(_updates);
removeUpdateCallbacksTemplate<BasicAnimationManagerMap, osgAnimation::BasicAnimationManager>(_managers);
}
template<typename C, typename T>
void AnimationCleanerVisitor::removeUpdateCallbacksTemplate(C& callbackNodes) {
for(typename C::iterator callbackNode = callbackNodes.begin() ; callbackNode != callbackNodes.end() ; ++ callbackNode) {
if(callbackNode->first && callbackNode->second.valid()) {
osg::Callback* callback = callbackNode->first.get();
osg::Node* node = callbackNode->second.get();
do {
node->removeUpdateCallback(callback);
callback = getCallbackType<T>(node->getUpdateCallback());
}
while(callback);
}
}
}
void AnimationCleanerVisitor::removeAnimationTransforms() {
for(MatrixTransformList::iterator transform = _transforms.begin() ; transform != _transforms.end() ; ++ transform) {
if(transform->valid()) {
removeFromParents(transform->get());
}
}
}
void AnimationCleanerVisitor::removeAnimatedGeometries() {
for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ++ morphGeometry) {
if(morphGeometry->first.valid()) {
replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second);
}
}
for(RigGeometryList::iterator rigGeometry = _rigGeometries.begin() ; rigGeometry != _rigGeometries.end() ; ++ rigGeometry) {
if(rigGeometry->valid()) {
replaceRigGeometryBySource(*(rigGeometry->get()));
}
}
}
void AnimationCleanerVisitor::removeFromParents(osg::Node* node) {
osg::Node::ParentList parents = node->getParents();
for(osg::Node::ParentList::iterator parent = parents.begin() ; parent != parents.end() ; ++ parent) {
if(*parent) {
(*parent)->removeChild(node);
}
}
}
void AnimationCleanerVisitor::replaceRigGeometryBySource(osgAnimation::RigGeometry& rigGeometry) const {
if(osgAnimation::MorphGeometry* source = dynamic_cast<osgAnimation::MorphGeometry*>(rigGeometry.getSourceGeometry())) {
osgAnimation::MorphGeometry* morph = new osgAnimation::MorphGeometry(*source);
replaceAnimatedGeometryByStaticGeometry(&rigGeometry, morph);
}
else {
replaceAnimatedGeometryByStaticGeometry(&rigGeometry,
new osg::Geometry(*rigGeometry.getSourceGeometry()));
}
}
void AnimationCleanerVisitor::replaceMorphGeometryByGeometry(osgAnimation::MorphGeometry& morphGeometry,
osgAnimation::RigGeometry* rigGeometry) const {
osg::Geometry* geometry = new osg::Geometry(morphGeometry);
if(!rigGeometry) {
replaceAnimatedGeometryByStaticGeometry(&morphGeometry, geometry);
}
else {
rigGeometry->setSourceGeometry(geometry);
}
}
void AnimationCleanerVisitor::replaceAnimatedGeometryByStaticGeometry(osg::Geometry* animatedGeometry,
osg::Geometry* staticGeometry) const {
for(unsigned int i = 0 ; i < animatedGeometry->getNumParents() ; ++ i) {
osg::Geode* parent = (animatedGeometry->getParent(i) ? animatedGeometry->getParent(i)->asGeode() : 0);
if(parent) {
parent->addDrawable(staticGeometry);
parent->removeDrawable(animatedGeometry);
}
}
}
void AnimationCleanerVisitor::bakeRigInitialPose() {
// use RigTransformSoftware to compute T-pose and replace rig source by computed geometry
for(RigGeometryList::iterator rigiterator = _rigGeometries.begin() ; rigiterator != _rigGeometries.end() ; ++ rigiterator) {
osgAnimation::RigGeometry* rigGeometry = (*rigiterator).get();
rigGeometry->setRigTransformImplementation(new osgAnimation::RigTransformSoftware);
rigGeometry->update();
osg::Geometry* baked = static_cast<osg::Geometry*>(rigGeometry->clone(osg::CopyOp::DEEP_COPY_ALL));
rigGeometry->setSourceGeometry(baked);
}
}

View File

@ -19,289 +19,20 @@
// TODO: deprecated // TODO: deprecated
class BindPerVertexVisitor : public GeometryUniqueVisitor { class BindPerVertexVisitor : public GeometryUniqueVisitor {
public: public:
BindPerVertexVisitor(): GeometryUniqueVisitor("BindPerVertexVisitor") BindPerVertexVisitor():
GeometryUniqueVisitor("BindPerVertexVisitor")
{} {}
void process(osg::Geometry& geometry) { void process(osg::Geometry&);
if (geometry.getNormalArray() && geometry.getNormalBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getNormalArray(),
geometry.getNormalBinding(),
geometry.getPrimitiveSetList());
geometry.setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
}
if (geometry.getColorArray() && geometry.getColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getColorArray(),
geometry.getColorBinding(),
geometry.getPrimitiveSetList());
geometry.setColorBinding(osg::Geometry::BIND_PER_VERTEX);
}
if (geometry.getSecondaryColorArray() && geometry.getSecondaryColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getSecondaryColorArray(),
geometry.getSecondaryColorBinding(),
geometry.getPrimitiveSetList());
geometry.setSecondaryColorBinding(osg::Geometry::BIND_PER_VERTEX);
}
if (geometry.getFogCoordArray() && geometry.getFogCoordBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getFogCoordArray(),
geometry.getFogCoordBinding(),
geometry.getPrimitiveSetList());
geometry.setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
}
};
protected: protected:
void bindPerVertex(osg::Array* src, void bindPerVertex(osg::Array*, osg::Geometry::AttributeBinding, osg::Geometry::PrimitiveSetList&);
osg::Geometry::AttributeBinding fromBinding,
osg::Geometry::PrimitiveSetList& primitives) {
if (doConvert<osg::ByteArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::ShortArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::IntArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::UByteArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::UShortArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::UIntArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::FloatArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::DoubleArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2Array>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3Array>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4Array>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2bArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3bArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4bArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2sArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3sArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4sArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2iArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3iArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4iArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2dArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3dArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4dArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2ubArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3ubArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4ubArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2usArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3usArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4usArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2uiArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3uiArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4uiArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::MatrixfArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::MatrixdArray>(src, fromBinding, primitives))
return;
}
template <class T> template <class T>
bool doConvert(osg::Array* src, bool doConvert(osg::Array*, osg::Geometry::AttributeBinding, osg::Geometry::PrimitiveSetList&);
osg::Geometry::AttributeBinding fromBinding,
osg::Geometry::PrimitiveSetList& primitives) {
T* array= dynamic_cast<T*>(src);
if (array) {
convert(*array, fromBinding, primitives);
return true;
}
return false;
}
template <class T> template <class T>
void convert(T& array, void convert(T&, osg::Geometry::AttributeBinding, osg::Geometry::PrimitiveSetList&);
osg::Geometry::AttributeBinding fromBinding,
osg::Geometry::PrimitiveSetList& primitives)
{
osg::ref_ptr<T> result = new T();
for (unsigned int p = 0; p < primitives.size(); p++) {
switch ( primitives[p]->getMode() ) {
case osg::PrimitiveSet::POINTS:
osg::notify(osg::WARN) << "ConvertToBindPerVertex not supported for POINTS" << std::endl;
break;
case osg::PrimitiveSet::LINE_STRIP:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
unsigned int nb = primitives[p]->getNumIndices();
for (unsigned int i = 0; i < nb; i++)
result->push_back(array[p]);
}
break;
}
break;
case osg::PrimitiveSet::LINES:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
unsigned int nb = primitives[p]->getNumIndices();
for (unsigned int i = 0; i < nb; i++)
result->push_back(array[p]);
}
break;
}
break;
case osg::PrimitiveSet::TRIANGLES:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
unsigned int nb = primitives[p]->getNumIndices();
for (unsigned int i = 0; i < nb; i++)
result->push_back(array[p]);
}
break;
}
break;
case osg::PrimitiveSet::TRIANGLE_STRIP:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_STRIP" << std::endl;
}
break;
}
break;
case osg::PrimitiveSet::TRIANGLE_FAN:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_FAN" << std::endl;
}
break;
}
break;
case osg::PrimitiveSet::QUADS:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUADS" << std::endl;
}
break;
}
break;
case osg::PrimitiveSet::QUAD_STRIP:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUAD_STRIP" << std::endl;
}
break;
}
break;
}
}
array = *result;
}
}; };
#endif #endif

View File

@ -0,0 +1,283 @@
#include "BindPerVertexVisitor"
void BindPerVertexVisitor::process(osg::Geometry& geometry) {
if (geometry.getNormalArray() && geometry.getNormalBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getNormalArray(),
geometry.getNormalBinding(),
geometry.getPrimitiveSetList());
geometry.setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
}
if (geometry.getColorArray() && geometry.getColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getColorArray(),
geometry.getColorBinding(),
geometry.getPrimitiveSetList());
geometry.setColorBinding(osg::Geometry::BIND_PER_VERTEX);
}
if (geometry.getSecondaryColorArray() && geometry.getSecondaryColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getSecondaryColorArray(),
geometry.getSecondaryColorBinding(),
geometry.getPrimitiveSetList());
geometry.setSecondaryColorBinding(osg::Geometry::BIND_PER_VERTEX);
}
if (geometry.getFogCoordArray() && geometry.getFogCoordBinding() != osg::Geometry::BIND_PER_VERTEX) {
bindPerVertex(geometry.getFogCoordArray(),
geometry.getFogCoordBinding(),
geometry.getPrimitiveSetList());
geometry.setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
}
}
void BindPerVertexVisitor::bindPerVertex(osg::Array* src,
osg::Geometry::AttributeBinding fromBinding,
osg::Geometry::PrimitiveSetList& primitives) {
if (doConvert<osg::ByteArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::ShortArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::IntArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::UByteArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::UShortArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::UIntArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::FloatArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::DoubleArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2Array>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3Array>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4Array>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2bArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3bArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4bArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2sArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3sArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4sArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2iArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3iArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4iArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2dArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3dArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4dArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2ubArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3ubArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4ubArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2usArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3usArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4usArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec2uiArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec3uiArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::Vec4uiArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::MatrixfArray>(src, fromBinding, primitives))
return;
if (doConvert<osg::MatrixdArray>(src, fromBinding, primitives))
return;
}
template <class T>
bool BindPerVertexVisitor::doConvert(osg::Array* src,
osg::Geometry::AttributeBinding fromBinding,
osg::Geometry::PrimitiveSetList& primitives) {
T* array= dynamic_cast<T*>(src);
if (array) {
convert(*array, fromBinding, primitives);
return true;
}
return false;
}
template <class T>
void BindPerVertexVisitor::convert(T& array,
osg::Geometry::AttributeBinding fromBinding,
osg::Geometry::PrimitiveSetList& primitives) {
osg::ref_ptr<T> result = new T();
for (unsigned int p = 0; p < primitives.size(); p++) {
switch ( primitives[p]->getMode() ) {
case osg::PrimitiveSet::POINTS:
osg::notify(osg::WARN) << "ConvertToBindPerVertex not supported for POINTS" << std::endl;
break;
case osg::PrimitiveSet::LINE_STRIP:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
unsigned int nb = primitives[p]->getNumIndices();
for (unsigned int i = 0; i < nb; i++)
result->push_back(array[p]);
}
break;
}
break;
case osg::PrimitiveSet::LINES:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
unsigned int nb = primitives[p]->getNumIndices();
for (unsigned int i = 0; i < nb; i++)
result->push_back(array[p]);
}
break;
}
break;
case osg::PrimitiveSet::TRIANGLES:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
unsigned int nb = primitives[p]->getNumIndices();
for (unsigned int i = 0; i < nb; i++)
result->push_back(array[p]);
}
break;
}
break;
case osg::PrimitiveSet::TRIANGLE_STRIP:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_STRIP" << std::endl;
}
break;
}
break;
case osg::PrimitiveSet::TRIANGLE_FAN:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_FAN" << std::endl;
}
break;
}
break;
case osg::PrimitiveSet::QUADS:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUADS" << std::endl;
}
break;
}
break;
case osg::PrimitiveSet::QUAD_STRIP:
switch(fromBinding) {
case osg::Geometry::BIND_OFF:
case osg::Geometry::BIND_PER_VERTEX:
break;
case osg::Geometry::BIND_OVERALL:
{
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
result->push_back(array[0]);
}
break;
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
{
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUAD_STRIP" << std::endl;
}
break;
}
break;
}
}
array = *result;
}

View File

@ -1,6 +1,17 @@
SET(TARGET_SRC SET(TARGET_SRC
ReaderWriterGLES.cpp ReaderWriterGLES.cpp
AABBonBoneVisitor.cpp
AnimationCleanerVisitor.cpp
BindPerVertexVisitor.cpp
DetachPrimitiveVisitor.cpp
GeometryIndexSplitter.cpp
GeometrySplitterVisitor.cpp
SubGeometry.cpp
OpenGLESGeometryOptimizer.cpp OpenGLESGeometryOptimizer.cpp
RigAnimationVisitor.cpp
RigAttributesVisitor.cpp
TriangleMeshSmoother.cpp
TangentSpaceVisitor.cpp
TriangleStripVisitor.cpp TriangleStripVisitor.cpp
IndexMeshVisitor.cpp IndexMeshVisitor.cpp
UnIndexMeshVisitor.cpp) UnIndexMeshVisitor.cpp)
@ -15,6 +26,7 @@ SET(TARGET_H
DrawArrayVisitor DrawArrayVisitor
EdgeIndexFunctor EdgeIndexFunctor
GeometryArray GeometryArray
GeometryIndexSplitter
GeometryInspector GeometryInspector
GeometrySplitterVisitor GeometrySplitterVisitor
GeometryUniqueVisitor GeometryUniqueVisitor
@ -35,6 +47,7 @@ SET(TARGET_H
SubGeometry SubGeometry
TangentSpaceVisitor TangentSpaceVisitor
TriangleMeshGraph TriangleMeshGraph
TriangleMeshSmoother
TriangleStripVisitor TriangleStripVisitor
UnIndexMeshVisitor UnIndexMeshVisitor
WireframeVisitor WireframeVisitor

View File

@ -23,140 +23,23 @@ public:
_userValue(userValue), _keepGeometryAttributes(keepGeometryAttributes), _inlined(inlined) _userValue(userValue), _keepGeometryAttributes(keepGeometryAttributes), _inlined(inlined)
{} {}
void reparentDuplicatedGeometry(osg::Geometry& geometry, osg::Geometry& duplicated) void reparentDuplicatedGeometry(osg::Geometry&, osg::Geometry&);
{ void process(osg::Geometry&);
unsigned int nbParents = geometry.getNumParents(); void process(osgAnimation::RigGeometry&);
for(unsigned int i = 0 ; i < nbParents ; ++ i) {
osg::Node* parent = geometry.getParent(i);
if(parent && parent->asGeode()) {
osg::Geode* geode = parent->asGeode();
geode->addDrawable(&duplicated);
if(!_inlined) {
geode->removeDrawable(&duplicated);
}
}
}
}
void process(osg::Geometry& geometry) {
if(shouldDetach(geometry)) {
osg::Geometry* detached = detachGeometry(geometry);
reparentDuplicatedGeometry(geometry, *detached);
setProcessed(detached);
}
}
void process(osgAnimation::RigGeometry& rigGeometry) {
return process(static_cast<osg::Geometry&>(rigGeometry));
}
protected: protected:
bool shouldDetach(const osg::Geometry& geometry) const { bool shouldDetach(const osg::Geometry&) const;
if(const osgAnimation::RigGeometry* rigGeometry = dynamic_cast<const osgAnimation::RigGeometry*>(&geometry)) {
return shouldDetach(*rigGeometry->getSourceGeometry());
}
bool detach = false; osg::Geometry* detachGeometry(osg::Geometry&);
for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { osg::Geometry* makeDetachedGeometry(osg::Geometry&);
const osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i); osg::Geometry* createDetachedGeometry(osg::Geometry&);
if(primitive && primitive->getUserValue(_userValue, detach) && detach) {
return true;
}
}
return false;
}
osg::Geometry* detachGeometry(osg::Geometry& source) { osg::Geometry::PrimitiveSetList createDetachedPrimitives(osg::Geometry&);
// filter vertex buffers depending on geometry type osgAnimation::MorphGeometry* createDetachedGeometry(osgAnimation::MorphGeometry&);
osg::Geometry* detached = makeDetachedGeometry(source); osg::Geometry* createDetachedGeometry(osgAnimation::RigGeometry&);
detached->setUserValue(_userValue, true);
return detached;
}
osg::Geometry* makeDetachedGeometry(osg::Geometry& geometry) {
if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry)) {
return createDetachedGeometry(*rigGeometry);
}
if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(&geometry)) {
return createDetachedGeometry(*morphGeometry);
}
return createDetachedGeometry(geometry);
}
osg::Geometry* createDetachedGeometry(osg::Geometry& source) {
osg::Geometry* detached = new osg::Geometry(source, osg::CopyOp::SHALLOW_COPY);
if(!_keepGeometryAttributes) {
// we keep only vertexes and clean all other attributes and values
detached->setNormalArray(0);
detached->setColorArray(0);
detached->setSecondaryColorArray(0);
detached->setFogCoordArray(0);
for (unsigned int i = 0 ; i < source.getNumTexCoordArrays(); ++ i) {
detached->setTexCoordArray(i, 0);
}
detached->getVertexAttribArrayList().clear();
detached->setStateSet(0);
detached->setUserDataContainer(0);
}
detached->setPrimitiveSetList(createDetachedPrimitives(source));
return detached;
}
osg::Geometry::PrimitiveSetList createDetachedPrimitives(osg::Geometry& source) {
// filter primitivesets
osg::Geometry::PrimitiveSetList detachedPrimitives;
for(int i = source.getNumPrimitiveSets() - 1 ; i >= 0 ; -- i) {
osg::PrimitiveSet* primitive = source.getPrimitiveSet(i);
bool isTrue = false;
if(primitive && primitive->getUserValue(_userValue, isTrue) && isTrue) {
detachedPrimitives.push_back(primitive);
source.removePrimitiveSet(i);
}
}
return detachedPrimitives;
}
osgAnimation::MorphGeometry* createDetachedGeometry(osgAnimation::MorphGeometry& source) {
osgAnimation::MorphGeometry* detached = new osgAnimation::MorphGeometry(*createDetachedGeometry(static_cast<osg::Geometry&>(source)));
detached->setVertexArray(source.getVertexArray());
osgAnimation::MorphGeometry::MorphTargetList& targets = source.getMorphTargetList();
for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) {
detached->addMorphTarget(target->getGeometry(), target->getWeight());
}
return detached;
}
osg::Geometry* createDetachedGeometry(osgAnimation::RigGeometry& source) {
osgAnimation::RigGeometry* detached;
if(!_keepGeometryAttributes) {
detached = new osgAnimation::RigGeometry();
detached->setSourceGeometry(makeDetachedGeometry(*source.getSourceGeometry()));
// Only keep vertexes and Bones/Weights attrib arrays
detached->setVertexArray(source.getVertexArray());
for(unsigned int i = 0 ; i < source.getVertexAttribArrayList().size() ; ++ i) {
osg::Array* attribute = source.getVertexAttribArray(i);
if(attribute) {
bool isBones = false;
bool isWeights = false;
attribute->getUserValue("bones", isBones);
attribute->getUserValue("weights", isWeights);
if (isBones || isWeights) {
detached->setVertexAttribArray(i, source.getVertexAttribArray(i));
}
}
}
}
else {
detached = new osgAnimation::RigGeometry(source, osg::CopyOp::SHALLOW_COPY);
}
return detached;
}
protected:
std::string _userValue; std::string _userValue;
bool _keepGeometryAttributes; bool _keepGeometryAttributes;
bool _inlined; bool _inlined;

View File

@ -0,0 +1,142 @@
#include "DetachPrimitiveVisitor"
void DetachPrimitiveVisitor::reparentDuplicatedGeometry(osg::Geometry& geometry, osg::Geometry& duplicated) {
unsigned int nbParents = geometry.getNumParents();
for(unsigned int i = 0 ; i < nbParents ; ++ i) {
osg::Node* parent = geometry.getParent(i);
if(parent && parent->asGeode()) {
osg::Geode* geode = parent->asGeode();
geode->addDrawable(&duplicated);
if(!_inlined) {
geode->removeDrawable(&duplicated);
}
}
}
}
void DetachPrimitiveVisitor::process(osg::Geometry& geometry) {
if(shouldDetach(geometry)) {
osg::Geometry* detached = detachGeometry(geometry);
reparentDuplicatedGeometry(geometry, *detached);
setProcessed(detached);
}
}
void DetachPrimitiveVisitor::process(osgAnimation::RigGeometry& rigGeometry) {
return process(static_cast<osg::Geometry&>(rigGeometry));
}
bool DetachPrimitiveVisitor::shouldDetach(const osg::Geometry& geometry) const {
if(const osgAnimation::RigGeometry* rigGeometry = dynamic_cast<const osgAnimation::RigGeometry*>(&geometry)) {
return shouldDetach(*rigGeometry->getSourceGeometry());
}
bool detach = false;
for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) {
const osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
if(primitive && primitive->getUserValue(_userValue, detach) && detach) {
return true;
}
}
return false;
}
osg::Geometry* DetachPrimitiveVisitor::detachGeometry(osg::Geometry& source) {
// filter vertex buffers depending on geometry type
osg::Geometry* detached = makeDetachedGeometry(source);
detached->setUserValue(_userValue, true);
return detached;
}
osg::Geometry* DetachPrimitiveVisitor::makeDetachedGeometry(osg::Geometry& geometry) {
if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry)) {
return createDetachedGeometry(*rigGeometry);
}
if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(&geometry)) {
return createDetachedGeometry(*morphGeometry);
}
return createDetachedGeometry(geometry);
}
osg::Geometry* DetachPrimitiveVisitor::createDetachedGeometry(osg::Geometry& source) {
osg::Geometry* detached = new osg::Geometry(source, osg::CopyOp::SHALLOW_COPY);
if(!_keepGeometryAttributes) {
// we keep only vertexes and clean all other attributes and values
detached->setNormalArray(0);
detached->setColorArray(0);
detached->setSecondaryColorArray(0);
detached->setFogCoordArray(0);
for (unsigned int i = 0 ; i < source.getNumTexCoordArrays(); ++ i) {
detached->setTexCoordArray(i, 0);
}
detached->getVertexAttribArrayList().clear();
detached->setStateSet(0);
detached->setUserDataContainer(0);
}
detached->setPrimitiveSetList(createDetachedPrimitives(source));
return detached;
}
osg::Geometry::PrimitiveSetList DetachPrimitiveVisitor::createDetachedPrimitives(osg::Geometry& source) {
// filter primitivesets
osg::Geometry::PrimitiveSetList detachedPrimitives;
for(int i = source.getNumPrimitiveSets() - 1 ; i >= 0 ; -- i) {
osg::PrimitiveSet* primitive = source.getPrimitiveSet(i);
bool isTrue = false;
if(primitive && primitive->getUserValue(_userValue, isTrue) && isTrue) {
detachedPrimitives.push_back(primitive);
source.removePrimitiveSet(i);
}
}
return detachedPrimitives;
}
osgAnimation::MorphGeometry* DetachPrimitiveVisitor::createDetachedGeometry(osgAnimation::MorphGeometry& source) {
osgAnimation::MorphGeometry* detached = new osgAnimation::MorphGeometry(*createDetachedGeometry(static_cast<osg::Geometry&>(source)));
detached->setVertexArray(source.getVertexArray());
osgAnimation::MorphGeometry::MorphTargetList& targets = source.getMorphTargetList();
for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) {
detached->addMorphTarget(target->getGeometry(), target->getWeight());
}
return detached;
}
osg::Geometry* DetachPrimitiveVisitor::createDetachedGeometry(osgAnimation::RigGeometry& source) {
osgAnimation::RigGeometry* detached;
if(!_keepGeometryAttributes) {
detached = new osgAnimation::RigGeometry();
detached->setSourceGeometry(makeDetachedGeometry(*source.getSourceGeometry()));
// Only keep vertexes and Bones/Weights attrib arrays
detached->setVertexArray(source.getVertexArray());
for(unsigned int i = 0 ; i < source.getVertexAttribArrayList().size() ; ++ i) {
osg::Array* attribute = source.getVertexAttribArray(i);
if(attribute) {
bool isBones = false;
bool isWeights = false;
attribute->getUserValue("bones", isBones);
attribute->getUserValue("weights", isWeights);
if (isBones || isWeights) {
detached->setVertexAttribArray(i, source.getVertexAttribArray(i));
}
}
}
}
else {
detached = new osgAnimation::RigGeometry(source, osg::CopyOp::SHALLOW_COPY);
}
return detached;
}

View File

@ -0,0 +1,78 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef GEOMETRY_INDEX_SPLITTER
#define GEOMETRY_INDEX_SPLITTER
#include <limits>
#include <algorithm>
#include <osg/ref_ptr>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <osg/ValueObject>
#include <osgUtil/MeshOptimizers>
#include <osgAnimation/RigGeometry>
#include "glesUtil"
#include "GeometryArray"
#include "TriangleMeshGraph"
#include "SubGeometry"
#include "Line"
class GeometryIndexSplitter
{
protected:
class IndexCache : public IndexDeque {
public:
IndexCache(unsigned int size=64) : _size(size)
{}
void push_back(unsigned int value) {
IndexDeque::push_back(value);
if(size() > _size) pop_front();
}
protected:
unsigned int _size;
};
public:
typedef std::vector< osg::ref_ptr<osg::Geometry> > GeometryList;
GeometryIndexSplitter(unsigned int maxAllowedIndex):
_maxAllowedIndex(maxAllowedIndex)
{}
bool split(osg::Geometry&);
unsigned int findCandidate(const IndexVector&);
unsigned int findCandidate(const IndexVector&, const IndexVector&);
void setTriangleCluster(const TriangleMeshGraph&, unsigned int, unsigned int, IndexVector&, IndexSet&, unsigned int&);
void extract_primitives(const IndexSet&, const osg::DrawElements*, IndexVector&, unsigned int);
protected:
bool needToSplit(const osg::Geometry&) const;
bool needToSplit(const osg::DrawElements&) const;
void attachBufferBoundingBox(osg::Geometry&) const;
template<typename T>
void setBufferBoundingBox(T*) const;
void setValidIndices(std::set<unsigned int>&, const osg::DrawElements*) const;
public:
const unsigned int _maxAllowedIndex;
GeometryList _geometryList;
};
#endif

View File

@ -0,0 +1,285 @@
#include "GeometryIndexSplitter"
bool GeometryIndexSplitter::split(osg::Geometry& geometry) {
if(!needToSplit(geometry)) {
_geometryList.push_back(&geometry);
return false;
}
// keep bounding box data as user value if needed in subsequent processing
attachBufferBoundingBox(geometry);
osg::DrawElements *wire_primitive = 0,
*line_primitive = 0,
*point_primitive = 0;
for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) {
osg::DrawElements* primitive = (geometry.getPrimitiveSet(i) ? geometry.getPrimitiveSet(i)->getDrawElements() : 0);
if(primitive) {
if(primitive->getMode() == osg::PrimitiveSet::LINES) {
bool isWireframe = false;
if(primitive->getUserValue("wireframe", isWireframe) && isWireframe) {
wire_primitive = primitive;
}
else {
line_primitive = primitive;
}
}
else if(primitive->getMode() == osg::PrimitiveSet::POINTS) {
point_primitive = primitive;
}
}
}
// only wireframe can be processed directly as they simply "duplicate" triangle or edge data;
// lines/points may reference points not used for triangles so we keep a set of primitives
// that remain to process
LineSet source_lines;
IndexSet source_points;
if(line_primitive) {
for(unsigned int i = 0 ; i < line_primitive->getNumIndices() ; i += 2) {
source_lines.insert(Line(line_primitive->index(i), line_primitive->index(i + 1)));
}
}
if(point_primitive) {
for(unsigned int i = 0 ; i < point_primitive->getNumIndices() ; ++ i) {
source_points.insert(point_primitive->index(i));
}
}
TriangleMeshGraph graph(geometry, false);
unsigned int remaining_triangles = graph.getNumTriangles(),
cluster = 0;
IndexVector clusters(remaining_triangles, 0);
IndexCache cache;
// assign a cluster id for each triangle
// 1. bootstrap cluster by selecting first remaining triangle
// 2. while cluster size is < max cluster size
// 2.1 look for a triangle that is a neighbor of one cluster triangle (considering N last cluster triangles from cache)
// 2.2 if 2.1 was not successful and there still is room for 3 new vertices (i.e. a full new triangle) then
// add any (might share an edge/vertex/nothing with existing cluster) triangle
// 2.3 if we still have lines/points, add anything we can 'naively'
// 3. insert wireframe edges corresponding to selected triangles
// 4. extract subgeometry
while(remaining_triangles || !source_lines.empty() || !source_points.empty()) {
IndexVector subtriangles, subwireframe, sublines, subpoints;
IndexSet cluster_vertices;
++ cluster;
if(remaining_triangles) {
// find first unmarked triangle (as remaining_triangles > 0 there *must* be at least one)
cache.push_back(findCandidate(clusters));
setTriangleCluster(graph, cache.back(), cluster, clusters, cluster_vertices, remaining_triangles);
while(remaining_triangles && cluster_vertices.size() < _maxAllowedIndex) {
unsigned int candidate = std::numeric_limits<unsigned int>::max();
for(IndexCache::const_reverse_iterator cached = cache.rbegin() ; cached != cache.rend() ; ++ cached) {
candidate = findCandidate(graph.triangleNeighbors(*cached), clusters);
if(candidate != std::numeric_limits<unsigned int>::max()) break;
}
if(candidate == std::numeric_limits<unsigned int>::max()) {
// do we have room for a triangle having all vertices not in the cluster?
if(!(cluster_vertices.size() + 2 < _maxAllowedIndex)) {
break;
}
candidate = findCandidate(clusters);
}
cache.push_back(candidate);
setTriangleCluster(graph, candidate, cluster, clusters, cluster_vertices, remaining_triangles);
}
// build list of cluster triangles
for(unsigned int triangle = 0 ; triangle < clusters.size() ; ++ triangle) {
if(clusters[triangle] == cluster) {
const Triangle& t = graph.triangle(triangle);
subtriangles.push_back(t.v1());
subtriangles.push_back(t.v2());
subtriangles.push_back(t.v3());
}
}
// update lines/points: if all vertices referenced by a point/line primitive are
// already extracted, let's insert it in the subgeometry and update the set of
// primitives still remaining Lines may e.g. reference one vertex in cluster A and
// the other in cluster B hence need specific care
if(line_primitive) {
extract_primitives(cluster_vertices, line_primitive, sublines, 2);
for(unsigned int i = 0 ; i < sublines.size() / 2 ; i += 2) {
source_lines.erase(Line(sublines[i], sublines[i + 1]));
}
}
if(point_primitive) {
extract_primitives(cluster_vertices, point_primitive, subpoints, 1);
for(unsigned int i = 0 ; i < subpoints.size() ; ++ i) {
source_points.erase(subpoints[i]);
}
}
}
// let's consider that every new lines adds 2 vertices for simplicity
while(!source_lines.empty() && cluster_vertices.size() - 1 < _maxAllowedIndex) {
Line line = *source_lines.begin();
source_lines.erase(source_lines.begin());
cluster_vertices.insert(line._a);
cluster_vertices.insert(line._b);
sublines.push_back(line._a);
sublines.push_back(line._b);
}
while(!source_points.empty() && cluster_vertices.size() < _maxAllowedIndex) {
unsigned int point = *source_points.begin();
source_points.erase(source_points.begin());
cluster_vertices.insert(point);
subpoints.push_back(point);
}
// finally extract wireframe (may originate from triangles or lines but necessarily have
// to reference vertices that are *all* in the geometry)
if(wire_primitive) {
extract_primitives(cluster_vertices, wire_primitive, subwireframe, 2);
}
_geometryList.push_back(SubGeometry(geometry,
subtriangles,
sublines,
subwireframe,
subpoints).geometry());
}
osg::notify(osg::NOTICE) << "geometry " << &geometry << " " << geometry.getName()
<< " vertexes (" << geometry.getVertexArray()->getNumElements()
<< ") has DrawElements index > " << _maxAllowedIndex << ", splitted to "
<< _geometryList.size() << " geometry" << std::endl;
return true;
}
unsigned int GeometryIndexSplitter::findCandidate(const IndexVector& clusters) {
for(unsigned int i = 0 ; i < clusters.size() ; ++ i) {
if(!clusters[i]) {
return i;
}
}
return std::numeric_limits<unsigned int>::max();
}
unsigned int GeometryIndexSplitter::findCandidate(const IndexVector& candidates, const IndexVector& clusters) {
for(IndexVector::const_iterator candidate = candidates.begin() ; candidate != candidates.end() ; ++ candidate) {
if(!clusters[*candidate]) {
return *candidate;
}
}
return std::numeric_limits<unsigned int>::max();
}
void GeometryIndexSplitter::setTriangleCluster(const TriangleMeshGraph& graph,
unsigned int triangle,
unsigned int cluster,
IndexVector& clusters,
IndexSet& cluster_vertices,
unsigned int& remaining) {
clusters[triangle] = cluster;
const Triangle& t = graph.triangle(triangle);
cluster_vertices.insert(t.v1());
cluster_vertices.insert(t.v2());
cluster_vertices.insert(t.v3());
remaining --;
}
void GeometryIndexSplitter::extract_primitives(const IndexSet& vertices,
const osg::DrawElements* elements,
IndexVector& indices,
unsigned int primitive_size) {
for(unsigned int i = 0 ; i < elements->getNumIndices() ; i += primitive_size) {
bool is_included = true;
for(unsigned int j = 0 ; j < primitive_size ; ++ j) {
if(!vertices.count(elements->index(i + j))) {
is_included = false;
break;
}
}
if(is_included) {
for(unsigned int j = 0 ; j < primitive_size ; ++ j) {
indices.push_back(elements->index(i + j));
}
}
}
}
bool GeometryIndexSplitter::needToSplit(const osg::Geometry& geometry) const {
for(unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) {
const osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements();
if (primitive && needToSplit(*primitive)) {
return true;
}
}
return false;
}
bool GeometryIndexSplitter::needToSplit(const osg::DrawElements& primitive) const {
for(unsigned int j = 0; j < primitive.getNumIndices(); j++) {
if (primitive.index(j) > _maxAllowedIndex){
return true;
}
}
return false;
}
void GeometryIndexSplitter::attachBufferBoundingBox(osg::Geometry& geometry) const {
// positions
setBufferBoundingBox(dynamic_cast<osg::Vec3Array*>(geometry.getVertexArray()));
// uvs
for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) {
setBufferBoundingBox(dynamic_cast<osg::Vec2Array*>(geometry.getTexCoordArray(i)));
}
}
template<typename T>
void GeometryIndexSplitter::setBufferBoundingBox(T* buffer) const {
if(!buffer) return;
typename T::ElementDataType bbl;
typename T::ElementDataType ufr;
const unsigned int dimension = buffer->getDataSize();
if(buffer->getNumElements()) {
for(unsigned int i = 0 ; i < dimension ; ++i) {
bbl[i] = ufr[i] = (*buffer->begin())[i];
}
for(typename T::const_iterator it = buffer->begin() + 1 ; it != buffer->end() ; ++ it) {
for(unsigned int i = 0 ; i < dimension ; ++ i) {
bbl[i] = std::min(bbl[i], (*it)[i]);
ufr[i] = std::max(ufr[i], (*it)[i]);
}
}
buffer->setUserValue("bbl", bbl);
buffer->setUserValue("ufr", ufr);
}
}
void GeometryIndexSplitter::setValidIndices(std::set<unsigned int>& indices, const osg::DrawElements* primitive) const {
for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) {
indices.insert(primitive->index(j));
}
}

View File

@ -13,328 +13,13 @@
#ifndef GEOMETRY_SPLITTER_VISITOR #ifndef GEOMETRY_SPLITTER_VISITOR
#define GEOMETRY_SPLITTER_VISITOR #define GEOMETRY_SPLITTER_VISITOR
#include <set> #include <map>
#include <limits>
#include <algorithm>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osg/Geometry> #include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <osg/ValueObject>
#include <osgUtil/MeshOptimizers>
#include <osgAnimation/RigGeometry> #include <osgAnimation/RigGeometry>
#include "glesUtil"
#include "GeometryArray"
#include "GeometryUniqueVisitor" #include "GeometryUniqueVisitor"
#include "AnimationCleanerVisitor"
#include "TriangleMeshGraph"
#include "SubGeometry"
#include "Line"
class GeometryIndexSplitter
{
protected:
class IndexCache : public IndexDeque {
public:
IndexCache(unsigned int size=64) : _size(size)
{}
void push_back(unsigned int value) {
IndexDeque::push_back(value);
if(size() > _size) pop_front();
}
protected:
unsigned int _size;
};
public:
typedef std::vector< osg::ref_ptr<osg::Geometry> > GeometryList;
GeometryIndexSplitter(unsigned int maxAllowedIndex):
_maxAllowedIndex(maxAllowedIndex)
{}
bool split(osg::Geometry& geometry) {
if(!needToSplit(geometry)) {
_geometryList.push_back(&geometry);
return false;
}
// keep bounding box data as user value if needed in subsequent processing
attachBufferBoundingBox(geometry);
osg::DrawElements *wire_primitive = 0,
*line_primitive = 0,
*point_primitive = 0;
for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) {
osg::DrawElements* primitive = (geometry.getPrimitiveSet(i) ? geometry.getPrimitiveSet(i)->getDrawElements() : 0);
if(primitive) {
if(primitive->getMode() == osg::PrimitiveSet::LINES) {
bool isWireframe = false;
if(primitive->getUserValue("wireframe", isWireframe) && isWireframe) {
wire_primitive = primitive;
}
else {
line_primitive = primitive;
}
}
else if(primitive->getMode() == osg::PrimitiveSet::POINTS) {
point_primitive = primitive;
}
}
}
// only wireframe can be processed directly as they simply "duplicate" triangle or edge data;
// lines/points may reference points not used for triangles so we keep a set of primitives
// that remain to process
LineSet source_lines;
IndexSet source_points;
if(line_primitive) {
for(unsigned int i = 0 ; i < line_primitive->getNumIndices() ; i += 2) {
source_lines.insert(Line(line_primitive->index(i), line_primitive->index(i + 1)));
}
}
if(point_primitive) {
for(unsigned int i = 0 ; i < point_primitive->getNumIndices() ; ++ i) {
source_points.insert(point_primitive->index(i));
}
}
TriangleMeshGraph graph(geometry, false);
unsigned int remaining_triangles = graph.getNumTriangles(),
cluster = 0;
IndexVector clusters(remaining_triangles, 0);
IndexCache cache;
// assign a cluster id for each triangle
// 1. bootstrap cluster by selecting first remaining triangle
// 2. while cluster size is < max cluster size
// 2.1 look for a triangle that is a neighbor of one cluster triangle (considering N last cluster triangles from cache)
// 2.2 if 2.1 was not successful and there still is room for 3 new vertices (i.e. a full new triangle) then
// add any (might share an edge/vertex/nothing with existing cluster) triangle
// 2.3 if we still have lines/points, add anything we can 'naively'
// 3. insert wireframe edges corresponding to selected triangles
// 4. extract subgeometry
while(remaining_triangles || !source_lines.empty() || !source_points.empty()) {
IndexVector subtriangles, subwireframe, sublines, subpoints;
IndexSet cluster_vertices;
++ cluster;
if(remaining_triangles) {
// find first unmarked triangle (as remaining_triangles > 0 there *must* be at least one)
cache.push_back(findCandidate(clusters));
setTriangleCluster(graph, cache.back(), cluster, clusters, cluster_vertices, remaining_triangles);
while(remaining_triangles && cluster_vertices.size() < _maxAllowedIndex) {
unsigned int candidate = std::numeric_limits<unsigned int>::max();
for(IndexCache::const_reverse_iterator cached = cache.rbegin() ; cached != cache.rend() ; ++ cached) {
candidate = findCandidate(graph.triangleNeighbors(*cached), clusters);
if(candidate != std::numeric_limits<unsigned int>::max()) break;
}
if(candidate == std::numeric_limits<unsigned int>::max()) {
// do we have room for a triangle having all vertices not in the cluster?
if(!(cluster_vertices.size() + 2 < _maxAllowedIndex)) {
break;
}
candidate = findCandidate(clusters);
}
cache.push_back(candidate);
setTriangleCluster(graph, candidate, cluster, clusters, cluster_vertices, remaining_triangles);
}
// build list of cluster triangles
for(unsigned int triangle = 0 ; triangle < clusters.size() ; ++ triangle) {
if(clusters[triangle] == cluster) {
const Triangle& t = graph.triangle(triangle);
subtriangles.push_back(t.v1());
subtriangles.push_back(t.v2());
subtriangles.push_back(t.v3());
}
}
// update lines/points: if all vertices referenced by a point/line primitive are
// already extracted, let's insert it in the subgeometry and update the set of
// primitives still remaining Lines may e.g. reference one vertex in cluster A and
// the other in cluster B hence need specific care
if(line_primitive) {
extract_primitives(cluster_vertices, line_primitive, sublines, 2);
for(unsigned int i = 0 ; i < sublines.size() / 2 ; i += 2) {
source_lines.erase(Line(sublines[i], sublines[i + 1]));
}
}
if(point_primitive) {
extract_primitives(cluster_vertices, point_primitive, subpoints, 1);
for(unsigned int i = 0 ; i < subpoints.size() ; ++ i) {
source_points.erase(subpoints[i]);
}
}
}
// let's consider that every new lines adds 2 vertices for simplicity
while(!source_lines.empty() && cluster_vertices.size() - 1 < _maxAllowedIndex) {
Line line = *source_lines.begin();
source_lines.erase(source_lines.begin());
cluster_vertices.insert(line._a);
cluster_vertices.insert(line._b);
sublines.push_back(line._a);
sublines.push_back(line._b);
}
while(!source_points.empty() && cluster_vertices.size() < _maxAllowedIndex) {
unsigned int point = *source_points.begin();
source_points.erase(source_points.begin());
cluster_vertices.insert(point);
subpoints.push_back(point);
}
// finally extract wireframe (may originate from triangles or lines but necessarily have
// to reference vertices that are *all* in the geometry)
if(wire_primitive) {
extract_primitives(cluster_vertices, wire_primitive, subwireframe, 2);
}
_geometryList.push_back(SubGeometry(geometry,
subtriangles,
sublines,
subwireframe,
subpoints).geometry());
}
osg::notify(osg::NOTICE) << "geometry " << &geometry << " " << geometry.getName()
<< " vertexes (" << geometry.getVertexArray()->getNumElements()
<< ") has DrawElements index > " << _maxAllowedIndex << ", splitted to "
<< _geometryList.size() << " geometry" << std::endl;
return true;
}
unsigned int findCandidate(const IndexVector& clusters) {
for(unsigned int i = 0 ; i < clusters.size() ; ++ i) {
if(!clusters[i]) {
return i;
}
}
return std::numeric_limits<unsigned int>::max();
}
unsigned int findCandidate(const IndexVector& candidates, const IndexVector& clusters) {
for(IndexVector::const_iterator candidate = candidates.begin() ; candidate != candidates.end() ; ++ candidate) {
if(!clusters[*candidate]) {
return *candidate;
}
}
return std::numeric_limits<unsigned int>::max();
}
void setTriangleCluster(const TriangleMeshGraph& graph,
unsigned int triangle,
unsigned int cluster,
IndexVector& clusters,
IndexSet& cluster_vertices,
unsigned int& remaining) {
clusters[triangle] = cluster;
const Triangle& t = graph.triangle(triangle);
cluster_vertices.insert(t.v1());
cluster_vertices.insert(t.v2());
cluster_vertices.insert(t.v3());
remaining --;
}
void extract_primitives(const IndexSet& vertices, const osg::DrawElements* elements, IndexVector& indices, unsigned int primitive_size) {
for(unsigned int i = 0 ; i < elements->getNumIndices() ; i += primitive_size) {
bool is_included = true;
for(unsigned int j = 0 ; j < primitive_size ; ++ j) {
if(!vertices.count(elements->index(i + j))) {
is_included = false;
break;
}
}
if(is_included) {
for(unsigned int j = 0 ; j < primitive_size ; ++ j) {
indices.push_back(elements->index(i + j));
}
}
}
}
protected:
bool needToSplit(const osg::Geometry& geometry) const {
for(unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) {
const osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements();
if (primitive && needToSplit(*primitive)) {
return true;
}
}
return false;
}
bool needToSplit(const osg::DrawElements& primitive) const {
for(unsigned int j = 0; j < primitive.getNumIndices(); j++) {
if (primitive.index(j) > _maxAllowedIndex){
return true;
}
}
return false;
}
void attachBufferBoundingBox(osg::Geometry& geometry) const {
// positions
setBufferBoundingBox(dynamic_cast<osg::Vec3Array*>(geometry.getVertexArray()));
// uvs
for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) {
setBufferBoundingBox(dynamic_cast<osg::Vec2Array*>(geometry.getTexCoordArray(i)));
}
}
template<typename T>
void setBufferBoundingBox(T* buffer) const {
if(!buffer) return;
typename T::ElementDataType bbl;
typename T::ElementDataType ufr;
const unsigned int dimension = buffer->getDataSize();
if(buffer->getNumElements()) {
for(unsigned int i = 0 ; i < dimension ; ++i) {
bbl[i] = ufr[i] = (*buffer->begin())[i];
}
for(typename T::const_iterator it = buffer->begin() + 1 ; it != buffer->end() ; ++ it) {
for(unsigned int i = 0 ; i < dimension ; ++ i) {
bbl[i] = std::min(bbl[i], (*it)[i]);
ufr[i] = std::max(ufr[i], (*it)[i]);
}
}
buffer->setUserValue("bbl", bbl);
buffer->setUserValue("ufr", ufr);
}
}
void setValidIndices(std::set<unsigned int>& indices, const osg::DrawElements* primitive) const {
for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) {
indices.insert(primitive->index(j));
}
}
public:
const unsigned int _maxAllowedIndex;
GeometryList _geometryList;
};
class GeometrySplitterVisitor : public GeometryUniqueVisitor { class GeometrySplitterVisitor : public GeometryUniqueVisitor {
@ -349,70 +34,14 @@ public:
_exportNonGeometryDrawables(exportNonGeometryDrawables) _exportNonGeometryDrawables(exportNonGeometryDrawables)
{} {}
void apply(osg::Geode& geode) { void apply(osg::Geode&);
GeometryUniqueVisitor::apply(geode); void process(osg::Geometry&);
GeometryList remappedGeometries;
DrawableList nonGeometryDrawables;
for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) {
osg::Geometry* geometry = geode.getDrawable(i)->asGeometry();
if(geometry) {
if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(geometry)) {
SplitMap::iterator lookup = _split.find(rigGeometry->getSourceGeometry());
if(lookup != _split.end() && !lookup->second.empty()) {
for(GeometryList::iterator splittedSource = lookup->second.begin() ; splittedSource != lookup->second.end() ; ++ splittedSource) {
if(hasPositiveWeights(splittedSource->get())) {
osgAnimation::RigGeometry* splittedRig = new osgAnimation::RigGeometry(*rigGeometry);
splittedRig->setSourceGeometry(splittedSource->get());
remappedGeometries.push_back(splittedRig);
}
else {
remappedGeometries.push_back(splittedSource->get());
}
}
}
}
else {
SplitMap::iterator lookup = _split.find(geometry);
if(lookup != _split.end() && !lookup->second.empty()) {
remappedGeometries.insert(remappedGeometries.end(), lookup->second.begin(), lookup->second.end());
}
}
}
else {
nonGeometryDrawables.push_back(geode.getDrawable(i));
}
}
// remove all drawables
geode.removeDrawables(0, geode.getNumDrawables());
// insert splitted geometries
for(unsigned int i = 0 ; i < remappedGeometries.size() ; ++ i) {
geode.addDrawable(remappedGeometries[i].get());
}
if(_exportNonGeometryDrawables) {
// insert other drawables (e.g. osgText)
for(unsigned int i = 0 ; i < nonGeometryDrawables.size() ; ++ i) {
geode.addDrawable(nonGeometryDrawables[i].get());
}
}
}
void process(osg::Geometry& geometry) {
GeometryIndexSplitter splitter(_maxAllowedIndex);
splitter.split(geometry);
setProcessed(&geometry, splitter._geometryList);
}
protected: protected:
bool isProcessed(osg::Geometry* node) { bool isProcessed(osg::Geometry*);
return _split.find(node) != _split.end(); void setProcessed(osg::Geometry*, const GeometryList&);
}
void setProcessed(osg::Geometry* node, const GeometryList& list) { protected:
_split.insert(std::pair<osg::Geometry*, GeometryList>(node, GeometryList(list)));
}
unsigned int _maxAllowedIndex; unsigned int _maxAllowedIndex;
std::map<osg::Geometry*, GeometryList> _split; std::map<osg::Geometry*, GeometryList> _split;
bool _exportNonGeometryDrawables; bool _exportNonGeometryDrawables;

View File

@ -0,0 +1,70 @@
#include "GeometrySplitterVisitor"
#include "GeometryIndexSplitter"
void GeometrySplitterVisitor::apply(osg::Geode& geode) {
GeometryUniqueVisitor::apply(geode);
GeometryList remappedGeometries;
DrawableList nonGeometryDrawables;
for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) {
osg::Geometry* geometry = geode.getDrawable(i)->asGeometry();
if(geometry) {
if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(geometry)) {
SplitMap::iterator lookup = _split.find(rigGeometry->getSourceGeometry());
if(lookup != _split.end() && !lookup->second.empty()) {
for(GeometryList::iterator splittedSource = lookup->second.begin() ; splittedSource != lookup->second.end() ; ++ splittedSource) {
if(glesUtil::hasPositiveWeights(splittedSource->get())) {
osgAnimation::RigGeometry* splittedRig = new osgAnimation::RigGeometry(*rigGeometry);
splittedRig->setSourceGeometry(splittedSource->get());
remappedGeometries.push_back(splittedRig);
}
else {
remappedGeometries.push_back(splittedSource->get());
}
}
}
}
else {
SplitMap::iterator lookup = _split.find(geometry);
if(lookup != _split.end() && !lookup->second.empty()) {
remappedGeometries.insert(remappedGeometries.end(), lookup->second.begin(), lookup->second.end());
}
}
}
else {
nonGeometryDrawables.push_back(geode.getDrawable(i));
}
}
// remove all drawables
geode.removeDrawables(0, geode.getNumDrawables());
// insert splitted geometries
for(unsigned int i = 0 ; i < remappedGeometries.size() ; ++ i) {
geode.addDrawable(remappedGeometries[i].get());
}
if(_exportNonGeometryDrawables) {
// insert other drawables (e.g. osgText)
for(unsigned int i = 0 ; i < nonGeometryDrawables.size() ; ++ i) {
geode.addDrawable(nonGeometryDrawables[i].get());
}
}
}
void GeometrySplitterVisitor::process(osg::Geometry& geometry) {
GeometryIndexSplitter splitter(_maxAllowedIndex);
splitter.split(geometry);
setProcessed(&geometry, splitter._geometryList);
}
bool GeometrySplitterVisitor::isProcessed(osg::Geometry* node) {
return _split.find(node) != _split.end();
}
void GeometrySplitterVisitor::setProcessed(osg::Geometry* node, const GeometryList& list) {
_split.insert(std::pair<osg::Geometry*, GeometryList>(node, GeometryList(list)));
}

View File

@ -30,19 +30,6 @@
#include "StatLogger" #include "StatLogger"
struct sort_weights {
bool operator()(const std::pair<unsigned int, float> &left, const std::pair<unsigned int, float> &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 // the idea is to create true Geometry if skeleton with RigGeometry
class RigAnimationVisitor : public osgUtil::UpdateVisitor class RigAnimationVisitor : public osgUtil::UpdateVisitor
{ {
@ -55,146 +42,28 @@ public:
setFrameStamp(new osg::FrameStamp()); setFrameStamp(new osg::FrameStamp());
} }
void apply(osg::Drawable& drawable) { void apply(osg::Drawable&);
// skip drawables already processed void apply(osg::Geometry*);
if (isProcessed(drawable)) { void apply(osgAnimation::RigGeometry&);
return;
}
apply(drawable.asGeometry());
setProcessed(drawable);
}
void apply(osg::Geometry* geometry) {
osgAnimation::RigGeometry* rig = dynamic_cast<osgAnimation::RigGeometry*>(geometry);
if(rig) {
apply(*rig);
}
}
protected:
inline void normalizeWeight(osg::Vec4f &v) const { inline void normalizeWeight(osg::Vec4f &v) const {
// do not consider positive weights only // do not consider positive weights only
float sum = std::abs(v[0]) + std::abs(v[1]) + std::abs(v[2]) + std::abs(v[3]); float sum = std::abs(v[0]) + std::abs(v[1]) + std::abs(v[2]) + std::abs(v[3]);
if (sum > 0) v /= sum; if (sum > 0) v /= sum;
} }
void apply(osgAnimation::RigGeometry& rigGeometry) { boneIndices remapGeometryBones(const osg::Vec4usArray&);
// find skeleton void applyBoneIndicesRemap(osg::Vec4usArray&, const boneIndices&);
osgAnimation::UpdateRigGeometry rigUpdater;
osg::Geometry* source = rigGeometry.getSourceGeometry();
if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(source)) { void serializeBonesUserValues(osg::Vec4usArray&,
// skip normals blending when rigging a morph as targets may not have normals yet const std::map<unsigned int, unsigned short>&,
morphGeometry->setMorphNormals(false); const osgAnimation::RigTransformHardware::BoneNamePaletteIndex&);
}
rigUpdater.update(0, &rigGeometry); bool isProcessed(osg::Drawable&);
void setProcessed(osg::Drawable&);
osgAnimation::RigTransformHardware rth;
rth(rigGeometry);
std::vector< std::vector< std::pair<unsigned int, float> > > vertexBoneWeights(rigGeometry.getVertexArray()->getNumElements());
// collect all bone/weight pairs for *all* vertices
for(unsigned int i = 0 ; i < static_cast<unsigned int>(rth.getNumVertexAttrib()) ; ++ i) {
osg::Vec4Array* weights = dynamic_cast<osg::Vec4Array*>(rth.getVertexAttrib(i));
for(unsigned int k = 0 ; k < weights->getNumElements() ; ++ k) {
vertexBoneWeights[k].push_back(std::pair<unsigned int, float>((*weights)[k][0], (*weights)[k][1]));
vertexBoneWeights[k].push_back(std::pair<unsigned int, float>((*weights)[k][2], (*weights)[k][3]));
}
}
osg::ref_ptr<osg::Vec4usArray> bones = new osg::Vec4usArray;
osg::ref_ptr<osg::Vec4Array> 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<unsigned int, float> > 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: 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<unsigned short>(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<unsigned int, unsigned short>& oldIndexToNewIndex,
const osgAnimation::RigTransformHardware::BoneNamePaletteIndex& boneNamePaletteIndex) {
// map 'global' palette index to bone name
std::map<unsigned int, std::string> oldIndexToBoneName;
for(osgAnimation::RigTransformHardware::BoneNamePaletteIndex::const_iterator it = boneNamePaletteIndex.begin() ;
it != boneNamePaletteIndex.end() ; ++ it) {
oldIndexToBoneName[static_cast<unsigned int>(it->second)] = it->first;
}
// serialize geometry 'palette index' => 'bone name' with user value using animationBone_ as
// name prefix
for(std::map<unsigned int, unsigned short>::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<osg::Drawable*> _processed; std::set<osg::Drawable*> _processed;
StatLogger _logger; StatLogger _logger;
}; };

View File

@ -0,0 +1,155 @@
#include "RigAnimationVisitor"
struct sort_weights {
bool operator()(const std::pair<unsigned int, float> &left, const std::pair<unsigned int, float> &right) {
// in case weights are equal, order elements by ascending bone ids
if(left.second == right.second) {
return left.first < right.first;
}
else {
return left.second > right.second;
}
}
};
void RigAnimationVisitor::apply(osg::Drawable& drawable) {
// skip drawables already processed
if (isProcessed(drawable)) {
return;
}
apply(drawable.asGeometry());
setProcessed(drawable);
}
void RigAnimationVisitor::apply(osg::Geometry* geometry) {
osgAnimation::RigGeometry* rig = dynamic_cast<osgAnimation::RigGeometry*>(geometry);
if(rig) {
apply(*rig);
}
}
void RigAnimationVisitor::apply(osgAnimation::RigGeometry& rigGeometry) {
// find skeleton
osgAnimation::UpdateRigGeometry rigUpdater;
osg::Geometry* source = rigGeometry.getSourceGeometry();
if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(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<unsigned int, float> > > vertexBoneWeights(rigGeometry.getVertexArray()->getNumElements());
// collect all bone/weight pairs for *all* vertices
for(unsigned int i = 0 ; i < static_cast<unsigned int>(rth.getNumVertexAttrib()) ; ++ i) {
osg::Vec4Array* weights = dynamic_cast<osg::Vec4Array*>(rth.getVertexAttrib(i));
for(unsigned int k = 0 ; k < weights->getNumElements() ; ++ k) {
vertexBoneWeights[k].push_back(std::pair<unsigned int, float>((*weights)[k][0], (*weights)[k][1]));
vertexBoneWeights[k].push_back(std::pair<unsigned int, float>((*weights)[k][2], (*weights)[k][3]));
}
}
osg::ref_ptr<osg::Vec4usArray> bones = new osg::Vec4usArray;
osg::ref_ptr<osg::Vec4Array> 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<unsigned int, float> > maxVertexBoneWeight(4);
std::partial_sort_copy(vertexBoneWeights[i].begin(), vertexBoneWeights[i].end(),
maxVertexBoneWeight.begin(), maxVertexBoneWeight.end(),
sort_weights());
osg::Vec4 vertexWeights;
osg::Vec4us vertexBones;
for(unsigned int j = 0 ; j < 4 ; ++ j) {
vertexBones[j] = maxVertexBoneWeight[j].first;
vertexWeights[j] = maxVertexBoneWeight[j].second;
}
normalizeWeight(vertexWeights);
bones->push_back(vertexBones);
weights->push_back(vertexWeights);
}
RigAnimationVisitor::boneIndices geometryBoneIndices = remapGeometryBones(*bones);
applyBoneIndicesRemap(*bones, geometryBoneIndices);
serializeBonesUserValues(*bones, geometryBoneIndices, rth.getBoneNameToPalette());
bones->setUserValue("bones", true);
weights->setUserValue("weights", true);
// attach bones & weights to source geometry during scene graph processing
source->setVertexAttribArray(source->getNumVertexAttribArrays(), bones.get(), osg::Array::BIND_PER_VERTEX);
source->setVertexAttribArray(source->getNumVertexAttribArrays(), weights.get(), osg::Array::BIND_PER_VERTEX);
rigGeometry.setRigTransformImplementation(0); //Remove current implementation to force implementation re-init
}
RigAnimationVisitor::boneIndices RigAnimationVisitor::remapGeometryBones(const osg::Vec4usArray& bones) {
RigAnimationVisitor::boneIndices remap;
for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) {
for(unsigned int j = 0 ; j < 4 ; ++ j) {
if(remap.find(bones[i][j]) == remap.end()) {
remap[bones[i][j]] = static_cast<unsigned short>(remap.size() - 1);
}
}
}
return remap;
}
void RigAnimationVisitor::applyBoneIndicesRemap(osg::Vec4usArray& bones, const RigAnimationVisitor::boneIndices& remap) {
for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) {
RigAnimationVisitor::boneIndices::const_iterator x = remap.find(bones[i][0]),
y = remap.find(bones[i][1]),
z = remap.find(bones[i][2]),
w = remap.find(bones[i][3]);
bones[i] = osg::Vec4us(x->second,
y->second,
z->second,
w->second);
}
}
void RigAnimationVisitor::serializeBonesUserValues(osg::Vec4usArray& bones,
const std::map<unsigned int, unsigned short>& oldIndexToNewIndex,
const osgAnimation::RigTransformHardware::BoneNamePaletteIndex& boneNamePaletteIndex) {
// map 'global' palette index to bone name
std::map<unsigned int, std::string> oldIndexToBoneName;
for(osgAnimation::RigTransformHardware::BoneNamePaletteIndex::const_iterator it = boneNamePaletteIndex.begin() ;
it != boneNamePaletteIndex.end() ; ++ it) {
oldIndexToBoneName[static_cast<unsigned int>(it->second)] = it->first;
}
// serialize geometry 'palette index' => 'bone name' with user value using animationBone_ as
// name prefix
for(std::map<unsigned int, unsigned short>::const_iterator it = oldIndexToNewIndex.begin() ; it != oldIndexToNewIndex.end() ; ++ it) {
std::ostringstream oss;
oss << "animationBone_" << it->second;
bones.setUserValue(oss.str(), oldIndexToBoneName[it->first]);
}
}
bool RigAnimationVisitor::isProcessed(osg::Drawable& node) {
return _processed.find(&node) != _processed.end();
}
void RigAnimationVisitor::setProcessed(osg::Drawable& node) {
_processed.insert(&node);
}

View File

@ -12,44 +12,13 @@ public:
GeometryUniqueVisitor("RigAttributesVisitor") GeometryUniqueVisitor("RigAttributesVisitor")
{} {}
void process(osgAnimation::RigGeometry& rigGeometry) { 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*/) { void process(osg::Geometry& /*geometry*/) {
return; return;
} }
protected: protected:
int getPropertyIndex(const osg::Geometry& geometry, const std::string& property) { 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 #endif

View File

@ -0,0 +1,35 @@
#include "RigAttributesVisitor"
void RigAttributesVisitor::process(osgAnimation::RigGeometry& rigGeometry) {
osg::Geometry* source = rigGeometry.getSourceGeometry();
if(source) {
int sourceBones = getPropertyIndex(*source, std::string("bones"));
int rigBones = getPropertyIndex(rigGeometry, std::string("bones"));
if(sourceBones >= 0) {
rigBones = (rigBones >= 0 ? rigBones : rigGeometry.getNumVertexAttribArrays());
rigGeometry.setVertexAttribArray(rigBones, source->getVertexAttribArray(sourceBones));
source->setVertexAttribArray(sourceBones, 0);
}
int sourceWeights = getPropertyIndex(*source, std::string("weights"));
int rigWeights = getPropertyIndex(rigGeometry, std::string("weights"));
if(sourceWeights >= 0) {
rigWeights = (rigWeights >= 0 ? rigWeights : rigGeometry.getNumVertexAttribArrays());
rigGeometry.setVertexAttribArray(rigWeights, source->getVertexAttribArray(sourceWeights));
source->setVertexAttribArray(sourceWeights, 0);
}
}
}
int RigAttributesVisitor::getPropertyIndex(const osg::Geometry& geometry, const std::string& property) {
for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) {
const osg::Array* attribute = geometry.getVertexAttribArray(i);
bool isProperty = false;
if(attribute && attribute->getUserValue(property, isProperty) && isProperty) {
return i;
}
}
return -1;
}

View File

@ -1,372 +1,10 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */ /* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */
#include <vector>
#include <list>
#include <set>
#include <limits>
#include <algorithm>
#include <cmath>
#include <cassert>
#include <osg/Geometry> #include <osg/Geometry>
#include <osg/Array>
#include <osg/Notify>
#include <osgAnimation/MorphGeometry> #include <osgAnimation/MorphGeometry>
#include <osgUtil/MeshOptimizers>
#include "GeometryUniqueVisitor" #include "GeometryUniqueVisitor"
#include "TriangleMeshGraph" #include "TriangleMeshSmoother"
// 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 <class ARRAY>
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<osg::Vec3Array*>(_geometry.getNormalArray());
osg::Vec3Array* positions = dynamic_cast<osg::Vec3Array*>(_geometry.getVertexArray());
if(!positions || !normals || normals->getNumElements() != positions->getNumElements()) {
OSG_WARN << std::endl
<< "Warning: [smoothVertexNormals] [[normals]] Geometry '" << _geometry.getName()
<< "' has invalid positions/normals";
return;
}
for(unsigned int index = 0 ; index < positions->getNumElements() ; ++ index) {
std::vector<IndexVector> 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<IndexVector>::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<unsigned int> processed;
std::vector<IndexVector> oneRing = _graph->vertexOneRing(index, _creaseAngle);
for(std::vector<IndexVector>::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) {
osg::Vec3f clusterNormal = cumulateTriangleNormals(*cluster);
clusterNormal.normalize();
std::set<unsigned int> 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<unsigned int>::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 { class SmoothNormalVisitor : public GeometryUniqueVisitor {

View File

@ -10,15 +10,19 @@
class StatLogger class StatLogger
{ {
public: public:
StatLogger(const std::string& label): _label(label) StatLogger(const std::string& label):
_label(label)
{ {
_start = _stop = getTick(); _start = _stop = getTick();
} }
~StatLogger() { ~StatLogger() {
_stop = getTick(); _stop = getTick();
OSG_INFO << std::flush
OSG_INFO << std::endl
<< "Info: " << _label << " timing: " << getElapsedSeconds() << "s" << "Info: " << _label << " timing: " << getElapsedSeconds() << "s"
<< std::endl << std::flush; << std::endl;
} }
protected: protected:

View File

@ -19,259 +19,36 @@ public:
typedef std::map<osg::Array*, const osg::Array*>::iterator BufferIterator; typedef std::map<osg::Array*, const osg::Array*>::iterator BufferIterator;
typedef std::map<unsigned int, unsigned int> IndexMapping; typedef std::map<unsigned int, unsigned int> IndexMapping;
SubGeometry(const osg::Geometry& source, SubGeometry(const osg::Geometry&,
const std::vector<unsigned int>& triangles, const std::vector<unsigned int>&,
const std::vector<unsigned int>& lines, const std::vector<unsigned int>&,
const std::vector<unsigned int>& wireframe, const std::vector<unsigned int>&,
const std::vector<unsigned int>& points) const std::vector<unsigned int>&);
{
// 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<const osgAnimation::MorphGeometry*>(&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<osg::StateSet*>(source.getStateSet()));
}
addSourceBuffers(_geometry.get(), source);
// process morph targets if needed
if(const osgAnimation::MorphGeometry* morphSource = dynamic_cast<const osgAnimation::MorphGeometry*>(&source)) {
osgAnimation::MorphGeometry* morph = dynamic_cast<osgAnimation::MorphGeometry*>(_geometry.get());
if (morph)
{
const osgAnimation::MorphGeometry::MorphTargetList& morphTargetList = morphSource->getMorphTargetList();
osgAnimation::MorphGeometry::MorphTargetList::const_iterator targetSource;
for(targetSource = morphTargetList.begin() ; targetSource != morphTargetList.end() ; ++ targetSource) {
if(targetSource->getGeometry()) {
osg::Geometry* target = new osg::Geometry;
addSourceBuffers(target, *targetSource->getGeometry());
morph->addMorphTarget(target, targetSource->getWeight());
}
}
}
}
// remap primitives indices by decreasing ordering (triangles > lines > wireframe > points)
for(unsigned int i = 0 ; i < triangles.size() ; i += 3) {
copyTriangle(triangles[i], triangles[i + 1], triangles[i + 2]);
}
for(unsigned int i = 0 ; i < lines.size() ; i += 2) {
copyEdge(lines[i], lines[i + 1], false);
}
for(unsigned int i = 0 ; i < wireframe.size() ; i += 2) {
copyEdge(wireframe[i], wireframe[i + 1], true);
}
for(unsigned int i = 0 ; i < points.size() ; ++ i) {
copyPoint(points[i]);
}
// remap vertex buffers accordingly to primitives
for(BufferIterator it = _bufferMap.begin() ; it != _bufferMap.end() ; ++ it) {
if(it->first) {
copyFrom(*(it->second), *(it->first));
}
}
}
osg::Geometry* geometry() const { osg::Geometry* geometry() const {
return _geometry.get(); return _geometry.get();
} }
protected: protected:
void addSourceBuffers(osg::Geometry* geometry, const osg::Geometry& source) { osg::Array* makeVertexBuffer(const osg::Array*, bool /*copyUserData*/=true);
// create necessary vertex containers
const osg::Array* array = 0;
geometry->setName(source.getName()); void addSourceBuffers(osg::Geometry*, const osg::Geometry&);
const osg::Array* vertexArray(const osg::Array* array);
// collect all buffers that are BIND_PER_VERTEX for eventual vertex duplication unsigned int mapVertex(unsigned int);
if( (array = vertexArray(source.getVertexArray())) ) {
geometry->setVertexArray(makeVertexBuffer(array));
}
if( (array = vertexArray(source.getNormalArray())) ){
geometry->setNormalArray(makeVertexBuffer(array));
}
if( (array = vertexArray(source.getColorArray())) ){
geometry->setColorArray(makeVertexBuffer(array));
}
if( (array = vertexArray(source.getSecondaryColorArray())) ){
geometry->setSecondaryColorArray(makeVertexBuffer(array));
}
if( (array = vertexArray(source.getFogCoordArray())) ){
geometry->setFogCoordArray(makeVertexBuffer(array));
}
for(unsigned int i = 0; i < source.getNumVertexAttribArrays(); ++ i) {
if( (array = vertexArray(source.getVertexAttribArray(i))) ){
geometry->setVertexAttribArray(i, makeVertexBuffer(array));
}
}
for(unsigned int i = 0; i < source.getNumTexCoordArrays(); ++ i) {
if( (array = vertexArray(source.getTexCoordArray(i))) ){
geometry->setTexCoordArray(i, makeVertexBuffer(array));
}
}
}
void copyTriangle(unsigned int v1, unsigned int v2, unsigned int v3) {
osg::DrawElements* triangles = getOrCreateTriangles();
triangles->addElement(mapVertex(v1));
triangles->addElement(mapVertex(v2));
triangles->addElement(mapVertex(v3));
}
void copyEdge(unsigned int v1, unsigned int v2, bool wireframe) {
osg::DrawElements* edges = getOrCreateLines(wireframe);
edges->addElement(mapVertex(v1));
edges->addElement(mapVertex(v2));
}
void copyPoint(unsigned int v1) {
osg::DrawElements* points = getOrCreatePoints();
points->addElement(mapVertex(v1));
}
const osg::Array* vertexArray(const osg::Array* array) {
// filter invalid vertex buffers
if (array && array->getNumElements() && array->getBinding() == osg::Array::BIND_PER_VERTEX) {
return array;
}
else {
return 0;
}
}
inline osg::Array* makeVertexBuffer(const osg::Array* array, bool copyUserData=true) {
osg::Array* buffer = array ? osg::cloneType(array) : 0;
if(buffer) {
buffer->setBinding(osg::Array::BIND_PER_VERTEX);
if(copyUserData && array->getUserDataContainer()) {
buffer->setUserDataContainer(osg::clone(array->getUserDataContainer(), osg::CopyOp::DEEP_COPY_ALL));
}
_bufferMap[buffer] = array;
}
return buffer;
}
void copyTriangle(unsigned int, unsigned int, unsigned int);
void copyEdge(unsigned int, unsigned int, bool);
void copyPoint(unsigned int);
void copyFrom(const osg::Array&, osg::Array&);
template<typename C> template<typename C>
void copyValues(const C& src, C& dst) { 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) \ osg::DrawElements* getOrCreateTriangles();
if (dynamic_cast<const T*>(&src)) \ osg::DrawElements* getOrCreateLines(bool);
{ return copyValues<T>(dynamic_cast<const T&>(src), dynamic_cast<T&>(dst)); } osg::DrawElements* getOrCreatePoints();
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"];
}
protected:
osg::ref_ptr<osg::Geometry> _geometry; osg::ref_ptr<osg::Geometry> _geometry;
std::map<osg::Array*, const osg::Array*> _bufferMap; std::map<osg::Array*, const osg::Array*> _bufferMap;
std::map<unsigned int, unsigned int> _indexMap; std::map<unsigned int, unsigned int> _indexMap;

View File

@ -0,0 +1,261 @@
#include "SubGeometry"
SubGeometry::SubGeometry(const osg::Geometry& source,
const std::vector<unsigned int>& triangles,
const std::vector<unsigned int>& lines,
const std::vector<unsigned int>& wireframe,
const std::vector<unsigned int>& 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<const osgAnimation::MorphGeometry*>(&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<osg::StateSet*>(source.getStateSet()));
}
addSourceBuffers(_geometry.get(), source);
// process morph targets if needed
if(const osgAnimation::MorphGeometry* morphSource = dynamic_cast<const osgAnimation::MorphGeometry*>(&source)) {
osgAnimation::MorphGeometry* morph = dynamic_cast<osgAnimation::MorphGeometry*>(_geometry.get());
if (morph)
{
const osgAnimation::MorphGeometry::MorphTargetList& morphTargetList = morphSource->getMorphTargetList();
osgAnimation::MorphGeometry::MorphTargetList::const_iterator targetSource;
for(targetSource = morphTargetList.begin() ; targetSource != morphTargetList.end() ; ++ targetSource) {
if(targetSource->getGeometry()) {
osg::Geometry* target = new osg::Geometry;
addSourceBuffers(target, *targetSource->getGeometry());
morph->addMorphTarget(target, targetSource->getWeight());
}
}
}
}
// remap primitives indices by decreasing ordering (triangles > lines > wireframe > points)
for(unsigned int i = 0 ; i < triangles.size() ; i += 3) {
copyTriangle(triangles[i], triangles[i + 1], triangles[i + 2]);
}
for(unsigned int i = 0 ; i < lines.size() ; i += 2) {
copyEdge(lines[i], lines[i + 1], false);
}
for(unsigned int i = 0 ; i < wireframe.size() ; i += 2) {
copyEdge(wireframe[i], wireframe[i + 1], true);
}
for(unsigned int i = 0 ; i < points.size() ; ++ i) {
copyPoint(points[i]);
}
// remap vertex buffers accordingly to primitives
for(BufferIterator it = _bufferMap.begin() ; it != _bufferMap.end() ; ++ it) {
if(it->first) {
copyFrom(*(it->second), *(it->first));
}
}
}
void SubGeometry::addSourceBuffers(osg::Geometry* geometry, const osg::Geometry& source) {
// create necessary vertex containers
const osg::Array* array = 0;
geometry->setName(source.getName());
// collect all buffers that are BIND_PER_VERTEX for eventual vertex duplication
if( (array = vertexArray(source.getVertexArray())) ) {
geometry->setVertexArray(makeVertexBuffer(array));
}
if( (array = vertexArray(source.getNormalArray())) ){
geometry->setNormalArray(makeVertexBuffer(array));
}
if( (array = vertexArray(source.getColorArray())) ){
geometry->setColorArray(makeVertexBuffer(array));
}
if( (array = vertexArray(source.getSecondaryColorArray())) ){
geometry->setSecondaryColorArray(makeVertexBuffer(array));
}
if( (array = vertexArray(source.getFogCoordArray())) ){
geometry->setFogCoordArray(makeVertexBuffer(array));
}
for(unsigned int i = 0; i < source.getNumVertexAttribArrays(); ++ i) {
if( (array = vertexArray(source.getVertexAttribArray(i))) ){
geometry->setVertexAttribArray(i, makeVertexBuffer(array));
}
}
for(unsigned int i = 0; i < source.getNumTexCoordArrays(); ++ i) {
if( (array = vertexArray(source.getTexCoordArray(i))) ){
geometry->setTexCoordArray(i, makeVertexBuffer(array));
}
}
}
void SubGeometry::copyTriangle(unsigned int v1, unsigned int v2, unsigned int v3) {
osg::DrawElements* triangles = getOrCreateTriangles();
triangles->addElement(mapVertex(v1));
triangles->addElement(mapVertex(v2));
triangles->addElement(mapVertex(v3));
}
void SubGeometry::copyEdge(unsigned int v1, unsigned int v2, bool wireframe) {
osg::DrawElements* edges = getOrCreateLines(wireframe);
edges->addElement(mapVertex(v1));
edges->addElement(mapVertex(v2));
}
void SubGeometry::copyPoint(unsigned int v1) {
osg::DrawElements* points = getOrCreatePoints();
points->addElement(mapVertex(v1));
}
const osg::Array* SubGeometry::vertexArray(const osg::Array* array) {
// filter invalid vertex buffers
if (array && array->getNumElements() && array->getBinding() == osg::Array::BIND_PER_VERTEX) {
return array;
}
else {
return 0;
}
}
osg::Array* SubGeometry::makeVertexBuffer(const osg::Array* array, bool copyUserData) {
osg::Array* buffer = array ? osg::cloneType(array) : 0;
if(buffer) {
buffer->setBinding(osg::Array::BIND_PER_VERTEX);
if(copyUserData && array->getUserDataContainer()) {
buffer->setUserDataContainer(osg::clone(array->getUserDataContainer(), osg::CopyOp::DEEP_COPY_ALL));
}
_bufferMap[buffer] = array;
}
return buffer;
}
template<typename C>
void SubGeometry::copyValues(const C& src, C& dst) {
dst.resize(_indexMap.size());
for(IndexMapping::const_iterator remapper = _indexMap.begin() ; remapper != _indexMap.end() ; ++ remapper) {
dst[remapper->second] = src[remapper->first];
}
}
#define COPY_TEMPLATE(T) \
if (dynamic_cast<const T*>(&src)) \
{ copyValues<T>(dynamic_cast<const T&>(src), dynamic_cast<T&>(dst)); }
void SubGeometry::copyFrom(const osg::Array& src, osg::Array& dst) {
COPY_TEMPLATE(osg::Vec2Array);
COPY_TEMPLATE(osg::Vec3Array);
COPY_TEMPLATE(osg::Vec4Array);
COPY_TEMPLATE(osg::Vec2dArray);
COPY_TEMPLATE(osg::Vec3dArray);
COPY_TEMPLATE(osg::Vec4dArray);
COPY_TEMPLATE(osg::ByteArray);
COPY_TEMPLATE(osg::ShortArray);
COPY_TEMPLATE(osg::IntArray);
COPY_TEMPLATE(osg::UByteArray);
COPY_TEMPLATE(osg::UShortArray);
COPY_TEMPLATE(osg::UIntArray);
COPY_TEMPLATE(osg::FloatArray);
COPY_TEMPLATE(osg::DoubleArray);
COPY_TEMPLATE(osg::Vec2iArray);
COPY_TEMPLATE(osg::Vec3iArray);
COPY_TEMPLATE(osg::Vec4iArray);
COPY_TEMPLATE(osg::Vec2uiArray);
COPY_TEMPLATE(osg::Vec3uiArray);
COPY_TEMPLATE(osg::Vec4uiArray);
COPY_TEMPLATE(osg::Vec2sArray);
COPY_TEMPLATE(osg::Vec3sArray);
COPY_TEMPLATE(osg::Vec4sArray);
COPY_TEMPLATE(osg::Vec2usArray);
COPY_TEMPLATE(osg::Vec3usArray);
COPY_TEMPLATE(osg::Vec4usArray);
COPY_TEMPLATE(osg::Vec2bArray);
COPY_TEMPLATE(osg::Vec3bArray);
COPY_TEMPLATE(osg::Vec4bArray);
COPY_TEMPLATE(osg::Vec2ubArray);
COPY_TEMPLATE(osg::Vec3ubArray);
COPY_TEMPLATE(osg::Vec4ubArray);
COPY_TEMPLATE(osg::MatrixfArray);
COPY_TEMPLATE(osg::QuatArray);
}
unsigned int SubGeometry::mapVertex(unsigned int i) {
if(_indexMap.find(i) == _indexMap.end()) {
unsigned int index = _indexMap.size();
_indexMap[i] = index;
}
return _indexMap[i];
}
osg::DrawElements* SubGeometry::getOrCreateTriangles() {
if(_primitives.find("triangles") == _primitives.end()) {
_primitives["triangles"] = new osg::DrawElementsUInt(GL_TRIANGLES);
_geometry->addPrimitiveSet(_primitives["triangles"]);
}
return _primitives["triangles"];
}
osg::DrawElements* SubGeometry::getOrCreateLines(bool wireframe) {
std::string label = wireframe ? "wireframe" : "lines";
if(_primitives.find(label) == _primitives.end()) {
_primitives[label] = new osg::DrawElementsUInt(GL_LINES);
if(wireframe) {
_primitives[label]->setUserValue("wireframe", true);
}
_geometry->addPrimitiveSet(_primitives[label]);
}
return _primitives[label];
}
osg::DrawElements* SubGeometry::getOrCreatePoints() {
if(_primitives.find("points") == _primitives.end()) {
_primitives["points"] = new osg::DrawElementsUInt(GL_POINTS);
_geometry->addPrimitiveSet(_primitives["points"]);
}
return _primitives["points"];
}

View File

@ -28,110 +28,13 @@
class TangentSpaceVisitor : public GeometryUniqueVisitor class TangentSpaceVisitor : public GeometryUniqueVisitor
{ {
public: public:
TangentSpaceVisitor(int textureUnit = 0): TangentSpaceVisitor(int textureUnit=0):
GeometryUniqueVisitor("TangentSpaceVisitor"), GeometryUniqueVisitor("TangentSpaceVisitor"),
_textureUnit(textureUnit) _textureUnit(textureUnit)
{} {}
void process(osgAnimation::MorphGeometry& morphGeometry) { void process(osgAnimation::MorphGeometry&);
process(static_cast<osg::Geometry&>(morphGeometry)); void process(osg::Geometry&);
osgAnimation::MorphGeometry::MorphTargetList& targets = morphGeometry.getMorphTargetList();
for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) {
osg::Geometry* geometry = target->getGeometry();
bool useParentMorphTexCoord = geometry->getTexCoordArrayList().empty();
if(useParentMorphTexCoord) {
// tangent space require tex coords; in case a target has no tex coords, we try to
// bind the parent geometry tex coords
geometry->setTexCoordArrayList(morphGeometry.getTexCoordArrayList());
}
process(*geometry);
if(useParentMorphTexCoord) {
// drop parent tex coords after tangent space computation
geometry->setTexCoordArrayList(osg::Geometry::ArrayList());
}
}
}
void process(osg::Geometry& geom) {
// We don't have to recompute the tangent space if we already have the data
int tangentIndex = -1;
if (geom.getUserValue(std::string("tangent"), tangentIndex) && tangentIndex != -1)
{
if(geom.getVertexAttribArray(tangentIndex)) {
OSG_INFO << "[TangentSpaceVisitor::apply] Geometry '" << geom.getName()
<< "' The tangent space is not recomputed as it was given within the original file" << std::endl;
geom.getVertexAttribArray(tangentIndex)->setUserValue("tangent", true);
return;
}
}
if (!geom.getTexCoordArray(_textureUnit)){
int texUnit = 0;
bool found = false;
while(texUnit < 32){
if (_textureUnit != texUnit && geom.getTexCoordArray(texUnit)){
_textureUnit = texUnit;
found = true;
break;
}
texUnit++;
}
if (!found)
return;
}
osg::ref_ptr<osgUtil::TangentSpaceGenerator> generator = new osgUtil::TangentSpaceGenerator;
generator->generate(&geom, _textureUnit);
// keep original normal array
if (!geom.getNormalArray()) {
if (generator->getNormalArray()) {
osg::Vec3Array* vec3Normals = new osg::Vec3Array();
osg::Vec4Array* vec4Normals = generator->getNormalArray();
for (unsigned int i = 0; i < vec4Normals->size(); i++) {
osg::Vec3 n = osg::Vec3((*vec4Normals)[i][0],
(*vec4Normals)[i][1],
(*vec4Normals)[i][2]);
vec3Normals->push_back(n);
}
geom.setNormalArray(vec3Normals, osg::Array::BIND_PER_VERTEX);
}
}
if (generator->getTangentArray()) {
osg::Vec4Array* normal = generator->getNormalArray();
osg::Vec4Array* tangent = generator->getTangentArray();
osg::Vec4Array* tangent2 = generator->getBinormalArray();
osg::Vec4Array* finalTangent = osg::clone(generator->getTangentArray(), osg::CopyOp::DEEP_COPY_ALL);
for (unsigned int i = 0; i < tangent->size(); i++) {
osg::Vec3 n = osg::Vec3((*normal)[i][0],
(*normal)[i][1],
(*normal)[i][2]);
osg::Vec3 t = osg::Vec3((*tangent)[i][0],
(*tangent)[i][1],
(*tangent)[i][2]);
osg::Vec3 t2 = osg::Vec3((*tangent2)[i][0],
(*tangent2)[i][1],
(*tangent2)[i][2]);
// Gram-Schmidt orthogonalize
osg::Vec3 t3 = (t - n * (n * t));
t3.normalize();
(*finalTangent)[i] = osg::Vec4(t3, 0.0);
// Calculate handedness
(*finalTangent)[i][3] = (((n ^ t) * t2) < 0.0) ? -1.0 : 1.0;
// The bitangent vector B is then given by B = (N × T) · Tw
}
finalTangent->setUserValue("tangent", true);
tangentIndex = (tangentIndex >= 0 ? tangentIndex : geom.getNumVertexAttribArrays()) ;
geom.setVertexAttribArray(tangentIndex, finalTangent, osg::Array::BIND_PER_VERTEX);
}
}
protected: protected:
int _textureUnit; int _textureUnit;

View File

@ -0,0 +1,103 @@
#include "TangentSpaceVisitor"
void TangentSpaceVisitor::process(osgAnimation::MorphGeometry& morphGeometry) {
process(static_cast<osg::Geometry&>(morphGeometry));
osgAnimation::MorphGeometry::MorphTargetList& targets = morphGeometry.getMorphTargetList();
for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) {
osg::Geometry* geometry = target->getGeometry();
bool useParentMorphTexCoord = geometry->getTexCoordArrayList().empty();
if(useParentMorphTexCoord) {
// tangent space require tex coords; in case a target has no tex coords, we try to
// bind the parent geometry tex coords
geometry->setTexCoordArrayList(morphGeometry.getTexCoordArrayList());
}
process(*geometry);
if(useParentMorphTexCoord) {
// drop parent tex coords after tangent space computation
geometry->setTexCoordArrayList(osg::Geometry::ArrayList());
}
}
}
void TangentSpaceVisitor::process(osg::Geometry& geometry) {
// We don't have to recompute the tangent space if we already have the data
int tangentIndex = -1;
if (geometry.getUserValue(std::string("tangent"), tangentIndex) && tangentIndex != -1)
{
if(geometry.getVertexAttribArray(tangentIndex)) {
OSG_INFO << "[TangentSpaceVisitor::apply] Geometry '" << geometry.getName()
<< "' The tangent space is not recomputed as it was given within the original file" << std::endl;
geometry.getVertexAttribArray(tangentIndex)->setUserValue("tangent", true);
return;
}
}
if (!geometry.getTexCoordArray(_textureUnit)){
int texUnit = 0;
bool found = false;
while(texUnit < 32){
if (_textureUnit != texUnit && geometry.getTexCoordArray(texUnit)){
_textureUnit = texUnit;
found = true;
break;
}
texUnit++;
}
if (!found)
return;
}
osg::ref_ptr<osgUtil::TangentSpaceGenerator> generator = new osgUtil::TangentSpaceGenerator;
generator->generate(&geometry, _textureUnit);
// keep original normal array
if (!geometry.getNormalArray()) {
if (generator->getNormalArray()) {
osg::Vec3Array* vec3Normals = new osg::Vec3Array();
osg::Vec4Array* vec4Normals = generator->getNormalArray();
for (unsigned int i = 0; i < vec4Normals->size(); i++) {
osg::Vec3 n = osg::Vec3((*vec4Normals)[i][0],
(*vec4Normals)[i][1],
(*vec4Normals)[i][2]);
vec3Normals->push_back(n);
}
geometry.setNormalArray(vec3Normals, osg::Array::BIND_PER_VERTEX);
}
}
if (generator->getTangentArray()) {
osg::Vec4Array* normal = generator->getNormalArray();
osg::Vec4Array* tangent = generator->getTangentArray();
osg::Vec4Array* tangent2 = generator->getBinormalArray();
osg::Vec4Array* finalTangent = osg::clone(generator->getTangentArray(), osg::CopyOp::DEEP_COPY_ALL);
for (unsigned int i = 0; i < tangent->size(); i++) {
osg::Vec3 n = osg::Vec3((*normal)[i][0],
(*normal)[i][1],
(*normal)[i][2]);
osg::Vec3 t = osg::Vec3((*tangent)[i][0],
(*tangent)[i][1],
(*tangent)[i][2]);
osg::Vec3 t2 = osg::Vec3((*tangent2)[i][0],
(*tangent2)[i][1],
(*tangent2)[i][2]);
// Gram-Schmidt orthogonalize
osg::Vec3 t3 = (t - n * (n * t));
t3.normalize();
(*finalTangent)[i] = osg::Vec4(t3, 0.0);
// Calculate handedness
(*finalTangent)[i][3] = (((n ^ t) * t2) < 0.0) ? -1.0 : 1.0;
// The bitangent vector B is then given by B = (N × T) · Tw
}
finalTangent->setUserValue("tangent", true);
tangentIndex = (tangentIndex >= 0 ? tangentIndex : geometry.getNumVertexAttribArrays()) ;
geometry.setVertexAttribArray(tangentIndex, finalTangent, osg::Array::BIND_PER_VERTEX);
}
}

View File

@ -0,0 +1,134 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */
#include <vector>
#include <list>
#include <set>
#include <limits>
#include <algorithm>
#include <cmath>
#include <cassert>
#include <osg/Geometry>
#include <osg/Array>
#include <osg/Notify>
#include <osgAnimation/MorphGeometry>
#include <osgUtil/MeshOptimizers>
#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 <class ARRAY>
void apply_imp(ARRAY& array) {
_end = array.size();
array.push_back(array[_i]);
}
virtual void apply(osg::ByteArray& array) { apply_imp(array); }
virtual void apply(osg::ShortArray& array) { apply_imp(array); }
virtual void apply(osg::IntArray& array) { apply_imp(array); }
virtual void apply(osg::UByteArray& array) { apply_imp(array); }
virtual void apply(osg::UShortArray& array) { apply_imp(array); }
virtual void apply(osg::UIntArray& array) { apply_imp(array); }
virtual void apply(osg::FloatArray& array) { apply_imp(array); }
virtual void apply(osg::DoubleArray& array) { apply_imp(array); }
virtual void apply(osg::Vec2Array& array) { apply_imp(array); }
virtual void apply(osg::Vec3Array& array) { apply_imp(array); }
virtual void apply(osg::Vec4Array& array) { apply_imp(array); }
virtual void apply(osg::Vec2bArray& array) { apply_imp(array); }
virtual void apply(osg::Vec3bArray& array) { apply_imp(array); }
virtual void apply(osg::Vec4bArray& array) { apply_imp(array); }
virtual void apply(osg::Vec2sArray& array) { apply_imp(array); }
virtual void apply(osg::Vec3sArray& array) { apply_imp(array); }
virtual void apply(osg::Vec4sArray& array) { apply_imp(array); }
virtual void apply(osg::Vec2iArray& array) { apply_imp(array); }
virtual void apply(osg::Vec3iArray& array) { apply_imp(array); }
virtual void apply(osg::Vec4iArray& array) { apply_imp(array); }
virtual void apply(osg::Vec2dArray& array) { apply_imp(array); }
virtual void apply(osg::Vec3dArray& array) { apply_imp(array); }
virtual void apply(osg::Vec4dArray& array) { apply_imp(array); }
virtual void apply(osg::Vec2ubArray& array) { apply_imp(array); }
virtual void apply(osg::Vec3ubArray& array) { apply_imp(array); }
virtual void apply(osg::Vec4ubArray& array) { apply_imp(array); }
virtual void apply(osg::Vec2usArray& array) { apply_imp(array); }
virtual void apply(osg::Vec3usArray& array) { apply_imp(array); }
virtual void apply(osg::Vec4usArray& array) { apply_imp(array); }
virtual void apply(osg::Vec2uiArray& array) { apply_imp(array); }
virtual void apply(osg::Vec3uiArray& array) { apply_imp(array); }
virtual void apply(osg::Vec4uiArray& array) { apply_imp(array); }
virtual void apply(osg::MatrixfArray& array) { apply_imp(array); }
virtual void apply(osg::MatrixdArray& array) { apply_imp(array); }
};
public:
TriangleMeshSmoother(osg::Geometry& geometry, float creaseAngle, bool /*comparePosition*/=false, int /*mode*/=diagnose);
~TriangleMeshSmoother() {
if(_graph) {
delete _graph;
}
}
protected:
unsigned int duplicateVertex(unsigned int);
void smoothVertexNormals(bool /*fix*/=true, bool /*force*/=false);
void computeVertexNormals();
osg::Vec3f cumulateTriangleNormals(const IndexVector&) const;
void replaceVertexIndexInTriangles(const IndexVector&, unsigned int, unsigned int);
void addArray(osg::Array*);
void updateGeometryPrimitives();
protected:
osg::Geometry& _geometry;
float _creaseAngle;
TriangleMeshGraph* _graph;
TriangleVector _triangles;
ArrayVector _vertexArrays;
int _mode; // smooth or recompute normals
};

View File

@ -0,0 +1,257 @@
#include "TriangleMeshSmoother"
TriangleMeshSmoother::TriangleMeshSmoother(osg::Geometry& geometry, float creaseAngle, bool comparePosition, int mode):
_geometry(geometry),
_creaseAngle(creaseAngle),
_graph(0),
_mode(mode)
{
if(!_geometry.getVertexArray() || !_geometry.getVertexArray()->getNumElements()) {
return;
}
osgUtil::SharedArrayOptimizer deduplicator;
deduplicator.findDuplicatedUVs(geometry);
// duplicate shared arrays as it isn't safe to duplicate vertices when arrays are shared.
if (geometry.containsSharedArrays()) {
geometry.duplicateSharedArrays();
}
if(!_geometry.getNormalArray() || _geometry.getNormalArray()->getNumElements() != _geometry.getVertexArray()->getNumElements()) {
_geometry.setNormalArray(new osg::Vec3Array(_geometry.getVertexArray()->getNumElements()), osg::Array::BIND_PER_VERTEX);
}
// build a unifier to consider deduplicated vertex indices
_graph = new TriangleMeshGraph(_geometry, comparePosition);
unsigned int nbTriangles = 0;
for(unsigned int i = 0 ; i < _geometry.getNumPrimitiveSets() ; ++ i) {
osg::PrimitiveSet* primitive = _geometry.getPrimitiveSet(i);
if(!primitive || !primitive->getNumIndices()) {
continue;
}
else if(primitive->getMode() > osg::PrimitiveSet::TRIANGLES) {
OSG_INFO << "[smoother] Cannot smooth geometry '" << _geometry.getName()
<< "' due to not tessellated primitives" << std::endl;
return;
}
else if(primitive->getMode() == osg::PrimitiveSet::TRIANGLES) {
nbTriangles += primitive->getNumIndices() / 3;
}
}
_triangles.reserve(nbTriangles);
// collect all buffers that are BIND_PER_VERTEX for eventual vertex duplication
addArray(_geometry.getVertexArray());
addArray(_geometry.getColorArray());
addArray(_geometry.getSecondaryColorArray());
addArray(_geometry.getFogCoordArray());
for(unsigned int i = 0; i < _geometry.getNumTexCoordArrays(); ++ i) {
addArray(_geometry.getTexCoordArray(i));
}
for(unsigned int i = 0; i < _geometry.getNumVertexAttribArrays(); ++ i) {
addArray(_geometry.getVertexAttribArray(i));
}
switch(_mode) {
case recompute:
computeVertexNormals();
break;
case smooth_all:
smoothVertexNormals(true, true);
break;
case smooth_flipped:
smoothVertexNormals(true, false);
break;
case diagnose:
smoothVertexNormals(false, false);
break;
};
// deduplicate UVs array that were only shared within the geometry
deduplicator.deduplicateUVs(geometry);
}
unsigned int TriangleMeshSmoother::duplicateVertex(unsigned int index) {
DuplicateVertex duplicate(index);
for(ArrayVector::iterator array = _vertexArrays.begin(); array != _vertexArrays.end(); ++ array) {
(*array)->accept(duplicate);
}
_graph->add(duplicate._end, index);
return duplicate._end;
}
void TriangleMeshSmoother::smoothVertexNormals(bool fix, bool force) {
_vertexArrays.clear(); // make sure we do not change vertex count
bool flipped = false;
osg::Vec3Array* normals = dynamic_cast<osg::Vec3Array*>(_geometry.getNormalArray());
osg::Vec3Array* positions = dynamic_cast<osg::Vec3Array*>(_geometry.getVertexArray());
if(!positions || !normals || normals->getNumElements() != positions->getNumElements()) {
OSG_WARN << std::endl
<< "Warning: [smoothVertexNormals] [[normals]] Geometry '" << _geometry.getName()
<< "' has invalid positions/normals";
return;
}
for(unsigned int index = 0 ; index < positions->getNumElements() ; ++ index) {
std::vector<IndexVector> 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<IndexVector>::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) {
smoothedNormal += cumulateTriangleNormals(*cluster);
}
float length = smoothedNormal.normalize();
if(length > 0.) {
if(force || smoothedNormal * normals->at(index) < 1.e-6) {
flipped = true;
if(fix) {
(*normals)[index] = smoothedNormal;
}
}
}
}
if(flipped) {
OSG_WARN << std::endl << "Warning: [smoothVertexNormals] [[normals]] Geometry '" << _geometry.getName() << "' ";
switch(_mode) {
case diagnose:
OSG_WARN << "has some flipped normals; please check that the shading is correct" << std::endl;
OSG_WARN << "Monitor: normal.invalid" << std::endl;
break;
case smooth_flipped:
OSG_WARN << "has some flipped normals that have been fixed" << std::endl;
OSG_WARN << "Monitor: normal.smooth_flipped" << std::endl;
break;
case smooth_all:
OSG_WARN << "normals have all been smoothed" << std::endl;
OSG_WARN << "Monitor: normal.smooth_all" << std::endl;
break;
}
}
}
void TriangleMeshSmoother::computeVertexNormals() {
osg::Vec3Array* normals = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX,
_geometry.getVertexArray()->getNumElements());
addArray(normals);
for(unsigned int i = 0 ; i < normals->getNumElements() ; ++ i) {
(*normals)[i].set(0.f, 0.f, 0.f);
}
for(VertexIterator uniqueIndex = _graph->begin() ; uniqueIndex != _graph->end() ; ++ uniqueIndex) {
unsigned int index = uniqueIndex->_index;
std::set<unsigned int> processed;
std::vector<IndexVector> oneRing = _graph->vertexOneRing(index, _creaseAngle);
for(std::vector<IndexVector>::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) {
osg::Vec3f clusterNormal = cumulateTriangleNormals(*cluster);
clusterNormal.normalize();
std::set<unsigned int> 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<unsigned int>::iterator vertex = duplicates.begin() ; vertex != duplicates.end() ; ++ vertex) {
if(processed.find(*vertex) == processed.end()) {
// vertex not yet processed
(*normals)[*vertex] = clusterNormal;
processed.insert(*vertex);
}
else {
// vertex already processed in a previous cluster: need to duplicate
unsigned int duplicate = duplicateVertex(*vertex);
replaceVertexIndexInTriangles(*cluster, *vertex, duplicate);
(*normals)[duplicate] = clusterNormal;
processed.insert(duplicate);
}
}
}
}
_geometry.setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
updateGeometryPrimitives();
OSG_WARN << std::endl <<"Warning: [computeVertexNormals] [[normals]] Geometry '" << _geometry.getName()
<< "' normals have been recomputed" << std::endl;
OSG_WARN << "Monitor: normal.recompute" << std::endl;
}
osg::Vec3f TriangleMeshSmoother::cumulateTriangleNormals(const IndexVector& triangles) const {
osg::Vec3f normal;
normal.set(0.f, 0.f, 0.f);
for(IndexVector::const_iterator triangle = triangles.begin() ; triangle != triangles.end() ; ++ triangle) {
const Triangle& t = _graph->triangle(*triangle);
normal += (t._normal * t._area);
}
return normal;
}
void TriangleMeshSmoother::replaceVertexIndexInTriangles(const IndexVector& triangles, unsigned int oldIndex, unsigned int newIndex) {
for(IndexVector::const_iterator tri = triangles.begin() ; tri != triangles.end() ; ++ tri) {
Triangle& triangle = _graph->triangle(*tri);
if(triangle.v1() == oldIndex) {
triangle.v1() = newIndex;
}
else if(triangle.v2() == oldIndex) {
triangle.v2() = newIndex;
}
else if(triangle.v3() == oldIndex) {
triangle.v3() = newIndex;
}
}
}
void TriangleMeshSmoother::addArray(osg::Array* array) {
if (array && array->getBinding() == osg::Array::BIND_PER_VERTEX) {
_vertexArrays.push_back(array);
}
}
void TriangleMeshSmoother::updateGeometryPrimitives() {
osg::Geometry::PrimitiveSetList primitives;
for(unsigned int i = 0 ; i < _geometry.getNumPrimitiveSets() ; ++ i) {
osg::PrimitiveSet* primitive = _geometry.getPrimitiveSet(i);
if(primitive && primitive->getMode() < osg::PrimitiveSet::TRIANGLES) {
primitives.push_back(primitive);
}
}
osg::DrawElementsUInt* triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
for(unsigned int i = 0 ; i < _graph->getNumTriangles() ; ++ i) {
const Triangle& triangle = _graph->triangle(i);
triangles->push_back(triangle.v1());
triangles->push_back(triangle.v2());
triangles->push_back(triangle.v3());
}
primitives.push_back(triangles);
_geometry.setPrimitiveSetList(primitives);
}

View File

@ -9,6 +9,7 @@
#include <algorithm> #include <algorithm>
#include <osg/Array> #include <osg/Array>
#include <osg/ValueObject>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osgUtil/MeshOptimizers> #include <osgUtil/MeshOptimizers>
#include <osg/TriangleIndexFunctor> #include <osg/TriangleIndexFunctor>
@ -25,6 +26,33 @@ namespace glesUtil {
using namespace osg; using namespace osg;
typedef std::vector<unsigned int> IndexList; typedef std::vector<unsigned int> IndexList;
inline bool hasPositiveWeights(const osg::Geometry* geometry) {
const osg::Vec4Array* weights = 0;
for(unsigned int i = 0 ; i < geometry->getNumVertexAttribArrays() ; ++ i) {
const osg::Array* attribute = geometry->getVertexAttribArray(i);
bool isWeights = false;
if(attribute && attribute->getUserValue("weights", isWeights) && isWeights) {
weights = dynamic_cast<const osg::Vec4Array*>(attribute);
break;
}
}
if(weights) {
for(osg::Vec4Array::const_iterator weight = weights->begin() ; weight != weights->end() ; ++ weight) {
const osg::Vec4& value = *weight;
// weights are sorted in decreasing order
if(value[0] != 0.f) {
return true;
}
}
}
return false;
}
// A helper class that gathers up all the attribute arrays of an // A helper class that gathers up all the attribute arrays of an
// osg::Geometry object that are BIND_PER_VERTEX and runs an // osg::Geometry object that are BIND_PER_VERTEX and runs an
// ArrayVisitor on them. // ArrayVisitor on them.