Merge pull request #94 from marchelbling/gles_plugin_update

Updates gles plugin.

This PR updates the GLES plugin with latest Sketchfab changes:

    improves geometry splitting
    processes/cleans animation (for subsequent osgjs serialization)
    adds (yet another) normal smoother
This commit is contained in:
OpenSceneGraph git repository 2016-07-01 18:03:29 +01:00 committed by GitHub
commit d0b755e6cc
39 changed files with 3750 additions and 1348 deletions

View 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

View 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

View File

@ -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

View File

@ -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>

View File

@ -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)

View File

@ -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;

View 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

View File

@ -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);
}
};

View File

@ -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;
}

View 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

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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);
}

View 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
View 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

View File

@ -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

View 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

View File

@ -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

View File

@ -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();
}

View File

@ -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);
}
};

View File

@ -3,9 +3,9 @@
#include <vector>
#include <osg/TriangleIndexFunctor>
#include <osg/TriangleLinePointIndexFunctor>
#include <osgUtil/MeshOptimizers>
#include "TriangleLinePointIndexFunctor"
#include "EdgeIndexFunctor"
#include "LineIndexFunctor"
#include "PointIndexFunctor"

View File

@ -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());
}
}
}
}

View 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

View 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

View 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;
};

View 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

View File

@ -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:

View File

@ -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

View 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

View File

@ -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);

View File

@ -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

View File

@ -22,7 +22,7 @@ public:
UnIndexMeshVisitor(): GeometryUniqueVisitor("UnIndexMeshVisitor")
{}
void apply(osg::Geometry& geom);
void process(osg::Geometry& geom);
};
#endif

View File

@ -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);
}

View File

@ -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
View 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);
}

View File

@ -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

View File

@ -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__

View File

@ -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);