root/OpenSceneGraph/branches/OpenSceneGraph-2.8/src/osgUtil/Optimizer.cpp @ 9702

Revision 9702, 155.4 kB (checked in by robert, 6 years ago)

Added merge.setting of StateSet? during replacement of Transforms in FlattenStaticTransfrom? and CombineAdjacentTransfroms?

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
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#include <stdlib.h>
14#include <string.h>
15
16#include <osgUtil/Optimizer>
17
18#include <osg/ApplicationUsage>
19#include <osg/Transform>
20#include <osg/MatrixTransform>
21#include <osg/PositionAttitudeTransform>
22#include <osg/LOD>
23#include <osg/Billboard>
24#include <osg/Geometry>
25#include <osg/Notify>
26#include <osg/OccluderNode>
27#include <osg/Sequence>
28#include <osg/Switch>
29#include <osg/Texture>
30#include <osg/PagedLOD>
31#include <osg/ProxyNode>
32#include <osg/ImageStream>
33#include <osg/Timer>
34#include <osg/TexMat>
35#include <osg/io_utils>
36
37#include <osgUtil/TransformAttributeFunctor>
38#include <osgUtil/TriStripVisitor>
39#include <osgUtil/Tessellator>
40#include <osgUtil/Statistics>
41
42#include <typeinfo>
43#include <algorithm>
44#include <numeric>
45#include <sstream>
46
47using namespace osgUtil;
48
49
50void Optimizer::reset()
51{
52}
53
54static osg::ApplicationUsageProxy Optimizer_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_OPTIMIZER \"<type> [<type>]\"","OFF | DEFAULT | FLATTEN_STATIC_TRANSFORMS | FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS | REMOVE_REDUNDANT_NODES | COMBINE_ADJACENT_LODS | SHARE_DUPLICATE_STATE | MERGE_GEOMETRY | MERGE_GEODES | SPATIALIZE_GROUPS  | COPY_SHARED_NODES  | TRISTRIP_GEOMETRY | OPTIMIZE_TEXTURE_SETTINGS | REMOVE_LOADED_PROXY_NODES | TESSELLATE_GEOMETRY | CHECK_GEOMETRY |  FLATTEN_BILLBOARDS | TEXTURE_ATLAS_BUILDER | STATIC_OBJECT_DETECTION");
55
56void Optimizer::optimize(osg::Node* node)
57{
58    unsigned int options = 0;
59   
60   
61    const char* env = getenv("OSG_OPTIMIZER");
62    if (env)
63    {
64        std::string str(env);
65
66        if(str.find("OFF")!=std::string::npos) options = 0;
67
68        if(str.find("~DEFAULT")!=std::string::npos) options ^= DEFAULT_OPTIMIZATIONS;
69        else if(str.find("DEFAULT")!=std::string::npos) options |= DEFAULT_OPTIMIZATIONS;
70
71        if(str.find("~FLATTEN_STATIC_TRANSFORMS")!=std::string::npos) options ^= FLATTEN_STATIC_TRANSFORMS;
72        else if(str.find("FLATTEN_STATIC_TRANSFORMS")!=std::string::npos) options |= FLATTEN_STATIC_TRANSFORMS;
73
74        if(str.find("~FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS")!=std::string::npos) options ^= FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS;
75        else if(str.find("FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS")!=std::string::npos) options |= FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS;
76
77        if(str.find("~REMOVE_REDUNDANT_NODES")!=std::string::npos) options ^= REMOVE_REDUNDANT_NODES;
78        else if(str.find("REMOVE_REDUNDANT_NODES")!=std::string::npos) options |= REMOVE_REDUNDANT_NODES;
79
80        if(str.find("~REMOVE_LOADED_PROXY_NODES")!=std::string::npos) options ^= REMOVE_LOADED_PROXY_NODES;
81        else if(str.find("REMOVE_LOADED_PROXY_NODES")!=std::string::npos) options |= REMOVE_LOADED_PROXY_NODES;
82
83        if(str.find("~COMBINE_ADJACENT_LODS")!=std::string::npos) options ^= COMBINE_ADJACENT_LODS;
84        else if(str.find("COMBINE_ADJACENT_LODS")!=std::string::npos) options |= COMBINE_ADJACENT_LODS;
85
86        if(str.find("~SHARE_DUPLICATE_STATE")!=std::string::npos) options ^= SHARE_DUPLICATE_STATE;
87        else if(str.find("SHARE_DUPLICATE_STATE")!=std::string::npos) options |= SHARE_DUPLICATE_STATE;
88
89        if(str.find("~MERGE_GEODES")!=std::string::npos) options ^= MERGE_GEODES;
90        else if(str.find("MERGE_GEODES")!=std::string::npos) options |= MERGE_GEODES;
91
92        if(str.find("~MERGE_GEOMETRY")!=std::string::npos) options ^= MERGE_GEOMETRY;
93        else if(str.find("MERGE_GEOMETRY")!=std::string::npos) options |= MERGE_GEOMETRY;
94
95        if(str.find("~SPATIALIZE_GROUPS")!=std::string::npos) options ^= SPATIALIZE_GROUPS;
96        else if(str.find("SPATIALIZE_GROUPS")!=std::string::npos) options |= SPATIALIZE_GROUPS;
97
98        if(str.find("~COPY_SHARED_NODES")!=std::string::npos) options ^= COPY_SHARED_NODES;
99        else if(str.find("COPY_SHARED_NODES")!=std::string::npos) options |= COPY_SHARED_NODES;
100
101        if(str.find("~TESSELLATE_GEOMETRY")!=std::string::npos) options ^= TESSELLATE_GEOMETRY;
102        else if(str.find("TESSELLATE_GEOMETRY")!=std::string::npos) options |= TESSELLATE_GEOMETRY;
103
104        if(str.find("~TRISTRIP_GEOMETRY")!=std::string::npos) options ^= TRISTRIP_GEOMETRY;
105        else if(str.find("TRISTRIP_GEOMETRY")!=std::string::npos) options |= TRISTRIP_GEOMETRY;
106
107        if(str.find("~OPTIMIZE_TEXTURE_SETTINGS")!=std::string::npos) options ^= OPTIMIZE_TEXTURE_SETTINGS;
108        else if(str.find("OPTIMIZE_TEXTURE_SETTINGS")!=std::string::npos) options |= OPTIMIZE_TEXTURE_SETTINGS;
109
110        if(str.find("~CHECK_GEOMETRY")!=std::string::npos) options ^= CHECK_GEOMETRY;
111        else if(str.find("CHECK_GEOMETRY")!=std::string::npos) options |= CHECK_GEOMETRY;
112       
113        if(str.find("~FLATTEN_BILLBOARDS")!=std::string::npos) options ^= FLATTEN_BILLBOARDS;
114        else if(str.find("FLATTEN_BILLBOARDS")!=std::string::npos) options |= FLATTEN_BILLBOARDS;
115       
116        if(str.find("~TEXTURE_ATLAS_BUILDER")!=std::string::npos) options ^= TEXTURE_ATLAS_BUILDER;
117        else if(str.find("TEXTURE_ATLAS_BUILDER")!=std::string::npos) options |= TEXTURE_ATLAS_BUILDER;
118       
119        if(str.find("~STATIC_OBJECT_DETECTION")!=std::string::npos) options ^= STATIC_OBJECT_DETECTION;
120        else if(str.find("STATIC_OBJECT_DETECTION")!=std::string::npos) options |= STATIC_OBJECT_DETECTION;
121
122    }
123    else
124    {
125        options = DEFAULT_OPTIMIZATIONS;
126    }
127
128    optimize(node,options);
129
130}
131
132void Optimizer::optimize(osg::Node* node, unsigned int options)
133{
134    StatsVisitor stats;
135   
136    if (osg::getNotifyLevel()>=osg::INFO)
137    {
138        node->accept(stats);
139        stats.totalUpStats();
140        osg::notify(osg::NOTICE)<<std::endl<<"Stats before:"<<std::endl;
141        stats.print(osg::notify(osg::NOTICE));
142    }
143
144    if (options & STATIC_OBJECT_DETECTION)
145    {
146        StaticObjectDetectionVisitor sodv;
147        node->accept(sodv);
148    }
149
150    if (options & TESSELLATE_GEOMETRY)
151    {
152        osg::notify(osg::INFO)<<"Optimizer::optimize() doing TESSELLATE_GEOMETRY"<<std::endl;
153
154        TessellateVisitor tsv;
155        node->accept(tsv);       
156    }
157   
158    if (options & REMOVE_LOADED_PROXY_NODES)
159    {
160        osg::notify(osg::INFO)<<"Optimizer::optimize() doing REMOVE_LOADED_PROXY_NODES"<<std::endl;
161
162        RemoveLoadedProxyNodesVisitor rlpnv(this);
163        node->accept(rlpnv);
164        rlpnv.removeRedundantNodes();
165
166    }
167
168    if (options & COMBINE_ADJACENT_LODS)
169    {
170        osg::notify(osg::INFO)<<"Optimizer::optimize() doing COMBINE_ADJACENT_LODS"<<std::endl;
171
172        CombineLODsVisitor clv(this);
173        node->accept(clv);       
174        clv.combineLODs();
175    }
176   
177    if (options & OPTIMIZE_TEXTURE_SETTINGS)
178    {
179        osg::notify(osg::INFO)<<"Optimizer::optimize() doing OPTIMIZE_TEXTURE_SETTINGS"<<std::endl;
180
181        TextureVisitor tv(true,true, // unref image
182                          false,false, // client storage
183                          false,1.0, // anisotropic filtering
184                          this );
185        node->accept(tv);
186    }
187
188    if (options & SHARE_DUPLICATE_STATE)
189    {
190        osg::notify(osg::INFO)<<"Optimizer::optimize() doing SHARE_DUPLICATE_STATE"<<std::endl;
191
192        bool combineDynamicState = false;
193        bool combineStaticState = true;
194        bool combineUnspecifiedState = true;
195
196        StateVisitor osv(combineDynamicState, combineStaticState, combineUnspecifiedState, this);
197        node->accept(osv);
198        osv.optimize();
199    }
200   
201    if (options & TEXTURE_ATLAS_BUILDER)
202    {
203        osg::notify(osg::INFO)<<"Optimizer::optimize() doing TEXTURE_ATLAS_BUILDER"<<std::endl;
204       
205        // traverse the scene collecting textures into texture atlas.
206        TextureAtlasVisitor tav(this);
207        node->accept(tav);
208        tav.optimize();
209
210        // now merge duplicate state, that may have been introduced by merge textures into texture atlas'
211        bool combineDynamicState = false;
212        bool combineStaticState = true;
213        bool combineUnspecifiedState = true;
214
215        StateVisitor osv(combineDynamicState, combineStaticState, combineUnspecifiedState, this);
216        node->accept(osv);
217        osv.optimize();
218    }
219   
220    if (options & COPY_SHARED_NODES)
221    {
222        osg::notify(osg::INFO)<<"Optimizer::optimize() doing COPY_SHARED_NODES"<<std::endl;
223
224        CopySharedSubgraphsVisitor cssv(this);
225        node->accept(cssv);
226        cssv.copySharedNodes();
227    }
228   
229    if (options & FLATTEN_STATIC_TRANSFORMS)
230    {
231        osg::notify(osg::INFO)<<"Optimizer::optimize() doing FLATTEN_STATIC_TRANSFORMS"<<std::endl;
232
233        int i=0;
234        bool result = false;
235        do
236        {
237            osg::notify(osg::DEBUG_INFO) << "** RemoveStaticTransformsVisitor *** Pass "<<i<<std::endl;
238            FlattenStaticTransformsVisitor fstv(this);
239            node->accept(fstv);
240            result = fstv.removeTransforms(node);
241            ++i;
242        } while (result);
243
244        // no combine any adjacent static transforms.
245        CombineStaticTransformsVisitor cstv(this);
246        node->accept(cstv);
247        cstv.removeTransforms(node);
248    }
249
250    if (options & FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS)
251    {
252        osg::notify(osg::INFO)<<"Optimizer::optimize() doing FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS"<<std::endl;
253
254        // no combine any adjacent static transforms.
255        FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor fstdssv(this);
256        node->accept(fstdssv);
257
258    }
259
260    if (options & MERGE_GEODES)
261    {
262        osg::notify(osg::INFO)<<"Optimizer::optimize() doing MERGE_GEODES"<<std::endl;
263
264        osg::Timer_t startTick = osg::Timer::instance()->tick();
265
266        MergeGeodesVisitor visitor;
267        node->accept(visitor);
268
269        osg::Timer_t endTick = osg::Timer::instance()->tick();
270       
271        osg::notify(osg::INFO)<<"MERGE_GEODES took "<<osg::Timer::instance()->delta_s(startTick,endTick)<<std::endl;
272    }
273
274    if (options & CHECK_GEOMETRY)
275    {
276        osg::notify(osg::INFO)<<"Optimizer::optimize() doing CHECK_GEOMETRY"<<std::endl;
277
278        CheckGeometryVisitor mgv(this);
279        node->accept(mgv);
280    }
281
282    if (options & MERGE_GEOMETRY)
283    {
284        osg::notify(osg::INFO)<<"Optimizer::optimize() doing MERGE_GEOMETRY"<<std::endl;
285
286        osg::Timer_t startTick = osg::Timer::instance()->tick();
287
288        MergeGeometryVisitor mgv(this);
289        mgv.setTargetMaximumNumberOfVertices(10000);
290        node->accept(mgv);
291
292        osg::Timer_t endTick = osg::Timer::instance()->tick();
293
294        osg::notify(osg::INFO)<<"MERGE_GEOMETRY took "<<osg::Timer::instance()->delta_s(startTick,endTick)<<std::endl;
295    }
296   
297    if (options & TRISTRIP_GEOMETRY)
298    {
299        osg::notify(osg::INFO)<<"Optimizer::optimize() doing TRISTRIP_GEOMETRY"<<std::endl;
300
301        TriStripVisitor tsv(this);
302        node->accept(tsv);
303        tsv.stripify();
304    }
305
306    if (options & REMOVE_REDUNDANT_NODES)
307    {
308        osg::notify(osg::INFO)<<"Optimizer::optimize() doing REMOVE_REDUNDANT_NODES"<<std::endl;
309
310        RemoveEmptyNodesVisitor renv(this);
311        node->accept(renv);
312        renv.removeEmptyNodes();
313
314        RemoveRedundantNodesVisitor rrnv(this);
315        node->accept(rrnv);
316        rrnv.removeRedundantNodes();
317
318    }
319   
320    if (options & FLATTEN_BILLBOARDS)
321    {
322        FlattenBillboardVisitor fbv(this);
323        node->accept(fbv);
324        fbv.process();
325    }
326
327    if (options & SPATIALIZE_GROUPS)
328    {
329        osg::notify(osg::INFO)<<"Optimizer::optimize() doing SPATIALIZE_GROUPS"<<std::endl;
330
331        SpatializeGroupsVisitor sv(this);
332        node->accept(sv);
333        sv.divide();
334    }
335
336    if (osg::getNotifyLevel()>=osg::INFO)
337    {
338        stats.reset();
339        node->accept(stats);
340        stats.totalUpStats();
341        osg::notify(osg::NOTICE)<<std::endl<<"Stats after:"<<std::endl;
342        stats.print(osg::notify(osg::NOTICE));
343    }
344}
345
346
347////////////////////////////////////////////////////////////////////////////
348// Tessellate geometry - eg break complex POLYGONS into triangles, strips, fans..
349////////////////////////////////////////////////////////////////////////////
350void Optimizer::TessellateVisitor::apply(osg::Geode& geode)
351{
352    for(unsigned int i=0;i<geode.getNumDrawables();++i)
353    {
354        osg::Geometry* geom = dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
355        if (geom) {
356            osgUtil::Tessellator Tessellator;
357            Tessellator.retessellatePolygons(*geom);
358        }
359    }
360    traverse(geode);
361}
362
363
364////////////////////////////////////////////////////////////////////////////
365// Optimize State Visitor
366////////////////////////////////////////////////////////////////////////////
367
368template<typename T>
369struct LessDerefFunctor
370{
371    bool operator () (const T* lhs,const T* rhs) const
372    {
373        return (*lhs<*rhs);
374    }
375};
376
377struct LessStateSetFunctor
378{
379    bool operator () (const osg::StateSet* lhs,const osg::StateSet* rhs) const
380    {
381        return (*lhs<*rhs);
382    }
383};
384
385
386
387void Optimizer::StateVisitor::reset()
388{
389    _statesets.clear();
390}
391
392void Optimizer::StateVisitor::addStateSet(osg::StateSet* stateset,osg::Object* obj)
393{
394    _statesets[stateset].insert(obj);
395}
396
397void Optimizer::StateVisitor::apply(osg::Node& node)
398{
399   
400    osg::StateSet* ss = node.getStateSet();
401    if (ss && ss->getDataVariance()==osg::Object::STATIC)
402    {
403        if (isOperationPermissibleForObject(&node) &&
404            isOperationPermissibleForObject(ss))
405        {
406            addStateSet(ss,&node);
407        }
408    }
409
410    traverse(node);
411}
412
413void Optimizer::StateVisitor::apply(osg::Geode& geode)
414{
415    if (!isOperationPermissibleForObject(&geode)) return;
416
417    osg::StateSet* ss = geode.getStateSet();
418   
419   
420    if (ss && ss->getDataVariance()==osg::Object::STATIC)
421    {
422        if (isOperationPermissibleForObject(ss))
423        {
424            addStateSet(ss,&geode);
425        }
426    }
427    for(unsigned int i=0;i<geode.getNumDrawables();++i)
428    {
429        osg::Drawable* drawable = geode.getDrawable(i);
430        if (drawable)
431        {
432            ss = drawable->getStateSet();
433            if (ss && ss->getDataVariance()==osg::Object::STATIC)
434            {
435                if (isOperationPermissibleForObject(drawable) &&
436                    isOperationPermissibleForObject(ss))
437                {
438                    addStateSet(ss,drawable);
439                }
440            }
441        }
442    }
443}
444
445void Optimizer::StateVisitor::optimize()
446{
447    osg::notify(osg::INFO) << "Num of StateSet="<<_statesets.size()<< std::endl;
448
449    {
450        // create map from state attributes to stateset which contain them.
451        typedef std::pair<osg::StateSet*,unsigned int>      StateSetUnitPair;
452        typedef std::set<StateSetUnitPair>                  StateSetList;
453        typedef std::map<osg::StateAttribute*,StateSetList> AttributeToStateSetMap;
454        AttributeToStateSetMap attributeToStateSetMap;
455
456        // create map from uniforms to stateset when contain them.
457        typedef std::set<osg::StateSet*>                    StateSetSet;
458        typedef std::map<osg::Uniform*,StateSetSet>         UniformToStateSetMap;
459       
460        const unsigned int NON_TEXTURE_ATTRIBUTE = 0xffffffff;
461       
462        UniformToStateSetMap  uniformToStateSetMap;
463
464        // NOTE - TODO will need to track state attribute override value too.
465
466        for(StateSetMap::iterator sitr=_statesets.begin();
467            sitr!=_statesets.end();
468            ++sitr)
469        {
470            osg::StateSet::AttributeList& attributes = sitr->first->getAttributeList();
471            for(osg::StateSet::AttributeList::iterator aitr= attributes.begin();
472                aitr!=attributes.end();
473                ++aitr)
474            {
475                if (optimize(aitr->second.first->getDataVariance()))
476                {
477                    attributeToStateSetMap[aitr->second.first.get()].insert(StateSetUnitPair(sitr->first,NON_TEXTURE_ATTRIBUTE));
478                }
479            }
480
481
482            osg::StateSet::TextureAttributeList& texAttributes = sitr->first->getTextureAttributeList();
483            for(unsigned int unit=0;unit<texAttributes.size();++unit)
484            {
485                osg::StateSet::AttributeList& attributes = texAttributes[unit];
486                for(osg::StateSet::AttributeList::iterator aitr= attributes.begin();
487                    aitr!=attributes.end();
488                    ++aitr)
489                {
490                    if (optimize(aitr->second.first->getDataVariance()))
491                    {
492                        attributeToStateSetMap[aitr->second.first.get()].insert(StateSetUnitPair(sitr->first,unit));
493                    }
494                }
495            }
496
497
498            osg::StateSet::UniformList& uniforms = sitr->first->getUniformList();
499            for(osg::StateSet::UniformList::iterator uitr= uniforms.begin();
500                uitr!=uniforms.end();
501                ++uitr)
502            {
503                if (optimize(uitr->second.first->getDataVariance()))
504                {
505                    uniformToStateSetMap[uitr->second.first.get()].insert(sitr->first);
506                }
507            }
508
509        }
510
511        if (attributeToStateSetMap.size()>=2)
512        {
513            // create unique set of state attribute pointers.
514            typedef std::vector<osg::StateAttribute*> AttributeList;
515            AttributeList attributeList;
516
517            for(AttributeToStateSetMap::iterator aitr=attributeToStateSetMap.begin();
518                aitr!=attributeToStateSetMap.end();
519                ++aitr)
520            {
521                attributeList.push_back(aitr->first);
522            }
523
524            // sort the attributes so that equal attributes sit along side each
525            // other.
526            std::sort(attributeList.begin(),attributeList.end(),LessDerefFunctor<osg::StateAttribute>());
527
528            osg::notify(osg::INFO) << "state attribute list"<< std::endl;
529            for(AttributeList::iterator aaitr = attributeList.begin();
530                aaitr!=attributeList.end();
531                ++aaitr)
532            {
533                osg::notify(osg::INFO) << "    "<<*aaitr << "  "<<(*aaitr)->className()<< std::endl;
534            }
535
536            osg::notify(osg::INFO) << "searching for duplicate attributes"<< std::endl;
537            // find the duplicates.
538            AttributeList::iterator first_unique = attributeList.begin();
539            AttributeList::iterator current = first_unique;
540            ++current;
541            for(; current!=attributeList.end();++current)
542            {
543                if (**current==**first_unique)
544                {
545                    osg::notify(osg::INFO) << "    found duplicate "<<(*current)->className()<<"  first="<<*first_unique<<"  current="<<*current<< std::endl;
546                    StateSetList& statesetlist = attributeToStateSetMap[*current];
547                    for(StateSetList::iterator sitr=statesetlist.begin();
548                        sitr!=statesetlist.end();
549                        ++sitr)
550                    {
551                        osg::notify(osg::INFO) << "       replace duplicate "<<*current<<" with "<<*first_unique<< std::endl;
552                        osg::StateSet* stateset = sitr->first;
553                        unsigned int unit = sitr->second;
554                        if (unit==NON_TEXTURE_ATTRIBUTE) stateset->setAttribute(*first_unique);
555                        else stateset->setTextureAttribute(unit,*first_unique);
556                    }
557                }
558                else first_unique = current;
559            }
560        }
561       
562               
563        if (uniformToStateSetMap.size()>=2)
564        {
565            // create unique set of uniform pointers.
566            typedef std::vector<osg::Uniform*> UniformList;
567            UniformList uniformList;
568
569            for(UniformToStateSetMap::iterator aitr=uniformToStateSetMap.begin();
570                aitr!=uniformToStateSetMap.end();
571                ++aitr)
572            {
573                uniformList.push_back(aitr->first);
574            }
575
576            // sort the uniforms so that equal uniforms sit along side each
577            // other.
578            std::sort(uniformList.begin(),uniformList.end(),LessDerefFunctor<osg::Uniform>());
579
580            osg::notify(osg::INFO) << "state uniform list"<< std::endl;
581            for(UniformList::iterator uuitr = uniformList.begin();
582                uuitr!=uniformList.end();
583                ++uuitr)
584            {
585                osg::notify(osg::INFO) << "    "<<*uuitr << "  "<<(*uuitr)->getName()<< std::endl;
586            }
587
588            osg::notify(osg::INFO) << "searching for duplicate uniforms"<< std::endl;
589            // find the duplicates.
590            UniformList::iterator first_unique_uniform = uniformList.begin();
591            UniformList::iterator current_uniform = first_unique_uniform;
592            ++current_uniform;
593            for(; current_uniform!=uniformList.end();++current_uniform)
594            {
595                if ((**current_uniform)==(**first_unique_uniform))
596                {
597                    osg::notify(osg::INFO) << "    found duplicate uniform "<<(*current_uniform)->getName()<<"  first_unique_uniform="<<*first_unique_uniform<<"  current_uniform="<<*current_uniform<< std::endl;
598                    StateSetSet& statesetset = uniformToStateSetMap[*current_uniform];
599                    for(StateSetSet::iterator sitr=statesetset.begin();
600                        sitr!=statesetset.end();
601                        ++sitr)
602                    {
603                        osg::notify(osg::INFO) << "       replace duplicate "<<*current_uniform<<" with "<<*first_unique_uniform<< std::endl;
604                        osg::StateSet* stateset = *sitr;
605                        stateset->addUniform(*first_unique_uniform);
606                    }
607                }
608                else first_unique_uniform = current_uniform;
609            }
610        }
611
612    }
613   
614    // duplicate state attributes removed.
615    // now need to look at duplicate state sets.
616    if (_statesets.size()>=2)
617    {
618        // create the list of stateset's.
619        typedef std::vector<osg::StateSet*> StateSetSortList;
620        StateSetSortList statesetSortList;
621        for(StateSetMap::iterator ssitr=_statesets.begin();
622            ssitr!=_statesets.end();
623            ++ssitr)
624        {
625            statesetSortList.push_back(ssitr->first);
626        }
627
628
629        // sort the StateSet's so that equal StateSet's sit along side each
630        // other.
631        std::sort(statesetSortList.begin(),statesetSortList.end(),LessDerefFunctor<osg::StateSet>());
632
633        osg::notify(osg::INFO) << "searching for duplicate attributes"<< std::endl;
634        // find the duplicates.
635        StateSetSortList::iterator first_unique = statesetSortList.begin();
636        StateSetSortList::iterator current = first_unique; ++current;
637        for(; current!=statesetSortList.end();++current)
638        {
639            if (**current==**first_unique)
640            {
641                osg::notify(osg::INFO) << "    found duplicate "<<(*current)->className()<<"  first="<<*first_unique<<"  current="<<*current<< std::endl;
642                ObjectSet& objSet = _statesets[*current];
643                for(ObjectSet::iterator sitr=objSet.begin();
644                    sitr!=objSet.end();
645                    ++sitr)
646                {
647                    osg::notify(osg::INFO) << "       replace duplicate "<<*current<<" with "<<*first_unique<< std::endl;
648                    osg::Object* obj = *sitr;
649                    osg::Drawable* drawable = dynamic_cast<osg::Drawable*>(obj);
650                    if (drawable)
651                    {
652                        drawable->setStateSet(*first_unique);
653                    }
654                    else 
655                    {
656                        osg::Node* node = dynamic_cast<osg::Node*>(obj);
657                        if (node)
658                        {
659                            node->setStateSet(*first_unique);
660                        }
661                    }
662                }
663            }
664            else first_unique = current;
665        }
666    }
667   
668}
669
670////////////////////////////////////////////////////////////////////////////
671// Flatten static transforms
672////////////////////////////////////////////////////////////////////////////
673
674class CollectLowestTransformsVisitor : public BaseOptimizerVisitor
675{
676    public:
677
678
679        CollectLowestTransformsVisitor(Optimizer* optimizer=0):
680                    BaseOptimizerVisitor(optimizer,Optimizer::FLATTEN_STATIC_TRANSFORMS),
681                    _transformFunctor(osg::Matrix())
682        {
683            setTraversalMode(osg::NodeVisitor::TRAVERSE_PARENTS);
684        }
685
686        virtual void apply(osg::Node& node)
687        {
688            if (node.getNumParents())
689            {
690                traverse(node);
691            }
692            else
693            {
694                // for all current objects mark a NULL transform for them.
695                registerWithCurrentObjects(0);
696            }
697        }
698
699        virtual void apply(osg::LOD& lod)
700        {
701            _currentObjectList.push_back(&lod);
702
703            traverse(lod);
704
705            _currentObjectList.pop_back();
706        }
707
708        virtual void apply(osg::Transform& transform)
709        {
710            // for all current objects associated this transform with them.
711            registerWithCurrentObjects(&transform);
712        }
713       
714        virtual void apply(osg::Geode& geode)
715        {
716            traverse(geode);
717        }
718       
719        virtual void apply(osg::Billboard& geode)
720        {
721            traverse(geode);
722        }
723       
724
725        void collectDataFor(osg::Node* node)
726        {
727            _currentObjectList.push_back(node);
728           
729            node->accept(*this);
730           
731            _currentObjectList.pop_back();
732        }
733
734        void collectDataFor(osg::Billboard* billboard)
735        {
736            _currentObjectList.push_back(billboard);
737           
738            billboard->accept(*this);
739           
740            _currentObjectList.pop_back();
741        }
742       
743        void collectDataFor(osg::Drawable* drawable)
744        {
745            _currentObjectList.push_back(drawable);
746
747            const osg::Drawable::ParentList& parents = drawable->getParents();
748            for(osg::Drawable::ParentList::const_iterator itr=parents.begin();
749                itr!=parents.end();
750                ++itr)
751            {
752                (*itr)->accept(*this);
753            }
754
755            _currentObjectList.pop_back();
756        }
757
758        void setUpMaps();
759        void disableTransform(osg::Transform* transform);
760        bool removeTransforms(osg::Node* nodeWeCannotRemove);
761
762        inline bool isOperationPermissibleForObject(const osg::Object* object) const
763        {
764            const osg::Drawable* drawable = dynamic_cast<const osg::Drawable*>(object);
765            if (drawable) return isOperationPermissibleForObject(drawable);
766           
767            const osg::Node* node = dynamic_cast<const osg::Node*>(object);
768            if (node) return isOperationPermissibleForObject(node);
769
770            return true;
771        }
772
773        inline bool isOperationPermissibleForObject(const osg::Drawable* drawable) const
774        {
775            // disable if cannot apply transform functor.
776            if (drawable && !drawable->supports(_transformFunctor)) return false;
777            return BaseOptimizerVisitor::isOperationPermissibleForObject(drawable);
778        }
779           
780        inline bool isOperationPermissibleForObject(const osg::Node* node) const
781        {
782            // disable if object is a light point node.
783            if (strcmp(node->className(),"LightPointNode")==0) return false;
784            if (dynamic_cast<const osg::ProxyNode*>(node)) return false;
785            if (dynamic_cast<const osg::PagedLOD*>(node)) return false;
786            return BaseOptimizerVisitor::isOperationPermissibleForObject(node);
787        }
788
789    protected:       
790
791        struct TransformStruct
792        {
793            typedef std::set<osg::Object*> ObjectSet;
794
795            TransformStruct():_canBeApplied(true) {}
796
797            void add(osg::Object* obj)
798            {
799                _objectSet.insert(obj);
800            }
801
802            bool        _canBeApplied;
803            ObjectSet   _objectSet;
804        };
805
806        struct ObjectStruct
807        {
808            typedef std::set<osg::Transform*> TransformSet;
809
810            ObjectStruct():_canBeApplied(true),_moreThanOneMatrixRequired(false) {}
811
812            void add(osg::Transform* transform)
813            {
814                if (transform)
815                {
816                    if (transform->getDataVariance()!=osg::Transform::STATIC) _moreThanOneMatrixRequired=true;
817                    else if (transform->getReferenceFrame()!=osg::Transform::RELATIVE_RF) _moreThanOneMatrixRequired=true;
818                    else
819                    {
820                        if (_transformSet.empty()) transform->computeLocalToWorldMatrix(_firstMatrix,0);
821                        else
822                        {
823                            osg::Matrix matrix;
824                            transform->computeLocalToWorldMatrix(matrix,0);
825                            if (_firstMatrix!=matrix) _moreThanOneMatrixRequired=true;
826                        }
827                    }
828                }
829                else
830                {
831                    if (!_transformSet.empty())
832                    {
833                        if (!_firstMatrix.isIdentity()) _moreThanOneMatrixRequired=true;
834                    }
835
836                }
837                _transformSet.insert(transform);
838            }
839
840            bool            _canBeApplied;
841            bool            _moreThanOneMatrixRequired;
842            osg::Matrix     _firstMatrix;
843            TransformSet    _transformSet;
844        };   
845
846
847        void registerWithCurrentObjects(osg::Transform* transform)
848        {
849            for(ObjectList::iterator itr=_currentObjectList.begin();
850                itr!=_currentObjectList.end();
851                ++itr)
852            {
853                _objectMap[*itr].add(transform);
854            }
855        }
856
857        typedef std::map<osg::Transform*,TransformStruct>   TransformMap;
858        typedef std::map<osg::Object*,ObjectStruct>         ObjectMap;
859        typedef std::vector<osg::Object*>                   ObjectList;
860
861        void disableObject(osg::Object* object)
862        {
863            disableObject(_objectMap.find(object));
864        }
865
866        void disableObject(ObjectMap::iterator itr);
867        void doTransform(osg::Object* obj,osg::Matrix& matrix);
868       
869        osgUtil::TransformAttributeFunctor _transformFunctor;
870        TransformMap    _transformMap;
871        ObjectMap       _objectMap;
872        ObjectList      _currentObjectList;
873
874};
875
876
877void CollectLowestTransformsVisitor::doTransform(osg::Object* obj,osg::Matrix& matrix)
878{
879    osg::Drawable* drawable = dynamic_cast<osg::Drawable*>(obj);
880    if (drawable)
881    {
882        osgUtil::TransformAttributeFunctor tf(matrix);
883        drawable->accept(tf);
884        drawable->dirtyBound();
885        drawable->dirtyDisplayList();
886
887        return;
888    }
889
890    osg::LOD* lod = dynamic_cast<osg::LOD*>(obj);
891    if (lod)
892    {
893        osg::Matrix matrix_no_trans = matrix;
894        matrix_no_trans.setTrans(0.0f,0.0f,0.0f);
895       
896        osg::Vec3 v111(1.0f,1.0f,1.0f);
897        osg::Vec3 new_v111 = v111*matrix_no_trans;
898        float ratio = new_v111.length()/v111.length();
899
900        // move center point.
901        lod->setCenter(lod->getCenter()*matrix);
902       
903        // adjust ranges to new scale.
904        for(unsigned int i=0;i<lod->getNumRanges();++i)
905        {
906            lod->setRange(i,lod->getMinRange(i)*ratio,lod->getMaxRange(i)*ratio);
907        }
908       
909        lod->dirtyBound();
910        return;
911    }
912
913    osg::Billboard* billboard = dynamic_cast<osg::Billboard*>(obj);
914    if (billboard)
915    {
916        osg::Matrix matrix_no_trans = matrix;
917        matrix_no_trans.setTrans(0.0f,0.0f,0.0f);
918 
919        osgUtil::TransformAttributeFunctor tf(matrix_no_trans);
920
921        osg::Vec3 axis = osg::Matrix::transform3x3(tf._im,billboard->getAxis());
922        axis.normalize();
923        billboard->setAxis(axis);
924
925        osg::Vec3 normal = osg::Matrix::transform3x3(tf._im,billboard->getNormal());
926        normal.normalize();
927        billboard->setNormal(normal);
928
929
930        for(unsigned int i=0;i<billboard->getNumDrawables();++i)
931        {
932            billboard->setPosition(i,billboard->getPosition(i)*matrix);
933            billboard->getDrawable(i)->accept(tf);
934            billboard->getDrawable(i)->dirtyBound();
935        }
936       
937        billboard->dirtyBound();
938
939        return;
940    }
941}
942
943void CollectLowestTransformsVisitor::disableObject(ObjectMap::iterator itr)
944{
945    if (itr==_objectMap.end())
946    {
947        return;
948    }   
949
950    if (itr->second._canBeApplied)
951    {
952        // we havn't been disabled yet so we need to disable,
953        itr->second._canBeApplied = false;
954
955        // and then inform everybody we have been disabled.
956        for(ObjectStruct::TransformSet::iterator titr = itr->second._transformSet.begin();
957            titr != itr->second._transformSet.end();
958            ++titr)
959        {
960            disableTransform(*titr);
961        }
962    }
963}
964
965void CollectLowestTransformsVisitor::disableTransform(osg::Transform* transform)
966{
967    TransformMap::iterator itr=_transformMap.find(transform);
968    if (itr==_transformMap.end())
969    {
970        return;
971    }   
972
973    if (itr->second._canBeApplied)
974    {
975   
976        // we havn't been disabled yet so we need to disable,
977        itr->second._canBeApplied = false;
978        // and then inform everybody we have been disabled.
979        for(TransformStruct::ObjectSet::iterator oitr = itr->second._objectSet.begin();
980            oitr != itr->second._objectSet.end();
981            ++oitr)
982        {
983            disableObject(*oitr);
984        }
985    }
986}
987
988void CollectLowestTransformsVisitor::setUpMaps()
989{
990    // create the TransformMap from the ObjectMap
991    ObjectMap::iterator oitr;
992    for(oitr=_objectMap.begin();
993        oitr!=_objectMap.end();
994        ++oitr)
995    {
996        osg::Object* object = oitr->first;
997        ObjectStruct& os = oitr->second;
998       
999        for(ObjectStruct::TransformSet::iterator titr = os._transformSet.begin();
1000            titr != os._transformSet.end();
1001            ++titr)
1002        {
1003            _transformMap[*titr].add(object);
1004        }
1005    }
1006
1007    // disable all the objects which have more than one matrix associated
1008    // with them, and then disable all transforms which have an object associated
1009    // them that can't be applied, and then disable all objects which have
1010    // disabled transforms associated, recursing until all disabled
1011    // associativity.
1012    // and disable all objects that the operation is not permisable for)
1013    for(oitr=_objectMap.begin();
1014        oitr!=_objectMap.end();
1015        ++oitr)
1016    {
1017        osg::Object* object = oitr->first;
1018        ObjectStruct& os = oitr->second;
1019        if (os._canBeApplied)
1020        {
1021            if (os._moreThanOneMatrixRequired || !isOperationPermissibleForObject(object))
1022            {
1023                disableObject(oitr);
1024            }
1025        }
1026    }
1027
1028}
1029
1030bool CollectLowestTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)
1031{
1032    // transform the objects that can be applied.
1033    for(ObjectMap::iterator oitr=_objectMap.begin();
1034        oitr!=_objectMap.end();
1035        ++oitr)
1036    {
1037        osg::Object* object = oitr->first;
1038        ObjectStruct& os = oitr->second;
1039        if (os._canBeApplied)
1040        {
1041            doTransform(object,os._firstMatrix);
1042        }
1043    }
1044
1045
1046    bool transformRemoved = false;
1047
1048    // clean up the transforms.
1049    for(TransformMap::iterator titr=_transformMap.begin();
1050        titr!=_transformMap.end();
1051        ++titr)
1052    {
1053        if (titr->second._canBeApplied)
1054        {
1055            if (titr->first!=nodeWeCannotRemove)
1056            {
1057                transformRemoved = true;
1058
1059                osg::ref_ptr<osg::Transform> transform = titr->first;
1060                osg::ref_ptr<osg::Group>     group = new osg::Group;
1061                group->setName( transform->getName() );
1062                group->setDataVariance(osg::Object::STATIC);
1063                group->setNodeMask(transform->getNodeMask());
1064                group->setStateSet(transform->getStateSet());
1065                for(unsigned int i=0;i<transform->getNumChildren();++i)
1066                {
1067                    for(unsigned int j=0;j<transform->getNumParents();++j)
1068                    {
1069                        group->addChild(transform->getChild(i));
1070                    }
1071                }
1072
1073                for(int i2=transform->getNumParents()-1;i2>=0;--i2)
1074                {
1075                    transform->getParent(i2)->replaceChild(transform.get(),group.get());
1076                }
1077            }
1078            else
1079            {
1080                osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(titr->first);
1081                if (mt) mt->setMatrix(osg::Matrix::identity());
1082                else
1083                {
1084                    osg::PositionAttitudeTransform* pat = dynamic_cast<osg::PositionAttitudeTransform*>(titr->first);
1085                    if (pat)
1086                    {
1087                        pat->setPosition(osg::Vec3(0.0f,0.0f,0.0f));
1088                        pat->setAttitude(osg::Quat());
1089                        pat->setPivotPoint(osg::Vec3(0.0f,0.0f,0.0f));
1090                    }
1091                    else
1092                    {
1093                        osg::notify(osg::WARN)<<"Warning:: during Optimize::CollectLowestTransformsVisitor::removeTransforms(Node*)"<<std::endl;
1094                        osg::notify(osg::WARN)<<"          unhandled of setting of indentity matrix on "<< titr->first->className()<<std::endl;
1095                        osg::notify(osg::WARN)<<"          model will appear in the incorrect position."<<std::endl;
1096                    }
1097                }
1098               
1099            }               
1100        }
1101    }
1102    _objectMap.clear();
1103    _transformMap.clear();
1104   
1105    return transformRemoved;
1106}
1107
1108void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Node& node)
1109{
1110    if (strcmp(node.className(),"LightPointNode")==0)
1111    {
1112        _excludedNodeSet.insert(&node);
1113    }
1114    traverse(node);
1115}
1116
1117
1118void Optimizer::FlattenStaticTransformsVisitor::apply(osg::ProxyNode& node)
1119{
1120    _excludedNodeSet.insert(&node);
1121
1122    traverse(node);
1123}
1124
1125void Optimizer::FlattenStaticTransformsVisitor::apply(osg::PagedLOD& node)
1126{
1127    _excludedNodeSet.insert(&node);
1128
1129    traverse(node);
1130}
1131
1132
1133void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Geode& geode)
1134{
1135    if (!_transformStack.empty())
1136    {
1137        for(unsigned int i=0;i<geode.getNumDrawables();++i)
1138        {
1139            osg::Geometry *geometry = geode.getDrawable(i)->asGeometry();
1140            if((geometry) && (isOperationPermissibleForObject(&geode)) && (isOperationPermissibleForObject(geometry)))
1141            {
1142                if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1) {
1143                    geometry->setVertexArray(dynamic_cast<osg::Array*>(geometry->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)));
1144                }
1145                if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1) {
1146                    geometry->setNormalArray(dynamic_cast<osg::Array*>(geometry->getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL)));
1147                }
1148            }
1149            _drawableSet.insert(geode.getDrawable(i));
1150        }
1151    }
1152}
1153
1154void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Billboard& billboard)
1155{
1156    if (!_transformStack.empty())
1157    {
1158        _billboardSet.insert(&billboard);
1159    }
1160}
1161
1162void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Transform& transform)
1163{
1164    if (!_transformStack.empty())
1165    {
1166        // we need to disable any transform higher in the list.
1167        _transformSet.insert(_transformStack.back());
1168    }
1169
1170    _transformStack.push_back(&transform);
1171
1172    // simple traverse the children as if this Transform didn't exist.
1173    traverse(transform);
1174
1175    _transformStack.pop_back();
1176}
1177
1178bool Optimizer::FlattenStaticTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)
1179{
1180    CollectLowestTransformsVisitor cltv(_optimizer);
1181
1182    for(NodeSet::iterator nitr=_excludedNodeSet.begin();
1183        nitr!=_excludedNodeSet.end();
1184        ++nitr)
1185    {
1186        cltv.collectDataFor(*nitr);
1187    }
1188
1189    for(DrawableSet::iterator ditr=_drawableSet.begin();
1190        ditr!=_drawableSet.end();
1191        ++ditr)
1192    {
1193        cltv.collectDataFor(*ditr);
1194    }
1195
1196    for(BillboardSet::iterator bitr=_billboardSet.begin();
1197        bitr!=_billboardSet.end();
1198        ++bitr)
1199    {
1200        cltv.collectDataFor(*bitr);
1201    }
1202   
1203    cltv.setUpMaps();
1204   
1205    for(TransformSet::iterator titr=_transformSet.begin();
1206        titr!=_transformSet.end();
1207        ++titr)
1208    {
1209        cltv.disableTransform(*titr);
1210    }
1211   
1212
1213    return cltv.removeTransforms(nodeWeCannotRemove);
1214}
1215
1216////////////////////////////////////////////////////////////////////////////
1217// CombineStatucTransforms
1218////////////////////////////////////////////////////////////////////////////
1219
1220void Optimizer::CombineStaticTransformsVisitor::apply(osg::MatrixTransform& transform)
1221{
1222    if (transform.getDataVariance()==osg::Object::STATIC &&
1223        transform.getNumChildren()==1 &&
1224        transform.getChild(0)->asTransform()!=0 &&
1225        transform.getChild(0)->asTransform()->asMatrixTransform()!=0 &&
1226        transform.getChild(0)->asTransform()->getDataVariance()==osg::Object::STATIC &&
1227        isOperationPermissibleForObject(&transform) && isOperationPermissibleForObject(transform.getChild(0)))
1228    {
1229        _transformSet.insert(&transform);
1230    }
1231   
1232    traverse(transform);
1233}
1234
1235bool Optimizer::CombineStaticTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)
1236{
1237    if (nodeWeCannotRemove && nodeWeCannotRemove->asTransform()!=0 && nodeWeCannotRemove->asTransform()->asMatrixTransform()!=0)
1238    {
1239        // remove topmost node if it exists in the transform set.
1240        TransformSet::iterator itr = _transformSet.find(nodeWeCannotRemove->asTransform()->asMatrixTransform());
1241        if (itr!=_transformSet.end()) _transformSet.erase(itr);
1242    }
1243   
1244    bool transformRemoved = false;
1245
1246    while (!_transformSet.empty())
1247    {
1248        // get the first available transform to combine.
1249        osg::ref_ptr<osg::MatrixTransform> transform = *_transformSet.begin();
1250        _transformSet.erase(_transformSet.begin());
1251       
1252        if (transform->getNumChildren()==1 &&
1253            transform->getChild(0)->asTransform()!=0 &&
1254            transform->getChild(0)->asTransform()->asMatrixTransform()!=0 &&
1255            transform->getChild(0)->asTransform()->getDataVariance()==osg::Object::STATIC)
1256        {
1257            // now combine with its child.
1258            osg::MatrixTransform* child = transform->getChild(0)->asTransform()->asMatrixTransform();
1259           
1260            osg::Matrix newMatrix = child->getMatrix()*transform->getMatrix();
1261            child->setMatrix(newMatrix);
1262            if (transform->getStateSet())
1263            {
1264                if(child->getStateSet()) child->getStateSet()->merge(*transform->getStateSet());
1265                else child->setStateSet(transform->getStateSet());
1266            }
1267           
1268            transformRemoved = true;
1269
1270            osg::Node::ParentList parents = transform->getParents();
1271            for(osg::Node::ParentList::iterator pitr=parents.begin();
1272                pitr!=parents.end();
1273                ++pitr)
1274            {
1275                (*pitr)->replaceChild(transform.get(),child);
1276            }
1277           
1278        }
1279       
1280    }
1281    return transformRemoved;
1282}
1283
1284////////////////////////////////////////////////////////////////////////////
1285// RemoveEmptyNodes.
1286////////////////////////////////////////////////////////////////////////////
1287
1288void Optimizer::RemoveEmptyNodesVisitor::apply(osg::Geode& geode)
1289{
1290    for(int i=geode.getNumDrawables()-1;i>=0;--i)
1291    {
1292        osg::Geometry* geom = geode.getDrawable(i)->asGeometry();
1293        if (geom && geom->empty() && isOperationPermissibleForObject(geom))
1294        {
1295           geode.removeDrawables(i,1);
1296        }
1297    }
1298
1299    if (geode.getNumParents()>0)
1300    {
1301        if (geode.getNumDrawables()==0 && isOperationPermissibleForObject(&geode)) _redundantNodeList.insert(&geode);
1302    }
1303}
1304
1305void Optimizer::RemoveEmptyNodesVisitor::apply(osg::Group& group)
1306{
1307    if (group.getNumParents()>0)
1308    {
1309        // only remove empty groups, but not empty occluders.
1310        if (group.getNumChildren()==0 && isOperationPermissibleForObject(&group) &&
1311            (typeid(group)==typeid(osg::Group) || dynamic_cast<osg::Transform*>(&group)))
1312        {
1313            _redundantNodeList.insert(&group);
1314        }
1315    }
1316    traverse(group);
1317}
1318
1319void Optimizer::RemoveEmptyNodesVisitor::removeEmptyNodes()
1320{
1321
1322    NodeList newEmptyGroups;
1323
1324    // keep iterator through until scene graph is cleaned of empty nodes.
1325    while (!_redundantNodeList.empty())
1326    {
1327        for(NodeList::iterator itr=_redundantNodeList.begin();
1328            itr!=_redundantNodeList.end();
1329            ++itr)
1330        {
1331           
1332            osg::ref_ptr<osg::Node> nodeToRemove = (*itr);
1333           
1334            // take a copy of parents list since subsequent removes will modify the original one.
1335            osg::Node::ParentList parents = nodeToRemove->getParents();
1336
1337            for(osg::Node::ParentList::iterator pitr=parents.begin();
1338                pitr!=parents.end();
1339                ++pitr)
1340            {
1341                osg::Group* parent = *pitr;
1342                if (!dynamic_cast<osg::Sequence*>(parent) &&
1343                    !dynamic_cast<osg::Switch*>(parent) &&
1344                    strcmp(parent->className(),"MultiSwitch")!=0)
1345                {
1346                    parent->removeChild(nodeToRemove.get());
1347                    if (parent->getNumChildren()==0) newEmptyGroups.insert(*pitr);
1348                }
1349            }               
1350        }
1351
1352        _redundantNodeList.clear();
1353        _redundantNodeList.swap(newEmptyGroups);
1354    }
1355}
1356
1357
1358////////////////////////////////////////////////////////////////////////////
1359// RemoveRedundantNodes.
1360////////////////////////////////////////////////////////////////////////////
1361
1362bool Optimizer::RemoveRedundantNodesVisitor::isOperationPermissible(osg::Node& node)
1363{
1364    return node.getNumParents()>0 &&
1365           !node.getStateSet() &&
1366           !node.getUserData() &&
1367           !node.getUpdateCallback() &&
1368           !node.getEventCallback() &&
1369           !node.getUpdateCallback() &&
1370           node.getDescriptions().empty() &&
1371           isOperationPermissibleForObject(&node);   
1372}
1373
1374void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Group& group)
1375{
1376    if (group.getNumChildren()==1 &&
1377        typeid(group)==typeid(osg::Group) &&
1378        isOperationPermissible(group))
1379    {
1380        _redundantNodeList.insert(&group);
1381    }
1382
1383    traverse(group);
1384}
1385
1386
1387
1388void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Transform& transform)
1389{
1390    if (transform.getDataVariance()==osg::Object::STATIC &&
1391        isOperationPermissible(transform))
1392    {
1393        osg::Matrix matrix;
1394        transform.computeWorldToLocalMatrix(matrix,NULL);
1395        if (matrix.isIdentity())
1396        {
1397            _redundantNodeList.insert(&transform);
1398        }
1399    }
1400    traverse(transform);
1401}
1402
1403
1404void Optimizer::RemoveRedundantNodesVisitor::removeRedundantNodes()
1405{
1406
1407    for(NodeList::iterator itr=_redundantNodeList.begin();
1408        itr!=_redundantNodeList.end();
1409        ++itr)
1410    {
1411        osg::ref_ptr<osg::Group> group = dynamic_cast<osg::Group*>(*itr);
1412        if (group.valid())
1413        {
1414            // take a copy of parents list since subsequent removes will modify the original one.
1415            osg::Node::ParentList parents = group->getParents();
1416
1417            if (group->getNumChildren()==1)
1418            {
1419                osg::Node* child = group->getChild(0);
1420                for(osg::Node::ParentList::iterator pitr=parents.begin();
1421                    pitr!=parents.end();
1422                    ++pitr)
1423                {
1424                    (*pitr)->replaceChild(group.get(),child);
1425                }
1426
1427            }
1428
1429        }
1430        else
1431        {
1432            osg::notify(osg::WARN)<<"Optimizer::RemoveRedundantNodesVisitor::removeRedundantNodes() - failed dynamic_cast"<<std::endl;
1433        }                               
1434    }
1435    _redundantNodeList.clear();
1436}
1437
1438
1439////////////////////////////////////////////////////////////////////////////
1440// RemoveLoadedProxyNodesVisitor.
1441////////////////////////////////////////////////////////////////////////////
1442
1443void Optimizer::RemoveLoadedProxyNodesVisitor::apply(osg::ProxyNode& proxyNode)
1444{
1445    if (proxyNode.getNumParents()>0 && proxyNode.getNumFileNames()==proxyNode.getNumChildren())
1446    {
1447        if (isOperationPermissibleForObject(&proxyNode))
1448        {
1449            _redundantNodeList.insert(&proxyNode);
1450        }
1451    }
1452    traverse(proxyNode);
1453}
1454
1455void Optimizer::RemoveLoadedProxyNodesVisitor::removeRedundantNodes()
1456{
1457
1458    for(NodeList::iterator itr=_redundantNodeList.begin();
1459        itr!=_redundantNodeList.end();
1460        ++itr)
1461    {
1462        osg::ref_ptr<osg::Group> group = dynamic_cast<osg::Group*>(*itr);
1463        if (group.valid())
1464        {
1465           
1466            // first check to see if data was attached to the ProxyNode that we need to keep.
1467            bool keepData = false;
1468            if (!group->getName().empty()) keepData = true;
1469            if (!group->getDescriptions().empty()) keepData = true;
1470            if (group->getNodeMask()) keepData = true;
1471            if (group->getUpdateCallback()) keepData = true;
1472            if (group->getEventCallback()) keepData = true;
1473            if (group->getCullCallback()) keepData = true;
1474
1475            if (keepData)
1476            {
1477                // create a group to store all proxy's children and data.
1478                osg::ref_ptr<osg::Group> newGroup = new osg::Group(*group,osg::CopyOp::SHALLOW_COPY);
1479               
1480
1481                // take a copy of parents list since subsequent removes will modify the original one.
1482                osg::Node::ParentList parents = group->getParents();
1483
1484                for(osg::Node::ParentList::iterator pitr=parents.begin();
1485                    pitr!=parents.end();
1486                    ++pitr)
1487                {
1488                    (*pitr)->replaceChild(group.get(),newGroup.get());
1489                }
1490
1491            }
1492            else
1493            {
1494                // take a copy of parents list since subsequent removes will modify the original one.
1495                osg::Node::ParentList parents = group->getParents();
1496
1497                for(unsigned int i=0;i<group->getNumChildren();++i)
1498                {
1499                    osg::Node* child = group->getChild(i);
1500                    for(osg::Node::ParentList::iterator pitr=parents.begin();
1501                        pitr!=parents.end();
1502                        ++pitr)
1503                    {
1504                        (*pitr)->replaceChild(group.get(),child);
1505                    }
1506
1507                }
1508            }
1509        }
1510        else
1511        {
1512            osg::notify(osg::WARN)<<"Optimizer::RemoveLoadedProxyNodesVisitor::removeRedundantNodes() - failed dynamic_cast"<<std::endl;
1513        }                               
1514    }
1515    _redundantNodeList.clear();
1516}
1517
1518
1519
1520////////////////////////////////////////////////////////////////////////////
1521// combine LOD's.
1522////////////////////////////////////////////////////////////////////////////
1523void Optimizer::CombineLODsVisitor::apply(osg::LOD& lod)
1524{
1525    if (dynamic_cast<osg::PagedLOD*>(&lod)==0)
1526    {   
1527        for(unsigned int i=0;i<lod.getNumParents();++i)
1528        {
1529            if (typeid(*lod.getParent(i))==typeid(osg::Group))
1530            {
1531                if (isOperationPermissibleForObject(&lod))
1532                {
1533                    _groupList.insert(lod.getParent(i));
1534                }
1535            }
1536        }
1537    }
1538    traverse(lod);
1539}
1540
1541
1542void Optimizer::CombineLODsVisitor::combineLODs()
1543{
1544    for(GroupList::iterator itr=_groupList.begin();
1545        itr!=_groupList.end();
1546        ++itr)
1547    {
1548        osg::Group* group = *itr;
1549
1550        typedef std::set<osg::LOD*>  LODSet;
1551
1552        LODSet    lodChildren;
1553
1554        for(unsigned int i=0;i<group->getNumChildren();++i)
1555        {
1556            osg::Node* child = group->getChild(i);
1557            osg::LOD* lod = dynamic_cast<osg::LOD*>(child);
1558            if (lod)
1559            {
1560                lodChildren.insert(lod);
1561            }
1562        }
1563
1564        if (lodChildren.size()>=2)
1565        {
1566            osg::BoundingBox bb;
1567            LODSet::iterator lod_itr;
1568            float smallestRadius=FLT_MAX;
1569            for(lod_itr=lodChildren.begin();
1570                lod_itr!=lodChildren.end();
1571                ++lod_itr)
1572            {
1573                float r = (*lod_itr)->getBound().radius();
1574                if (r>=0 && r<smallestRadius) smallestRadius = r;
1575                bb.expandBy((*lod_itr)->getCenter());
1576            }
1577            if (bb.radius()<smallestRadius*0.1f)
1578            {
1579                typedef std::pair<float,float> RangePair;
1580                typedef std::multimap<RangePair,osg::Node*> RangeMap;
1581                RangeMap rangeMap;
1582                for(lod_itr=lodChildren.begin();
1583                    lod_itr!=lodChildren.end();
1584                    ++lod_itr)
1585                {
1586
1587                    osg::LOD* lod = *lod_itr;
1588                    for(unsigned int i=0;i<lod->getNumRanges();++i)
1589                    {
1590                        rangeMap.insert(RangeMap::value_type(RangePair(lod->getMinRange(i),lod->getMaxRange(i)),lod->getChild(i)));
1591                    }
1592
1593                }
1594
1595                // create new LOD containing all other LOD's children.
1596                osg::LOD* newLOD = new osg::LOD;
1597                newLOD->setName("newLOD");
1598                newLOD->setCenter(bb.center());
1599
1600                int i=0;
1601                for(RangeMap::iterator c_itr=rangeMap.begin();
1602                    c_itr!=rangeMap.end();
1603                    ++c_itr,++i)
1604                {
1605                    newLOD->setRange(i,c_itr->first.first,c_itr->first.second);
1606                    newLOD->addChild(c_itr->second);
1607                }
1608
1609                // add LOD into parent.
1610                group->addChild(newLOD);
1611
1612                // remove all the old LOD's from group.
1613                for(lod_itr=lodChildren.begin();
1614                    lod_itr!=lodChildren.end();
1615                    ++lod_itr)
1616                {
1617                    group->removeChild(*lod_itr);
1618                }
1619
1620            }
1621
1622        }
1623    }
1624    _groupList.clear();
1625}
1626
1627////////////////////////////////////////////////////////////////////////////
1628// code to merge geometry object which share, state, and attribute bindings.
1629////////////////////////////////////////////////////////////////////////////
1630
1631struct LessGeometry
1632{
1633    bool operator() (const osg::Geometry* lhs,const osg::Geometry* rhs) const
1634    {
1635        if (lhs->getStateSet()<rhs->getStateSet()) return true;
1636        if (rhs->getStateSet()<lhs->getStateSet()) return false;
1637       
1638        if (rhs->getVertexIndices()) { if (!lhs->getVertexIndices()) return true; }
1639        else if (lhs->getVertexIndices()) return false;
1640       
1641        if (lhs->getNormalBinding()<rhs->getNormalBinding()) return true;
1642        if (rhs->getNormalBinding()<lhs->getNormalBinding()) return false;
1643
1644        if (rhs->getNormalIndices()) { if (!lhs->getNormalIndices()) return true; }
1645        else if (lhs->getNormalIndices()) return false;
1646
1647        if (lhs->getColorBinding()<rhs->getColorBinding()) return true;
1648        if (rhs->getColorBinding()<lhs->getColorBinding()) return false;
1649       
1650        if (rhs->getColorIndices()) { if (!lhs->getColorIndices()) return true; }
1651        else if (lhs->getColorIndices()) return false;
1652
1653        if (lhs->getSecondaryColorBinding()<rhs->getSecondaryColorBinding()) return true;
1654        if (rhs->getSecondaryColorBinding()<lhs->getSecondaryColorBinding()) return false;
1655
1656        if (rhs->getSecondaryColorIndices()) { if (!lhs->getSecondaryColorIndices()) return true; }
1657        else if (lhs->getSecondaryColorIndices()) return false;
1658
1659        if (lhs->getFogCoordBinding()<rhs->getFogCoordBinding()) return true;
1660        if (rhs->getFogCoordBinding()<lhs->getFogCoordBinding()) return false;
1661
1662        if (rhs->getFogCoordIndices()) { if (!lhs->getFogCoordIndices()) return true; }
1663        else if (lhs->getFogCoordIndices()) return false;
1664
1665        if (lhs->getNumTexCoordArrays()<rhs->getNumTexCoordArrays()) return true;
1666        if (rhs->getNumTexCoordArrays()<lhs->getNumTexCoordArrays()) return false;
1667   
1668        // therefore lhs->getNumTexCoordArrays()==rhs->getNumTexCoordArrays()
1669       
1670        unsigned int i;
1671        for(i=0;i<lhs->getNumTexCoordArrays();++i)
1672        {
1673            if (rhs->getTexCoordArray(i))
1674            {
1675                if (!lhs->getTexCoordArray(i)) return true;
1676            }
1677            else if (lhs->getTexCoordArray(i)) return false;
1678
1679            if (rhs->getTexCoordIndices(i)) { if (!lhs->getTexCoordIndices(i)) return true; }
1680            else if (lhs->getTexCoordIndices(i)) return false;
1681        }
1682       
1683        for(i=0;i<lhs->getNumVertexAttribArrays();++i)
1684        {
1685            if (rhs->getVertexAttribArray(i))
1686            {
1687                if (!lhs->getVertexAttribArray(i)) return true;
1688            }
1689            else if (lhs->getVertexAttribArray(i)) return false;
1690
1691            if (rhs->getVertexAttribIndices(i)) { if (!lhs->getVertexAttribIndices(i)) return true; }
1692            else if (lhs->getVertexAttribIndices(i)) return false;
1693        }
1694       
1695       
1696        if (lhs->getNormalBinding()==osg::Geometry::BIND_OVERALL)
1697        {
1698            // assumes that the bindings and arrays are set up correctly, this
1699            // should be the case after running computeCorrectBindingsAndArraySizes();
1700            const osg::Array* lhs_normalArray = lhs->getNormalArray();
1701            const osg::Array* rhs_normalArray = rhs->getNormalArray();
1702            if (lhs_normalArray->getType()<rhs_normalArray->getType()) return true;
1703            if (rhs_normalArray->getType()<lhs_normalArray->getType()) return false;
1704            switch(lhs_normalArray->getType())
1705            {
1706            case(osg::Array::Vec3bArrayType):
1707                if ((*static_cast<const osg::Vec3bArray*>(lhs_normalArray))[0]<(*static_cast<const osg::Vec3bArray*>(rhs_normalArray))[0]) return true;
1708                if ((*static_cast<const osg::Vec3bArray*>(rhs_normalArray))[0]<(*static_cast<const osg::Vec3bArray*>(lhs_normalArray))[0]) return false;
1709                break;
1710            case(osg::Array::Vec3sArrayType):
1711                if ((*static_cast<const osg::Vec3sArray*>(lhs_normalArray))[0]<(*static_cast<const osg::Vec3sArray*>(rhs_normalArray))[0]) return true;
1712                if ((*static_cast<const osg::Vec3sArray*>(rhs_normalArray))[0]<(*static_cast<const osg::Vec3sArray*>(lhs_normalArray))[0]) return false;
1713                break;
1714            case(osg::Array::Vec3ArrayType):
1715                if ((*static_cast<const osg::Vec3Array*>(lhs_normalArray))[0]<(*static_cast<const osg::Vec3Array*>(rhs_normalArray))[0]) return true;
1716                if ((*static_cast<const osg::Vec3Array*>(rhs_normalArray))[0]<(*static_cast<const osg::Vec3Array*>(lhs_normalArray))[0]) return false;
1717                break;
1718            default:
1719                break;
1720            }
1721        }
1722       
1723        if (lhs->getColorBinding()==osg::Geometry::BIND_OVERALL)
1724        {
1725            const osg::Array* lhs_colorArray = lhs->getColorArray();
1726            const osg::Array* rhs_colorArray = rhs->getColorArray();
1727            if (lhs_colorArray->getType()<rhs_colorArray->getType()) return true;
1728            if (rhs_colorArray->getType()<lhs_colorArray->getType()) return false;
1729            switch(lhs_colorArray->getType())
1730            {
1731                case(osg::Array::Vec4ubArrayType):
1732                    if ((*static_cast<const osg::Vec4ubArray*>(lhs_colorArray))[0]<(*static_cast<const osg::Vec4ubArray*>(rhs_colorArray))[0]) return true;
1733                    if ((*static_cast<const osg::Vec4ubArray*>(rhs_colorArray))[0]<(*static_cast<const osg::Vec4ubArray*>(lhs_colorArray))[0]) return false;
1734                    break;
1735                case(osg::Array::Vec3ArrayType):
1736                    if ((*static_cast<const osg::Vec3Array*>(lhs_colorArray))[0]<(*static_cast<const osg::Vec3Array*>(rhs_colorArray))[0]) return true;
1737                    if ((*static_cast<const osg::Vec3Array*>(rhs_colorArray))[0]<(*static_cast<const osg::Vec3Array*>(lhs_colorArray))[0]) return false;
1738                    break;
1739                case(osg::Array::Vec4ArrayType):
1740                    if ((*static_cast<const osg::Vec4Array*>(lhs_colorArray))[0]<(*static_cast<const osg::Vec4Array*>(rhs_colorArray))[0]) return true;
1741                    if ((*static_cast<const osg::Vec4Array*>(rhs_colorArray))[0]<(*static_cast<const osg::Vec4Array*>(lhs_colorArray))[0]) return false;
1742                    break;
1743                default:
1744                    break;
1745            }
1746           
1747        }
1748
1749        return false;
1750
1751    }
1752};
1753
1754struct LessGeometryPrimitiveType
1755{
1756    bool operator() (const osg::Geometry* lhs,const osg::Geometry* rhs) const
1757    {
1758        for(unsigned int i=0;
1759            i<lhs->getNumPrimitiveSets() && i<rhs->getNumPrimitiveSets();
1760            ++i)
1761        {
1762            if (lhs->getPrimitiveSet(i)->getType()<rhs->getPrimitiveSet(i)->getType()) return true;
1763            else if (rhs->getPrimitiveSet(i)->getType()<lhs->getPrimitiveSet(i)->getType()) return false;
1764
1765            if (lhs->getPrimitiveSet(i)->getMode()<rhs->getPrimitiveSet(i)->getMode()) return true;
1766            else if (rhs->getPrimitiveSet(i)->getMode()<lhs->getPrimitiveSet(i)->getMode()) return false;
1767
1768        }
1769        return lhs->getNumPrimitiveSets()<rhs->getNumPrimitiveSets();
1770    }
1771};
1772
1773void Optimizer::CheckGeometryVisitor::checkGeode(osg::Geode& geode)
1774{
1775    if (isOperationPermissibleForObject(&geode))
1776    {
1777        for(unsigned int i=0;i<geode.getNumDrawables();++i)
1778        {
1779            osg::Geometry* geom = geode.getDrawable(i)->asGeometry();
1780            if (geom && isOperationPermissibleForObject(geom))
1781            {
1782                geom->computeCorrectBindingsAndArraySizes();
1783            }
1784        }
1785    }
1786}
1787
1788bool Optimizer::MergeGeometryVisitor::mergeGeode(osg::Geode& geode)
1789{
1790    if (!isOperationPermissibleForObject(&geode)) return false;
1791
1792    if (geode.getNumDrawables()>=2)
1793    {
1794   
1795        // osg::notify(osg::NOTICE)<<"Before "<<geode.getNumDrawables()<<std::endl;
1796   
1797        typedef std::vector<osg::Geometry*>                         DuplicateList;
1798        typedef std::map<osg::Geometry*,DuplicateList,LessGeometry> GeometryDuplicateMap;
1799
1800        typedef std::vector<DuplicateList> MergeList;
1801
1802        GeometryDuplicateMap geometryDuplicateMap;
1803        osg::Geode::DrawableList standardDrawables;
1804
1805        unsigned int i;
1806        for(i=0;i<geode.getNumDrawables();++i)
1807        {
1808            osg::Geometry* geom = geode.getDrawable(i)->asGeometry();
1809            if (geom)
1810            {
1811                //geom->computeCorrectBindingsAndArraySizes();
1812
1813                if (!geometryContainsSharedArrays(*geom) && isOperationPermissibleForObject(geom))
1814                {
1815                    geometryDuplicateMap[geom].push_back(geom);
1816                }
1817                else
1818                {
1819                    standardDrawables.push_back(geode.getDrawable(i));
1820                }
1821            }
1822            else
1823            {           
1824                standardDrawables.push_back(geode.getDrawable(i));
1825            }
1826        }
1827
1828#if 1
1829        bool needToDoMerge = false;
1830        MergeList mergeList;
1831        for(GeometryDuplicateMap::iterator itr=geometryDuplicateMap.begin();
1832            itr!=geometryDuplicateMap.end();
1833            ++itr)
1834        {
1835            mergeList.push_back(DuplicateList());
1836            DuplicateList* duplicateList = &mergeList.back();
1837           
1838            if (itr->second.size()>1)
1839            {
1840                std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType());
1841                osg::Geometry* lhs = itr->second[0];
1842
1843                duplicateList->push_back(lhs);
1844               
1845                unsigned int numVertices = lhs->getVertexArray() ? lhs->getVertexArray()->getNumElements() : 0;
1846
1847                for(DuplicateList::iterator dupItr=itr->second.begin()+1;
1848                    dupItr!=itr->second.end();
1849                    ++dupItr)
1850                {
1851               
1852                    osg::Geometry* rhs = *dupItr;
1853                   
1854                    unsigned int numRhsVertices = rhs->getVertexArray() ? rhs->getVertexArray()->getNumElements() : 0;
1855
1856                    if (numVertices+numRhsVertices < _targetMaximumNumberOfVertices)
1857                    {
1858                        duplicateList->push_back(rhs);
1859                        numVertices += numRhsVertices;
1860                        needToDoMerge = true;
1861                    }
1862                    else
1863                    {
1864                        numVertices = numRhsVertices;
1865                        mergeList.push_back(DuplicateList());
1866                        duplicateList = &mergeList.back();
1867                        duplicateList->push_back(rhs);
1868                    }
1869
1870                }
1871            }
1872            else if (itr->second.size()>0)
1873            {
1874                duplicateList->push_back(itr->second[0]);
1875            }
1876        }
1877       
1878        if (needToDoMerge)
1879        {
1880            // first take a reference to all the drawables to prevent them being deleted prematurely
1881            osg::Geode::DrawableList keepDrawables;
1882            keepDrawables.resize(geode.getNumDrawables());
1883            for(i=0; i<geode.getNumDrawables(); ++i)
1884            {
1885                keepDrawables[i] = geode.getDrawable(i);
1886            }
1887           
1888            // now clear the drawable list of the Geode so we don't have to remove items one by one (which is slow)
1889            geode.removeDrawables(0, geode.getNumDrawables());
1890           
1891            // add back in the standard drawables which arn't possible to merge.
1892            for(osg::Geode::DrawableList::iterator sitr = standardDrawables.begin();
1893                sitr != standardDrawables.end();
1894                ++sitr)
1895            {
1896                geode.addDrawable(sitr->get());
1897            }
1898               
1899            // now do the merging of geometries
1900            for(MergeList::iterator mitr = mergeList.begin();
1901                mitr != mergeList.end();
1902                ++mitr)
1903            {
1904                DuplicateList& duplicateList = *mitr;
1905                if (duplicateList.size()>1)
1906                {
1907                    osg::Geometry* lhs = duplicateList.front();
1908                    geode.addDrawable(lhs);
1909                    for(DuplicateList::iterator ditr = duplicateList.begin()+1;
1910                        ditr != duplicateList.end();
1911                        ++ditr)
1912                    {
1913                        mergeGeometry(*lhs,**ditr);
1914                    }
1915                }
1916                else if (duplicateList.size()>0)
1917                {
1918                    geode.addDrawable(duplicateList.front());
1919                }
1920            }
1921        }
1922
1923#else
1924        // don't merge geometry if its above a maximum number of vertices.
1925        for(GeometryDuplicateMap::iterator itr=geometryDuplicateMap.begin();
1926            itr!=geometryDuplicateMap.end();
1927            ++itr)
1928        {
1929            if (itr->second.size()>1)
1930            {
1931                std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType());
1932                osg::Geometry* lhs = itr->second[0];
1933                for(DuplicateList::iterator dupItr=itr->second.begin()+1;
1934                    dupItr!=itr->second.end();
1935                    ++dupItr)
1936                {
1937               
1938                    osg::Geometry* rhs = *dupItr;
1939                   
1940                    if (lhs->getVertexArray() && lhs->getVertexArray()->getNumElements()>=_targetMaximumNumberOfVertices)
1941                    {
1942                        lhs = rhs;
1943                        continue;
1944                    }
1945
1946                    if (rhs->getVertexArray() && rhs->getVertexArray()->getNumElements()>=_targetMaximumNumberOfVertices)
1947                    {
1948                        continue;
1949                    }
1950                   
1951                    if (lhs->getVertexArray() && rhs->getVertexArray() &&
1952                        (lhs->getVertexArray()->getNumElements()+rhs->getVertexArray()->getNumElements())>=_targetMaximumNumberOfVertices)
1953                    {
1954                        continue;
1955                    }
1956                   
1957                    if (mergeGeometry(*lhs,*rhs))
1958                    {
1959                        geode.removeDrawable(rhs);
1960
1961                        static int co = 0;
1962                        osg::notify(osg::INFO)<<"merged and removed Geometry "<<++co<<std::endl;
1963                    }
1964                }
1965            }
1966        }
1967#endif
1968
1969        // osg::notify(osg::NOTICE)<<"After "<<geode.getNumDrawables()<<std::endl;
1970
1971    }
1972
1973
1974    // convert all polygon primitives which has 3 indices into TRIANGLES, 4 indices into QUADS.
1975    unsigned int i;
1976    for(i=0;i<geode.getNumDrawables();++i)
1977    {
1978        osg::Geometry* geom = dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
1979        if (geom)
1980        {
1981            osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();
1982            for(osg::Geometry::PrimitiveSetList::iterator itr=primitives.begin();
1983                itr!=primitives.end();
1984                ++itr)
1985            {
1986                osg::PrimitiveSet* prim = itr->get();
1987                if (prim->getMode()==osg::PrimitiveSet::POLYGON)
1988                {
1989                    if (prim->getNumIndices()==3)
1990                    {
1991                        prim->setMode(osg::PrimitiveSet::TRIANGLES);
1992                    }
1993                    else if (prim->getNumIndices()==4)
1994                    {
1995                        prim->setMode(osg::PrimitiveSet::QUADS);
1996                    }
1997                }
1998            }
1999        }
2000    }
2001
2002    // now merge any compatible primitives.
2003    for(i=0;i<geode.getNumDrawables();++i)
2004    {
2005        osg::Geometry* geom = dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
2006        if (geom)
2007        {
2008            if (geom->getNumPrimitiveSets()>0 &&
2009                geom->getNormalBinding()!=osg::Geometry::BIND_PER_PRIMITIVE_SET &&
2010                geom->getColorBinding()!=osg::Geometry::BIND_PER_PRIMITIVE_SET &&
2011                geom->getSecondaryColorBinding()!=osg::Geometry::BIND_PER_PRIMITIVE_SET &&
2012                geom->getFogCoordBinding()!=osg::Geometry::BIND_PER_PRIMITIVE_SET)
2013            {
2014           
2015#if 1
2016                bool doneCombine = false;
2017
2018                osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();
2019                unsigned int lhsNo=0;
2020                unsigned int rhsNo=1;
2021                while(rhsNo<primitives.size())
2022                {
2023                    osg::PrimitiveSet* lhs = primitives[lhsNo].get();
2024                    osg::PrimitiveSet* rhs = primitives[rhsNo].get();
2025 
2026                    bool combine = false;
2027
2028                    if (lhs->getType()==rhs->getType() &&
2029                        lhs->getMode()==rhs->getMode())
2030                    {
2031
2032                        switch(lhs->getMode())
2033                        {
2034                        case(osg::PrimitiveSet::POINTS):
2035                        case(osg::PrimitiveSet::LINES):
2036                        case(osg::PrimitiveSet::TRIANGLES):
2037                        case(osg::PrimitiveSet::QUADS):
2038                            combine = true;       
2039                            break;
2040                        }
2041                       
2042                    }
2043
2044                    if (combine)
2045                    {
2046                   
2047                        switch(lhs->getType())
2048                        {
2049                        case(osg::PrimitiveSet::DrawArraysPrimitiveType):
2050                            combine = mergePrimitive(*(static_cast<osg::DrawArrays*>(lhs)),*(static_cast<osg::DrawArrays*>(rhs)));
2051                            break;
2052                        case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):
2053                            combine = mergePrimitive(*(static_cast<osg::DrawArrayLengths*>(lhs)),*(static_cast<osg::DrawArrayLengths*>(rhs)));
2054                            break;
2055                        case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):
2056                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUByte*>(lhs)),*(static_cast<osg::DrawElementsUByte*>(rhs)));
2057                            break;
2058                        case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):
2059                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUShort*>(lhs)),*(static_cast<osg::DrawElementsUShort*>(rhs)));
2060                            break;
2061                        case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):
2062                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUInt*>(lhs)),*(static_cast<osg::DrawElementsUInt*>(rhs)));
2063                            break;
2064                        default:
2065                            combine = false;
2066                            break;
2067                        }
2068                    }
2069
2070                    if (combine)
2071                    {
2072                        // make this primitive set as invalid and needing cleaning up.
2073                        rhs->setMode(0xffffff);
2074                        doneCombine = true;
2075                        ++rhsNo;
2076                    }
2077                    else
2078                    {
2079                        lhsNo = rhsNo;
2080                        ++rhsNo;
2081                    }
2082                }
2083
2084    #if 1               
2085                if (doneCombine)
2086                {
2087                    // now need to clean up primitiveset so it no longer contains the rhs combined primitives.
2088
2089                    // first swap with a empty primitiveSet to empty it completely.
2090                    osg::Geometry::PrimitiveSetList oldPrimitives;
2091                    primitives.swap(oldPrimitives);
2092                   
2093                    // now add the active primitive sets
2094                    for(osg::Geometry::PrimitiveSetList::iterator pitr = oldPrimitives.begin();
2095                        pitr != oldPrimitives.end();
2096                        ++pitr)
2097                    {
2098                        if ((*pitr)->getMode()!=0xffffff) primitives.push_back(*pitr);
2099                    }
2100                }
2101    #endif
2102
2103#else           
2104
2105                osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();
2106                unsigned int primNo=0;
2107                while(primNo+1<primitives.size())
2108                {
2109                    osg::PrimitiveSet* lhs = primitives[primNo].get();
2110                    osg::PrimitiveSet* rhs = primitives[primNo+1].get();
2111 
2112                    bool combine = false;
2113
2114                    if (lhs->getType()==rhs->getType() &&
2115                        lhs->getMode()==rhs->getMode())
2116                    {
2117
2118                        switch(lhs->getMode())
2119                        {
2120                        case(osg::PrimitiveSet::POINTS):
2121                        case(osg::PrimitiveSet::LINES):
2122                        case(osg::PrimitiveSet::TRIANGLES):
2123                        case(osg::PrimitiveSet::QUADS):
2124                            combine = true;       
2125                            break;
2126                        }
2127                       
2128                    }
2129
2130                    if (combine)
2131                    {
2132                   
2133                        switch(lhs->getType())
2134                        {
2135                        case(osg::PrimitiveSet::DrawArraysPrimitiveType):
2136                            combine = mergePrimitive(*(static_cast<osg::DrawArrays*>(lhs)),*(static_cast<osg::DrawArrays*>(rhs)));
2137                            break;
2138                        case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):
2139                            combine = mergePrimitive(*(static_cast<osg::DrawArrayLengths*>(lhs)),*(static_cast<osg::DrawArrayLengths*>(rhs)));
2140                            break;
2141                        case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):
2142                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUByte*>(lhs)),*(static_cast<osg::DrawElementsUByte*>(rhs)));
2143                            break;
2144                        case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):
2145                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUShort*>(lhs)),*(static_cast<osg::DrawElementsUShort*>(rhs)));
2146                            break;
2147                        case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):
2148                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUInt*>(lhs)),*(static_cast<osg::DrawElementsUInt*>(rhs)));
2149                            break;
2150                        default:
2151                            break;
2152                        }
2153                    }
2154                    if (combine)
2155                    {
2156                        primitives.erase(primitives.begin()+primNo+1);
2157                    }
2158
2159                    if (!combine)
2160                    {
2161                        primNo++;
2162                    }
2163                }
2164#endif
2165            }
2166        }
2167
2168
2169    }
2170
2171//    geode.dirtyBound();
2172
2173
2174    return false;
2175}
2176
2177bool Optimizer::MergeGeometryVisitor::geometryContainsSharedArrays(osg::Geometry& geom)
2178{
2179    if (geom.getVertexArray() && geom.getVertexArray()->referenceCount()>1) return true;
2180    if (geom.getNormalArray() && geom.getNormalArray()->referenceCount()>1) return true;
2181    if (geom.getColorArray() && geom.getColorArray()->referenceCount()>1) return true;
2182    if (geom.getSecondaryColorArray() && geom.getSecondaryColorArray()->referenceCount()>1) return true;
2183    if (geom.getFogCoordArray() && geom.getFogCoordArray()->referenceCount()>1) return true;
2184   
2185
2186    for(unsigned int unit=0;unit<geom.getNumTexCoordArrays();++unit)
2187    {
2188        osg::Array* tex = geom.getTexCoordArray(unit);
2189        if (tex && tex->referenceCount()>1) return true;
2190    }
2191   
2192    // shift the indices of the incoming primitives to account for the pre existing geometry.
2193    for(osg::Geometry::PrimitiveSetList::iterator primItr=geom.getPrimitiveSetList().begin();
2194        primItr!=geom.getPrimitiveSetList().end();
2195        ++primItr)
2196    {
2197        if ((*primItr)->referenceCount()>1) return true;
2198    }
2199   
2200   
2201    return false;
2202}
2203
2204
2205class MergeArrayVisitor : public osg::ArrayVisitor
2206{
2207    public:
2208   
2209        osg::Array* _lhs;
2210        int         _offset;
2211   
2212        MergeArrayVisitor() :
2213            _lhs(0),
2214            _offset(0) {}
2215           
2216           
2217        void merge(osg::Array* lhs,osg::Array* rhs, int offset=0)
2218        {
2219            if (lhs==0 || rhs==0) return;
2220            if (lhs->getType()!=rhs->getType()) return;
2221           
2222            _lhs = lhs;
2223            _offset = offset;
2224           
2225            rhs->accept(*this);
2226        }
2227       
2228        template<typename T>
2229        void _merge(T& rhs)
2230        {
2231            T* lhs = static_cast<T*>(_lhs);
2232            lhs->insert(lhs->end(),rhs.begin(),rhs.end());
2233        }
2234       
2235        template<typename T>
2236        void _mergeAndOffset(T& rhs)
2237        {
2238            T* lhs = static_cast<T*>(_lhs);
2239
2240            typename T::iterator itr;
2241            for(itr = rhs.begin();
2242                itr != rhs.end();
2243                ++itr)
2244            {
2245                lhs->push_back(*itr + _offset);
2246            }
2247        }
2248           
2249        virtual void apply(osg::Array&) { osg::notify(osg::WARN) << "Warning: Optimizer's MergeArrayVisitor cannot merge Array type." << std::endl; }
2250
2251        virtual void apply(osg::ByteArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2252        virtual void apply(osg::ShortArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2253        virtual void apply(osg::IntArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2254        virtual void apply(osg::UByteArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2255        virtual void apply(osg::UShortArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2256        virtual void apply(osg::UIntArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2257
2258        virtual void apply(osg::Vec4ubArray& rhs) { _merge(rhs); }
2259        virtual void apply(osg::FloatArray& rhs) { _merge(rhs); }
2260        virtual void apply(osg::Vec2Array& rhs) { _merge(rhs); }
2261        virtual void apply(osg::Vec3Array& rhs) { _merge(rhs); }
2262        virtual void apply(osg::Vec4Array& rhs) { _merge(rhs); }
2263       
2264        virtual void apply(osg::DoubleArray& rhs) { _merge(rhs); }
2265        virtual void apply(osg::Vec2dArray& rhs) { _merge(rhs); }
2266        virtual void apply(osg::Vec3dArray& rhs) { _merge(rhs); }
2267        virtual void apply(osg::Vec4dArray& rhs) { _merge(rhs); }
2268       
2269        virtual void apply(osg::Vec2bArray&  rhs) { _merge(rhs); }
2270        virtual void apply(osg::Vec3bArray&  rhs) { _merge(rhs); }
2271        virtual void apply(osg::Vec4bArray&  rhs) { _merge(rhs); }       
2272        virtual void apply(osg::Vec2sArray& rhs) { _merge(rhs); }
2273        virtual void apply(osg::Vec3sArray& rhs) { _merge(rhs); }
2274        virtual void apply(osg::Vec4sArray& rhs) { _merge(rhs); }
2275};
2276
2277bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs)
2278{
2279
2280    MergeArrayVisitor merger;
2281 
2282    unsigned int base = 0;
2283    unsigned int vbase = lhs.getVertexArray() ? lhs.getVertexArray()->getNumElements() : 0;
2284    if (lhs.getVertexArray() && rhs.getVertexArray())
2285    {
2286
2287        base = lhs.getVertexArray()->getNumElements();
2288        merger.merge(lhs.getVertexArray(),rhs.getVertexArray());
2289
2290    }
2291    else if (rhs.getVertexArray())
2292    {
2293        base = 0;
2294        lhs.setVertexArray(rhs.getVertexArray());
2295    }
2296   
2297    if (lhs.getVertexIndices() && rhs.getVertexIndices())
2298    {
2299
2300        base = lhs.getVertexIndices()->getNumElements();
2301        merger.merge(lhs.getVertexIndices(),rhs.getVertexIndices(),vbase);
2302
2303    }
2304    else if (rhs.getVertexIndices())
2305    {
2306        base = 0;
2307        lhs.setVertexIndices(rhs.getVertexIndices());
2308    }
2309   
2310
2311    unsigned int nbase = lhs.getNormalArray() ? lhs.getNormalArray()->getNumElements() : 0;
2312    if (lhs.getNormalArray() && rhs.getNormalArray() && lhs.getNormalBinding()!=osg::Geometry::BIND_OVERALL)
2313    {
2314        merger.merge(lhs.getNormalArray(),rhs.getNormalArray());
2315    }
2316    else if (rhs.getNormalArray())
2317    {
2318        lhs.setNormalArray(rhs.getNormalArray());
2319    }
2320
2321    if (lhs.getNormalIndices() && rhs.getNormalIndices() && lhs.getNormalBinding()!=osg::Geometry::BIND_OVERALL)
2322    {
2323        merger.merge(lhs.getNormalIndices(),rhs.getNormalIndices(),nbase);
2324    }
2325    else if (rhs.getNormalIndices())
2326    {
2327        // this assignment makes the assumption that lhs.NormalArray is empty as well and NormalIndices
2328        lhs.setNormalIndices(rhs.getNormalIndices());
2329    }
2330
2331
2332    unsigned int cbase = lhs.getColorArray() ? lhs.getColorArray()->getNumElements() : 0;
2333    if (lhs.getColorArray() && rhs.getColorArray() && lhs.getColorBinding()!=osg::Geometry::BIND_OVERALL)
2334    {
2335        merger.merge(lhs.getColorArray(),rhs.getColorArray());
2336    }
2337    else if (rhs.getColorArray())
2338    {
2339        lhs.setColorArray(rhs.getColorArray());
2340    }
2341   
2342    if (lhs.getColorIndices() && rhs.getColorIndices() && lhs.getColorBinding()!=osg::Geometry::BIND_OVERALL)
2343    {
2344        merger.merge(lhs.getColorIndices(),rhs.getColorIndices(),cbase);
2345    }
2346    else if (rhs.getColorIndices())
2347    {
2348        // this assignment makes the assumption that lhs.ColorArray is empty as well and ColorIndices
2349        lhs.setColorIndices(rhs.getColorIndices());
2350    }
2351
2352    unsigned int scbase = lhs.getSecondaryColorArray() ? lhs.getSecondaryColorArray()->getNumElements() : 0;
2353    if (lhs.getSecondaryColorArray() && rhs.getSecondaryColorArray() && lhs.getSecondaryColorBinding()!=osg::Geometry::BIND_OVERALL)
2354    {
2355        merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray());
2356    }
2357    else if (rhs.getSecondaryColorArray())
2358    {
2359        lhs.setSecondaryColorArray(rhs.getSecondaryColorArray());
2360    }
2361   
2362    if (lhs.getSecondaryColorIndices() && rhs.getSecondaryColorIndices() && lhs.getSecondaryColorBinding()!=osg::Geometry::BIND_OVERALL)
2363    {
2364        merger.merge(lhs.getSecondaryColorIndices(),rhs.getSecondaryColorIndices(),scbase);
2365    }
2366    else if (rhs.getSecondaryColorIndices())
2367    {
2368        // this assignment makes the assumption that lhs.SecondaryColorArray is empty as well and SecondaryColorIndices
2369        lhs.setSecondaryColorIndices(rhs.getSecondaryColorIndices());
2370    }
2371
2372    unsigned int fcbase = lhs.getFogCoordArray() ? lhs.getFogCoordArray()->getNumElements() : 0;
2373    if (lhs.getFogCoordArray() && rhs.getFogCoordArray() && lhs.getFogCoordBinding()!=osg::Geometry::BIND_OVERALL)
2374    {
2375        merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray());
2376    }
2377    else if (rhs.getFogCoordArray())
2378    {
2379        lhs.setFogCoordArray(rhs.getFogCoordArray());
2380    }
2381
2382    if (lhs.getFogCoordIndices() && rhs.getFogCoordIndices() && lhs.getFogCoordBinding()!=osg::Geometry::BIND_OVERALL)
2383    {
2384        merger.merge(lhs.getFogCoordIndices(),rhs.getFogCoordIndices(),fcbase);
2385    }
2386    else if (rhs.getFogCoordIndices())
2387    {
2388        // this assignment makes the assumption that lhs.FogCoordArray is empty as well and FogCoordIndices
2389        lhs.setFogCoordIndices(rhs.getFogCoordIndices());
2390    }
2391
2392
2393    unsigned int unit;
2394    for(unit=0;unit<lhs.getNumTexCoordArrays();++unit)
2395    {
2396        unsigned int tcbase = lhs.getTexCoordArray(unit) ? lhs.getTexCoordArray(unit)->getNumElements() : 0;
2397        merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit));
2398       
2399        if (lhs.getTexCoordIndices(unit) && rhs.getTexCoordIndices(unit))
2400        {
2401            merger.merge(lhs.getTexCoordIndices(unit),rhs.getTexCoordIndices(unit),tcbase);
2402        }
2403    }
2404   
2405    for(unit=0;unit<lhs.getNumVertexAttribArrays();++unit)
2406    {
2407        unsigned int vabase = lhs.getVertexAttribArray(unit) ? lhs.getVertexAttribArray(unit)->getNumElements() : 0;
2408        merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit));
2409       
2410        if (lhs.getVertexAttribIndices(unit) && rhs.getVertexAttribIndices(unit))
2411        {
2412            merger.merge(lhs.getVertexAttribIndices(unit),rhs.getVertexAttribIndices(unit),vabase);
2413        }
2414    }
2415
2416
2417    // shift the indices of the incoming primitives to account for the pre existing geometry.
2418    for(osg::Geometry::PrimitiveSetList::iterator primItr=rhs.getPrimitiveSetList().begin();
2419        primItr!=rhs.getPrimitiveSetList().end();
2420        ++primItr)
2421    {
2422        osg::PrimitiveSet* primitive = primItr->get();
2423        switch(primitive->getType())
2424        {
2425        case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):
2426            {
2427                osg::DrawElementsUByte* primitiveUByte = static_cast<osg::DrawElementsUByte*>(primitive);
2428                unsigned int currentMaximum = 0;
2429                for(osg::DrawElementsUByte::iterator eitr=primitiveUByte->begin();
2430                    eitr!=primitiveUByte->end();
2431                    ++eitr)
2432                {
2433                    currentMaximum = osg::maximum(currentMaximum,(unsigned int)*eitr);
2434                }
2435                if ((base+currentMaximum)>=65536)
2436                {
2437                    // must promote to a DrawElementsUInt
2438                    osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode());
2439                    std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive));
2440                    new_primitive->offsetIndices(base);
2441                    (*primItr) = new_primitive;
2442                } else if ((base+currentMaximum)>=256)
2443                {
2444                    // must promote to a DrawElementsUShort
2445                    osg::DrawElementsUShort* new_primitive = new osg::DrawElementsUShort(primitive->getMode());
2446                    std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive));
2447                    new_primitive->offsetIndices(base);
2448                    (*primItr) = new_primitive;
2449                }
2450                else
2451                {
2452                    primitive->offsetIndices(base);
2453                }
2454            }
2455            break;
2456
2457        case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):
2458            {
2459                osg::DrawElementsUShort* primitiveUShort = static_cast<osg::DrawElementsUShort*>(primitive);
2460                unsigned int currentMaximum = 0;
2461                for(osg::DrawElementsUShort::iterator eitr=primitiveUShort->begin();
2462                    eitr!=primitiveUShort->end();
2463                    ++eitr)
2464                {
2465                    currentMaximum = osg::maximum(currentMaximum,(unsigned int)*eitr);
2466                }
2467                if ((base+currentMaximum)>=65536)
2468                {
2469                    // must promote to a DrawElementsUInt
2470                    osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode());
2471                    std::copy(primitiveUShort->begin(),primitiveUShort->end(),std::back_inserter(*new_primitive));
2472                    new_primitive->offsetIndices(base);
2473                    (*primItr) = new_primitive;
2474                }
2475                else
2476                {
2477                    primitive->offsetIndices(base);
2478                }
2479            }
2480            break;
2481
2482        case(osg::PrimitiveSet::DrawArraysPrimitiveType):
2483        case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):
2484        case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):
2485        default:
2486            primitive->offsetIndices(base);
2487            break;
2488        }
2489
2490       
2491    }
2492   
2493    lhs.getPrimitiveSetList().insert(lhs.getPrimitiveSetList().end(),
2494                                     rhs.getPrimitiveSetList().begin(),rhs.getPrimitiveSetList().end());
2495
2496    lhs.dirtyBound();
2497    lhs.dirtyDisplayList();
2498   
2499    return true;
2500}
2501
2502bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs)
2503{
2504    if (lhs.getFirst()+lhs.getCount()==rhs.getFirst())
2505    {
2506        lhs.setCount(lhs.getCount()+rhs.getCount());
2507        return true;
2508    }
2509    return false;
2510}
2511
2512bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawArrayLengths& lhs,osg::DrawArrayLengths& rhs)
2513{
2514    int lhs_count = std::accumulate(lhs.begin(),lhs.end(),0);
2515
2516    if (lhs.getFirst()+lhs_count==rhs.getFirst())
2517    {
2518        lhs.insert(lhs.end(),rhs.begin(),rhs.end());
2519        return true;
2520    }
2521    return false;
2522}
2523
2524bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUByte& lhs,osg::DrawElementsUByte& rhs)
2525{
2526    lhs.insert(lhs.end(),rhs.begin(),rhs.end());
2527    return true;
2528}
2529
2530bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUShort& lhs,osg::DrawElementsUShort& rhs)
2531{
2532    lhs.insert(lhs.end(),rhs.begin(),rhs.end());
2533    return true;
2534}
2535
2536bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUInt& lhs,osg::DrawElementsUInt& rhs)
2537{
2538    lhs.insert(lhs.end(),rhs.begin(),rhs.end());
2539    return true;
2540}
2541
2542
2543////////////////////////////////////////////////////////////////////////////////////////////
2544//
2545//  Spatialize the scene to accelerate culling
2546//
2547
2548void Optimizer::SpatializeGroupsVisitor::apply(osg::Group& group)
2549{
2550    if (typeid(group)==typeid(osg::Group) || group.asTransform())
2551    {
2552        if (isOperationPermissibleForObject(&group))
2553        {
2554            _groupsToDivideList.insert(&group);
2555        }
2556    }
2557    traverse(group);
2558}
2559
2560void Optimizer::SpatializeGroupsVisitor::apply(osg::Geode& geode)
2561{
2562    if (typeid(geode)==typeid(osg::Geode))
2563    {
2564        if (isOperationPermissibleForObject(&geode))
2565        {
2566            _geodesToDivideList.insert(&geode);
2567        }
2568    }
2569    traverse(geode);
2570}
2571
2572bool Optimizer::SpatializeGroupsVisitor::divide(unsigned int maxNumTreesPerCell)
2573{
2574    bool divided = false;
2575    for(GroupsToDivideList::iterator itr=_groupsToDivideList.begin();
2576        itr!=_groupsToDivideList.end();
2577        ++itr)
2578    {
2579        if (divide(*itr,maxNumTreesPerCell)) divided = true;
2580    }
2581   
2582    for(GeodesToDivideList::iterator geode_itr=_geodesToDivideList.begin();
2583        geode_itr!=_geodesToDivideList.end();
2584        ++geode_itr)
2585    {
2586        if (divide(*geode_itr,maxNumTreesPerCell)) divided = true;
2587    }
2588   
2589    return divided;
2590}
2591
2592bool Optimizer::SpatializeGroupsVisitor::divide(osg::Group* group, unsigned int maxNumTreesPerCell)
2593{
2594    if (group->getNumChildren()<=maxNumTreesPerCell) return false;
2595   
2596    // create the original box.
2597    osg::BoundingBox bb;
2598    unsigned int i;
2599    for(i=0;i<group->getNumChildren();++i)
2600    {
2601        bb.expandBy(group->getChild(i)->getBound().center());
2602    }
2603   
2604    float radius = bb.radius();
2605    float divide_distance = radius*0.7f;
2606    bool xAxis = (bb.xMax()-bb.xMin())>divide_distance;
2607    bool yAxis = (bb.yMax()-bb.yMin())>divide_distance;
2608    bool zAxis = (bb.zMax()-bb.zMin())>divide_distance;
2609
2610    osg::notify(osg::INFO)<<"Dividing "<<group->className()<<"  num children = "<<group->getNumChildren()<<"  xAxis="<<xAxis<<"  yAxis="<<yAxis<<"   zAxis="<<zAxis<<std::endl;
2611   
2612    if (!xAxis && !yAxis && !zAxis)
2613    {
2614        osg::notify(osg::INFO)<<"  No axis to divide, stopping division."<<std::endl;
2615        return false;
2616    }
2617   
2618    unsigned int numChildrenOnEntry = group->getNumChildren();
2619   
2620    typedef std::pair< osg::BoundingBox, osg::ref_ptr<osg::Group> > BoxGroupPair;
2621    typedef std::vector< BoxGroupPair > Boxes;
2622    Boxes boxes;
2623    boxes.push_back( BoxGroupPair(bb,new osg::Group) );
2624
2625    // divide up on each axis   
2626    if (xAxis)
2627    {
2628        unsigned int numCellsToDivide=boxes.size();
2629        for(unsigned int i=0;i<numCellsToDivide;++i)
2630        {
2631            osg::BoundingBox& orig_cell = boxes[i].first;
2632            osg::BoundingBox new_cell = orig_cell;
2633
2634            float xCenter = (orig_cell.xMin()+orig_cell.xMax())*0.5f;
2635            orig_cell.xMax() = xCenter;
2636            new_cell.xMin() = xCenter;
2637
2638            boxes.push_back(BoxGroupPair(new_cell,new osg::Group));
2639        }
2640    }
2641
2642    if (yAxis)
2643    {
2644        unsigned int numCellsToDivide=boxes.size();
2645        for(unsigned int i=0;i<numCellsToDivide;++i)
2646        {
2647            osg::BoundingBox& orig_cell = boxes[i].first;
2648            osg::BoundingBox new_cell = orig_cell;
2649
2650            float yCenter = (orig_cell.yMin()+orig_cell.yMax())*0.5f;
2651            orig_cell.yMax() = yCenter;
2652            new_cell.yMin() = yCenter;
2653
2654            boxes.push_back(BoxGroupPair(new_cell,new osg::Group));
2655        }
2656    }
2657
2658    if (zAxis)
2659    {
2660        unsigned int numCellsToDivide=boxes.size();
2661        for(unsigned int i=0;i<numCellsToDivide;++i)
2662        {
2663            osg::BoundingBox& orig_cell = boxes[i].first;
2664            osg::BoundingBox new_cell = orig_cell;
2665
2666            float zCenter = (orig_cell.zMin()+orig_cell.zMax())*0.5f;
2667            orig_cell.zMax() = zCenter;
2668            new_cell.zMin() = zCenter;
2669
2670            boxes.push_back(BoxGroupPair(new_cell,new osg::Group));
2671        }
2672    }
2673
2674
2675    // create the groups to drop the children into
2676   
2677
2678    // bin each child into associated bb group
2679    typedef std::vector< osg::ref_ptr<osg::Node> > NodeList;
2680    NodeList unassignedList;
2681    for(i=0;i<group->getNumChildren();++i)
2682    {
2683        bool assigned = false;
2684        osg::Vec3 center = group->getChild(i)->getBound().center();
2685        for(Boxes::iterator itr=boxes.begin();
2686            itr!=boxes.end() && !assigned;
2687            ++itr)
2688        {
2689            if (itr->first.contains(center))
2690            {
2691                // move child from main group into bb group.
2692                (itr->second)->addChild(group->getChild(i));
2693                assigned = true;
2694            }
2695        }
2696        if (!assigned)
2697        {
2698            unassignedList.push_back(group->getChild(i));
2699        }
2700    }
2701
2702
2703    // now transfer nodes across, by :
2704    //      first removing from the original group,
2705    //      add in the bb groups
2706    //      add then the unassigned children.
2707   
2708   
2709    // first removing from the original group,
2710    group->removeChildren(0,group->getNumChildren());
2711   
2712    // add in the bb groups
2713    typedef std::vector< osg::ref_ptr<osg::Group> > GroupList;
2714    GroupList groupsToDivideList;
2715    for(Boxes::iterator itr=boxes.begin();
2716        itr!=boxes.end();
2717        ++itr)
2718    {
2719        // move child from main group into bb group.
2720        osg::Group* bb_group = (itr->second).get();
2721        if (bb_group->getNumChildren()>0)
2722        {
2723            if (bb_group->getNumChildren()==1)
2724            {
2725                group->addChild(bb_group->getChild(0));
2726            }
2727            else
2728            {
2729                group->addChild(bb_group);
2730                if (bb_group->getNumChildren()>maxNumTreesPerCell)
2731                {
2732                    groupsToDivideList.push_back(bb_group);
2733                }
2734            }
2735        }
2736    }
2737   
2738   
2739    // add then the unassigned children.
2740    for(NodeList::iterator nitr=unassignedList.begin();
2741        nitr!=unassignedList.end();
2742        ++nitr)
2743    {
2744        group->addChild(nitr->get());
2745    }
2746
2747    // now call divide on all groups that require it.
2748    for(GroupList::iterator gitr=groupsToDivideList.begin();
2749        gitr!=groupsToDivideList.end();
2750        ++gitr)
2751    {
2752        divide(gitr->get(),maxNumTreesPerCell);
2753    }
2754
2755    return (numChildrenOnEntry<group->getNumChildren());
2756   
2757}
2758
2759bool Optimizer::SpatializeGroupsVisitor::divide(osg::Geode* geode, unsigned int maxNumTreesPerCell)
2760{
2761
2762    if (geode->getNumDrawables()<=maxNumTreesPerCell) return false;
2763   
2764    // create the original box.
2765    osg::BoundingBox bb;
2766    unsigned int i;
2767    for(i=0; i<geode->getNumDrawables(); ++i)
2768    {
2769        bb.expandBy(geode->getDrawable(i)->getBound().center());
2770    }
2771   
2772    float radius = bb.radius();
2773    float divide_distance = radius*0.7f;
2774    bool xAxis = (bb.xMax()-bb.xMin())>divide_distance;
2775    bool yAxis = (bb.yMax()-bb.yMin())>divide_distance;
2776    bool zAxis = (bb.zMax()-bb.zMin())>divide_distance;
2777
2778    osg::notify(osg::INFO)<<"INFO "<<geode->className()<<"  num drawables = "<<geode->getNumDrawables()<<"  xAxis="<<xAxis<<"  yAxis="<<yAxis<<"   zAxis="<<zAxis<<std::endl;
2779   
2780    if (!xAxis && !yAxis && !zAxis)
2781    {
2782        osg::notify(osg::INFO)<<"  No axis to divide, stopping division."<<std::endl;
2783        return false;
2784    }
2785   
2786    osg::Node::ParentList parents = geode->getParents();
2787    if (parents.empty())
2788    {
2789        osg::notify(osg::INFO)<<"  Cannot perform spatialize on root Geode, add a Group above it to allow subdivision."<<std::endl;
2790        return false;
2791    }
2792
2793    osg::ref_ptr<osg::Group> group = new osg::Group;
2794    for(i=0; i<geode->getNumDrawables(); ++i)
2795    {
2796        osg::Geode* newGeode = new osg::Geode;
2797        newGeode->addDrawable(geode->getDrawable(i));
2798        group->addChild(newGeode);
2799    }
2800   
2801    divide(group.get(), maxNumTreesPerCell);
2802   
2803    // keep reference around to prevent it being deleted.
2804    osg::ref_ptr<osg::Geode> keepRefGeode = geode;
2805   
2806    for(osg::Node::ParentList::iterator itr = parents.begin();
2807        itr != parents.end();
2808        ++itr)
2809    {
2810        (*itr)->replaceChild(geode, group.get());
2811    }
2812   
2813    return true;
2814}
2815
2816////////////////////////////////////////////////////////////////////////////////////////////
2817//
2818//  Duplicated subgraphs which are shared
2819//
2820
2821void Optimizer::CopySharedSubgraphsVisitor::apply(osg::Node& node)
2822{
2823    if (node.getNumParents()>1 && isOperationPermissibleForObject(&node))
2824    {
2825        _sharedNodeList.insert(&node);
2826    }
2827    traverse(node);
2828}
2829
2830void Optimizer::CopySharedSubgraphsVisitor::copySharedNodes()
2831{
2832    osg::notify(osg::INFO)<<"Shared node "<<_sharedNodeList.size()<<std::endl;
2833    for(SharedNodeList::iterator itr=_sharedNodeList.begin();
2834        itr!=_sharedNodeList.end();
2835        ++itr)
2836    {
2837        osg::notify(osg::INFO)<<"   No parents "<<(*itr)->getNumParents()<<std::endl;
2838        osg::Node* node = *itr;
2839        for(unsigned int i=node->getNumParents()-1;i>0;--i)
2840        {
2841            // create a clone.
2842            osg::ref_ptr<osg::Object> new_object = node->clone(osg::CopyOp::DEEP_COPY_NODES |
2843                                                          osg::CopyOp::DEEP_COPY_DRAWABLES);
2844            // cast it to node.                                             
2845            osg::Node* new_node = dynamic_cast<osg::Node*>(new_object.get());
2846
2847            // replace the node by new_new
2848            if (new_node) node->getParent(i)->replaceChild(node,new_node);
2849        }
2850    }
2851}
2852
2853
2854////////////////////////////////////////////////////////////////////////////////////////////
2855//
2856//  Set the attributes of textures up.
2857//
2858
2859
2860void Optimizer::TextureVisitor::apply(osg::Node& node)
2861{
2862   
2863    osg::StateSet* ss = node.getStateSet();
2864    if (ss &&
2865        isOperationPermissibleForObject(&node) &&
2866        isOperationPermissibleForObject(ss))
2867    {
2868        apply(*ss);
2869    }
2870
2871    traverse(node);
2872}
2873
2874void Optimizer::TextureVisitor::apply(osg::Geode& geode)
2875{
2876    if (!isOperationPermissibleForObject(&geode)) return;
2877
2878    osg::StateSet* ss = geode.getStateSet();
2879   
2880    if (ss && isOperationPermissibleForObject(ss))
2881    {
2882        apply(*ss);
2883    }
2884
2885    for(unsigned int i=0;i<geode.getNumDrawables();++i)
2886    {
2887        osg::Drawable* drawable = geode.getDrawable(i);
2888        if (drawable)
2889        {
2890            ss = drawable->getStateSet();
2891            if (ss &&
2892               isOperationPermissibleForObject(drawable) &&
2893               isOperationPermissibleForObject(ss))
2894            {
2895                apply(*ss);
2896            }
2897        }
2898    }
2899}
2900
2901void Optimizer::TextureVisitor::apply(osg::StateSet& stateset)
2902{
2903    for(unsigned int i=0;i<stateset.getTextureAttributeList().size();++i)
2904    {
2905        osg::StateAttribute* sa = stateset.getTextureAttribute(i,osg::StateAttribute::TEXTURE);
2906        osg::Texture* texture = dynamic_cast<osg::Texture*>(sa);
2907        if (texture && isOperationPermissibleForObject(texture))
2908        {
2909            apply(*texture);
2910        }
2911    }
2912}
2913
2914void Optimizer::TextureVisitor::apply(osg::Texture& texture)
2915{
2916    if (_changeAutoUnRef)
2917    {
2918        unsigned numImageStreams = 0;
2919        for (unsigned int i=0; i<texture.getNumImages(); ++i)
2920        {
2921            osg::ImageStream* is = dynamic_cast<osg::ImageStream*>(texture.getImage(i));
2922            if (is) ++numImageStreams;
2923        }
2924       
2925        if (numImageStreams==0)
2926        {
2927            texture.setUnRefImageDataAfterApply(_valueAutoUnRef);
2928        }
2929    }
2930   
2931    if (_changeClientImageStorage)
2932    {
2933        texture.setClientStorageHint(_valueClientImageStorage);
2934    }
2935   
2936    if (_changeAnisotropy)
2937    {
2938        texture.setMaxAnisotropy(_valueAnisotropy);
2939    }
2940
2941}
2942
2943////////////////////////////////////////////////////////////////////////////
2944// Merge geodes
2945////////////////////////////////////////////////////////////////////////////
2946
2947void Optimizer::MergeGeodesVisitor::apply(osg::Group& group)
2948{
2949    if (typeid(group)==typeid(osg::Group)) mergeGeodes(group);
2950    traverse(group);
2951}
2952
2953struct LessGeode
2954{
2955    bool operator() (const osg::Geode* lhs,const osg::Geode* rhs) const
2956    {
2957        if (lhs->getNodeMask()<rhs->getNodeMask()) return true;
2958        if (lhs->getNodeMask()>rhs->getNodeMask()) return false;
2959
2960        return (lhs->getStateSet()<rhs->getStateSet());
2961    }
2962};
2963
2964bool Optimizer::MergeGeodesVisitor::mergeGeodes(osg::Group& group)
2965{
2966    if (!isOperationPermissibleForObject(&group)) return false;
2967
2968    typedef std::vector< osg::Geode* >                      DuplicateList;
2969    typedef std::map<osg::Geode*,DuplicateList,LessGeode>   GeodeDuplicateMap;
2970
2971    unsigned int i;
2972    osg::NodeList children;
2973    children.resize(group.getNumChildren());
2974    for (i=0; i<group.getNumChildren(); ++i)
2975    {
2976        // keep a reference to this child so we can safely clear the group of all children
2977        // this is done so we don't have to do a search and remove from the list later on.
2978        children[i] = group.getChild(i);
2979    }
2980   
2981    // remove all children
2982    group.removeChildren(0,group.getNumChildren());
2983
2984    GeodeDuplicateMap geodeDuplicateMap;
2985    for (i=0; i<children.size(); ++i)
2986    {
2987        osg::Node* child = children[i].get();
2988
2989        if (typeid(*child)==typeid(osg::Geode))
2990        {
2991            osg::Geode* geode = static_cast<osg::Geode*>(child);
2992            geodeDuplicateMap[geode].push_back(geode);
2993        }
2994        else
2995        {
2996            // not a geode so just add back into group as its a normal child
2997            group.addChild(child);
2998        }
2999    }
3000
3001    // if no geodes then just return.
3002    if (geodeDuplicateMap.empty()) return false;
3003   
3004    osg::notify(osg::INFO)<<"mergeGeodes in group '"<<group.getName()<<"' "<<geodeDuplicateMap.size()<<std::endl;
3005   
3006    // merge
3007    for(GeodeDuplicateMap::iterator itr=geodeDuplicateMap.begin();
3008        itr!=geodeDuplicateMap.end();
3009        ++itr)
3010    {
3011        if (itr->second.size()>1)
3012        {
3013            osg::Geode* lhs = itr->second[0];
3014
3015            // add geode back into group
3016            group.addChild(lhs);
3017           
3018            for(DuplicateList::iterator dupItr=itr->second.begin()+1;
3019                dupItr!=itr->second.end();
3020                ++dupItr)
3021            {
3022                osg::Geode* rhs = *dupItr;
3023                mergeGeode(*lhs,*rhs);
3024            }
3025        }
3026        else
3027        {
3028            osg::Geode* lhs = itr->second[0];
3029
3030            // add geode back into group
3031            group.addChild(lhs);
3032        }
3033    }
3034
3035    return true;
3036}
3037
3038bool Optimizer::MergeGeodesVisitor::mergeGeode(osg::Geode& lhs, osg::Geode& rhs)
3039{
3040    for (unsigned int i=0; i<rhs.getNumDrawables(); ++i)
3041    {
3042        lhs.addDrawable(rhs.getDrawable(i));
3043    }
3044
3045    return true;
3046}
3047
3048
3049
3050////////////////////////////////////////////////////////////////////////////
3051// FlattenBillboardVisitor
3052////////////////////////////////////////////////////////////////////////////
3053void Optimizer::FlattenBillboardVisitor::reset()
3054{
3055    _billboards.clear();
3056}
3057
3058void Optimizer::FlattenBillboardVisitor::apply(osg::Billboard& billboard)
3059{
3060    _billboards[&billboard].push_back(getNodePath());
3061}
3062
3063void Optimizer::FlattenBillboardVisitor::process()
3064{
3065    for(BillboardNodePathMap::iterator itr = _billboards.begin();
3066        itr != _billboards.end();
3067        ++itr)
3068    {
3069        bool mergeAcceptable = true;   
3070
3071        osg::ref_ptr<osg::Billboard> billboard = itr->first;
3072
3073        NodePathList& npl = itr->second;
3074        osg::Group* mainGroup = 0;
3075        if (npl.size()>1)
3076        {       
3077            for(NodePathList::iterator nitr = npl.begin();
3078                nitr != npl.end();
3079                ++nitr)
3080            {
3081                osg::NodePath& np = *nitr;
3082                if (np.size()>=3)
3083                {
3084                    osg::Group* group = dynamic_cast<osg::Group*>(np[np.size()-3]);
3085                    if (mainGroup==0) mainGroup = group;
3086
3087                    osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(np[np.size()-2]);
3088
3089                    if (group == mainGroup &&
3090                        np[np.size()-1]==billboard.get() &&
3091                        mt && mt->getDataVariance()==osg::Object::STATIC &&
3092                        mt->getNumChildren()==1)
3093                    {
3094                        const osg::Matrix& m = mt->getMatrix();
3095                        mergeAcceptable = (m(0,0)==1.0 && m(0,1)==0.0 && m(0,2)==0.0 && m(0,3)==0.0 &&
3096                                           m(1,0)==0.0 && m(1,1)==1.0 && m(1,2)==0.0 && m(1,3)==0.0 &&
3097                                           m(2,0)==0.0 && m(2,1)==0.0 && m(2,2)==1.0 && m(2,3)==0.0 &&
3098                                           m(3,3)==1.0);
3099                    }
3100                    else
3101                    {
3102                       mergeAcceptable = false;
3103                    }
3104                }
3105                else
3106                {
3107                    mergeAcceptable = false;
3108                }
3109            }
3110        }
3111        else
3112        {
3113            mergeAcceptable = false;
3114        }
3115
3116        if (mergeAcceptable)
3117        {
3118            osg::Billboard* new_billboard = new osg::Billboard;
3119            new_billboard->setMode(billboard->getMode());
3120            new_billboard->setAxis(billboard->getAxis());
3121            new_billboard->setStateSet(billboard->getStateSet());
3122            new_billboard->setName(billboard->getName());               
3123
3124            mainGroup->addChild(new_billboard);
3125
3126            typedef std::set<osg::MatrixTransform*> MatrixTransformSet;
3127            MatrixTransformSet mts;
3128
3129            for(NodePathList::iterator nitr = npl.begin();
3130                nitr != npl.end();
3131                ++nitr)
3132            {
3133                osg::NodePath& np = *nitr;
3134                osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(np[np.size()-2]);
3135                mts.insert(mt);
3136            }
3137
3138            for(MatrixTransformSet::iterator mitr = mts.begin();
3139                mitr != mts.end();
3140                ++mitr)
3141            {
3142                osg::MatrixTransform* mt = *mitr;
3143                for(unsigned int i=0; i<billboard->getNumDrawables(); ++i)
3144                {
3145                    new_billboard->addDrawable(billboard->getDrawable(i),
3146                                               billboard->getPosition(i)*mt->getMatrix());
3147                }
3148                mainGroup->removeChild(mt);
3149            }
3150        }
3151    }
3152
3153}
3154
3155
3156
3157////////////////////////////////////////////////////////////////////////////
3158// TextureAtlasBuilder
3159////////////////////////////////////////////////////////////////////////////
3160
3161Optimizer::TextureAtlasBuilder::TextureAtlasBuilder():
3162    _maximumAtlasWidth(2048),
3163    _maximumAtlasHeight(2048),
3164    _margin(8)
3165{
3166}
3167
3168void Optimizer::TextureAtlasBuilder::reset()
3169{
3170    _sourceList.clear();
3171    _atlasList.clear();
3172}
3173
3174void Optimizer::TextureAtlasBuilder::setMaximumAtlasSize(unsigned int width, unsigned int height)
3175{
3176    _maximumAtlasWidth = width;
3177    _maximumAtlasHeight = height;
3178}
3179
3180void Optimizer::TextureAtlasBuilder::setMargin(unsigned int margin)
3181{
3182    _margin = margin;
3183}
3184
3185void Optimizer::TextureAtlasBuilder::addSource(const osg::Image* image)
3186{
3187    if (!getSource(image)) _sourceList.push_back(new Source(image));
3188}
3189
3190void Optimizer::TextureAtlasBuilder::addSource(const osg::Texture2D* texture)
3191{
3192    if (!getSource(texture)) _sourceList.push_back(new Source(texture));
3193}
3194
3195void Optimizer::TextureAtlasBuilder::buildAtlas()
3196{
3197    // assign the source to the atlas
3198    _atlasList.clear();
3199    for(SourceList::iterator sitr = _sourceList.begin();
3200        sitr != _sourceList.end();
3201        ++sitr)
3202    {
3203        Source* source = sitr->get();
3204        if (source->suitableForAtlas(_maximumAtlasWidth,_maximumAtlasHeight,_margin))
3205        {
3206            bool addedSourceToAtlas = false;
3207            for(AtlasList::iterator aitr = _atlasList.begin();
3208                aitr != _atlasList.end() && !addedSourceToAtlas;
3209                ++aitr)
3210            {
3211                osg::notify(osg::INFO)<<"checking source "<<source->_image->getFileName()<<" to see it it'll fit in atlas "<<aitr->get()<<std::endl;
3212                if ((*aitr)->doesSourceFit(source))
3213                {
3214                    addedSourceToAtlas = true;
3215                    (*aitr)->addSource(source);
3216                }
3217                else
3218                {
3219                    osg::notify(osg::INFO)<<"source "<<source->_image->getFileName()<<" does not fit in atlas "<<aitr->get()<<std::endl;
3220                }
3221            }
3222
3223            if (!addedSourceToAtlas)
3224            {
3225                osg::notify(osg::INFO)<<"creating new Atlas for "<<source->_image->getFileName()<<std::endl;
3226
3227                osg::ref_ptr<Atlas> atlas = new Atlas(_maximumAtlasWidth,_maximumAtlasHeight,_margin);
3228                _atlasList.push_back(atlas.get());
3229               
3230                atlas->addSource(source);
3231            }
3232        }
3233    }
3234   
3235    // build the atlas which are suitable for use, and discard the rest.
3236    AtlasList activeAtlasList;
3237    for(AtlasList::iterator aitr = _atlasList.begin();
3238        aitr != _atlasList.end();
3239        ++aitr)
3240    {
3241        Atlas* atlas = aitr->get();
3242
3243        if (atlas->_sourceList.size()==1)
3244        {
3245            // no point building an atlas with only one entry
3246            // so disconnect the source.
3247            Source* source = atlas->_sourceList[0].get();
3248            source->_atlas = 0;
3249            atlas->_sourceList.clear();
3250        }
3251       
3252        if (!(atlas->_sourceList.empty()))
3253        {
3254            std::stringstream ostr;
3255            ostr<<"atlas_"<<activeAtlasList.size()<<".rgb";
3256            atlas->_image->setFileName(ostr.str());
3257       
3258            activeAtlasList.push_back(atlas);
3259            atlas->clampToNearestPowerOfTwoSize();
3260            atlas->copySources();
3261           
3262        }
3263    }
3264   
3265    // keep only the active atlas'
3266    _atlasList.swap(activeAtlasList);
3267
3268}
3269
3270osg::Image* Optimizer::TextureAtlasBuilder::getImageAtlas(unsigned int i)
3271{
3272    Source* source = _sourceList[i].get();
3273    Atlas* atlas = source ? source->_atlas : 0;
3274    return atlas ? atlas->_image.get() : 0;
3275}
3276
3277osg::Texture2D* Optimizer::TextureAtlasBuilder::getTextureAtlas(unsigned int i)
3278{
3279    Source* source = _sourceList[i].get();
3280    Atlas* atlas = source ? source->_atlas : 0;
3281    return atlas ? atlas->_texture.get() : 0;
3282}
3283
3284osg::Matrix Optimizer::TextureAtlasBuilder::getTextureMatrix(unsigned int i)
3285{
3286    Source* source = _sourceList[i].get();
3287    return source ? source->computeTextureMatrix() : osg::Matrix();
3288}
3289
3290osg::Image* Optimizer::TextureAtlasBuilder::getImageAtlas(const osg::Image* image)
3291{
3292    Source* source = getSource(image);
3293    Atlas* atlas = source ? source->_atlas : 0;
3294    return atlas ? atlas->_image.get() : 0;
3295}
3296
3297osg::Texture2D* Optimizer::TextureAtlasBuilder::getTextureAtlas(const osg::Image* image)
3298{
3299    Source* source = getSource(image);
3300    Atlas* atlas = source ? source->_atlas : 0;
3301    return atlas ? atlas->_texture.get() : 0;
3302}
3303
3304osg::Matrix Optimizer::TextureAtlasBuilder::getTextureMatrix(const osg::Image* image)
3305{
3306    Source* source = getSource(image);
3307    return source ? source->computeTextureMatrix() : osg::Matrix();
3308}
3309
3310osg::Image* Optimizer::TextureAtlasBuilder::getImageAtlas(const osg::Texture2D* texture)
3311{
3312    Source* source = getSource(texture);
3313    Atlas* atlas = source ? source->_atlas : 0;
3314    return atlas ? atlas->_image.get() : 0;
3315}
3316
3317osg::Texture2D* Optimizer::TextureAtlasBuilder::getTextureAtlas(const osg::Texture2D* texture)
3318{
3319    Source* source = getSource(texture);
3320    Atlas* atlas = source ? source->_atlas : 0;
3321    return atlas ? atlas->_texture.get() : 0;
3322}
3323
3324osg::Matrix Optimizer::TextureAtlasBuilder::getTextureMatrix(const osg::Texture2D* texture)
3325{
3326    Source* source = getSource(texture);
3327    return source ? source->computeTextureMatrix() : osg::Matrix();
3328}
3329
3330Optimizer::TextureAtlasBuilder::Source* Optimizer::TextureAtlasBuilder::getSource(const osg::Image* image)
3331{
3332    for(SourceList::iterator itr = _sourceList.begin();
3333        itr != _sourceList.end();
3334        ++itr)
3335    {
3336        if ((*itr)->_image == image) return itr->get();
3337    }
3338    return 0;
3339}
3340
3341Optimizer::TextureAtlasBuilder::Source* Optimizer::TextureAtlasBuilder::getSource(const osg::Texture2D* texture)
3342{
3343    for(SourceList::iterator itr = _sourceList.begin();
3344        itr != _sourceList.end();
3345        ++itr)
3346    {
3347        if ((*itr)->_texture == texture) return itr->get();
3348    }
3349    return 0;
3350}
3351
3352bool Optimizer::TextureAtlasBuilder::Source::suitableForAtlas(unsigned int maximumAtlasWidth, unsigned int maximumAtlasHeight, unsigned int margin)
3353{
3354    if (!_image) return false;
3355   
3356    // size too big?
3357    if (_image->s()+margin*2 > maximumAtlasWidth) return false;
3358    if (_image->t()+margin*2 > maximumAtlasHeight) return false;
3359   
3360    switch(_image->getPixelFormat())
3361    {
3362        case(GL_COMPRESSED_ALPHA_ARB):
3363        case(GL_COMPRESSED_INTENSITY_ARB):
3364        case(GL_COMPRESSED_LUMINANCE_ALPHA_ARB):
3365        case(GL_COMPRESSED_LUMINANCE_ARB):
3366        case(GL_COMPRESSED_RGBA_ARB):
3367        case(GL_COMPRESSED_RGB_ARB):
3368        case(GL_COMPRESSED_RGB_S3TC_DXT1_EXT):
3369        case(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT):
3370        case(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT):
3371        case(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT):
3372            // can't handle compressed textures inside an atlas
3373            return false;
3374        default:
3375            break;
3376    }
3377
3378    if ((_image->getPixelSizeInBits() % 8) != 0)
3379    {
3380        // pixel size not byte aligned so report as not suitable to prevent other atlas code from having problems with byte boundaries.
3381        return false;
3382    }
3383
3384    if (_texture.valid())
3385    {
3386
3387        if (_texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
3388            _texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR)
3389        {
3390            // can't support repeating textures in texture atlas
3391            return false;
3392        }
3393
3394        if (_texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
3395            _texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR)
3396        {
3397            // can't support repeating textures in texture atlas
3398            return false;
3399        }
3400
3401        if (_texture->getReadPBuffer()!=0)
3402        {
3403            // pbuffer textures not suitable
3404            return false;
3405        }
3406    }
3407   
3408    return true;
3409}
3410
3411osg::Matrix Optimizer::TextureAtlasBuilder::Source::computeTextureMatrix() const
3412{
3413    if (!_atlas) return osg::Matrix();
3414    if (!_image) return osg::Matrix();
3415    if (!(_atlas->_image)) return osg::Matrix();
3416   
3417    return osg::Matrix::scale(float(_image->s())/float(_atlas->_image->s()), float(_image->t())/float(_atlas->_image->t()), 1.0)*
3418           osg::Matrix::translate(float(_x)/float(_atlas->_image->s()), float(_y)/float(_atlas->_image->t()), 0.0);
3419}
3420
3421bool Optimizer::TextureAtlasBuilder::Atlas::doesSourceFit(Source* source)
3422{
3423    // does the source have a valid image?
3424    const osg::Image* sourceImage = source->_image.get();
3425    if (!sourceImage) return false;
3426   
3427    // does pixel format match?
3428    if (_image.valid())
3429    {
3430        if (_image->getPixelFormat() != sourceImage->getPixelFormat()) return false;
3431        if (_image->getDataType() != sourceImage->getDataType()) return false;
3432    }
3433   
3434    const osg::Texture2D* sourceTexture = source->_texture.get();
3435    if (sourceTexture)
3436    {
3437        if (sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
3438            sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR)
3439        {
3440            // can't support repeating textures in texture atlas
3441            return false;
3442        }
3443
3444        if (sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
3445            sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR)
3446        {
3447            // can't support repeating textures in texture atlas
3448            return false;
3449        }
3450
3451        if (sourceTexture->getReadPBuffer()!=0)
3452        {
3453            // pbuffer textures not suitable
3454            return false;
3455        }
3456
3457        if (_texture.valid())
3458        {
3459
3460            bool sourceUsesBorder = sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::CLAMP_TO_BORDER ||
3461                                    sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::CLAMP_TO_BORDER;
3462
3463            bool atlasUsesBorder = sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::CLAMP_TO_BORDER ||
3464                                   sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::CLAMP_TO_BORDER;
3465
3466            if (sourceUsesBorder!=atlasUsesBorder)
3467            {
3468                // border wrapping does not match
3469                return false;
3470            }
3471
3472            if (sourceUsesBorder)
3473            {
3474                // border colours don't match
3475                if (_texture->getBorderColor() != sourceTexture->getBorderColor()) return false;
3476            }
3477           
3478            if (_texture->getFilter(osg::Texture2D::MIN_FILTER) != sourceTexture->getFilter(osg::Texture2D::MIN_FILTER))
3479            {
3480                // inconsitent min filters
3481                return false;
3482            }
3483 
3484            if (_texture->getFilter(osg::Texture2D::MAG_FILTER) != sourceTexture->getFilter(osg::Texture2D::MAG_FILTER))
3485            {
3486                // inconsitent mag filters
3487                return false;
3488            }
3489           
3490            if (_texture->getMaxAnisotropy() != sourceTexture->getMaxAnisotropy())
3491            {
3492                // anisotropy different.
3493                return false;
3494            }
3495           
3496            if (_texture->getInternalFormat() != sourceTexture->getInternalFormat())
3497            {
3498                // internal formats inconistent
3499                return false;
3500            }
3501           
3502            if (_texture->getShadowCompareFunc() != sourceTexture->getShadowCompareFunc())
3503            {
3504                // shadow functions inconsitent
3505                return false;
3506            }
3507               
3508            if (_texture->getShadowTextureMode() != sourceTexture->getShadowTextureMode())
3509            {
3510                // shadow texture mode inconsitent
3511                return false;
3512            }
3513
3514            if (_texture->getShadowAmbient() != sourceTexture->getShadowAmbient())
3515            {
3516                // shadow ambient inconsitent
3517                return false;
3518            }
3519        }
3520    }
3521   
3522    if (sourceImage->s() + 2*_margin > _maximumAtlasWidth)
3523    {
3524        // image too big for Atlas
3525        return false;
3526    }
3527   
3528    if (sourceImage->t() + 2*_margin > _maximumAtlasHeight)
3529    {
3530        // image too big for Atlas
3531        return false;
3532    }
3533
3534    if ((_y + sourceImage->t() + 2*_margin) > _maximumAtlasHeight)
3535    {
3536        // image doesn't have up space in height axis.
3537        return false;
3538    }
3539
3540    // does the source fit in the current row?
3541    if ((_x + sourceImage->s() + 2*_margin) <= _maximumAtlasWidth)
3542    {
3543        // yes it fits :-)
3544        osg::notify(osg::INFO)<<"Fits in current row"<<std::endl;
3545        return true;
3546    }
3547
3548    // does the source fit in the new row up?
3549    if ((_height + sourceImage->t() + 2*_margin) <= _maximumAtlasHeight)
3550    {
3551        // yes it fits :-)
3552        osg::notify(osg::INFO)<<"Fits in next row"<<std::endl;
3553        return true;
3554    }
3555   
3556    // no space for the texture
3557    return false;
3558}
3559
3560bool Optimizer::TextureAtlasBuilder::Atlas::addSource(Source* source)
3561{
3562    // double check source is compatible
3563    if (!doesSourceFit(source))
3564    {
3565        osg::notify(osg::INFO)<<"source "<<source->_image->getFileName()<<" does not fit in atlas "<<this<<std::endl;
3566        return false;
3567    }
3568   
3569    const osg::Image* sourceImage = source->_image.get();
3570    const osg::Texture2D* sourceTexture = source->_texture.get();
3571
3572    if (!_image)
3573    {
3574        // need to create an image of the same pixel format to store the atlas in
3575        _image = new osg::Image;
3576        _image->setPixelFormat(sourceImage->getPixelFormat());
3577        _image->setDataType(sourceImage->getDataType());
3578    }
3579   
3580    if (!_texture && sourceTexture)
3581    {
3582        _texture = new osg::Texture2D(_image.get());
3583
3584        _texture->setWrap(osg::Texture2D::WRAP_S, sourceTexture->getWrap(osg::Texture2D::WRAP_S));
3585        _texture->setWrap(osg::Texture2D::WRAP_T, sourceTexture->getWrap(osg::Texture2D::WRAP_T));
3586       
3587        _texture->setBorderColor(sourceTexture->getBorderColor());
3588        _texture->setBorderWidth(0);
3589           
3590        _texture->setFilter(osg::Texture2D::MIN_FILTER, sourceTexture->getFilter(osg::Texture2D::MIN_FILTER));
3591        _texture->setFilter(osg::Texture2D::MAG_FILTER, sourceTexture->getFilter(osg::Texture2D::MAG_FILTER));
3592
3593        _texture->setMaxAnisotropy(sourceTexture->getMaxAnisotropy());
3594
3595        _texture->setInternalFormat(sourceTexture->getInternalFormat());
3596
3597        _texture->setShadowCompareFunc(sourceTexture->getShadowCompareFunc());
3598        _texture->setShadowTextureMode(sourceTexture->getShadowTextureMode());
3599        _texture->setShadowAmbient(sourceTexture->getShadowAmbient());
3600
3601    }
3602
3603    // now work out where to fit it, first try current row.
3604    if ((_x + sourceImage->s() + 2*_margin) <= _maximumAtlasWidth)
3605    {
3606        // yes it fits, so add the source to the atlas's list of sources it contains
3607        _sourceList.push_back(source);
3608
3609        osg::notify(osg::INFO)<<"current row insertion, source "<<source->_image->getFileName()<<" "<<_x<<","<<_y<<" fits in row of atlas "<<this<<std::endl;
3610
3611        // set up the source so it knows where it is in the atlas
3612        source->_x = _x + _margin;
3613        source->_y = _y + _margin;
3614        source->_atlas = this;
3615       
3616        // move the atlas' cursor along to the right
3617        _x += sourceImage->s() + 2*_margin;
3618       
3619        if (_x > _width) _width = _x;
3620       
3621        unsigned int localTop = _y + sourceImage->t() + 2*_margin;
3622        if ( localTop > _height) _height = localTop;
3623
3624        return true;
3625    }
3626
3627    // does the source fit in the new row up?
3628    if ((_height + sourceImage->t() + 2*_margin) <= _maximumAtlasHeight)
3629    {
3630        // now row so first need to reset the atlas cursor
3631        _x = 0;
3632        _y = _height;
3633
3634        // yes it fits, so add the source to the atlas' list of sources it contains
3635        _sourceList.push_back(source);
3636
3637        osg::notify(osg::INFO)<<"next row insertion, source "<<source->_image->getFileName()<<" "<<_x<<","<<_y<<" fits in row of atlas "<<this<<std::endl;
3638
3639        // set up the source so it knows where it is in the atlas
3640        source->_x = _x + _margin;
3641        source->_y = _y + _margin;
3642        source->_atlas = this;
3643       
3644        // move the atlas' cursor along to the right
3645        _x += sourceImage->s() + 2*_margin;
3646
3647        if (_x > _width) _width = _x;
3648
3649        _height = _y + sourceImage->t() + 2*_margin;
3650
3651        osg::notify(osg::INFO)<<"source "<<source->_image->getFileName()<<" "<<_x<<","<<_y<<" fits in row of atlas "<<this<<std::endl;
3652
3653        return true;
3654    }
3655
3656    osg::notify(osg::INFO)<<"source "<<source->_image->getFileName()<<" does not fit in atlas "<<this<<std::endl;
3657
3658    // shouldn't get here, unless doesSourceFit isn't working...
3659    return false;
3660}
3661
3662void Optimizer::TextureAtlasBuilder::Atlas::clampToNearestPowerOfTwoSize()
3663{
3664    unsigned int w = 1;
3665    while (w<_width) w *= 2;
3666
3667    unsigned int h = 1;
3668    while (h<_height) h *= 2;
3669   
3670    osg::notify(osg::INFO)<<"Clamping "<<_width<<", "<<_height<<" to "<<w<<","<<h<<std::endl;
3671   
3672    _width = w;
3673    _height = h;
3674}
3675
3676
3677void Optimizer::TextureAtlasBuilder::Atlas::copySources()
3678{
3679    osg::notify(osg::INFO)<<"Allocated to "<<_width<<","<<_height<<std::endl;
3680    _image->allocateImage(_width,_height,1,
3681                          _image->getPixelFormat(),_image->getDataType(),
3682                          _image->getPacking());
3683                         
3684    {
3685        // clear memory
3686        unsigned int size = _image->getTotalSizeInBytes();
3687        unsigned char* str = _image->data();
3688        for(unsigned int i=0; i<size; ++i) *(str++) = 0;
3689    }       
3690
3691    osg::notify(osg::INFO)<<"Atlas::copySources() "<<std::endl;
3692    for(SourceList::iterator itr = _sourceList.begin();
3693        itr !=_sourceList.end();
3694        ++itr)
3695    {
3696        Source* source = itr->get();
3697        Atlas* atlas = source->_atlas;
3698
3699        if (atlas)
3700        {
3701            osg::notify(osg::INFO)<<"Copying image "<<source->_image->getFileName()<<" to "<<source->_x<<" ,"<<source->_y<<std::endl;
3702            osg::notify(osg::INFO)<<"        image size "<<source->_image->s()<<","<<source->_image->t()<<std::endl;
3703
3704            const osg::Image* sourceImage = source->_image.get();
3705            osg::Image* atlasImage = atlas->_image.get();
3706
3707            unsigned int rowSize = sourceImage->getRowSizeInBytes();
3708            unsigned int pixelSizeInBits = sourceImage->getPixelSizeInBits();
3709            unsigned int pixelSizeInBytes = pixelSizeInBits/8;
3710            unsigned int marginSizeInBytes = pixelSizeInBytes*_margin;
3711
3712            unsigned int x = source->_x;
3713            unsigned int y = source->_y;
3714
3715            int t;
3716            for(t=0; t<sourceImage->t(); ++t, ++y)
3717            {
3718                unsigned char* destPtr = atlasImage->data(x, y);
3719                const unsigned char* sourcePtr = sourceImage->data(0, t);
3720                for(unsigned int i=0; i<rowSize; i++)
3721                {
3722                    *(destPtr++) = *(sourcePtr++);
3723                }
3724            }
3725           
3726            // copy top row margin
3727            y = source->_y + sourceImage->t();
3728            unsigned int m;
3729            for(m=0; m<_margin; ++m, ++y)
3730            {
3731                unsigned char* destPtr = atlasImage->data(x, y);
3732                const unsigned char* sourcePtr = sourceImage->data(0, sourceImage->t()-1);
3733                for(unsigned int i=0; i<rowSize; i++)
3734                {
3735                    *(destPtr++) = *(sourcePtr++);
3736                }
3737               
3738            }
3739           
3740
3741
3742            // copy bottom row margin
3743            y = source->_y-1;
3744            for(m=0; m<_margin; ++m, --y)
3745            {
3746                unsigned char* destPtr = atlasImage->data(x, y);
3747                const unsigned char* sourcePtr = sourceImage->data(0, 0);
3748                for(unsigned int i=0; i<rowSize; i++)
3749                {
3750                    *(destPtr++) = *(sourcePtr++);
3751                }
3752               
3753            }
3754
3755            // copy left column margin
3756            y = source->_y;
3757            for(t=0; t<sourceImage->t(); ++t, ++y)
3758            {
3759                x = source->_x-1;
3760                for(m=0; m<_margin; ++m, --x)
3761                {
3762                    unsigned char* destPtr = atlasImage->data(x, y);
3763                    const unsigned char* sourcePtr = sourceImage->data(0, t);
3764                    for(unsigned int i=0; i<pixelSizeInBytes; i++)
3765                    {
3766                        *(destPtr++) = *(sourcePtr++);
3767                    }
3768                }
3769            }           
3770
3771            // copy right column margin
3772            y = source->_y;
3773            for(t=0; t<sourceImage->t(); ++t, ++y)
3774            {
3775                x = source->_x + sourceImage->s();
3776                for(m=0; m<_margin; ++m, ++x)
3777                {
3778                    unsigned char* destPtr = atlasImage->data(x, y);
3779                    const unsigned char* sourcePtr = sourceImage->data(sourceImage->s()-1, t);
3780                    for(unsigned int i=0; i<pixelSizeInBytes; i++)
3781                    {
3782                        *(destPtr++) = *(sourcePtr++);
3783                    }
3784                }
3785            }           
3786
3787            // copy top left corner margin
3788            y = source->_y + sourceImage->t();
3789            for(m=0; m<_margin; ++m, ++y)
3790            {
3791                unsigned char* destPtr = atlasImage->data(source->_x - _margin, y);
3792                unsigned char* sourcePtr = atlasImage->data(source->_x - _margin, y-1); // copy from row below
3793                for(unsigned int i=0; i<marginSizeInBytes; i++)
3794                {
3795                    *(destPtr++) = *(sourcePtr++);
3796                }
3797            }
3798
3799            // copy top right corner margin
3800            y = source->_y + sourceImage->t();
3801            for(m=0; m<_margin; ++m, ++y)
3802            {
3803                unsigned char* destPtr = atlasImage->data(source->_x + sourceImage->s(), y);
3804                unsigned char* sourcePtr = atlasImage->data(source->_x + sourceImage->s(), y-1); // copy from row below
3805                for(unsigned int i=0; i<marginSizeInBytes; i++)
3806                {
3807                    *(destPtr++) = *(sourcePtr++);
3808                }
3809            }
3810
3811            // copy bottom left corner margin
3812            y = source->_y - 1;
3813            for(m=0; m<_margin; ++m, --y)
3814            {
3815                unsigned char* destPtr = atlasImage->data(source->_x - _margin, y);
3816                unsigned char* sourcePtr = atlasImage->data(source->_x - _margin, y+1); // copy from row below
3817                for(unsigned int i=0; i<marginSizeInBytes; i++)
3818                {
3819                    *(destPtr++) = *(sourcePtr++);
3820                }
3821            }
3822
3823            // copy bottom right corner margin
3824            y = source->_y - 1;
3825            for(m=0; m<_margin; ++m, --y)
3826            {
3827                unsigned char* destPtr = atlasImage->data(source->_x + sourceImage->s(), y);
3828                unsigned char* sourcePtr = atlasImage->data(source->_x + sourceImage->s(), y+1); // copy from row below
3829                for(unsigned int i=0; i<marginSizeInBytes; i++)
3830                {
3831                    *(destPtr++) = *(sourcePtr++);
3832                }
3833            }
3834
3835        }
3836    }
3837}
3838
3839
3840void Optimizer::TextureAtlasVisitor::reset()
3841{
3842    _statesetMap.clear();
3843    _statesetStack.clear();
3844    _textures.clear();
3845    _builder.reset();
3846}
3847
3848bool Optimizer::TextureAtlasVisitor::pushStateSet(osg::StateSet* stateset)
3849{
3850    osg::StateSet::TextureAttributeList& tal = stateset->getTextureAttributeList();
3851   
3852    // if no textures ignore
3853    if (tal.empty()) return false;
3854   
3855    bool pushStateState = false;
3856
3857    // if already in stateset list ignore
3858    if (_statesetMap.count(stateset)>0)
3859    {
3860        pushStateState = true;
3861    }
3862    else
3863    {
3864        bool containsTexture2D = false;
3865        for(unsigned int unit=0; unit<tal.size(); ++unit)
3866        {
3867            osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(unit,osg::StateAttribute::TEXTURE));
3868            if (texture2D)
3869            {
3870                containsTexture2D = true;
3871                _textures.insert(texture2D);
3872            }
3873        }
3874
3875        if (containsTexture2D)
3876        {
3877            _statesetMap[stateset];
3878            pushStateState = true;
3879        }
3880    }
3881   
3882    if (pushStateState)
3883    {
3884        _statesetStack.push_back(stateset);
3885    }
3886
3887   
3888    return pushStateState; 
3889}
3890
3891void Optimizer::TextureAtlasVisitor::popStateSet()
3892{
3893    _statesetStack.pop_back();
3894}
3895
3896void Optimizer::TextureAtlasVisitor::apply(osg::Node& node)
3897{
3898    bool pushedStateState = false;
3899   
3900    osg::StateSet* ss = node.getStateSet();
3901    if (ss && ss->getDataVariance()==osg::Object::STATIC)
3902    {
3903        if (isOperationPermissibleForObject(&node) &&
3904            isOperationPermissibleForObject(ss))
3905        {
3906            pushedStateState = pushStateSet(ss);
3907        }
3908    }
3909
3910    traverse(node);
3911   
3912    if (pushedStateState) popStateSet();
3913}
3914
3915void Optimizer::TextureAtlasVisitor::apply(osg::Geode& geode)
3916{
3917    if (!isOperationPermissibleForObject(&geode)) return;
3918
3919    osg::StateSet* ss = geode.getStateSet();
3920   
3921   
3922    bool pushedGeodeStateState = false;
3923
3924    if (ss && ss->getDataVariance()==osg::Object::STATIC)
3925    {
3926        if (isOperationPermissibleForObject(ss))
3927        {
3928            pushedGeodeStateState = pushStateSet(ss);
3929        }
3930    }
3931    for(unsigned int i=0;i<geode.getNumDrawables();++i)
3932    {
3933
3934        osg::Drawable* drawable = geode.getDrawable(i);
3935        if (drawable)
3936        {
3937            bool pushedDrawableStateState = false;
3938
3939            ss = drawable->getStateSet();
3940            if (ss && ss->getDataVariance()==osg::Object::STATIC)
3941            {
3942                if (isOperationPermissibleForObject(drawable) &&
3943                    isOperationPermissibleForObject(ss))
3944                {
3945                    pushedDrawableStateState = pushStateSet(ss);
3946                }
3947            }
3948           
3949            if (!_statesetStack.empty())
3950            {
3951                for(StateSetStack::iterator ssitr = _statesetStack.begin();
3952                    ssitr != _statesetStack.end();
3953                    ++ssitr)
3954                {
3955                    _statesetMap[*ssitr].insert(drawable);
3956                }
3957            }
3958
3959            if (pushedDrawableStateState) popStateSet();
3960        }
3961       
3962    }
3963   
3964    if (pushedGeodeStateState) popStateSet();
3965}
3966
3967void Optimizer::TextureAtlasVisitor::optimize()
3968{
3969    _builder.reset();
3970   
3971    if (_textures.size()<2)
3972    {
3973        // nothing to optimize
3974        return;
3975    }
3976
3977    Textures texturesThatRepeat;
3978    Textures texturesThatRepeatAndAreOutOfRange;
3979
3980    StateSetMap::iterator sitr;
3981    for(sitr = _statesetMap.begin();
3982        sitr != _statesetMap.end();
3983        ++sitr)
3984    {
3985        osg::StateSet* stateset = sitr->first;
3986        Drawables& drawables = sitr->second;
3987
3988        osg::StateSet::TextureAttributeList& tal = stateset->getTextureAttributeList();
3989        for(unsigned int unit=0; unit<tal.size(); ++unit)
3990        {
3991            osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(unit,osg::StateAttribute::TEXTURE));
3992            if (texture)
3993            {
3994                bool s_repeat = texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
3995                                texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR;
3996
3997                bool t_repeat = texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
3998                                texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR;
3999                               
4000                if (s_repeat || t_repeat)
4001                {
4002                    texturesThatRepeat.insert(texture);
4003               
4004                    bool s_outOfRange = false;
4005                    bool t_outOfRange = false;
4006 
4007                    float s_min = -0.001;
4008                    float s_max = 1.001;
4009
4010                    float t_min = -0.001;
4011                    float t_max = 1.001;
4012                   
4013                    for(Drawables::iterator ditr = drawables.begin();
4014                        ditr != drawables.end();
4015                        ++ditr)
4016                    {
4017                        osg::Geometry* geom = (*ditr)->asGeometry();
4018                        osg::Vec2Array* texcoords = geom ? dynamic_cast<osg::Vec2Array*>(geom->getTexCoordArray(unit)) : 0;
4019                        if (texcoords && !texcoords->empty())
4020                        {
4021                            for(osg::Vec2Array::iterator titr = texcoords->begin();
4022                                titr != texcoords->end() /*&& !s_outOfRange && !t_outOfRange*/;
4023                                ++titr)
4024                            {
4025                                osg::Vec2 tc = *titr;
4026                                if (tc[0]<s_min) { s_min = tc[0]; s_outOfRange = true; }
4027                                if (tc[0]>s_max) { s_max = tc[0]; s_outOfRange = true; }
4028
4029                                if (tc[1]<t_min) { t_min = tc[1]; t_outOfRange = true; }
4030                                if (tc[1]>t_max) { t_max = tc[1]; t_outOfRange = true; }
4031                            }
4032                        }
4033                        else
4034                        {
4035                            // if no texcoords then texgen must be being used, therefore must assume that texture is truely repeating
4036                            s_outOfRange = true;
4037                            t_outOfRange = true;
4038                        }                       
4039                    }
4040
4041                    if (s_outOfRange || t_outOfRange)
4042                    {                   
4043                        texturesThatRepeatAndAreOutOfRange.insert(texture);
4044                    }
4045
4046                }
4047            }
4048        }
4049    }
4050
4051    // now change any texture that repeat but all texcoords to them
4052    // are in 0 to 1 range than converting the to CLAMP mode, to allow them
4053    // to be used in an atlas.
4054    Textures::iterator titr;
4055    for(titr = texturesThatRepeat.begin();
4056        titr != texturesThatRepeat.end();
4057        ++titr)
4058    {
4059        osg::Texture2D* texture = *titr;
4060        if (texturesThatRepeatAndAreOutOfRange.count(texture)==0)
4061        {
4062            // safe to convert into CLAMP wrap mode.
4063            osg::notify(osg::INFO)<<"Changing wrap mode to CLAMP"<<std::endl;
4064            texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture::CLAMP);
4065            texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture::CLAMP);
4066        }
4067    }
4068
4069    // add the textures as sources for the TextureAtlasBuilder
4070    for(titr = _textures.begin();
4071        titr != _textures.end();
4072        ++titr)
4073    {
4074        osg::Texture2D* texture = *titr;
4075
4076        bool s_repeat = texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
4077                        texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR;
4078
4079        bool t_repeat = texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
4080                        texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR;
4081
4082        if (!s_repeat && !t_repeat)
4083        {
4084            _builder.addSource(*titr);
4085        }
4086    }
4087   
4088    // build the atlas'
4089    _builder.buildAtlas();
4090
4091
4092    typedef std::set<osg::StateSet*> StateSetSet;
4093    typedef std::map<osg::Drawable*, StateSetSet> DrawableStateSetMap;
4094    DrawableStateSetMap dssm;
4095    for(sitr = _statesetMap.begin();
4096        sitr != _statesetMap.end();
4097        ++sitr)
4098    {
4099        Drawables& drawables = sitr->second;
4100        for(Drawables::iterator ditr = drawables.begin();
4101            ditr != drawables.end();
4102            ++ditr)
4103        {
4104            dssm[(*ditr)->asGeometry()].insert(sitr->first);
4105        }
4106    }
4107   
4108    Drawables drawablesThatHaveMultipleTexturesOnOneUnit;
4109    for(DrawableStateSetMap::iterator ditr = dssm.begin();
4110        ditr != dssm.end();
4111        ++ditr)
4112    {
4113        osg::Drawable* drawable = ditr->first;
4114        StateSetSet& ssm = ditr->second;
4115        if (ssm.size()>1)
4116        {
4117            typedef std::map<unsigned int, Textures> UnitTextureMap;
4118            UnitTextureMap unitTextureMap;
4119            for(StateSetSet::iterator ssm_itr = ssm.begin();
4120                ssm_itr != ssm.end();
4121                ++ssm_itr)
4122            {
4123                osg::StateSet* ss = *ssm_itr;
4124                unsigned int numTextureUnits = ss->getTextureAttributeList().size();
4125                for(unsigned int unit=0; unit<numTextureUnits; ++unit)
4126                {
4127                    osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE));
4128                    if (texture) unitTextureMap[unit].insert(texture);
4129                }
4130            }
4131            bool drawablesHasMultiTextureOnOneUnit = false;
4132            for(UnitTextureMap::iterator utm_itr = unitTextureMap.begin();
4133                utm_itr != unitTextureMap.end() && !drawablesHasMultiTextureOnOneUnit;
4134                ++utm_itr)
4135            {
4136                if (utm_itr->second.size()>1)
4137                {
4138                    drawablesHasMultiTextureOnOneUnit = true;
4139                }
4140            }
4141            if (drawablesHasMultiTextureOnOneUnit)
4142            {
4143                drawablesThatHaveMultipleTexturesOnOneUnit.insert(drawable);
4144            }
4145
4146        }
4147    }
4148       
4149    // remap the textures in the StateSet's
4150    for(sitr = _statesetMap.begin();
4151        sitr != _statesetMap.end();
4152        ++sitr)
4153    {
4154        osg::StateSet* stateset = sitr->first;
4155        osg::StateSet::TextureAttributeList& tal = stateset->getTextureAttributeList();
4156        for(unsigned int unit=0; unit<tal.size(); ++unit)
4157        {
4158            osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(unit,osg::StateAttribute::TEXTURE));
4159            if (texture)
4160            {
4161                bool s_repeat = texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
4162                                texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR;
4163
4164                bool t_repeat = texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
4165                                texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR;
4166
4167                osg::Texture2D* newTexture = _builder.getTextureAtlas(texture);
4168                if (newTexture && newTexture!=texture)
4169                {
4170                    if (s_repeat || t_repeat)
4171                    {
4172                        osg::notify(osg::NOTICE)<<"Warning!!! shouldn't get here"<<std::endl;
4173                    }
4174
4175                    stateset->setTextureAttribute(unit, newTexture);
4176                   
4177                    Drawables& drawables = sitr->second;
4178                   
4179                    osg::Matrix matrix = _builder.getTextureMatrix(texture);
4180                   
4181                    // first check to see if all drawables are ok for applying texturematrix to.
4182                    bool canTexMatBeFlattenedToAllDrawables = true;
4183                    for(Drawables::iterator ditr = drawables.begin();
4184                        ditr != drawables.end() && canTexMatBeFlattenedToAllDrawables;
4185                        ++ditr)
4186                    {
4187                        osg::Geometry* geom = (*ditr)->asGeometry();
4188                        osg::Vec2Array* texcoords = geom ? dynamic_cast<osg::Vec2Array*>(geom->getTexCoordArray(unit)) : 0;
4189
4190                        if (!texcoords)
4191                        {
4192                            canTexMatBeFlattenedToAllDrawables = false;
4193                        }
4194                       
4195                        if (drawablesThatHaveMultipleTexturesOnOneUnit.count(*ditr)!=0)
4196                        {
4197                            canTexMatBeFlattenedToAllDrawables = false;
4198                        }
4199                    }
4200
4201                    if (canTexMatBeFlattenedToAllDrawables)
4202                    {
4203                        // osg::notify(osg::NOTICE)<<"All drawables can be flattened "<<drawables.size()<<std::endl;
4204                        for(Drawables::iterator ditr = drawables.begin();
4205                            ditr != drawables.end();
4206                            ++ditr)
4207                        {
4208                            osg::Geometry* geom = (*ditr)->asGeometry();
4209                            osg::Vec2Array* texcoords = geom ? dynamic_cast<osg::Vec2Array*>(geom->getTexCoordArray(unit)) : 0;
4210                            if (texcoords)
4211                            {
4212                                for(osg::Vec2Array::iterator titr = texcoords->begin();
4213                                    titr != texcoords->end();
4214                                    ++titr)
4215                                {
4216                                    osg::Vec2 tc = *titr;
4217                                    (*titr).set(tc[0]*matrix(0,0) + tc[1]*matrix(1,0) + matrix(3,0),
4218                                              tc[0]*matrix(0,1) + tc[1]*matrix(1,1) + matrix(3,1));
4219                                }
4220                            }
4221                            else
4222                            {
4223                                osg::notify(osg::NOTICE)<<"Error, Optimizer::TextureAtlasVisitor::optimize() shouldn't ever get here..."<<std::endl;
4224                            }                       
4225                        }
4226                    }
4227                    else
4228                    {
4229                        // osg::notify(osg::NOTICE)<<"Applying TexMat "<<drawables.size()<<std::endl;
4230                        stateset->setTextureAttribute(unit, new osg::TexMat(matrix));
4231                    }
4232                }
4233            }
4234        }
4235
4236    }
4237}
4238
4239
4240
4241
4242////////////////////////////////////////////////////////////////////////////
4243// StaticObjectDectionVisitor
4244////////////////////////////////////////////////////////////////////////////
4245
4246void Optimizer::StaticObjectDetectionVisitor::apply(osg::Node& node)
4247{
4248    if (node.getStateSet()) applyStateSet(*node.getStateSet());
4249
4250    traverse(node);
4251}
4252
4253void Optimizer::StaticObjectDetectionVisitor::apply(osg::Geode& geode)
4254{
4255    if (geode.getStateSet()) applyStateSet(*geode.getStateSet());
4256
4257    for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
4258    {
4259        applyDrawable(*geode.getDrawable(i));
4260    }
4261}
4262
4263void Optimizer::StaticObjectDetectionVisitor::applyStateSet(osg::StateSet& stateset)
4264{
4265    stateset.computeDataVariance();
4266}
4267
4268
4269void Optimizer::StaticObjectDetectionVisitor::applyDrawable(osg::Drawable& drawable)
4270{   
4271   
4272    if (drawable.getStateSet()) applyStateSet(*drawable.getStateSet());
4273   
4274    drawable.computeDataVariance();
4275}
4276
4277
4278
4279////////////////////////////////////////////////////////////////////////////
4280// FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor
4281////////////////////////////////////////////////////////////////////////////
4282
4283void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::reset()
4284{
4285    _matrixStack.clear();
4286}
4287
4288void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Group& group)
4289{
4290    // only continue if there is a parent
4291    const unsigned int nodepathsize = _nodePath.size();
4292    if(!_matrixStack.empty() && group.getNumParents() > 1 && nodepathsize > 1)
4293    {
4294        // copy this Group
4295        osg::ref_ptr<osg::Object> new_obj = group.clone(osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4296        osg::Group* new_group = dynamic_cast<osg::Group*>(new_obj.get());
4297
4298        // New Group should only be added to parent through which this Group
4299        // was traversed, not to all parents of this Group.
4300        osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4301        if(parent_group)
4302        {
4303            parent_group->replaceChild(&group, new_group);
4304            // traverse the new Group
4305            traverse(*(new_group));
4306        }
4307        else
4308        {
4309            osg::notify(osg::NOTICE) << "No parent for this Group" << std::endl;
4310        }
4311    }
4312    else
4313    {
4314        // traverse original node
4315        traverse(group);
4316    }
4317}
4318
4319
4320void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Transform& transform)
4321{
4322    bool pushed = false;
4323
4324    // only continue if there is a parent and this is a STATIC transform
4325    const unsigned int nodepathsize = _nodePath.size();
4326    if(transform.getDataVariance() == osg::Object::STATIC && nodepathsize > 1)
4327    {
4328        osg::Matrix matrix;
4329        if(!_matrixStack.empty())
4330            matrix = _matrixStack.back();
4331        transform.computeLocalToWorldMatrix(matrix, this);
4332        _matrixStack.push_back(matrix);
4333        pushed = true;
4334   
4335        // convert this Transform to a Group
4336        osg::ref_ptr<osg::Group> group = new osg::Group(dynamic_cast<osg::Group&>(transform),
4337            osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4338
4339        // New Group should only be added to parent through which this Transform
4340        // was traversed, not to all parents of this Transform.
4341        osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4342        if(parent_group)
4343        {
4344            parent_group->replaceChild(&transform, group.get());
4345            // traverse the new Group
4346            traverse(*(group.get()));
4347        }
4348        else
4349        {
4350            osg::notify(osg::NOTICE) << "No parent for this Group" << std::endl;
4351        }
4352    }
4353    else
4354    {
4355        // traverse original node
4356        traverse(transform);
4357    }
4358
4359    // pop matrix off of stack
4360    if(pushed)
4361        _matrixStack.pop_back();
4362}
4363
4364
4365void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::LOD& lod)
4366{
4367    const unsigned int nodepathsize = _nodePath.size();
4368    if(!_matrixStack.empty() && lod.getNumParents() > 1 && nodepathsize > 1)
4369    {
4370        osg::ref_ptr<osg::LOD> new_lod = new osg::LOD(lod,
4371            osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4372
4373        // New LOD should only be added to parent through which this LOD
4374        // was traversed, not to all parents of this LOD.
4375        osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4376        if(parent_group)
4377        {
4378            parent_group->replaceChild(&lod, new_lod.get());
4379
4380            // move center point
4381            if(!_matrixStack.empty())
4382                new_lod->setCenter(new_lod->getCenter() * _matrixStack.back());
4383
4384            // traverse the new Group
4385            traverse(*(new_lod.get()));
4386        }
4387        else
4388            osg::notify(osg::NOTICE) << "No parent for this LOD" << std::endl;
4389    }
4390    else
4391    {
4392        // move center point
4393        if(!_matrixStack.empty())
4394            lod.setCenter(lod.getCenter() * _matrixStack.back());
4395
4396        traverse(lod);
4397    }
4398}
4399
4400
4401void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Geode& geode)
4402{
4403    if(!_matrixStack.empty())
4404    {
4405        // If there is only one parent, just transform all vertices and normals
4406        if(geode.getNumParents() == 1)
4407        {
4408            transformGeode(geode);
4409        }   
4410        else
4411        {
4412            // Else make a copy and then transform
4413            const unsigned int nodepathsize = _nodePath.size();
4414            if(nodepathsize > 1)
4415            {
4416                // convert this Transform to a Group
4417                osg::ref_ptr<osg::Geode> new_geode = new osg::Geode(geode,
4418                    osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4419
4420                // New Group should only be added to parent through which this Transform
4421                // was traversed, not to all parents of this Transform.
4422                osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4423                if(parent_group)
4424                    parent_group->replaceChild(&geode, new_geode.get());
4425                else
4426                    osg::notify(osg::NOTICE) << "No parent for this Geode" << std::endl;
4427
4428                transformGeode(*(new_geode.get()));
4429            }
4430        }
4431    }
4432}
4433
4434
4435void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Billboard& billboard)
4436{
4437    if(!_matrixStack.empty())
4438    {
4439        // If there is only one parent, just transform this Billboard
4440        if(billboard.getNumParents() == 1)
4441        {
4442            transformBillboard(billboard);
4443        }
4444        else
4445        {
4446            // Else make a copy and then transform
4447            const unsigned int nodepathsize = _nodePath.size();
4448            if(nodepathsize > 1)
4449            {
4450                // convert this Transform to a Group
4451                osg::ref_ptr<osg::Billboard> new_billboard = new osg::Billboard(billboard,
4452                    osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4453
4454                // New Billboard should only be added to parent through which this Billboard
4455                // was traversed, not to all parents of this Billboard.
4456                osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4457                if(parent_group)
4458                    parent_group->replaceChild(&billboard, new_billboard.get());
4459                else
4460                    osg::notify(osg::NOTICE) << "No parent for this Billboard" << std::endl;
4461
4462                transformBillboard(*(new_billboard.get()));
4463            }
4464        }
4465    }
4466}
4467
4468
4469void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::transformGeode(osg::Geode& geode)
4470{
4471    for(unsigned int i=0; i<geode.getNumDrawables(); i++)
4472    {
4473        transformDrawable(*geode.getDrawable(i));
4474    }
4475
4476    geode.dirtyBound();
4477}
4478
4479
4480void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::transformDrawable(osg::Drawable& drawable)
4481{
4482    osg::Geometry* geometry = drawable.asGeometry();
4483    if(geometry)
4484    {
4485        // transform all geometry
4486        osg::Vec3Array* verts = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
4487        if(verts)
4488        {
4489            for(unsigned int j=0; j<verts->size(); j++)
4490            {
4491                (*verts)[j] = (*verts)[j] * _matrixStack.back();
4492            }
4493        }
4494        else
4495        {
4496            osg::Vec4Array* verts = dynamic_cast<osg::Vec4Array*>(geometry->getVertexArray());
4497            if(verts)
4498            {
4499                for(unsigned int j=0; j<verts->size(); j++)
4500                {
4501                    (*verts)[j] = _matrixStack.back() * (*verts)[j];
4502                }
4503            }
4504        }
4505        osg::Vec3Array* normals = dynamic_cast<osg::Vec3Array*>(geometry->getNormalArray());
4506        if(normals)
4507        {
4508            for(unsigned int j=0; j<normals->size(); j++)
4509                (*normals)[j] = osg::Matrix::transform3x3((*normals)[j], _matrixStack.back());
4510        }
4511
4512        geometry->dirtyBound();
4513        geometry->dirtyDisplayList();
4514    }
4515}
4516
4517
4518void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::transformBillboard(osg::Billboard& billboard)
4519{
4520    osg::Vec3 axis = osg::Matrix::transform3x3(billboard.getAxis(), _matrixStack.back());
4521    axis.normalize();
4522    billboard.setAxis(axis);
4523
4524    osg::Vec3 normal = osg::Matrix::transform3x3(billboard.getNormal(), _matrixStack.back());
4525    normal.normalize();
4526    billboard.setNormal(normal);
4527
4528    for(unsigned int i=0; i<billboard.getNumDrawables(); i++)
4529    {
4530        osg::Vec3 originalBillboardPosition = billboard.getPosition(i);
4531        billboard.setPosition(i, originalBillboardPosition * _matrixStack.back());
4532
4533        osg::Matrix matrixForDrawable = _matrixStack.back();
4534        matrixForDrawable.preMult(osg::Matrix::translate(originalBillboardPosition));
4535        matrixForDrawable.postMult(osg::Matrix::translate(-billboard.getPosition(i)));
4536
4537        _matrixStack.push_back(matrixForDrawable);
4538        transformDrawable(*billboard.getDrawable(i));
4539        _matrixStack.pop_back();
4540    }
4541
4542    billboard.dirtyBound();
4543}
4544
4545
Note: See TracBrowser for help on using the browser.