Moves gles visitors implementation in cpp files
This commit is contained in:
parent
fd0af1d9df
commit
a44057b0df
@ -35,234 +35,33 @@ public:
|
||||
typedef std::vector<osgAnimation::Bone*> BoneList;
|
||||
typedef std::vector<osgAnimation::RigGeometry*> RigGeometryList;
|
||||
|
||||
|
||||
ComputeAABBOnBoneVisitor(bool createGeometry):
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
||||
_root(0),
|
||||
_createGeometry(createGeometry)
|
||||
{}
|
||||
|
||||
void apply(osg::Transform& node) {
|
||||
if(!_root)
|
||||
_root = dynamic_cast<osgAnimation::Skeleton*>(&node);
|
||||
|
||||
osgAnimation::Bone * b = dynamic_cast<osgAnimation::Bone*>(&node);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
void apply(osg::Transform&);
|
||||
void apply(osg::Geometry&);
|
||||
void apply(osgAnimation::Bone &);
|
||||
void apply(osgAnimation::RigGeometry &);
|
||||
void computeBoundingBoxOnBones();
|
||||
|
||||
protected:
|
||||
osg::Geometry* createBox(const osg::BoundingBox &bb, const osg::Matrix &transform,
|
||||
float ratio=1.0, osg::Vec4 color=osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)) {
|
||||
osg::Geometry *cube = new osg::Geometry;
|
||||
osg::Geometry* createBox(const osg::BoundingBox &,
|
||||
const osg::Matrix &,
|
||||
float /*ratio*/=1.0,
|
||||
osg::Vec4 /*color*/=osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
osg::Vec3 center = bb.center();
|
||||
double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50;
|
||||
double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50;
|
||||
double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50;
|
||||
void serializeBoundingBox(const osg::BoundingBox &,
|
||||
const osg::Matrix &,
|
||||
osgAnimation::Bone &,
|
||||
float /*ratio*/=1.0);
|
||||
|
||||
halfLenghtX *= ratio;
|
||||
halfLenghtY *= ratio;
|
||||
halfLenghtZ *= ratio;
|
||||
|
||||
osg::Vec3Array *cubeVertices = new osg::Vec3Array;
|
||||
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
|
||||
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
|
||||
cube->setVertexArray(cubeVertices);
|
||||
|
||||
{
|
||||
osg::DrawElementsUInt* up = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
up->push_back(4);
|
||||
up->push_back(5);
|
||||
up->push_back(1);
|
||||
up->push_back(0);
|
||||
cube->addPrimitiveSet(up);
|
||||
}
|
||||
|
||||
{
|
||||
osg::DrawElementsUInt* down = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
down->push_back(2);
|
||||
down->push_back(6);
|
||||
down->push_back(7);
|
||||
down->push_back(3);
|
||||
cube->addPrimitiveSet(down);
|
||||
}
|
||||
|
||||
{
|
||||
osg::DrawElementsUInt* left = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
left->push_back(2);
|
||||
left->push_back(3);
|
||||
left->push_back(0);
|
||||
left->push_back(1);
|
||||
cube->addPrimitiveSet(left);
|
||||
}
|
||||
|
||||
{
|
||||
osg::DrawElementsUInt* right = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
right->push_back(7);
|
||||
right->push_back(6);
|
||||
right->push_back(5);
|
||||
right->push_back(4);
|
||||
cube->addPrimitiveSet(right);
|
||||
}
|
||||
|
||||
{
|
||||
osg::DrawElementsUInt* front = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
front->push_back(3);
|
||||
front->push_back(7);
|
||||
front->push_back(4);
|
||||
front->push_back(0);
|
||||
cube->addPrimitiveSet(front);
|
||||
}
|
||||
|
||||
{
|
||||
osg::DrawElementsUInt* back = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
back->push_back(6);
|
||||
back->push_back(2);
|
||||
back->push_back(1);
|
||||
back->push_back(5);
|
||||
cube->addPrimitiveSet(back);
|
||||
}
|
||||
|
||||
osg::Vec4Array* colors = new osg::Vec4Array;
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
|
||||
cube->setColorArray(colors);
|
||||
cube->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
|
||||
return cube;
|
||||
}
|
||||
|
||||
void serializeBoundingBox(const osg::BoundingBox &bb, const osg::Matrix &transform, osgAnimation::Bone &b, float ratio = 1.0) {
|
||||
osg::Vec3 center = bb.center();
|
||||
double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50;
|
||||
double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50;
|
||||
double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50;
|
||||
|
||||
halfLenghtX *= ratio;
|
||||
halfLenghtY *= ratio;
|
||||
halfLenghtZ *= ratio;
|
||||
|
||||
osg::BoundingBox serializedBB;
|
||||
|
||||
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
|
||||
b.setUserValue("AABBonBone_min", serializedBB._min);
|
||||
b.setUserValue("AABBonBone_max", serializedBB._max);
|
||||
}
|
||||
|
||||
void updateBones() {
|
||||
osgUtil::UpdateVisitor update;
|
||||
_root->accept(update);
|
||||
}
|
||||
|
||||
void updateRigGeometries() {
|
||||
for (unsigned int i = 0, size = _rigGeometries.size(); i < size; i++) {
|
||||
osgAnimation::RigGeometry * rig = _rigGeometries.at(i);
|
||||
osg::Drawable::UpdateCallback * callback = dynamic_cast<osg::Drawable::UpdateCallback*>(rig->getUpdateCallback());
|
||||
if(callback) {
|
||||
callback->update(0, rig);
|
||||
}
|
||||
}
|
||||
}
|
||||
void updateBones();
|
||||
void updateRigGeometries();
|
||||
|
||||
protected:
|
||||
BoneList _bones;
|
||||
RigGeometryList _rigGeometries;
|
||||
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/NodeVisitor>
|
||||
#include <osg/Geode>
|
||||
#include <osg/ValueObject>
|
||||
|
||||
#include <osgAnimation/UpdateMatrixTransform>
|
||||
#include <osgAnimation/BasicAnimationManager>
|
||||
@ -30,32 +29,7 @@
|
||||
#include <osgAnimation/RigTransformSoftware>
|
||||
|
||||
#include "StatLogger"
|
||||
|
||||
|
||||
inline bool hasPositiveWeights(const osg::Geometry* geometry) {
|
||||
const osg::Vec4Array* weights = 0;
|
||||
|
||||
for(unsigned int i = 0 ; i < geometry->getNumVertexAttribArrays() ; ++ i) {
|
||||
const osg::Array* attribute = geometry->getVertexAttribArray(i);
|
||||
bool isWeights = false;
|
||||
if(attribute && attribute->getUserValue("weights", isWeights) && isWeights) {
|
||||
weights = dynamic_cast<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;
|
||||
}
|
||||
#include "glesUtil"
|
||||
|
||||
|
||||
class HasGeometryVisitor : public osg::NodeVisitor {
|
||||
@ -93,559 +67,54 @@ public:
|
||||
{}
|
||||
|
||||
|
||||
void apply(osg::Node& node) {
|
||||
osgAnimation::BasicAnimationManager* manager = getCallbackType<osgAnimation::BasicAnimationManager>(node.getUpdateCallback());
|
||||
if(manager) {
|
||||
_managers[manager] = osg::ref_ptr<osg::Node>(&node);
|
||||
collectAnimationChannels(*manager);
|
||||
}
|
||||
void apply(osg::Node&);
|
||||
void apply(osg::MatrixTransform&);
|
||||
void apply(osg::Geometry&);
|
||||
|
||||
collectUpdateCallback(node);
|
||||
void collectUpdateCallback(osg::Node&);
|
||||
void collectAnimationChannels(osgAnimation::BasicAnimationManager&);
|
||||
|
||||
traverse(node);
|
||||
}
|
||||
virtual void clean();
|
||||
void removeAnimation();
|
||||
|
||||
void apply(osg::MatrixTransform& transform) {
|
||||
HasGeometryVisitor hasData;
|
||||
transform.traverse(hasData);
|
||||
void cleanInvalidMorphGeometries();
|
||||
void cleanInvalidRigGeometries();
|
||||
void cleanUnusedMorphTarget();
|
||||
|
||||
if(!hasData.geometry) {
|
||||
// if animation transforms have no child geometry they are cleanable
|
||||
osgAnimation::Skeleton* skeleton = dynamic_cast<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 ++);
|
||||
}
|
||||
}
|
||||
}
|
||||
void cleanInvalidUpdateMorph();
|
||||
|
||||
protected:
|
||||
void warn(const std::string& method, const std::string& label, const osgAnimation::Channel& channel, const std::string& message) const {
|
||||
OSG_WARN << std::flush << "Warning: " << "[" << method << "] " << "[[" << label << "]] "
|
||||
<< "Channel '" << channel.getName() << "' with target '" << channel.getTargetName()
|
||||
<< " '" << message << std::endl;
|
||||
}
|
||||
void warn(const std::string&, const std::string&, const osgAnimation::Channel&, const std::string&) const;
|
||||
|
||||
template<typename T>
|
||||
T* getCallbackType(osg::Callback* callback) {
|
||||
if(!callback) return 0;
|
||||
T* getCallbackType(osg::Callback*);
|
||||
|
||||
T* callback_type = dynamic_cast<T*>(callback);
|
||||
if(callback_type) {
|
||||
return callback_type;
|
||||
}
|
||||
void cleanAnimations(osgAnimation::BasicAnimationManager&);
|
||||
void cleanAnimation(osgAnimation::Animation&);
|
||||
void cleanChannel(osgAnimation::Channel&);
|
||||
bool isValidAnimationManager(const osgAnimation::BasicAnimationManager&) const;
|
||||
bool isValidAnimation(const osgAnimation::Animation&) const;
|
||||
bool isValidChannel(const osgAnimation::Channel&) const;
|
||||
|
||||
return getCallbackType<T>(callback->getNestedCallback());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
const osgAnimation::StackedTransformElement* getStackedElement(const osgAnimation::StackedTransform&, const std::string&) const;
|
||||
bool isChannelEqualToStackedTransform(const osgAnimation::Channel&, const osgAnimation::UpdateMatrixTransform*) const;
|
||||
|
||||
template<typename T, typename V>
|
||||
bool isChannelEqualToStackedTransform(const T* channel, const V& value) const {
|
||||
if(!channel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const typename T::KeyframeContainerType* keys = channel->getSamplerTyped()->getKeyframeContainerTyped();
|
||||
if(keys->size() == 0) {
|
||||
// channel with no keyframe is equal to anything
|
||||
return true;
|
||||
}
|
||||
if(keys->size() == 1) {
|
||||
return (*keys)[0].getValue() == value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void removeAnimationUpdateCallbacks() {
|
||||
removeUpdateCallbacksTemplate<AnimationUpdateCallBackMap, osg::NodeCallback>(_updates);
|
||||
removeUpdateCallbacksTemplate<BasicAnimationManagerMap, osgAnimation::BasicAnimationManager>(_managers);
|
||||
}
|
||||
bool isChannelEqualToStackedTransform(const T*, const V&) const;
|
||||
|
||||
void removeAnimationUpdateCallbacks();
|
||||
template<typename C, typename T>
|
||||
void removeUpdateCallbacksTemplate(C& callbackNodes) {
|
||||
for(typename C::iterator callbackNode = callbackNodes.begin() ; callbackNode != callbackNodes.end() ; ++ callbackNode) {
|
||||
if(callbackNode->first && callbackNode->second.valid()) {
|
||||
osg::Callback* callback = callbackNode->first.get();
|
||||
osg::Node* node = callbackNode->second.get();
|
||||
do {
|
||||
node->removeUpdateCallback(callback);
|
||||
callback = getCallbackType<T>(node->getUpdateCallback());
|
||||
}
|
||||
while(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
void removeUpdateCallbacksTemplate(C&);
|
||||
|
||||
void removeAnimationTransforms() {
|
||||
for(MatrixTransformList::iterator transform = _transforms.begin() ; transform != _transforms.end() ; ++ transform) {
|
||||
if(transform->valid()) {
|
||||
removeFromParents(transform->get());
|
||||
}
|
||||
}
|
||||
}
|
||||
void removeAnimationTransforms();
|
||||
void removeAnimatedGeometries();
|
||||
void removeFromParents(osg::Node*);
|
||||
|
||||
void removeAnimatedGeometries() {
|
||||
for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ++ morphGeometry) {
|
||||
if(morphGeometry->first.valid()) {
|
||||
replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second);
|
||||
}
|
||||
}
|
||||
void replaceRigGeometryBySource(osgAnimation::RigGeometry&) const;
|
||||
void replaceMorphGeometryByGeometry(osgAnimation::MorphGeometry&, osgAnimation::RigGeometry* /*rigGeometry*/=0) const;
|
||||
void replaceAnimatedGeometryByStaticGeometry(osg::Geometry*, osg::Geometry*) const;
|
||||
|
||||
for(RigGeometryList::iterator rigGeometry = _rigGeometries.begin() ; rigGeometry != _rigGeometries.end() ; ++ rigGeometry) {
|
||||
if(rigGeometry->valid()) {
|
||||
replaceRigGeometryBySource(*(rigGeometry->get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeFromParents(osg::Node* node) {
|
||||
osg::Node::ParentList parents = node->getParents();
|
||||
for(osg::Node::ParentList::iterator parent = parents.begin() ; parent != parents.end() ; ++ parent) {
|
||||
if(*parent) {
|
||||
(*parent)->removeChild(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void replaceRigGeometryBySource(osgAnimation::RigGeometry& rigGeometry) const {
|
||||
if(osgAnimation::MorphGeometry* source = dynamic_cast<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);
|
||||
}
|
||||
}
|
||||
void bakeRigInitialPose();
|
||||
|
||||
protected:
|
||||
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
|
||||
class BindPerVertexVisitor : public GeometryUniqueVisitor {
|
||||
public:
|
||||
BindPerVertexVisitor(): GeometryUniqueVisitor("BindPerVertexVisitor")
|
||||
BindPerVertexVisitor():
|
||||
GeometryUniqueVisitor("BindPerVertexVisitor")
|
||||
{}
|
||||
|
||||
void process(osg::Geometry& geometry) {
|
||||
if (geometry.getNormalArray() && geometry.getNormalBinding() != osg::Geometry::BIND_PER_VERTEX) {
|
||||
bindPerVertex(geometry.getNormalArray(),
|
||||
geometry.getNormalBinding(),
|
||||
geometry.getPrimitiveSetList());
|
||||
geometry.setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (geometry.getColorArray() && geometry.getColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
|
||||
bindPerVertex(geometry.getColorArray(),
|
||||
geometry.getColorBinding(),
|
||||
geometry.getPrimitiveSetList());
|
||||
geometry.setColorBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (geometry.getSecondaryColorArray() && geometry.getSecondaryColorBinding() != osg::Geometry::BIND_PER_VERTEX) {
|
||||
bindPerVertex(geometry.getSecondaryColorArray(),
|
||||
geometry.getSecondaryColorBinding(),
|
||||
geometry.getPrimitiveSetList());
|
||||
geometry.setSecondaryColorBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (geometry.getFogCoordArray() && geometry.getFogCoordBinding() != osg::Geometry::BIND_PER_VERTEX) {
|
||||
bindPerVertex(geometry.getFogCoordArray(),
|
||||
geometry.getFogCoordBinding(),
|
||||
geometry.getPrimitiveSetList());
|
||||
geometry.setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
};
|
||||
void process(osg::Geometry&);
|
||||
|
||||
protected:
|
||||
void bindPerVertex(osg::Array* src,
|
||||
osg::Geometry::AttributeBinding fromBinding,
|
||||
osg::Geometry::PrimitiveSetList& primitives) {
|
||||
if (doConvert<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;
|
||||
|
||||
}
|
||||
void bindPerVertex(osg::Array*, osg::Geometry::AttributeBinding, osg::Geometry::PrimitiveSetList&);
|
||||
|
||||
template <class T>
|
||||
bool doConvert(osg::Array* src,
|
||||
osg::Geometry::AttributeBinding fromBinding,
|
||||
osg::Geometry::PrimitiveSetList& primitives) {
|
||||
T* array= dynamic_cast<T*>(src);
|
||||
if (array) {
|
||||
convert(*array, fromBinding, primitives);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool doConvert(osg::Array*, osg::Geometry::AttributeBinding, osg::Geometry::PrimitiveSetList&);
|
||||
|
||||
template <class T>
|
||||
void convert(T& array,
|
||||
osg::Geometry::AttributeBinding fromBinding,
|
||||
osg::Geometry::PrimitiveSetList& primitives)
|
||||
{
|
||||
osg::ref_ptr<T> result = new T();
|
||||
for (unsigned int p = 0; p < primitives.size(); p++) {
|
||||
switch ( primitives[p]->getMode() ) {
|
||||
case osg::PrimitiveSet::POINTS:
|
||||
osg::notify(osg::WARN) << "ConvertToBindPerVertex not supported for POINTS" << std::endl;
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::LINE_STRIP:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
unsigned int nb = primitives[p]->getNumIndices();
|
||||
for (unsigned int i = 0; i < nb; i++)
|
||||
result->push_back(array[p]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::LINES:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
unsigned int nb = primitives[p]->getNumIndices();
|
||||
for (unsigned int i = 0; i < nb; i++)
|
||||
result->push_back(array[p]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::TRIANGLES:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
unsigned int nb = primitives[p]->getNumIndices();
|
||||
for (unsigned int i = 0; i < nb; i++)
|
||||
result->push_back(array[p]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::TRIANGLE_STRIP:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_STRIP" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::TRIANGLE_FAN:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for TRIANGLE_FAN" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::QUADS:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUADS" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case osg::PrimitiveSet::QUAD_STRIP:
|
||||
switch(fromBinding) {
|
||||
case osg::Geometry::BIND_OFF:
|
||||
case osg::Geometry::BIND_PER_VERTEX:
|
||||
break;
|
||||
case osg::Geometry::BIND_OVERALL:
|
||||
{
|
||||
for (unsigned int i = 0; i < primitives[p]->getNumIndices(); i++)
|
||||
result->push_back(array[0]);
|
||||
}
|
||||
break;
|
||||
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Can't convert Array from BIND_PER_PRIMITIVE_SET to BIND_PER_VERTEX, for QUAD_STRIP" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
array = *result;
|
||||
}
|
||||
void convert(T&, osg::Geometry::AttributeBinding, osg::Geometry::PrimitiveSetList&);
|
||||
};
|
||||
|
||||
#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
|
||||
ReaderWriterGLES.cpp
|
||||
AABBonBoneVisitor.cpp
|
||||
AnimationCleanerVisitor.cpp
|
||||
BindPerVertexVisitor.cpp
|
||||
DetachPrimitiveVisitor.cpp
|
||||
GeometryIndexSplitter.cpp
|
||||
GeometrySplitterVisitor.cpp
|
||||
SubGeometry.cpp
|
||||
OpenGLESGeometryOptimizer.cpp
|
||||
RigAnimationVisitor.cpp
|
||||
RigAttributesVisitor.cpp
|
||||
TriangleMeshSmoother.cpp
|
||||
TangentSpaceVisitor.cpp
|
||||
TriangleStripVisitor.cpp
|
||||
IndexMeshVisitor.cpp
|
||||
UnIndexMeshVisitor.cpp)
|
||||
@ -15,6 +26,7 @@ SET(TARGET_H
|
||||
DrawArrayVisitor
|
||||
EdgeIndexFunctor
|
||||
GeometryArray
|
||||
GeometryIndexSplitter
|
||||
GeometryInspector
|
||||
GeometrySplitterVisitor
|
||||
GeometryUniqueVisitor
|
||||
@ -35,6 +47,7 @@ SET(TARGET_H
|
||||
SubGeometry
|
||||
TangentSpaceVisitor
|
||||
TriangleMeshGraph
|
||||
TriangleMeshSmoother
|
||||
TriangleStripVisitor
|
||||
UnIndexMeshVisitor
|
||||
WireframeVisitor
|
||||
|
@ -23,140 +23,23 @@ public:
|
||||
_userValue(userValue), _keepGeometryAttributes(keepGeometryAttributes), _inlined(inlined)
|
||||
{}
|
||||
|
||||
void reparentDuplicatedGeometry(osg::Geometry& geometry, osg::Geometry& duplicated)
|
||||
{
|
||||
unsigned int nbParents = geometry.getNumParents();
|
||||
for(unsigned int i = 0 ; i < nbParents ; ++ i) {
|
||||
osg::Node* parent = geometry.getParent(i);
|
||||
if(parent && parent->asGeode()) {
|
||||
osg::Geode* geode = parent->asGeode();
|
||||
geode->addDrawable(&duplicated);
|
||||
|
||||
if(!_inlined) {
|
||||
geode->removeDrawable(&duplicated);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process(osg::Geometry& geometry) {
|
||||
if(shouldDetach(geometry)) {
|
||||
osg::Geometry* detached = detachGeometry(geometry);
|
||||
reparentDuplicatedGeometry(geometry, *detached);
|
||||
setProcessed(detached);
|
||||
}
|
||||
}
|
||||
|
||||
void process(osgAnimation::RigGeometry& rigGeometry) {
|
||||
return process(static_cast<osg::Geometry&>(rigGeometry));
|
||||
}
|
||||
void reparentDuplicatedGeometry(osg::Geometry&, osg::Geometry&);
|
||||
void process(osg::Geometry&);
|
||||
void process(osgAnimation::RigGeometry&);
|
||||
|
||||
protected:
|
||||
bool shouldDetach(const osg::Geometry& geometry) const {
|
||||
if(const osgAnimation::RigGeometry* rigGeometry = dynamic_cast<const osgAnimation::RigGeometry*>(&geometry)) {
|
||||
return shouldDetach(*rigGeometry->getSourceGeometry());
|
||||
}
|
||||
bool shouldDetach(const osg::Geometry&) const;
|
||||
|
||||
bool detach = false;
|
||||
for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) {
|
||||
const osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
|
||||
if(primitive && primitive->getUserValue(_userValue, detach) && detach) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
osg::Geometry* detachGeometry(osg::Geometry&);
|
||||
osg::Geometry* makeDetachedGeometry(osg::Geometry&);
|
||||
osg::Geometry* createDetachedGeometry(osg::Geometry&);
|
||||
|
||||
osg::Geometry* detachGeometry(osg::Geometry& source) {
|
||||
// filter vertex buffers depending on geometry type
|
||||
osg::Geometry* detached = makeDetachedGeometry(source);
|
||||
detached->setUserValue(_userValue, true);
|
||||
return detached;
|
||||
}
|
||||
osg::Geometry::PrimitiveSetList createDetachedPrimitives(osg::Geometry&);
|
||||
osgAnimation::MorphGeometry* createDetachedGeometry(osgAnimation::MorphGeometry&);
|
||||
osg::Geometry* createDetachedGeometry(osgAnimation::RigGeometry&);
|
||||
|
||||
osg::Geometry* makeDetachedGeometry(osg::Geometry& geometry) {
|
||||
if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast<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;
|
||||
bool _keepGeometryAttributes;
|
||||
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
|
||||
#define GEOMETRY_SPLITTER_VISITOR
|
||||
|
||||
#include <set>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/ValueObject>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
|
||||
#include <osgAnimation/RigGeometry>
|
||||
|
||||
#include "glesUtil"
|
||||
#include "GeometryArray"
|
||||
#include "GeometryUniqueVisitor"
|
||||
#include "AnimationCleanerVisitor"
|
||||
#include "TriangleMeshGraph"
|
||||
#include "SubGeometry"
|
||||
#include "Line"
|
||||
|
||||
|
||||
|
||||
class GeometryIndexSplitter
|
||||
{
|
||||
protected:
|
||||
class IndexCache : public IndexDeque {
|
||||
public:
|
||||
IndexCache(unsigned int size=64) : _size(size)
|
||||
{}
|
||||
|
||||
void push_back(unsigned int value) {
|
||||
IndexDeque::push_back(value);
|
||||
if(size() > _size) pop_front();
|
||||
}
|
||||
protected:
|
||||
unsigned int _size;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef std::vector< osg::ref_ptr<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 {
|
||||
@ -349,70 +34,14 @@ public:
|
||||
_exportNonGeometryDrawables(exportNonGeometryDrawables)
|
||||
{}
|
||||
|
||||
void apply(osg::Geode& geode) {
|
||||
GeometryUniqueVisitor::apply(geode);
|
||||
GeometryList remappedGeometries;
|
||||
DrawableList nonGeometryDrawables;
|
||||
|
||||
for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) {
|
||||
osg::Geometry* geometry = geode.getDrawable(i)->asGeometry();
|
||||
if(geometry) {
|
||||
if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast<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);
|
||||
}
|
||||
void apply(osg::Geode&);
|
||||
void process(osg::Geometry&);
|
||||
|
||||
protected:
|
||||
bool isProcessed(osg::Geometry* node) {
|
||||
return _split.find(node) != _split.end();
|
||||
}
|
||||
bool isProcessed(osg::Geometry*);
|
||||
void setProcessed(osg::Geometry*, const GeometryList&);
|
||||
|
||||
void setProcessed(osg::Geometry* node, const GeometryList& list) {
|
||||
_split.insert(std::pair<osg::Geometry*, GeometryList>(node, GeometryList(list)));
|
||||
}
|
||||
protected:
|
||||
unsigned int _maxAllowedIndex;
|
||||
std::map<osg::Geometry*, GeometryList> _split;
|
||||
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"
|
||||
|
||||
|
||||
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
|
||||
class RigAnimationVisitor : public osgUtil::UpdateVisitor
|
||||
{
|
||||
@ -55,146 +42,28 @@ public:
|
||||
setFrameStamp(new osg::FrameStamp());
|
||||
}
|
||||
|
||||
void apply(osg::Drawable& drawable) {
|
||||
// skip drawables already processed
|
||||
if (isProcessed(drawable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
apply(drawable.asGeometry());
|
||||
|
||||
setProcessed(drawable);
|
||||
}
|
||||
|
||||
void apply(osg::Geometry* geometry) {
|
||||
osgAnimation::RigGeometry* rig = dynamic_cast<osgAnimation::RigGeometry*>(geometry);
|
||||
if(rig) {
|
||||
apply(*rig);
|
||||
}
|
||||
}
|
||||
void apply(osg::Drawable&);
|
||||
void apply(osg::Geometry*);
|
||||
void apply(osgAnimation::RigGeometry&);
|
||||
|
||||
protected:
|
||||
inline void normalizeWeight(osg::Vec4f &v) const {
|
||||
// do not consider positive weights only
|
||||
float sum = std::abs(v[0]) + std::abs(v[1]) + std::abs(v[2]) + std::abs(v[3]);
|
||||
if (sum > 0) v /= sum;
|
||||
}
|
||||
|
||||
void apply(osgAnimation::RigGeometry& rigGeometry) {
|
||||
// find skeleton
|
||||
osgAnimation::UpdateRigGeometry rigUpdater;
|
||||
osg::Geometry* source = rigGeometry.getSourceGeometry();
|
||||
boneIndices remapGeometryBones(const osg::Vec4usArray&);
|
||||
void applyBoneIndicesRemap(osg::Vec4usArray&, const boneIndices&);
|
||||
|
||||
if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(source)) {
|
||||
// skip normals blending when rigging a morph as targets may not have normals yet
|
||||
morphGeometry->setMorphNormals(false);
|
||||
}
|
||||
void serializeBonesUserValues(osg::Vec4usArray&,
|
||||
const std::map<unsigned int, unsigned short>&,
|
||||
const osgAnimation::RigTransformHardware::BoneNamePaletteIndex&);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
boneIndices geometryBoneIndices = remapGeometryBones(*bones);
|
||||
applyBoneIndicesRemap(*bones, geometryBoneIndices);
|
||||
serializeBonesUserValues(*bones, geometryBoneIndices, rth.getBoneNameToPalette());
|
||||
|
||||
bones->setUserValue("bones", true);
|
||||
weights->setUserValue("weights", true);
|
||||
|
||||
// attach bones & weights to source geometry during scene graph processing
|
||||
source->setVertexAttribArray(source->getNumVertexAttribArrays(), bones.get(), osg::Array::BIND_PER_VERTEX);
|
||||
source->setVertexAttribArray(source->getNumVertexAttribArrays(), weights.get(), osg::Array::BIND_PER_VERTEX);
|
||||
|
||||
rigGeometry.setRigTransformImplementation(0); //Remove current implementation to force implementation re-init
|
||||
}
|
||||
bool isProcessed(osg::Drawable&);
|
||||
void setProcessed(osg::Drawable&);
|
||||
|
||||
protected:
|
||||
boneIndices remapGeometryBones(const osg::Vec4usArray& bones) {
|
||||
boneIndices remap;
|
||||
for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) {
|
||||
for(unsigned int j = 0 ; j < 4 ; ++ j) {
|
||||
if(remap.find(bones[i][j]) == remap.end()) {
|
||||
remap[bones[i][j]] = static_cast<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;
|
||||
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")
|
||||
{}
|
||||
|
||||
void process(osgAnimation::RigGeometry& rigGeometry) {
|
||||
osg::Geometry* source = rigGeometry.getSourceGeometry();
|
||||
if(source) {
|
||||
int sourceBones = getPropertyIndex(*source, std::string("bones"));
|
||||
int rigBones = getPropertyIndex(rigGeometry, std::string("bones"));
|
||||
if(sourceBones >= 0) {
|
||||
rigBones = (rigBones >= 0 ? rigBones : rigGeometry.getNumVertexAttribArrays());
|
||||
rigGeometry.setVertexAttribArray(rigBones, source->getVertexAttribArray(sourceBones));
|
||||
source->setVertexAttribArray(sourceBones, 0);
|
||||
}
|
||||
|
||||
int sourceWeights = getPropertyIndex(*source, std::string("weights"));
|
||||
int rigWeights = getPropertyIndex(rigGeometry, std::string("weights"));
|
||||
if(sourceWeights >= 0) {
|
||||
rigWeights = (rigWeights >= 0 ? rigWeights : rigGeometry.getNumVertexAttribArrays());
|
||||
rigGeometry.setVertexAttribArray(rigWeights, source->getVertexAttribArray(sourceWeights));
|
||||
source->setVertexAttribArray(sourceWeights, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process(osgAnimation::RigGeometry& rigGeometry);
|
||||
void process(osg::Geometry& /*geometry*/) {
|
||||
return;
|
||||
}
|
||||
|
||||
protected:
|
||||
int getPropertyIndex(const osg::Geometry& geometry, const std::string& property) {
|
||||
for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) {
|
||||
const osg::Array* attribute = geometry.getVertexAttribArray(i);
|
||||
bool isProperty = false;
|
||||
if(attribute && attribute->getUserValue(property, isProperty) && isProperty) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int getPropertyIndex(const osg::Geometry& geometry, const std::string& property);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
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 */
|
||||
|
||||
#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):
|
||||
_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
|
||||
};
|
||||
|
||||
#include "TriangleMeshSmoother"
|
||||
|
||||
|
||||
class SmoothNormalVisitor : public GeometryUniqueVisitor {
|
||||
|
@ -10,15 +10,19 @@
|
||||
class StatLogger
|
||||
{
|
||||
public:
|
||||
StatLogger(const std::string& label): _label(label)
|
||||
StatLogger(const std::string& label):
|
||||
_label(label)
|
||||
{
|
||||
_start = _stop = getTick();
|
||||
}
|
||||
|
||||
|
||||
~StatLogger() {
|
||||
_stop = getTick();
|
||||
OSG_INFO << std::flush
|
||||
|
||||
OSG_INFO << std::endl
|
||||
<< "Info: " << _label << " timing: " << getElapsedSeconds() << "s"
|
||||
<< std::endl << std::flush;
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -19,259 +19,36 @@ public:
|
||||
typedef std::map<osg::Array*, const osg::Array*>::iterator BufferIterator;
|
||||
typedef std::map<unsigned int, unsigned int> IndexMapping;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
SubGeometry(const osg::Geometry&,
|
||||
const std::vector<unsigned int>&,
|
||||
const std::vector<unsigned int>&,
|
||||
const std::vector<unsigned int>&,
|
||||
const std::vector<unsigned int>&);
|
||||
|
||||
osg::Geometry* geometry() const {
|
||||
return _geometry.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
void addSourceBuffers(osg::Geometry* geometry, const osg::Geometry& source) {
|
||||
// create necessary vertex containers
|
||||
const osg::Array* array = 0;
|
||||
osg::Array* makeVertexBuffer(const osg::Array*, bool /*copyUserData*/=true);
|
||||
|
||||
geometry->setName(source.getName());
|
||||
|
||||
// collect all buffers that are BIND_PER_VERTEX for eventual vertex duplication
|
||||
if( (array = vertexArray(source.getVertexArray())) ) {
|
||||
geometry->setVertexArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
if( (array = vertexArray(source.getNormalArray())) ){
|
||||
geometry->setNormalArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
if( (array = vertexArray(source.getColorArray())) ){
|
||||
geometry->setColorArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
if( (array = vertexArray(source.getSecondaryColorArray())) ){
|
||||
geometry->setSecondaryColorArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
if( (array = vertexArray(source.getFogCoordArray())) ){
|
||||
geometry->setFogCoordArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < source.getNumVertexAttribArrays(); ++ i) {
|
||||
if( (array = vertexArray(source.getVertexAttribArray(i))) ){
|
||||
geometry->setVertexAttribArray(i, makeVertexBuffer(array));
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < source.getNumTexCoordArrays(); ++ i) {
|
||||
if( (array = vertexArray(source.getTexCoordArray(i))) ){
|
||||
geometry->setTexCoordArray(i, makeVertexBuffer(array));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void copyTriangle(unsigned int v1, unsigned int v2, unsigned int v3) {
|
||||
osg::DrawElements* triangles = getOrCreateTriangles();
|
||||
triangles->addElement(mapVertex(v1));
|
||||
triangles->addElement(mapVertex(v2));
|
||||
triangles->addElement(mapVertex(v3));
|
||||
}
|
||||
|
||||
void copyEdge(unsigned int v1, unsigned int v2, bool wireframe) {
|
||||
osg::DrawElements* edges = getOrCreateLines(wireframe);
|
||||
edges->addElement(mapVertex(v1));
|
||||
edges->addElement(mapVertex(v2));
|
||||
}
|
||||
|
||||
void copyPoint(unsigned int v1) {
|
||||
osg::DrawElements* points = getOrCreatePoints();
|
||||
points->addElement(mapVertex(v1));
|
||||
}
|
||||
|
||||
const osg::Array* vertexArray(const osg::Array* array) {
|
||||
// filter invalid vertex buffers
|
||||
if (array && array->getNumElements() && array->getBinding() == osg::Array::BIND_PER_VERTEX) {
|
||||
return array;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline osg::Array* makeVertexBuffer(const osg::Array* array, bool copyUserData=true) {
|
||||
osg::Array* buffer = array ? osg::cloneType(array) : 0;
|
||||
if(buffer) {
|
||||
buffer->setBinding(osg::Array::BIND_PER_VERTEX);
|
||||
if(copyUserData && array->getUserDataContainer()) {
|
||||
buffer->setUserDataContainer(osg::clone(array->getUserDataContainer(), osg::CopyOp::DEEP_COPY_ALL));
|
||||
}
|
||||
_bufferMap[buffer] = array;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
void addSourceBuffers(osg::Geometry*, const osg::Geometry&);
|
||||
const osg::Array* vertexArray(const osg::Array* array);
|
||||
unsigned int mapVertex(unsigned int);
|
||||
|
||||
void copyTriangle(unsigned int, unsigned int, unsigned int);
|
||||
void copyEdge(unsigned int, unsigned int, bool);
|
||||
void copyPoint(unsigned int);
|
||||
|
||||
void copyFrom(const osg::Array&, osg::Array&);
|
||||
template<typename C>
|
||||
void copyValues(const C& src, C& dst) {
|
||||
dst.resize(_indexMap.size());
|
||||
for(IndexMapping::const_iterator remapper = _indexMap.begin() ; remapper != _indexMap.end() ; ++ remapper) {
|
||||
dst[remapper->second] = src[remapper->first];
|
||||
}
|
||||
}
|
||||
void copyValues(const C& src, C& dst);
|
||||
|
||||
#define COPY_TEMPLATE(T) \
|
||||
if (dynamic_cast<const T*>(&src)) \
|
||||
{ return copyValues<T>(dynamic_cast<const T&>(src), dynamic_cast<T&>(dst)); }
|
||||
|
||||
void copyFrom(const osg::Array& src, osg::Array& dst) {
|
||||
COPY_TEMPLATE(osg::Vec2Array);
|
||||
COPY_TEMPLATE(osg::Vec3Array);
|
||||
COPY_TEMPLATE(osg::Vec4Array);
|
||||
|
||||
COPY_TEMPLATE(osg::Vec2dArray);
|
||||
COPY_TEMPLATE(osg::Vec3dArray);
|
||||
COPY_TEMPLATE(osg::Vec4dArray);
|
||||
|
||||
COPY_TEMPLATE(osg::ByteArray);
|
||||
COPY_TEMPLATE(osg::ShortArray);
|
||||
COPY_TEMPLATE(osg::IntArray);
|
||||
COPY_TEMPLATE(osg::UByteArray);
|
||||
COPY_TEMPLATE(osg::UShortArray);
|
||||
COPY_TEMPLATE(osg::UIntArray);
|
||||
COPY_TEMPLATE(osg::FloatArray);
|
||||
COPY_TEMPLATE(osg::DoubleArray);
|
||||
|
||||
COPY_TEMPLATE(osg::Vec2iArray);
|
||||
COPY_TEMPLATE(osg::Vec3iArray);
|
||||
COPY_TEMPLATE(osg::Vec4iArray);
|
||||
|
||||
COPY_TEMPLATE(osg::Vec2uiArray);
|
||||
COPY_TEMPLATE(osg::Vec3uiArray);
|
||||
COPY_TEMPLATE(osg::Vec4uiArray);
|
||||
|
||||
COPY_TEMPLATE(osg::Vec2sArray);
|
||||
COPY_TEMPLATE(osg::Vec3sArray);
|
||||
COPY_TEMPLATE(osg::Vec4sArray);
|
||||
|
||||
COPY_TEMPLATE(osg::Vec2usArray);
|
||||
COPY_TEMPLATE(osg::Vec3usArray);
|
||||
COPY_TEMPLATE(osg::Vec4usArray);
|
||||
|
||||
COPY_TEMPLATE(osg::Vec2bArray);
|
||||
COPY_TEMPLATE(osg::Vec3bArray);
|
||||
COPY_TEMPLATE(osg::Vec4bArray);
|
||||
|
||||
COPY_TEMPLATE(osg::Vec2ubArray);
|
||||
COPY_TEMPLATE(osg::Vec3ubArray);
|
||||
COPY_TEMPLATE(osg::Vec4ubArray);
|
||||
|
||||
COPY_TEMPLATE(osg::MatrixfArray);
|
||||
|
||||
COPY_TEMPLATE(osg::QuatArray);
|
||||
}
|
||||
|
||||
unsigned int mapVertex(unsigned int i) {
|
||||
if(_indexMap.find(i) == _indexMap.end()) {
|
||||
unsigned int index = _indexMap.size();
|
||||
_indexMap[i] = index;
|
||||
}
|
||||
return _indexMap[i];
|
||||
}
|
||||
|
||||
osg::DrawElements* getOrCreateTriangles() {
|
||||
if(_primitives.find("triangles") == _primitives.end()) {
|
||||
_primitives["triangles"] = new osg::DrawElementsUInt(GL_TRIANGLES);
|
||||
_geometry->addPrimitiveSet(_primitives["triangles"]);
|
||||
}
|
||||
return _primitives["triangles"];
|
||||
}
|
||||
|
||||
osg::DrawElements* getOrCreateLines(bool wireframe) {
|
||||
std::string label = wireframe ? "wireframe" : "lines";
|
||||
|
||||
if(_primitives.find(label) == _primitives.end()) {
|
||||
_primitives[label] = new osg::DrawElementsUInt(GL_LINES);
|
||||
if(wireframe) {
|
||||
_primitives[label]->setUserValue("wireframe", true);
|
||||
}
|
||||
_geometry->addPrimitiveSet(_primitives[label]);
|
||||
}
|
||||
return _primitives[label];
|
||||
}
|
||||
|
||||
osg::DrawElements* getOrCreatePoints() {
|
||||
if(_primitives.find("points") == _primitives.end()) {
|
||||
_primitives["points"] = new osg::DrawElementsUInt(GL_POINTS);
|
||||
_geometry->addPrimitiveSet(_primitives["points"]);
|
||||
}
|
||||
return _primitives["points"];
|
||||
}
|
||||
osg::DrawElements* getOrCreateTriangles();
|
||||
osg::DrawElements* getOrCreateLines(bool);
|
||||
osg::DrawElements* getOrCreatePoints();
|
||||
|
||||
protected:
|
||||
osg::ref_ptr<osg::Geometry> _geometry;
|
||||
std::map<osg::Array*, const osg::Array*> _bufferMap;
|
||||
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
|
||||
{
|
||||
public:
|
||||
TangentSpaceVisitor(int textureUnit = 0):
|
||||
TangentSpaceVisitor(int textureUnit=0):
|
||||
GeometryUniqueVisitor("TangentSpaceVisitor"),
|
||||
_textureUnit(textureUnit)
|
||||
{}
|
||||
|
||||
void 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 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);
|
||||
}
|
||||
}
|
||||
void process(osgAnimation::MorphGeometry&);
|
||||
void process(osg::Geometry&);
|
||||
|
||||
protected:
|
||||
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 <osg/Array>
|
||||
#include <osg/ValueObject>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
#include <osg/TriangleIndexFunctor>
|
||||
@ -25,6 +26,33 @@ namespace glesUtil {
|
||||
using namespace osg;
|
||||
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
|
||||
// osg::Geometry object that are BIND_PER_VERTEX and runs an
|
||||
// ArrayVisitor on them.
|
||||
|
Loading…
Reference in New Issue
Block a user