Merge pull request #105 from marchelbling/compiler-friendly-gles
Moves gles visitors implementation in cpp files
This commit is contained in:
commit
0faebbef33
@ -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;
|
||||||
|
235
src/osgPlugins/gles/AABBonBoneVisitor.cpp
Normal file
235
src/osgPlugins/gles/AABBonBoneVisitor.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
589
src/osgPlugins/gles/AnimationCleanerVisitor.cpp
Normal file
589
src/osgPlugins/gles/AnimationCleanerVisitor.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
283
src/osgPlugins/gles/BindPerVertexVisitor.cpp
Normal file
283
src/osgPlugins/gles/BindPerVertexVisitor.cpp
Normal 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;
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
142
src/osgPlugins/gles/DetachPrimitiveVisitor.cpp
Normal file
142
src/osgPlugins/gles/DetachPrimitiveVisitor.cpp
Normal 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;
|
||||||
|
}
|
78
src/osgPlugins/gles/GeometryIndexSplitter
Normal file
78
src/osgPlugins/gles/GeometryIndexSplitter
Normal 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
|
285
src/osgPlugins/gles/GeometryIndexSplitter.cpp
Normal file
285
src/osgPlugins/gles/GeometryIndexSplitter.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
70
src/osgPlugins/gles/GeometrySplitterVisitor.cpp
Normal file
70
src/osgPlugins/gles/GeometrySplitterVisitor.cpp
Normal 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)));
|
||||||
|
}
|
@ -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;
|
||||||
};
|
};
|
||||||
|
155
src/osgPlugins/gles/RigAnimationVisitor.cpp
Normal file
155
src/osgPlugins/gles/RigAnimationVisitor.cpp
Normal 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);
|
||||||
|
}
|
@ -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
|
||||||
|
35
src/osgPlugins/gles/RigAttributesVisitor.cpp
Normal file
35
src/osgPlugins/gles/RigAttributesVisitor.cpp
Normal 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;
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
261
src/osgPlugins/gles/SubGeometry.cpp
Normal file
261
src/osgPlugins/gles/SubGeometry.cpp
Normal 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"];
|
||||||
|
}
|
@ -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;
|
||||||
|
103
src/osgPlugins/gles/TangentSpaceVisitor.cpp
Normal file
103
src/osgPlugins/gles/TangentSpaceVisitor.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
134
src/osgPlugins/gles/TriangleMeshSmoother
Normal file
134
src/osgPlugins/gles/TriangleMeshSmoother
Normal 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
|
||||||
|
};
|
257
src/osgPlugins/gles/TriangleMeshSmoother.cpp
Normal file
257
src/osgPlugins/gles/TriangleMeshSmoother.cpp
Normal 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);
|
||||||
|
}
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user