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