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

Revision 11264, 155.6 kB (checked in by paulmartz, 5 years ago)

2.8 branch: Mergine recent changes to FBX. Revisions in this commit: r11251, r11252, r11262.

  • Property svn:mergeinfo set to
    /OpenSceneGraph/branches/OpenSceneGraph-2.8.2/src/osgUtil/Optimizer.cpp:10664
    /OpenSceneGraph/trunk/include/osg/src/osgUtil/Optimizer.cpp:9882
    /OpenSceneGraph/trunk/src/osgUtil/Optimizer.cpp:9769,9816-9818,9826-9827,9837-9843,9847,9850,9854-9857,9860-9861,9865,9869,9879-9880,9885,9895,9902,9906,9910,9912,9920,9932-9933,9948,9959-9960,9965,9968-9969,9987,9990,10010,10012-10014,10030,10040-10041,10053,10057,10076,10079-10080,10082,10084,10088,10128,10149,10161,10208,10264,10283,10340,10391-10392,10408,10414,10417,10422,10440,10456,10480,10487,10491,10520,10538,10622-10623,10625,10671-10672,10697,10722,10753,10758,10761,10788,10805,10809,10818,10851,10853-10855,10858,10887,10891-10892,10914,10923,10925,10932-10933,10938,10941-10942,10945,10961,11019,11032-11035,11056,11111,11123,11127,11131,11159,11165,11173,11175,11177,11180,11182,11194,11203,11237,11251-11252,11262
  • 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                group->setUserData(transform->getUserData());
