root/OpenSceneGraph/trunk/src/osgUtil/Optimizer.cpp @ 9886

Revision 9886, 156.4 kB (checked in by robert, 6 years ago)

From Roland Smeenk, "While working on the Collada plugin I noticed that all geometry created by the dae reader result in slow path geometry.
Because there already exists the option to convert slow path geometry to the fast path by computing an internal fast path alternative, I added a new optimizer option that automatically does this. To check the results I also made some changes to the statistics gathering and rendering.

Somewhat unrelated, but also part of the optimizer I disabled removal of CameraView? nodes during RemoveRedundantNodes? optimization.
As discussed on the ML, CameraViews? were removed from the scenegraph. This solves that issue.

Summary:
-Geometry::areFastPathsUsed now also looks at internalOptimizedGeometry
-Added Optimize option to make all slow path geometry compute their internal fast path alternative
-Added fast geometry counter to the statistics
-Disabled removel of CameraViews? in optimizer
"

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