Updates gles plugin
This commit is contained in:
parent
7c0c98b504
commit
9fbdaaae65
261
src/osgPlugins/gles/AABBonBoneVisitor
Normal file
261
src/osgPlugins/gles/AABBonBoneVisitor
Normal file
@ -0,0 +1,261 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AABBONEVISITOR_H
|
||||
#define AABBONEVISITOR_H
|
||||
|
||||
#include <iostream>
|
||||
#include <osg/Geode>
|
||||
#include <osgAnimation/Bone>
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osgAnimation/RigGeometry>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Array>
|
||||
#include <osgUtil/UpdateVisitor>
|
||||
#include <osgAnimation/UpdateBone>
|
||||
#include <osg/PolygonMode>
|
||||
#include <osg/ShapeDrawable>
|
||||
#include <osg/Shape>
|
||||
|
||||
|
||||
// Here the idea is to compute AABB by bone for a skelton
|
||||
// You just need to call this visitor on a skeleton and after call computeBoundingBoxOnBones
|
||||
// If you have more than one skeleton you should crete a visitor by skeleton
|
||||
class ComputeAABBOnBoneVisitor : public osg::NodeVisitor {
|
||||
public:
|
||||
typedef std::vector<osgAnimation::Bone*> BoneList;
|
||||
typedef std::vector<osgAnimation::RigGeometry*> RigGeometryList;
|
||||
|
||||
|
||||
osg::Geometry* createBox(const osg::BoundingBox &bb, const osg::Matrix &transform,
|
||||
float ratio=1.0, osg::Vec4 color=osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)) {
|
||||
osg::Geometry *cube = new osg::Geometry;
|
||||
|
||||
osg::Vec3 center = bb.center();
|
||||
double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50;
|
||||
double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50;
|
||||
double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50;
|
||||
|
||||
halfLenghtX *= ratio;
|
||||
halfLenghtY *= ratio;
|
||||
halfLenghtZ *= ratio;
|
||||
|
||||
osg::Vec3Array *cubeVertices = new osg::Vec3Array;
|
||||
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
|
||||
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
cubeVertices->push_back(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
|
||||
cube->setVertexArray(cubeVertices);
|
||||
|
||||
osg::DrawElementsUInt* up = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
up->push_back(4);
|
||||
up->push_back(5);
|
||||
up->push_back(1);
|
||||
up->push_back(0);
|
||||
cube->addPrimitiveSet(up);
|
||||
|
||||
osg::DrawElementsUInt* down = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
down->push_back(2);
|
||||
down->push_back(6);
|
||||
down->push_back(7);
|
||||
down->push_back(3);
|
||||
cube->addPrimitiveSet(down);
|
||||
|
||||
osg::DrawElementsUInt* left = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
left->push_back(2);
|
||||
left->push_back(3);
|
||||
left->push_back(0);
|
||||
left->push_back(1);
|
||||
cube->addPrimitiveSet(left);
|
||||
|
||||
osg::DrawElementsUInt* right = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
right->push_back(7);
|
||||
right->push_back(6);
|
||||
right->push_back(5);
|
||||
right->push_back(4);
|
||||
cube->addPrimitiveSet(right);
|
||||
|
||||
osg::DrawElementsUInt* front = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
front->push_back(3);
|
||||
front->push_back(7);
|
||||
front->push_back(4);
|
||||
front->push_back(0);
|
||||
cube->addPrimitiveSet(front);
|
||||
|
||||
osg::DrawElementsUInt* back = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
|
||||
back->push_back(6);
|
||||
back->push_back(2);
|
||||
back->push_back(1);
|
||||
back->push_back(5);
|
||||
cube->addPrimitiveSet(back);
|
||||
|
||||
osg::Vec4Array* colors = new osg::Vec4Array;
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
colors->push_back(color);
|
||||
|
||||
cube->setColorArray(colors);
|
||||
cube->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
|
||||
return cube;
|
||||
}
|
||||
|
||||
ComputeAABBOnBoneVisitor(bool createGeometry):
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
||||
_root(0),
|
||||
_createGeometry(createGeometry)
|
||||
{}
|
||||
|
||||
void apply(osg::Transform& node) {
|
||||
if(!_root)
|
||||
_root = dynamic_cast<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 serializeBoundingBox(const osg::BoundingBox &bb, const osg::Matrix &transform, osgAnimation::Bone &b, float ratio = 1.0) {
|
||||
osg::Vec3 center = bb.center();
|
||||
double halfLenghtX = (bb._max.x() - bb._min.x()) * 0.50;
|
||||
double halfLenghtY = (bb._max.y() - bb._min.y()) * 0.50;
|
||||
double halfLenghtZ = (bb._max.z() - bb._min.z()) * 0.50;
|
||||
|
||||
halfLenghtX *= ratio;
|
||||
halfLenghtY *= ratio;
|
||||
halfLenghtZ *= ratio;
|
||||
|
||||
osg::BoundingBox serializedBB;
|
||||
|
||||
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() - halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() + halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() - halfLenghtZ) * transform);
|
||||
serializedBB.expandBy(osg::Vec3(center.x() + halfLenghtX, center.y() - halfLenghtY, center.z() + halfLenghtZ) * transform);
|
||||
|
||||
b.setUserValue("AABBonBone_min", serializedBB._min);
|
||||
b.setUserValue("AABBonBone_max", serializedBB._max);
|
||||
}
|
||||
|
||||
void computeBoundingBoxOnBones() {
|
||||
//Perform Updates
|
||||
|
||||
//Update Bones
|
||||
osgUtil::UpdateVisitor up;
|
||||
_root->accept(up);
|
||||
|
||||
//Update rigGeometries
|
||||
for (unsigned int i = 0, size = _rigGeometries.size(); i < size; i++) {
|
||||
osgAnimation::RigGeometry * rig = _rigGeometries.at(i);
|
||||
osg::Drawable::UpdateCallback * up = dynamic_cast<osg::Drawable::UpdateCallback*>(rig->getUpdateCallback());
|
||||
if(up) up->update(0, rig);
|
||||
}
|
||||
|
||||
//We have our T pose, we can compute an AABB for each bone
|
||||
for (BoneList::iterator bone = _bones.begin(); bone != _bones.end(); ++ bone) {
|
||||
osg::BoundingBox bb;
|
||||
//For each geometry
|
||||
for (RigGeometryList::iterator rigGeometry = _rigGeometries.begin(); rigGeometry != _rigGeometries.end(); ++ rigGeometry) {
|
||||
osg::Matrix mtxLocalToSkl = (*rigGeometry)->getWorldMatrices(_root).at(0);
|
||||
|
||||
//For each Vertex influence
|
||||
osgAnimation::VertexInfluenceMap * infMap = (*rigGeometry)->getInfluenceMap();
|
||||
osgAnimation::VertexInfluenceMap::iterator itMap = infMap->find((*bone)->getName());
|
||||
if(itMap == infMap->end()) continue;
|
||||
|
||||
osgAnimation::VertexInfluence vxtInf = (*itMap).second;
|
||||
osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array*>((*rigGeometry)->getVertexArray());
|
||||
|
||||
//Expand the boundingBox with each vertex
|
||||
for(unsigned int j = 0; j < vxtInf.size(); j++) {
|
||||
if(vxtInf.at(j).second < 10e-2) continue; //Skip vertex if low weight
|
||||
osg::Vec3 vx = vertices->at(vxtInf.at(j).first);
|
||||
vx = vx * mtxLocalToSkl;
|
||||
bb.expandBy(vx);
|
||||
}
|
||||
|
||||
// Compare initial and actual boundingBox if (no change) => no box on bone
|
||||
if(bb == osg::BoundingBox() || bb._min.x() == bb._max.x() || bb._min.y() == bb._max.y() || bb._min.z() == bb._max.z()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
osg::Matrix worldToBone = osg::Matrix::inverse((*bone)->getWorldMatrices(_root).at(0));
|
||||
|
||||
if(_createGeometry) {
|
||||
osg::Geode *g = new osg::Geode;
|
||||
g->setName("AABB_for_bone_" + (*bone)->getName());
|
||||
g->addDrawable(createBox(bb, worldToBone));
|
||||
(*bone)->addChild(g);
|
||||
}
|
||||
|
||||
serializeBoundingBox(bb, worldToBone, *(*bone));
|
||||
}
|
||||
}
|
||||
|
||||
//Clear geometries
|
||||
for (RigGeometryList::iterator rigGeometry = _rigGeometries.begin(); rigGeometry != _rigGeometries.end(); ++ rigGeometry) {
|
||||
(*rigGeometry)->copyFrom(*(*rigGeometry)->getSourceGeometry());
|
||||
(*rigGeometry)->setRigTransformImplementation(0);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<osgAnimation::Bone*> _bones;
|
||||
std::vector<osgAnimation::RigGeometry*> _rigGeometries;
|
||||
osgAnimation::Skeleton *_root;
|
||||
bool _createGeometry;
|
||||
};
|
||||
|
||||
|
||||
struct FindSkeletons : public osg::NodeVisitor
|
||||
{
|
||||
FindSkeletons():
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
||||
{}
|
||||
|
||||
void apply(osg::Transform& node) {
|
||||
osgAnimation::Skeleton *skl = dynamic_cast<osgAnimation::Skeleton*>(&node);
|
||||
if(skl) _skls.push_back(skl);
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
std::vector<osgAnimation::Skeleton*> _skls;
|
||||
};
|
||||
|
||||
#endif // AABBONEVISITOR_H
|
663
src/osgPlugins/gles/AnimationCleanerVisitor
Normal file
663
src/osgPlugins/gles/AnimationCleanerVisitor
Normal file
@ -0,0 +1,663 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */
|
||||
|
||||
#ifndef ANIMATION_CLEANER_VISITOR
|
||||
#define ANIMATION_CLEANER_VISITOR
|
||||
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/Geode>
|
||||
#include <osg/ValueObject>
|
||||
|
||||
#include <osgAnimation/UpdateMatrixTransform>
|
||||
#include <osgAnimation/BasicAnimationManager>
|
||||
#include <osgAnimation/RigGeometry>
|
||||
#include <osgAnimation/MorphGeometry>
|
||||
#include <osgAnimation/Bone>
|
||||
#include <osgAnimation/Skeleton>
|
||||
#include <osgAnimation/UpdateBone>
|
||||
#include <osgAnimation/Sampler>
|
||||
#include <osgAnimation/StackedTransform>
|
||||
#include <osgAnimation/StackedTranslateElement>
|
||||
#include <osgAnimation/StackedScaleElement>
|
||||
#include <osgAnimation/StackedQuaternionElement>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
class HasGeometryVisitor : public osg::NodeVisitor {
|
||||
public:
|
||||
HasGeometryVisitor():
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
||||
geometry(false)
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& object) {
|
||||
geometry = true;
|
||||
}
|
||||
|
||||
bool geometry;
|
||||
};
|
||||
|
||||
|
||||
class AnimationCleanerVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
typedef std::map< osg::ref_ptr<osgAnimation::BasicAnimationManager>, osg::ref_ptr<osg::Node> > BasicAnimationManagerMap;
|
||||
typedef osgAnimation::AnimationUpdateCallback<osg::NodeCallback> BaseAnimationUpdateCallback;
|
||||
typedef std::map< osg::ref_ptr<BaseAnimationUpdateCallback>, osg::ref_ptr<osg::Node> > AnimationUpdateCallBackMap;
|
||||
typedef std::vector< osg::ref_ptr<osg::MatrixTransform> > MatrixTransformList;
|
||||
typedef std::vector< osg::ref_ptr<osgAnimation::RigGeometry> > RigGeometryList;
|
||||
typedef std::map< osg::ref_ptr<osgAnimation::MorphGeometry>, osgAnimation::RigGeometry* > MorphGeometryMap;
|
||||
typedef std::map< std::string, osgAnimation::MorphGeometry* > NameMorphMap;
|
||||
typedef std::set< std::string > NameSet;
|
||||
typedef std::vector< std::pair<std::string, osgAnimation::Channel*> > TargetChannelList;
|
||||
|
||||
|
||||
AnimationCleanerVisitor(std::string name="AnimationCleanerVisitor"):
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
||||
_logger(name + "::apply(..)")
|
||||
{}
|
||||
|
||||
|
||||
void apply(osg::Node& node) {
|
||||
osgAnimation::BasicAnimationManager* manager = getCallbackType<osgAnimation::BasicAnimationManager>(node.getUpdateCallback());
|
||||
if(manager) {
|
||||
_managers[manager] = osg::ref_ptr<osg::Node>(&node);
|
||||
collectAnimationChannels(*manager);
|
||||
}
|
||||
|
||||
collectUpdateCallback(node);
|
||||
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
void apply(osg::MatrixTransform& transform) {
|
||||
HasGeometryVisitor hasData;
|
||||
transform.traverse(hasData);
|
||||
|
||||
if(!hasData.geometry) {
|
||||
// if animation transforms have no child geometry they are cleanable
|
||||
osgAnimation::Skeleton* skeleton = dynamic_cast<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
|
||||
|
||||
RigGeometryList::iterator rigGeometry = _rigGeometries.begin();
|
||||
while(rigGeometry != _rigGeometries.end()) {
|
||||
if(rigGeometry->valid()) {
|
||||
if(!hasPositiveWeights((*rigGeometry)->getSourceGeometry())) {
|
||||
OSG_WARN << "Monitor: animation.invalid_riggeometry" << std::endl;
|
||||
replaceRigGeometryBySource(*(rigGeometry->get()));
|
||||
_rigGeometries.erase(rigGeometry);
|
||||
continue; // skip iterator increment
|
||||
}
|
||||
}
|
||||
++ rigGeometry;
|
||||
}
|
||||
}
|
||||
|
||||
void cleanUnusedMorphTarget() {
|
||||
// Removes MorphGeometry targets not updated by any channel
|
||||
std::set<std::string> kept, removed;
|
||||
|
||||
for(NameMorphMap::iterator targetMorph = _morphTargets.begin() ; targetMorph != _morphTargets.end() ; ) {
|
||||
const std::string& target = targetMorph->first;
|
||||
unsigned int count = 0;
|
||||
for(TargetChannelList::const_iterator targetChannel = _channels.begin() ; targetChannel != _channels.end() ; ++ targetChannel) {
|
||||
if(targetChannel->first == target) {
|
||||
++ count;
|
||||
}
|
||||
}
|
||||
|
||||
if(count == 0) {
|
||||
removed.insert(target);
|
||||
targetMorph->second->removeMorphTarget(target);
|
||||
_morphTargets.erase(targetMorph ++);
|
||||
}
|
||||
else {
|
||||
kept.insert(target);
|
||||
++ targetMorph;
|
||||
}
|
||||
}
|
||||
|
||||
if(!removed.empty()) {
|
||||
OSG_WARN << "Monitor: animation.unused_morphtarget" << std::endl;
|
||||
for(TargetChannelList::iterator targetChannel = _channels.begin() ; targetChannel != _channels.end() ; ) {
|
||||
std::string target = targetChannel->first;
|
||||
|
||||
if(removed.find(target) != removed.end()) {
|
||||
_channels.erase(targetChannel ++);
|
||||
}
|
||||
else {
|
||||
if(kept.find(target) != kept.end()) {
|
||||
// update target channel names with the (possibly) new target index in MophTargetList
|
||||
osgAnimation::MorphGeometry& morphGeometry = *_morphTargets[target];
|
||||
const osgAnimation::MorphGeometry::MorphTargetList& targets = morphGeometry.getMorphTargetList();
|
||||
for(unsigned int i = 0 ; i < targets.size() ; ++ i) {
|
||||
if(targets[i].getGeometry()->getName() == target) {
|
||||
std::ostringstream oss;
|
||||
oss << i;
|
||||
targetChannel->second->setName(oss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++ targetChannel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cleanInvalidUpdateMorph() {
|
||||
// Removes unused UpdateMorph targets (i.e. name does not match any MorphGeometry target)
|
||||
for(AnimationUpdateCallBackMap::iterator update = _updates.begin() ; update != _updates.end() ; ++ update) {
|
||||
osgAnimation::UpdateMorph *updateMorph = dynamic_cast<osgAnimation::UpdateMorph*>(update->first.get());
|
||||
if(!updateMorph) continue;
|
||||
|
||||
NameSet toRemove;
|
||||
for(unsigned int i = 0, numTarget = updateMorph->getNumTarget(); i < numTarget; ++i) {
|
||||
const std::string& name = updateMorph->getTargetName(i);
|
||||
if(_morphTargets.count(name) == 0) {
|
||||
toRemove.insert(name);
|
||||
}
|
||||
}
|
||||
|
||||
for(NameSet::iterator targetName = toRemove.begin(); targetName != toRemove.end(); ++targetName) {
|
||||
updateMorph->removeTarget(*targetName);
|
||||
}
|
||||
}
|
||||
|
||||
// Removes empty UpdateMorphCallback
|
||||
for(AnimationUpdateCallBackMap::iterator update = _updates.begin() ; update != _updates.end() ; ) {
|
||||
osgAnimation::UpdateMorph *updateMorph = dynamic_cast<osgAnimation::UpdateMorph*>(update->first.get());
|
||||
if(!updateMorph || updateMorph->getNumTarget() != 0) {
|
||||
++ update;
|
||||
}
|
||||
else {
|
||||
osg::Callback *callBack = update->second.get()->getUpdateCallback();
|
||||
if(callBack) {
|
||||
if(callBack == updateMorph)
|
||||
update->second.get()->setUpdateCallback(callBack->getNestedCallback());
|
||||
else
|
||||
callBack->removeNestedCallback(updateMorph);
|
||||
}
|
||||
_updates.erase(update ++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void warn(const std::string& method, const std::string& label, const osgAnimation::Channel& channel, const std::string& message) const {
|
||||
OSG_WARN << std::flush << "Warning: " << "[" << method << "] " << "[[" << label << "]] "
|
||||
<< "Channel '" << channel.getName() << "' with target '" << channel.getTargetName()
|
||||
<< " '" << message << std::endl;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* 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 cleanAnimations(osgAnimation::BasicAnimationManager& manager) {
|
||||
// remove manager's invalid animations
|
||||
osgAnimation::AnimationList& animations = manager.getAnimationList();
|
||||
|
||||
std::vector<osgAnimation::Animation*> invalids;
|
||||
for(osgAnimation::AnimationList::iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) {
|
||||
if(animation->valid()) {
|
||||
cleanAnimation(*animation->get());
|
||||
}
|
||||
|
||||
if(!(animation->valid()) || !isValidAnimation(*animation->get())) {
|
||||
invalids.push_back(animation->get());
|
||||
}
|
||||
}
|
||||
|
||||
for(std::vector<osgAnimation::Animation*>::iterator invalid = invalids.begin() ; invalid != invalids.end() ; ++ invalid) {
|
||||
manager.unregisterAnimation(*invalid);
|
||||
}
|
||||
}
|
||||
|
||||
void cleanAnimation(osgAnimation::Animation& animation) {
|
||||
// remove animation's invalid channels
|
||||
osgAnimation::ChannelList& channels = animation.getChannels();
|
||||
osgAnimation::ChannelList invalids;
|
||||
|
||||
for(osgAnimation::ChannelList::iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) {
|
||||
if(channel->valid()) {
|
||||
cleanChannel(*channel->get());
|
||||
}
|
||||
|
||||
if(!channel->valid() || !isValidChannel(*channel->get())) {
|
||||
invalids.push_back(channel->get());
|
||||
}
|
||||
}
|
||||
|
||||
for(osgAnimation::ChannelList::iterator invalid = invalids.begin() ; invalid != invalids.end() ; ++ invalid) {
|
||||
animation.removeChannel(invalid->get());
|
||||
}
|
||||
}
|
||||
|
||||
void cleanChannel(osgAnimation::Channel& channel) {
|
||||
// deduplicate successive KeyFrames that are identical
|
||||
osgAnimation::Sampler* sampler = channel.getSampler();
|
||||
if(sampler) {
|
||||
osgAnimation::KeyframeContainer* container = sampler->getKeyframeContainer();
|
||||
if(container && container->size()) {
|
||||
unsigned int deduplicated = container->linearInterpolationDeduplicate();
|
||||
if(deduplicated) {
|
||||
OSG_WARN << "Deduplicated " << deduplicated << " keyframes on channel " << channel.getName() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isValidAnimationManager(const osgAnimation::BasicAnimationManager& manager) const {
|
||||
// a valid manager has at least one animation and all animations must be valid
|
||||
const osgAnimation::AnimationList& animations = manager.getAnimationList();
|
||||
|
||||
for(osgAnimation::AnimationList::const_iterator animation = animations.begin() ; animation != animations.end() ; ++ animation) {
|
||||
if(!animation->valid() || !isValidAnimation(*animation->get())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return manager.getAnimationList().size() > 0;
|
||||
}
|
||||
|
||||
bool isValidAnimation(const osgAnimation::Animation& animation) const {
|
||||
// a valid animation has at least one channel and all channels must be valid
|
||||
const osgAnimation::ChannelList& channels = animation.getChannels();
|
||||
|
||||
for(osgAnimation::ChannelList::const_iterator channel = channels.begin() ; channel != channels.end() ; ++ channel) {
|
||||
if(!channel->valid() || !isValidChannel(*channel->get())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return channels.size() > 0;
|
||||
}
|
||||
|
||||
bool isValidChannel(const osgAnimation::Channel& channel) const {
|
||||
// a valid channel has valid target i.e.
|
||||
// 1. there exists some UpdateMatrixTransform with channel's target name
|
||||
// 2. the channel does not simply mimick the UpdateMatrixTransform content
|
||||
std::string target = channel.getTargetName();
|
||||
for(AnimationUpdateCallBackMap::const_iterator update = _updates.begin() ; update != _updates.end() ; ++ update) {
|
||||
osgAnimation::UpdateMorph *updateMorph = dynamic_cast<osgAnimation::UpdateMorph*>(update->first.get());
|
||||
if(updateMorph) {
|
||||
for(int i = 0, num = updateMorph->getNumTarget(); i < num; ++i) {
|
||||
if(updateMorph->getTargetName(i) == target) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(update->first->getName() == target) {
|
||||
// check if channel contains necessary data or just mimick the UpdateCallback's StackedTransform
|
||||
bool channelMimickingTransform = isChannelEqualToStackedTransform(channel,
|
||||
dynamic_cast<osgAnimation::UpdateMatrixTransform*>(update->first.get()));
|
||||
if(channelMimickingTransform) {
|
||||
warn("isChannelEqualToStackedTransform", "animation", channel, "seems redundant with stacked transform and has been removed.");
|
||||
}
|
||||
return !channelMimickingTransform;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const osgAnimation::StackedTransformElement* getStackedElement(const osgAnimation::StackedTransform& transforms, const std::string& name) const {
|
||||
for(osgAnimation::StackedTransform::const_iterator transform = transforms.begin() ; transform != transforms.end() ; ++ transform) {
|
||||
if(transform->valid() && transform->get()->getName() == name) {
|
||||
return transform->get();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isChannelEqualToStackedTransform(const osgAnimation::Channel& channel, const osgAnimation::UpdateMatrixTransform* matrixTransform) const {
|
||||
// if the channel has no 'StackedTransform' we compare the KeyFrame with the 'no-op' transform
|
||||
const osgAnimation::StackedTransformElement* element = getStackedElement(matrixTransform->getStackedTransforms(), channel.getName());
|
||||
|
||||
if(channel.getName() == "translate") {
|
||||
const osgAnimation::StackedTranslateElement* translation = dynamic_cast<const osgAnimation::StackedTranslateElement*>(element);
|
||||
osg::Vec3 value(0., 0., 0.);
|
||||
if(translation) {
|
||||
value = translation->getTranslate();
|
||||
}
|
||||
return isChannelEqualToStackedTransform(dynamic_cast<const osgAnimation::Vec3LinearChannel*>(&channel), value);
|
||||
}
|
||||
else if(channel.getName() == "scale") {
|
||||
const osgAnimation::StackedScaleElement* scale = dynamic_cast<const osgAnimation::StackedScaleElement*>(element);
|
||||
osg::Vec3 value(1., 1., 1.);
|
||||
if(scale) {
|
||||
value = scale->getScale();
|
||||
}
|
||||
return isChannelEqualToStackedTransform(dynamic_cast<const osgAnimation::Vec3LinearChannel*>(&channel), value);
|
||||
}
|
||||
else if(channel.getName() == "rotate") {
|
||||
const osgAnimation::StackedQuaternionElement* rotation = dynamic_cast<const osgAnimation::StackedQuaternionElement*>(element);
|
||||
osg::Quat value(0., 0., 0., 1.);
|
||||
if(rotation) {
|
||||
value = rotation->getQuaternion();
|
||||
}
|
||||
return isChannelEqualToStackedTransform(dynamic_cast<const osgAnimation::QuatSphericalLinearChannel*>(&channel), value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T, typename V>
|
||||
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);
|
||||
}
|
||||
|
||||
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 removeAnimationTransforms() {
|
||||
for(MatrixTransformList::iterator transform = _transforms.begin() ; transform != _transforms.end() ; ++ transform) {
|
||||
if(transform->valid()) {
|
||||
removeFromParents(transform->get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeAnimatedGeometries() {
|
||||
for(MorphGeometryMap::iterator morphGeometry = _morphGeometries.begin() ; morphGeometry != _morphGeometries.end() ; ++ morphGeometry) {
|
||||
if(morphGeometry->first.valid()) {
|
||||
replaceMorphGeometryByGeometry(*morphGeometry->first.get(), morphGeometry->second);
|
||||
}
|
||||
}
|
||||
|
||||
for(RigGeometryList::iterator rigGeometry = _rigGeometries.begin() ; rigGeometry != _rigGeometries.end() ; ++ rigGeometry) {
|
||||
if(rigGeometry->valid()) {
|
||||
replaceRigGeometryBySource(*(rigGeometry->get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeFromParents(osg::Node* node) {
|
||||
osg::Node::ParentList parents = node->getParents();
|
||||
for(osg::Node::ParentList::iterator parent = parents.begin() ; parent != parents.end() ; ++ parent) {
|
||||
if(*parent) {
|
||||
(*parent)->removeChild(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void replaceRigGeometryBySource(osgAnimation::RigGeometry& rigGeometry) const {
|
||||
if(osgAnimation::MorphGeometry* source = dynamic_cast<osgAnimation::MorphGeometry*>(rigGeometry.getSourceGeometry())) {
|
||||
osgAnimation::MorphGeometry* morph = new osgAnimation::MorphGeometry(*source);
|
||||
replaceAnimatedGeometryByStaticGeometry(&rigGeometry, morph);
|
||||
}
|
||||
else {
|
||||
replaceAnimatedGeometryByStaticGeometry(&rigGeometry,
|
||||
new osg::Geometry(*rigGeometry.getSourceGeometry()));
|
||||
}
|
||||
}
|
||||
|
||||
void replaceMorphGeometryByGeometry(osgAnimation::MorphGeometry& morphGeometry, osgAnimation::RigGeometry* rigGeometry=0) const {
|
||||
osg::Geometry* geometry = new osg::Geometry(morphGeometry);
|
||||
if(!rigGeometry) {
|
||||
replaceAnimatedGeometryByStaticGeometry(&morphGeometry, geometry);
|
||||
}
|
||||
else {
|
||||
rigGeometry->setSourceGeometry(geometry);
|
||||
}
|
||||
}
|
||||
|
||||
void replaceAnimatedGeometryByStaticGeometry(osg::Geometry* animatedGeometry, osg::Geometry* staticGeometry) const {
|
||||
for(unsigned int i = 0 ; i < animatedGeometry->getNumParents() ; ++ i) {
|
||||
osg::Geode* parent = (animatedGeometry->getParent(i) ? animatedGeometry->getParent(i)->asGeode() : 0);
|
||||
if(parent) {
|
||||
parent->addDrawable(staticGeometry);
|
||||
parent->removeDrawable(animatedGeometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bakeRigInitialPose() {
|
||||
// use RigTransformSoftware to compute T-pose and replace rig source by computed geometry
|
||||
for(RigGeometryList::iterator rigiterator = _rigGeometries.begin() ; rigiterator != _rigGeometries.end() ; ++ rigiterator) {
|
||||
osgAnimation::RigGeometry* rigGeometry = (*rigiterator).get();
|
||||
rigGeometry->setRigTransformImplementation(new osgAnimation::RigTransformSoftware);
|
||||
rigGeometry->update();
|
||||
|
||||
osg::Geometry* baked = static_cast<osg::Geometry*>(rigGeometry->clone(osg::CopyOp::DEEP_COPY_ALL));
|
||||
rigGeometry->setSourceGeometry(baked);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
BasicAnimationManagerMap _managers;
|
||||
AnimationUpdateCallBackMap _updates;
|
||||
MatrixTransformList _transforms;
|
||||
RigGeometryList _rigGeometries;
|
||||
MorphGeometryMap _morphGeometries;
|
||||
NameMorphMap _morphTargets;
|
||||
TargetChannelList _channels;
|
||||
StatLogger _logger;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,31 +0,0 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ANIMATION_VISITOR
|
||||
#define ANIMATION_VISITOR
|
||||
|
||||
#include <osgUtil/UpdateVisitor>
|
||||
#include <osgAnimation/UpdateMatrixTransform>
|
||||
#include <osgAnimation/AnimationManagerBase>
|
||||
#include <osgAnimation/BasicAnimationManager>
|
||||
|
||||
|
||||
// the idea is to create true Geometry if skeleton with RigGeometry
|
||||
class AnimationVisitor : public osgUtil::UpdateVisitor
|
||||
{
|
||||
public:
|
||||
AnimationVisitor() {
|
||||
setFrameStamp(new osg::FrameStamp());
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -22,7 +22,7 @@ public:
|
||||
BindPerVertexVisitor(): GeometryUniqueVisitor("BindPerVertexVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
void process(osg::Geometry& geometry) {
|
||||
if (geometry.getNormalArray() && geometry.getNormalBinding() != osg::Geometry::BIND_PER_VERTEX) {
|
||||
bindPerVertex(geometry.getNormalArray(),
|
||||
geometry.getNormalBinding(),
|
||||
@ -50,25 +50,90 @@ public:
|
||||
geometry.getPrimitiveSetList());
|
||||
geometry.setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
setProcessed(&geometry);
|
||||
};
|
||||
|
||||
protected:
|
||||
void bindPerVertex(osg::Array* src,
|
||||
osg::Geometry::AttributeBinding fromBinding,
|
||||
osg::Geometry::PrimitiveSetList& primitives) {
|
||||
if (doConvert<osg::Vec3Array>(src, fromBinding, 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>
|
||||
|
@ -3,10 +3,44 @@ SET(TARGET_SRC
|
||||
OpenGLESGeometryOptimizer.cpp
|
||||
TriangleStripVisitor.cpp
|
||||
IndexMeshVisitor.cpp
|
||||
UnIndexMeshVisitor.cpp
|
||||
forsythtriangleorderoptimizer.cpp)
|
||||
UnIndexMeshVisitor.cpp)
|
||||
|
||||
SET(TARGET_H
|
||||
AABBonBoneVisitor
|
||||
AnimationCleanerVisitor
|
||||
BindPerVertexVisitor
|
||||
debug
|
||||
DetachPrimitiveVisitor
|
||||
DisableAnimationVisitor
|
||||
DrawArrayVisitor
|
||||
EdgeIndexFunctor
|
||||
GeometryArray
|
||||
GeometryInspector
|
||||
GeometrySplitterVisitor
|
||||
GeometryUniqueVisitor
|
||||
glesUtil
|
||||
IndexMeshVisitor
|
||||
LimitMorphTargetCount
|
||||
Line
|
||||
LineIndexFunctor
|
||||
MostInfluencedGeometryByBone
|
||||
OpenGLESGeometryOptimizer
|
||||
PointIndexFunctor
|
||||
PreTransformVisitor
|
||||
PrimitiveIndexors
|
||||
RigAnimationVisitor
|
||||
RigAttributesVisitor
|
||||
SmoothNormalVisitor
|
||||
StatLogger
|
||||
SubGeometry
|
||||
TangentSpaceVisitor
|
||||
TriangleMeshGraph
|
||||
TriangleStripVisitor
|
||||
UnIndexMeshVisitor
|
||||
WireframeVisitor
|
||||
)
|
||||
|
||||
#### end var setup ###
|
||||
SET(TARGET_ADDED_LIBRARIES
|
||||
osgUtil)
|
||||
osgUtil osgAnimation)
|
||||
SETUP_PLUGIN(gles)
|
||||
|
@ -23,32 +23,43 @@ public:
|
||||
_userValue(userValue), _keepGeometryAttributes(keepGeometryAttributes), _inlined(inlined)
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
if(shouldDetach(geometry)) {
|
||||
osg::Geometry* detached = createDetachedGeometry(geometry);
|
||||
|
||||
void reparentDuplicatedGeometry(osg::Geometry& geometry, osg::Geometry& duplicated)
|
||||
{
|
||||
unsigned int nbParents = geometry.getNumParents();
|
||||
for(unsigned int i = 0 ; i < nbParents ; ++ i) {
|
||||
osg::Node* parent = geometry.getParent(i);
|
||||
// TODO: Geode will be soon deprecated
|
||||
if(parent && parent->asGeode()) {
|
||||
osg::Geode* geode = parent->asGeode();
|
||||
geode->addDrawable(detached);
|
||||
geode->addDrawable(&duplicated);
|
||||
|
||||
if(!_inlined) {
|
||||
geode->removeDrawable(&geometry);
|
||||
geode->removeDrawable(&duplicated);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process(osg::Geometry& geometry) {
|
||||
if(shouldDetach(geometry)) {
|
||||
osg::Geometry* detached = detachGeometry(geometry);
|
||||
reparentDuplicatedGeometry(geometry, *detached);
|
||||
setProcessed(detached);
|
||||
}
|
||||
setProcessed(&geometry);
|
||||
}
|
||||
|
||||
void process(osgAnimation::RigGeometry& rigGeometry) {
|
||||
return process(static_cast<osg::Geometry&>(rigGeometry));
|
||||
}
|
||||
|
||||
protected:
|
||||
bool shouldDetach(osg::Geometry& geometry) {
|
||||
bool 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) {
|
||||
osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
|
||||
const osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
|
||||
if(primitive && primitive->getUserValue(_userValue, detach) && detach) {
|
||||
return true;
|
||||
}
|
||||
@ -56,6 +67,23 @@ protected:
|
||||
return false;
|
||||
}
|
||||
|
||||
osg::Geometry* detachGeometry(osg::Geometry& source) {
|
||||
// filter vertex buffers depending on geometry type
|
||||
osg::Geometry* detached = makeDetachedGeometry(source);
|
||||
detached->setUserValue(_userValue, true);
|
||||
return detached;
|
||||
}
|
||||
|
||||
osg::Geometry* makeDetachedGeometry(osg::Geometry& geometry) {
|
||||
if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast<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) {
|
||||
@ -72,6 +100,11 @@ protected:
|
||||
detached->setUserDataContainer(0);
|
||||
}
|
||||
|
||||
detached->setPrimitiveSetList(createDetachedPrimitives(source));
|
||||
return detached;
|
||||
}
|
||||
|
||||
osg::Geometry::PrimitiveSetList createDetachedPrimitives(osg::Geometry& source) {
|
||||
// filter primitivesets
|
||||
osg::Geometry::PrimitiveSetList detachedPrimitives;
|
||||
for(int i = source.getNumPrimitiveSets() - 1 ; i >= 0 ; -- i) {
|
||||
@ -82,12 +115,47 @@ protected:
|
||||
source.removePrimitiveSet(i);
|
||||
}
|
||||
}
|
||||
return detachedPrimitives;
|
||||
}
|
||||
|
||||
detached->setPrimitiveSetList(detachedPrimitives);
|
||||
detached->setUserValue(_userValue, true);
|
||||
osgAnimation::MorphGeometry* createDetachedGeometry(osgAnimation::MorphGeometry& source) {
|
||||
osgAnimation::MorphGeometry* detached = new osgAnimation::MorphGeometry(*createDetachedGeometry(static_cast<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;
|
||||
}
|
||||
|
||||
std::string _userValue;
|
||||
bool _keepGeometryAttributes;
|
||||
|
47
src/osgPlugins/gles/DisableAnimationVisitor
Normal file
47
src/osgPlugins/gles/DisableAnimationVisitor
Normal file
@ -0,0 +1,47 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DISABLE_ANIMATION_VISITOR
|
||||
#define DISABLE_ANIMATION_VISITOR
|
||||
|
||||
#include "AnimationCleanerVisitor"
|
||||
|
||||
|
||||
// Derive from AnimationCleanerVisitor to only allow animation data removal
|
||||
class DisableAnimationVisitor : public AnimationCleanerVisitor
|
||||
{
|
||||
public:
|
||||
DisableAnimationVisitor():
|
||||
AnimationCleanerVisitor("DisableAnimationVisitor"),
|
||||
_removeDone(false)
|
||||
{}
|
||||
|
||||
~DisableAnimationVisitor() {
|
||||
// make sure removal has been done
|
||||
remove();
|
||||
}
|
||||
|
||||
void clean()
|
||||
{}
|
||||
|
||||
void remove() {
|
||||
if(!_removeDone) {
|
||||
AnimationCleanerVisitor::removeAnimation();
|
||||
}
|
||||
_removeDone = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool _removeDone;
|
||||
};
|
||||
|
||||
#endif
|
@ -22,7 +22,7 @@ public:
|
||||
DrawArrayVisitor(): GeometryUniqueVisitor("DrawArrayVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
void process(osg::Geometry& geometry) {
|
||||
GeometryArrayList srcArrays(geometry);
|
||||
|
||||
// clone but clear content
|
||||
@ -87,7 +87,6 @@ public:
|
||||
|
||||
dst.setToGeometry(geometry);
|
||||
geometry.setPrimitiveSetList(newGeometry->getPrimitiveSetList());
|
||||
setProcessed(&geometry);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -57,21 +57,36 @@ struct GeometryArrayList {
|
||||
virtual void apply(osg::Vec3Array& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4Array& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec4ubArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2bArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3bArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4bArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2ubArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3ubArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4ubArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2iArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3iArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4iArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2uiArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3uiArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4uiArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2sArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3sArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4sArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2usArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3usArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4usArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::Vec2dArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec3dArray& array) { copy(array); }
|
||||
virtual void apply(osg::Vec4dArray& array) { copy(array); }
|
||||
|
||||
virtual void apply(osg::MatrixfArray& array) { copy(array); }
|
||||
virtual void apply(osg::MatrixdArray& array) { copy(array); }
|
||||
protected:
|
||||
|
||||
ArrayIndexAppendVisitor& operator = (const ArrayIndexAppendVisitor&) { return *this; }
|
||||
@ -82,10 +97,9 @@ struct GeometryArrayList {
|
||||
|
||||
template <class T> bool arrayAppendElement(osg::Array* src, unsigned int index, osg::Array* dst)
|
||||
{
|
||||
T* array= dynamic_cast<T*>(src);
|
||||
T* array = dynamic_cast<T*>(src);
|
||||
if (array) {
|
||||
T* arrayDst = dynamic_cast<T*>(dst);
|
||||
if (array && arrayDst)
|
||||
{
|
||||
arrayDst->push_back((*array)[index]);
|
||||
return true;
|
||||
}
|
||||
@ -93,10 +107,30 @@ struct GeometryArrayList {
|
||||
}
|
||||
|
||||
void operator()(osg::Array* src, unsigned int index, osg::Array* dst) {
|
||||
if (arrayAppendElement<osg::ByteArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::ShortArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::IntArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::UByteArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::UShortArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::UIntArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::FloatArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::DoubleArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2Array>(src, index, dst))
|
||||
return;
|
||||
|
||||
@ -106,8 +140,83 @@ struct GeometryArrayList {
|
||||
if (arrayAppendElement<osg::Vec4Array>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2Array>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec3Array>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4Array>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2bArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec3bArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4bArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2ubArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec3ubArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4ubArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2sArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec3sArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4sArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2usArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec3usArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4usArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2iArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec3iArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4iArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2uiArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec3uiArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4uiArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec2dArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec3dArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::Vec4dArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::MatrixfArray>(src, index, dst))
|
||||
return;
|
||||
|
||||
if (arrayAppendElement<osg::MatrixdArray>(src, index, dst))
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@ -124,10 +233,30 @@ struct GeometryArrayList {
|
||||
}
|
||||
|
||||
void operator()(osg::Array* array, unsigned int numElements) {
|
||||
if (arraySetNumElements<osg::ByteArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::ShortArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::IntArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::UByteArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::UShortArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::UIntArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::FloatArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::DoubleArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2Array>(array, numElements))
|
||||
return;
|
||||
|
||||
@ -137,8 +266,83 @@ struct GeometryArrayList {
|
||||
if (arraySetNumElements<osg::Vec4Array>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2Array>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec3Array>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4Array>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2bArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec3bArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4bArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2ubArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec3ubArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4ubArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2sArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec3sArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4sArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2usArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec3usArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4usArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2iArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec3iArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4iArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2uiArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec3uiArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4uiArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec2dArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec3dArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::Vec4dArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::MatrixfArray>(array, numElements))
|
||||
return;
|
||||
|
||||
if (arraySetNumElements<osg::MatrixdArray>(array, numElements))
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@ -154,84 +358,110 @@ struct GeometryArrayList {
|
||||
GeometryArrayList(osg::Geometry& geometry) {
|
||||
_vertexes = geometry.getVertexArray();
|
||||
unsigned int nbvertexes = _vertexes->getNumElements();
|
||||
if (geometry.getNormalArray() && nbvertexes == geometry.getNormalArray()->getNumElements())
|
||||
|
||||
if (geometry.getNormalArray() && nbvertexes == geometry.getNormalArray()->getNumElements()) {
|
||||
_normals = geometry.getNormalArray();
|
||||
}
|
||||
|
||||
if (geometry.getColorArray() && nbvertexes == geometry.getColorArray()->getNumElements())
|
||||
if (geometry.getColorArray() && nbvertexes == geometry.getColorArray()->getNumElements()) {
|
||||
_colors = geometry.getColorArray();
|
||||
}
|
||||
|
||||
if (geometry.getSecondaryColorArray() && nbvertexes == geometry.getSecondaryColorArray()->getNumElements())
|
||||
if (geometry.getSecondaryColorArray() && nbvertexes == geometry.getSecondaryColorArray()->getNumElements()) {
|
||||
_secondaryColors = geometry.getSecondaryColorArray();
|
||||
}
|
||||
|
||||
if (geometry.getFogCoordArray() && nbvertexes == geometry.getFogCoordArray()->getNumElements())
|
||||
if (geometry.getFogCoordArray() && nbvertexes == geometry.getFogCoordArray()->getNumElements()) {
|
||||
_fogCoords = geometry.getFogCoordArray();
|
||||
}
|
||||
|
||||
_texCoordArrays.resize(geometry.getNumTexCoordArrays());
|
||||
for(unsigned int i=0;i<geometry.getNumTexCoordArrays();++i)
|
||||
if (geometry.getTexCoordArray(i) && nbvertexes == geometry.getTexCoordArray(i)->getNumElements())
|
||||
for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) {
|
||||
if (geometry.getTexCoordArray(i) && nbvertexes == geometry.getTexCoordArray(i)->getNumElements()) {
|
||||
_texCoordArrays[i] = geometry.getTexCoordArray(i);
|
||||
}
|
||||
}
|
||||
|
||||
_attributesArrays.resize(geometry.getNumVertexAttribArrays());
|
||||
for(unsigned int i=0;i<geometry.getNumVertexAttribArrays();++i)
|
||||
if (geometry.getVertexAttribArrayList()[i] && nbvertexes == geometry.getVertexAttribArrayList()[i]->getNumElements())
|
||||
for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) {
|
||||
if (geometry.getVertexAttribArrayList()[i] && nbvertexes == geometry.getVertexAttribArrayList()[i]->getNumElements()) {
|
||||
_attributesArrays[i] = geometry.getVertexAttribArrayList()[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setNumElements(unsigned int nbElements) {
|
||||
if (_vertexes.valid())
|
||||
if (_vertexes.valid()) {
|
||||
ArraySetNumElements()(_vertexes.get(), nbElements);
|
||||
}
|
||||
|
||||
if (_normals.valid())
|
||||
if (_normals.valid()) {
|
||||
ArraySetNumElements()(_normals.get(), nbElements);
|
||||
}
|
||||
|
||||
if (_colors.valid())
|
||||
if (_colors.valid()) {
|
||||
ArraySetNumElements()(_colors.get(), nbElements);
|
||||
}
|
||||
|
||||
if (_secondaryColors.valid())
|
||||
if (_secondaryColors.valid()) {
|
||||
ArraySetNumElements()(_secondaryColors.get(), nbElements);
|
||||
}
|
||||
|
||||
if (_fogCoords.valid())
|
||||
if (_fogCoords.valid()) {
|
||||
ArraySetNumElements()(_fogCoords.get(), nbElements);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++)
|
||||
if (_texCoordArrays[i].valid())
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++) {
|
||||
if (_texCoordArrays[i].valid()) {
|
||||
ArraySetNumElements()(_texCoordArrays[i].get(), nbElements);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++)
|
||||
if (_attributesArrays[i].valid())
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++) {
|
||||
if (_attributesArrays[i].valid()) {
|
||||
ArraySetNumElements()(_attributesArrays[i].get(), nbElements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int append(unsigned int index, GeometryArrayList& dst) {
|
||||
if (_vertexes.valid())
|
||||
if (_vertexes.valid()) {
|
||||
ArrayAppendElement()(_vertexes.get(), index, dst._vertexes.get());
|
||||
}
|
||||
|
||||
if (_normals.valid())
|
||||
if (_normals.valid()) {
|
||||
ArrayAppendElement()(_normals.get(), index, dst._normals.get());
|
||||
}
|
||||
|
||||
if (_colors.valid())
|
||||
if (_colors.valid()) {
|
||||
ArrayAppendElement()(_colors.get(), index, dst._colors.get());
|
||||
}
|
||||
|
||||
if (_secondaryColors.valid())
|
||||
if (_secondaryColors.valid()) {
|
||||
ArrayAppendElement()(_secondaryColors.get(), index, dst._secondaryColors.get());
|
||||
}
|
||||
|
||||
if (_fogCoords.valid())
|
||||
if (_fogCoords.valid()) {
|
||||
ArrayAppendElement()(_fogCoords.get(), index, dst._fogCoords.get());
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++)
|
||||
if (_texCoordArrays[i].valid())
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++) {
|
||||
if (_texCoordArrays[i].valid()) {
|
||||
ArrayAppendElement()(_texCoordArrays[i].get(), index, dst._texCoordArrays[i].get());
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++)
|
||||
if (_attributesArrays[i].valid())
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++) {
|
||||
if (_attributesArrays[i].valid()) {
|
||||
ArrayAppendElement()(_attributesArrays[i].get(), index, dst._attributesArrays[i].get());
|
||||
}
|
||||
}
|
||||
|
||||
return dst._vertexes->getNumElements()-1;
|
||||
}
|
||||
|
||||
|
||||
unsigned int append(const IndexList& indexes, GeometryArrayList& dst) {
|
||||
|
||||
if (_vertexes.valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._vertexes.get());
|
||||
_vertexes->accept(append);
|
||||
@ -257,17 +487,19 @@ struct GeometryArrayList {
|
||||
_fogCoords->accept(append);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++)
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++) {
|
||||
if (_texCoordArrays[i].valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._texCoordArrays[i].get());
|
||||
_texCoordArrays[i]->accept(append);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++)
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); ++ i) {
|
||||
if (_attributesArrays[i].valid()) {
|
||||
ArrayIndexAppendVisitor append(indexes, dst._attributesArrays[i].get());
|
||||
_attributesArrays[i]->accept(append);
|
||||
}
|
||||
}
|
||||
|
||||
return dst._vertexes->getNumElements()-1;
|
||||
}
|
||||
@ -291,15 +523,17 @@ struct GeometryArrayList {
|
||||
|
||||
array._texCoordArrays.resize(_texCoordArrays.size());
|
||||
for (unsigned int i = 0; i < _texCoordArrays.size(); i++) {
|
||||
if (_texCoordArrays[i].valid())
|
||||
if (_texCoordArrays[i].valid()) {
|
||||
array._texCoordArrays[i] = dynamic_cast<osg::Array*>(_texCoordArrays[i]->cloneType());
|
||||
}
|
||||
}
|
||||
|
||||
array._attributesArrays.resize(_attributesArrays.size());
|
||||
for (unsigned int i = 0; i < _attributesArrays.size(); i++) {
|
||||
if (_attributesArrays[i].valid())
|
||||
if (_attributesArrays[i].valid()) {
|
||||
array._attributesArrays[i] = dynamic_cast<osg::Array*>(_attributesArrays[i]->cloneType());
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
65
src/osgPlugins/gles/GeometryInspector
Normal file
65
src/osgPlugins/gles/GeometryInspector
Normal file
@ -0,0 +1,65 @@
|
||||
#ifndef GEOMETRY_INSPECTOR
|
||||
#define GEOMETRY_INSPECTOR
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
|
||||
#include <osg/Geode>
|
||||
#include <osgDB/ReaderWriter>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/WriteFile>
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
|
||||
class GeometryInspector : public GeometryUniqueVisitor {
|
||||
public:
|
||||
void process(osg::Geometry& geometry) {}
|
||||
void process(osgAnimation::RigGeometry& rigGeometry) {
|
||||
osgAnimation::MorphGeometry* morph = dynamic_cast<osgAnimation::MorphGeometry*>(rigGeometry.getSourceGeometry());
|
||||
if(morph) {
|
||||
process(*morph);
|
||||
}
|
||||
else {
|
||||
process(*rigGeometry.getSourceGeometry());
|
||||
}
|
||||
}
|
||||
void process(osgAnimation::MorphGeometry& morphGeometry) {
|
||||
osgAnimation::MorphGeometry::MorphTargetList targets = morphGeometry.getMorphTargetList();
|
||||
unsigned int count = 0;
|
||||
for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target, ++ count) {
|
||||
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry(*target->getGeometry());
|
||||
geometry->setPrimitiveSetList(morphGeometry.getPrimitiveSetList());
|
||||
std::ostringstream oss;
|
||||
if(geometry->getName().empty()) {
|
||||
oss << "/tmp/noname_" << _processed.size();
|
||||
}
|
||||
else {
|
||||
oss << "/tmp/" << geometry->getName();
|
||||
}
|
||||
oss << "_" << count << ".osgt";
|
||||
dump(*geometry, oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void dump(osg::Geometry& geometry, const std::string& path) {
|
||||
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
|
||||
geode->addDrawable(&geometry);
|
||||
|
||||
osg::ref_ptr<osgDB::Registry> registry = osgDB::Registry::instance();
|
||||
std::string ext = osgDB::getLowerCaseFileExtension(path);
|
||||
osgDB::ReaderWriter* writer = registry->getReaderWriterForExtension(ext);
|
||||
|
||||
OSG_WARN << "extension: " << ext << std::flush;
|
||||
OSG_WARN << "writer: " << writer << std::flush;
|
||||
if(writer) {
|
||||
writer->writeNode(*geode, path.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -14,6 +14,7 @@
|
||||
#define GEOMETRY_SPLITTER_VISITOR
|
||||
|
||||
#include <set>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
@ -22,121 +23,257 @@
|
||||
#include <osg/ValueObject>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
|
||||
#include <osgAnimation/RigGeometry>
|
||||
|
||||
#include "glesUtil"
|
||||
#include "GeometryArray"
|
||||
#include "GeometryUniqueVisitor"
|
||||
#include "glesUtil"
|
||||
#include "AnimationCleanerVisitor"
|
||||
#include "TriangleMeshGraph"
|
||||
#include "SubGeometry"
|
||||
#include "Line"
|
||||
|
||||
|
||||
|
||||
class GeometryIndexSplitter
|
||||
{
|
||||
public:
|
||||
typedef std::vector<osg::ref_ptr<osg::Geometry> > GeometryList;
|
||||
protected:
|
||||
class IndexCache : public IndexDeque {
|
||||
public:
|
||||
IndexCache(unsigned int size=64) : _size(size)
|
||||
{}
|
||||
|
||||
GeometryIndexSplitter(unsigned int maxIndex, bool disablePostTransform):
|
||||
_maxIndexToSplit(maxIndex), _disablePostTransform(disablePostTransform)
|
||||
void push_back(unsigned int value) {
|
||||
IndexDeque::push_back(value);
|
||||
if(size() > _size) pop_front();
|
||||
}
|
||||
protected:
|
||||
unsigned int _size;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef std::vector< osg::ref_ptr<osg::Geometry> > GeometryList;
|
||||
|
||||
GeometryIndexSplitter(unsigned int maxAllowedIndex):
|
||||
_maxAllowedIndex(maxAllowedIndex)
|
||||
{}
|
||||
|
||||
bool split(osg::Geometry& geometry) {
|
||||
if(!hasValidPrimitives(geometry) || !needToSplit(geometry)) {
|
||||
if(!_disablePostTransform) {
|
||||
// optimize cache for rendering
|
||||
glesUtil::VertexCacheVisitor posttransform;
|
||||
posttransform.optimizeVertices(geometry);
|
||||
}
|
||||
_geometryList.push_back(&geometry); // remap geometry to itself
|
||||
if(!needToSplit(geometry)) {
|
||||
_geometryList.push_back(&geometry);
|
||||
return false;
|
||||
}
|
||||
|
||||
// keep bounding box data as user value if needed in subsequent processing
|
||||
attachBufferBoundingBox(geometry);
|
||||
|
||||
// first optimize primitives indexing
|
||||
{
|
||||
if(!_disablePostTransform) {
|
||||
// post-transform for better locality
|
||||
glesUtil::VertexCacheVisitor posttransform;
|
||||
posttransform.optimizeVertices(geometry);
|
||||
}
|
||||
// pre-transform to reindex correctly
|
||||
glesUtil::VertexAccessOrderVisitor pretransform;
|
||||
pretransform.optimizeOrder(geometry);
|
||||
}
|
||||
|
||||
// Clone geometry as we will modify vertex arrays and primitives
|
||||
// To avoid issues with shared buffers, arrays & primitives should be
|
||||
// deep cloned.
|
||||
// As UserValues might be changed on a per-geometry basis afterwards, we
|
||||
// also deep clone userdata
|
||||
// We do *not* want to clone statesets as they reference a UniqueID that
|
||||
// should be unique (see #83056464).
|
||||
osg::ref_ptr<osg::Geometry> processing = osg::clone(&geometry, osg::CopyOp::DEEP_COPY_ARRAYS |
|
||||
osg::CopyOp::DEEP_COPY_PRIMITIVES |
|
||||
osg::CopyOp::DEEP_COPY_USERDATA);
|
||||
osg::ref_ptr<osg::Geometry> reported;
|
||||
while (true) {
|
||||
reported = doSplit(*processing);
|
||||
|
||||
// reduce vertex array if needed
|
||||
if(processing && processing->getNumPrimitiveSets()) {
|
||||
GeometryArrayList arrayList(*processing);
|
||||
arrayList.setNumElements(osg::minimum(arrayList.size(), _maxIndexToSplit + 1));
|
||||
_geometryList.push_back(processing);
|
||||
}
|
||||
|
||||
if (!reported.valid()) {
|
||||
break;
|
||||
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 {
|
||||
processing = reported;
|
||||
reported = 0;
|
||||
|
||||
// re order index elements
|
||||
glesUtil::VertexAccessOrderVisitor preTransform;
|
||||
preTransform.optimizeOrder(*processing);
|
||||
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;
|
||||
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 > " << _maxIndexToSplit << ", splitted to "
|
||||
<< ") 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 hasValidPrimitives(osg::Geometry& geometry) const {
|
||||
for (unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) {
|
||||
osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
|
||||
if (primitive) {
|
||||
if (!primitive->getDrawElements()) {
|
||||
osg::notify(osg::INFO) << "can't split Geometry " << geometry.getName()
|
||||
<< " (" << &geometry << ") contains non indexed primitives"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (primitive->getMode()) {
|
||||
case osg::PrimitiveSet::TRIANGLES:
|
||||
case osg::PrimitiveSet::LINES:
|
||||
case osg::PrimitiveSet::POINTS:
|
||||
break;
|
||||
default:
|
||||
osg::notify(osg::FATAL) << "can't split Geometry " << geometry.getName()
|
||||
<< " (" << &geometry << ") contains non point/line/triangle primitives"
|
||||
<< std::endl;
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool needToSplit(const osg::Geometry& geometry) const {
|
||||
for(unsigned int i = 0; i < geometry.getNumPrimitiveSets(); ++ i) {
|
||||
const osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements();
|
||||
if (needToSplit(*primitive)) {
|
||||
if (primitive && needToSplit(*primitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -145,7 +282,7 @@ protected:
|
||||
|
||||
bool needToSplit(const osg::DrawElements& primitive) const {
|
||||
for(unsigned int j = 0; j < primitive.getNumIndices(); j++) {
|
||||
if (primitive.index(j) > _maxIndexToSplit){
|
||||
if (primitive.index(j) > _maxAllowedIndex){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -186,149 +323,14 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
osg::Geometry* doSplit(osg::Geometry& geometry) const {
|
||||
osg::Geometry::PrimitiveSetList geomPrimitives;
|
||||
osg::Geometry::PrimitiveSetList wirePrimitives;
|
||||
osg::Geometry::PrimitiveSetList reportedPrimitives;
|
||||
std::vector< osg::ref_ptr<osg::DrawElements> > primitivesToFix;
|
||||
|
||||
osg::Geometry::PrimitiveSetList& primitives = geometry.getPrimitiveSetList();
|
||||
std::set<unsigned int> validIndices;
|
||||
osg::ref_ptr<osg::Geometry> reportGeometry;
|
||||
|
||||
for (unsigned int i = 0; i < primitives.size(); i++) {
|
||||
osg::DrawElements *primitive = primitives[i]->getDrawElements();
|
||||
|
||||
bool isWireframe = false;
|
||||
if(primitive->getUserValue("wireframe", isWireframe)) {
|
||||
wirePrimitives.push_back(primitive);
|
||||
}
|
||||
else if (needToSplit(*primitive)) {
|
||||
primitivesToFix.push_back(primitive);
|
||||
}
|
||||
else {
|
||||
geomPrimitives.push_back(primitive);
|
||||
setValidIndices(validIndices, primitive);
|
||||
}
|
||||
}
|
||||
|
||||
if (!primitivesToFix.empty()) {
|
||||
// filter all indices > _maxIndexValue in primitives
|
||||
for (unsigned int i = 0; i < primitivesToFix.size(); i++) {
|
||||
osg::DrawElementsUInt* source = dynamic_cast<osg::DrawElementsUInt*>(primitivesToFix[i].get());
|
||||
if (source)
|
||||
{
|
||||
osg::DrawElements* large = removeLargeIndices(source);
|
||||
reportedPrimitives.push_back(large);
|
||||
geomPrimitives.push_back(source);
|
||||
setValidIndices(validIndices, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keep wireframe data associated to the current solid geometry
|
||||
extractWireframePrimitive(wirePrimitives, validIndices, geomPrimitives, reportedPrimitives);
|
||||
|
||||
geometry.setPrimitiveSetList(geomPrimitives);
|
||||
if (!reportedPrimitives.empty()) {
|
||||
reportGeometry = osg::clone(&geometry, osg::CopyOp::DEEP_COPY_ARRAYS |
|
||||
osg::CopyOp::DEEP_COPY_USERDATA);
|
||||
reportGeometry->setPrimitiveSetList(reportedPrimitives);
|
||||
return reportGeometry.release();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setValidIndices(std::set<unsigned int>& indices, const osg::DrawElements* primitive) const {
|
||||
for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) {
|
||||
indices.insert(primitive->index(j));
|
||||
}
|
||||
}
|
||||
|
||||
osg::DrawElements* removeLargeIndices(osg::DrawElementsUInt* source) const {
|
||||
osg::DrawElementsUInt* large = new osg::DrawElementsUInt(source->getMode());
|
||||
unsigned int primitive_size = 0;
|
||||
switch(source->getMode()) {
|
||||
case osg::PrimitiveSet::POINTS:
|
||||
primitive_size = 1;
|
||||
break;
|
||||
case osg::PrimitiveSet::LINES:
|
||||
primitive_size = 2;
|
||||
break;
|
||||
case osg::PrimitiveSet::TRIANGLES:
|
||||
primitive_size = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int id = source->getNumPrimitives() - 1; id >= 0; -- id) {
|
||||
const unsigned int arrayIndex = id * primitive_size;
|
||||
for(unsigned int i = 0 ; i < primitive_size ; ++ i) {
|
||||
if(source->index(arrayIndex + i) > _maxIndexToSplit) {
|
||||
// add primitive in the large DrawElements
|
||||
for(unsigned int j = 0 ; j < primitive_size ; ++ j) {
|
||||
large->addElement(source->index(arrayIndex + j));
|
||||
}
|
||||
// remove primitive from source DrawElements
|
||||
for(int j = primitive_size - 1 ; j >= 0 ; -- j) {
|
||||
source->erase(source->begin() + arrayIndex + j);
|
||||
}
|
||||
break; // skip to next primitive
|
||||
}
|
||||
}
|
||||
}
|
||||
return large;
|
||||
}
|
||||
|
||||
// keep wireframe data associated to the solid geometry
|
||||
void extractWireframePrimitive(osg::Geometry::PrimitiveSetList& lines,
|
||||
const std::set<unsigned int>& indices,
|
||||
osg::Geometry::PrimitiveSetList& primitives,
|
||||
osg::Geometry::PrimitiveSetList& reported) const {
|
||||
if(indices.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0 ; i < lines.size() ; ++ i)
|
||||
{
|
||||
const osg::DrawElementsUInt* line = dynamic_cast<osg::DrawElementsUInt*>(lines[i].get());
|
||||
if(!line || line->getMode() != osg::PrimitiveSet::LINES)
|
||||
{
|
||||
osg::notify(osg::INFO) << "Primitive with bad mode flagged as wireframe. Skipping."<< std::endl;
|
||||
continue;
|
||||
}
|
||||
osg::ref_ptr<osg::DrawElementsUInt> small = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES);
|
||||
osg::ref_ptr<osg::DrawElementsUInt> large = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES);
|
||||
|
||||
for (unsigned int id = 0 ; id < line->getNumPrimitives() ; ++ id) {
|
||||
unsigned int arrayIndex = id * 2;
|
||||
unsigned int a = line->index(arrayIndex),
|
||||
b = line->index(arrayIndex + 1);
|
||||
|
||||
if(indices.find(a) != indices.end() && indices.find(b) != indices.end()) {
|
||||
small->addElement(a);
|
||||
small->addElement(b);
|
||||
}
|
||||
else {
|
||||
large->addElement(a);
|
||||
large->addElement(b);
|
||||
}
|
||||
}
|
||||
|
||||
if(small->size()) {
|
||||
small->setUserValue("wireframe", true);
|
||||
primitives.push_back(small);
|
||||
}
|
||||
|
||||
if(large->size()) {
|
||||
large->setUserValue("wireframe", true);
|
||||
reported.push_back(large);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
const unsigned int _maxIndexToSplit;
|
||||
bool _disablePostTransform;
|
||||
const unsigned int _maxAllowedIndex;
|
||||
GeometryList _geometryList;
|
||||
};
|
||||
|
||||
@ -337,34 +339,67 @@ public:
|
||||
class GeometrySplitterVisitor : public GeometryUniqueVisitor {
|
||||
public:
|
||||
typedef std::vector< osg::ref_ptr<osg::Geometry> > GeometryList;
|
||||
typedef std::vector< osg::ref_ptr<osg::Drawable> > DrawableList;
|
||||
typedef std::map<osg::Geometry*, GeometryList> SplitMap;
|
||||
|
||||
GeometrySplitterVisitor(unsigned int maxIndexValue = 65535, bool disablePostTransform=false):
|
||||
GeometrySplitterVisitor(unsigned int maxAllowedIndex=65535, bool exportNonGeometryDrawables=false):
|
||||
GeometryUniqueVisitor("GeometrySplitterVisitor"),
|
||||
_maxIndexValue(maxIndexValue),
|
||||
_disablePostTransform(disablePostTransform)
|
||||
_maxAllowedIndex(maxAllowedIndex),
|
||||
_exportNonGeometryDrawables(exportNonGeometryDrawables)
|
||||
{}
|
||||
|
||||
void apply(osg::Geode& geode) {
|
||||
GeometryUniqueVisitor::apply(geode);
|
||||
GeometryList remapped;
|
||||
GeometryList remappedGeometries;
|
||||
DrawableList nonGeometryDrawables;
|
||||
|
||||
for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) {
|
||||
osg::Geometry* geometry = geode.getDrawable(i)->asGeometry();
|
||||
if(geometry) {
|
||||
std::map<osg::Geometry*, GeometryList>::iterator lookup = _split.find(geometry);
|
||||
if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(geometry)) {
|
||||
SplitMap::iterator lookup = _split.find(rigGeometry->getSourceGeometry());
|
||||
if(lookup != _split.end() && !lookup->second.empty()) {
|
||||
remapped.insert(remapped.end(), lookup->second.begin(), lookup->second.end());
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove all drawables
|
||||
geode.removeDrawables(0, geode.getNumDrawables());
|
||||
for(unsigned int i = 0 ; i < remapped.size() ; ++ i) {
|
||||
geode.addDrawable(remapped[i].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));
|
||||
}
|
||||
}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
GeometryIndexSplitter splitter(_maxIndexValue, _disablePostTransform);
|
||||
// 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);
|
||||
}
|
||||
@ -377,10 +412,9 @@ protected:
|
||||
void setProcessed(osg::Geometry* node, const GeometryList& list) {
|
||||
_split.insert(std::pair<osg::Geometry*, GeometryList>(node, GeometryList(list)));
|
||||
}
|
||||
|
||||
unsigned int _maxIndexValue;
|
||||
unsigned int _maxAllowedIndex;
|
||||
std::map<osg::Geometry*, GeometryList> _split;
|
||||
bool _disablePostTransform;
|
||||
bool _exportNonGeometryDrawables;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -17,6 +17,9 @@
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Geode>
|
||||
|
||||
#include <osgAnimation/RigGeometry>
|
||||
#include <osgAnimation/MorphGeometry>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
@ -37,14 +40,48 @@ public:
|
||||
}
|
||||
|
||||
virtual void apply(osg::Drawable& drawable){
|
||||
osg::Geometry* geometry = drawable.asGeometry();
|
||||
if (!geometry || isProcessed(geometry)) {
|
||||
return;
|
||||
}
|
||||
if (osg::Geometry* geometry = drawable.asGeometry()) {
|
||||
// call overloaded geometry processing
|
||||
apply(*geometry);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void apply(osg::Geometry& /*geometry*/) {} // to be implemented by actual visitors
|
||||
virtual void apply(osg::Geometry& geometry) {
|
||||
// skip already processed geometry
|
||||
if (isProcessed(&geometry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry)) {
|
||||
process(*rigGeometry);
|
||||
}
|
||||
else if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(&geometry)) {
|
||||
process(*morphGeometry);
|
||||
}
|
||||
else {
|
||||
process(geometry);
|
||||
}
|
||||
|
||||
// flag geometry as processed
|
||||
setProcessed(&geometry);
|
||||
}
|
||||
|
||||
virtual void process(osg::Geometry& node) = 0;
|
||||
|
||||
virtual void process(osgAnimation::MorphGeometry& node) {
|
||||
// by default process a MorphGeometry like a regular geometry. If a visitor updates
|
||||
// vertices, it *has* to override this apply otherwise there might be a mismatch between the
|
||||
// 'master' morph geometry and morph targets.
|
||||
process(static_cast<osg::Geometry&>(node));
|
||||
}
|
||||
|
||||
virtual void process(osgAnimation::RigGeometry& rigGeometry) {
|
||||
// by default, consider RigGeometry is just considered as a "shell" and visitor
|
||||
// processing should be performed on the rig source
|
||||
if(rigGeometry.getSourceGeometry()) {
|
||||
apply(*rigGeometry.getSourceGeometry());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
bool isProcessed(osg::Geometry* node) {
|
||||
|
@ -21,7 +21,7 @@ public:
|
||||
IndexMeshVisitor(): GeometryUniqueVisitor("IndexMeshVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geom);
|
||||
void process(osg::Geometry& geom);
|
||||
|
||||
protected:
|
||||
typedef std::vector<unsigned int> IndexList;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <limits> // numeric_limits
|
||||
|
||||
#include <osg/Geometry>
|
||||
#include <osgAnimation/MorphGeometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/ValueObject>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
@ -14,7 +15,21 @@
|
||||
|
||||
using namespace glesUtil;
|
||||
|
||||
void IndexMeshVisitor::apply(osg::Geometry& geom) {
|
||||
void remapGeometryVertices(RemapArray ra, osg::Geometry& geom)
|
||||
{
|
||||
osgAnimation::MorphGeometry *morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(&geom);
|
||||
if(morphGeometry) {
|
||||
osgAnimation::MorphGeometry::MorphTargetList targetList = morphGeometry->getMorphTargetList();
|
||||
for (osgAnimation::MorphGeometry::MorphTargetList::iterator ti = targetList.begin(); ti != targetList.end(); ++ti) {
|
||||
osgAnimation::MorphGeometry::MorphTarget *morphTarget = &(*ti);
|
||||
osg::Geometry *target = morphTarget->getGeometry();
|
||||
VertexAttribComparitor arrayComparitor(*target);
|
||||
arrayComparitor.accept(ra);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IndexMeshVisitor::process(osg::Geometry& geom) {
|
||||
// TODO: this is deprecated
|
||||
if (geom.getNormalBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
|
||||
if (geom.getColorBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
|
||||
@ -22,7 +37,7 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) {
|
||||
if (geom.getFogCoordBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET) return;
|
||||
|
||||
// no point optimizing if we don't have enough vertices.
|
||||
if (!geom.getVertexArray() || geom.getVertexArray()->getNumElements() < 3) return;
|
||||
if (!geom.getVertexArray() || geom.getVertexArray()->getNumElements() < 2) return;
|
||||
|
||||
|
||||
osgUtil::SharedArrayOptimizer deduplicator;
|
||||
@ -105,6 +120,9 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) {
|
||||
RemapArray ra(copyMapping);
|
||||
arrayComparitor.accept(ra);
|
||||
|
||||
//Remap morphGeometry target
|
||||
remapGeometryVertices(ra, geom);
|
||||
|
||||
// triangulate faces
|
||||
{
|
||||
TriangleIndexor ti;
|
||||
@ -112,7 +130,10 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) {
|
||||
ti._remapping = finalMapping;
|
||||
|
||||
for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) {
|
||||
(*itr)->accept(ti);
|
||||
osg::PrimitiveSet* primitive = (*itr).get();
|
||||
if(primitive && primitive->getMode() >= osg::PrimitiveSet::TRIANGLES) {
|
||||
primitive->accept(ti);
|
||||
}
|
||||
}
|
||||
|
||||
addDrawElements(ti._indices, osg::PrimitiveSet::TRIANGLES, new_primitives);
|
||||
@ -127,12 +148,15 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) {
|
||||
wi._remapping = finalMapping;
|
||||
|
||||
for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) {
|
||||
osg::PrimitiveSet* primitive = (*itr).get();
|
||||
if(primitive && primitive->getMode() >= osg::PrimitiveSet::LINES && primitive->getMode() <= osg::PrimitiveSet::LINE_LOOP) {
|
||||
bool isWireframe = false;
|
||||
if((*itr)->getUserValue("wireframe", isWireframe) && isWireframe) {
|
||||
(*itr)->accept(wi);
|
||||
if(primitive->getUserValue("wireframe", isWireframe) && isWireframe) {
|
||||
primitive->accept(wi);
|
||||
}
|
||||
else {
|
||||
(*itr)->accept(li);
|
||||
primitive->accept(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
addDrawElements(li._indices, osg::PrimitiveSet::LINES, new_primitives);
|
||||
@ -143,9 +167,10 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) {
|
||||
{
|
||||
IndexList points;
|
||||
for(itr = primitives.begin() ; itr != primitives.end() ; ++ itr) {
|
||||
if((*itr) && (*itr)->getMode() == osg::PrimitiveSet::POINTS) {
|
||||
for(unsigned int k = 0 ; k < (*itr)->getNumIndices() ; ++ k) {
|
||||
points.push_back(finalMapping[(*itr)->index(k)]);
|
||||
osg::PrimitiveSet* primitive = (*itr).get();
|
||||
if(primitive && primitive->getMode() == osg::PrimitiveSet::POINTS) {
|
||||
for(unsigned int k = 0 ; k < primitive->getNumIndices() ; ++ k) {
|
||||
points.push_back(finalMapping[primitive->index(k)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,7 +179,6 @@ void IndexMeshVisitor::apply(osg::Geometry& geom) {
|
||||
|
||||
geom.setPrimitiveSetList(new_primitives);
|
||||
deduplicator.deduplicateUVs(geom);
|
||||
setProcessed(&geom);
|
||||
}
|
||||
|
||||
|
||||
|
31
src/osgPlugins/gles/LimitMorphTargetCount
Normal file
31
src/osgPlugins/gles/LimitMorphTargetCount
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef LIMIT_MORPH_TARGET_COUNT_VISITOR
|
||||
#define LIMIT_MORPH_TARGET_COUNT_VISITOR
|
||||
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
|
||||
|
||||
class LimitMorphTargetCount : public GeometryUniqueVisitor
|
||||
{
|
||||
public:
|
||||
LimitMorphTargetCount(unsigned int maxMorphTarget=0) : _maxMorphTarget(maxMorphTarget) {}
|
||||
|
||||
void process(osgAnimation::MorphGeometry& node)
|
||||
{
|
||||
if(_maxMorphTarget) {
|
||||
osgAnimation::MorphGeometry::MorphTargetList& morphTargetList = node.getMorphTargetList();
|
||||
while(morphTargetList.size() > _maxMorphTarget) {
|
||||
morphTargetList.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process(osg::Geometry& ) {}
|
||||
|
||||
protected:
|
||||
unsigned int _maxMorphTarget;
|
||||
};
|
||||
|
||||
|
||||
#endif // LIMIT_MORPH_TARGET_COUNT_VISITOR
|
29
src/osgPlugins/gles/Line
Normal file
29
src/osgPlugins/gles/Line
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef GLES_LINE
|
||||
#define GLES_LINE
|
||||
|
||||
|
||||
class Line {
|
||||
public:
|
||||
Line(unsigned int a, unsigned int b) {
|
||||
_a = std::min<unsigned int>(a, b);
|
||||
_b = std::max<unsigned int>(a, b);
|
||||
}
|
||||
|
||||
bool operator<(const Line& e) {
|
||||
return _a < e._a || (_a == e._a && _b < e._b);
|
||||
}
|
||||
|
||||
unsigned int _a, _b;
|
||||
};
|
||||
|
||||
|
||||
struct LineCompare {
|
||||
bool operator()(const Line& lhs, const Line& rhs) const {
|
||||
return lhs._a < rhs._a || (lhs._a == rhs._a && lhs._b < rhs._b);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef std::set<Line, LineCompare> LineSet;
|
||||
|
||||
#endif
|
@ -16,29 +16,7 @@
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/Array>
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
|
||||
class Line {
|
||||
public:
|
||||
Line(unsigned int a, unsigned int b) {
|
||||
_a = std::min<unsigned int>(a, b);
|
||||
_b = std::max<unsigned int>(a, b);
|
||||
}
|
||||
|
||||
bool operator<(const Line& e) {
|
||||
return _a < e._a || (_a == e._a && _b < e._b);
|
||||
}
|
||||
|
||||
unsigned int _a, _b;
|
||||
};
|
||||
|
||||
struct LineCompare {
|
||||
bool operator()(const Line& lhs, const Line& rhs) const {
|
||||
return lhs._a < rhs._a || (lhs._a == rhs._a && lhs._b < rhs._b);
|
||||
}
|
||||
};
|
||||
#include "Line"
|
||||
|
||||
|
||||
template<class T>
|
||||
@ -172,7 +150,7 @@ public:
|
||||
|
||||
GLenum _modeCache;
|
||||
std::vector<GLuint> _indexCache;
|
||||
std::set<Line, LineCompare> _lineCache;
|
||||
LineSet _lineCache;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
229
src/osgPlugins/gles/MostInfluencedGeometryByBone
Normal file
229
src/osgPlugins/gles/MostInfluencedGeometryByBone
Normal file
@ -0,0 +1,229 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MOST_INFLUENCED_GEOMETRY_BY_BONE_H
|
||||
#define MOST_INFLUENCED_GEOMETRY_BY_BONE_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Array>
|
||||
|
||||
#include <osgAnimation/RigGeometry>
|
||||
#include <osgAnimation/Bone>
|
||||
|
||||
#include "StatLogger"
|
||||
|
||||
|
||||
class InfluenceAttribute;
|
||||
|
||||
//{
|
||||
// "Bone001": {
|
||||
// Geom1: {
|
||||
// numVertexInfluenced: (int),
|
||||
// gloabalWeight: (float)
|
||||
// },
|
||||
// Geom2: {
|
||||
// numVertexInfluenced: (int),
|
||||
// gloabalWeight: (float)
|
||||
// },
|
||||
// ...
|
||||
// },
|
||||
// "Bone002": {
|
||||
// Geom1: {
|
||||
// numVertexInfluenced: (int),
|
||||
// gloabalWeight: (float)
|
||||
// },
|
||||
// Geom4: {
|
||||
// numVertexInfluenced: (int),
|
||||
// gloabalWeight: (float)
|
||||
// },
|
||||
// ...
|
||||
// },
|
||||
// ...
|
||||
//}
|
||||
//
|
||||
//Here we store influences by bone, we will sort it and take the biggest one
|
||||
typedef std::map< osgAnimation::Bone*, std::map< osgAnimation::RigGeometry*, InfluenceAttribute > > RigGeometryInfluenceByBoneMap;
|
||||
|
||||
typedef std::map< osgAnimation::RigGeometry*, InfluenceAttribute > BoneInfluenceMap;
|
||||
typedef std::pair< osgAnimation::RigGeometry*, InfluenceAttribute > BoneInfluence;
|
||||
typedef std::vector< BoneInfluence > BoneInfluences;
|
||||
|
||||
|
||||
typedef std::set< osgAnimation::RigGeometry* > RigGeometrySet;
|
||||
typedef std::set< osgAnimation::Bone* > BoneSet;
|
||||
|
||||
//Here we simply collect all bones and all rigGeometries
|
||||
class CollectBonesAndRigGeometriesVisitor: public osg::NodeVisitor {
|
||||
|
||||
public:
|
||||
CollectBonesAndRigGeometriesVisitor():
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry &geometry) {
|
||||
osgAnimation::RigGeometry *rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry);
|
||||
if(rigGeometry) {
|
||||
_rigGeometrySet.insert(rigGeometry);
|
||||
}
|
||||
traverse(geometry);
|
||||
}
|
||||
|
||||
void apply(osg::MatrixTransform &node) {
|
||||
osgAnimation::Bone *bone = dynamic_cast<osgAnimation::Bone*>(&node);
|
||||
if(bone) {
|
||||
_boneSet.insert(bone);
|
||||
}
|
||||
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
RigGeometrySet& getRigGeometrySet() {
|
||||
return _rigGeometrySet;
|
||||
}
|
||||
|
||||
BoneSet& getBoneSet() {
|
||||
return _boneSet;
|
||||
}
|
||||
|
||||
protected:
|
||||
RigGeometrySet _rigGeometrySet;
|
||||
BoneSet _boneSet;
|
||||
};
|
||||
|
||||
|
||||
//Store and compute influence attributes i.e number of influenced vertex and accumulate weight
|
||||
class InfluenceAttribute {
|
||||
public:
|
||||
InfluenceAttribute():
|
||||
_accumulatedWeight(0),
|
||||
_weightCount(0)
|
||||
{}
|
||||
|
||||
void addWeight(float weight) {
|
||||
_accumulatedWeight += weight;
|
||||
_weightCount++;
|
||||
}
|
||||
|
||||
unsigned int getNumInfluencedVertex() {
|
||||
return _weightCount;
|
||||
}
|
||||
|
||||
unsigned int getNumInfluencedVertex() const {
|
||||
return _weightCount;
|
||||
}
|
||||
|
||||
float getNormalizedWeight() const {
|
||||
if(_weightCount == 0) return 0;
|
||||
return _accumulatedWeight / _weightCount;
|
||||
}
|
||||
|
||||
protected:
|
||||
float _accumulatedWeight;
|
||||
unsigned int _weightCount;
|
||||
};
|
||||
|
||||
typedef std::pair< std::string, osgAnimation::Bone* > StringBonePair;
|
||||
typedef std::pair< osgAnimation::RigGeometry*, unsigned int > RigGeometryIntPair;
|
||||
|
||||
class BoneNameBoneMap : public std::map<std::string, osgAnimation::Bone*> {
|
||||
|
||||
public:
|
||||
BoneNameBoneMap(const BoneSet& bones) {
|
||||
for(BoneSet::iterator bone = bones.begin(); bone != bones.end(); ++bone) {
|
||||
insert(StringBonePair((*bone)->getName(), *bone));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class RigGeometryIndexMap : public std::map<osgAnimation::RigGeometry*, unsigned int> {
|
||||
|
||||
public:
|
||||
RigGeometryIndexMap(const RigGeometrySet& rigGeometrySet) {
|
||||
unsigned int index = 0;
|
||||
for(RigGeometrySet::iterator rigGeometry = rigGeometrySet.begin(); rigGeometry != rigGeometrySet.end(); ++rigGeometry, ++index) {
|
||||
insert(RigGeometryIntPair(*rigGeometry, index));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ComputeMostInfluencedGeometryByBone {
|
||||
|
||||
public:
|
||||
ComputeMostInfluencedGeometryByBone(RigGeometrySet &rigGeometrySet, BoneSet &boneSet):
|
||||
_rigGeometrySet(rigGeometrySet),
|
||||
_boneSet(boneSet),
|
||||
_logger("ComputeMostInfluencedGeometryByBone::compute(...)")
|
||||
{}
|
||||
|
||||
void compute() {
|
||||
RigGeometryIndexMap rigGeometryIndexMap(_rigGeometrySet);
|
||||
|
||||
RigGeometryInfluenceByBoneMap ribbm;
|
||||
computeInfluences(_boneSet, _rigGeometrySet, ribbm);
|
||||
for(RigGeometryInfluenceByBoneMap::iterator boneInfluencePair = ribbm.begin(); boneInfluencePair != ribbm.end(); ++boneInfluencePair) {
|
||||
osg::ref_ptr<osgAnimation::Bone> bone = boneInfluencePair->first;
|
||||
BoneInfluenceMap boneInfluenceMap = boneInfluencePair->second;
|
||||
BoneInfluences influences(boneInfluenceMap.begin(), boneInfluenceMap.end());
|
||||
|
||||
std::sort(influences.begin(), influences.end(), sort_influences());
|
||||
bone->setUserValue("rigIndex", rigGeometryIndexMap [ influences.front().first ]);
|
||||
}
|
||||
|
||||
RigGeometrySet &rigGeometrySet(_rigGeometrySet);
|
||||
for(RigGeometrySet::iterator rigGeometry = rigGeometrySet.begin(); rigGeometry != rigGeometrySet.end(); ++rigGeometry) {
|
||||
(*rigGeometry)->setUserValue("rigIndex", rigGeometryIndexMap[ *rigGeometry ]);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void computeInfluences(const BoneSet& bones, const RigGeometrySet& rigGeometries, RigGeometryInfluenceByBoneMap& rigGeometryInfluenceByBoneMap) {
|
||||
BoneNameBoneMap boneMap(bones);
|
||||
|
||||
for(RigGeometrySet::const_iterator rigGeometry = rigGeometries.begin(); rigGeometry != rigGeometries.end(); ++rigGeometry) {
|
||||
osg::ref_ptr<osgAnimation::VertexInfluenceMap> vertexInfluenceMap = (*rigGeometry)->getInfluenceMap();
|
||||
|
||||
for(osgAnimation::VertexInfluenceMap::iterator vertexInfluencePair = vertexInfluenceMap->begin(); vertexInfluencePair != vertexInfluenceMap->end(); ++vertexInfluencePair) {
|
||||
BoneNameBoneMap::iterator bone_it = boneMap.find(vertexInfluencePair->first);
|
||||
if(bone_it == boneMap.end()) continue;
|
||||
osg::ref_ptr<osgAnimation::Bone> bone = bone_it->second;
|
||||
const osgAnimation::VertexInfluence& vertexInfluence = (*vertexInfluencePair).second;
|
||||
|
||||
for(osgAnimation::VertexInfluence::const_iterator vertexIndexWeight = vertexInfluence.begin(); vertexIndexWeight != vertexInfluence.end(); ++vertexIndexWeight) {
|
||||
rigGeometryInfluenceByBoneMap[bone.get()][*rigGeometry].addWeight((*vertexIndexWeight).second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct sort_influences {
|
||||
//We sort influences by number of influenced vertex first and then by normalized weight (number_of_vertex_influence / accumulated_weight)
|
||||
//i.e we choose to keep geometries with many vertex insted of geometries with high normalized weight, it makes more sense for geometry
|
||||
//selection via bone influence box @see AABBonBoneVisitor class
|
||||
bool operator()(const BoneInfluence &a, const BoneInfluence &b) {
|
||||
return (a.second.getNumInfluencedVertex() > b.second.getNumInfluencedVertex()) ||
|
||||
(a.second.getNumInfluencedVertex() == b.second.getNumInfluencedVertex() && \
|
||||
a.second.getNormalizedWeight() > b.second.getNormalizedWeight());
|
||||
}
|
||||
};
|
||||
|
||||
RigGeometrySet &_rigGeometrySet;
|
||||
BoneSet &_boneSet;
|
||||
StatLogger _logger;
|
||||
};
|
||||
|
||||
|
||||
#endif // MOST_INFLUENCED_GEOMETRY_BY_BONE_H
|
@ -16,49 +16,66 @@
|
||||
#include <osg/Node>
|
||||
#include <algorithm> //std::max
|
||||
|
||||
#include "AnimationVisitor"
|
||||
#include "AnimationCleanerVisitor"
|
||||
#include "RigAnimationVisitor"
|
||||
#include "AABBonBoneVisitor"
|
||||
#include "DisableAnimationVisitor"
|
||||
#include "BindPerVertexVisitor"
|
||||
#include "DetachPrimitiveVisitor"
|
||||
#include "DrawArrayVisitor"
|
||||
#include "GeometrySplitterVisitor"
|
||||
#include "IndexMeshVisitor"
|
||||
#include "PreTransformVisitor"
|
||||
#include "SmoothNormalVisitor"
|
||||
#include "TangentSpaceVisitor"
|
||||
#include "TriangleStripVisitor"
|
||||
#include "UnIndexMeshVisitor"
|
||||
#include "WireframeVisitor"
|
||||
#include "AABBonBoneVisitor"
|
||||
#include "AnimationCleanerVisitor"
|
||||
#include "MostInfluencedGeometryByBone"
|
||||
#include "LimitMorphTargetCount"
|
||||
#include "RigAttributesVisitor"
|
||||
#include "GeometryInspector"
|
||||
|
||||
|
||||
class OpenGLESGeometryOptimizer
|
||||
{
|
||||
public:
|
||||
OpenGLESGeometryOptimizer() :
|
||||
_mode("all"),
|
||||
_useDrawArray(false),
|
||||
_disableTriStrip(false),
|
||||
_disableMergeTriStrip(false),
|
||||
_disablePreTransform(false),
|
||||
_disablePostTransform(false),
|
||||
_disableAnimation(false),
|
||||
_enableAABBonBone(false),
|
||||
_triStripCacheSize(16),
|
||||
_triStripMinSize(2),
|
||||
_generateTangentSpace(false),
|
||||
_tangentUnit(0),
|
||||
_maxIndexValue(65535),
|
||||
_wireframe("")
|
||||
_wireframe(""),
|
||||
_maxMorphTarget(0),
|
||||
_exportNonGeometryDrawables(false)
|
||||
{}
|
||||
|
||||
// run the optimizer
|
||||
osg::Node* optimize(osg::Node& node);
|
||||
|
||||
// handle options
|
||||
void setMode(const std::string& mode) { _mode = mode; }
|
||||
void setUseDrawArray(bool s) { _useDrawArray = s; }
|
||||
|
||||
void setDisableTriStrip(bool s) { _disableTriStrip = s; }
|
||||
void setDisableMergeTriStrip(bool s) { _disableMergeTriStrip = s; }
|
||||
void setDisablePreTransform(bool s) { _disablePreTransform = s; }
|
||||
void setDisablePostTransform(bool s) { _disablePostTransform = s; }
|
||||
void setDisableAnimation(bool s) { _disableAnimation = s; }
|
||||
void setDisableAnimationCleaning(bool s) { _disableAnimationCleaning = s; }
|
||||
void setEnableAABBonBone(bool s) { _enableAABBonBone = s; }
|
||||
void setTripStripCacheSize(unsigned int size) { _triStripCacheSize = size; }
|
||||
void setTripStripMinSize(unsigned int size) { _triStripMinSize = std::max<unsigned int>(size, 2); }
|
||||
|
||||
void setExportNonGeometryDrawables(bool value) { _exportNonGeometryDrawables = value; }
|
||||
void setTexCoordChannelForTangentSpace(int uv) {
|
||||
_tangentUnit = uv;
|
||||
_generateTangentSpace = true;
|
||||
@ -72,13 +89,66 @@ public:
|
||||
setDisableTriStrip(true);
|
||||
}
|
||||
}
|
||||
void setMaxMorphTarget(unsigned int maxMorphTarget) {
|
||||
_maxMorphTarget = maxMorphTarget;
|
||||
}
|
||||
|
||||
protected:
|
||||
void makeAnimation(osg::Node* node) {
|
||||
AnimationVisitor anim;
|
||||
makeRigAnimation(node);
|
||||
if(_disableAnimation) {
|
||||
makeDisableAnimation(node);
|
||||
}
|
||||
else {
|
||||
if(!_disableAnimationCleaning) {
|
||||
makeCleanAnimation(node);
|
||||
}
|
||||
makeLimitMorphTargetCount(node);
|
||||
makeAABBonBone(node, _enableAABBonBone);
|
||||
makeMostInfluencedGeometryByBone(node);
|
||||
}
|
||||
}
|
||||
|
||||
void makeDisableAnimation(osg::Node* node) {
|
||||
DisableAnimationVisitor disabler;
|
||||
node->accept(disabler);
|
||||
}
|
||||
|
||||
void makeCleanAnimation(osg::Node* node) {
|
||||
AnimationCleanerVisitor cleaner;
|
||||
node->accept(cleaner);
|
||||
cleaner.clean();
|
||||
}
|
||||
|
||||
void makeRigAnimation(osg::Node* node) {
|
||||
RigAnimationVisitor anim;
|
||||
node->accept(anim);
|
||||
}
|
||||
|
||||
void makeAABBonBone(osg::Node* node, bool enableAABBonBone) {
|
||||
FindSkeletons fs;
|
||||
node->accept(fs);
|
||||
|
||||
for(unsigned int i = 0; i < fs._skls.size(); i++) {
|
||||
osgAnimation::Skeleton * skl = fs._skls[i];
|
||||
ComputeAABBOnBoneVisitor cabv(enableAABBonBone);
|
||||
skl->accept(cabv);
|
||||
cabv.computeBoundingBoxOnBones();
|
||||
}
|
||||
}
|
||||
|
||||
void makeMostInfluencedGeometryByBone(osg::Node *node) {
|
||||
CollectBonesAndRigGeometriesVisitor collector;
|
||||
node->accept(collector);
|
||||
ComputeMostInfluencedGeometryByBone computor(collector.getRigGeometrySet(), collector.getBoneSet());
|
||||
computor.compute();
|
||||
}
|
||||
|
||||
void makeLimitMorphTargetCount(osg::Node* node) {
|
||||
LimitMorphTargetCount limit(_maxMorphTarget);
|
||||
node->accept(limit);
|
||||
}
|
||||
|
||||
void makeWireframe(osg::Node* node) {
|
||||
WireframeVisitor wireframe(_wireframe == std::string("inline"));
|
||||
node->accept(wireframe);
|
||||
@ -94,13 +164,18 @@ protected:
|
||||
node->accept(indexer);
|
||||
}
|
||||
|
||||
void makeSmoothNormal(osg::Node* node) {
|
||||
SmoothNormalVisitor smoother(osg::PI / 4.f, true);
|
||||
node->accept(smoother);
|
||||
}
|
||||
|
||||
void makeTangentSpace(osg::Node* node) {
|
||||
TangentSpaceVisitor tangent(_tangentUnit);
|
||||
node->accept(tangent);
|
||||
}
|
||||
|
||||
void makeSplit(osg::Node* node) {
|
||||
GeometrySplitterVisitor splitter(_maxIndexValue, _disablePostTransform);
|
||||
GeometrySplitterVisitor splitter(_maxIndexValue, _exportNonGeometryDrawables );
|
||||
node->accept(splitter);
|
||||
}
|
||||
|
||||
@ -124,12 +199,25 @@ protected:
|
||||
node->accept(detacher);
|
||||
}
|
||||
|
||||
bool _useDrawArray;
|
||||
void makeBonesAndWeightOnRigGeometry(osg::Node* node) {
|
||||
RigAttributesVisitor rigger;
|
||||
node->accept(rigger);
|
||||
}
|
||||
|
||||
void makeInspectGeometry(osg::Node* node) {
|
||||
GeometryInspector inspector;
|
||||
node->accept(inspector);
|
||||
}
|
||||
|
||||
|
||||
std::string _mode;
|
||||
bool _useDrawArray;
|
||||
bool _disableTriStrip;
|
||||
bool _disableMergeTriStrip;
|
||||
bool _disablePreTransform;
|
||||
bool _disablePostTransform;
|
||||
bool _disableAnimation;
|
||||
bool _disableAnimationCleaning;
|
||||
bool _enableAABBonBone;
|
||||
unsigned int _triStripCacheSize;
|
||||
unsigned int _triStripMinSize;
|
||||
|
||||
@ -138,6 +226,10 @@ protected:
|
||||
|
||||
unsigned int _maxIndexValue;
|
||||
std::string _wireframe;
|
||||
|
||||
unsigned int _maxMorphTarget;
|
||||
|
||||
bool _exportNonGeometryDrawables;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -9,9 +9,12 @@ const unsigned glesUtil::Remapper::invalidIndex = std::numeric_limits<unsigned>:
|
||||
osg::Node* OpenGLESGeometryOptimizer::optimize(osg::Node& node) {
|
||||
osg::ref_ptr<osg::Node> model = osg::clone(&node);
|
||||
|
||||
// animation: create regular Geometry if RigGeometry
|
||||
if(_mode == "all" || _mode == "animation") {
|
||||
// animation: process bones/weights or remove all animation data if disabled
|
||||
makeAnimation(model.get());
|
||||
}
|
||||
|
||||
if(_mode == "all" || _mode == "geometry") {
|
||||
// wireframe
|
||||
if (!_wireframe.empty()) {
|
||||
makeWireframe(model.get());
|
||||
@ -23,6 +26,9 @@ osg::Node* OpenGLESGeometryOptimizer::optimize(osg::Node& node) {
|
||||
// index (merge exact duplicates + uses simple triangles & lines i.e. no strip/fan/loop)
|
||||
makeIndexMesh(model.get());
|
||||
|
||||
// smooth vertex normals (if geometry has no normal compute smooth normals)
|
||||
makeSmoothNormal(model.get());
|
||||
|
||||
// tangent space
|
||||
if (_generateTangentSpace) {
|
||||
makeTangentSpace(model.get());
|
||||
@ -47,8 +53,12 @@ osg::Node* OpenGLESGeometryOptimizer::optimize(osg::Node& node) {
|
||||
makePreTransform(model.get());
|
||||
}
|
||||
|
||||
// unbind bones/weights from source and bind on RigGeometry
|
||||
makeBonesAndWeightOnRigGeometry(model.get());
|
||||
|
||||
// detach wireframe
|
||||
makeDetach(model.get());
|
||||
}
|
||||
|
||||
return model.release();
|
||||
}
|
||||
|
@ -23,11 +23,9 @@ public:
|
||||
PreTransformVisitor(): GeometryUniqueVisitor("PreTransformVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
void process(osg::Geometry& geometry) {
|
||||
glesUtil::VertexAccessOrderVisitor optimizer;
|
||||
optimizer.optimizeOrder(geometry);
|
||||
|
||||
setProcessed(&geometry);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
#include <vector>
|
||||
#include <osg/TriangleIndexFunctor>
|
||||
#include <osg/TriangleLinePointIndexFunctor>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
|
||||
#include "TriangleLinePointIndexFunctor"
|
||||
#include "EdgeIndexFunctor"
|
||||
#include "LineIndexFunctor"
|
||||
#include "PointIndexFunctor"
|
||||
|
@ -36,32 +36,42 @@ class ReaderWriterGLES : public osgDB::ReaderWriter
|
||||
public:
|
||||
|
||||
struct OptionsStruct {
|
||||
std::string glesMode;
|
||||
std::string enableWireframe;
|
||||
bool generateTangentSpace;
|
||||
int tangentSpaceTextureUnit;
|
||||
bool disableTriStrip;
|
||||
bool disableMergeTriStrip;
|
||||
bool disablePreTransform;
|
||||
bool disablePostTransform;
|
||||
bool disableAnimation;
|
||||
bool disableAnimationCleaning;
|
||||
bool enableAABBonBone;
|
||||
unsigned int triStripCacheSize;
|
||||
unsigned int triStripMinSize;
|
||||
bool useDrawArray;
|
||||
bool disableIndex;
|
||||
unsigned int maxIndexValue;
|
||||
unsigned int maxMorphTarget;
|
||||
bool exportNonGeometryDrawables;
|
||||
|
||||
OptionsStruct() {
|
||||
glesMode = "all";
|
||||
enableWireframe = "";
|
||||
generateTangentSpace = false;
|
||||
tangentSpaceTextureUnit = 0;
|
||||
disableTriStrip = false;
|
||||
disableMergeTriStrip = false;
|
||||
disablePreTransform = false;
|
||||
disablePostTransform = false;
|
||||
disableAnimation = false;
|
||||
disableAnimationCleaning = false;
|
||||
enableAABBonBone = false;
|
||||
triStripCacheSize = 16;
|
||||
triStripMinSize = 2;
|
||||
useDrawArray = false;
|
||||
disableIndex = false;
|
||||
maxIndexValue = 0;
|
||||
maxMorphTarget = 0;
|
||||
exportNonGeometryDrawables = false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -70,6 +80,7 @@ public:
|
||||
{
|
||||
supportsExtension("gles","OpenGL ES optimized format");
|
||||
|
||||
supportsOption("glesMode[=all|animation|geometry]","run all optimizations (default) or simply animation/geometry.");
|
||||
supportsOption("enableWireframe[=inline]","create a wireframe geometry for each triangles geometry. The wire geometry will be stored along the solid geometry if 'inline' is specified.");
|
||||
supportsOption("generateTangentSpace","Build tangent space to each geometry");
|
||||
supportsOption("tangentSpaceTextureUnit=<unit>","Specify on which texture unit normal map is");
|
||||
@ -78,17 +89,21 @@ public:
|
||||
supportsOption("disableMergeTriStrip","disable the merge of all tristrip into one");
|
||||
supportsOption("disableTriStrip","disable generation of tristrip");
|
||||
supportsOption("disablePreTransform","disable pre-transform of geometries after split");
|
||||
supportsOption("disablePostTransform","disable post-transform of geometries (called during geometry splitting)");
|
||||
supportsOption("disableAnimation","disable animation support");
|
||||
supportsOption("disableAnimationCleaning","disable animations/channels cleaning");
|
||||
supportsOption("enableAABBonBone","Create AABB on bone for rigGeometry (Adds a Geometry in the graph)");
|
||||
supportsOption("useDrawArray","prefer drawArray instead of drawelement with split of geometry");
|
||||
supportsOption("disableIndex","Do not index the geometry");
|
||||
supportsOption("maxIndexValue=<int>","set the maximum index value (first index is 0)");
|
||||
supportsOption("maxMorphTarget=<int>", "set the maximum morph target in morph geometry (no limit by default)");
|
||||
supportsOption("exportNonGeometryDrawables", "export non geometry drawables, right now only text 2D supported" );
|
||||
}
|
||||
|
||||
virtual const char* className() const { return "GLES Optimizer"; }
|
||||
|
||||
virtual osg::Node* optimizeModel(const Node& node, const OptionsStruct& options) const
|
||||
{
|
||||
osg::Node* model = osg::clone(&node);
|
||||
osg::ref_ptr<osg::Node> model = osg::clone(&node);
|
||||
|
||||
if (options.disableIndex) {
|
||||
UnIndexMeshVisitor unindex;
|
||||
@ -97,24 +112,29 @@ public:
|
||||
else {
|
||||
OpenGLESGeometryOptimizer optimizer;
|
||||
|
||||
optimizer.setMode(options.glesMode);
|
||||
optimizer.setUseDrawArray(options.useDrawArray);
|
||||
optimizer.setTripStripCacheSize(options.triStripCacheSize);
|
||||
optimizer.setTripStripMinSize(options.triStripMinSize);
|
||||
optimizer.setDisableTriStrip(options.disableTriStrip);
|
||||
optimizer.setDisableMergeTriStrip(options.disableMergeTriStrip);
|
||||
optimizer.setDisablePreTransform(options.disablePreTransform);
|
||||
optimizer.setDisablePostTransform(options.disablePostTransform);
|
||||
optimizer.setDisableAnimation(options.disableAnimation);
|
||||
optimizer.setDisableAnimationCleaning(options.disableAnimationCleaning);
|
||||
optimizer.setEnableAABBonBone(options.enableAABBonBone);
|
||||
optimizer.setWireframe(options.enableWireframe);
|
||||
optimizer.setExportNonGeometryDrawables(options.exportNonGeometryDrawables);
|
||||
if (options.generateTangentSpace) {
|
||||
optimizer.setTexCoordChannelForTangentSpace(options.tangentSpaceTextureUnit);
|
||||
}
|
||||
if(options.maxIndexValue) {
|
||||
optimizer.setMaxIndexValue(options.maxIndexValue);
|
||||
}
|
||||
optimizer.setMaxMorphTarget(options.maxMorphTarget);
|
||||
|
||||
model = optimizer.optimize(*model);
|
||||
}
|
||||
return model;
|
||||
return model.release();
|
||||
}
|
||||
|
||||
|
||||
@ -201,6 +221,12 @@ public:
|
||||
pre_equals = opt;
|
||||
}
|
||||
|
||||
if (pre_equals == "glesMode")
|
||||
{
|
||||
if(post_equals == "animation" || post_equals == "geometry") {
|
||||
localOptions.glesMode = post_equals;
|
||||
}
|
||||
}
|
||||
if (pre_equals == "enableWireframe")
|
||||
{
|
||||
if(post_equals == "inline") {
|
||||
@ -222,9 +248,17 @@ public:
|
||||
{
|
||||
localOptions.disablePreTransform = true;
|
||||
}
|
||||
if (pre_equals == "disablePostTransform")
|
||||
if (pre_equals == "disableAnimation")
|
||||
{
|
||||
localOptions.disablePostTransform = true;
|
||||
localOptions.disableAnimation = true;
|
||||
}
|
||||
if (pre_equals == "disableAnimationCleaning")
|
||||
{
|
||||
localOptions.disableAnimationCleaning = true;
|
||||
}
|
||||
if (pre_equals == "enableAABBonBone")
|
||||
{
|
||||
localOptions.enableAABBonBone = true;
|
||||
}
|
||||
if (pre_equals == "disableTriStrip")
|
||||
{
|
||||
@ -238,7 +272,10 @@ public:
|
||||
{
|
||||
localOptions.disableIndex = true;
|
||||
}
|
||||
|
||||
if (pre_equals == "exportNonGeometryDrawables")
|
||||
{
|
||||
localOptions.exportNonGeometryDrawables = true;
|
||||
}
|
||||
if (post_equals.length() > 0) {
|
||||
if (pre_equals == "tangentSpaceTextureUnit") {
|
||||
localOptions.tangentSpaceTextureUnit = atoi(post_equals.c_str());
|
||||
@ -252,6 +289,9 @@ public:
|
||||
if (pre_equals == "maxIndexValue") {
|
||||
localOptions.maxIndexValue = atoi(post_equals.c_str());
|
||||
}
|
||||
if(pre_equals == "maxMorphTarget") {
|
||||
localOptions.maxMorphTarget = atoi(post_equals.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
202
src/osgPlugins/gles/RigAnimationVisitor
Normal file
202
src/osgPlugins/gles/RigAnimationVisitor
Normal file
@ -0,0 +1,202 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Cedric Pinson
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RIG_ANIMATION_VISITOR
|
||||
#define RIG_ANIMATION_VISITOR
|
||||
|
||||
#include <osgUtil/UpdateVisitor>
|
||||
#include <osgAnimation/RigTransformHardware>
|
||||
#include <osgAnimation/RigGeometry>
|
||||
#include <osgAnimation/MorphGeometry>
|
||||
|
||||
#include <osg/ValueObject>
|
||||
#include <osg/Array>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
||||
#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
|
||||
{
|
||||
public:
|
||||
typedef std::map<unsigned int, unsigned short> boneIndices;
|
||||
|
||||
RigAnimationVisitor():
|
||||
_logger("RigAnimationVisitor::apply(..)")
|
||||
{
|
||||
setFrameStamp(new osg::FrameStamp());
|
||||
}
|
||||
|
||||
void apply(osg::Drawable& drawable) {
|
||||
// skip drawables already processed
|
||||
if (isProcessed(drawable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
apply(drawable.asGeometry());
|
||||
|
||||
setProcessed(drawable);
|
||||
}
|
||||
|
||||
void apply(osg::Geometry* geometry) {
|
||||
osgAnimation::RigGeometry* rig = dynamic_cast<osgAnimation::RigGeometry*>(geometry);
|
||||
if(rig) {
|
||||
apply(*rig);
|
||||
}
|
||||
}
|
||||
|
||||
inline void normalizeWeight(osg::Vec4f &v) const {
|
||||
// do not consider positive weights only
|
||||
float sum = std::abs(v[0]) + std::abs(v[1]) + std::abs(v[2]) + std::abs(v[3]);
|
||||
if (sum > 0) v /= sum;
|
||||
}
|
||||
|
||||
void apply(osgAnimation::RigGeometry& rigGeometry) {
|
||||
// find skeleton
|
||||
osgAnimation::UpdateRigGeometry rigUpdater;
|
||||
osg::Geometry* source = rigGeometry.getSourceGeometry();
|
||||
|
||||
if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast<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);
|
||||
}
|
||||
|
||||
boneIndices geometryBoneIndices = remapGeometryBones(*bones);
|
||||
applyBoneIndicesRemap(*bones, geometryBoneIndices);
|
||||
serializeBonesUserValues(*bones, geometryBoneIndices, rth.getBoneNameToPalette());
|
||||
|
||||
bones->setUserValue("bones", true);
|
||||
weights->setUserValue("weights", true);
|
||||
|
||||
// attach bones & weights to source geometry during scene graph processing
|
||||
source->setVertexAttribArray(source->getNumVertexAttribArrays(), bones.get(), osg::Array::BIND_PER_VERTEX);
|
||||
source->setVertexAttribArray(source->getNumVertexAttribArrays(), weights.get(), osg::Array::BIND_PER_VERTEX);
|
||||
|
||||
rigGeometry.setRigTransformImplementation(0); //Remove current implementation to force implementation re-init
|
||||
}
|
||||
|
||||
protected:
|
||||
boneIndices remapGeometryBones(const osg::Vec4usArray& bones) {
|
||||
boneIndices remap;
|
||||
for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) {
|
||||
for(unsigned int j = 0 ; j < 4 ; ++ j) {
|
||||
if(remap.find(bones[i][j]) == remap.end()) {
|
||||
remap[bones[i][j]] = static_cast<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;
|
||||
};
|
||||
|
||||
#endif
|
53
src/osgPlugins/gles/RigAttributesVisitor
Normal file
53
src/osgPlugins/gles/RigAttributesVisitor
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef RIG_ATTRIBUTES_VISITOR
|
||||
#define RIG_ATTRIBUTES_VISITOR
|
||||
|
||||
#include "GeometryUniqueVisitor"
|
||||
|
||||
|
||||
class RigAttributesVisitor : public GeometryUniqueVisitor {
|
||||
public:
|
||||
RigAttributesVisitor():
|
||||
GeometryUniqueVisitor("RigAttributesVisitor")
|
||||
{}
|
||||
|
||||
void process(osgAnimation::RigGeometry& rigGeometry) {
|
||||
osg::Geometry* source = rigGeometry.getSourceGeometry();
|
||||
if(source) {
|
||||
int sourceBones = getPropertyIndex(*source, std::string("bones"));
|
||||
int rigBones = getPropertyIndex(rigGeometry, std::string("bones"));
|
||||
if(sourceBones >= 0) {
|
||||
rigBones = (rigBones >= 0 ? rigBones : rigGeometry.getNumVertexAttribArrays());
|
||||
rigGeometry.setVertexAttribArray(rigBones, source->getVertexAttribArray(sourceBones));
|
||||
source->setVertexAttribArray(sourceBones, 0);
|
||||
}
|
||||
|
||||
int sourceWeights = getPropertyIndex(*source, std::string("weights"));
|
||||
int rigWeights = getPropertyIndex(rigGeometry, std::string("weights"));
|
||||
if(sourceWeights >= 0) {
|
||||
rigWeights = (rigWeights >= 0 ? rigWeights : rigGeometry.getNumVertexAttribArrays());
|
||||
rigGeometry.setVertexAttribArray(rigWeights, source->getVertexAttribArray(sourceWeights));
|
||||
source->setVertexAttribArray(sourceWeights, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process(osg::Geometry& geometry) {
|
||||
return;
|
||||
}
|
||||
|
||||
protected:
|
||||
int getPropertyIndex(const osg::Geometry& geometry, const std::string& property) {
|
||||
for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) {
|
||||
const osg::Array* attribute = geometry.getVertexAttribArray(i);
|
||||
bool isProperty = false;
|
||||
if(attribute && attribute->getUserValue(property, isProperty) && isProperty) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
395
src/osgPlugins/gles/SmoothNormalVisitor
Normal file
395
src/osgPlugins/gles/SmoothNormalVisitor
Normal file
@ -0,0 +1,395 @@
|
||||
#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());
|
||||
for(unsigned int index = 0 ; index < _geometry.getVertexArray()->getNumElements() ; ++ index) {
|
||||
std::vector<IndexVector> oneRing = _graph->vertexOneRing(_graph->unify(index), _creaseAngle);
|
||||
osg::Vec3f smoothedNormal(0.f, 0.f, 0.f);
|
||||
|
||||
// sum normals for each cluster in the one-ring
|
||||
for(std::vector<IndexVector>::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) {
|
||||
smoothedNormal += cumulateTriangleNormals(*cluster);
|
||||
}
|
||||
|
||||
float length = smoothedNormal.normalize();
|
||||
if(length > 0.) {
|
||||
if(force || smoothedNormal * normals->at(index) < 1.e-6) {
|
||||
flipped = true;
|
||||
if(fix) {
|
||||
(*normals)[index] = smoothedNormal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(flipped) {
|
||||
OSG_WARN << std::endl << "Warning: [smoothVertexNormals] [[normals]] Geometry '" << _geometry.getName() << "' ";
|
||||
switch(_mode) {
|
||||
case diagnose:
|
||||
OSG_WARN << "has some flipped normals; please check that the shading is correct" << std::endl;
|
||||
OSG_WARN << "Monitor: normal.invalid" << std::endl;
|
||||
break;
|
||||
case smooth_flipped:
|
||||
OSG_WARN << "has some flipped normals that have been fixed" << std::endl;
|
||||
OSG_WARN << "Monitor: normal.smooth_flipped" << std::endl;
|
||||
break;
|
||||
case smooth_all:
|
||||
OSG_WARN << "normals have all been smoothed" << std::endl;
|
||||
OSG_WARN << "Monitor: normal.smooth_all" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void computeVertexNormals() {
|
||||
osg::Vec3Array* normals = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX,
|
||||
_geometry.getVertexArray()->getNumElements());
|
||||
addArray(normals);
|
||||
|
||||
for(unsigned int i = 0 ; i < normals->getNumElements() ; ++ i) {
|
||||
(*normals)[i].set(0.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
for(VertexIterator uniqueIndex = _graph->begin() ; uniqueIndex != _graph->end() ; ++ uniqueIndex) {
|
||||
unsigned int index = uniqueIndex->_index;
|
||||
std::set<unsigned int> processed;
|
||||
|
||||
std::vector<IndexVector> oneRing = _graph->vertexOneRing(index, _creaseAngle);
|
||||
for(std::vector<IndexVector>::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) {
|
||||
osg::Vec3f clusterNormal = cumulateTriangleNormals(*cluster);
|
||||
clusterNormal.normalize();
|
||||
|
||||
std::set<unsigned int> duplicates;
|
||||
for(IndexVector::const_iterator tri = cluster->begin() ; tri != cluster->end() ; ++ tri) {
|
||||
const Triangle& triangle = _graph->triangle(*tri);
|
||||
|
||||
if(_graph->unify(triangle.v1()) == index) {
|
||||
duplicates.insert(triangle.v1());
|
||||
}
|
||||
else if(_graph->unify(triangle.v2()) == index) {
|
||||
duplicates.insert(triangle.v2());
|
||||
}
|
||||
else if(_graph->unify(triangle.v3()) == index) {
|
||||
duplicates.insert(triangle.v3());
|
||||
}
|
||||
}
|
||||
|
||||
for(std::set<unsigned int>::iterator vertex = duplicates.begin() ; vertex != duplicates.end() ; ++ vertex) {
|
||||
if(processed.find(*vertex) == processed.end()) {
|
||||
// vertex not yet processed
|
||||
(*normals)[*vertex] = clusterNormal;
|
||||
processed.insert(*vertex);
|
||||
}
|
||||
else {
|
||||
// vertex already processed in a previous cluster: need to duplicate
|
||||
unsigned int duplicate = duplicateVertex(*vertex);
|
||||
replaceVertexIndexInTriangles(*cluster, *vertex, duplicate);
|
||||
(*normals)[duplicate] = clusterNormal;
|
||||
|
||||
processed.insert(duplicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_geometry.setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
|
||||
updateGeometryPrimitives();
|
||||
|
||||
OSG_WARN << std::endl <<"Warning: [computeVertexNormals] [[normals]] Geometry '" << _geometry.getName()
|
||||
<< "' normals have been recomputed" << std::endl;
|
||||
OSG_WARN << "Monitor: normal.recompute" << std::endl;
|
||||
}
|
||||
|
||||
osg::Vec3f cumulateTriangleNormals(const IndexVector& triangles) const {
|
||||
osg::Vec3f normal;
|
||||
normal.set(0.f, 0.f, 0.f);
|
||||
for(IndexVector::const_iterator triangle = triangles.begin() ; triangle != triangles.end() ; ++ triangle) {
|
||||
const Triangle& t = _graph->triangle(*triangle);
|
||||
normal += (t._normal * t._area);
|
||||
}
|
||||
return normal;
|
||||
}
|
||||
|
||||
void replaceVertexIndexInTriangles(const IndexVector& triangles, unsigned int oldIndex, unsigned int newIndex) {
|
||||
for(IndexVector::const_iterator tri = triangles.begin() ; tri != triangles.end() ; ++ tri) {
|
||||
Triangle& triangle = _graph->triangle(*tri);
|
||||
if(triangle.v1() == oldIndex) {
|
||||
triangle.v1() = newIndex;
|
||||
}
|
||||
else if(triangle.v2() == oldIndex) {
|
||||
triangle.v2() = newIndex;
|
||||
}
|
||||
else if(triangle.v3() == oldIndex) {
|
||||
triangle.v3() = newIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addArray(osg::Array* array) {
|
||||
if (array && array->getBinding() == osg::Array::BIND_PER_VERTEX) {
|
||||
_vertexArrays.push_back(array);
|
||||
}
|
||||
}
|
||||
|
||||
void updateGeometryPrimitives() {
|
||||
osg::Geometry::PrimitiveSetList primitives;
|
||||
for(unsigned int i = 0 ; i < _geometry.getNumPrimitiveSets() ; ++ i) {
|
||||
osg::PrimitiveSet* primitive = _geometry.getPrimitiveSet(i);
|
||||
if(primitive && primitive->getMode() < osg::PrimitiveSet::TRIANGLES) {
|
||||
primitives.push_back(primitive);
|
||||
}
|
||||
}
|
||||
|
||||
osg::DrawElementsUInt* triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
|
||||
for(unsigned int i = 0 ; i < _graph->getNumTriangles() ; ++ i) {
|
||||
const Triangle& triangle = _graph->triangle(i);
|
||||
triangles->push_back(triangle.v1());
|
||||
triangles->push_back(triangle.v2());
|
||||
triangles->push_back(triangle.v3());
|
||||
}
|
||||
primitives.push_back(triangles);
|
||||
|
||||
_geometry.setPrimitiveSetList(primitives);
|
||||
}
|
||||
|
||||
|
||||
osg::Geometry& _geometry;
|
||||
float _creaseAngle;
|
||||
TriangleMeshGraph* _graph;
|
||||
TriangleVector _triangles;
|
||||
ArrayVector _vertexArrays;
|
||||
int _mode; // smooth or recompute normals
|
||||
};
|
||||
|
||||
|
||||
|
||||
class SmoothNormalVisitor : public GeometryUniqueVisitor {
|
||||
public:
|
||||
SmoothNormalVisitor(float creaseAngle, bool comparePosition=false):
|
||||
GeometryUniqueVisitor("SmoothNormalVisitor"),
|
||||
_creaseAngle(creaseAngle),
|
||||
_comparePosition(comparePosition)
|
||||
{}
|
||||
|
||||
void process(osg::Geometry& geometry) {
|
||||
if(!geometry.getNormalArray()) {
|
||||
TriangleMeshSmoother(geometry, _creaseAngle, _comparePosition, TriangleMeshSmoother::recompute);
|
||||
}
|
||||
else {
|
||||
TriangleMeshSmoother(geometry, _creaseAngle, _comparePosition, TriangleMeshSmoother::diagnose);
|
||||
}
|
||||
}
|
||||
|
||||
void process(osgAnimation::MorphGeometry& morphGeometry) {
|
||||
TriangleMeshSmoother(morphGeometry, 0, true, TriangleMeshSmoother::smooth_all);
|
||||
osgAnimation::MorphGeometry::MorphTargetList targets = morphGeometry.getMorphTargetList();
|
||||
for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) {
|
||||
// check normal orientation using the same primitives as parent geometry
|
||||
osg::Geometry::PrimitiveSetList& primitives = target->getGeometry()->getPrimitiveSetList();
|
||||
target->getGeometry()->setPrimitiveSetList(morphGeometry.getPrimitiveSetList());
|
||||
|
||||
TriangleMeshSmoother(*target->getGeometry(), 0, true, TriangleMeshSmoother::smooth_all);
|
||||
|
||||
target->getGeometry()->setPrimitiveSetList(primitives);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
float _creaseAngle;
|
||||
bool _comparePosition;
|
||||
};
|
275
src/osgPlugins/gles/SubGeometry
Normal file
275
src/osgPlugins/gles/SubGeometry
Normal file
@ -0,0 +1,275 @@
|
||||
#ifndef SUB_GEOMETRY
|
||||
#define SUB_GEOMETRY
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <osg/Object>
|
||||
#include <osg/Array>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <osgAnimation/MorphGeometry>
|
||||
|
||||
|
||||
class SubGeometry {
|
||||
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());
|
||||
const osgAnimation::MorphGeometry::MorphTargetList& morphTargetList = morphSource->getMorphTargetList();
|
||||
for(osgAnimation::MorphGeometry::MorphTargetList::const_iterator targetSource = morphTargetList.begin() ;
|
||||
targetSource != morphTargetList.end() ; ++ targetSource) {
|
||||
osg::Geometry* target = new osg::Geometry;
|
||||
addSourceBuffers(target, *targetSource->getGeometry());
|
||||
morph->addMorphTarget(target, targetSource->getWeight());
|
||||
}
|
||||
}
|
||||
|
||||
// remap primitives indices by decreasing ordering (triangles > lines > wireframe > points)
|
||||
for(unsigned int i = 0 ; i < triangles.size() ; i += 3) {
|
||||
copyTriangle(triangles[i], triangles[i + 1], triangles[i + 2]);
|
||||
}
|
||||
|
||||
for(unsigned int i = 0 ; i < lines.size() ; i += 2) {
|
||||
copyEdge(lines[i], lines[i + 1], false);
|
||||
}
|
||||
|
||||
for(unsigned int i = 0 ; i < wireframe.size() ; i += 2) {
|
||||
copyEdge(wireframe[i], wireframe[i + 1], true);
|
||||
}
|
||||
|
||||
for(unsigned int i = 0 ; i < points.size() ; ++ i) {
|
||||
copyPoint(points[i]);
|
||||
}
|
||||
|
||||
// remap vertex buffers accordingly to primitives
|
||||
for(BufferIterator it = _bufferMap.begin() ; it != _bufferMap.end() ; ++ it) {
|
||||
if(it->first) {
|
||||
copyFrom(*(it->second), *(it->first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osg::Geometry* geometry() const {
|
||||
return _geometry.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
void addSourceBuffers(osg::Geometry* geometry, const osg::Geometry& source) {
|
||||
// create necessary vertex containers
|
||||
const osg::Array* array = 0;
|
||||
|
||||
geometry->setName(source.getName());
|
||||
|
||||
// collect all buffers that are BIND_PER_VERTEX for eventual vertex duplication
|
||||
if( (array = vertexArray(source.getVertexArray())) ) {
|
||||
geometry->setVertexArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
if( (array = vertexArray(source.getNormalArray())) ){
|
||||
geometry->setNormalArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
if( (array = vertexArray(source.getColorArray())) ){
|
||||
geometry->setColorArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
if( (array = vertexArray(source.getSecondaryColorArray())) ){
|
||||
geometry->setSecondaryColorArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
if( (array = vertexArray(source.getFogCoordArray())) ){
|
||||
geometry->setFogCoordArray(makeVertexBuffer(array));
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < source.getNumVertexAttribArrays(); ++ i) {
|
||||
if( (array = vertexArray(source.getVertexAttribArray(i))) ){
|
||||
geometry->setVertexAttribArray(i, makeVertexBuffer(array));
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < source.getNumTexCoordArrays(); ++ i) {
|
||||
if( (array = vertexArray(source.getTexCoordArray(i))) ){
|
||||
geometry->setTexCoordArray(i, makeVertexBuffer(array));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void copyTriangle(unsigned int v1, unsigned int v2, unsigned int v3) {
|
||||
osg::DrawElements* triangles = getOrCreateTriangles();
|
||||
triangles->addElement(mapVertex(v1));
|
||||
triangles->addElement(mapVertex(v2));
|
||||
triangles->addElement(mapVertex(v3));
|
||||
}
|
||||
|
||||
void copyEdge(unsigned int v1, unsigned int v2, bool wireframe) {
|
||||
osg::DrawElements* edges = getOrCreateLines(wireframe);
|
||||
edges->addElement(mapVertex(v1));
|
||||
edges->addElement(mapVertex(v2));
|
||||
}
|
||||
|
||||
void copyPoint(unsigned int v1) {
|
||||
osg::DrawElements* points = getOrCreatePoints();
|
||||
points->addElement(mapVertex(v1));
|
||||
}
|
||||
|
||||
const osg::Array* vertexArray(const osg::Array* array) {
|
||||
// filter invalid vertex buffers
|
||||
if (array && array->getNumElements() && array->getBinding() == osg::Array::BIND_PER_VERTEX) {
|
||||
return array;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline osg::Array* makeVertexBuffer(const osg::Array* array, bool copyUserData=true) {
|
||||
osg::Array* buffer = 0;
|
||||
if(array) {
|
||||
buffer = dynamic_cast<osg::Array*>(array->cloneType());
|
||||
buffer->setBinding(osg::Array::BIND_PER_VERTEX);
|
||||
if(copyUserData && array->getUserDataContainer()) {
|
||||
buffer->setUserDataContainer(osg::clone(array->getUserDataContainer(), osg::CopyOp::DEEP_COPY_ALL));
|
||||
}
|
||||
_bufferMap[buffer] = array;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
template<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];
|
||||
}
|
||||
}
|
||||
|
||||
#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::ref_ptr<osg::Geometry> _geometry;
|
||||
std::map<osg::Array*, const osg::Array*> _bufferMap;
|
||||
std::map<unsigned int, unsigned int> _indexMap;
|
||||
std::map<std::string, osg::DrawElements*> _primitives;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -33,7 +33,42 @@ public:
|
||||
_textureUnit(textureUnit)
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geom) {
|
||||
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;
|
||||
@ -93,9 +128,9 @@ public:
|
||||
// The bitangent vector B is then given by B = (N × T) · Tw
|
||||
}
|
||||
finalTangent->setUserValue("tangent", true);
|
||||
geom.setVertexAttribArray(geom.getNumVertexAttribArrays(), finalTangent, osg::Array::BIND_PER_VERTEX);
|
||||
tangentIndex = (tangentIndex >= 0 ? tangentIndex : geom.getNumVertexAttribArrays()) ;
|
||||
geom.setVertexAttribArray(tangentIndex, finalTangent, osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
setProcessed(&geom);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -1,257 +0,0 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified
|
||||
* freely and without restriction, both in commercial and non commercial
|
||||
* applications, as long as this copyright notice is maintained.
|
||||
*
|
||||
* This application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TRIANGLE_LINE_POINT_INDEX_FUNCTOR
|
||||
#define TRIANGLE_LINE_POINT_INDEX_FUNCTOR
|
||||
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/Array>
|
||||
|
||||
|
||||
template<class T>
|
||||
class TriangleLinePointIndexFunctor : public osg::PrimitiveIndexFunctor, public T
|
||||
{
|
||||
public:
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2*)
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec2d*)
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int ,const osg::Vec3d* )
|
||||
{}
|
||||
|
||||
virtual void setVertexArray(unsigned int,const osg::Vec4d* )
|
||||
{}
|
||||
|
||||
virtual void begin(GLenum mode) {
|
||||
_modeCache = mode;
|
||||
_indexCache.clear();
|
||||
}
|
||||
|
||||
virtual void vertex(unsigned int vert) {
|
||||
_indexCache.push_back(vert);
|
||||
}
|
||||
|
||||
virtual void end() {
|
||||
if (!_indexCache.empty()) {
|
||||
drawElements(_modeCache,_indexCache.size(),&_indexCache.front());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_TRIANGLES):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 2 ; i < count ; i += 3, pos += 3) {
|
||||
this->operator()(pos, pos + 1, pos + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_TRIANGLE_STRIP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) {
|
||||
if ((i%2)) this->operator()(pos, pos + 2, pos + 1);
|
||||
else this->operator()(pos, pos + 1, pos + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUADS):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 3 ; i < count ; i += 4, pos += 4) {
|
||||
this->operator()(pos,pos + 1, pos + 2);
|
||||
this->operator()(pos,pos + 2, pos + 3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUAD_STRIP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 3 ; i < count ; i += 2, pos += 2) {
|
||||
this->operator()(pos, pos + 1,pos + 2);
|
||||
this->operator()(pos + 1,pos + 3,pos + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
|
||||
case(GL_TRIANGLE_FAN):
|
||||
{
|
||||
unsigned int pos = first + 1;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) {
|
||||
this->operator()(first, pos, pos + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINES):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 0 ; i < count ; i += 2, pos += 2) {
|
||||
this->operator()(pos, pos + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINE_STRIP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
|
||||
this->operator()(pos, pos + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINE_LOOP):
|
||||
{
|
||||
unsigned int pos = first;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) {
|
||||
this->operator()(pos, pos + 1);
|
||||
}
|
||||
this->operator()(pos, first);
|
||||
break;
|
||||
}
|
||||
case(GL_POINTS):
|
||||
{
|
||||
unsigned int pos=first;
|
||||
for(GLsizei i = 0 ; i < count ; ++ i) {
|
||||
this->operator()(pos + i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
void drawElements(GLenum mode, GLsizei count, const I* indices)
|
||||
{
|
||||
typedef I Index;
|
||||
typedef const I* IndexPointer;
|
||||
|
||||
if (indices == 0 || count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case(GL_TRIANGLES):
|
||||
{
|
||||
IndexPointer ilast = &indices[count];
|
||||
for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 3) {
|
||||
this->operator()(*iptr, *(iptr + 1), *(iptr + 2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_TRIANGLE_STRIP):
|
||||
{
|
||||
IndexPointer iptr = indices;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
|
||||
if ((i%2)) this->operator()(*(iptr), *(iptr + 2), *(iptr + 1));
|
||||
else this->operator()(*(iptr), *(iptr + 1), *(iptr + 2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUADS):
|
||||
{
|
||||
IndexPointer iptr = indices;
|
||||
for(GLsizei i = 3 ; i < count ; i += 4, iptr += 4) {
|
||||
this->operator()(*(iptr), *(iptr + 1), *(iptr + 2));
|
||||
this->operator()(*(iptr), *(iptr + 2), *(iptr + 3));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_QUAD_STRIP):
|
||||
{
|
||||
IndexPointer iptr = indices;
|
||||
for(GLsizei i = 3 ; i < count ; i += 2, iptr += 2) {
|
||||
this->operator()(*(iptr), *(iptr + 1), *(iptr + 2));
|
||||
this->operator()(*(iptr + 1), *(iptr + 3), *(iptr + 2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
|
||||
case(GL_TRIANGLE_FAN):
|
||||
{
|
||||
IndexPointer iptr = indices;
|
||||
Index first = *iptr;
|
||||
++iptr;
|
||||
for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) {
|
||||
this->operator()(first, *(iptr), *(iptr + 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINES):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 0 ; i < count ; i += 2, iptr += 2) {
|
||||
this->operator()(*iptr, *(iptr + 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINE_STRIP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
|
||||
this->operator()(*iptr, *(iptr + 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(GL_LINE_LOOP):
|
||||
{
|
||||
const I* iptr = indices;
|
||||
I first = *iptr;
|
||||
for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) {
|
||||
this->operator()(*iptr, *(iptr + 1));
|
||||
}
|
||||
this->operator()(*iptr, first);
|
||||
break;
|
||||
}
|
||||
case GL_POINTS:
|
||||
{
|
||||
IndexPointer ilast = &indices[count];
|
||||
for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 1) {
|
||||
this->operator()(*iptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) {
|
||||
drawElements<GLubyte>(mode, count, indices);
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) {
|
||||
drawElements<GLushort>(mode, count, indices);
|
||||
}
|
||||
|
||||
virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) {
|
||||
drawElements<GLuint>(mode, count, indices);
|
||||
}
|
||||
|
||||
|
||||
GLenum _modeCache;
|
||||
std::vector<GLuint> _indexCache;
|
||||
std::vector<unsigned int> _remap;
|
||||
};
|
||||
|
||||
#endif
|
340
src/osgPlugins/gles/TriangleMeshGraph
Normal file
340
src/osgPlugins/gles/TriangleMeshGraph
Normal file
@ -0,0 +1,340 @@
|
||||
#ifndef MESH_GRAPH
|
||||
#define MESH_GRAPH
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#include <osg/Array>
|
||||
#include <osg/TriangleIndexFunctor>
|
||||
#include <osg/Geometry>
|
||||
|
||||
class Vertex;
|
||||
class Triangle;
|
||||
typedef std::vector<unsigned int> IndexVector;
|
||||
typedef std::deque<unsigned int> IndexDeque;
|
||||
typedef std::set<unsigned int> IndexSet;
|
||||
typedef std::set<Vertex>::const_iterator VertexIterator;
|
||||
typedef std::vector<Triangle> TriangleVector;
|
||||
typedef std::vector< osg::ref_ptr<osg::Array> > ArrayVector;
|
||||
|
||||
|
||||
inline float clamp(float value, float minValue, float maxValue) {
|
||||
return std::min(maxValue, std::max(minValue, value));
|
||||
}
|
||||
|
||||
|
||||
class Triangle {
|
||||
public:
|
||||
unsigned int _v[3];
|
||||
osg::Vec3 _normal;
|
||||
float _area;
|
||||
|
||||
Triangle(unsigned int v1, unsigned int v2, unsigned int v3, const osg::Vec3& normal)
|
||||
{
|
||||
_v[0] = v1; _v[1] = v2; _v[2] = v3;
|
||||
_area = normal.length();
|
||||
_normal = normal / _area;
|
||||
}
|
||||
|
||||
Triangle unique(const std::vector<unsigned int>& toUnique) const {
|
||||
return Triangle(toUnique[v1()], toUnique[v2()], toUnique[v3()], _normal);
|
||||
}
|
||||
|
||||
bool operator==(const Triangle& other) const {
|
||||
return v1() == other.v1() &&
|
||||
v2() == other.v2() &&
|
||||
v3() == other.v3();
|
||||
}
|
||||
|
||||
inline unsigned int& v1() {
|
||||
return _v[0];
|
||||
}
|
||||
|
||||
inline unsigned int v1() const {
|
||||
return _v[0];
|
||||
}
|
||||
|
||||
inline unsigned int& v2() {
|
||||
return _v[1];
|
||||
}
|
||||
|
||||
inline unsigned int v2() const {
|
||||
return _v[1];
|
||||
}
|
||||
|
||||
inline unsigned int& v3() {
|
||||
return _v[2];
|
||||
}
|
||||
|
||||
inline unsigned int v3() const {
|
||||
return _v[2];
|
||||
}
|
||||
|
||||
inline unsigned int operator[](unsigned int i) const {
|
||||
return _v[i];
|
||||
}
|
||||
|
||||
bool hasEdge(unsigned int e1, unsigned int e2) const {
|
||||
return hasVertex(e1) && hasVertex(e2);
|
||||
}
|
||||
|
||||
inline bool hasVertex(unsigned int vertex) const {
|
||||
return v1() == vertex || v2() == vertex || v3() == vertex;
|
||||
}
|
||||
|
||||
bool intersect(const Triangle& other) const {
|
||||
return other.hasEdge(v1(), v2()) ||
|
||||
other.hasEdge(v1(), v3()) ||
|
||||
other.hasEdge(v2(), v3());
|
||||
}
|
||||
|
||||
inline float angleCosine(const Triangle& other) const {
|
||||
// Triangle._normal is normalized so no need to divide by lengthes
|
||||
return (_normal * other._normal);
|
||||
}
|
||||
|
||||
inline float angle(const Triangle& other) const {
|
||||
return acos(clamp(angleCosine(other), -1.f, 1.f));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Edge {
|
||||
public:
|
||||
unsigned int _v[2];
|
||||
|
||||
Edge(unsigned int e1, unsigned int e2) {
|
||||
_v[0] = e1;
|
||||
_v[1] = e2;
|
||||
}
|
||||
|
||||
bool operator==(const Edge& other) const {
|
||||
return v1() == other.v1() && v2() == other.v2();
|
||||
}
|
||||
|
||||
unsigned int v1() const
|
||||
{ return _v[0]; }
|
||||
|
||||
unsigned int& v1()
|
||||
{ return _v[0]; }
|
||||
|
||||
unsigned int v2() const
|
||||
{ return _v[1]; }
|
||||
|
||||
unsigned int& v2()
|
||||
{ return _v[1]; }
|
||||
};
|
||||
|
||||
|
||||
class Vertex {
|
||||
public:
|
||||
Vertex(const osg::Vec3 position): _position(position),
|
||||
_index(std::numeric_limits<unsigned int>::max())
|
||||
{}
|
||||
|
||||
bool operator<(const Vertex& other) const {
|
||||
return _position < other._position;
|
||||
}
|
||||
|
||||
const osg::Vec3 _position;
|
||||
mutable unsigned int _index; // not used in operator<
|
||||
};
|
||||
|
||||
|
||||
class TriangleMeshGraph {
|
||||
protected:
|
||||
class TriangleRegistror {
|
||||
public:
|
||||
void operator() (unsigned int p1, unsigned int p2, unsigned int p3) {
|
||||
if (p1 == p2 || p2 == p3 || p1 == p3) {
|
||||
return;
|
||||
}
|
||||
_graph->addTriangle(p1, p2, p3);
|
||||
}
|
||||
|
||||
void setGraph(TriangleMeshGraph* graph) {
|
||||
_graph = graph;
|
||||
}
|
||||
|
||||
protected:
|
||||
TriangleMeshGraph* _graph;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
TriangleMeshGraph(const osg::Geometry& geometry, bool comparePosition=true):
|
||||
_geometry(geometry),
|
||||
_positions(dynamic_cast<const osg::Vec3Array*>(geometry.getVertexArray())),
|
||||
_comparePosition(comparePosition)
|
||||
{
|
||||
unsigned int nbVertex = _positions->getNumElements();
|
||||
_unique.resize(nbVertex, std::numeric_limits<unsigned int>::max());
|
||||
_vertexTriangles.resize(nbVertex, IndexVector());
|
||||
build();
|
||||
}
|
||||
|
||||
VertexIterator begin() const {
|
||||
return _vertices.begin();
|
||||
}
|
||||
|
||||
VertexIterator end() const {
|
||||
return _vertices.end();
|
||||
}
|
||||
|
||||
void setComparePosition(bool use) {
|
||||
_comparePosition = use;
|
||||
}
|
||||
|
||||
unsigned int getNumTriangles() const {
|
||||
return _triangles.size();
|
||||
}
|
||||
|
||||
const Triangle& triangle(unsigned int index) const {
|
||||
return _triangles[index];
|
||||
}
|
||||
|
||||
Triangle& triangle(unsigned int index) {
|
||||
return _triangles[index];
|
||||
}
|
||||
|
||||
void addTriangle(unsigned int v1, unsigned int v2, unsigned int v3) {
|
||||
osg::Vec3f p1 = (*_positions)[v1],
|
||||
p2 = (*_positions)[v2],
|
||||
p3 = (*_positions)[v3];
|
||||
|
||||
osg::Vec3f cross = (p2 - p1) ^ (p3 - p1);
|
||||
if(cross.length()) {
|
||||
registerTriangleForVertex(_triangles.size(), v1, unify(v1));
|
||||
registerTriangleForVertex(_triangles.size(), v2, unify(v2));
|
||||
registerTriangleForVertex(_triangles.size(), v3, unify(v3));
|
||||
_triangles.push_back(Triangle(v1, v2, v3, cross));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int unify(unsigned int i) {
|
||||
if(_unique[i] == std::numeric_limits<unsigned int>::max()) {
|
||||
if(_comparePosition) {
|
||||
std::pair<std::set<Vertex>::iterator, bool> result = _vertices.insert(Vertex((*_positions)[i]));
|
||||
if(result.second) { // new element
|
||||
result.first->_index = i;
|
||||
}
|
||||
_unique[i] = result.first->_index;
|
||||
}
|
||||
else {
|
||||
_unique[i] = i;
|
||||
}
|
||||
}
|
||||
return _unique[i];
|
||||
}
|
||||
|
||||
void add(unsigned int newIndex, unsigned int oldIndex) {
|
||||
if(newIndex >= _unique.size()) {
|
||||
_unique.resize(newIndex + 1);
|
||||
}
|
||||
_unique[newIndex] = _unique[oldIndex];
|
||||
}
|
||||
|
||||
const IndexVector& triangles(unsigned int index) const {
|
||||
return _vertexTriangles[index];
|
||||
}
|
||||
|
||||
std::vector<IndexVector> vertexOneRing(unsigned int index, const float creaseAngle) const {
|
||||
std::vector<IndexVector> oneRing;
|
||||
|
||||
IndexDeque triangles(_vertexTriangles[index].begin(), _vertexTriangles[index].end());
|
||||
|
||||
while(!triangles.empty()) {
|
||||
IndexDeque cluster;
|
||||
cluster.push_front(triangles.front());
|
||||
triangles.pop_front();
|
||||
|
||||
IndexDeque::iterator neighbor;
|
||||
// expand from front
|
||||
while(!triangles.empty()) {
|
||||
neighbor = findNeighbor(triangles, cluster.front(), creaseAngle);
|
||||
if(neighbor == triangles.end()) {
|
||||
break;
|
||||
}
|
||||
cluster.push_front(*neighbor);
|
||||
triangles.erase(neighbor);
|
||||
}
|
||||
|
||||
// expand from back
|
||||
while(!triangles.empty()) {
|
||||
neighbor = findNeighbor(triangles, cluster.back(), creaseAngle);
|
||||
if(neighbor == triangles.end()) {
|
||||
break;
|
||||
}
|
||||
cluster.push_back(*neighbor);
|
||||
triangles.erase(neighbor);
|
||||
}
|
||||
oneRing.push_back(IndexVector(cluster.begin(), cluster.end()));
|
||||
}
|
||||
return oneRing;
|
||||
}
|
||||
|
||||
IndexVector triangleNeighbors(unsigned int index) const {
|
||||
IndexVector neighbors;
|
||||
const Triangle& t = _triangles[index];
|
||||
|
||||
for(unsigned int i = 0 ; i < 3 ; ++ i) {
|
||||
const IndexVector& others = triangles(t[i]);
|
||||
for(IndexVector::const_iterator other = others.begin() ; other != others.end() ; ++ other) {
|
||||
if(*other == index) {
|
||||
continue;
|
||||
}
|
||||
else if (t.intersect(_triangles[*other])){
|
||||
neighbors.push_back(*other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
protected:
|
||||
void build() {
|
||||
osg::TriangleIndexFunctor<TriangleRegistror> functor;
|
||||
functor.setGraph(this);
|
||||
_geometry.accept(functor);
|
||||
}
|
||||
|
||||
inline void registerTriangleForVertex(unsigned int triangle, unsigned int vertex, unsigned int deduplicate) {
|
||||
_vertexTriangles[vertex].push_back(triangle);
|
||||
if(vertex != deduplicate) {
|
||||
_vertexTriangles[deduplicate].push_back(triangle);
|
||||
}
|
||||
}
|
||||
|
||||
IndexDeque::iterator findNeighbor(IndexDeque& candidates, const unsigned int index, const float creaseAngle) const {
|
||||
Triangle triangle = _triangles[index].unique(_unique);
|
||||
|
||||
for(IndexDeque::iterator candidate = candidates.begin() ; candidate != candidates.end() ; ++ candidate) {
|
||||
Triangle other = _triangles[*candidate].unique(_unique);
|
||||
|
||||
if(triangle.intersect(other) && isSmoothEdge(triangle, other, creaseAngle)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return candidates.end();
|
||||
}
|
||||
|
||||
inline bool isSmoothEdge(const Triangle& triangle1, const Triangle& triangle2, const float creaseAngle) const {
|
||||
return (creaseAngle == 0.f ? true : triangle1.angle(triangle2) < creaseAngle);
|
||||
}
|
||||
|
||||
const osg::Geometry& _geometry;
|
||||
const osg::Vec3Array* _positions;
|
||||
bool _comparePosition;
|
||||
std::set<Vertex> _vertices;
|
||||
IndexVector _unique;
|
||||
std::vector<IndexVector> _vertexTriangles;
|
||||
TriangleVector _triangles;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -22,7 +22,7 @@ public:
|
||||
_cacheSize(cacheSize), _minSize(minSize), _merge(merge)
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geometry);
|
||||
void process(osg::Geometry& geometry);
|
||||
|
||||
protected:
|
||||
void mergeTrianglesStrip(osg::Geometry& geometry);
|
||||
|
@ -2,10 +2,11 @@
|
||||
#include "TriangleStripVisitor"
|
||||
|
||||
|
||||
void TriangleStripVisitor::apply(osg::Geometry& geometry) {
|
||||
void TriangleStripVisitor::process(osg::Geometry& geometry) {
|
||||
osgUtil::TriStripVisitor tristrip;
|
||||
tristrip.setCacheSize(_cacheSize);
|
||||
tristrip.setMinStripSize(_minSize);
|
||||
tristrip.setIndexMesh(false);
|
||||
tristrip.stripify(geometry);
|
||||
|
||||
// merge stritrip to one using degenerated triangles as glue
|
||||
|
@ -22,7 +22,7 @@ public:
|
||||
UnIndexMeshVisitor(): GeometryUniqueVisitor("UnIndexMeshVisitor")
|
||||
{}
|
||||
|
||||
void apply(osg::Geometry& geom);
|
||||
void process(osg::Geometry& geom);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -21,7 +21,7 @@ typedef std::vector<unsigned int> IndexList;
|
||||
// this help works only for indexed primitive to unindex it
|
||||
|
||||
|
||||
void UnIndexMeshVisitor::apply(osg::Geometry& geom)
|
||||
void UnIndexMeshVisitor::process(osg::Geometry& geom)
|
||||
{
|
||||
// no point optimizing if we don't have enough vertices.
|
||||
if (!geom.getVertexArray()) return;
|
||||
@ -124,5 +124,4 @@ void UnIndexMeshVisitor::apply(osg::Geometry& geom)
|
||||
|
||||
arrayList.setToGeometry(geom);
|
||||
geom.setPrimitiveSetList(newPrimitives);
|
||||
setProcessed(&geom);
|
||||
}
|
||||
|
@ -13,8 +13,6 @@
|
||||
#ifndef WIREFRAME_VISITOR
|
||||
#define WIREFRAME_VISITOR
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <osg/ValueObject>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
@ -38,21 +36,10 @@ public:
|
||||
|
||||
void apply(osg::Geode& geode) {
|
||||
handleStateSet(geode);
|
||||
for (unsigned int i = 0; i < geode.getNumDrawables(); i++) {
|
||||
apply(*geode.getDrawable(i));
|
||||
}
|
||||
GeometryUniqueVisitor::apply(geode);
|
||||
}
|
||||
|
||||
void apply(osg::Drawable& drawable) {
|
||||
osg::Geometry* geometry = drawable.asGeometry();
|
||||
if (!geometry) {
|
||||
return;
|
||||
}
|
||||
apply(*geometry);
|
||||
}
|
||||
|
||||
void apply(osg::Geometry& geometry) {
|
||||
if(_processed.find(&geometry) == _processed.end()) {
|
||||
void process(osg::Geometry& geometry) {
|
||||
const unsigned int nbSourcePrimitives = geometry.getNumPrimitiveSets();
|
||||
for(unsigned int i = 0 ; i < nbSourcePrimitives ; ++ i) {
|
||||
osg::PrimitiveSet* primitive = geometry.getPrimitiveSet(i);
|
||||
@ -66,9 +53,6 @@ public:
|
||||
geometry.getPrimitiveSetList().push_back(wireframe);
|
||||
}
|
||||
}
|
||||
|
||||
_processed.insert(&geometry);
|
||||
}
|
||||
}
|
||||
|
||||
void handleStateSet(osg::Node& node) {
|
||||
@ -77,7 +61,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::set<osg::Geometry*> _processed;
|
||||
bool _inlined;
|
||||
};
|
||||
|
||||
|
46
src/osgPlugins/gles/debug
Normal file
46
src/osgPlugins/gles/debug
Normal file
@ -0,0 +1,46 @@
|
||||
#include <osgDB/ReaderWriter>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/WriteFile>
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
inline void save_debug(osg::Node& node, const std::string& path) {
|
||||
osg::ref_ptr<osgDB::Registry> registry = osgDB::Registry::instance();
|
||||
std::string ext = osgDB::getLowerCaseFileExtension(path);
|
||||
osgDB::ReaderWriter* writer = registry->getReaderWriterForExtension(ext);
|
||||
if(writer) {
|
||||
writer->writeNode(node, path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
inline void save_debug(osg::Geometry& geometry, const std::string& path) {
|
||||
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
|
||||
geode->addDrawable(&geometry);
|
||||
save_debug(*geode, path);
|
||||
}
|
||||
|
||||
template<typename VV>
|
||||
std::string dump(const VV& v) {
|
||||
std::ostringstream oss;
|
||||
oss << "(" << v[0];
|
||||
for(int i = 1 ; i < VV::num_components ; ++ i) {
|
||||
oss << ", " << v[i];
|
||||
}
|
||||
oss << ")";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
template<typename VV>
|
||||
float length(const VV& v) {
|
||||
float l = 0;
|
||||
for(int i = 0 ; i < VV::num_components ; ++ i) {
|
||||
l += v[i] * v[i];
|
||||
}
|
||||
return std::sqrt(l);
|
||||
}
|
||||
|
@ -1,350 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
|
||||
// Optimization" algorithm as described here:
|
||||
// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
|
||||
//
|
||||
// This code was authored and released into the public domain by
|
||||
// Adrian Stone (stone@gameangst.com).
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
// SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Forsyth
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
// OptimizeFaces
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parameters:
|
||||
// indexList
|
||||
// input index list
|
||||
// indexCount
|
||||
// the number of indices in the list
|
||||
// vertexCount
|
||||
// the largest index value in indexList
|
||||
// newIndexList
|
||||
// a pointer to a preallocated buffer the same size as indexList to
|
||||
// hold the optimized index list
|
||||
// lruCacheSize
|
||||
// the size of the simulated post-transform cache (max:64)
|
||||
//-----------------------------------------------------------------------------
|
||||
void OptimizeFaces(const unsigned int* indexList, unsigned int indexCount, unsigned int vertexCount, unsigned int* newIndexList, unsigned int lruCacheSize);
|
||||
|
||||
namespace
|
||||
{
|
||||
#if 0
|
||||
// code for computing vertex score was taken, as much as possible
|
||||
// directly from the original publication.
|
||||
float ComputeVertexCacheScore(int cachePosition, int vertexCacheSize)
|
||||
{
|
||||
const float FindVertexScore_CacheDecayPower = 1.5f;
|
||||
const float FindVertexScore_LastTriScore = 0.75f;
|
||||
|
||||
float score = 0.0f;
|
||||
if ( cachePosition < 0 )
|
||||
{
|
||||
// Vertex is not in FIFO cache - no score.
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( cachePosition < 3 )
|
||||
{
|
||||
// This vertex was used in the last triangle,
|
||||
// so it has a fixed score, whichever of the three
|
||||
// it's in. Otherwise, you can get very different
|
||||
// answers depending on whether you add
|
||||
// the triangle 1,2,3 or 3,1,2 - which is silly.
|
||||
score = FindVertexScore_LastTriScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert ( cachePosition < vertexCacheSize );
|
||||
// Points for being high in the cache.
|
||||
const float scaler = 1.0f / ( vertexCacheSize - 3 );
|
||||
score = 1.0f - ( cachePosition - 3 ) * scaler;
|
||||
score = powf ( score, FindVertexScore_CacheDecayPower );
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
#endif
|
||||
|
||||
float ComputeVertexValenceScore(unsigned int numActiveFaces)
|
||||
{
|
||||
const float FindVertexScore_ValenceBoostScale = 2.0f;
|
||||
const float FindVertexScore_ValenceBoostPower = 0.5f;
|
||||
|
||||
float score = 0.f;
|
||||
|
||||
// Bonus points for having a low number of tris still to
|
||||
// use the vert, so we get rid of lone verts quickly.
|
||||
float valenceBoost = powf ( static_cast<float>(numActiveFaces),
|
||||
-FindVertexScore_ValenceBoostPower );
|
||||
score += FindVertexScore_ValenceBoostScale * valenceBoost;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
const int kMaxVertexCacheSize = 64;
|
||||
const unsigned int kMaxPrecomputedVertexValenceScores = 64;
|
||||
float s_vertexCacheScores[kMaxVertexCacheSize+1][kMaxVertexCacheSize];
|
||||
float s_vertexValenceScores[kMaxPrecomputedVertexValenceScores];
|
||||
|
||||
#if 0
|
||||
bool ComputeVertexScores()
|
||||
{
|
||||
for (int cacheSize=0; cacheSize<=kMaxVertexCacheSize; ++cacheSize)
|
||||
{
|
||||
for (int cachePos=0; cachePos<cacheSize; ++cachePos)
|
||||
{
|
||||
s_vertexCacheScores[cacheSize][cachePos] = ComputeVertexCacheScore(cachePos, cacheSize);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int valence=0; valence<kMaxPrecomputedVertexValenceScores; ++valence)
|
||||
{
|
||||
s_vertexValenceScores[valence] = ComputeVertexValenceScore(valence);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool s_vertexScoresComputed = ComputeVertexScores();
|
||||
#endif
|
||||
|
||||
// inline float FindVertexCacheScore(unsigned int cachePosition, unsigned int maxSizeVertexCache)
|
||||
// {
|
||||
// return s_vertexCacheScores[maxSizeVertexCache][cachePosition];
|
||||
// }
|
||||
|
||||
// inline float FindVertexValenceScore(unsigned int numActiveTris)
|
||||
// {
|
||||
// return s_vertexValenceScores[numActiveTris];
|
||||
// }
|
||||
|
||||
float FindVertexScore(unsigned int numActiveFaces, unsigned int cachePosition, unsigned int vertexCacheSize)
|
||||
{
|
||||
// assert(s_vertexScoresComputed);
|
||||
|
||||
if ( numActiveFaces == 0 )
|
||||
{
|
||||
// No tri needs this vertex!
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
float score = 0.f;
|
||||
if (cachePosition < vertexCacheSize)
|
||||
{
|
||||
score += s_vertexCacheScores[vertexCacheSize][cachePosition];
|
||||
}
|
||||
|
||||
if (numActiveFaces < kMaxPrecomputedVertexValenceScores)
|
||||
{
|
||||
score += s_vertexValenceScores[numActiveFaces];
|
||||
}
|
||||
else
|
||||
{
|
||||
score += ComputeVertexValenceScore(numActiveFaces);
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
struct OptimizeVertexData
|
||||
{
|
||||
float score;
|
||||
unsigned int activeFaceListStart;
|
||||
unsigned int activeFaceListSize;
|
||||
unsigned int cachePos0;
|
||||
unsigned int cachePos1;
|
||||
OptimizeVertexData() : score(0.f), activeFaceListStart(0), activeFaceListSize(0), cachePos0(0), cachePos1(0) { }
|
||||
};
|
||||
}
|
||||
|
||||
void OptimizeFaces(const unsigned int* indexList, unsigned int indexCount, unsigned int vertexCount, unsigned int* newIndexList, unsigned int lruCacheSize)
|
||||
{
|
||||
std::vector<OptimizeVertexData> vertexDataList;
|
||||
vertexDataList.resize(vertexCount);
|
||||
|
||||
// compute face count per vertex
|
||||
for (unsigned int i=0; i<indexCount; ++i)
|
||||
{
|
||||
unsigned int index = indexList[i];
|
||||
assert(index < vertexCount);
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
vertexData.activeFaceListSize++;
|
||||
}
|
||||
|
||||
std::vector<unsigned int> activeFaceList;
|
||||
|
||||
const unsigned int kEvictedCacheIndex = std::numeric_limits<unsigned int>::max();
|
||||
|
||||
{
|
||||
// allocate face list per vertex
|
||||
unsigned int curActiveFaceListPos = 0;
|
||||
for (unsigned int i=0; i<vertexCount; ++i)
|
||||
{
|
||||
OptimizeVertexData& vertexData = vertexDataList[i];
|
||||
vertexData.cachePos0 = kEvictedCacheIndex;
|
||||
vertexData.cachePos1 = kEvictedCacheIndex;
|
||||
vertexData.activeFaceListStart = curActiveFaceListPos;
|
||||
curActiveFaceListPos += vertexData.activeFaceListSize;
|
||||
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos0, lruCacheSize);
|
||||
vertexData.activeFaceListSize = 0;
|
||||
}
|
||||
activeFaceList.resize(curActiveFaceListPos);
|
||||
}
|
||||
|
||||
// fill out face list per vertex
|
||||
for (unsigned int i=0; i<indexCount; i+=3)
|
||||
{
|
||||
for (unsigned int j=0; j<3; ++j)
|
||||
{
|
||||
unsigned int index = indexList[i+j];
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize] = i;
|
||||
vertexData.activeFaceListSize++;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<unsigned int> processedFaceList;
|
||||
processedFaceList.resize(indexCount);
|
||||
|
||||
unsigned int vertexCacheBuffer[(kMaxVertexCacheSize+3)*2];
|
||||
unsigned int* cache0 = vertexCacheBuffer;
|
||||
unsigned int* cache1 = vertexCacheBuffer+(kMaxVertexCacheSize+3);
|
||||
unsigned int entriesInCache0 = 0;
|
||||
|
||||
unsigned int bestFace = 0;
|
||||
float bestScore = -1.f;
|
||||
|
||||
const float maxValenceScore = FindVertexScore(1, kEvictedCacheIndex, lruCacheSize) * 3.f;
|
||||
|
||||
for (unsigned int i = 0; i < indexCount; i += 3)
|
||||
{
|
||||
if (bestScore < 0.f)
|
||||
{
|
||||
// no verts in the cache are used by any unprocessed faces so
|
||||
// search all unprocessed faces for a new starting point
|
||||
for (unsigned int j = 0; j < indexCount; j += 3)
|
||||
{
|
||||
if (processedFaceList[j] == 0)
|
||||
{
|
||||
unsigned int face = j;
|
||||
float faceScore = 0.f;
|
||||
for (unsigned int k=0; k<3; ++k)
|
||||
{
|
||||
unsigned int index = indexList[face+k];
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
assert(vertexData.activeFaceListSize > 0);
|
||||
assert(vertexData.cachePos0 >= lruCacheSize);
|
||||
faceScore += vertexData.score;
|
||||
}
|
||||
|
||||
if (faceScore > bestScore)
|
||||
{
|
||||
bestScore = faceScore;
|
||||
bestFace = face;
|
||||
|
||||
assert(bestScore <= maxValenceScore);
|
||||
if (bestScore >= maxValenceScore)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(bestScore >= 0.f);
|
||||
}
|
||||
|
||||
processedFaceList[bestFace] = 1;
|
||||
unsigned int entriesInCache1 = 0;
|
||||
|
||||
// add bestFace to LRU cache and to newIndexList
|
||||
for (unsigned int v = 0; v < 3; ++v)
|
||||
{
|
||||
unsigned int index = indexList[bestFace+v];
|
||||
newIndexList[i+v] = index;
|
||||
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
|
||||
if (vertexData.cachePos1 >= entriesInCache1)
|
||||
{
|
||||
vertexData.cachePos1 = entriesInCache1;
|
||||
cache1[entriesInCache1++] = index;
|
||||
|
||||
if (vertexData.activeFaceListSize == 1)
|
||||
{
|
||||
--vertexData.activeFaceListSize;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
assert(vertexData.activeFaceListSize > 0);
|
||||
unsigned int* begin = &activeFaceList[vertexData.activeFaceListStart];
|
||||
unsigned int* end = &activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize];
|
||||
unsigned int* it = std::find(begin, end, bestFace);
|
||||
assert(it != end);
|
||||
std::swap(*it, *(end-1));
|
||||
--vertexData.activeFaceListSize;
|
||||
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
|
||||
|
||||
}
|
||||
|
||||
// move the rest of the old verts in the cache down and compute their new scores
|
||||
for (unsigned int c0 = 0; c0 < entriesInCache0; ++c0)
|
||||
{
|
||||
unsigned int index = cache0[c0];
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
|
||||
if (vertexData.cachePos1 >= entriesInCache1)
|
||||
{
|
||||
vertexData.cachePos1 = entriesInCache1;
|
||||
cache1[entriesInCache1++] = index;
|
||||
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
|
||||
}
|
||||
}
|
||||
|
||||
// find the best scoring triangle in the current cache (including up to 3 that were just evicted)
|
||||
bestScore = -1.f;
|
||||
for (unsigned int c1 = 0; c1 < entriesInCache1; ++c1)
|
||||
{
|
||||
unsigned int index = cache1[c1];
|
||||
OptimizeVertexData& vertexData = vertexDataList[index];
|
||||
vertexData.cachePos0 = vertexData.cachePos1;
|
||||
vertexData.cachePos1 = kEvictedCacheIndex;
|
||||
for (unsigned int j=0; j<vertexData.activeFaceListSize; ++j)
|
||||
{
|
||||
unsigned int face = activeFaceList[vertexData.activeFaceListStart+j];
|
||||
float faceScore = 0.f;
|
||||
for (unsigned int v=0; v<3; v++)
|
||||
{
|
||||
unsigned int faceIndex = indexList[face+v];
|
||||
OptimizeVertexData& faceVertexData = vertexDataList[faceIndex];
|
||||
faceScore += faceVertexData.score;
|
||||
}
|
||||
if (faceScore > bestScore)
|
||||
{
|
||||
bestScore = faceScore;
|
||||
bestFace = face;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(cache0, cache1);
|
||||
entriesInCache0 = std::min(entriesInCache1, lruCacheSize);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Forsyth
|
@ -1,46 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
|
||||
// Optimization" algorithm as described here:
|
||||
// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
|
||||
//
|
||||
// This code was authored and released into the public domain by
|
||||
// Adrian Stone (stone@gameangst.com).
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
// SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef __FORSYTH_TRIANGLE_REORDER__
|
||||
#define __FORSYTH_TRIANGLE_REORDER__
|
||||
|
||||
namespace Forsyth
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
// OptimizeFaces
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parameters:
|
||||
// indexList
|
||||
// input index list
|
||||
// indexCount
|
||||
// the number of indices in the list
|
||||
// vertexCount
|
||||
// the largest index value in indexList
|
||||
// newIndexList
|
||||
// a pointer to a preallocated buffer the same size as indexList to
|
||||
// hold the optimized index list
|
||||
// lruCacheSize
|
||||
// the size of the simulated post-transform cache (max:64)
|
||||
//-----------------------------------------------------------------------------
|
||||
void OptimizeFaces(const unsigned int* indexList,
|
||||
unsigned int indexCount,
|
||||
unsigned int vertexCount,
|
||||
unsigned int* newIndexList,
|
||||
unsigned int lruCacheSize);
|
||||
|
||||
} // namespace Forsyth
|
||||
|
||||
#endif // __FORSYTH_TRIANGLE_REORDER__
|
@ -10,10 +10,12 @@
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
#include <osg/TriangleIndexFunctor>
|
||||
#include <osg/TriangleLinePointIndexFunctor>
|
||||
|
||||
#include <osgAnimation/MorphGeometry>
|
||||
#include <osgAnimation/RigGeometry>
|
||||
|
||||
#include "TriangleLinePointIndexFunctor"
|
||||
#include "StatLogger"
|
||||
#include "forsythtriangleorderoptimizer.h"
|
||||
|
||||
|
||||
namespace glesUtil {
|
||||
@ -21,212 +23,6 @@ namespace glesUtil {
|
||||
using namespace osg;
|
||||
typedef std::vector<unsigned int> IndexList;
|
||||
|
||||
/////////// Post-transform
|
||||
|
||||
// A list of triangles that use a vertex is associated with the Vertex
|
||||
// structure in another array.
|
||||
struct Vertex
|
||||
{
|
||||
int trisUsing;
|
||||
size_t triList; // index to start of triangle storage
|
||||
Vertex()
|
||||
: trisUsing(0), triList(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
typedef vector<Vertex> VertexList;
|
||||
|
||||
struct Triangle
|
||||
{
|
||||
unsigned verts[3];
|
||||
};
|
||||
typedef vector<Triangle> TriangleList;
|
||||
|
||||
struct TriangleCounterOperator
|
||||
{
|
||||
VertexList* vertices;
|
||||
int triangleCount;
|
||||
TriangleCounterOperator() : vertices(0), triangleCount(0) {}
|
||||
|
||||
void doVertex(unsigned p)
|
||||
{
|
||||
if (vertices->size() <= p)
|
||||
vertices->resize(p + 1);
|
||||
(*vertices)[p].trisUsing++;
|
||||
}
|
||||
|
||||
void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
|
||||
{
|
||||
if (p1 == p2 || p2 == p3 || p1 == p3)
|
||||
return;
|
||||
doVertex(p1);
|
||||
doVertex(p2);
|
||||
doVertex(p3);
|
||||
triangleCount++;
|
||||
}
|
||||
};
|
||||
|
||||
struct TriangleCounter : public TriangleIndexFunctor<TriangleCounterOperator>
|
||||
{
|
||||
TriangleCounter(vector<Vertex>* vertices_)
|
||||
{
|
||||
vertices = vertices_;
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize the vertex triangle lists and the triangle data structures
|
||||
struct TriangleAddOperator
|
||||
{
|
||||
VertexList* vertices;
|
||||
TriangleList* triangles;
|
||||
int triIdx;
|
||||
TriangleAddOperator() : vertices(0), triangles(0), triIdx(0) {}
|
||||
|
||||
void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
|
||||
{
|
||||
if (p1 == p2 || p2 == p3 || p1 == p3)
|
||||
return;
|
||||
(*triangles)[triIdx].verts[0] = p1;
|
||||
(*triangles)[triIdx].verts[1] = p2;
|
||||
(*triangles)[triIdx].verts[2] = p3;
|
||||
triIdx++;
|
||||
}
|
||||
};
|
||||
|
||||
struct TriangleAdder : public TriangleIndexFunctor<TriangleAddOperator>
|
||||
{
|
||||
TriangleAdder(VertexList* vertices_, TriangleList* triangles_)
|
||||
{
|
||||
vertices = vertices_;
|
||||
triangles = triangles_;
|
||||
}
|
||||
};
|
||||
|
||||
struct is_not_soup
|
||||
{
|
||||
is_not_soup(const VertexList& vertices) : _vertices(vertices) {}
|
||||
bool operator()(const Triangle &t)
|
||||
{
|
||||
return _vertices[t.verts[0]].trisUsing > 1 ||
|
||||
_vertices[t.verts[1]].trisUsing > 1 ||
|
||||
_vertices[t.verts[2]].trisUsing > 1;
|
||||
}
|
||||
|
||||
VertexList _vertices;
|
||||
};
|
||||
|
||||
|
||||
class VertexCacheVisitor : osgUtil::VertexCacheVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
void optimizeVertices(Geometry& geom)
|
||||
{
|
||||
StatLogger logger("glesUtil::VertexCacheVisitor::optimizeVertices(" + geom.getName() + ")");
|
||||
|
||||
Array* vertArray = geom.getVertexArray();
|
||||
if (!vertArray)
|
||||
return;
|
||||
unsigned vertArraySize = vertArray->getNumElements();
|
||||
// If all the vertices fit in the cache, there's no point in
|
||||
// doing this optimization.
|
||||
if (vertArraySize <= 16)
|
||||
return;
|
||||
|
||||
osg::ref_ptr<osg::Geometry> dummy = new osg::Geometry;
|
||||
osg::Geometry::PrimitiveSetList newPrims;
|
||||
|
||||
for (int ii = geom.getNumPrimitiveSets() - 1 ; ii >= 0 ; -- ii) {
|
||||
osg::PrimitiveSet* primitive = geom.getPrimitiveSet(ii);
|
||||
if(!primitive || !primitive->getNumIndices()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect all 'surface' primitives in the dummy geometry
|
||||
if(primitive->getMode() >= PrimitiveSet::TRIANGLES && primitive->getDrawElements()) {
|
||||
dummy->addPrimitiveSet(primitive);
|
||||
}
|
||||
else {
|
||||
newPrims.push_back(primitive);
|
||||
}
|
||||
}
|
||||
|
||||
if(!dummy->getNumPrimitiveSets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector<unsigned> newVertList;
|
||||
doVertexOptimization(*dummy, newVertList);
|
||||
|
||||
osg::DrawElementsUInt* elements = new DrawElementsUInt(GL_TRIANGLES, newVertList.begin(), newVertList.end());
|
||||
if (geom.getUseVertexBufferObjects()) {
|
||||
elements->setElementBufferObject(new ElementBufferObject);
|
||||
}
|
||||
newPrims.insert(newPrims.begin(), elements);
|
||||
|
||||
geom.setPrimitiveSetList(newPrims);
|
||||
|
||||
geom.dirtyDisplayList();
|
||||
}
|
||||
|
||||
void doVertexOptimization(Geometry& geom, vector<unsigned>& vertDrawList)
|
||||
{
|
||||
Geometry::PrimitiveSetList& primSets = geom.getPrimitiveSetList();
|
||||
// lists for all the vertices and triangles
|
||||
VertexList vertices;
|
||||
TriangleList triangles;
|
||||
TriangleCounter triCounter(&vertices);
|
||||
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
|
||||
end = primSets.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
(*itr)->accept(triCounter);
|
||||
triangles.resize(triCounter.triangleCount);
|
||||
// Get total of triangles used by all the vertices
|
||||
size_t vertTrisSize = 0;
|
||||
for (VertexList::iterator itr = vertices.begin(), end = vertices.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
{
|
||||
itr->triList = vertTrisSize;
|
||||
vertTrisSize += itr->trisUsing;
|
||||
}
|
||||
// Store for lists of triangles (indices) used by the vertices
|
||||
vector<unsigned> vertTriListStore(vertTrisSize);
|
||||
TriangleAdder triAdder(&vertices, &triangles);
|
||||
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
|
||||
end = primSets.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
(*itr)->accept(triAdder);
|
||||
|
||||
// discard triangle soup as it cannot be cache-optimized
|
||||
TriangleList::iterator soupIterator = std::partition(triangles.begin(), triangles.end(),
|
||||
is_not_soup(vertices));
|
||||
TriangleList soup(soupIterator, triangles.end());
|
||||
triangles.erase(soupIterator, triangles.end());
|
||||
OSG_INFO << "Info: glesUtil::VertexCacheVisitor::doVertexOptimization(..) found "
|
||||
<< soup.size() << " soup triangles" << std::endl << std::flush;
|
||||
|
||||
std::vector<unsigned int> indices;
|
||||
for(TriangleList::const_iterator it_tri = triangles.begin() ; it_tri != triangles.end() ; ++ it_tri) {
|
||||
indices.push_back(it_tri->verts[0]);
|
||||
indices.push_back(it_tri->verts[1]);
|
||||
indices.push_back(it_tri->verts[2]);
|
||||
}
|
||||
|
||||
// call bgfx forsyth-algorithm implementation
|
||||
vertDrawList.resize(indices.size());
|
||||
Forsyth::OptimizeFaces(&indices[0], indices.size(), vertices.size(), &vertDrawList[0], 16);
|
||||
for(TriangleList::iterator itr = soup.begin() ; itr != soup.end() ; ++ itr) {
|
||||
vertDrawList.push_back(itr->verts[0]);
|
||||
vertDrawList.push_back(itr->verts[1]);
|
||||
vertDrawList.push_back(itr->verts[2]);
|
||||
}
|
||||
}
|
||||
|
||||
}; // Post-transform
|
||||
|
||||
// A helper class that gathers up all the attribute arrays of an
|
||||
// osg::Geometry object that are BIND_PER_VERTEX and runs an
|
||||
// ArrayVisitor on them.
|
||||
@ -389,11 +185,13 @@ namespace glesUtil {
|
||||
{
|
||||
ref_ptr<T> newarray = new T(_newsize);
|
||||
T* newptr = newarray.get();
|
||||
for (size_t i = 0; i < array.size(); ++i)
|
||||
if (_remapping[i] != invalidIndex)
|
||||
(*newptr)[_remapping[i]] = array[i];
|
||||
array.swap(*newptr);
|
||||
|
||||
for (size_t i = 0; i < _remapping.size(); ++i) {
|
||||
if (_remapping[i] != invalidIndex) {
|
||||
(*newptr)[_remapping[i]] = array[i];
|
||||
}
|
||||
}
|
||||
array.swap(*newptr);
|
||||
}
|
||||
|
||||
virtual void apply(osg::Array&) {}
|
||||
@ -410,8 +208,6 @@ namespace glesUtil {
|
||||
virtual void apply(osg::Vec3Array& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4Array& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec4ubArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2bArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3bArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4bArray& array) { remap(array); }
|
||||
@ -420,11 +216,28 @@ namespace glesUtil {
|
||||
virtual void apply(osg::Vec3sArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4sArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2iArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3iArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4iArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2dArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3dArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4dArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2ubArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3ubArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4ubArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2usArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3usArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4usArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::Vec2uiArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec3uiArray& array) { remap(array); }
|
||||
virtual void apply(osg::Vec4uiArray& array) { remap(array); }
|
||||
|
||||
virtual void apply(osg::MatrixfArray& array) { remap(array); }
|
||||
virtual void apply(osg::MatrixdArray& array) { remap(array); }
|
||||
};
|
||||
|
||||
|
||||
@ -474,16 +287,15 @@ namespace glesUtil {
|
||||
};
|
||||
|
||||
|
||||
template<typename DE>
|
||||
inline void reorderDrawElements(DE& drawElements,
|
||||
inline osg::DrawElementsUInt* reorderDrawElements(PrimitiveSet* primitive,
|
||||
const vector<unsigned>& reorder)
|
||||
{
|
||||
for (typename DE::iterator itr = drawElements.begin(), end = drawElements.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
{
|
||||
*itr = static_cast<typename DE::value_type>(reorder[*itr]);
|
||||
osg::DrawElementsUInt* newElements = new osg::DrawElementsUInt(primitive->getMode());
|
||||
for (unsigned int i = 0 ; i < primitive->getNumIndices() ; ++ i) {
|
||||
newElements->addElement(static_cast<unsigned>(reorder[primitive->index(i)]));
|
||||
}
|
||||
newElements->setUserDataContainer(primitive->getUserDataContainer());
|
||||
return newElements;
|
||||
}
|
||||
|
||||
|
||||
@ -491,10 +303,10 @@ namespace glesUtil {
|
||||
{
|
||||
struct OrderByPrimitiveMode
|
||||
{
|
||||
inline bool operator() (const osg::ref_ptr<osg::PrimitiveSet>& prim1, const osg::ref_ptr<osg::PrimitiveSet>& prim2)
|
||||
inline bool operator() (const osg::ref_ptr<osg::PrimitiveSet> prim1, const osg::ref_ptr<osg::PrimitiveSet> prim2)
|
||||
{
|
||||
if(prim1.get() && prim2.get()) {
|
||||
return prim1->getMode() >= prim2->getMode();
|
||||
return prim1->getMode() > prim2->getMode();
|
||||
}
|
||||
else if(prim1.get()) {
|
||||
return true;
|
||||
@ -504,6 +316,19 @@ namespace glesUtil {
|
||||
} order_by_primitive_mode;
|
||||
|
||||
public:
|
||||
void remapTargetVertices(Remapper remapper, Geometry& geom)
|
||||
{
|
||||
if(osgAnimation::MorphGeometry *morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(&geom)) {
|
||||
osgAnimation::MorphGeometry::MorphTargetList targetList = morphGeometry->getMorphTargetList();
|
||||
for (osgAnimation::MorphGeometry::MorphTargetList::iterator ti = targetList.begin(); ti != targetList.end(); ++ti) {
|
||||
osgAnimation::MorphGeometry::MorphTarget *morphTarget = &(*ti);
|
||||
osg::Geometry *target = morphTarget->getGeometry();
|
||||
GeometryArrayGatherer gatherer(*target);
|
||||
gatherer.accept(remapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void optimizeOrder(Geometry& geom)
|
||||
{
|
||||
StatLogger logger("glesUtil::VertexAccessOrderVisitor::optimizeOrder(" + geom.getName() + ")");
|
||||
@ -541,27 +366,21 @@ namespace glesUtil {
|
||||
|
||||
Remapper remapper(vr.remap);
|
||||
gatherer.accept(remapper);
|
||||
|
||||
//Remap morphGeometry target
|
||||
remapTargetVertices(remapper, geom);
|
||||
|
||||
Geometry::PrimitiveSetList newPrimitives;
|
||||
for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(),
|
||||
end = primSets.end();
|
||||
itr != end;
|
||||
++itr)
|
||||
{
|
||||
PrimitiveSet* ps = itr->get();
|
||||
switch (ps->getType())
|
||||
{
|
||||
case PrimitiveSet::DrawElementsUBytePrimitiveType:
|
||||
reorderDrawElements(*static_cast<DrawElementsUByte*>(ps), vr.remap);
|
||||
break;
|
||||
case PrimitiveSet::DrawElementsUShortPrimitiveType:
|
||||
reorderDrawElements(*static_cast<DrawElementsUShort*>(ps), vr.remap);
|
||||
break;
|
||||
case PrimitiveSet::DrawElementsUIntPrimitiveType:
|
||||
reorderDrawElements(*static_cast<DrawElementsUInt*>(ps), vr.remap);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
newPrimitives.push_back(reorderDrawElements(ps, vr.remap));
|
||||
}
|
||||
geom.setPrimitiveSetList(newPrimitives);
|
||||
|
||||
|
||||
// deduplicate UVs array that were only shared within the geometry
|
||||
deduplicator.deduplicateUVs(geom);
|
||||
|
Loading…
Reference in New Issue
Block a user