1066                group->setDescriptions(transform->getDescriptions());
1067                for(unsigned int i=0;i<transform->getNumChildren();++i)
1068                {
1069                    group->addChild(transform->getChild(i));
1070                }
1071
1072                for(int i2=transform->getNumParents()-1;i2>=0;--i2)
1073                {
1074                    transform->getParent(i2)->replaceChild(transform.get(),group.get());
1075                }
1076            }
1077            else
1078            {
1079                osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(titr->first);
1080                if (mt) mt->setMatrix(osg::Matrix::identity());
1081                else
1082                {
1083                    osg::PositionAttitudeTransform* pat = dynamic_cast<osg::PositionAttitudeTransform*>(titr->first);
1084                    if (pat)
1085                    {
1086                        pat->setPosition(osg::Vec3(0.0f,0.0f,0.0f));
1087                        pat->setAttitude(osg::Quat());
1088                        pat->setPivotPoint(osg::Vec3(0.0f,0.0f,0.0f));
1089                    }
1090                    else
1091                    {
1092                        osg::notify(osg::WARN)<<"Warning:: during Optimize::CollectLowestTransformsVisitor::removeTransforms(Node*)"<<std::endl;
1093                        osg::notify(osg::WARN)<<"          unhandled of setting of indentity matrix on "<< titr->first->className()<<std::endl;
1094                        osg::notify(osg::WARN)<<"          model will appear in the incorrect position."<<std::endl;
1095                    }
1096                }
1097               
1098            }               
1099        }
1100    }
1101    _objectMap.clear();
1102    _transformMap.clear();
1103   
1104    return transformRemoved;
1105}
1106
1107void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Node& node)
1108{
1109    if (strcmp(node.className(),"LightPointNode")==0)
1110    {
1111        _excludedNodeSet.insert(&node);
1112    }
1113    traverse(node);
1114}
1115
1116
1117void Optimizer::FlattenStaticTransformsVisitor::apply(osg::ProxyNode& node)
1118{
1119    _excludedNodeSet.insert(&node);
1120
1121    traverse(node);
1122}
1123
1124void Optimizer::FlattenStaticTransformsVisitor::apply(osg::PagedLOD& node)
1125{
1126    _excludedNodeSet.insert(&node);
1127
1128    traverse(node);
1129}
1130
1131
1132void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Geode& geode)
1133{
1134    if (!_transformStack.empty())
1135    {
1136        for(unsigned int i=0;i<geode.getNumDrawables();++i)
1137        {
1138            osg::Geometry *geometry = geode.getDrawable(i)->asGeometry();
1139            if((geometry) && (isOperationPermissibleForObject(&geode)) && (isOperationPermissibleForObject(geometry)))
1140            {
1141                if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1) {
1142                    geometry->setVertexArray(dynamic_cast<osg::Array*>(geometry->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)));
1143                }
1144                if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1) {
1145                    geometry->setNormalArray(dynamic_cast<osg::Array*>(geometry->getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL)));
1146                }
1147            }
1148            _drawableSet.insert(geode.getDrawable(i));
1149        }
1150    }
1151}
1152
1153void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Billboard& billboard)
1154{
1155    if (!_transformStack.empty())
1156    {
1157        _billboardSet.insert(&billboard);
1158    }
1159}
1160
1161void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Transform& transform)
1162{
1163    if (!_transformStack.empty())
1164    {
1165        // we need to disable any transform higher in the list.
1166        _transformSet.insert(_transformStack.back());
1167    }
1168
1169    _transformStack.push_back(&transform);
1170
1171    // simple traverse the children as if this Transform didn't exist.
1172    traverse(transform);
1173
1174    _transformStack.pop_back();
1175}
1176
1177bool Optimizer::FlattenStaticTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)
1178{
1179    CollectLowestTransformsVisitor cltv(_optimizer);
1180
1181    for(NodeSet::iterator nitr=_excludedNodeSet.begin();
1182        nitr!=_excludedNodeSet.end();
1183        ++nitr)
1184    {
1185        cltv.collectDataFor(*nitr);
1186    }
1187
1188    for(DrawableSet::iterator ditr=_drawableSet.begin();
1189        ditr!=_drawableSet.end();
1190        ++ditr)
1191    {
1192        cltv.collectDataFor(*ditr);
1193    }
1194
1195    for(BillboardSet::iterator bitr=_billboardSet.begin();
1196        bitr!=_billboardSet.end();
1197        ++bitr)
1198    {
1199        cltv.collectDataFor(*bitr);
1200    }
1201   
1202    cltv.setUpMaps();
1203   
1204    for(TransformSet::iterator titr=_transformSet.begin();
1205        titr!=_transformSet.end();
1206        ++titr)
1207    {
1208        cltv.disableTransform(*titr);
1209    }
1210   
1211
1212    return cltv.removeTransforms(nodeWeCannotRemove);
1213}
1214
1215////////////////////////////////////////////////////////////////////////////
1216// CombineStatucTransforms
1217////////////////////////////////////////////////////////////////////////////
1218
1219void Optimizer::CombineStaticTransformsVisitor::apply(osg::MatrixTransform& transform)
1220{
1221    if (transform.getDataVariance()==osg::Object::STATIC &&
1222        transform.getNumChildren()==1 &&
1223        transform.getChild(0)->asTransform()!=0 &&
1224        transform.getChild(0)->asTransform()->asMatrixTransform()!=0 &&
1225        transform.getChild(0)->asTransform()->getDataVariance()==osg::Object::STATIC &&
1226        isOperationPermissibleForObject(&transform) && isOperationPermissibleForObject(transform.getChild(0)))
1227    {
1228        _transformSet.insert(&transform);
1229    }
1230   
1231    traverse(transform);
1232}
1233
1234bool Optimizer::CombineStaticTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)
1235{
1236    if (nodeWeCannotRemove && nodeWeCannotRemove->asTransform()!=0 && nodeWeCannotRemove->asTransform()->asMatrixTransform()!=0)
1237    {
1238        // remove topmost node if it exists in the transform set.
1239        TransformSet::iterator itr = _transformSet.find(nodeWeCannotRemove->asTransform()->asMatrixTransform());
1240        if (itr!=_transformSet.end()) _transformSet.erase(itr);
1241    }
1242   
1243    bool transformRemoved = false;
1244
1245    while (!_transformSet.empty())
1246    {
1247        // get the first available transform to combine.
1248        osg::ref_ptr<osg::MatrixTransform> transform = *_transformSet.begin();
1249        _transformSet.erase(_transformSet.begin());
1250       
1251        if (transform->getNumChildren()==1 &&
1252            transform->getChild(0)->asTransform()!=0 &&
1253            transform->getChild(0)->asTransform()->asMatrixTransform()!=0 &&
1254            transform->getChild(0)->asTransform()->getDataVariance()==osg::Object::STATIC)
1255        {
1256            // now combine with its child.
1257            osg::MatrixTransform* child = transform->getChild(0)->asTransform()->asMatrixTransform();
1258           
1259            osg::Matrix newMatrix = child->getMatrix()*transform->getMatrix();
1260            child->setMatrix(newMatrix);
1261            if (transform->getStateSet())
1262            {
1263                if(child->getStateSet()) child->getStateSet()->merge(*transform->getStateSet());
1264                else child->setStateSet(transform->getStateSet());
1265            }
1266           
1267            transformRemoved = true;
1268
1269            osg::Node::ParentList parents = transform->getParents();
1270            for(osg::Node::ParentList::iterator pitr=parents.begin();
1271                pitr!=parents.end();
1272                ++pitr)
1273            {
1274                (*pitr)->replaceChild(transform.get(),child);
1275            }
1276           
1277        }
1278       
1279    }
1280    return transformRemoved;
1281}
1282
1283////////////////////////////////////////////////////////////////////////////
1284// RemoveEmptyNodes.
1285////////////////////////////////////////////////////////////////////////////
1286
1287void Optimizer::RemoveEmptyNodesVisitor::apply(osg::Geode& geode)
1288{
1289    for(int i=geode.getNumDrawables()-1;i>=0;--i)
1290    {
1291        osg::Geometry* geom = geode.getDrawable(i)->asGeometry();
1292        if (geom && geom->empty() && isOperationPermissibleForObject(geom))
1293        {
1294           geode.removeDrawables(i,1);
1295        }
1296    }
1297
1298    if (geode.getNumParents()>0)
1299    {
1300        if (geode.getNumDrawables()==0 && isOperationPermissibleForObject(&geode)) _redundantNodeList.insert(&geode);
1301    }
1302}
1303
1304void Optimizer::RemoveEmptyNodesVisitor::apply(osg::Group& group)
1305{
1306    if (group.getNumParents()>0)
1307    {
1308        // only remove empty groups, but not empty occluders.
1309        if (group.getNumChildren()==0 && isOperationPermissibleForObject(&group) &&
1310            (typeid(group)==typeid(osg::Group) || dynamic_cast<osg::Transform*>(&group)))
1311        {
1312            _redundantNodeList.insert(&group);
1313        }
1314    }
1315    traverse(group);
1316}
1317
1318void Optimizer::RemoveEmptyNodesVisitor::removeEmptyNodes()
1319{
1320
1321    NodeList newEmptyGroups;
1322
1323    // keep iterator through until scene graph is cleaned of empty nodes.
1324    while (!_redundantNodeList.empty())
1325    {
1326        for(NodeList::iterator itr=_redundantNodeList.begin();
1327            itr!=_redundantNodeList.end();
1328            ++itr)
1329        {
1330           
1331            osg::ref_ptr<osg::Node> nodeToRemove = (*itr);
1332           
1333            // take a copy of parents list since subsequent removes will modify the original one.
1334            osg::Node::ParentList parents = nodeToRemove->getParents();
1335
1336            for(osg::Node::ParentList::iterator pitr=parents.begin();
1337                pitr!=parents.end();
1338                ++pitr)
1339            {
1340                osg::Group* parent = *pitr;
1341                if (!dynamic_cast<osg::Sequence*>(parent) &&
1342                    !dynamic_cast<osg::Switch*>(parent) &&
1343                    strcmp(parent->className(),"MultiSwitch")!=0)
1344                {
1345                    parent->removeChild(nodeToRemove.get());
1346                    if (parent->getNumChildren()==0) newEmptyGroups.insert(*pitr);
1347                }
1348            }               
1349        }
1350
1351        _redundantNodeList.clear();
1352        _redundantNodeList.swap(newEmptyGroups);
1353    }
1354}
1355
1356
1357////////////////////////////////////////////////////////////////////////////
1358// RemoveRedundantNodes.
1359////////////////////////////////////////////////////////////////////////////
1360
1361bool Optimizer::RemoveRedundantNodesVisitor::isOperationPermissible(osg::Node& node)
1362{
1363    return node.getNumParents()>0 &&
1364           !node.getStateSet() &&
1365           !node.getUserData() &&
1366           !node.getUpdateCallback() &&
1367           !node.getEventCallback() &&
1368           !node.getUpdateCallback() &&
1369           node.getDescriptions().empty() &&
1370           isOperationPermissibleForObject(&node);   
1371}
1372
1373void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Group& group)
1374{
1375    if (group.getNumChildren()==1 &&
1376        typeid(group)==typeid(osg::Group) &&
1377        isOperationPermissible(group))
1378    {
1379        _redundantNodeList.insert(&group);
1380    }
1381
1382    traverse(group);
1383}
1384
1385
1386
1387void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Transform& transform)
1388{
1389    if (transform.getDataVariance()==osg::Object::STATIC &&
1390        isOperationPermissible(transform))
1391    {
1392        osg::Matrix matrix;
1393        transform.computeWorldToLocalMatrix(matrix,NULL);
1394        if (matrix.isIdentity())
1395        {
1396            _redundantNodeList.insert(&transform);
1397        }
1398    }
1399    traverse(transform);
1400}
1401
1402
1403void Optimizer::RemoveRedundantNodesVisitor::removeRedundantNodes()
1404{
1405
1406    for(NodeList::iterator itr=_redundantNodeList.begin();
1407        itr!=_redundantNodeList.end();
1408        ++itr)
1409    {
1410        osg::ref_ptr<osg::Group> group = dynamic_cast<osg::Group*>(*itr);
1411        if (group.valid())
1412        {
1413            // take a copy of parents list since subsequent removes will modify the original one.
1414            osg::Node::ParentList parents = group->getParents();
1415
1416            if (group->getNumChildren()==1)
1417            {
1418                osg::Node* child = group->getChild(0);
1419                for(osg::Node::ParentList::iterator pitr=parents.begin();
1420                    pitr!=parents.end();
1421                    ++pitr)
1422                {
1423                    (*pitr)->replaceChild(group.get(),child);
1424                }
1425
1426            }
1427
1428        }
1429        else
1430        {
1431            osg::notify(osg::WARN)<<"Optimizer::RemoveRedundantNodesVisitor::removeRedundantNodes() - failed dynamic_cast"<<std::endl;
1432        }                               
1433    }
1434    _redundantNodeList.clear();
1435}
1436
1437
1438////////////////////////////////////////////////////////////////////////////
1439// RemoveLoadedProxyNodesVisitor.
1440////////////////////////////////////////////////////////////////////////////
1441
1442void Optimizer::RemoveLoadedProxyNodesVisitor::apply(osg::ProxyNode& proxyNode)
1443{
1444    if (proxyNode.getNumParents()>0 && proxyNode.getNumFileNames()==proxyNode.getNumChildren())
1445    {
1446        if (isOperationPermissibleForObject(&proxyNode))
1447        {
1448            _redundantNodeList.insert(&proxyNode);
1449        }
1450    }
1451    traverse(proxyNode);
1452}
1453
1454void Optimizer::RemoveLoadedProxyNodesVisitor::removeRedundantNodes()
1455{
1456
1457    for(NodeList::iterator itr=_redundantNodeList.begin();
1458        itr!=_redundantNodeList.end();
1459        ++itr)
1460    {
1461        osg::ref_ptr<osg::Group> group = dynamic_cast<osg::Group*>(*itr);
1462        if (group.valid())
1463        {
1464           
1465            // first check to see if data was attached to the ProxyNode that we need to keep.
1466            bool keepData = false;
1467            if (!group->getName().empty()) keepData = true;
1468            if (!group->getDescriptions().empty()) keepData = true;
1469            if (group->getNodeMask()) keepData = true;
1470            if (group->getUpdateCallback()) keepData = true;
1471            if (group->getEventCallback()) keepData = true;
1472            if (group->getCullCallback()) keepData = true;
1473
1474            if (keepData)
1475            {
1476                // create a group to store all proxy's children and data.
1477                osg::ref_ptr<osg::Group> newGroup = new osg::Group(*group,osg::CopyOp::SHALLOW_COPY);
1478               
1479
1480                // take a copy of parents list since subsequent removes will modify the original one.
1481                osg::Node::ParentList parents = group->getParents();
1482
1483                for(osg::Node::ParentList::iterator pitr=parents.begin();
1484                    pitr!=parents.end();
1485                    ++pitr)
1486                {
1487                    (*pitr)->replaceChild(group.get(),newGroup.get());
1488                }
1489
1490            }
1491            else
1492            {
1493                // take a copy of parents list since subsequent removes will modify the original one.
1494                osg::Node::ParentList parents = group->getParents();
1495
1496                for(osg::Node::ParentList::iterator pitr=parents.begin();
1497                    pitr!=parents.end();
1498                    ++pitr)
1499                {
1500                    (*pitr)->removeChild(group.get());
1501                    for(unsigned int i=0;i<group->getNumChildren();++i)
1502                    {
1503                        osg::Node* child = group->getChild(i);
1504                        (*pitr)->addChild(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) &&
1814                      geom->getDataVariance()!=osg::Object::DYNAMIC &&
1815                      isOperationPermissibleForObject(geom))
1816                {
1817                    geometryDuplicateMap[geom].push_back(geom);
1818                }
1819                else
1820                {
1821                    standardDrawables.push_back(geode.getDrawable(i));
1822                }
1823            }
1824            else
1825            {           
1826                standardDrawables.push_back(geode.getDrawable(i));
1827            }
1828        }
1829
1830#if 1
1831        bool needToDoMerge = false;
1832        MergeList mergeList;
1833        for(GeometryDuplicateMap::iterator itr=geometryDuplicateMap.begin();
1834            itr!=geometryDuplicateMap.end();
1835            ++itr)
1836        {
1837            mergeList.push_back(DuplicateList());
1838            DuplicateList* duplicateList = &mergeList.back();
1839           
1840            if (itr->second.size()>1)
1841            {
1842                std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType());
1843                osg::Geometry* lhs = itr->second[0];
1844
1845                duplicateList->push_back(lhs);
1846               
1847                unsigned int numVertices = lhs->getVertexArray() ? lhs->getVertexArray()->getNumElements() : 0;
1848
1849                for(DuplicateList::iterator dupItr=itr->second.begin()+1;
1850                    dupItr!=itr->second.end();
1851                    ++dupItr)
1852                {
1853               
1854                    osg::Geometry* rhs = *dupItr;
1855                   
1856                    unsigned int numRhsVertices = rhs->getVertexArray() ? rhs->getVertexArray()->getNumElements() : 0;
1857
1858                    if (numVertices+numRhsVertices < _targetMaximumNumberOfVertices)
1859                    {
1860                        duplicateList->push_back(rhs);
1861                        numVertices += numRhsVertices;
1862                        needToDoMerge = true;
1863                    }
1864                    else
1865                    {
1866                        numVertices = numRhsVertices;
1867                        mergeList.push_back(DuplicateList());
1868                        duplicateList = &mergeList.back();
1869                        duplicateList->push_back(rhs);
1870                    }
1871
1872                }
1873            }
1874            else if (itr->second.size()>0)
1875            {
1876                duplicateList->push_back(itr->second[0]);
1877            }
1878        }
1879       
1880        if (needToDoMerge)
1881        {
1882            // first take a reference to all the drawables to prevent them being deleted prematurely
1883            osg::Geode::DrawableList keepDrawables;
1884            keepDrawables.resize(geode.getNumDrawables());
1885            for(i=0; i<geode.getNumDrawables(); ++i)
1886            {
1887                keepDrawables[i] = geode.getDrawable(i);
1888            }
1889           
1890            // now clear the drawable list of the Geode so we don't have to remove items one by one (which is slow)
1891            geode.removeDrawables(0, geode.getNumDrawables());
1892           
1893            // add back in the standard drawables which arn't possible to merge.
1894            for(osg::Geode::DrawableList::iterator sitr = standardDrawables.begin();
1895                sitr != standardDrawables.end();
1896                ++sitr)
1897            {
1898                geode.addDrawable(sitr->get());
1899            }
1900               
1901            // now do the merging of geometries
1902            for(MergeList::iterator mitr = mergeList.begin();
1903                mitr != mergeList.end();
1904                ++mitr)
1905            {
1906                DuplicateList& duplicateList = *mitr;
1907                if (duplicateList.size()>1)
1908                {
1909                    osg::Geometry* lhs = duplicateList.front();
1910                    geode.addDrawable(lhs);
1911                    for(DuplicateList::iterator ditr = duplicateList.begin()+1;
1912                        ditr != duplicateList.end();
1913                        ++ditr)
1914                    {
1915                        mergeGeometry(*lhs,**ditr);
1916                    }
1917                }
1918                else if (duplicateList.size()>0)
1919                {
1920                    geode.addDrawable(duplicateList.front());
1921                }
1922            }
1923        }
1924
1925#else
1926        // don't merge geometry if its above a maximum number of vertices.
1927        for(GeometryDuplicateMap::iterator itr=geometryDuplicateMap.begin();
1928            itr!=geometryDuplicateMap.end();
1929            ++itr)
1930        {
1931            if (itr->second.size()>1)
1932            {
1933                std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType());
1934                osg::Geometry* lhs = itr->second[0];
1935                for(DuplicateList::iterator dupItr=itr->second.begin()+1;
1936                    dupItr!=itr->second.end();
1937                    ++dupItr)
1938                {
1939               
1940                    osg::Geometry* rhs = *dupItr;
1941                   
1942                    if (lhs->getVertexArray() && lhs->getVertexArray()->getNumElements()>=_targetMaximumNumberOfVertices)
1943                    {
1944                        lhs = rhs;
1945                        continue;
1946                    }
1947
1948                    if (rhs->getVertexArray() && rhs->getVertexArray()->getNumElements()>=_targetMaximumNumberOfVertices)
1949                    {
1950                        continue;
1951                    }
1952                   
1953                    if (lhs->getVertexArray() && rhs->getVertexArray() &&
1954                        (lhs->getVertexArray()->getNumElements()+rhs->getVertexArray()->getNumElements())>=_targetMaximumNumberOfVertices)
1955                    {
1956                        continue;
1957                    }
1958                   
1959                    if (mergeGeometry(*lhs,*rhs))
1960                    {
1961                        geode.removeDrawable(rhs);
1962
1963                        static int co = 0;
1964                        osg::notify(osg::INFO)<<"merged and removed Geometry "<<++co<<std::endl;
1965                    }
1966                }
1967            }
1968        }
1969#endif
1970
1971        // osg::notify(osg::NOTICE)<<"After "<<geode.getNumDrawables()<<std::endl;
1972
1973    }
1974
1975
1976    // convert all polygon primitives which has 3 indices into TRIANGLES, 4 indices into QUADS.
1977    unsigned int i;
1978    for(i=0;i<geode.getNumDrawables();++i)
1979    {
1980        osg::Geometry* geom = dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
1981        if (geom)
1982        {
1983            osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();
1984            for(osg::Geometry::PrimitiveSetList::iterator itr=primitives.begin();
1985                itr!=primitives.end();
1986                ++itr)
1987            {
1988                osg::PrimitiveSet* prim = itr->get();
1989                if (prim->getMode()==osg::PrimitiveSet::POLYGON)
1990                {
1991                    if (prim->getNumIndices()==3)
1992                    {
1993                        prim->setMode(osg::PrimitiveSet::TRIANGLES);
1994                    }
1995                    else if (prim->getNumIndices()==4)
1996                    {
1997                        prim->setMode(osg::PrimitiveSet::QUADS);
1998                    }
1999                }
2000            }
2001        }
2002    }
2003
2004    // now merge any compatible primitives.
2005    for(i=0;i<geode.getNumDrawables();++i)
2006    {
2007        osg::Geometry* geom = dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
2008        if (geom)
2009        {
2010            if (geom->getNumPrimitiveSets()>0 &&
2011                geom->getNormalBinding()!=osg::Geometry::BIND_PER_PRIMITIVE_SET &&
2012                geom->getColorBinding()!=osg::Geometry::BIND_PER_PRIMITIVE_SET &&
2013                geom->getSecondaryColorBinding()!=osg::Geometry::BIND_PER_PRIMITIVE_SET &&
2014                geom->getFogCoordBinding()!=osg::Geometry::BIND_PER_PRIMITIVE_SET)
2015            {
2016           
2017#if 1
2018                bool doneCombine = false;
2019
2020                osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();
2021                unsigned int lhsNo=0;
2022                unsigned int rhsNo=1;
2023                while(rhsNo<primitives.size())
2024                {
2025                    osg::PrimitiveSet* lhs = primitives[lhsNo].get();
2026                    osg::PrimitiveSet* rhs = primitives[rhsNo].get();
2027 
2028                    bool combine = false;
2029
2030                    if (lhs->getType()==rhs->getType() &&
2031                        lhs->getMode()==rhs->getMode())
2032                    {
2033
2034                        switch(lhs->getMode())
2035                        {
2036                        case(osg::PrimitiveSet::POINTS):
2037                        case(osg::PrimitiveSet::LINES):
2038                        case(osg::PrimitiveSet::TRIANGLES):
2039                        case(osg::PrimitiveSet::QUADS):
2040                            combine = true;       
2041                            break;
2042                        }
2043                       
2044                    }
2045
2046                    if (combine)
2047                    {
2048                   
2049                        switch(lhs->getType())
2050                        {
2051                        case(osg::PrimitiveSet::DrawArraysPrimitiveType):
2052                            combine = mergePrimitive(*(static_cast<osg::DrawArrays*>(lhs)),*(static_cast<osg::DrawArrays*>(rhs)));
2053                            break;
2054                        case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):
2055                            combine = mergePrimitive(*(static_cast<osg::DrawArrayLengths*>(lhs)),*(static_cast<osg::DrawArrayLengths*>(rhs)));
2056                            break;
2057                        case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):
2058                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUByte*>(lhs)),*(static_cast<osg::DrawElementsUByte*>(rhs)));
2059                            break;
2060                        case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):
2061                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUShort*>(lhs)),*(static_cast<osg::DrawElementsUShort*>(rhs)));
2062                            break;
2063                        case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):
2064                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUInt*>(lhs)),*(static_cast<osg::DrawElementsUInt*>(rhs)));
2065                            break;
2066                        default:
2067                            combine = false;
2068                            break;
2069                        }
2070                    }
2071
2072                    if (combine)
2073                    {
2074                        // make this primitive set as invalid and needing cleaning up.
2075                        rhs->setMode(0xffffff);
2076                        doneCombine = true;
2077                        ++rhsNo;
2078                    }
2079                    else
2080                    {
2081                        lhsNo = rhsNo;
2082                        ++rhsNo;
2083                    }
2084                }
2085
2086    #if 1               
2087                if (doneCombine)
2088                {
2089                    // now need to clean up primitiveset so it no longer contains the rhs combined primitives.
2090
2091                    // first swap with a empty primitiveSet to empty it completely.
2092                    osg::Geometry::PrimitiveSetList oldPrimitives;
2093                    primitives.swap(oldPrimitives);
2094                   
2095                    // now add the active primitive sets
2096                    for(osg::Geometry::PrimitiveSetList::iterator pitr = oldPrimitives.begin();
2097                        pitr != oldPrimitives.end();
2098                        ++pitr)
2099                    {
2100                        if ((*pitr)->getMode()!=0xffffff) primitives.push_back(*pitr);
2101                    }
2102                }
2103    #endif
2104
2105#else           
2106
2107                osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();
2108                unsigned int primNo=0;
2109                while(primNo+1<primitives.size())
2110                {
2111                    osg::PrimitiveSet* lhs = primitives[primNo].get();
2112                    osg::PrimitiveSet* rhs = primitives[primNo+1].get();
2113 
2114                    bool combine = false;
2115
2116                    if (lhs->getType()==rhs->getType() &&
2117                        lhs->getMode()==rhs->getMode())
2118                    {
2119
2120                        switch(lhs->getMode())
2121                        {
2122                        case(osg::PrimitiveSet::POINTS):
2123                        case(osg::PrimitiveSet::LINES):
2124                        case(osg::PrimitiveSet::TRIANGLES):
2125                        case(osg::PrimitiveSet::QUADS):
2126                            combine = true;       
2127                            break;
2128                        }
2129                       
2130                    }
2131
2132                    if (combine)
2133                    {
2134                   
2135                        switch(lhs->getType())
2136                        {
2137                        case(osg::PrimitiveSet::DrawArraysPrimitiveType):
2138                            combine = mergePrimitive(*(static_cast<osg::DrawArrays*>(lhs)),*(static_cast<osg::DrawArrays*>(rhs)));
2139                            break;
2140                        case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):
2141                            combine = mergePrimitive(*(static_cast<osg::DrawArrayLengths*>(lhs)),*(static_cast<osg::DrawArrayLengths*>(rhs)));
2142                            break;
2143                        case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):
2144                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUByte*>(lhs)),*(static_cast<osg::DrawElementsUByte*>(rhs)));
2145                            break;
2146                        case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):
2147                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUShort*>(lhs)),*(static_cast<osg::DrawElementsUShort*>(rhs)));
2148                            break;
2149                        case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):
2150                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUInt*>(lhs)),*(static_cast<osg::DrawElementsUInt*>(rhs)));
2151                            break;
2152                        default:
2153                            break;
2154                        }
2155                    }
2156                    if (combine)
2157                    {
2158                        primitives.erase(primitives.begin()+primNo+1);
2159                    }
2160
2161                    if (!combine)
2162                    {
2163                        primNo++;
2164                    }
2165                }
2166#endif
2167            }
2168        }
2169
2170
2171    }
2172
2173//    geode.dirtyBound();
2174
2175
2176    return false;
2177}
2178
2179bool Optimizer::MergeGeometryVisitor::geometryContainsSharedArrays(osg::Geometry& geom)
2180{
2181    if (geom.getVertexArray() && geom.getVertexArray()->referenceCount()>1) return true;
2182    if (geom.getNormalArray() && geom.getNormalArray()->referenceCount()>1) return true;
2183    if (geom.getColorArray() && geom.getColorArray()->referenceCount()>1) return true;
2184    if (geom.getSecondaryColorArray() && geom.getSecondaryColorArray()->referenceCount()>1) return true;
2185    if (geom.getFogCoordArray() && geom.getFogCoordArray()->referenceCount()>1) return true;
2186   
2187
2188    for(unsigned int unit=0;unit<geom.getNumTexCoordArrays();++unit)
2189    {
2190        osg::Array* tex = geom.getTexCoordArray(unit);
2191        if (tex && tex->referenceCount()>1) return true;
2192    }
2193   
2194    // shift the indices of the incoming primitives to account for the pre existing geometry.
2195    for(osg::Geometry::PrimitiveSetList::iterator primItr=geom.getPrimitiveSetList().begin();
2196        primItr!=geom.getPrimitiveSetList().end();
2197        ++primItr)
2198    {
2199        if ((*primItr)->referenceCount()>1) return true;
2200    }
2201   
2202   
2203    return false;
2204}
2205
2206
2207class MergeArrayVisitor : public osg::ArrayVisitor
2208{
2209    public:
2210   
2211        osg::Array* _lhs;
2212        int         _offset;
2213   
2214        MergeArrayVisitor() :
2215            _lhs(0),
2216            _offset(0) {}
2217           
2218           
2219        void merge(osg::Array* lhs,osg::Array* rhs, int offset=0)
2220        {
2221            if (lhs==0 || rhs==0) return;
2222            if (lhs->getType()!=rhs->getType()) return;
2223           
2224            _lhs = lhs;
2225            _offset = offset;
2226           
2227            rhs->accept(*this);
2228        }
2229       
2230        template<typename T>
2231        void _merge(T& rhs)
2232        {
2233            T* lhs = static_cast<T*>(_lhs);
2234            lhs->insert(lhs->end(),rhs.begin(),rhs.end());
2235        }
2236       
2237        template<typename T>
2238        void _mergeAndOffset(T& rhs)
2239        {
2240            T* lhs = static_cast<T*>(_lhs);
2241
2242            typename T::iterator itr;
2243            for(itr = rhs.begin();
2244                itr != rhs.end();
2245                ++itr)
2246            {
2247                lhs->push_back(*itr + _offset);
2248            }
2249        }
2250           
2251        virtual void apply(osg::Array&) { osg::notify(osg::WARN) << "Warning: Optimizer's MergeArrayVisitor cannot merge Array type." << std::endl; }
2252
2253        virtual void apply(osg::ByteArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2254        virtual void apply(osg::ShortArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2255        virtual void apply(osg::IntArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2256        virtual void apply(osg::UByteArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2257        virtual void apply(osg::UShortArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2258        virtual void apply(osg::UIntArray& rhs) { if (_offset) _mergeAndOffset(rhs); else  _merge(rhs); }
2259
2260        virtual void apply(osg::Vec4ubArray& rhs) { _merge(rhs); }
2261        virtual void apply(osg::FloatArray& rhs) { _merge(rhs); }
2262        virtual void apply(osg::Vec2Array& rhs) { _merge(rhs); }
2263        virtual void apply(osg::Vec3Array& rhs) { _merge(rhs); }
2264        virtual void apply(osg::Vec4Array& rhs) { _merge(rhs); }
2265       
2266        virtual void apply(osg::DoubleArray& rhs) { _merge(rhs); }
2267        virtual void apply(osg::Vec2dArray& rhs) { _merge(rhs); }
2268        virtual void apply(osg::Vec3dArray& rhs) { _merge(rhs); }
2269        virtual void apply(osg::Vec4dArray& rhs) { _merge(rhs); }
2270       
2271        virtual void apply(osg::Vec2bArray&  rhs) { _merge(rhs); }
2272        virtual void apply(osg::Vec3bArray&  rhs) { _merge(rhs); }
2273        virtual void apply(osg::Vec4bArray&  rhs) { _merge(rhs); }       
2274        virtual void apply(osg::Vec2sArray& rhs) { _merge(rhs); }
2275        virtual void apply(osg::Vec3sArray& rhs) { _merge(rhs); }
2276        virtual void apply(osg::Vec4sArray& rhs) { _merge(rhs); }
2277};
2278
2279bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs)
2280{
2281
2282    MergeArrayVisitor merger;
2283 
2284    unsigned int base = 0;
2285    unsigned int vbase = lhs.getVertexArray() ? lhs.getVertexArray()->getNumElements() : 0;
2286    if (lhs.getVertexArray() && rhs.getVertexArray())
2287    {
2288
2289        base = lhs.getVertexArray()->getNumElements();
2290        merger.merge(lhs.getVertexArray(),rhs.getVertexArray());
2291
2292    }
2293    else if (rhs.getVertexArray())
2294    {
2295        base = 0;
2296        lhs.setVertexArray(rhs.getVertexArray());
2297    }
2298   
2299    if (lhs.getVertexIndices() && rhs.getVertexIndices())
2300    {
2301
2302        base = lhs.getVertexIndices()->getNumElements();
2303        merger.merge(lhs.getVertexIndices(),rhs.getVertexIndices(),vbase);
2304
2305    }
2306    else if (rhs.getVertexIndices())
2307    {
2308        base = 0;
2309        lhs.setVertexIndices(rhs.getVertexIndices());
2310    }
2311   
2312
2313    unsigned int nbase = lhs.getNormalArray() ? lhs.getNormalArray()->getNumElements() : 0;
2314    if (lhs.getNormalArray() && rhs.getNormalArray() && lhs.getNormalBinding()!=osg::Geometry::BIND_OVERALL)
2315    {
2316        merger.merge(lhs.getNormalArray(),rhs.getNormalArray());
2317    }
2318    else if (rhs.getNormalArray())
2319    {
2320        lhs.setNormalArray(rhs.getNormalArray());
2321    }
2322
2323    if (lhs.getNormalIndices() && rhs.getNormalIndices() && lhs.getNormalBinding()!=osg::Geometry::BIND_OVERALL)
2324    {
2325        merger.merge(lhs.getNormalIndices(),rhs.getNormalIndices(),nbase);
2326    }
2327    else if (rhs.getNormalIndices())
2328    {
2329        // this assignment makes the assumption that lhs.NormalArray is empty as well and NormalIndices
2330        lhs.setNormalIndices(rhs.getNormalIndices());
2331    }
2332
2333
2334    unsigned int cbase = lhs.getColorArray() ? lhs.getColorArray()->getNumElements() : 0;
2335    if (lhs.getColorArray() && rhs.getColorArray() && lhs.getColorBinding()!=osg::Geometry::BIND_OVERALL)
2336    {
2337        merger.merge(lhs.getColorArray(),rhs.getColorArray());
2338    }
2339    else if (rhs.getColorArray())
2340    {
2341        lhs.setColorArray(rhs.getColorArray());
2342    }
2343   
2344    if (lhs.getColorIndices() && rhs.getColorIndices() && lhs.getColorBinding()!=osg::Geometry::BIND_OVERALL)
2345    {
2346        merger.merge(lhs.getColorIndices(),rhs.getColorIndices(),cbase);
2347    }
2348    else if (rhs.getColorIndices())
2349    {
2350        // this assignment makes the assumption that lhs.ColorArray is empty as well and ColorIndices
2351        lhs.setColorIndices(rhs.getColorIndices());
2352    }
2353
2354    unsigned int scbase = lhs.getSecondaryColorArray() ? lhs.getSecondaryColorArray()->getNumElements() : 0;
2355    if (lhs.getSecondaryColorArray() && rhs.getSecondaryColorArray() && lhs.getSecondaryColorBinding()!=osg::Geometry::BIND_OVERALL)
2356    {
2357        merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray());
2358    }
2359    else if (rhs.getSecondaryColorArray())
2360    {
2361        lhs.setSecondaryColorArray(rhs.getSecondaryColorArray());
2362    }
2363   
2364    if (lhs.getSecondaryColorIndices() && rhs.getSecondaryColorIndices() && lhs.getSecondaryColorBinding()!=osg::Geometry::BIND_OVERALL)
2365    {
2366        merger.merge(lhs.getSecondaryColorIndices(),rhs.getSecondaryColorIndices(),scbase);
2367    }
2368    else if (rhs.getSecondaryColorIndices())
2369    {
2370        // this assignment makes the assumption that lhs.SecondaryColorArray is empty as well and SecondaryColorIndices
2371        lhs.setSecondaryColorIndices(rhs.getSecondaryColorIndices());
2372    }
2373
2374    unsigned int fcbase = lhs.getFogCoordArray() ? lhs.getFogCoordArray()->getNumElements() : 0;
2375    if (lhs.getFogCoordArray() && rhs.getFogCoordArray() && lhs.getFogCoordBinding()!=osg::Geometry::BIND_OVERALL)
2376    {
2377        merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray());
2378    }
2379    else if (rhs.getFogCoordArray())
2380    {
2381        lhs.setFogCoordArray(rhs.getFogCoordArray());
2382    }
2383
2384    if (lhs.getFogCoordIndices() && rhs.getFogCoordIndices() && lhs.getFogCoordBinding()!=osg::Geometry::BIND_OVERALL)
2385    {
2386        merger.merge(lhs.getFogCoordIndices(),rhs.getFogCoordIndices(),fcbase);
2387    }
2388    else if (rhs.getFogCoordIndices())
2389    {
2390        // this assignment makes the assumption that lhs.FogCoordArray is empty as well and FogCoordIndices
2391        lhs.setFogCoordIndices(rhs.getFogCoordIndices());
2392    }
2393
2394
2395    unsigned int unit;
2396    for(unit=0;unit<lhs.getNumTexCoordArrays();++unit)
2397    {
2398        unsigned int tcbase = lhs.getTexCoordArray(unit) ? lhs.getTexCoordArray(unit)->getNumElements() : 0;
2399        merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit));
2400       
2401        if (lhs.getTexCoordIndices(unit) && rhs.getTexCoordIndices(unit))
2402        {
2403            merger.merge(lhs.getTexCoordIndices(unit),rhs.getTexCoordIndices(unit),tcbase);
2404        }
2405    }
2406   
2407    for(unit=0;unit<lhs.getNumVertexAttribArrays();++unit)
2408    {
2409        unsigned int vabase = lhs.getVertexAttribArray(unit) ? lhs.getVertexAttribArray(unit)->getNumElements() : 0;
2410        merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit));
2411       
2412        if (lhs.getVertexAttribIndices(unit) && rhs.getVertexAttribIndices(unit))
2413        {
2414            merger.merge(lhs.getVertexAttribIndices(unit),rhs.getVertexAttribIndices(unit),vabase);
2415        }
2416    }
2417
2418
2419    // shift the indices of the incoming primitives to account for the pre existing geometry.
2420    osg::Geometry::PrimitiveSetList::iterator primItr;
2421    for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr)
2422    {
2423        osg::PrimitiveSet* primitive = primItr->get();
2424       
2425        switch(primitive->getType())
2426        {
2427        case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):
2428            {
2429                osg::DrawElementsUByte* primitiveUByte = static_cast<osg::DrawElementsUByte*>(primitive);
2430                unsigned int currentMaximum = 0;
2431                for(osg::DrawElementsUByte::iterator eitr=primitiveUByte->begin();
2432                    eitr!=primitiveUByte->end();
2433                    ++eitr)
2434                {
2435                    currentMaximum = osg::maximum(currentMaximum,(unsigned int)*eitr);
2436                }
2437                if ((base+currentMaximum)>=65536)
2438                {
2439                    // must promote to a DrawElementsUInt
2440                    osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode());
2441                    std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive));
2442                    new_primitive->offsetIndices(base);
2443                    (*primItr) = new_primitive;
2444                } else if ((base+currentMaximum)>=256)
2445                {
2446                    // must promote to a DrawElementsUShort
2447                    osg::DrawElementsUShort* new_primitive = new osg::DrawElementsUShort(primitive->getMode());
2448                    std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive));
2449                    new_primitive->offsetIndices(base);
2450                    (*primItr) = new_primitive;
2451                }
2452                else
2453                {
2454                    primitive->offsetIndices(base);
2455                }
2456            }
2457            break;
2458
2459        case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):
2460            {
2461                osg::DrawElementsUShort* primitiveUShort = static_cast<osg::DrawElementsUShort*>(primitive);
2462                unsigned int currentMaximum = 0;
2463                for(osg::DrawElementsUShort::iterator eitr=primitiveUShort->begin();
2464                    eitr!=primitiveUShort->end();
2465                    ++eitr)
2466                {
2467                    currentMaximum = osg::maximum(currentMaximum,(unsigned int)*eitr);
2468                }
2469                if ((base+currentMaximum)>=65536)
2470                {
2471                    // must promote to a DrawElementsUInt
2472                    osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode());
2473                    std::copy(primitiveUShort->begin(),primitiveUShort->end(),std::back_inserter(*new_primitive));
2474                    new_primitive->offsetIndices(base);
2475                    (*primItr) = new_primitive;
2476                }
2477                else
2478                {
2479                    primitive->offsetIndices(base);
2480                }
2481            }
2482            break;
2483
2484        case(osg::PrimitiveSet::DrawArraysPrimitiveType):
2485        case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):
2486        case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):
2487        default:
2488            primitive->offsetIndices(base);
2489            break;
2490        }
2491    }
2492   
2493    for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr)
2494    {
2495        lhs.addPrimitiveSet(primItr->get());
2496    }
2497
2498    lhs.dirtyBound();
2499    lhs.dirtyDisplayList();
2500   
2501    return true;
2502}
2503
2504bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs)
2505{
2506    if (lhs.getFirst()+lhs.getCount()==rhs.getFirst())
2507    {
2508        lhs.setCount(lhs.getCount()+rhs.getCount());
2509        return true;
2510    }
2511    return false;
2512}
2513
2514bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawArrayLengths& lhs,osg::DrawArrayLengths& rhs)
2515{
2516    int lhs_count = std::accumulate(lhs.begin(),lhs.end(),0);
2517
2518    if (lhs.getFirst()+lhs_count==rhs.getFirst())
2519    {
2520        lhs.insert(lhs.end(),rhs.begin(),rhs.end());
2521        return true;
2522    }
2523    return false;
2524}
2525
2526bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUByte& lhs,osg::DrawElementsUByte& rhs)
2527{
2528    lhs.insert(lhs.end(),rhs.begin(),rhs.end());
2529    return true;
2530}
2531
2532bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUShort& lhs,osg::DrawElementsUShort& rhs)
2533{
2534    lhs.insert(lhs.end(),rhs.begin(),rhs.end());
2535    return true;
2536}
2537
2538bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUInt& lhs,osg::DrawElementsUInt& rhs)
2539{
2540    lhs.insert(lhs.end(),rhs.begin(),rhs.end());
2541    return true;
2542}
2543
2544
2545////////////////////////////////////////////////////////////////////////////////////////////
2546//
2547//  Spatialize the scene to accelerate culling
2548//
2549
2550void Optimizer::SpatializeGroupsVisitor::apply(osg::Group& group)
2551{
2552    if (typeid(group)==typeid(osg::Group) || group.asTransform())
2553    {
2554        if (isOperationPermissibleForObject(&group))
2555        {
2556            _groupsToDivideList.insert(&group);
2557        }
2558    }
2559    traverse(group);
2560}
2561
2562void Optimizer::SpatializeGroupsVisitor::apply(osg::Geode& geode)
2563{
2564    if (typeid(geode)==typeid(osg::Geode))
2565    {
2566        if (isOperationPermissibleForObject(&geode))
2567        {
2568            _geodesToDivideList.insert(&geode);
2569        }
2570    }
2571    traverse(geode);
2572}
2573
2574bool Optimizer::SpatializeGroupsVisitor::divide(unsigned int maxNumTreesPerCell)
2575{
2576    bool divided = false;
2577    for(GroupsToDivideList::iterator itr=_groupsToDivideList.begin();
2578        itr!=_groupsToDivideList.end();
2579        ++itr)
2580    {
2581        if (divide(*itr,maxNumTreesPerCell)) divided = true;
2582    }
2583   
2584    for(GeodesToDivideList::iterator geode_itr=_geodesToDivideList.begin();
2585        geode_itr!=_geodesToDivideList.end();
2586        ++geode_itr)
2587    {
2588        if (divide(*geode_itr,maxNumTreesPerCell)) divided = true;
2589    }
2590   
2591    return divided;
2592}
2593
2594bool Optimizer::SpatializeGroupsVisitor::divide(osg::Group* group, unsigned int maxNumTreesPerCell)
2595{
2596    if (group->getNumChildren()<=maxNumTreesPerCell) return false;
2597   
2598    // create the original box.
2599    osg::BoundingBox bb;
2600    unsigned int i;
2601    for(i=0;i<group->getNumChildren();++i)
2602    {
2603        bb.expandBy(group->getChild(i)->getBound().center());
2604    }
2605   
2606    float radius = bb.radius();
2607    float divide_distance = radius*0.7f;
2608    bool xAxis = (bb.xMax()-bb.xMin())>divide_distance;
2609    bool yAxis = (bb.yMax()-bb.yMin())>divide_distance;
2610    bool zAxis = (bb.zMax()-bb.zMin())>divide_distance;
2611
2612    osg::notify(osg::INFO)<<"Dividing "<<group->className()<<"  num children = "<<group->getNumChildren()<<"  xAxis="<<xAxis<<"  yAxis="<<yAxis<<"   zAxis="<<zAxis<<std::endl;
2613   
2614    if (!xAxis && !yAxis && !zAxis)
2615    {
2616        osg::notify(osg::INFO)<<"  No axis to divide, stopping division."<<std::endl;
2617        return false;
2618    }
2619   
2620    unsigned int numChildrenOnEntry = group->getNumChildren();
2621   
2622    typedef std::pair< osg::BoundingBox, osg::ref_ptr<osg::Group> > BoxGroupPair;
2623    typedef std::vector< BoxGroupPair > Boxes;
2624    Boxes boxes;
2625    boxes.push_back( BoxGroupPair(bb,new osg::Group) );
2626
2627    // divide up on each axis   
2628    if (xAxis)
2629    {
2630        unsigned int numCellsToDivide=boxes.size();
2631        for(unsigned int i=0;i<numCellsToDivide;++i)
2632        {
2633            osg::BoundingBox& orig_cell = boxes[i].first;
2634            osg::BoundingBox new_cell = orig_cell;
2635
2636            float xCenter = (orig_cell.xMin()+orig_cell.xMax())*0.5f;
2637            orig_cell.xMax() = xCenter;
2638            new_cell.xMin() = xCenter;
2639
2640            boxes.push_back(BoxGroupPair(new_cell,new osg::Group));
2641        }
2642    }
2643
2644    if (yAxis)
2645    {
2646        unsigned int numCellsToDivide=boxes.size();
2647        for(unsigned int i=0;i<numCellsToDivide;++i)
2648        {
2649            osg::BoundingBox& orig_cell = boxes[i].first;
2650            osg::BoundingBox new_cell = orig_cell;
2651
2652            float yCenter = (orig_cell.yMin()+orig_cell.yMax())*0.5f;
2653            orig_cell.yMax() = yCenter;
2654            new_cell.yMin() = yCenter;
2655
2656            boxes.push_back(BoxGroupPair(new_cell,new osg::Group));
2657        }
2658    }
2659
2660    if (zAxis)
2661    {
2662        unsigned int numCellsToDivide=boxes.size();
2663        for(unsigned int i=0;i<numCellsToDivide;++i)
2664        {
2665            osg::BoundingBox& orig_cell = boxes[i].first;
2666            osg::BoundingBox new_cell = orig_cell;
2667
2668            float zCenter = (orig_cell.zMin()+orig_cell.zMax())*0.5f;
2669            orig_cell.zMax() = zCenter;
2670            new_cell.zMin() = zCenter;
2671
2672            boxes.push_back(BoxGroupPair(new_cell,new osg::Group));
2673        }
2674    }
2675
2676
2677    // create the groups to drop the children into
2678   
2679
2680    // bin each child into associated bb group
2681    typedef std::vector< osg::ref_ptr<osg::Node> > NodeList;
2682    NodeList unassignedList;
2683    for(i=0;i<group->getNumChildren();++i)
2684    {
2685        bool assigned = false;
2686        osg::Vec3 center = group->getChild(i)->getBound().center();
2687        for(Boxes::iterator itr=boxes.begin();
2688            itr!=boxes.end() && !assigned;
2689            ++itr)
2690        {
2691            if (itr->first.contains(center))
2692            {
2693                // move child from main group into bb group.
2694                (itr->second)->addChild(group->getChild(i));
2695                assigned = true;
2696            }
2697        }
2698        if (!assigned)
2699        {
2700            unassignedList.push_back(group->getChild(i));
2701        }
2702    }
2703
2704
2705    // now transfer nodes across, by :
2706    //      first removing from the original group,
2707    //      add in the bb groups
2708    //      add then the unassigned children.
2709   
2710   
2711    // first removing from the original group,
2712    group->removeChildren(0,group->getNumChildren());
2713   
2714    // add in the bb groups
2715    typedef std::vector< osg::ref_ptr<osg::Group> > GroupList;
2716    GroupList groupsToDivideList;
2717    for(Boxes::iterator itr=boxes.begin();
2718        itr!=boxes.end();
2719        ++itr)
2720    {
2721        // move child from main group into bb group.
2722        osg::Group* bb_group = (itr->second).get();
2723        if (bb_group->getNumChildren()>0)
2724        {
2725            if (bb_group->getNumChildren()==1)
2726            {
2727                group->addChild(bb_group->getChild(0));
2728            }
2729            else
2730            {
2731                group->addChild(bb_group);
2732                if (bb_group->getNumChildren()>maxNumTreesPerCell)
2733                {
2734                    groupsToDivideList.push_back(bb_group);
2735                }
2736            }
2737        }
2738    }
2739   
2740   
2741    // add then the unassigned children.
2742    for(NodeList::iterator nitr=unassignedList.begin();
2743        nitr!=unassignedList.end();
2744        ++nitr)
2745    {
2746        group->addChild(nitr->get());
2747    }
2748
2749    // now call divide on all groups that require it.
2750    for(GroupList::iterator gitr=groupsToDivideList.begin();
2751        gitr!=groupsToDivideList.end();
2752        ++gitr)
2753    {
2754        divide(gitr->get(),maxNumTreesPerCell);
2755    }
2756
2757    return (numChildrenOnEntry<group->getNumChildren());
2758   
2759}
2760
2761bool Optimizer::SpatializeGroupsVisitor::divide(osg::Geode* geode, unsigned int maxNumTreesPerCell)
2762{
2763
2764    if (geode->getNumDrawables()<=maxNumTreesPerCell) return false;
2765   
2766    // create the original box.
2767    osg::BoundingBox bb;
2768    unsigned int i;
2769    for(i=0; i<geode->getNumDrawables(); ++i)
2770    {
2771        bb.expandBy(geode->getDrawable(i)->getBound().center());
2772    }
2773   
2774    float radius = bb.radius();
2775    float divide_distance = radius*0.7f;
2776    bool xAxis = (bb.xMax()-bb.xMin())>divide_distance;
2777    bool yAxis = (bb.yMax()-bb.yMin())>divide_distance;
2778    bool zAxis = (bb.zMax()-bb.zMin())>divide_distance;
2779
2780    osg::notify(osg::INFO)<<"INFO "<<geode->className()<<"  num drawables = "<<geode->getNumDrawables()<<"  xAxis="<<xAxis<<"  yAxis="<<yAxis<<"   zAxis="<<zAxis<<std::endl;
2781   
2782    if (!xAxis && !yAxis && !zAxis)
2783    {
2784        osg::notify(osg::INFO)<<"  No axis to divide, stopping division."<<std::endl;
2785        return false;
2786    }
2787   
2788    osg::Node::ParentList parents = geode->getParents();
2789    if (parents.empty())
2790    {
2791        osg::notify(osg::INFO)<<"  Cannot perform spatialize on root Geode, add a Group above it to allow subdivision."<<std::endl;
2792        return false;
2793    }
2794
2795    osg::ref_ptr<osg::Group> group = new osg::Group;
2796    group->setName(geode->getName());
2797    group->setStateSet(geode->getStateSet());
2798    for(i=0; i<geode->getNumDrawables(); ++i)
2799    {
2800        osg::Geode* newGeode = new osg::Geode;
2801        newGeode->addDrawable(geode->getDrawable(i));
2802        group->addChild(newGeode);
2803    }
2804   
2805    divide(group.get(), maxNumTreesPerCell);
2806   
2807    // keep reference around to prevent it being deleted.
2808    osg::ref_ptr<osg::Geode> keepRefGeode = geode;
2809   
2810    for(osg::Node::ParentList::iterator itr = parents.begin();
2811        itr != parents.end();
2812        ++itr)
2813    {
2814        (*itr)->replaceChild(geode, group.get());
2815    }
2816   
2817    return true;
2818}
2819
2820////////////////////////////////////////////////////////////////////////////////////////////
2821//
2822//  Duplicated subgraphs which are shared
2823//
2824
2825void Optimizer::CopySharedSubgraphsVisitor::apply(osg::Node& node)
2826{
2827    if (node.getNumParents()>1 && isOperationPermissibleForObject(&node))
2828    {
2829        _sharedNodeList.insert(&node);
2830    }
2831    traverse(node);
2832}
2833
2834void Optimizer::CopySharedSubgraphsVisitor::copySharedNodes()
2835{
2836    osg::notify(osg::INFO)<<"Shared node "<<_sharedNodeList.size()<<std::endl;
2837    for(SharedNodeList::iterator itr=_sharedNodeList.begin();
2838        itr!=_sharedNodeList.end();
2839        ++itr)
2840    {
2841        osg::notify(osg::INFO)<<"   No parents "<<(*itr)->getNumParents()<<std::endl;
2842        osg::Node* node = *itr;
2843        for(unsigned int i=node->getNumParents()-1;i>0;--i)
2844        {
2845            // create a clone.
2846            osg::ref_ptr<osg::Object> new_object = node->clone(osg::CopyOp::DEEP_COPY_NODES |
2847                                                          osg::CopyOp::DEEP_COPY_DRAWABLES);
2848            // cast it to node.                                             
2849            osg::Node* new_node = dynamic_cast<osg::Node*>(new_object.get());
2850
2851            // replace the node by new_new
2852            if (new_node) node->getParent(i)->replaceChild(node,new_node);
2853        }
2854    }
2855}
2856
2857
2858////////////////////////////////////////////////////////////////////////////////////////////
2859//
2860//  Set the attributes of textures up.
2861//
2862
2863
2864void Optimizer::TextureVisitor::apply(osg::Node& node)
2865{
2866   
2867    osg::StateSet* ss = node.getStateSet();
2868    if (ss &&
2869        isOperationPermissibleForObject(&node) &&
2870        isOperationPermissibleForObject(ss))
2871    {
2872        apply(*ss);
2873    }
2874
2875    traverse(node);
2876}
2877
2878void Optimizer::TextureVisitor::apply(osg::Geode& geode)
2879{
2880    if (!isOperationPermissibleForObject(&geode)) return;
2881
2882    osg::StateSet* ss = geode.getStateSet();
2883   
2884    if (ss && isOperationPermissibleForObject(ss))
2885    {
2886        apply(*ss);
2887    }
2888
2889    for(unsigned int i=0;i<geode.getNumDrawables();++i)
2890    {
2891        osg::Drawable* drawable = geode.getDrawable(i);
2892        if (drawable)
2893        {
2894            ss = drawable->getStateSet();
2895            if (ss &&
2896               isOperationPermissibleForObject(drawable) &&
2897               isOperationPermissibleForObject(ss))
2898            {
2899                apply(*ss);
2900            }
2901        }
2902    }
2903}
2904
2905void Optimizer::TextureVisitor::apply(osg::StateSet& stateset)
2906{
2907    for(unsigned int i=0;i<stateset.getTextureAttributeList().size();++i)
2908    {
2909        osg::StateAttribute* sa = stateset.getTextureAttribute(i,osg::StateAttribute::TEXTURE);
2910        osg::Texture* texture = dynamic_cast<osg::Texture*>(sa);
2911        if (texture && isOperationPermissibleForObject(texture))
2912        {
2913            apply(*texture);
2914        }
2915    }
2916}
2917
2918void Optimizer::TextureVisitor::apply(osg::Texture& texture)
2919{
2920    if (_changeAutoUnRef)
2921    {
2922        unsigned numImageStreams = 0;
2923        for (unsigned int i=0; i<texture.getNumImages(); ++i)
2924        {
2925            osg::ImageStream* is = dynamic_cast<osg::ImageStream*>(texture.getImage(i));
2926            if (is) ++numImageStreams;
2927        }
2928       
2929        if (numImageStreams==0)
2930        {
2931            texture.setUnRefImageDataAfterApply(_valueAutoUnRef);
2932        }
2933    }
2934   
2935    if (_changeClientImageStorage)
2936    {
2937        texture.setClientStorageHint(_valueClientImageStorage);
2938    }
2939   
2940    if (_changeAnisotropy)
2941    {
2942        texture.setMaxAnisotropy(_valueAnisotropy);
2943    }
2944
2945}
2946
2947////////////////////////////////////////////////////////////////////////////
2948// Merge geodes
2949////////////////////////////////////////////////////////////////////////////
2950
2951void Optimizer::MergeGeodesVisitor::apply(osg::Group& group)
2952{
2953    if (typeid(group)==typeid(osg::Group)) mergeGeodes(group);
2954    traverse(group);
2955}
2956
2957struct LessGeode
2958{
2959    bool operator() (const osg::Geode* lhs,const osg::Geode* rhs) const
2960    {
2961        if (lhs->getNodeMask()<rhs->getNodeMask()) return true;
2962        if (lhs->getNodeMask()>rhs->getNodeMask()) return false;
2963
2964        return (lhs->getStateSet()<rhs->getStateSet());
2965    }
2966};
2967
2968bool Optimizer::MergeGeodesVisitor::mergeGeodes(osg::Group& group)
2969{
2970    if (!isOperationPermissibleForObject(&group)) return false;
2971
2972    typedef std::vector< osg::Geode* >                      DuplicateList;
2973    typedef std::map<osg::Geode*,DuplicateList,LessGeode>   GeodeDuplicateMap;
2974
2975    unsigned int i;
2976    osg::NodeList children;
2977    children.resize(group.getNumChildren());
2978    for (i=0; i<group.getNumChildren(); ++i)
2979    {
2980        // keep a reference to this child so we can safely clear the group of all children
2981        // this is done so we don't have to do a search and remove from the list later on.
2982        children[i] = group.getChild(i);
2983    }
2984   
2985    // remove all children
2986    group.removeChildren(0,group.getNumChildren());
2987
2988    GeodeDuplicateMap geodeDuplicateMap;
2989    for (i=0; i<children.size(); ++i)
2990    {
2991        osg::Node* child = children[i].get();
2992
2993        if (typeid(*child)==typeid(osg::Geode))
2994        {
2995            osg::Geode* geode = static_cast<osg::Geode*>(child);
2996            geodeDuplicateMap[geode].push_back(geode);
2997        }
2998        else
2999        {
3000            // not a geode so just add back into group as its a normal child
3001            group.addChild(child);
3002        }
3003    }
3004
3005    // if no geodes then just return.
3006    if (geodeDuplicateMap.empty()) return false;
3007   
3008    osg::notify(osg::INFO)<<"mergeGeodes in group '"<<group.getName()<<"' "<<geodeDuplicateMap.size()<<std::endl;
3009   
3010    // merge
3011    for(GeodeDuplicateMap::iterator itr=geodeDuplicateMap.begin();
3012        itr!=geodeDuplicateMap.end();
3013        ++itr)
3014    {
3015        if (itr->second.size()>1)
3016        {
3017            osg::Geode* lhs = itr->second[0];
3018
3019            // add geode back into group
3020            group.addChild(lhs);
3021           
3022            for(DuplicateList::iterator dupItr=itr->second.begin()+1;
3023                dupItr!=itr->second.end();
3024                ++dupItr)
3025            {
3026                osg::Geode* rhs = *dupItr;
3027                mergeGeode(*lhs,*rhs);
3028            }
3029        }
3030        else
3031        {
3032            osg::Geode* lhs = itr->second[0];
3033
3034            // add geode back into group
3035            group.addChild(lhs);
3036        }
3037    }
3038
3039    return true;
3040}
3041
3042bool Optimizer::MergeGeodesVisitor::mergeGeode(osg::Geode& lhs, osg::Geode& rhs)
3043{
3044    for (unsigned int i=0; i<rhs.getNumDrawables(); ++i)
3045    {
3046        lhs.addDrawable(rhs.getDrawable(i));
3047    }
3048
3049    return true;
3050}
3051
3052
3053
3054////////////////////////////////////////////////////////////////////////////
3055// FlattenBillboardVisitor
3056////////////////////////////////////////////////////////////////////////////
3057void Optimizer::FlattenBillboardVisitor::reset()
3058{
3059    _billboards.clear();
3060}
3061
3062void Optimizer::FlattenBillboardVisitor::apply(osg::Billboard& billboard)
3063{
3064    _billboards[&billboard].push_back(getNodePath());
3065}
3066
3067void Optimizer::FlattenBillboardVisitor::process()
3068{
3069    for(BillboardNodePathMap::iterator itr = _billboards.begin();
3070        itr != _billboards.end();
3071        ++itr)
3072    {
3073        bool mergeAcceptable = true;   
3074
3075        osg::ref_ptr<osg::Billboard> billboard = itr->first;
3076
3077        NodePathList& npl = itr->second;
3078        osg::Group* mainGroup = 0;
3079        if (npl.size()>1)
3080        {       
3081            for(NodePathList::iterator nitr = npl.begin();
3082                nitr != npl.end();
3083                ++nitr)
3084            {
3085                osg::NodePath& np = *nitr;
3086                if (np.size()>=3)
3087                {
3088                    osg::Group* group = dynamic_cast<osg::Group*>(np[np.size()-3]);
3089                    if (mainGroup==0) mainGroup = group;
3090
3091                    osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(np[np.size()-2]);
3092
3093                    if (group == mainGroup &&
3094                        np[np.size()-1]==billboard.get() &&
3095                        mt && mt->getDataVariance()==osg::Object::STATIC &&
3096                        mt->getNumChildren()==1)
3097                    {
3098                        const osg::Matrix& m = mt->getMatrix();
3099                        mergeAcceptable = (m(0,0)==1.0 && m(0,1)==0.0 && m(0,2)==0.0 && m(0,3)==0.0 &&
3100                                           m(1,0)==0.0 && m(1,1)==1.0 && m(1,2)==0.0 && m(1,3)==0.0 &&
3101                                           m(2,0)==0.0 && m(2,1)==0.0 && m(2,2)==1.0 && m(2,3)==0.0 &&
3102                                           m(3,3)==1.0);
3103                    }
3104                    else
3105                    {
3106                       mergeAcceptable = false;
3107                    }
3108                }
3109                else
3110                {
3111                    mergeAcceptable = false;
3112                }
3113            }
3114        }
3115        else
3116        {
3117            mergeAcceptable = false;
3118        }
3119
3120        if (mergeAcceptable)
3121        {
3122            osg::Billboard* new_billboard = new osg::Billboard;
3123            new_billboard->setMode(billboard->getMode());
3124            new_billboard->setAxis(billboard->getAxis());
3125            new_billboard->setStateSet(billboard->getStateSet());
3126            new_billboard->setName(billboard->getName());               
3127
3128            mainGroup->addChild(new_billboard);
3129
3130            typedef std::set<osg::MatrixTransform*> MatrixTransformSet;
3131            MatrixTransformSet mts;
3132
3133            for(NodePathList::iterator nitr = npl.begin();
3134                nitr != npl.end();
3135                ++nitr)
3136            {
3137                osg::NodePath& np = *nitr;
3138                osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(np[np.size()-2]);
3139                mts.insert(mt);
3140            }
3141
3142            for(MatrixTransformSet::iterator mitr = mts.begin();
3143                mitr != mts.end();
3144                ++mitr)
3145            {
3146                osg::MatrixTransform* mt = *mitr;
3147                for(unsigned int i=0; i<billboard->getNumDrawables(); ++i)
3148                {
3149                    new_billboard->addDrawable(billboard->getDrawable(i),
3150                                               billboard->getPosition(i)*mt->getMatrix());
3151                }
3152                mainGroup->removeChild(mt);
3153            }
3154        }
3155    }
3156
3157}
3158
3159
3160
3161////////////////////////////////////////////////////////////////////////////
3162// TextureAtlasBuilder
3163////////////////////////////////////////////////////////////////////////////
3164
3165Optimizer::TextureAtlasBuilder::TextureAtlasBuilder():
3166    _maximumAtlasWidth(2048),
3167    _maximumAtlasHeight(2048),
3168    _margin(8)
3169{
3170}
3171
3172void Optimizer::TextureAtlasBuilder::reset()
3173{
3174    _sourceList.clear();
3175    _atlasList.clear();
3176}
3177
3178void Optimizer::TextureAtlasBuilder::setMaximumAtlasSize(unsigned int width, unsigned int height)
3179{
3180    _maximumAtlasWidth = width;
3181    _maximumAtlasHeight = height;
3182}
3183
3184void Optimizer::TextureAtlasBuilder::setMargin(unsigned int margin)
3185{
3186    _margin = margin;
3187}
3188
3189void Optimizer::TextureAtlasBuilder::addSource(const osg::Image* image)
3190{
3191    if (!getSource(image)) _sourceList.push_back(new Source(image));
3192}
3193
3194void Optimizer::TextureAtlasBuilder::addSource(const osg::Texture2D* texture)
3195{
3196    if (!getSource(texture)) _sourceList.push_back(new Source(texture));
3197}
3198
3199void Optimizer::TextureAtlasBuilder::buildAtlas()
3200{
3201    // assign the source to the atlas
3202    _atlasList.clear();
3203    for(SourceList::iterator sitr = _sourceList.begin();
3204        sitr != _sourceList.end();
3205        ++sitr)
3206    {
3207        Source* source = sitr->get();
3208        if (source->suitableForAtlas(_maximumAtlasWidth,_maximumAtlasHeight,_margin))
3209        {
3210            bool addedSourceToAtlas = false;
3211            for(AtlasList::iterator aitr = _atlasList.begin();
3212                aitr != _atlasList.end() && !addedSourceToAtlas;
3213                ++aitr)
3214            {
3215                osg::notify(osg::INFO)<<"checking source "<<source->_image->getFileName()<<" to see it it'll fit in atlas "<<aitr->get()<<std::endl;
3216                if ((*aitr)->doesSourceFit(source))
3217                {
3218                    addedSourceToAtlas = true;
3219                    (*aitr)->addSource(source);
3220                }
3221                else
3222                {
3223                    osg::notify(osg::INFO)<<"source "<<source->_image->getFileName()<<" does not fit in atlas "<<aitr->get()<<std::endl;
3224                }
3225            }
3226
3227            if (!addedSourceToAtlas)
3228            {
3229                osg::notify(osg::INFO)<<"creating new Atlas for "<<source->_image->getFileName()<<std::endl;
3230
3231                osg::ref_ptr<Atlas> atlas = new Atlas(_maximumAtlasWidth,_maximumAtlasHeight,_margin);
3232                _atlasList.push_back(atlas.get());
3233               
3234                atlas->addSource(source);
3235            }
3236        }
3237    }
3238   
3239    // build the atlas which are suitable for use, and discard the rest.
3240    AtlasList activeAtlasList;
3241    for(AtlasList::iterator aitr = _atlasList.begin();
3242        aitr != _atlasList.end();
3243        ++aitr)
3244    {
3245        Atlas* atlas = aitr->get();
3246
3247        if (atlas->_sourceList.size()==1)
3248        {
3249            // no point building an atlas with only one entry
3250            // so disconnect the source.
3251            Source* source = atlas->_sourceList[0].get();
3252            source->_atlas = 0;
3253            atlas->_sourceList.clear();
3254        }
3255       
3256        if (!(atlas->_sourceList.empty()))
3257        {
3258            std::stringstream ostr;
3259            ostr<<"atlas_"<<activeAtlasList.size()<<".rgb";
3260            atlas->_image->setFileName(ostr.str());
3261       
3262            activeAtlasList.push_back(atlas);
3263            atlas->clampToNearestPowerOfTwoSize();
3264            atlas->copySources();
3265           
3266        }
3267    }
3268   
3269    // keep only the active atlas'
3270    _atlasList.swap(activeAtlasList);
3271
3272}
3273
3274osg::Image* Optimizer::TextureAtlasBuilder::getImageAtlas(unsigned int i)
3275{
3276    Source* source = _sourceList[i].get();
3277    Atlas* atlas = source ? source->_atlas : 0;
3278    return atlas ? atlas->_image.get() : 0;
3279}
3280
3281osg::Texture2D* Optimizer::TextureAtlasBuilder::getTextureAtlas(unsigned int i)
3282{
3283    Source* source = _sourceList[i].get();
3284    Atlas* atlas = source ? source->_atlas : 0;
3285    return atlas ? atlas->_texture.get() : 0;
3286}
3287
3288osg::Matrix Optimizer::TextureAtlasBuilder::getTextureMatrix(unsigned int i)
3289{
3290    Source* source = _sourceList[i].get();
3291    return source ? source->computeTextureMatrix() : osg::Matrix();
3292}
3293
3294osg::Image* Optimizer::TextureAtlasBuilder::getImageAtlas(const osg::Image* image)
3295{
3296    Source* source = getSource(image);
3297    Atlas* atlas = source ? source->_atlas : 0;
3298    return atlas ? atlas->_image.get() : 0;
3299}
3300
3301osg::Texture2D* Optimizer::TextureAtlasBuilder::getTextureAtlas(const osg::Image* image)
3302{
3303    Source* source = getSource(image);
3304    Atlas* atlas = source ? source->_atlas : 0;
3305    return atlas ? atlas->_texture.get() : 0;
3306}
3307
3308osg::Matrix Optimizer::TextureAtlasBuilder::getTextureMatrix(const osg::Image* image)
3309{
3310    Source* source = getSource(image);
3311    return source ? source->computeTextureMatrix() : osg::Matrix();
3312}
3313
3314osg::Image* Optimizer::TextureAtlasBuilder::getImageAtlas(const osg::Texture2D* texture)
3315{
3316    Source* source = getSource(texture);
3317    Atlas* atlas = source ? source->_atlas : 0;
3318    return atlas ? atlas->_image.get() : 0;
3319}
3320
3321osg::Texture2D* Optimizer::TextureAtlasBuilder::getTextureAtlas(const osg::Texture2D* texture)
3322{
3323    Source* source = getSource(texture);
3324    Atlas* atlas = source ? source->_atlas : 0;
3325    return atlas ? atlas->_texture.get() : 0;
3326}
3327
3328osg::Matrix Optimizer::TextureAtlasBuilder::getTextureMatrix(const osg::Texture2D* texture)
3329{
3330    Source* source = getSource(texture);
3331    return source ? source->computeTextureMatrix() : osg::Matrix();
3332}
3333
3334Optimizer::TextureAtlasBuilder::Source* Optimizer::TextureAtlasBuilder::getSource(const osg::Image* image)
3335{
3336    for(SourceList::iterator itr = _sourceList.begin();
3337        itr != _sourceList.end();
3338        ++itr)
3339    {
3340        if ((*itr)->_image == image) return itr->get();
3341    }
3342    return 0;
3343}
3344
3345Optimizer::TextureAtlasBuilder::Source* Optimizer::TextureAtlasBuilder::getSource(const osg::Texture2D* texture)
3346{
3347    for(SourceList::iterator itr = _sourceList.begin();
3348        itr != _sourceList.end();
3349        ++itr)
3350    {
3351        if ((*itr)->_texture == texture) return itr->get();
3352    }
3353    return 0;
3354}
3355
3356bool Optimizer::TextureAtlasBuilder::Source::suitableForAtlas(unsigned int maximumAtlasWidth, unsigned int maximumAtlasHeight, unsigned int margin)
3357{
3358    if (!_image) return false;
3359   
3360    // size too big?
3361    if (_image->s()+margin*2 > maximumAtlasWidth) return false;
3362    if (_image->t()+margin*2 > maximumAtlasHeight) return false;
3363   
3364    switch(_image->getPixelFormat())
3365    {
3366        case(GL_COMPRESSED_ALPHA_ARB):
3367        case(GL_COMPRESSED_INTENSITY_ARB):
3368        case(GL_COMPRESSED_LUMINANCE_ALPHA_ARB):
3369        case(GL_COMPRESSED_LUMINANCE_ARB):
3370        case(GL_COMPRESSED_RGBA_ARB):
3371        case(GL_COMPRESSED_RGB_ARB):
3372        case(GL_COMPRESSED_RGB_S3TC_DXT1_EXT):
3373        case(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT):
3374        case(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT):
3375        case(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT):
3376            // can't handle compressed textures inside an atlas
3377            return false;
3378        default:
3379            break;
3380    }
3381
3382    if ((_image->getPixelSizeInBits() % 8) != 0)
3383    {
3384        // pixel size not byte aligned so report as not suitable to prevent other atlas code from having problems with byte boundaries.
3385        return false;
3386    }
3387
3388    if (_texture.valid())
3389    {
3390
3391        if (_texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
3392            _texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR)
3393        {
3394            // can't support repeating textures in texture atlas
3395            return false;
3396        }
3397
3398        if (_texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
3399            _texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR)
3400        {
3401            // can't support repeating textures in texture atlas
3402            return false;
3403        }
3404
3405        if (_texture->getReadPBuffer()!=0)
3406        {
3407            // pbuffer textures not suitable
3408            return false;
3409        }
3410    }
3411   
3412    return true;
3413}
3414
3415osg::Matrix Optimizer::TextureAtlasBuilder::Source::computeTextureMatrix() const
3416{
3417    if (!_atlas) return osg::Matrix();
3418    if (!_image) return osg::Matrix();
3419    if (!(_atlas->_image)) return osg::Matrix();
3420   
3421    return osg::Matrix::scale(float(_image->s())/float(_atlas->_image->s()), float(_image->t())/float(_atlas->_image->t()), 1.0)*
3422           osg::Matrix::translate(float(_x)/float(_atlas->_image->s()), float(_y)/float(_atlas->_image->t()), 0.0);
3423}
3424
3425bool Optimizer::TextureAtlasBuilder::Atlas::doesSourceFit(Source* source)
3426{
3427    // does the source have a valid image?
3428    const osg::Image* sourceImage = source->_image.get();
3429    if (!sourceImage) return false;
3430   
3431    // does pixel format match?
3432    if (_image.valid())
3433    {
3434        if (_image->getPixelFormat() != sourceImage->getPixelFormat()) return false;
3435        if (_image->getDataType() != sourceImage->getDataType()) return false;
3436    }
3437   
3438    const osg::Texture2D* sourceTexture = source->_texture.get();
3439    if (sourceTexture)
3440    {
3441        if (sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
3442            sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR)
3443        {
3444            // can't support repeating textures in texture atlas
3445            return false;
3446        }
3447
3448        if (sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
3449            sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR)
3450        {
3451            // can't support repeating textures in texture atlas
3452            return false;
3453        }
3454
3455        if (sourceTexture->getReadPBuffer()!=0)
3456        {
3457            // pbuffer textures not suitable
3458            return false;
3459        }
3460
3461        if (_texture.valid())
3462        {
3463
3464            bool sourceUsesBorder = sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::CLAMP_TO_BORDER ||
3465                                    sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::CLAMP_TO_BORDER;
3466
3467            bool atlasUsesBorder = sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::CLAMP_TO_BORDER ||
3468                                   sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::CLAMP_TO_BORDER;
3469
3470            if (sourceUsesBorder!=atlasUsesBorder)
3471            {
3472                // border wrapping does not match
3473                return false;
3474            }
3475
3476            if (sourceUsesBorder)
3477            {
3478                // border colours don't match
3479                if (_texture->getBorderColor() != sourceTexture->getBorderColor()) return false;
3480            }
3481           
3482            if (_texture->getFilter(osg::Texture2D::MIN_FILTER) != sourceTexture->getFilter(osg::Texture2D::MIN_FILTER))
3483            {
3484                // inconsitent min filters
3485                return false;
3486            }
3487 
3488            if (_texture->getFilter(osg::Texture2D::MAG_FILTER) != sourceTexture->getFilter(osg::Texture2D::MAG_FILTER))
3489            {
3490                // inconsitent mag filters
3491                return false;
3492            }
3493           
3494            if (_texture->getMaxAnisotropy() != sourceTexture->getMaxAnisotropy())
3495            {
3496                // anisotropy different.
3497                return false;
3498            }
3499           
3500            if (_texture->getInternalFormat() != sourceTexture->getInternalFormat())
3501            {
3502                // internal formats inconistent
3503                return false;
3504            }
3505           
3506            if (_texture->getShadowCompareFunc() != sourceTexture->getShadowCompareFunc())
3507            {
3508                // shadow functions inconsitent
3509                return false;
3510            }
3511               
3512            if (_texture->getShadowTextureMode() != sourceTexture->getShadowTextureMode())
3513            {
3514                // shadow texture mode inconsitent
3515                return false;
3516            }
3517
3518            if (_texture->getShadowAmbient() != sourceTexture->getShadowAmbient())
3519            {
3520                // shadow ambient inconsitent
3521                return false;
3522            }
3523        }
3524    }
3525   
3526    if (sourceImage->s() + 2*_margin > _maximumAtlasWidth)
3527    {
3528        // image too big for Atlas
3529        return false;
3530    }
3531   
3532    if (sourceImage->t() + 2*_margin > _maximumAtlasHeight)
3533    {
3534        // image too big for Atlas
3535        return false;
3536    }
3537
3538    if ((_y + sourceImage->t() + 2*_margin) > _maximumAtlasHeight)
3539    {
3540        // image doesn't have up space in height axis.
3541        return false;
3542    }
3543
3544    // does the source fit in the current row?
3545    if ((_x + sourceImage->s() + 2*_margin) <= _maximumAtlasWidth)
3546    {
3547        // yes it fits :-)
3548        osg::notify(osg::INFO)<<"Fits in current row"<<std::endl;
3549        return true;
3550    }
3551
3552    // does the source fit in the new row up?
3553    if ((_height + sourceImage->t() + 2*_margin) <= _maximumAtlasHeight)
3554    {
3555        // yes it fits :-)
3556        osg::notify(osg::INFO)<<"Fits in next row"<<std::endl;
3557        return true;
3558    }
3559   
3560    // no space for the texture
3561    return false;
3562}
3563
3564bool Optimizer::TextureAtlasBuilder::Atlas::addSource(Source* source)
3565{
3566    // double check source is compatible
3567    if (!doesSourceFit(source))
3568    {
3569        osg::notify(osg::INFO)<<"source "<<source->_image->getFileName()<<" does not fit in atlas "<<this<<std::endl;
3570        return false;
3571    }
3572   
3573    const osg::Image* sourceImage = source->_image.get();
3574    const osg::Texture2D* sourceTexture = source->_texture.get();
3575
3576    if (!_image)
3577    {
3578        // need to create an image of the same pixel format to store the atlas in
3579        _image = new osg::Image;
3580        _image->setPixelFormat(sourceImage->getPixelFormat());
3581        _image->setDataType(sourceImage->getDataType());
3582    }
3583   
3584    if (!_texture && sourceTexture)
3585    {
3586        _texture = new osg::Texture2D(_image.get());
3587
3588        _texture->setWrap(osg::Texture2D::WRAP_S, sourceTexture->getWrap(osg::Texture2D::WRAP_S));
3589        _texture->setWrap(osg::Texture2D::WRAP_T, sourceTexture->getWrap(osg::Texture2D::WRAP_T));
3590       
3591        _texture->setBorderColor(sourceTexture->getBorderColor());
3592        _texture->setBorderWidth(0);
3593           
3594        _texture->setFilter(osg::Texture2D::MIN_FILTER, sourceTexture->getFilter(osg::Texture2D::MIN_FILTER));
3595        _texture->setFilter(osg::Texture2D::MAG_FILTER, sourceTexture->getFilter(osg::Texture2D::MAG_FILTER));
3596
3597        _texture->setMaxAnisotropy(sourceTexture->getMaxAnisotropy());
3598
3599        _texture->setInternalFormat(sourceTexture->getInternalFormat());
3600
3601        _texture->setShadowCompareFunc(sourceTexture->getShadowCompareFunc());
3602        _texture->setShadowTextureMode(sourceTexture->getShadowTextureMode());
3603        _texture->setShadowAmbient(sourceTexture->getShadowAmbient());
3604
3605    }
3606
3607    // now work out where to fit it, first try current row.
3608    if ((_x + sourceImage->s() + 2*_margin) <= _maximumAtlasWidth)
3609    {
3610        // yes it fits, so add the source to the atlas's list of sources it contains
3611        _sourceList.push_back(source);
3612
3613        osg::notify(osg::INFO)<<"current row insertion, source "<<source->_image->getFileName()<<" "<<_x<<","<<_y<<" fits in row of atlas "<<this<<std::endl;
3614
3615        // set up the source so it knows where it is in the atlas
3616        source->_x = _x + _margin;
3617        source->_y = _y + _margin;
3618        source->_atlas = this;
3619       
3620        // move the atlas' cursor along to the right
3621        _x += sourceImage->s() + 2*_margin;
3622       
3623        if (_x > _width) _width = _x;
3624       
3625        unsigned int localTop = _y + sourceImage->t() + 2*_margin;
3626        if ( localTop > _height) _height = localTop;
3627
3628        return true;
3629    }
3630
3631    // does the source fit in the new row up?
3632    if ((_height + sourceImage->t() + 2*_margin) <= _maximumAtlasHeight)
3633    {
3634        // now row so first need to reset the atlas cursor
3635        _x = 0;
3636        _y = _height;
3637
3638        // yes it fits, so add the source to the atlas' list of sources it contains
3639        _sourceList.push_back(source);
3640
3641        osg::notify(osg::INFO)<<"next row insertion, source "<<source->_image->getFileName()<<" "<<_x<<","<<_y<<" fits in row of atlas "<<this<<std::endl;
3642
3643        // set up the source so it knows where it is in the atlas
3644        source->_x = _x + _margin;
3645        source->_y = _y + _margin;
3646        source->_atlas = this;
3647       
3648        // move the atlas' cursor along to the right
3649        _x += sourceImage->s() + 2*_margin;
3650
3651        if (_x > _width) _width = _x;
3652
3653        _height = _y + sourceImage->t() + 2*_margin;
3654
3655        osg::notify(osg::INFO)<<"source "<<source->_image->getFileName()<<" "<<_x<<","<<_y<<" fits in row of atlas "<<this<<std::endl;
3656
3657        return true;
3658    }
3659
3660    osg::notify(osg::INFO)<<"source "<<source->_image->getFileName()<<" does not fit in atlas "<<this<<std::endl;
3661
3662    // shouldn't get here, unless doesSourceFit isn't working...
3663    return false;
3664}
3665
3666void Optimizer::TextureAtlasBuilder::Atlas::clampToNearestPowerOfTwoSize()
3667{
3668    unsigned int w = 1;
3669    while (w<_width) w *= 2;
3670
3671    unsigned int h = 1;
3672    while (h<_height) h *= 2;
3673   
3674    osg::notify(osg::INFO)<<"Clamping "<<_width<<", "<<_height<<" to "<<w<<","<<h<<std::endl;
3675   
3676    _width = w;
3677    _height = h;
3678}
3679
3680
3681void Optimizer::TextureAtlasBuilder::Atlas::copySources()
3682{
3683    osg::notify(osg::INFO)<<"Allocated to "<<_width<<","<<_height<<std::endl;
3684    _image->allocateImage(_width,_height,1,
3685                          _image->getPixelFormat(),_image->getDataType(),
3686                          _image->getPacking());
3687                         
3688    {
3689        // clear memory
3690        unsigned int size = _image->getTotalSizeInBytes();
3691        unsigned char* str = _image->data();
3692        for(unsigned int i=0; i<size; ++i) *(str++) = 0;
3693    }       
3694
3695    osg::notify(osg::INFO)<<"Atlas::copySources() "<<std::endl;
3696    for(SourceList::iterator itr = _sourceList.begin();
3697        itr !=_sourceList.end();
3698        ++itr)
3699    {
3700        Source* source = itr->get();
3701        Atlas* atlas = source->_atlas;
3702
3703        if (atlas)
3704        {
3705            osg::notify(osg::INFO)<<"Copying image "<<source->_image->getFileName()<<" to "<<source->_x<<" ,"<<source->_y<<std::endl;
3706            osg::notify(osg::INFO)<<"        image size "<<source->_image->s()<<","<<source->_image->t()<<std::endl;
3707
3708            const osg::Image* sourceImage = source->_image.get();
3709            osg::Image* atlasImage = atlas->_image.get();
3710
3711            unsigned int rowSize = sourceImage->getRowSizeInBytes();
3712            unsigned int pixelSizeInBits = sourceImage->getPixelSizeInBits();
3713            unsigned int pixelSizeInBytes = pixelSizeInBits/8;
3714            unsigned int marginSizeInBytes = pixelSizeInBytes*_margin;
3715
3716            unsigned int x = source->_x;
3717            unsigned int y = source->_y;
3718
3719            int t;
3720            for(t=0; t<sourceImage->t(); ++t, ++y)
3721            {
3722                unsigned char* destPtr = atlasImage->data(x, y);
3723                const unsigned char* sourcePtr = sourceImage->data(0, t);
3724                for(unsigned int i=0; i<rowSize; i++)
3725                {
3726                    *(destPtr++) = *(sourcePtr++);
3727                }
3728            }
3729           
3730            // copy top row margin
3731            y = source->_y + sourceImage->t();
3732            unsigned int m;
3733            for(m=0; m<_margin; ++m, ++y)
3734            {
3735                unsigned char* destPtr = atlasImage->data(x, y);
3736                const unsigned char* sourcePtr = sourceImage->data(0, sourceImage->t()-1);
3737                for(unsigned int i=0; i<rowSize; i++)
3738                {
3739                    *(destPtr++) = *(sourcePtr++);
3740                }
3741               
3742            }
3743           
3744
3745
3746            // copy bottom row margin
3747            y = source->_y-1;
3748            for(m=0; m<_margin; ++m, --y)
3749            {
3750                unsigned char* destPtr = atlasImage->data(x, y);
3751                const unsigned char* sourcePtr = sourceImage->data(0, 0);
3752                for(unsigned int i=0; i<rowSize; i++)
3753                {
3754                    *(destPtr++) = *(sourcePtr++);
3755                }
3756               
3757            }
3758
3759            // copy left column margin
3760            y = source->_y;
3761            for(t=0; t<sourceImage->t(); ++t, ++y)
3762            {
3763                x = source->_x-1;
3764                for(m=0; m<_margin; ++m, --x)
3765                {
3766                    unsigned char* destPtr = atlasImage->data(x, y);
3767                    const unsigned char* sourcePtr = sourceImage->data(0, t);
3768                    for(unsigned int i=0; i<pixelSizeInBytes; i++)
3769                    {
3770                        *(destPtr++) = *(sourcePtr++);
3771                    }
3772                }
3773            }           
3774
3775            // copy right column margin
3776            y = source->_y;
3777            for(t=0; t<sourceImage->t(); ++t, ++y)
3778            {
3779                x = source->_x + sourceImage->s();
3780                for(m=0; m<_margin; ++m, ++x)
3781                {
3782                    unsigned char* destPtr = atlasImage->data(x, y);
3783                    const unsigned char* sourcePtr = sourceImage->data(sourceImage->s()-1, t);
3784                    for(unsigned int i=0; i<pixelSizeInBytes; i++)
3785                    {
3786                        *(destPtr++) = *(sourcePtr++);
3787                    }
3788                }
3789            }           
3790
3791            // copy top left corner margin
3792            y = source->_y + sourceImage->t();
3793            for(m=0; m<_margin; ++m, ++y)
3794            {
3795                unsigned char* destPtr = atlasImage->data(source->_x - _margin, y);
3796                unsigned char* sourcePtr = atlasImage->data(source->_x - _margin, y-1); // copy from row below
3797                for(unsigned int i=0; i<marginSizeInBytes; i++)
3798                {
3799                    *(destPtr++) = *(sourcePtr++);
3800                }
3801            }
3802
3803            // copy top right corner margin
3804            y = source->_y + sourceImage->t();
3805            for(m=0; m<_margin; ++m, ++y)
3806            {
3807                unsigned char* destPtr = atlasImage->data(source->_x + sourceImage->s(), y);
3808                unsigned char* sourcePtr = atlasImage->data(source->_x + sourceImage->s(), y-1); // copy from row below
3809                for(unsigned int i=0; i<marginSizeInBytes; i++)
3810                {
3811                    *(destPtr++) = *(sourcePtr++);
3812                }
3813            }
3814
3815            // copy bottom left corner margin
3816            y = source->_y - 1;
3817            for(m=0; m<_margin; ++m, --y)
3818            {
3819                unsigned char* destPtr = atlasImage->data(source->_x - _margin, y);
3820                unsigned char* sourcePtr = atlasImage->data(source->_x - _margin, y+1); // copy from row below
3821                for(unsigned int i=0; i<marginSizeInBytes; i++)
3822                {
3823                    *(destPtr++) = *(sourcePtr++);
3824                }
3825            }
3826
3827            // copy bottom right corner margin
3828            y = source->_y - 1;
3829            for(m=0; m<_margin; ++m, --y)
3830            {
3831                unsigned char* destPtr = atlasImage->data(source->_x + sourceImage->s(), y);
3832                unsigned char* sourcePtr = atlasImage->data(source->_x + sourceImage->s(), y+1); // copy from row below
3833                for(unsigned int i=0; i<marginSizeInBytes; i++)
3834                {
3835                    *(destPtr++) = *(sourcePtr++);
3836                }
3837            }
3838
3839        }
3840    }
3841}
3842
3843
3844void Optimizer::TextureAtlasVisitor::reset()
3845{
3846    _statesetMap.clear();
3847    _statesetStack.clear();
3848    _textures.clear();
3849    _builder.reset();
3850}
3851
3852bool Optimizer::TextureAtlasVisitor::pushStateSet(osg::StateSet* stateset)
3853{
3854    osg::StateSet::TextureAttributeList& tal = stateset->getTextureAttributeList();
3855   
3856    // if no textures ignore
3857    if (tal.empty()) return false;
3858   
3859    bool pushStateState = false;
3860
3861    // if already in stateset list ignore
3862    if (_statesetMap.count(stateset)>0)
3863    {
3864        pushStateState = true;
3865    }
3866    else
3867    {
3868        bool containsTexture2D = false;
3869        for(unsigned int unit=0; unit<tal.size(); ++unit)
3870        {
3871            osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(unit,osg::StateAttribute::TEXTURE));
3872            if (texture2D)
3873            {
3874                containsTexture2D = true;
3875                _textures.insert(texture2D);
3876            }
3877        }
3878
3879        if (containsTexture2D)
3880        {
3881            _statesetMap[stateset];
3882            pushStateState = true;
3883        }
3884    }
3885   
3886    if (pushStateState)
3887    {
3888        _statesetStack.push_back(stateset);
3889    }
3890
3891   
3892    return pushStateState; 
3893}
3894
3895void Optimizer::TextureAtlasVisitor::popStateSet()
3896{
3897    _statesetStack.pop_back();
3898}
3899
3900void Optimizer::TextureAtlasVisitor::apply(osg::Node& node)
3901{
3902    bool pushedStateState = false;
3903   
3904    osg::StateSet* ss = node.getStateSet();
3905    if (ss && ss->getDataVariance()==osg::Object::STATIC)
3906    {
3907        if (isOperationPermissibleForObject(&node) &&
3908            isOperationPermissibleForObject(ss))
3909        {
3910            pushedStateState = pushStateSet(ss);
3911        }
3912    }
3913
3914    traverse(node);
3915   
3916    if (pushedStateState) popStateSet();
3917}
3918
3919void Optimizer::TextureAtlasVisitor::apply(osg::Geode& geode)
3920{
3921    if (!isOperationPermissibleForObject(&geode)) return;
3922
3923    osg::StateSet* ss = geode.getStateSet();
3924   
3925   
3926    bool pushedGeodeStateState = false;
3927
3928    if (ss && ss->getDataVariance()==osg::Object::STATIC)
3929    {
3930        if (isOperationPermissibleForObject(ss))
3931        {
3932            pushedGeodeStateState = pushStateSet(ss);
3933        }
3934    }
3935    for(unsigned int i=0;i<geode.getNumDrawables();++i)
3936    {
3937
3938        osg::Drawable* drawable = geode.getDrawable(i);
3939        if (drawable)
3940        {
3941            bool pushedDrawableStateState = false;
3942
3943            ss = drawable->getStateSet();
3944            if (ss && ss->getDataVariance()==osg::Object::STATIC)
3945            {
3946                if (isOperationPermissibleForObject(drawable) &&
3947                    isOperationPermissibleForObject(ss))
3948                {
3949                    pushedDrawableStateState = pushStateSet(ss);
3950                }
3951            }
3952           
3953            if (!_statesetStack.empty())
3954            {
3955                for(StateSetStack::iterator ssitr = _statesetStack.begin();
3956                    ssitr != _statesetStack.end();
3957                    ++ssitr)
3958                {
3959                    _statesetMap[*ssitr].insert(drawable);
3960                }
3961            }
3962
3963            if (pushedDrawableStateState) popStateSet();
3964        }
3965       
3966    }
3967   
3968    if (pushedGeodeStateState) popStateSet();
3969}
3970
3971void Optimizer::TextureAtlasVisitor::optimize()
3972{
3973    _builder.reset();
3974   
3975    if (_textures.size()<2)
3976    {
3977        // nothing to optimize
3978        return;
3979    }
3980
3981    Textures texturesThatRepeat;
3982    Textures texturesThatRepeatAndAreOutOfRange;
3983
3984    StateSetMap::iterator sitr;
3985    for(sitr = _statesetMap.begin();
3986        sitr != _statesetMap.end();
3987        ++sitr)
3988    {
3989        osg::StateSet* stateset = sitr->first;
3990        Drawables& drawables = sitr->second;
3991
3992        osg::StateSet::TextureAttributeList& tal = stateset->getTextureAttributeList();
3993        for(unsigned int unit=0; unit<tal.size(); ++unit)
3994        {
3995            osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(unit,osg::StateAttribute::TEXTURE));
3996            if (texture)
3997            {
3998                bool s_repeat = texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
3999                                texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR;
4000
4001                bool t_repeat = texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
4002                                texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR;
4003                               
4004                if (s_repeat || t_repeat)
4005                {
4006                    texturesThatRepeat.insert(texture);
4007               
4008                    bool s_outOfRange = false;
4009                    bool t_outOfRange = false;
4010 
4011                    float s_min = -0.001;
4012                    float s_max = 1.001;
4013
4014                    float t_min = -0.001;
4015                    float t_max = 1.001;
4016                   
4017                    for(Drawables::iterator ditr = drawables.begin();
4018                        ditr != drawables.end();
4019                        ++ditr)
4020                    {
4021                        osg::Geometry* geom = (*ditr)->asGeometry();
4022                        osg::Vec2Array* texcoords = geom ? dynamic_cast<osg::Vec2Array*>(geom->getTexCoordArray(unit)) : 0;
4023                        if (texcoords && !texcoords->empty())
4024                        {
4025                            for(osg::Vec2Array::iterator titr = texcoords->begin();
4026                                titr != texcoords->end() /*&& !s_outOfRange && !t_outOfRange*/;
4027                                ++titr)
4028                            {
4029                                osg::Vec2 tc = *titr;
4030                                if (tc[0]<s_min) { s_min = tc[0]; s_outOfRange = true; }
4031                                if (tc[0]>s_max) { s_max = tc[0]; s_outOfRange = true; }
4032
4033                                if (tc[1]<t_min) { t_min = tc[1]; t_outOfRange = true; }
4034                                if (tc[1]>t_max) { t_max = tc[1]; t_outOfRange = true; }
4035                            }
4036                        }
4037                        else
4038                        {
4039                            // if no texcoords then texgen must be being used, therefore must assume that texture is truely repeating
4040                            s_outOfRange = true;
4041                            t_outOfRange = true;
4042                        }                       
4043                    }
4044
4045                    if (s_outOfRange || t_outOfRange)
4046                    {                   
4047                        texturesThatRepeatAndAreOutOfRange.insert(texture);
4048                    }
4049
4050                }
4051            }
4052        }
4053    }
4054
4055    // now change any texture that repeat but all texcoords to them
4056    // are in 0 to 1 range than converting the to CLAMP mode, to allow them
4057    // to be used in an atlas.
4058    Textures::iterator titr;
4059    for(titr = texturesThatRepeat.begin();
4060        titr != texturesThatRepeat.end();
4061        ++titr)
4062    {
4063        osg::Texture2D* texture = *titr;
4064        if (texturesThatRepeatAndAreOutOfRange.count(texture)==0)
4065        {
4066            // safe to convert into CLAMP wrap mode.
4067            osg::notify(osg::INFO)<<"Changing wrap mode to CLAMP"<<std::endl;
4068            texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture::CLAMP);
4069            texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture::CLAMP);
4070        }
4071    }
4072
4073    // add the textures as sources for the TextureAtlasBuilder
4074    for(titr = _textures.begin();
4075        titr != _textures.end();
4076        ++titr)
4077    {
4078        osg::Texture2D* texture = *titr;
4079
4080        bool s_repeat = texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
4081                        texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR;
4082
4083        bool t_repeat = texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
4084                        texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR;
4085
4086        if (!s_repeat && !t_repeat)
4087        {
4088            _builder.addSource(*titr);
4089        }
4090    }
4091   
4092    // build the atlas'
4093    _builder.buildAtlas();
4094
4095
4096    typedef std::set<osg::StateSet*> StateSetSet;
4097    typedef std::map<osg::Drawable*, StateSetSet> DrawableStateSetMap;
4098    DrawableStateSetMap dssm;
4099    for(sitr = _statesetMap.begin();
4100        sitr != _statesetMap.end();
4101        ++sitr)
4102    {
4103        Drawables& drawables = sitr->second;
4104        for(Drawables::iterator ditr = drawables.begin();
4105            ditr != drawables.end();
4106            ++ditr)
4107        {
4108            dssm[(*ditr)->asGeometry()].insert(sitr->first);
4109        }
4110    }
4111   
4112    Drawables drawablesThatHaveMultipleTexturesOnOneUnit;
4113    for(DrawableStateSetMap::iterator ditr = dssm.begin();
4114        ditr != dssm.end();
4115        ++ditr)
4116    {
4117        osg::Drawable* drawable = ditr->first;
4118        StateSetSet& ssm = ditr->second;
4119        if (ssm.size()>1)
4120        {
4121            typedef std::map<unsigned int, Textures> UnitTextureMap;
4122            UnitTextureMap unitTextureMap;
4123            for(StateSetSet::iterator ssm_itr = ssm.begin();
4124                ssm_itr != ssm.end();
4125                ++ssm_itr)
4126            {
4127                osg::StateSet* ss = *ssm_itr;
4128                unsigned int numTextureUnits = ss->getTextureAttributeList().size();
4129                for(unsigned int unit=0; unit<numTextureUnits; ++unit)
4130                {
4131                    osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE));
4132                    if (texture) unitTextureMap[unit].insert(texture);
4133                }
4134            }
4135            bool drawablesHasMultiTextureOnOneUnit = false;
4136            for(UnitTextureMap::iterator utm_itr = unitTextureMap.begin();
4137                utm_itr != unitTextureMap.end() && !drawablesHasMultiTextureOnOneUnit;
4138                ++utm_itr)
4139            {
4140                if (utm_itr->second.size()>1)
4141                {
4142                    drawablesHasMultiTextureOnOneUnit = true;
4143                }
4144            }
4145            if (drawablesHasMultiTextureOnOneUnit)
4146            {
4147                drawablesThatHaveMultipleTexturesOnOneUnit.insert(drawable);
4148            }
4149
4150        }
4151    }
4152       
4153    // remap the textures in the StateSet's
4154    for(sitr = _statesetMap.begin();
4155        sitr != _statesetMap.end();
4156        ++sitr)
4157    {
4158        osg::StateSet* stateset = sitr->first;
4159        osg::StateSet::TextureAttributeList& tal = stateset->getTextureAttributeList();
4160        for(unsigned int unit=0; unit<tal.size(); ++unit)
4161        {
4162            osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(unit,osg::StateAttribute::TEXTURE));
4163            if (texture)
4164            {
4165                bool s_repeat = texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
4166                                texture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR;
4167
4168                bool t_repeat = texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
4169                                texture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR;
4170
4171                osg::Texture2D* newTexture = _builder.getTextureAtlas(texture);
4172                if (newTexture && newTexture!=texture)
4173                {
4174                    if (s_repeat || t_repeat)
4175                    {
4176                        osg::notify(osg::NOTICE)<<"Warning!!! shouldn't get here"<<std::endl;
4177                    }
4178
4179                    stateset->setTextureAttribute(unit, newTexture);
4180                   
4181                    Drawables& drawables = sitr->second;
4182                   
4183                    osg::Matrix matrix = _builder.getTextureMatrix(texture);
4184                   
4185                    // first check to see if all drawables are ok for applying texturematrix to.
4186                    bool canTexMatBeFlattenedToAllDrawables = true;
4187                    for(Drawables::iterator ditr = drawables.begin();
4188                        ditr != drawables.end() && canTexMatBeFlattenedToAllDrawables;
4189                        ++ditr)
4190                    {
4191                        osg::Geometry* geom = (*ditr)->asGeometry();
4192                        osg::Vec2Array* texcoords = geom ? dynamic_cast<osg::Vec2Array*>(geom->getTexCoordArray(unit)) : 0;
4193
4194                        if (!texcoords)
4195                        {
4196                            canTexMatBeFlattenedToAllDrawables = false;
4197                        }
4198                       
4199                        if (drawablesThatHaveMultipleTexturesOnOneUnit.count(*ditr)!=0)
4200                        {
4201                            canTexMatBeFlattenedToAllDrawables = false;
4202                        }
4203                    }
4204
4205                    if (canTexMatBeFlattenedToAllDrawables)
4206                    {
4207                        // osg::notify(osg::NOTICE)<<"All drawables can be flattened "<<drawables.size()<<std::endl;
4208                        for(Drawables::iterator ditr = drawables.begin();
4209                            ditr != drawables.end();
4210                            ++ditr)
4211                        {
4212                            osg::Geometry* geom = (*ditr)->asGeometry();
4213                            osg::Vec2Array* texcoords = geom ? dynamic_cast<osg::Vec2Array*>(geom->getTexCoordArray(unit)) : 0;
4214                            if (texcoords)
4215                            {
4216                                for(osg::Vec2Array::iterator titr = texcoords->begin();
4217                                    titr != texcoords->end();
4218                                    ++titr)
4219                                {
4220                                    osg::Vec2 tc = *titr;
4221                                    (*titr).set(tc[0]*matrix(0,0) + tc[1]*matrix(1,0) + matrix(3,0),
4222                                              tc[0]*matrix(0,1) + tc[1]*matrix(1,1) + matrix(3,1));
4223                                }
4224                            }
4225                            else
4226                            {
4227                                osg::notify(osg::NOTICE)<<"Error, Optimizer::TextureAtlasVisitor::optimize() shouldn't ever get here..."<<std::endl;
4228                            }                       
4229                        }
4230                    }
4231                    else
4232                    {
4233                        // osg::notify(osg::NOTICE)<<"Applying TexMat "<<drawables.size()<<std::endl;
4234                        stateset->setTextureAttribute(unit, new osg::TexMat(matrix));
4235                    }
4236                }
4237            }
4238        }
4239
4240    }
4241}
4242
4243
4244
4245
4246////////////////////////////////////////////////////////////////////////////
4247// StaticObjectDectionVisitor
4248////////////////////////////////////////////////////////////////////////////
4249
4250void Optimizer::StaticObjectDetectionVisitor::apply(osg::Node& node)
4251{
4252    if (node.getStateSet()) applyStateSet(*node.getStateSet());
4253
4254    traverse(node);
4255}
4256
4257void Optimizer::StaticObjectDetectionVisitor::apply(osg::Geode& geode)
4258{
4259    if (geode.getStateSet()) applyStateSet(*geode.getStateSet());
4260
4261    for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
4262    {
4263        applyDrawable(*geode.getDrawable(i));
4264    }
4265}
4266
4267void Optimizer::StaticObjectDetectionVisitor::applyStateSet(osg::StateSet& stateset)
4268{
4269    stateset.computeDataVariance();
4270}
4271
4272
4273void Optimizer::StaticObjectDetectionVisitor::applyDrawable(osg::Drawable& drawable)
4274{   
4275   
4276    if (drawable.getStateSet()) applyStateSet(*drawable.getStateSet());
4277   
4278    drawable.computeDataVariance();
4279}
4280
4281
4282
4283////////////////////////////////////////////////////////////////////////////
4284// FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor
4285////////////////////////////////////////////////////////////////////////////
4286
4287void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::reset()
4288{
4289    _matrixStack.clear();
4290}
4291
4292void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Group& group)
4293{
4294    // only continue if there is a parent
4295    const unsigned int nodepathsize = _nodePath.size();
4296    if(!_matrixStack.empty() && group.getNumParents() > 1 && nodepathsize > 1)
4297    {
4298        // copy this Group
4299        osg::ref_ptr<osg::Object> new_obj = group.clone(osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4300        osg::Group* new_group = dynamic_cast<osg::Group*>(new_obj.get());
4301
4302        // New Group should only be added to parent through which this Group
4303        // was traversed, not to all parents of this Group.
4304        osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4305        if(parent_group)
4306        {
4307            parent_group->replaceChild(&group, new_group);
4308            // traverse the new Group
4309            traverse(*(new_group));
4310        }
4311        else
4312        {
4313            osg::notify(osg::NOTICE) << "No parent for this Group" << std::endl;
4314        }
4315    }
4316    else
4317    {
4318        // traverse original node
4319        traverse(group);
4320    }
4321}
4322
4323
4324void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Transform& transform)
4325{
4326    bool pushed = false;
4327
4328    // only continue if there is a parent and this is a STATIC transform
4329    const unsigned int nodepathsize = _nodePath.size();
4330    if(transform.getDataVariance() == osg::Object::STATIC && nodepathsize > 1)
4331    {
4332        osg::Matrix matrix;
4333        if(!_matrixStack.empty())
4334            matrix = _matrixStack.back();
4335        transform.computeLocalToWorldMatrix(matrix, this);
4336        _matrixStack.push_back(matrix);
4337        pushed = true;
4338   
4339        // convert this Transform to a Group
4340        osg::ref_ptr<osg::Group> group = new osg::Group(dynamic_cast<osg::Group&>(transform),
4341            osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4342
4343        // New Group should only be added to parent through which this Transform
4344        // was traversed, not to all parents of this Transform.
4345        osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4346        if(parent_group)
4347        {
4348            parent_group->replaceChild(&transform, group.get());
4349            // traverse the new Group
4350            traverse(*(group.get()));
4351        }
4352        else
4353        {
4354            osg::notify(osg::NOTICE) << "No parent for this Group" << std::endl;
4355        }
4356    }
4357    else
4358    {
4359        // traverse original node
4360        traverse(transform);
4361    }
4362
4363    // pop matrix off of stack
4364    if(pushed)
4365        _matrixStack.pop_back();
4366}
4367
4368
4369void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::LOD& lod)
4370{
4371    const unsigned int nodepathsize = _nodePath.size();
4372    if(!_matrixStack.empty() && lod.getNumParents() > 1 && nodepathsize > 1)
4373    {
4374        osg::ref_ptr<osg::LOD> new_lod = new osg::LOD(lod,
4375            osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4376
4377        // New LOD should only be added to parent through which this LOD
4378        // was traversed, not to all parents of this LOD.
4379        osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4380        if(parent_group)
4381        {
4382            parent_group->replaceChild(&lod, new_lod.get());
4383
4384            // move center point
4385            if(!_matrixStack.empty())
4386                new_lod->setCenter(new_lod->getCenter() * _matrixStack.back());
4387
4388            // traverse the new Group
4389            traverse(*(new_lod.get()));
4390        }
4391        else
4392            osg::notify(osg::NOTICE) << "No parent for this LOD" << std::endl;
4393    }
4394    else
4395    {
4396        // move center point
4397        if(!_matrixStack.empty())
4398            lod.setCenter(lod.getCenter() * _matrixStack.back());
4399
4400        traverse(lod);
4401    }
4402}
4403
4404
4405void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Geode& geode)
4406{
4407    if(!_matrixStack.empty())
4408    {
4409        // If there is only one parent, just transform all vertices and normals
4410        if(geode.getNumParents() == 1)
4411        {
4412            transformGeode(geode);
4413        }   
4414        else
4415        {
4416            // Else make a copy and then transform
4417            const unsigned int nodepathsize = _nodePath.size();
4418            if(nodepathsize > 1)
4419            {
4420                // convert this Transform to a Group
4421                osg::ref_ptr<osg::Geode> new_geode = new osg::Geode(geode,
4422                    osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4423
4424                // New Group should only be added to parent through which this Transform
4425                // was traversed, not to all parents of this Transform.
4426                osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4427                if(parent_group)
4428                    parent_group->replaceChild(&geode, new_geode.get());
4429                else
4430                    osg::notify(osg::NOTICE) << "No parent for this Geode" << std::endl;
4431
4432                transformGeode(*(new_geode.get()));
4433            }
4434        }
4435    }
4436}
4437
4438
4439void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Billboard& billboard)
4440{
4441    if(!_matrixStack.empty())
4442    {
4443        // If there is only one parent, just transform this Billboard
4444        if(billboard.getNumParents() == 1)
4445        {
4446            transformBillboard(billboard);
4447        }
4448        else
4449        {
4450            // Else make a copy and then transform
4451            const unsigned int nodepathsize = _nodePath.size();
4452            if(nodepathsize > 1)
4453            {
4454                // convert this Transform to a Group
4455                osg::ref_ptr<osg::Billboard> new_billboard = new osg::Billboard(billboard,
4456                    osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS);
4457
4458                // New Billboard should only be added to parent through which this Billboard
4459                // was traversed, not to all parents of this Billboard.
4460                osg::Group* parent_group = dynamic_cast<osg::Group*>(_nodePath[nodepathsize-2]);
4461                if(parent_group)
4462                    parent_group->replaceChild(&billboard, new_billboard.get());
4463                else
4464                    osg::notify(osg::NOTICE) << "No parent for this Billboard" << std::endl;
4465
4466                transformBillboard(*(new_billboard.get()));
4467            }
4468        }
4469    }
4470}
4471
4472
4473void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::transformGeode(osg::Geode& geode)
4474{
4475    for(unsigned int i=0; i<geode.getNumDrawables(); i++)
4476    {
4477        transformDrawable(*geode.getDrawable(i));
4478    }
4479
4480    geode.dirtyBound();
4481}
4482
4483
4484void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::transformDrawable(osg::Drawable& drawable)
4485{
4486    osg::Geometry* geometry = drawable.asGeometry();
4487    if(geometry)
4488    {
4489        // transform all geometry
4490        osg::Vec3Array* verts = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
4491        if(verts)
4492        {
4493            for(unsigned int j=0; j<verts->size(); j++)
4494            {
4495                (*verts)[j] = (*verts)[j] * _matrixStack.back();
4496            }
4497        }
4498        else
4499        {
4500            osg::Vec4Array* verts = dynamic_cast<osg::Vec4Array*>(geometry->getVertexArray());
4501            if(verts)
4502            {
4503                for(unsigned int j=0; j<verts->size(); j++)
4504                {
4505                    (*verts)[j] = _matrixStack.back() * (*verts)[j];
4506                }
4507            }
4508        }
4509        osg::Vec3Array* normals = dynamic_cast<osg::Vec3Array*>(geometry->getNormalArray());
4510        if(normals)
4511        {
4512            for(unsigned int j=0; j<normals->size(); j++)
4513                (*normals)[j] = osg::Matrix::transform3x3((*normals)[j], _matrixStack.back());
4514        }
4515
4516        geometry->dirtyBound();
4517        geometry->dirtyDisplayList();
4518    }
4519}
4520
4521
4522void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::transformBillboard(osg::Billboard& billboard)
4523{
4524    osg::Vec3 axis = osg::Matrix::transform3x3(billboard.getAxis(), _matrixStack.back());
4525    axis.normalize();
4526    billboard.setAxis(axis);
4527
4528    osg::Vec3 normal = osg::Matrix::transform3x3(billboard.getNormal(), _matrixStack.back());
4529    normal.normalize();
4530    billboard.setNormal(normal);
4531
4532    for(unsigned int i=0; i<billboard.getNumDrawables(); i++)
4533    {
4534        osg::Vec3 originalBillboardPosition = billboard.getPosition(i);
4535        billboard.setPosition(i, originalBillboardPosition * _matrixStack.back());
4536
4537        osg::Matrix matrixForDrawable = _matrixStack.back();
4538        matrixForDrawable.preMult(osg::Matrix::translate(originalBillboardPosition));
4539        matrixForDrawable.postMult(osg::Matrix::translate(-billboard.getPosition(i)));
4540
4541        _matrixStack.push_back(matrixForDrawable);
4542        transformDrawable(*billboard.getDrawable(i));
4543        _matrixStack.pop_back();
4544    }
4545
4546    billboard.dirtyBound();
4547}
4548
4549
Note: See TracBrowser for help on using the browser.