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

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

From Lionel Lagarde, "the attachment contains a correction of the Optimizer::MergeGeometryVisitor?.
When 2 geometries are merged, the primitive sets of the second geometry
are copied to the first geometry.

The primitive sets were copied with a std::insert into the first geometry
primitive set vector. It doesn't work when the geometry is using VBOs (because
the element buffer object of the primitive set is not updated).

The correction replaces

lhs.getPrimitiveSetList().insert( lhs.getPrimitiveSetList().end(),

rhs.getPrimitiveSetList().begin(),
rhs.getPrimitiveSetList().end() );

by

for( primItr=rhs.getPrimitiveSetList().begin();

primItr!=rhs.getPrimitiveSetList().end();
++primItr )

{

lhs.addPrimitiveSet(primItr->get());

}
"

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