1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2  *
3  * This library is open source and may be redistributed and/or modified under
4  * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5  * (at your option) any later version.  The full license is in LICENSE file
6  * included with this distribution, and on the openscenegraph.org website.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * OpenSceneGraph Public License for more details.
12 */
13 
14 /* Modified for OpenMW */
15 
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include "optimizer.hpp"
20 
21 #include <osg/Version>
22 #include <osg/Transform>
23 #include <osg/MatrixTransform>
24 #include <osg/PositionAttitudeTransform>
25 #include <osg/LOD>
26 #include <osg/Billboard>
27 #include <osg/Geometry>
28 #include <osg/Notify>
29 #include <osg/Timer>
30 #include <osg/io_utils>
31 #include <osg/Depth>
32 
33 #include <osgUtil/TransformAttributeFunctor>
34 #include <osgUtil/Statistics>
35 #include <osgUtil/MeshOptimizers>
36 
37 #include <typeinfo>
38 #include <algorithm>
39 #include <numeric>
40 
41 #include <iterator>
42 
43 using namespace osgUtil;
44 
45 namespace SceneUtil
46 {
47 
reset()48 void Optimizer::reset()
49 {
50 }
51 
optimize(osg::Node * node,unsigned int options)52 void Optimizer::optimize(osg::Node* node, unsigned int options)
53 {
54     StatsVisitor stats;
55 
56     if (osg::getNotifyLevel()>=osg::INFO)
57     {
58         node->accept(stats);
59         stats.totalUpStats();
60         OSG_NOTICE<<std::endl<<"Stats before:"<<std::endl;
61         stats.print(osg::notify(osg::NOTICE));
62     }
63 
64     if (options & FLATTEN_STATIC_TRANSFORMS)
65     {
66         OSG_INFO<<"Optimizer::optimize() doing FLATTEN_STATIC_TRANSFORMS"<<std::endl;
67 
68         int i=0;
69         bool result = false;
70         do
71         {
72             OSG_DEBUG << "** RemoveStaticTransformsVisitor *** Pass "<<i<<std::endl;
73             FlattenStaticTransformsVisitor fstv(this);
74             node->accept(fstv);
75             result = fstv.removeTransforms(node);
76             ++i;
77         } while (result);
78 
79         // now combine any adjacent static transforms.
80         CombineStaticTransformsVisitor cstv(this);
81         node->accept(cstv);
82         cstv.removeTransforms(node);
83     }
84 
85     if (options & REMOVE_REDUNDANT_NODES)
86     {
87         OSG_INFO<<"Optimizer::optimize() doing REMOVE_REDUNDANT_NODES"<<std::endl;
88 
89         RemoveEmptyNodesVisitor renv(this);
90         node->accept(renv);
91         renv.removeEmptyNodes();
92 
93         RemoveRedundantNodesVisitor rrnv(this);
94         node->accept(rrnv);
95         rrnv.removeRedundantNodes();
96 
97         MergeGroupsVisitor mgrp(this);
98         node->accept(mgrp);
99     }
100 
101     if (options & MERGE_GEOMETRY)
102     {
103         OSG_INFO<<"Optimizer::optimize() doing MERGE_GEOMETRY"<<std::endl;
104 
105         osg::Timer_t startTick = osg::Timer::instance()->tick();
106 
107         MergeGeometryVisitor mgv(this);
108         mgv.setTargetMaximumNumberOfVertices(1000000);
109         mgv.setMergeAlphaBlending(_mergeAlphaBlending);
110         mgv.setViewPoint(_viewPoint);
111         node->accept(mgv);
112 
113         osg::Timer_t endTick = osg::Timer::instance()->tick();
114 
115         OSG_INFO<<"MERGE_GEOMETRY took "<<osg::Timer::instance()->delta_s(startTick,endTick)<<std::endl;
116     }
117 
118     if (options & VERTEX_POSTTRANSFORM)
119     {
120         OSG_INFO<<"Optimizer::optimize() doing VERTEX_POSTTRANSFORM"<<std::endl;
121         VertexCacheVisitor vcv;
122         node->accept(vcv);
123         vcv.optimizeVertices();
124     }
125 
126     if (options & VERTEX_PRETRANSFORM)
127     {
128         OSG_INFO<<"Optimizer::optimize() doing VERTEX_PRETRANSFORM"<<std::endl;
129         VertexAccessOrderVisitor vaov;
130         node->accept(vaov);
131         vaov.optimizeOrder();
132     }
133 
134     if (osg::getNotifyLevel()>=osg::INFO)
135     {
136         stats.reset();
137         node->accept(stats);
138         stats.totalUpStats();
139         OSG_NOTICE<<std::endl<<"Stats after:"<<std::endl;
140         stats.print(osg::notify(osg::NOTICE));
141     }
142 }
143 
144 
145 
146 
147 ////////////////////////////////////////////////////////////////////////////
148 // Flatten static transforms
149 ////////////////////////////////////////////////////////////////////////////
150 
151 class CollectLowestTransformsVisitor : public BaseOptimizerVisitor
152 {
153     public:
154 
155 
CollectLowestTransformsVisitor(Optimizer * optimizer=0)156         CollectLowestTransformsVisitor(Optimizer* optimizer=0):
157                     BaseOptimizerVisitor(optimizer,Optimizer::FLATTEN_STATIC_TRANSFORMS),
158                     _transformFunctor(osg::Matrix())
159         {
160             setTraversalMode(osg::NodeVisitor::TRAVERSE_PARENTS);
161         }
162 
apply(osg::Node & node)163         void apply(osg::Node& node) override
164         {
165             if (node.getNumParents())
166             {
167                 traverse(node);
168             }
169             else
170             {
171                 // for all current objects mark a nullptr transform for them.
172                 registerWithCurrentObjects(0);
173             }
174         }
175 
apply(osg::LOD & lod)176         void apply(osg::LOD& lod) override
177         {
178             _currentObjectList.push_back(&lod);
179 
180             traverse(lod);
181 
182             _currentObjectList.pop_back();
183         }
184 
apply(osg::Transform & transform)185         void apply(osg::Transform& transform) override
186         {
187             // for all current objects associated this transform with them.
188             registerWithCurrentObjects(&transform);
189         }
190 
apply(osg::Geode & geode)191         void apply(osg::Geode& geode) override
192         {
193             traverse(geode);
194         }
195 
apply(osg::Billboard & geode)196         void apply(osg::Billboard& geode) override
197         {
198             traverse(geode);
199         }
200 
collectDataFor(osg::Node * node)201         void collectDataFor(osg::Node* node)
202         {
203             _currentObjectList.push_back(node);
204 
205             node->accept(*this);
206 
207             _currentObjectList.pop_back();
208         }
209 
collectDataFor(osg::Billboard * billboard)210         void collectDataFor(osg::Billboard* billboard)
211         {
212             _currentObjectList.push_back(billboard);
213 
214             billboard->accept(*this);
215 
216             _currentObjectList.pop_back();
217         }
218 
collectDataFor(osg::Drawable * drawable)219         void collectDataFor(osg::Drawable* drawable)
220         {
221             _currentObjectList.push_back(drawable);
222 
223             const osg::Drawable::ParentList& parents = drawable->getParents();
224             for(osg::Drawable::ParentList::const_iterator itr=parents.begin();
225                 itr!=parents.end();
226                 ++itr)
227             {
228                 (*itr)->accept(*this);
229             }
230 
231             _currentObjectList.pop_back();
232         }
233 
234         void setUpMaps();
235         void disableTransform(osg::Transform* transform);
236         bool removeTransforms(osg::Node* nodeWeCannotRemove);
237 
isOperationPermissibleForObject(const osg::Object * object) const238         inline bool isOperationPermissibleForObject(const osg::Object* object) const
239         {
240             const osg::Node* node = object->asNode();
241             if (node)
242             {
243                 const osg::Drawable* drawable = node->asDrawable();
244                 if (drawable)
245                      return isOperationPermissibleForObject(drawable);
246                 else
247                     return isOperationPermissibleForObject(node);
248             }
249             return true;
250         }
251 
isOperationPermissibleForObject(const osg::Drawable * drawable) const252         inline bool isOperationPermissibleForObject(const osg::Drawable* drawable) const
253         {
254             return BaseOptimizerVisitor::isOperationPermissibleForObject(drawable);
255         }
256 
isOperationPermissibleForObject(const osg::Node * node) const257         inline bool isOperationPermissibleForObject(const osg::Node* node) const
258         {
259             return BaseOptimizerVisitor::isOperationPermissibleForObject(node);
260         }
261 
262     protected:
263 
264         struct TransformStruct
265         {
266             typedef std::set<osg::Object*> ObjectSet;
267 
TransformStructSceneUtil::CollectLowestTransformsVisitor::TransformStruct268             TransformStruct():_canBeApplied(true) {}
269 
addSceneUtil::CollectLowestTransformsVisitor::TransformStruct270             void add(osg::Object* obj)
271             {
272                 _objectSet.insert(obj);
273             }
274 
275             bool        _canBeApplied;
276             ObjectSet   _objectSet;
277         };
278 
279         struct ObjectStruct
280         {
281             typedef std::set<osg::Transform*> TransformSet;
282 
ObjectStructSceneUtil::CollectLowestTransformsVisitor::ObjectStruct283             ObjectStruct():_canBeApplied(true),_moreThanOneMatrixRequired(false) {}
284 
addSceneUtil::CollectLowestTransformsVisitor::ObjectStruct285             void add(osg::Transform* transform, bool canOptimize)
286             {
287                 if (transform)
288                 {
289                     if (!canOptimize) _moreThanOneMatrixRequired=true;
290                     else if (transform->getReferenceFrame()!=osg::Transform::RELATIVE_RF) _moreThanOneMatrixRequired=true;
291                     else
292                     {
293                         if (_transformSet.empty()) transform->computeLocalToWorldMatrix(_firstMatrix,0);
294                         else
295                         {
296                             osg::Matrix matrix;
297                             transform->computeLocalToWorldMatrix(matrix,0);
298                             if (_firstMatrix!=matrix) _moreThanOneMatrixRequired=true;
299                         }
300                     }
301                 }
302                 else
303                 {
304                     if (!_transformSet.empty())
305                     {
306                         if (!_firstMatrix.isIdentity()) _moreThanOneMatrixRequired=true;
307                     }
308 
309                 }
310                 _transformSet.insert(transform);
311             }
312 
313             bool            _canBeApplied;
314             bool            _moreThanOneMatrixRequired;
315             osg::Matrix     _firstMatrix;
316             TransformSet    _transformSet;
317         };
318 
319 
registerWithCurrentObjects(osg::Transform * transform)320         void registerWithCurrentObjects(osg::Transform* transform)
321         {
322             for(ObjectList::iterator itr=_currentObjectList.begin();
323                 itr!=_currentObjectList.end();
324                 ++itr)
325             {
326                 _objectMap[*itr].add(transform, transform && isOperationPermissibleForObject(transform));
327             }
328         }
329 
330         typedef std::map<osg::Transform*,TransformStruct>   TransformMap;
331         typedef std::map<osg::Object*,ObjectStruct>         ObjectMap;
332         typedef std::vector<osg::Object*>                   ObjectList;
333 
disableObject(osg::Object * object)334         void disableObject(osg::Object* object)
335         {
336             disableObject(_objectMap.find(object));
337         }
338 
339         void disableObject(ObjectMap::iterator itr);
340         void doTransform(osg::Object* obj,osg::Matrix& matrix);
341 
342         osgUtil::TransformAttributeFunctor _transformFunctor;
343         TransformMap    _transformMap;
344         ObjectMap       _objectMap;
345         ObjectList      _currentObjectList;
346 
347 };
348 
349 
doTransform(osg::Object * obj,osg::Matrix & matrix)350 void CollectLowestTransformsVisitor::doTransform(osg::Object* obj,osg::Matrix& matrix)
351 {
352     osg::Node* node = obj->asNode();
353     if (!node)
354         return;
355     osg::Drawable* drawable = node->asDrawable();
356     if (drawable)
357     {
358         osgUtil::TransformAttributeFunctor tf(matrix);
359         drawable->accept(tf);
360 
361         osg::Geometry *geom = drawable->asGeometry();
362         osg::Vec4Array* tangents = geom ? dynamic_cast<osg::Vec4Array*>(geom->getTexCoordArray(7)) : nullptr;
363         if (tangents)
364         {
365             for (unsigned int i=0; i<tangents->size(); ++i)
366             {
367                 osg::Vec4f& itr = (*tangents)[i];
368                 osg::Vec3f vec3 (itr.x(), itr.y(), itr.z());
369                 vec3 = osg::Matrix::transform3x3(tf._im, vec3);
370                 vec3.normalize();
371                 itr = osg::Vec4f(vec3.x(), vec3.y(), vec3.z(), itr.w());
372             }
373         }
374 
375         drawable->dirtyBound();
376         drawable->dirtyDisplayList();
377 
378         return;
379     }
380 
381     osg::LOD* lod = dynamic_cast<osg::LOD*>(obj);
382     if (lod)
383     {
384         osg::Matrix matrix_no_trans = matrix;
385         matrix_no_trans.setTrans(0.0f,0.0f,0.0f);
386 
387         osg::Vec3 v111(1.0f,1.0f,1.0f);
388         osg::Vec3 new_v111 = v111*matrix_no_trans;
389         float ratio = new_v111.length()/v111.length();
390 
391         // move center point.
392         lod->setCenter(lod->getCenter()*matrix);
393 
394         // adjust ranges to new scale.
395         for(unsigned int i=0;i<lod->getNumRanges();++i)
396         {
397             lod->setRange(i,lod->getMinRange(i)*ratio,lod->getMaxRange(i)*ratio);
398         }
399 
400         lod->dirtyBound();
401         return;
402     }
403 
404     osg::Billboard* billboard = dynamic_cast<osg::Billboard*>(obj);
405     if (billboard)
406     {
407         osg::Matrix matrix_no_trans = matrix;
408         matrix_no_trans.setTrans(0.0f,0.0f,0.0f);
409 
410         osgUtil::TransformAttributeFunctor tf(matrix_no_trans);
411 
412         osg::Vec3 axis = osg::Matrix::transform3x3(tf._im,billboard->getAxis());
413         axis.normalize();
414         billboard->setAxis(axis);
415 
416         osg::Vec3 normal = osg::Matrix::transform3x3(tf._im,billboard->getNormal());
417         normal.normalize();
418         billboard->setNormal(normal);
419 
420 
421         for(unsigned int i=0;i<billboard->getNumDrawables();++i)
422         {
423             billboard->setPosition(i,billboard->getPosition(i)*matrix);
424             billboard->getDrawable(i)->accept(tf);
425             billboard->getDrawable(i)->dirtyBound();
426         }
427 
428         billboard->dirtyBound();
429 
430         return;
431     }
432 }
433 
disableObject(ObjectMap::iterator itr)434 void CollectLowestTransformsVisitor::disableObject(ObjectMap::iterator itr)
435 {
436     if (itr==_objectMap.end())
437     {
438         return;
439     }
440 
441     if (itr->second._canBeApplied)
442     {
443         // we haven't been disabled yet so we need to disable,
444         itr->second._canBeApplied = false;
445 
446         // and then inform everybody we have been disabled.
447         for(ObjectStruct::TransformSet::iterator titr = itr->second._transformSet.begin();
448             titr != itr->second._transformSet.end();
449             ++titr)
450         {
451             disableTransform(*titr);
452         }
453     }
454 }
455 
disableTransform(osg::Transform * transform)456 void CollectLowestTransformsVisitor::disableTransform(osg::Transform* transform)
457 {
458     TransformMap::iterator itr=_transformMap.find(transform);
459     if (itr==_transformMap.end())
460     {
461         return;
462     }
463 
464     if (itr->second._canBeApplied)
465     {
466 
467         // we haven't been disabled yet so we need to disable,
468         itr->second._canBeApplied = false;
469         // and then inform everybody we have been disabled.
470         for(TransformStruct::ObjectSet::iterator oitr = itr->second._objectSet.begin();
471             oitr != itr->second._objectSet.end();
472             ++oitr)
473         {
474             disableObject(*oitr);
475         }
476     }
477 }
478 
setUpMaps()479 void CollectLowestTransformsVisitor::setUpMaps()
480 {
481     // create the TransformMap from the ObjectMap
482     ObjectMap::iterator oitr;
483     for(oitr=_objectMap.begin();
484         oitr!=_objectMap.end();
485         ++oitr)
486     {
487         osg::Object* object = oitr->first;
488         ObjectStruct& os = oitr->second;
489 
490         for(ObjectStruct::TransformSet::iterator titr = os._transformSet.begin();
491             titr != os._transformSet.end();
492             ++titr)
493         {
494             _transformMap[*titr].add(object);
495         }
496     }
497 
498     // disable all the objects which have more than one matrix associated
499     // with them, and then disable all transforms which have an object associated
500     // them that can't be applied, and then disable all objects which have
501     // disabled transforms associated, recursing until all disabled
502     // associativity.
503     // and disable all objects that the operation is not permisable for)
504     for(oitr=_objectMap.begin();
505         oitr!=_objectMap.end();
506         ++oitr)
507     {
508         osg::Object* object = oitr->first;
509         ObjectStruct& os = oitr->second;
510         if (os._canBeApplied)
511         {
512             if (os._moreThanOneMatrixRequired || !isOperationPermissibleForObject(object))
513             {
514                 disableObject(oitr);
515             }
516         }
517     }
518 
519 }
520 
removeTransforms(osg::Node * nodeWeCannotRemove)521 bool CollectLowestTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)
522 {
523     // transform the objects that can be applied.
524     for(ObjectMap::iterator oitr=_objectMap.begin();
525         oitr!=_objectMap.end();
526         ++oitr)
527     {
528         osg::Object* object = oitr->first;
529         ObjectStruct& os = oitr->second;
530         if (os._canBeApplied)
531         {
532             doTransform(object,os._firstMatrix);
533         }
534     }
535 
536 
537     bool transformRemoved = false;
538 
539     // clean up the transforms.
540     for(TransformMap::iterator titr=_transformMap.begin();
541         titr!=_transformMap.end();
542         ++titr)
543     {
544         if (titr->first!=0 && titr->second._canBeApplied)
545         {
546             if (titr->first!=nodeWeCannotRemove)
547             {
548                 transformRemoved = true;
549 
550                 osg::ref_ptr<osg::Transform> transform = titr->first;
551                 osg::ref_ptr<osg::Group>     group = new osg::Group;
552                 group->setName( transform->getName() );
553                 group->setDataVariance(osg::Object::STATIC);
554                 group->setNodeMask(transform->getNodeMask());
555                 group->setStateSet(transform->getStateSet());
556                 group->setUpdateCallback(transform->getUpdateCallback());
557                 group->setEventCallback(transform->getEventCallback());
558                 group->setCullCallback(transform->getCullCallback());
559                 group->setUserDataContainer(transform->getUserDataContainer());
560                 group->setDescriptions(transform->getDescriptions());
561                 for(unsigned int i=0;i<transform->getNumChildren();++i)
562                 {
563                     group->addChild(transform->getChild(i));
564                 }
565 
566                 for(int i2=transform->getNumParents()-1;i2>=0;--i2)
567                 {
568                     transform->getParent(i2)->replaceChild(transform.get(),group.get());
569                 }
570             }
571             else
572             {
573                 osg::MatrixTransform* mt = titr->first->asMatrixTransform();
574                 if (mt) mt->setMatrix(osg::Matrix::identity());
575                 else
576                 {
577                     osg::PositionAttitudeTransform* pat = titr->first->asPositionAttitudeTransform();
578                     if (pat)
579                     {
580                         pat->setPosition(osg::Vec3(0.0f,0.0f,0.0f));
581                         pat->setAttitude(osg::Quat());
582                         pat->setPivotPoint(osg::Vec3(0.0f,0.0f,0.0f));
583                     }
584                     else
585                     {
586                         OSG_WARN<<"Warning:: during Optimize::CollectLowestTransformsVisitor::removeTransforms(Node*)"<<std::endl;
587                         OSG_WARN<<"          unhandled of setting of indentity matrix on "<< titr->first->className()<<std::endl;
588                         OSG_WARN<<"          model will appear in the incorrect position."<<std::endl;
589                     }
590                 }
591 
592             }
593         }
594     }
595     _objectMap.clear();
596     _transformMap.clear();
597 
598     return transformRemoved;
599 }
600 
apply(osg::Node & node)601 void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Node& node)
602 {
603     traverse(node);
604 }
605 
needvbo(const osg::Geometry * geom)606 bool needvbo(const osg::Geometry* geom)
607 {
608 #if OSG_MIN_VERSION_REQUIRED(3,5,6)
609     return true;
610 #else
611     return geom->getUseVertexBufferObjects();
612 #endif
613 }
614 
cloneArray(osg::Array * array,osg::VertexBufferObject * & vbo,const osg::Geometry * geom)615 osg::Array* cloneArray(osg::Array* array, osg::VertexBufferObject*& vbo, const osg::Geometry* geom)
616 {
617     array = static_cast<osg::Array*>(array->clone(osg::CopyOp::DEEP_COPY_ALL));
618     if (!vbo && needvbo(geom))
619         vbo = new osg::VertexBufferObject;
620     if (vbo)
621         array->setVertexBufferObject(vbo);
622     return array;
623 }
624 
apply(osg::Drawable & drawable)625 void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Drawable& drawable)
626 {
627     osg::Geometry *geometry = drawable.asGeometry();
628     if((geometry) && (isOperationPermissibleForObject(&drawable)))
629     {
630         osg::VertexBufferObject* vbo = nullptr;
631         if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1)
632             geometry->setVertexArray(cloneArray(geometry->getVertexArray(), vbo, geometry));
633         if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1)
634             geometry->setNormalArray(cloneArray(geometry->getNormalArray(), vbo, geometry));
635         if(geometry->getTexCoordArray(7) && geometry->getTexCoordArray(7)->referenceCount() > 1) // tangents
636             geometry->setTexCoordArray(7, cloneArray(geometry->getTexCoordArray(7), vbo, geometry));
637     }
638     _drawableSet.insert(&drawable);
639 }
640 
apply(osg::Billboard & billboard)641 void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Billboard& billboard)
642 {
643     if (!_transformStack.empty())
644     {
645         _billboardSet.insert(&billboard);
646     }
647 }
648 
apply(osg::Transform & transform)649 void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Transform& transform)
650 {
651     if (!_transformStack.empty())
652     {
653         // we need to disable any transform higher in the list.
654         _transformSet.insert(_transformStack.back());
655     }
656 
657     _transformStack.push_back(&transform);
658 
659     // simple traverse the children as if this Transform didn't exist.
660     traverse(transform);
661 
662     _transformStack.pop_back();
663 }
664 
removeTransforms(osg::Node * nodeWeCannotRemove)665 bool Optimizer::FlattenStaticTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)
666 {
667     CollectLowestTransformsVisitor cltv(_optimizer);
668 
669     for(NodeSet::iterator nitr=_excludedNodeSet.begin();
670         nitr!=_excludedNodeSet.end();
671         ++nitr)
672     {
673         cltv.collectDataFor(*nitr);
674     }
675 
676     for(DrawableSet::iterator ditr=_drawableSet.begin();
677         ditr!=_drawableSet.end();
678         ++ditr)
679     {
680         cltv.collectDataFor(*ditr);
681     }
682 
683     for(BillboardSet::iterator bitr=_billboardSet.begin();
684         bitr!=_billboardSet.end();
685         ++bitr)
686     {
687         cltv.collectDataFor(*bitr);
688     }
689 
690     cltv.setUpMaps();
691 
692     for(TransformSet::iterator titr=_transformSet.begin();
693         titr!=_transformSet.end();
694         ++titr)
695     {
696         cltv.disableTransform(*titr);
697     }
698 
699 
700     return cltv.removeTransforms(nodeWeCannotRemove);
701 }
702 
703 ////////////////////////////////////////////////////////////////////////////
704 // CombineStaticTransforms
705 ////////////////////////////////////////////////////////////////////////////
706 
apply(osg::MatrixTransform & transform)707 void Optimizer::CombineStaticTransformsVisitor::apply(osg::MatrixTransform& transform)
708 {
709     if (transform.getDataVariance()==osg::Object::STATIC &&
710         transform.getNumChildren()==1 &&
711         transform.getChild(0)->asTransform()!=0 &&
712         transform.getChild(0)->asTransform()->asMatrixTransform()!=0 &&
713         transform.getChild(0)->asTransform()->getDataVariance()==osg::Object::STATIC &&
714         isOperationPermissibleForObject(&transform) && isOperationPermissibleForObject(transform.getChild(0)))
715     {
716         _transformSet.insert(&transform);
717     }
718 
719     traverse(transform);
720 }
721 
removeTransforms(osg::Node * nodeWeCannotRemove)722 bool Optimizer::CombineStaticTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)
723 {
724     if (nodeWeCannotRemove && nodeWeCannotRemove->asTransform()!=0 && nodeWeCannotRemove->asTransform()->asMatrixTransform()!=0)
725     {
726         // remove topmost node from transform set if it exists there.
727         TransformSet::iterator itr = _transformSet.find(nodeWeCannotRemove->asTransform()->asMatrixTransform());
728         if (itr!=_transformSet.end()) _transformSet.erase(itr);
729     }
730 
731     bool transformRemoved = false;
732 
733     while (!_transformSet.empty())
734     {
735         // get the first available transform to combine.
736         osg::ref_ptr<osg::MatrixTransform> transform = *_transformSet.begin();
737         _transformSet.erase(_transformSet.begin());
738 
739         if (transform->getNumChildren()==1 &&
740             transform->getChild(0)->asTransform()!=0 &&
741             transform->getChild(0)->asTransform()->asMatrixTransform()!=0 &&
742             transform->getChild(0)->asTransform()->getDataVariance()==osg::Object::STATIC)
743         {
744             // now combine with its child.
745             osg::MatrixTransform* child = transform->getChild(0)->asTransform()->asMatrixTransform();
746 
747             osg::Matrix newMatrix = child->getMatrix()*transform->getMatrix();
748             child->setMatrix(newMatrix);
749             if (transform->getStateSet())
750             {
751                 if(child->getStateSet()) child->getStateSet()->merge(*transform->getStateSet());
752                 else child->setStateSet(transform->getStateSet());
753             }
754 
755             transformRemoved = true;
756 
757             osg::Node::ParentList parents = transform->getParents();
758             for(osg::Node::ParentList::iterator pitr=parents.begin();
759                 pitr!=parents.end();
760                 ++pitr)
761             {
762                 (*pitr)->replaceChild(transform.get(),child);
763             }
764 
765         }
766 
767     }
768     return transformRemoved;
769 }
770 
771 ////////////////////////////////////////////////////////////////////////////
772 // RemoveEmptyNodes.
773 ////////////////////////////////////////////////////////////////////////////
774 
apply(osg::Group & group)775 void Optimizer::RemoveEmptyNodesVisitor::apply(osg::Group& group)
776 {
777     if (group.getNumParents()>0)
778     {
779         // only remove empty groups, but not empty occluders.
780         if (group.getNumChildren()==0 && isOperationPermissibleForObject(&group) &&
781             (typeid(group)==typeid(osg::Group) || (group.asTransform())) &&
782             (group.getNumChildrenRequiringUpdateTraversal()==0 && group.getNumChildrenRequiringEventTraversal()==0) )
783         {
784             _redundantNodeList.insert(&group);
785         }
786     }
787     traverse(group);
788 }
789 
removeEmptyNodes()790 void Optimizer::RemoveEmptyNodesVisitor::removeEmptyNodes()
791 {
792 
793     NodeList newEmptyGroups;
794 
795     // keep iterator through until scene graph is cleaned of empty nodes.
796     while (!_redundantNodeList.empty())
797     {
798         for(NodeList::iterator itr=_redundantNodeList.begin();
799             itr!=_redundantNodeList.end();
800             ++itr)
801         {
802 
803             osg::ref_ptr<osg::Node> nodeToRemove = (*itr);
804 
805             // take a copy of parents list since subsequent removes will modify the original one.
806             osg::Node::ParentList parents = nodeToRemove->getParents();
807 
808             for(osg::Node::ParentList::iterator pitr=parents.begin();
809                 pitr!=parents.end();
810                 ++pitr)
811             {
812                 osg::Group* parent = *pitr;
813                 if (!parent->asSwitch() && !dynamic_cast<osg::LOD*>(parent))
814                 {
815                     parent->removeChild(nodeToRemove.get());
816                     if (parent->getNumChildren()==0 && isOperationPermissibleForObject(parent)) newEmptyGroups.insert(parent);
817                 }
818             }
819         }
820 
821         _redundantNodeList.clear();
822         _redundantNodeList.swap(newEmptyGroups);
823     }
824 }
825 
826 
827 ////////////////////////////////////////////////////////////////////////////
828 // RemoveRedundantNodes.
829 ////////////////////////////////////////////////////////////////////////////
830 
isOperationPermissible(osg::Node & node)831 bool Optimizer::RemoveRedundantNodesVisitor::isOperationPermissible(osg::Node& node)
832 {
833     return node.getNumParents()>0 &&
834            !node.getStateSet() &&
835            !node.getCullCallback() &&
836            !node.getEventCallback() &&
837            !node.getUpdateCallback() &&
838            isOperationPermissibleForObject(&node);
839 }
840 
apply(osg::LOD & lod)841 void Optimizer::RemoveRedundantNodesVisitor::apply(osg::LOD& lod)
842 {
843     // don't remove any direct children of the LOD because they are used to define each LOD level.
844     for (unsigned int i=0; i<lod.getNumChildren(); ++i)
845         traverse(*lod.getChild(i));
846 }
847 
apply(osg::Switch & switchNode)848 void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Switch& switchNode)
849 {
850     // We should keep all switch child nodes since they reflect different switch states.
851     for (unsigned int i=0; i<switchNode.getNumChildren(); ++i)
852         traverse(*switchNode.getChild(i));
853 }
854 
apply(osg::Group & group)855 void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Group& group)
856 {
857     if (typeid(group)==typeid(osg::Group) &&
858         isOperationPermissible(group))
859     {
860         _redundantNodeList.insert(&group);
861     }
862 
863     traverse(group);
864 }
865 
866 
867 
apply(osg::Transform & transform)868 void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Transform& transform)
869 {
870     if (transform.getDataVariance()==osg::Object::STATIC &&
871         isOperationPermissible(transform))
872     {
873         osg::Matrix matrix;
874         transform.computeWorldToLocalMatrix(matrix,nullptr);
875         if (matrix.isIdentity())
876         {
877             _redundantNodeList.insert(&transform);
878         }
879     }
880     traverse(transform);
881 }
882 
883 
removeRedundantNodes()884 void Optimizer::RemoveRedundantNodesVisitor::removeRedundantNodes()
885 {
886 
887     for(NodeList::iterator itr=_redundantNodeList.begin();
888         itr!=_redundantNodeList.end();
889         ++itr)
890     {
891         osg::ref_ptr<osg::Group> group = (*itr)->asGroup();
892         if (group.valid())
893         {
894             // take a copy of parents list since subsequent removes will modify the original one.
895             osg::Node::ParentList parents = group->getParents();
896 
897             for(osg::Node::ParentList::iterator pitr=parents.begin();
898                 pitr!=parents.end();
899                 ++pitr)
900             {
901                 unsigned int childIndex = (*pitr)->getChildIndex(group);
902                 for (unsigned int i=0; i<group->getNumChildren(); ++i)
903                 {
904                     osg::Node* child = group->getChild(i);
905                     (*pitr)->insertChild(childIndex++, child);
906                 }
907 
908                 (*pitr)->removeChild(group);
909             }
910 
911             group->removeChildren(0, group->getNumChildren());
912         }
913         else
914         {
915             OSG_WARN<<"Optimizer::RemoveRedundantNodesVisitor::removeRedundantNodes() - failed dynamic_cast"<<std::endl;
916         }
917     }
918     _redundantNodeList.clear();
919 }
920 
921 
922 
923 ////////////////////////////////////////////////////////////////////////////
924 // code to merge geometry object which share, state, and attribute bindings.
925 ////////////////////////////////////////////////////////////////////////////
926 
927 #define COMPARE_BINDING(lhs, rhs) \
928         if (osg::getBinding(lhs)<osg::getBinding(rhs)) return true; \
929         if (osg::getBinding(rhs)<osg::getBinding(lhs)) return false;
930 
931 
932 struct LessGeometry
933 {
operator ()SceneUtil::LessGeometry934     bool operator() (const osg::ref_ptr<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& rhs) const
935     {
936         if (lhs->getStateSet()<rhs->getStateSet()) return true;
937         if (rhs->getStateSet()<lhs->getStateSet()) return false;
938 
939         COMPARE_BINDING(lhs->getNormalArray(), rhs->getNormalArray())
940         COMPARE_BINDING(lhs->getColorArray(), rhs->getColorArray())
941         COMPARE_BINDING(lhs->getSecondaryColorArray(), rhs->getSecondaryColorArray())
942         COMPARE_BINDING(lhs->getFogCoordArray(), rhs->getFogCoordArray())
943 
944 
945         if (lhs->getNumTexCoordArrays()<rhs->getNumTexCoordArrays()) return true;
946         if (rhs->getNumTexCoordArrays()<lhs->getNumTexCoordArrays()) return false;
947 
948         // therefore lhs->getNumTexCoordArrays()==rhs->getNumTexCoordArrays()
949 
950         unsigned int i;
951         for(i=0;i<lhs->getNumTexCoordArrays();++i)
952         {
953             if (rhs->getTexCoordArray(i))
954             {
955                 if (!lhs->getTexCoordArray(i)) return true;
956             }
957             else if (lhs->getTexCoordArray(i)) return false;
958         }
959 
960         for(i=0;i<lhs->getNumVertexAttribArrays();++i)
961         {
962             if (rhs->getVertexAttribArray(i))
963             {
964                 if (!lhs->getVertexAttribArray(i)) return true;
965             }
966             else if (lhs->getVertexAttribArray(i)) return false;
967         }
968 
969 
970         if (osg::getBinding(lhs->getNormalArray())==osg::Array::BIND_OVERALL)
971         {
972             // assumes that the bindings and arrays are set up correctly, this
973             // should be the case after running computeCorrectBindingsAndArraySizes();
974             const osg::Array* lhs_normalArray = lhs->getNormalArray();
975             const osg::Array* rhs_normalArray = rhs->getNormalArray();
976             if (lhs_normalArray->getType()<rhs_normalArray->getType()) return true;
977             if (rhs_normalArray->getType()<lhs_normalArray->getType()) return false;
978             switch(lhs_normalArray->getType())
979             {
980             case(osg::Array::Vec3bArrayType):
981                 if ((*static_cast<const osg::Vec3bArray*>(lhs_normalArray))[0]<(*static_cast<const osg::Vec3bArray*>(rhs_normalArray))[0]) return true;
982                 if ((*static_cast<const osg::Vec3bArray*>(rhs_normalArray))[0]<(*static_cast<const osg::Vec3bArray*>(lhs_normalArray))[0]) return false;
983                 break;
984             case(osg::Array::Vec3sArrayType):
985                 if ((*static_cast<const osg::Vec3sArray*>(lhs_normalArray))[0]<(*static_cast<const osg::Vec3sArray*>(rhs_normalArray))[0]) return true;
986                 if ((*static_cast<const osg::Vec3sArray*>(rhs_normalArray))[0]<(*static_cast<const osg::Vec3sArray*>(lhs_normalArray))[0]) return false;
987                 break;
988             case(osg::Array::Vec3ArrayType):
989                 if ((*static_cast<const osg::Vec3Array*>(lhs_normalArray))[0]<(*static_cast<const osg::Vec3Array*>(rhs_normalArray))[0]) return true;
990                 if ((*static_cast<const osg::Vec3Array*>(rhs_normalArray))[0]<(*static_cast<const osg::Vec3Array*>(lhs_normalArray))[0]) return false;
991                 break;
992             default:
993                 break;
994             }
995         }
996 
997         if (osg::getBinding(lhs->getColorArray())==osg::Array::BIND_OVERALL)
998         {
999             const osg::Array* lhs_colorArray = lhs->getColorArray();
1000             const osg::Array* rhs_colorArray = rhs->getColorArray();
1001             if (lhs_colorArray->getType()<rhs_colorArray->getType()) return true;
1002             if (rhs_colorArray->getType()<lhs_colorArray->getType()) return false;
1003             switch(lhs_colorArray->getType())
1004             {
1005                 case(osg::Array::Vec4ubArrayType):
1006                     if ((*static_cast<const osg::Vec4ubArray*>(lhs_colorArray))[0]<(*static_cast<const osg::Vec4ubArray*>(rhs_colorArray))[0]) return true;
1007                     if ((*static_cast<const osg::Vec4ubArray*>(rhs_colorArray))[0]<(*static_cast<const osg::Vec4ubArray*>(lhs_colorArray))[0]) return false;
1008                     break;
1009                 case(osg::Array::Vec3ArrayType):
1010                     if ((*static_cast<const osg::Vec3Array*>(lhs_colorArray))[0]<(*static_cast<const osg::Vec3Array*>(rhs_colorArray))[0]) return true;
1011                     if ((*static_cast<const osg::Vec3Array*>(rhs_colorArray))[0]<(*static_cast<const osg::Vec3Array*>(lhs_colorArray))[0]) return false;
1012                     break;
1013                 case(osg::Array::Vec4ArrayType):
1014                     if ((*static_cast<const osg::Vec4Array*>(lhs_colorArray))[0]<(*static_cast<const osg::Vec4Array*>(rhs_colorArray))[0]) return true;
1015                     if ((*static_cast<const osg::Vec4Array*>(rhs_colorArray))[0]<(*static_cast<const osg::Vec4Array*>(lhs_colorArray))[0]) return false;
1016                     break;
1017                 default:
1018                     break;
1019             }
1020 
1021         }
1022 
1023         return false;
1024 
1025     }
1026 };
1027 
1028 struct LessGeometryViewPoint
1029 {
1030     osg::Vec3f _viewPoint;
operator ()SceneUtil::LessGeometryViewPoint1031     bool operator() (const osg::ref_ptr<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& rhs) const
1032     {
1033         float len1 = (lhs->getBoundingBox().center() - _viewPoint).length2();
1034         float len2 = (rhs->getBoundingBox().center() - _viewPoint).length2();
1035         return len2 < len1;
1036     }
1037 };
1038 
1039 struct LessGeometryPrimitiveType
1040 {
operator ()SceneUtil::LessGeometryPrimitiveType1041     bool operator() (const osg::ref_ptr<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& rhs) const
1042     {
1043         for(unsigned int i=0;
1044             i<lhs->getNumPrimitiveSets() && i<rhs->getNumPrimitiveSets();
1045             ++i)
1046         {
1047             if (lhs->getPrimitiveSet(i)->getType()<rhs->getPrimitiveSet(i)->getType()) return true;
1048             else if (rhs->getPrimitiveSet(i)->getType()<lhs->getPrimitiveSet(i)->getType()) return false;
1049 
1050             if (lhs->getPrimitiveSet(i)->getMode()<rhs->getPrimitiveSet(i)->getMode()) return true;
1051             else if (rhs->getPrimitiveSet(i)->getMode()<lhs->getPrimitiveSet(i)->getMode()) return false;
1052 
1053         }
1054         return lhs->getNumPrimitiveSets()<rhs->getNumPrimitiveSets();
1055     }
1056 };
1057 
1058 
1059 /// Shortcut to get size of an array, even if pointer is nullptr.
getSize(const osg::Array * a)1060 inline unsigned int getSize(const osg::Array * a) { return a ? a->getNumElements() : 0; }
1061 
1062 /// When merging geometries, tests if two arrays can be merged, regarding to their number of components, and the number of vertices.
isArrayCompatible(unsigned int numVertice1,unsigned int numVertice2,const osg::Array * compare1,const osg::Array * compare2)1063 bool isArrayCompatible(unsigned int numVertice1, unsigned int numVertice2, const osg::Array* compare1, const osg::Array* compare2)
1064 {
1065     // Sumed up truth table:
1066     //  If array (1 or 2) not empty and vertices empty => error, should not happen (allows simplification in formulae below)
1067     //  If one side has both vertices and array, and the other side has only vertices => then arrays cannot be merged
1068     //  Else, arrays can be merged
1069     //assert(numVertice1 || !getSize(compare1));
1070     //assert(numVertice2 || !getSize(compare2));
1071     return !(   (numVertice1 && !getSize(compare1) && getSize(compare2))
1072              || (numVertice2 && !getSize(compare2) && getSize(compare1)) );
1073 }
1074 
1075 /// Return true only if both geometries have same array type and if arrays (such as TexCoords) are compatible (i.e. both empty or both filled)
isAbleToMerge(const osg::Geometry & g1,const osg::Geometry & g2)1076 bool isAbleToMerge(const osg::Geometry& g1, const osg::Geometry& g2)
1077 {
1078     unsigned int numVertice1( getSize(g1.getVertexArray()) );
1079     unsigned int numVertice2( getSize(g2.getVertexArray()) );
1080 
1081     // first verify arrays size
1082     if (!isArrayCompatible(numVertice1,numVertice2,g1.getNormalArray(),g2.getNormalArray()) ||
1083         !isArrayCompatible(numVertice1,numVertice2,g1.getColorArray(),g2.getColorArray()) ||
1084         !isArrayCompatible(numVertice1,numVertice2,g1.getSecondaryColorArray(),g2.getSecondaryColorArray()) ||
1085         !isArrayCompatible(numVertice1,numVertice2,g1.getFogCoordArray(),g2.getFogCoordArray()) ||
1086         g1.getNumTexCoordArrays()!=g2.getNumTexCoordArrays()) return false;
1087 
1088     for (unsigned int eachTexCoordArray=0;eachTexCoordArray<g1.getNumTexCoordArrays();++eachTexCoordArray)
1089     {
1090         if (!isArrayCompatible(numVertice1,numVertice2,g1.getTexCoordArray(eachTexCoordArray),g2.getTexCoordArray(eachTexCoordArray))) return false;
1091     }
1092 
1093     // then verify data type compatibility
1094     if (g1.getVertexArray() && g2.getVertexArray() && g1.getVertexArray()->getDataType()!=g2.getVertexArray()->getDataType()) return false;
1095     if (g1.getNormalArray() && g2.getNormalArray() && g1.getNormalArray()->getDataType()!=g2.getNormalArray()->getDataType()) return false;
1096     if (g1.getColorArray() && g2.getColorArray() && g1.getColorArray()->getDataType()!=g2.getColorArray()->getDataType()) return false;
1097     if (g1.getSecondaryColorArray() && g2.getSecondaryColorArray() && g1.getSecondaryColorArray()->getDataType()!=g2.getSecondaryColorArray()->getDataType()) return false;
1098     if (g1.getFogCoordArray() && g2.getNormalArray() && g1.getFogCoordArray()->getDataType()!=g2.getFogCoordArray()->getDataType()) return false;
1099     return true;
1100 }
1101 
1102 
pushStateSet(osg::StateSet * stateSet)1103 void Optimizer::MergeGeometryVisitor::pushStateSet(osg::StateSet *stateSet)
1104 {
1105     _stateSetStack.push_back(stateSet);
1106     checkAlphaBlendingActive();
1107 }
1108 
popStateSet()1109 void Optimizer::MergeGeometryVisitor::popStateSet()
1110 {
1111     _stateSetStack.pop_back();
1112     checkAlphaBlendingActive();
1113 }
1114 
checkAlphaBlendingActive()1115 void Optimizer::MergeGeometryVisitor::checkAlphaBlendingActive()
1116 {
1117     int renderingHint = 0;
1118     bool override = false;
1119     for (std::vector<osg::StateSet*>::const_iterator it = _stateSetStack.begin(); it != _stateSetStack.end(); ++it)
1120     {
1121         osg::StateSet* stateSet = *it;
1122         osg::StateSet::RenderBinMode mode = stateSet->getRenderBinMode();
1123         if (override && !(mode & osg::StateSet::PROTECTED_RENDERBIN_DETAILS))
1124             continue;
1125         if (mode & osg::StateSet::USE_RENDERBIN_DETAILS)
1126             renderingHint = stateSet->getRenderingHint();
1127         if (mode & osg::StateSet::OVERRIDE_RENDERBIN_DETAILS)
1128             override = true;
1129     }
1130     // Can't merge Geometry that are using a transparent sorting bin as that would cause the sorting to break.
1131     _alphaBlendingActive = renderingHint == osg::StateSet::TRANSPARENT_BIN;
1132 }
1133 
apply(osg::Group & group)1134 void Optimizer::MergeGeometryVisitor::apply(osg::Group &group)
1135 {
1136     if (group.getStateSet())
1137         pushStateSet(group.getStateSet());
1138 
1139     if (!_alphaBlendingActive || _mergeAlphaBlending)
1140         mergeGroup(group);
1141 
1142     traverse(group);
1143 
1144     if (group.getStateSet())
1145         popStateSet();
1146 }
1147 
clonePrimitive(osg::PrimitiveSet * ps,osg::ElementBufferObject * & ebo,const osg::Geometry * geom)1148 osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps, osg::ElementBufferObject*& ebo, const osg::Geometry* geom)
1149 {
1150     if (ps->referenceCount() <= 1)
1151         return ps;
1152     ps = static_cast<osg::PrimitiveSet*>(ps->clone(osg::CopyOp::DEEP_COPY_ALL));
1153 
1154     osg::DrawElements* drawElements = ps->getDrawElements();
1155     if (!drawElements) return ps;
1156 
1157     if (!ebo && needvbo(geom))
1158         ebo = new osg::ElementBufferObject;
1159     if (ebo)
1160         drawElements->setElementBufferObject(ebo);
1161 
1162     return ps;
1163 }
1164 
containsSharedPrimitives(const osg::Geometry * geom)1165 bool containsSharedPrimitives(const osg::Geometry* geom)
1166 {
1167     for (unsigned int i=0; i<geom->getNumPrimitiveSets(); ++i)
1168         if (geom->getPrimitiveSet(i)->referenceCount() > 1) return true;
1169     return false;
1170 }
1171 
mergeGroup(osg::Group & group)1172 bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group)
1173 {
1174     if (!isOperationPermissibleForObject(&group)) return false;
1175 
1176     if (group.getNumChildren()>=2)
1177     {
1178 
1179         typedef std::vector< osg::ref_ptr<osg::Geometry> >                          DuplicateList;
1180         typedef std::vector< osg::ref_ptr<osg::Node> >                              Nodes;
1181         typedef std::map< osg::ref_ptr<osg::Geometry> ,DuplicateList,LessGeometry>  GeometryDuplicateMap;
1182 
1183         typedef std::vector<DuplicateList> MergeList;
1184 
1185         GeometryDuplicateMap geometryDuplicateMap;
1186         Nodes standardChildren;
1187 
1188         unsigned int i;
1189         for(i=0;i<group.getNumChildren();++i)
1190         {
1191             osg::Node* child = group.getChild(i);
1192             osg::Geometry* geom = child->asGeometry();
1193             if (geom)
1194             {
1195                 if (
1196                     geom->getDataVariance()!=osg::Object::DYNAMIC &&
1197                     isOperationPermissibleForObject(geom))
1198                 {
1199                     geometryDuplicateMap[geom].push_back(geom);
1200                 }
1201                 else
1202                 {
1203                     standardChildren.push_back(geom);
1204                 }
1205             }
1206             else
1207             {
1208                 standardChildren.push_back(child);
1209             }
1210         }
1211 
1212         // first try to group geometries with the same properties
1213         // (i.e. array types) to avoid loss of data during merging
1214         MergeList mergeListChecked;        // List of drawables just before merging, grouped by "compatibility" and vertex limit
1215         MergeList mergeList;            // Intermediate list of drawables, grouped ony by "compatibility"
1216         for(GeometryDuplicateMap::iterator itr=geometryDuplicateMap.begin();
1217             itr!=geometryDuplicateMap.end();
1218             ++itr)
1219         {
1220             if (itr->second.empty()) continue;
1221             if (itr->second.size()==1)
1222             {
1223                 mergeList.push_back(DuplicateList());
1224                 DuplicateList* duplicateList = &mergeList.back();
1225                 duplicateList->push_back(itr->second[0]);
1226                 continue;
1227             }
1228 
1229             std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType());
1230 
1231             // initialize the temporary list by pushing the first geometry
1232             MergeList mergeListTmp;
1233             mergeListTmp.push_back(DuplicateList());
1234             DuplicateList* duplicateList = &mergeListTmp.back();
1235             duplicateList->push_back(itr->second[0]);
1236 
1237             for(DuplicateList::iterator dupItr=itr->second.begin()+1;
1238                 dupItr!=itr->second.end();
1239                 ++dupItr)
1240             {
1241                 osg::Geometry* geomToPush = dupItr->get();
1242 
1243                 // try to group geomToPush with another geometry
1244                 MergeList::iterator eachMergeList=mergeListTmp.begin();
1245                 for(;eachMergeList!=mergeListTmp.end();++eachMergeList)
1246                 {
1247                     if (!eachMergeList->empty() && eachMergeList->front()!=nullptr
1248                         && isAbleToMerge(*eachMergeList->front(),*geomToPush))
1249                     {
1250                         eachMergeList->push_back(geomToPush);
1251                         break;
1252                     }
1253                 }
1254 
1255                 // if no suitable group was found, then a new one is created
1256                 if (eachMergeList==mergeListTmp.end())
1257                 {
1258                     mergeListTmp.push_back(DuplicateList());
1259                     duplicateList = &mergeListTmp.back();
1260                     duplicateList->push_back(geomToPush);
1261                 }
1262             }
1263 
1264             // copy the group in the mergeListChecked
1265             for(MergeList::iterator eachMergeList=mergeListTmp.begin();eachMergeList!=mergeListTmp.end();++eachMergeList)
1266             {
1267                 mergeListChecked.push_back(*eachMergeList);
1268             }
1269         }
1270 
1271         // then build merge list using _targetMaximumNumberOfVertices
1272         bool needToDoMerge = false;
1273         // dequeue each DuplicateList when vertices limit is reached or when all elements has been checked
1274         for(MergeList::iterator itr=mergeListChecked.begin(); itr!=mergeListChecked.end(); ++itr)
1275         {
1276             DuplicateList& duplicateList(*itr);
1277             if (duplicateList.size()==0)
1278             {
1279                 continue;
1280             }
1281 
1282             if (duplicateList.size()==1)
1283             {
1284                 mergeList.push_back(duplicateList);
1285                 continue;
1286             }
1287 
1288             unsigned int totalNumberVertices = 0;
1289             DuplicateList subset;
1290             for(DuplicateList::iterator ditr = duplicateList.begin();
1291                 ditr != duplicateList.end();
1292                 ++ditr)
1293             {
1294                 osg::Geometry* geometry = ditr->get();
1295                 unsigned int numVertices = (geometry->getVertexArray() ? geometry->getVertexArray()->getNumElements() : 0);
1296                 if ((totalNumberVertices+numVertices)>_targetMaximumNumberOfVertices && !subset.empty())
1297                 {
1298                     mergeList.push_back(subset);
1299                     subset.clear();
1300                     totalNumberVertices = 0;
1301                 }
1302                 totalNumberVertices += numVertices;
1303                 subset.push_back(geometry);
1304                 if (subset.size()>1) needToDoMerge = true;
1305             }
1306             if (!subset.empty()) mergeList.push_back(subset);
1307         }
1308 
1309         if (needToDoMerge)
1310         {
1311             // to avoid performance issues associated with incrementally removing a large number children, we remove them all and add back the ones we need.
1312             group.removeChildren(0, group.getNumChildren());
1313 
1314             for(Nodes::iterator itr = standardChildren.begin();
1315                 itr != standardChildren.end();
1316                 ++itr)
1317             {
1318                 group.addChild(*itr);
1319             }
1320 
1321             // now do the merging of geometries
1322             for(MergeList::iterator mitr = mergeList.begin();
1323                 mitr != mergeList.end();
1324                 ++mitr)
1325             {
1326                 DuplicateList& duplicateList = *mitr;
1327                 if (!duplicateList.empty())
1328                 {
1329                     if (_alphaBlendingActive)
1330                     {
1331                         LessGeometryViewPoint lgvp;
1332                         lgvp._viewPoint = _viewPoint;
1333                         std::sort(duplicateList.begin(), duplicateList.end(), lgvp);
1334                     }
1335                     DuplicateList::iterator ditr = duplicateList.begin();
1336                     osg::ref_ptr<osg::Geometry> lhs = *ditr++;
1337                     group.addChild(lhs.get());
1338                     for(;
1339                         ditr != duplicateList.end();
1340                         ++ditr)
1341                     {
1342                         mergeGeometry(*lhs, **ditr);
1343                     }
1344                 }
1345             }
1346         }
1347 
1348     }
1349 
1350 
1351     // convert all polygon primitives which has 3 indices into TRIANGLES, 4 indices into QUADS.
1352     unsigned int i;
1353     for(i=0;i<group.getNumChildren();++i)
1354     {
1355         osg::Drawable* drawable = group.getChild(i)->asDrawable();
1356         if (!drawable)
1357             continue;
1358         osg::Geometry* geom = drawable->asGeometry();
1359         osg::ElementBufferObject* ebo = nullptr;
1360         if (geom)
1361         {
1362             osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();
1363             for(osg::Geometry::PrimitiveSetList::iterator itr=primitives.begin();
1364                 itr!=primitives.end();
1365                 ++itr)
1366             {
1367                 osg::PrimitiveSet* prim = itr->get();
1368                 if (prim->getMode()==osg::PrimitiveSet::POLYGON)
1369                 {
1370                     if (prim->getNumIndices()==3)
1371                     {
1372                         prim = clonePrimitive(prim, ebo, geom); (*itr) = prim;
1373                         prim->setMode(osg::PrimitiveSet::TRIANGLES);
1374                     }
1375                     else if (prim->getNumIndices()==4)
1376                     {
1377                         prim = clonePrimitive(prim, ebo, geom); (*itr) = prim;
1378                         prim->setMode(osg::PrimitiveSet::QUADS);
1379                     }
1380                 }
1381             }
1382         }
1383     }
1384 
1385     // now merge any compatible primitives.
1386     for(i=0;i<group.getNumChildren();++i)
1387     {
1388         osg::Drawable* drawable = group.getChild(i)->asDrawable();
1389         if (!drawable)
1390             continue;
1391         osg::Geometry* geom = drawable->asGeometry();
1392         osg::ElementBufferObject* ebo = nullptr;
1393         if (geom)
1394         {
1395             if (geom->getNumPrimitiveSets()>0 &&
1396                 osg::getBinding(geom->getNormalArray())!=osg::Array::BIND_PER_PRIMITIVE_SET &&
1397                 osg::getBinding(geom->getColorArray())!=osg::Array::BIND_PER_PRIMITIVE_SET &&
1398                 osg::getBinding(geom->getSecondaryColorArray())!=osg::Array::BIND_PER_PRIMITIVE_SET &&
1399                 osg::getBinding(geom->getFogCoordArray())!=osg::Array::BIND_PER_PRIMITIVE_SET)
1400             {
1401 
1402 #if 1
1403                 bool doneCombine = false;
1404 
1405                 std::set<osg::PrimitiveSet*> toremove;
1406 
1407                 osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();
1408                 unsigned int lhsNo=0;
1409                 unsigned int rhsNo=1;
1410                 while(rhsNo<primitives.size())
1411                 {
1412                     osg::PrimitiveSet* lhs = primitives[lhsNo].get();
1413                     osg::PrimitiveSet* rhs = primitives[rhsNo].get();
1414 
1415                     bool combine = false;
1416 
1417                     if (lhs->getType()==rhs->getType() &&
1418                         lhs->getMode()==rhs->getMode())
1419                     {
1420 
1421                         switch(lhs->getMode())
1422                         {
1423                         case(osg::PrimitiveSet::POINTS):
1424                         case(osg::PrimitiveSet::LINES):
1425                         case(osg::PrimitiveSet::TRIANGLES):
1426                         case(osg::PrimitiveSet::QUADS):
1427                             combine = true;
1428                             break;
1429                         }
1430 
1431                     }
1432 
1433                     if (combine)
1434                     {
1435                         lhs = clonePrimitive(lhs, ebo, geom);
1436                         primitives[lhsNo] = lhs;
1437 
1438                         switch(lhs->getType())
1439                         {
1440                         case(osg::PrimitiveSet::DrawArraysPrimitiveType):
1441                             combine = mergePrimitive(*(static_cast<osg::DrawArrays*>(lhs)),*(static_cast<osg::DrawArrays*>(rhs)));
1442                             break;
1443                         case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):
1444                             combine = mergePrimitive(*(static_cast<osg::DrawArrayLengths*>(lhs)),*(static_cast<osg::DrawArrayLengths*>(rhs)));
1445                             break;
1446                         case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):
1447                             combine = mergePrimitive(*(static_cast<osg::DrawElementsUByte*>(lhs)),*(static_cast<osg::DrawElementsUByte*>(rhs)));
1448                             break;
1449                         case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):
1450                             combine = mergePrimitive(*(static_cast<osg::DrawElementsUShort*>(lhs)),*(static_cast<osg::DrawElementsUShort*>(rhs)));
1451                             break;
1452                         case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):
1453                             combine = mergePrimitive(*(static_cast<osg::DrawElementsUInt*>(lhs)),*(static_cast<osg::DrawElementsUInt*>(rhs)));
1454                             break;
1455                         default:
1456                             combine = false;
1457                             break;
1458                         }
1459                     }
1460 
1461                     if (combine)
1462                     {
1463                         // make this primitive set as invalid and needing cleaning up.
1464                         toremove.insert(rhs);
1465                         doneCombine = true;
1466                         ++rhsNo;
1467                     }
1468                     else
1469                     {
1470                         lhsNo = rhsNo;
1471                         ++rhsNo;
1472                     }
1473                 }
1474 
1475     #if 1
1476                 if (doneCombine)
1477                 {
1478                     // now need to clean up primitiveset so it no longer contains the rhs combined primitives.
1479                     // first swap with a empty primitiveSet to empty it completely.
1480                     osg::Geometry::PrimitiveSetList oldPrimitives;
1481                     primitives.swap(oldPrimitives);
1482 
1483                     // now add the active primitive sets
1484                     for(osg::Geometry::PrimitiveSetList::iterator pitr = oldPrimitives.begin();
1485                         pitr != oldPrimitives.end();
1486                         ++pitr)
1487                     {
1488                         if (!toremove.count(*pitr)) primitives.push_back(*pitr);
1489                     }
1490                 }
1491     #endif
1492 
1493 #else
1494 
1495                 osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();
1496                 unsigned int primNo=0;
1497                 while(primNo+1<primitives.size())
1498                 {
1499                     osg::PrimitiveSet* lhs = primitives[primNo].get();
1500                     osg::PrimitiveSet* rhs = primitives[primNo+1].get();
1501 
1502                     bool combine = false;
1503 
1504                     if (lhs->getType()==rhs->getType() &&
1505                         lhs->getMode()==rhs->getMode())
1506                     {
1507 
1508                         switch(lhs->getMode())
1509                         {
1510                         case(osg::PrimitiveSet::POINTS):
1511                         case(osg::PrimitiveSet::LINES):
1512                         case(osg::PrimitiveSet::TRIANGLES):
1513                         case(osg::PrimitiveSet::QUADS):
1514                             combine = true;
1515                             break;
1516                         }
1517 
1518                     }
1519 
1520                     if (combine)
1521                     {
1522 
1523                         switch(lhs->getType())
1524                         {
1525                         case(osg::PrimitiveSet::DrawArraysPrimitiveType):
1526                             combine = mergePrimitive(*(static_cast<osg::DrawArrays*>(lhs)),*(static_cast<osg::DrawArrays*>(rhs)));
1527                             break;
1528                         case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):
1529                             combine = mergePrimitive(*(static_cast<osg::DrawArrayLengths*>(lhs)),*(static_cast<osg::DrawArrayLengths*>(rhs)));
1530                             break;
1531                         case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):
1532                             combine = mergePrimitive(*(static_cast<osg::DrawElementsUByte*>(lhs)),*(static_cast<osg::DrawElementsUByte*>(rhs)));
1533                             break;
1534                         case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):
1535                             combine = mergePrimitive(*(static_cast<osg::DrawElementsUShort*>(lhs)),*(static_cast<osg::DrawElementsUShort*>(rhs)));
1536                             break;
1537                         case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):
1538                             combine = mergePrimitive(*(static_cast<osg::DrawElementsUInt*>(lhs)),*(static_cast<osg::DrawElementsUInt*>(rhs)));
1539                             break;
1540                         default:
1541                             break;
1542                         }
1543                     }
1544                     if (combine)
1545                     {
1546                         primitives.erase(primitives.begin()+primNo+1);
1547                     }
1548 
1549                     if (!combine)
1550                     {
1551                         primNo++;
1552                     }
1553                 }
1554 #endif
1555                 if (doneCombine && !geom->containsSharedArrays() && !containsSharedPrimitives(geom))
1556                 {
1557                     // prefer to use vbo for merged geometries as vbo uses less memory than display lists.
1558                     geom->setUseVertexBufferObjects(true);
1559                     geom->setUseDisplayList(false);
1560                 }
1561                 if (_alphaBlendingActive && _mergeAlphaBlending && !geom->getStateSet())
1562                 {
1563                     osg::Depth* d = new osg::Depth;
1564                     d->setWriteMask(0);
1565                     geom->getOrCreateStateSet()->setAttribute(d);
1566                 }
1567             }
1568         }
1569 
1570 
1571     }
1572 
1573 //    geode.dirtyBound();
1574 
1575 
1576     return false;
1577 }
1578 
1579 class MergeArrayVisitor : public osg::ArrayVisitor
1580 {
1581     protected:
1582         osg::Array* _lhs;
1583     public:
MergeArrayVisitor()1584         MergeArrayVisitor() :
1585             _lhs(0) {}
1586 
1587 
1588         /// try to merge the content of two arrays.
merge(osg::Array * lhs,osg::Array * rhs)1589         bool merge(osg::Array* lhs,osg::Array* rhs)
1590         {
1591             if (lhs==0 || rhs==0) return true;
1592             if (lhs->getType()!=rhs->getType()) return false;
1593 
1594             _lhs = lhs;
1595 
1596             rhs->accept(*this);
1597             return true;
1598         }
1599 
1600         template<typename T>
_merge(T & rhs)1601         void _merge(T& rhs)
1602         {
1603             T* lhs = static_cast<T*>(_lhs);
1604             lhs->insert(lhs->end(),rhs.begin(),rhs.end());
1605         }
1606 
apply(osg::Array &)1607         void apply(osg::Array&) override { OSG_WARN << "Warning: Optimizer's MergeArrayVisitor cannot merge Array type." << std::endl; }
1608 
apply(osg::ByteArray & rhs)1609         void apply(osg::ByteArray& rhs) override { _merge(rhs); }
apply(osg::ShortArray & rhs)1610         void apply(osg::ShortArray& rhs) override { _merge(rhs); }
apply(osg::IntArray & rhs)1611         void apply(osg::IntArray& rhs) override { _merge(rhs); }
apply(osg::UByteArray & rhs)1612         void apply(osg::UByteArray& rhs) override { _merge(rhs); }
apply(osg::UShortArray & rhs)1613         void apply(osg::UShortArray& rhs) override { _merge(rhs); }
apply(osg::UIntArray & rhs)1614         void apply(osg::UIntArray& rhs) override { _merge(rhs); }
1615 
apply(osg::Vec4ubArray & rhs)1616         void apply(osg::Vec4ubArray& rhs) override { _merge(rhs); }
apply(osg::Vec3ubArray & rhs)1617         void apply(osg::Vec3ubArray& rhs) override{ _merge(rhs); }
apply(osg::Vec2ubArray & rhs)1618         void apply(osg::Vec2ubArray& rhs) override { _merge(rhs); }
1619 
apply(osg::Vec4usArray & rhs)1620         void apply(osg::Vec4usArray& rhs) override { _merge(rhs); }
apply(osg::Vec3usArray & rhs)1621         void apply(osg::Vec3usArray& rhs) override { _merge(rhs); }
apply(osg::Vec2usArray & rhs)1622         void apply(osg::Vec2usArray& rhs) override { _merge(rhs); }
1623 
apply(osg::FloatArray & rhs)1624         void apply(osg::FloatArray& rhs) override { _merge(rhs); }
apply(osg::Vec2Array & rhs)1625         void apply(osg::Vec2Array& rhs) override { _merge(rhs); }
apply(osg::Vec3Array & rhs)1626         void apply(osg::Vec3Array& rhs) override { _merge(rhs); }
apply(osg::Vec4Array & rhs)1627         void apply(osg::Vec4Array& rhs) override { _merge(rhs); }
1628 
apply(osg::DoubleArray & rhs)1629         void apply(osg::DoubleArray& rhs) override { _merge(rhs); }
apply(osg::Vec2dArray & rhs)1630         void apply(osg::Vec2dArray& rhs) override { _merge(rhs); }
apply(osg::Vec3dArray & rhs)1631         void apply(osg::Vec3dArray& rhs) override { _merge(rhs); }
apply(osg::Vec4dArray & rhs)1632         void apply(osg::Vec4dArray& rhs) override { _merge(rhs); }
1633 
apply(osg::Vec2bArray & rhs)1634         void apply(osg::Vec2bArray&  rhs) override { _merge(rhs); }
apply(osg::Vec3bArray & rhs)1635         void apply(osg::Vec3bArray&  rhs) override { _merge(rhs); }
apply(osg::Vec4bArray & rhs)1636         void apply(osg::Vec4bArray&  rhs) override { _merge(rhs); }
1637 
apply(osg::Vec2sArray & rhs)1638         void apply(osg::Vec2sArray& rhs) override { _merge(rhs); }
apply(osg::Vec3sArray & rhs)1639         void apply(osg::Vec3sArray& rhs) override { _merge(rhs); }
apply(osg::Vec4sArray & rhs)1640         void apply(osg::Vec4sArray& rhs) override { _merge(rhs); }
1641 };
1642 
mergeGeometry(osg::Geometry & lhs,osg::Geometry & rhs)1643 bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs)
1644 {
1645     MergeArrayVisitor merger;
1646     osg::VertexBufferObject* vbo = nullptr;
1647     unsigned int base = 0;
1648     if (lhs.getVertexArray() && rhs.getVertexArray())
1649     {
1650         base = lhs.getVertexArray()->getNumElements();
1651         if (lhs.getVertexArray()->referenceCount() > 1)
1652             lhs.setVertexArray(cloneArray(lhs.getVertexArray(), vbo, &lhs));
1653         if (!merger.merge(lhs.getVertexArray(),rhs.getVertexArray()))
1654         {
1655             OSG_DEBUG << "MergeGeometry: vertex array not merged. Some data may be lost." <<std::endl;
1656         }
1657     }
1658     else if (rhs.getVertexArray())
1659     {
1660         base = 0;
1661         lhs.setVertexArray(rhs.getVertexArray());
1662     }
1663 
1664 
1665     if (lhs.getNormalArray() && rhs.getNormalArray() && lhs.getNormalArray()->getBinding()!=osg::Array::BIND_OVERALL)
1666     {
1667         if (lhs.getNormalArray()->referenceCount() > 1)
1668             lhs.setNormalArray(cloneArray(lhs.getNormalArray(), vbo, &lhs));
1669         if (!merger.merge(lhs.getNormalArray(),rhs.getNormalArray()))
1670         {
1671             OSG_DEBUG << "MergeGeometry: normal array not merged. Some data may be lost." <<std::endl;
1672         }
1673     }
1674     else if (rhs.getNormalArray())
1675     {
1676         lhs.setNormalArray(rhs.getNormalArray());
1677     }
1678 
1679 
1680     if (lhs.getColorArray() && rhs.getColorArray() && lhs.getColorArray()->getBinding()!=osg::Array::BIND_OVERALL)
1681     {
1682         if (lhs.getColorArray()->referenceCount() > 1)
1683             lhs.setColorArray(cloneArray(lhs.getColorArray(), vbo, &lhs));
1684         if (!merger.merge(lhs.getColorArray(),rhs.getColorArray()))
1685         {
1686             OSG_DEBUG << "MergeGeometry: color array not merged. Some data may be lost." <<std::endl;
1687         }
1688     }
1689     else if (rhs.getColorArray())
1690     {
1691         lhs.setColorArray(rhs.getColorArray());
1692     }
1693 
1694     if (lhs.getSecondaryColorArray() && rhs.getSecondaryColorArray() && lhs.getSecondaryColorArray()->getBinding()!=osg::Array::BIND_OVERALL)
1695     {
1696         if (lhs.getSecondaryColorArray()->referenceCount() > 1)
1697             lhs.setSecondaryColorArray(cloneArray(lhs.getSecondaryColorArray(), vbo, &lhs));
1698         if (!merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray()))
1699         {
1700             OSG_DEBUG << "MergeGeometry: secondary color array not merged. Some data may be lost." <<std::endl;
1701         }
1702     }
1703     else if (rhs.getSecondaryColorArray())
1704     {
1705         lhs.setSecondaryColorArray(rhs.getSecondaryColorArray());
1706     }
1707 
1708     if (lhs.getFogCoordArray() && rhs.getFogCoordArray() && lhs.getFogCoordArray()->getBinding()!=osg::Array::BIND_OVERALL)
1709     {
1710         if (lhs.getFogCoordArray()->referenceCount() > 1)
1711             lhs.setFogCoordArray(cloneArray(lhs.getFogCoordArray(), vbo, &lhs));
1712         if (!merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray()))
1713         {
1714             OSG_DEBUG << "MergeGeometry: fog coord array not merged. Some data may be lost." <<std::endl;
1715         }
1716     }
1717     else if (rhs.getFogCoordArray())
1718     {
1719         lhs.setFogCoordArray(rhs.getFogCoordArray());
1720     }
1721 
1722 
1723     unsigned int unit;
1724     for(unit=0;unit<lhs.getNumTexCoordArrays();++unit)
1725     {
1726         if (!lhs.getTexCoordArray(unit)) continue;
1727         if (lhs.getTexCoordArray(unit)->referenceCount() > 1)
1728             lhs.setTexCoordArray(unit, cloneArray(lhs.getTexCoordArray(unit), vbo, &lhs));
1729         if (!merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit)))
1730         {
1731             OSG_DEBUG << "MergeGeometry: tex coord array not merged. Some data may be lost." <<std::endl;
1732         }
1733     }
1734 
1735     for(unit=0;unit<lhs.getNumVertexAttribArrays();++unit)
1736     {
1737         if (!lhs.getVertexAttribArray(unit)) continue;
1738         if (lhs.getVertexAttribArray(unit)->referenceCount() > 1)
1739             lhs.setVertexAttribArray(unit, cloneArray(lhs.getVertexAttribArray(unit), vbo, &lhs));
1740         if (!merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit)))
1741         {
1742             OSG_DEBUG << "MergeGeometry: vertex attrib array not merged. Some data may be lost." <<std::endl;
1743         }
1744     }
1745 
1746     // shift the indices of the incoming primitives to account for the pre existing geometry.
1747     osg::ElementBufferObject* ebo = nullptr;
1748     osg::Geometry::PrimitiveSetList::iterator primItr;
1749     for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr)
1750     {
1751         osg::PrimitiveSet* primitive = primItr->get();
1752 
1753         switch(primitive->getType())
1754         {
1755         case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):
1756             {
1757                 osg::DrawElementsUByte* primitiveUByte = static_cast<osg::DrawElementsUByte*>(primitive);
1758                 unsigned int currentMaximum = 0;
1759                 for(osg::DrawElementsUByte::iterator eitr=primitiveUByte->begin();
1760                     eitr!=primitiveUByte->end();
1761                     ++eitr)
1762                 {
1763                     currentMaximum = osg::maximum(currentMaximum,(unsigned int)*eitr);
1764                 }
1765                 if ((base+currentMaximum)>=65536)
1766                 {
1767                     // must promote to a DrawElementsUInt
1768                     osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode());
1769                     if (needvbo(&lhs))
1770                     {
1771                         if (!ebo) ebo = new osg::ElementBufferObject;
1772                          new_primitive->setElementBufferObject(ebo);
1773                     }
1774                     std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive));
1775                     new_primitive->offsetIndices(base);
1776                     (*primItr) = new_primitive;
1777                 } else if ((base+currentMaximum)>=256)
1778                 {
1779                     // must promote to a DrawElementsUShort
1780                     osg::DrawElementsUShort* new_primitive = new osg::DrawElementsUShort(primitive->getMode());
1781                     if (needvbo(&lhs))
1782                     {
1783                         if (!ebo) ebo = new osg::ElementBufferObject;
1784                          new_primitive->setElementBufferObject(ebo);
1785                     }
1786                     std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive));
1787                     new_primitive->offsetIndices(base);
1788                     (*primItr) = new_primitive;
1789                 }
1790                 else
1791                 {
1792                     (*primItr) = clonePrimitive(primitive, ebo, &lhs);
1793                     (*primItr)->offsetIndices(base);
1794                 }
1795             }
1796             break;
1797 
1798         case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):
1799             {
1800                 osg::DrawElementsUShort* primitiveUShort = static_cast<osg::DrawElementsUShort*>(primitive);
1801                 unsigned int currentMaximum = 0;
1802                 for(osg::DrawElementsUShort::iterator eitr=primitiveUShort->begin();
1803                     eitr!=primitiveUShort->end();
1804                     ++eitr)
1805                 {
1806                     currentMaximum = osg::maximum(currentMaximum,(unsigned int)*eitr);
1807                 }
1808                 if ((base+currentMaximum)>=65536)
1809                 {
1810                     // must promote to a DrawElementsUInt
1811                     osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode());
1812                     if (needvbo(&lhs))
1813                     {
1814                         if (!ebo) ebo = new osg::ElementBufferObject;
1815                          new_primitive->setElementBufferObject(ebo);
1816                     }
1817                     std::copy(primitiveUShort->begin(),primitiveUShort->end(),std::back_inserter(*new_primitive));
1818                     new_primitive->offsetIndices(base);
1819                     (*primItr) = new_primitive;
1820                 }
1821                 else
1822                 {
1823                     (*primItr) = clonePrimitive(primitive, ebo, &lhs);
1824                     (*primItr)->offsetIndices(base);
1825                 }
1826             }
1827             break;
1828 
1829         case(osg::PrimitiveSet::DrawArraysPrimitiveType):
1830         case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):
1831         case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):
1832         default:
1833             (*primItr) = clonePrimitive(primitive, ebo, &lhs);
1834             (*primItr)->offsetIndices(base);
1835             break;
1836         }
1837     }
1838 
1839     for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr)
1840     {
1841         lhs.addPrimitiveSet(primItr->get());
1842     }
1843 
1844     lhs.dirtyBound();
1845     lhs.dirtyDisplayList();
1846 
1847     if (osg::UserDataContainer* rhsUserData = rhs.getUserDataContainer())
1848         for (unsigned int i=0; i<rhsUserData->getNumUserObjects(); ++i)
1849             lhs.getOrCreateUserDataContainer()->addUserObject(rhsUserData->getUserObject(i));
1850 
1851     return true;
1852 }
1853 
mergePrimitive(osg::DrawArrays & lhs,osg::DrawArrays & rhs)1854 bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs)
1855 {
1856     if (lhs.getFirst()+lhs.getCount()==rhs.getFirst())
1857     {
1858         lhs.setCount(lhs.getCount()+rhs.getCount());
1859         return true;
1860     }
1861     return false;
1862 }
1863 
mergePrimitive(osg::DrawArrayLengths & lhs,osg::DrawArrayLengths & rhs)1864 bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawArrayLengths& lhs,osg::DrawArrayLengths& rhs)
1865 {
1866     int lhs_count = std::accumulate(lhs.begin(),lhs.end(),0);
1867 
1868     if (lhs.getFirst()+lhs_count==rhs.getFirst())
1869     {
1870         lhs.insert(lhs.end(),rhs.begin(),rhs.end());
1871         return true;
1872     }
1873     return false;
1874 }
1875 
mergePrimitive(osg::DrawElementsUByte & lhs,osg::DrawElementsUByte & rhs)1876 bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUByte& lhs,osg::DrawElementsUByte& rhs)
1877 {
1878     lhs.insert(lhs.end(),rhs.begin(),rhs.end());
1879     return true;
1880 }
1881 
mergePrimitive(osg::DrawElementsUShort & lhs,osg::DrawElementsUShort & rhs)1882 bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUShort& lhs,osg::DrawElementsUShort& rhs)
1883 {
1884     lhs.insert(lhs.end(),rhs.begin(),rhs.end());
1885     return true;
1886 }
1887 
mergePrimitive(osg::DrawElementsUInt & lhs,osg::DrawElementsUInt & rhs)1888 bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUInt& lhs,osg::DrawElementsUInt& rhs)
1889 {
1890     lhs.insert(lhs.end(),rhs.begin(),rhs.end());
1891     return true;
1892 }
1893 
1894 
1895 
isOperationPermissible(osg::Group & node)1896 bool Optimizer::MergeGroupsVisitor::isOperationPermissible(osg::Group& node)
1897 {
1898     return !node.getCullCallback() &&
1899            !node.getEventCallback() &&
1900            !node.getUpdateCallback() &&
1901             isOperationPermissibleForObject(&node) &&
1902            typeid(node)==typeid(osg::Group);
1903 }
1904 
apply(osg::LOD & lod)1905 void Optimizer::MergeGroupsVisitor::apply(osg::LOD &lod)
1906 {
1907     // don't merge the direct children of the LOD because they are used to define each LOD level.
1908     traverse(lod);
1909 }
1910 
apply(osg::Switch & switchNode)1911 void Optimizer::MergeGroupsVisitor::apply(osg::Switch &switchNode)
1912 {
1913     // We should keep all switch child nodes since they reflect different switch states.
1914     traverse(switchNode);
1915 }
1916 
apply(osg::Group & group)1917 void Optimizer::MergeGroupsVisitor::apply(osg::Group &group)
1918 {
1919     if (group.getNumChildren() <= 1)
1920         traverse(group);
1921     else
1922     {
1923         typedef std::map<osg::StateSet*, std::set<osg::Group*> > GroupMap;
1924         GroupMap childGroups;
1925         for (unsigned int i=0; i<group.getNumChildren(); ++i)
1926         {
1927             osg::Node* child = group.getChild(i);
1928             osg::Group* childGroup = child->asGroup();
1929             if (childGroup && isOperationPermissible(*childGroup))
1930             {
1931                 childGroups[childGroup->getStateSet()].insert(childGroup);
1932             }
1933         }
1934 
1935         for (GroupMap::iterator it = childGroups.begin(); it != childGroups.end(); ++it)
1936         {
1937             const std::set<osg::Group*>& groupSet = it->second;
1938             if (groupSet.size() <= 1)
1939                 continue;
1940             else
1941             {
1942                 osg::Group* first = *groupSet.begin();
1943                 for (std::set<osg::Group*>::const_iterator groupIt = ++groupSet.begin(); groupIt != groupSet.end(); ++groupIt)
1944                 {
1945                     osg::Group* toMerge = *groupIt;
1946                     for (unsigned int i=0; i<toMerge->getNumChildren(); ++i)
1947                         first->addChild(toMerge->getChild(i));
1948                     toMerge->removeChildren(0, toMerge->getNumChildren());
1949                     group.removeChild(toMerge);
1950                 }
1951             }
1952         }
1953         traverse(group);
1954     }
1955 }
1956 
1957 }
1958