]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/obj.cxx
Lots of (mostly) doxygen fixes/cleanup.
[simgear.git] / simgear / scene / tgdb / obj.cxx
1 // obj.cxx -- routines to handle loading scenery and building the plib
2 //            scene graph.
3 //
4 // Written by Curtis Olson, started October 1997.
5 //
6 // Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <simgear_config.h>
27 #endif
28
29 #include "obj.hxx"
30
31 #include <simgear/compiler.h>
32
33 #include <osg/Fog>
34 #include <osg/Geode>
35 #include <osg/Geometry>
36 #include <osg/Group>
37 #include <osg/LOD>
38 #include <osg/MatrixTransform>
39 #include <osg/Point>
40 #include <osg/Referenced>
41 #include <osg/StateSet>
42 #include <osg/Switch>
43
44 #include <osgUtil/Simplifier>
45
46 #include <boost/foreach.hpp>
47
48 #include <algorithm>
49
50 #include <simgear/debug/logstream.hxx>
51 #include <simgear/io/sg_binobj.hxx>
52 #include <simgear/math/sg_geodesy.hxx>
53 #include <simgear/math/sg_random.h>
54 #include <simgear/math/SGMisc.hxx>
55 #include <simgear/scene/material/Effect.hxx>
56 #include <simgear/scene/material/EffectGeode.hxx>
57 #include <simgear/scene/material/mat.hxx>
58 #include <simgear/scene/material/matmodel.hxx>
59 #include <simgear/scene/material/matlib.hxx>
60 #include <simgear/scene/model/SGOffsetTransform.hxx>
61 #include <simgear/scene/util/SGUpdateVisitor.hxx>
62 #include <simgear/scene/util/SGNodeMasks.hxx>
63 #include <simgear/scene/util/QuadTreeBuilder.hxx>
64 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
65 #include <simgear/scene/util/OptionsReadFileCallback.hxx>
66
67 #include "SGTexturedTriangleBin.hxx"
68 #include "SGLightBin.hxx"
69 #include "SGModelBin.hxx"
70 #include "SGBuildingBin.hxx"
71 #include "TreeBin.hxx"
72 #include "SGDirectionalLightBin.hxx"
73 #include "GroundLightManager.hxx"
74
75 #include "pt_lights.hxx"
76
77 #define SG_SIMPLIFIER_RATIO 0.001
78 #define SG_SIMPLIFIER_MAX_LENGTH 1000.0
79 #define SG_SIMPLIFIER_MAX_ERROR 2000.0
80 #define SG_OBJECT_RANGE 9000.0
81 #define SG_TILE_RADIUS 14000.0
82
83 using namespace simgear;
84
85 typedef std::map<std::string,SGTexturedTriangleBin> SGMaterialTriangleMap;
86 typedef std::list<SGLightBin> SGLightListBin;
87 typedef std::list<SGDirectionalLightBin> SGDirectionalLightListBin;
88
89 class SGTileGeometryBin : public osg::Referenced {
90 public:
91   SGMaterialTriangleMap materialTriangleMap;
92   SGLightBin tileLights;
93   SGLightBin randomTileLights;
94   SGTreeBinList randomForest;
95   SGDirectionalLightBin runwayLights;
96   SGDirectionalLightBin taxiLights;
97   SGDirectionalLightListBin vasiLights;
98   SGDirectionalLightListBin rabitLights;
99   SGLightListBin odalLights;
100   SGDirectionalLightListBin holdshortLights;
101   SGDirectionalLightListBin guardLights;
102   SGDirectionalLightListBin reilLights;
103   SGMatModelBin randomModels;
104   SGBuildingBinList randomBuildings;
105
106   static SGVec4f
107   getMaterialLightColor(const SGMaterial* material)
108   {
109     if (!material)
110       return SGVec4f(1, 1, 1, 0.8);
111     return material->get_light_color();
112   }
113
114   static void
115   addPointGeometry(SGLightBin& lights,
116                    const std::vector<SGVec3d>& vertices,
117                    const SGVec4f& color,
118                    const int_list& pts_v)
119   {
120     for (unsigned i = 0; i < pts_v.size(); ++i)
121       lights.insert(toVec3f(vertices[pts_v[i]]), color);
122   }
123
124   static void
125   addPointGeometry(SGDirectionalLightBin& lights,
126                    const std::vector<SGVec3d>& vertices,
127                    const std::vector<SGVec3f>& normals,
128                    const SGVec4f& color,
129                    const int_list& pts_v,
130                    const int_list& pts_n)
131   {
132     // If the normal indices match the vertex indices, use seperate
133     // normal indices. Else reuse the vertex indices for the normals.
134     if (pts_v.size() == pts_n.size()) {
135       for (unsigned i = 0; i < pts_v.size(); ++i)
136         lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_n[i]], color);
137     } else {
138       for (unsigned i = 0; i < pts_v.size(); ++i)
139         lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_v[i]], color);
140     }
141   }
142
143   bool
144   insertPtGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
145   {
146     if (obj.get_pts_v().size() != obj.get_pts_n().size()) {
147       SG_LOG(SG_TERRAIN, SG_ALERT,
148              "Group list sizes for points do not match!");
149       return false;
150     }
151
152     for (unsigned grp = 0; grp < obj.get_pts_v().size(); ++grp) {
153       std::string materialName = obj.get_pt_materials()[grp];
154       SGMaterial* material = 0;
155       if (matlib)
156           material = matlib->findCached(materialName);
157       SGVec4f color = getMaterialLightColor(material);
158
159       if (3 <= materialName.size() && materialName.substr(0, 3) != "RWY") {
160         // Just plain lights. Not something for the runway.
161         addPointGeometry(tileLights, obj.get_wgs84_nodes(), color,
162                          obj.get_pts_v()[grp]);
163       } else if (materialName == "RWY_BLUE_TAXIWAY_LIGHTS"
164                  || materialName == "RWY_GREEN_TAXIWAY_LIGHTS") {
165         addPointGeometry(taxiLights, obj.get_wgs84_nodes(), obj.get_normals(),
166                          color, obj.get_pts_v()[grp], obj.get_pts_n()[grp]);
167       } else if (materialName == "RWY_VASI_LIGHTS") {
168         vasiLights.push_back(SGDirectionalLightBin());
169         addPointGeometry(vasiLights.back(), obj.get_wgs84_nodes(),
170                          obj.get_normals(), color, obj.get_pts_v()[grp],
171                          obj.get_pts_n()[grp]);
172       } else if (materialName == "RWY_SEQUENCED_LIGHTS") {
173         rabitLights.push_back(SGDirectionalLightBin());
174         addPointGeometry(rabitLights.back(), obj.get_wgs84_nodes(),
175                          obj.get_normals(), color, obj.get_pts_v()[grp],
176                          obj.get_pts_n()[grp]);
177       } else if (materialName == "RWY_ODALS_LIGHTS") {
178         odalLights.push_back(SGLightBin());
179         addPointGeometry(odalLights.back(), obj.get_wgs84_nodes(),
180                          color, obj.get_pts_v()[grp]);
181       } else if (materialName == "RWY_YELLOW_PULSE_LIGHTS") {
182         holdshortLights.push_back(SGDirectionalLightBin());
183         addPointGeometry(holdshortLights.back(), obj.get_wgs84_nodes(),
184                          obj.get_normals(), color, obj.get_pts_v()[grp],
185                          obj.get_pts_n()[grp]);
186       } else if (materialName == "RWY_GUARD_LIGHTS") {
187         guardLights.push_back(SGDirectionalLightBin());
188         addPointGeometry(guardLights.back(), obj.get_wgs84_nodes(),
189                          obj.get_normals(), color, obj.get_pts_v()[grp],
190                          obj.get_pts_n()[grp]);
191       } else if (materialName == "RWY_REIL_LIGHTS") {
192         reilLights.push_back(SGDirectionalLightBin());
193         addPointGeometry(reilLights.back(), obj.get_wgs84_nodes(),
194                          obj.get_normals(), color, obj.get_pts_v()[grp],
195                          obj.get_pts_n()[grp]);
196       } else {
197         // what is left must be runway lights
198         addPointGeometry(runwayLights, obj.get_wgs84_nodes(),
199                          obj.get_normals(), color, obj.get_pts_v()[grp],
200                          obj.get_pts_n()[grp]);
201       }
202     }
203
204     return true;
205   }
206
207
208   static SGVec2f
209   getTexCoord(const std::vector<SGVec2f>& texCoords, const int_list& tc,
210               const SGVec2f& tcScale, unsigned i)
211   {
212     if (tc.empty())
213       return tcScale;
214     else if (tc.size() == 1)
215       return mult(texCoords[tc[0]], tcScale);
216     else
217       return mult(texCoords[tc[i]], tcScale);
218   }
219
220   static void
221   addTriangleGeometry(SGTexturedTriangleBin& triangles,
222                       const SGBinObject& obj, unsigned grp,
223                       const SGVec2f& tc0Scale, 
224                       const SGVec2f& tc1Scale)
225   {
226     const std::vector<SGVec3d>& vertices(obj.get_wgs84_nodes());
227     const std::vector<SGVec3f>& normals(obj.get_normals());
228     const std::vector<SGVec2f>& texCoords(obj.get_texcoords());
229     const int_list& tris_v(obj.get_tris_v()[grp]);
230     const int_list& tris_n(obj.get_tris_n()[grp]);
231     const tci_list& tris_tc(obj.get_tris_tcs()[grp]);
232     bool  num_norms_is_num_verts = true;  
233     
234     if (tris_v.size() != tris_n.size()) {
235         // If the normal indices do not match, they should be inmplicitly
236         // the same than the vertex indices. 
237         num_norms_is_num_verts = false;
238     }
239
240     if ( !tris_tc[1].empty() ) {
241         triangles.hasSecondaryTexCoord(true);
242     }
243     
244     for (unsigned i = 2; i < tris_v.size(); i += 3) {
245         SGVertNormTex v0;
246         v0.SetVertex( toVec3f(vertices[tris_v[i-2]]) );
247         v0.SetNormal( num_norms_is_num_verts ? normals[tris_n[i-2]] : 
248                                                normals[tris_v[i-2]] );
249         v0.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i-2) );
250         if (!tris_tc[1].empty()) {
251             v0.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i-2) );
252         }
253         SGVertNormTex v1;
254         v1.SetVertex( toVec3f(vertices[tris_v[i-1]]) );
255         v1.SetNormal( num_norms_is_num_verts ? normals[tris_n[i-1]] : 
256                                                normals[tris_v[i-1]] );
257         v1.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i-1) );
258         if (!tris_tc[1].empty()) {
259             v1.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i-1) );
260         }
261         SGVertNormTex v2;
262         v2.SetVertex( toVec3f(vertices[tris_v[i]]) );
263         v2.SetNormal( num_norms_is_num_verts ? normals[tris_n[i]] : 
264                                                normals[tris_v[i]] );
265         v2.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i) );
266         if (!tris_tc[1].empty()) {
267             v2.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i) );
268         }
269         
270         triangles.insert(v0, v1, v2);
271     }
272   }
273
274   static void
275   addStripGeometry(SGTexturedTriangleBin& triangles,
276                    const SGBinObject& obj, unsigned grp,
277                    const SGVec2f& tc0Scale, 
278                    const SGVec2f& tc1Scale)
279   {
280       const std::vector<SGVec3d>& vertices(obj.get_wgs84_nodes());
281       const std::vector<SGVec3f>& normals(obj.get_normals());
282       const std::vector<SGVec2f>& texCoords(obj.get_texcoords());
283       const int_list& strips_v(obj.get_strips_v()[grp]);
284       const int_list& strips_n(obj.get_strips_n()[grp]);
285       const tci_list& strips_tc(obj.get_strips_tcs()[grp]);
286       bool  num_norms_is_num_verts = true;  
287       
288       if (strips_v.size() != strips_n.size()) {
289           // If the normal indices do not match, they should be inmplicitly
290           // the same than the vertex indices. 
291           num_norms_is_num_verts = false;
292       }
293       
294       if ( !strips_tc[1].empty() ) {
295           triangles.hasSecondaryTexCoord(true);
296       }
297       
298     for (unsigned i = 2; i < strips_v.size(); ++i) {
299       SGVertNormTex v0;
300       v0.SetVertex( toVec3f(vertices[strips_v[i-2]]) );
301       v0.SetNormal( num_norms_is_num_verts ? normals[strips_n[i-2]] : 
302                                              normals[strips_v[i-2]] );
303       v0.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[0], tc0Scale, i-2) );
304       if (!strips_tc[1].empty()) {
305           v0.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i-2) );
306       }
307       SGVertNormTex v1;
308       v1.SetVertex( toVec3f(vertices[strips_v[i-1]]) );
309       v1.SetNormal( num_norms_is_num_verts ? normals[strips_n[i-1]] : 
310                                              normals[strips_v[i-1]] );
311       v1.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[1], tc0Scale, i-1) );
312       if (!strips_tc[1].empty()) {
313           v1.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i-1) );
314       }
315       SGVertNormTex v2;
316       v2.SetVertex( toVec3f(vertices[strips_v[i]]) );
317       v2.SetNormal( num_norms_is_num_verts ? normals[strips_n[i]] : 
318                                              normals[strips_v[i]] );
319       v2.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[0], tc0Scale, i) );
320       if (!strips_tc[1].empty()) {
321           v2.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i) );
322       }
323       if (i%2)
324         triangles.insert(v1, v0, v2);
325       else
326         triangles.insert(v0, v1, v2);
327     }
328   }
329
330   static void
331   addFanGeometry(SGTexturedTriangleBin& triangles,
332                  const SGBinObject& obj, unsigned grp,
333                  const SGVec2f& tc0Scale, 
334                  const SGVec2f& tc1Scale)
335   {
336       const std::vector<SGVec3d>& vertices(obj.get_wgs84_nodes());
337       const std::vector<SGVec3f>& normals(obj.get_normals());
338       const std::vector<SGVec2f>& texCoords(obj.get_texcoords());
339       const int_list& fans_v(obj.get_fans_v()[grp]);
340       const int_list& fans_n(obj.get_fans_n()[grp]);
341       const tci_list& fans_tc(obj.get_fans_tcs()[grp]);
342       bool  num_norms_is_num_verts = true;  
343       
344       if (fans_v.size() != fans_n.size()) {
345           // If the normal indices do not match, they should be inmplicitly
346           // the same than the vertex indices. 
347           num_norms_is_num_verts = false;
348       }
349       
350       if ( !fans_tc[1].empty() ) {
351           triangles.hasSecondaryTexCoord(true);
352       }
353       
354     SGVertNormTex v0;
355     v0.SetVertex( toVec3f(vertices[fans_v[0]]) );
356     v0.SetNormal( num_norms_is_num_verts ? normals[fans_n[0]] : 
357                                            normals[fans_v[0]] );
358     v0.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, 0) );
359     if (!fans_tc[1].empty()) {
360         v0.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, 0) );
361     }
362     SGVertNormTex v1;
363     v1.SetVertex( toVec3f(vertices[fans_v[1]]) );
364     v1.SetNormal( num_norms_is_num_verts ? normals[fans_n[1]] : 
365                                            normals[fans_v[1]] );
366     v1.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, 1) );
367     if (!fans_tc[1].empty()) {
368         v1.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, 1) );
369     }
370     for (unsigned i = 2; i < fans_v.size(); ++i) {
371       SGVertNormTex v2;
372       v2.SetVertex( toVec3f(vertices[fans_v[i]]) );
373       v2.SetNormal( num_norms_is_num_verts ? normals[fans_n[i]] : 
374                                              normals[fans_v[i]] );
375       v2.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, i) );
376       if (!fans_tc[1].empty()) {
377           v2.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, i) );
378       }
379       triangles.insert(v0, v1, v2);
380       v1 = v2;
381     }
382   }
383
384   SGVec2f getTexCoordScale(const std::string& name, SGMaterialLib* matlib)
385   {
386     if (!matlib)
387       return SGVec2f(1, 1);
388     SGMaterial* material = matlib->findCached(name);
389     if (!material)
390       return SGVec2f(1, 1);
391
392     return material->get_tex_coord_scale();
393   }
394
395   bool
396   insertSurfaceGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
397   {
398     if (obj.get_tris_n().size() < obj.get_tris_v().size() ||
399         obj.get_tris_tcs().size() < obj.get_tris_v().size()) {
400       SG_LOG(SG_TERRAIN, SG_ALERT,
401              "Group list sizes for triangles do not match!");
402       return false;
403     }
404
405     for (unsigned grp = 0; grp < obj.get_tris_v().size(); ++grp) {
406       std::string materialName = obj.get_tri_materials()[grp];
407       SGVec2f tc0Scale = getTexCoordScale(materialName, matlib);
408       SGVec2f tc1Scale(1.0, 1.0);
409       addTriangleGeometry(materialTriangleMap[materialName],
410                           obj, grp, tc0Scale, tc1Scale );
411     }
412
413     if (obj.get_strips_n().size() < obj.get_strips_v().size() ||
414         obj.get_strips_tcs().size() < obj.get_strips_v().size()) {
415       SG_LOG(SG_TERRAIN, SG_ALERT,
416              "Group list sizes for strips do not match!");
417       return false;
418     }
419     for (unsigned grp = 0; grp < obj.get_strips_v().size(); ++grp) {
420       std::string materialName = obj.get_strip_materials()[grp];
421       SGVec2f tc0Scale = getTexCoordScale(materialName, matlib);
422       SGVec2f tc1Scale(1.0, 1.0);
423       addStripGeometry(materialTriangleMap[materialName],
424                           obj, grp, tc0Scale, tc1Scale);
425     }
426
427     if (obj.get_fans_n().size() < obj.get_fans_v().size() ||
428         obj.get_fans_tcs().size() < obj.get_fans_v().size()) {
429       SG_LOG(SG_TERRAIN, SG_ALERT,
430              "Group list sizes for fans do not match!");
431       return false;
432     }
433     for (unsigned grp = 0; grp < obj.get_fans_v().size(); ++grp) {
434       std::string materialName = obj.get_fan_materials()[grp];
435       SGVec2f tc0Scale = getTexCoordScale(materialName, matlib);
436       SGVec2f tc1Scale(1.0, 1.0);
437       addFanGeometry(materialTriangleMap[materialName],
438                        obj, grp, tc0Scale, tc1Scale );
439     }
440     return true;
441   }
442
443   osg::Node* getSurfaceGeometry(SGMaterialLib* matlib, bool useVBOs) const
444   {
445     if (materialTriangleMap.empty())
446       return 0;
447
448     EffectGeode* eg = NULL;
449     osg::Group* group = (materialTriangleMap.size() > 1 ? new osg::Group : NULL);
450     if (group) {
451         group->setName("surfaceGeometryGroup");
452     }
453     
454     //osg::Geode* geode = new osg::Geode;
455     SGMaterialTriangleMap::const_iterator i;
456     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
457       osg::Geometry* geometry = i->second.buildGeometry(useVBOs);
458       SGMaterial *mat = NULL;
459       if (matlib) {
460         mat = matlib->findCached(i->first);
461       }
462       eg = new EffectGeode;
463       eg->setName("EffectGeode");
464       if (mat) {
465         eg->setEffect(mat->get_effect(i->second));
466       }
467       eg->addDrawable(geometry);
468       eg->runGenerators(geometry);  // Generate extra data needed by effect
469       if (group) {
470         group->addChild(eg);
471       }
472     }
473     
474     if (group) {
475         return group;
476     } else {
477         return eg;
478     }
479   }
480
481   void computeRandomSurfaceLights(SGMaterialLib* matlib)
482   {
483     SGMaterialTriangleMap::iterator i;
484
485     // generate a repeatable random seed
486     mt seed;
487     mt_init(&seed, unsigned(123));
488
489     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
490       SGMaterial *mat = matlib->findCached(i->first);
491       if (!mat)
492         continue;
493
494       float coverage = mat->get_light_coverage();
495       if (coverage <= 0)
496         continue;
497       if (coverage < 10000.0) {
498         SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
499                << coverage << ", pushing up to 10000");
500         coverage = 10000;
501       }
502
503       std::vector<SGVec3f> randomPoints;
504       i->second.addRandomSurfacePoints(coverage, 3, mat->get_object_mask(i->second), randomPoints);
505       std::vector<SGVec3f>::iterator j;
506       for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
507         float zombie = mt_rand(&seed);
508         // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
509         float factor = mt_rand(&seed);
510         factor *= factor;
511
512         float bright = 1;
513         SGVec4f color;
514         if ( zombie > 0.5 ) {
515           // 50% chance of yellowish
516           color = SGVec4f(0.9f, 0.9f, 0.3f, bright - factor * 0.2f);
517         } else if (zombie > 0.15f) {
518           // 35% chance of whitish
519           color = SGVec4f(0.9, 0.9f, 0.8f, bright - factor * 0.2f);
520         } else if (zombie > 0.05f) {
521           // 10% chance of orangish
522           color = SGVec4f(0.9f, 0.6f, 0.2f, bright - factor * 0.2f);
523         } else {
524           // 5% chance of redish
525           color = SGVec4f(0.9f, 0.2f, 0.2f, bright - factor * 0.2f);
526         }
527         randomTileLights.insert(*j, color);
528       }
529     }
530   }
531
532   void computeRandomObjectsAndBuildings(
533     SGMaterialLib* matlib,
534     float building_density,
535     bool use_random_objects,
536     bool use_random_buildings,
537     bool useVBOs)
538   {
539     SGMaterialTriangleMap::iterator i;
540
541     // generate a repeatable random seed
542     mt seed;
543     mt_init(&seed, unsigned(123));
544
545     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
546       SGMaterial *mat = matlib->findCached(i->first);
547       SGTexturedTriangleBin triangleBin = i->second;
548
549       if (!mat)
550         continue;
551
552       osg::Texture2D* object_mask  = mat->get_object_mask(triangleBin);
553
554       int   group_count            = mat->get_object_group_count();
555       float building_coverage      = mat->get_building_coverage();
556       float cos_zero_density_angle = mat->get_cos_object_zero_density_slope_angle();
557       float cos_max_density_angle  = mat->get_cos_object_max_density_slope_angle();
558
559       if (building_coverage == 0)
560          continue;
561
562       SGBuildingBin* bin = NULL;
563
564       if (building_coverage > 0) {
565         bin = new SGBuildingBin(mat, useVBOs);
566         randomBuildings.push_back(bin);
567       }
568
569       unsigned num = i->second.getNumTriangles();
570       int random_dropped = 0;
571       int mask_dropped = 0;
572       int building_dropped = 0;
573       int triangle_dropped = 0;
574
575       for (unsigned i = 0; i < num; ++i) {
576         SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i);
577
578         SGVec3f vorigin = triangleBin.getVertex(triangleRef[0]).GetVertex();
579         SGVec3f v0 = triangleBin.getVertex(triangleRef[1]).GetVertex() - vorigin;
580         SGVec3f v1 = triangleBin.getVertex(triangleRef[2]).GetVertex() - vorigin;
581         SGVec2f torigin = triangleBin.getVertex(triangleRef[0]).GetTexCoord(0);
582         SGVec2f t0 = triangleBin.getVertex(triangleRef[1]).GetTexCoord(0) - torigin;
583         SGVec2f t1 = triangleBin.getVertex(triangleRef[2]).GetTexCoord(0) - torigin;
584         SGVec3f normal = cross(v0, v1);
585
586         // Ensure the slope isn't too steep by checking the
587         // cos of the angle between the slope normal and the
588         // vertical (conveniently the z-component of the normalized
589         // normal) and values passed in.
590         float cos = normalize(normal).z();
591         float slope_density = 1.0;
592         if (cos < cos_zero_density_angle) continue; // Too steep for any objects
593         if (cos < cos_max_density_angle) {
594           slope_density =
595             (cos - cos_zero_density_angle) /
596             (cos_max_density_angle - cos_zero_density_angle);
597         }
598
599         // Containers to hold the random buildings and objects generated
600         // for this triangle for collision detection purposes.
601         std::vector< std::pair< SGVec3f, float> > triangleObjectsList;
602         std::vector< std::pair< SGVec3f, float> > triangleBuildingList;
603
604         // Compute the area
605         float area = 0.5f*length(normal);
606         if (area <= SGLimitsf::min())
607           continue;
608
609         // Generate any random objects
610         if (use_random_objects && (group_count > 0))
611         {
612           for (int j = 0; j < group_count; j++)
613           {
614             SGMatModelGroup *object_group =  mat->get_object_group(j);
615             int nObjects = object_group->get_object_count();
616
617             if (nObjects == 0) continue;
618
619             // For each of the random models in the group, determine an appropriate
620             // number of random placements and insert them.
621             for (int k = 0; k < nObjects; k++) {
622               SGMatModel * object = object_group->get_object(k);
623
624               // Determine the number of objecst to place, taking into account
625               // the slope density factor.
626               double n = slope_density * area / object->get_coverage_m2();
627
628               // Use the zombie door method to determine fractional object placement.
629               n = n + mt_rand(&seed);
630
631               // place an object each unit of area
632               while ( n > 1.0 ) {
633                 float a = mt_rand(&seed);
634                 float b = mt_rand(&seed);
635                 if ( a + b > 1 ) {
636                   a = 1 - a;
637                   b = 1 - b;
638                 }
639
640                 SGVec3f randomPoint = vorigin + a*v0 + b*v1;
641                 float rotation = static_cast<float>(mt_rand(&seed));
642
643                 // Check that the point is sufficiently far from
644                 // the edge of the triangle by measuring the distance
645                 // from the three lines that make up the triangle.
646                 float spacing = object->get_spacing_m();
647
648                 SGVec3f p = randomPoint - vorigin;
649                 float edges[] = { length(cross(p     , p - v0)) / length(v0),
650                                   length(cross(p - v0, p - v1)) / length(v1 - v0),
651                                   length(cross(p - v1, p     )) / length(v1)      };
652                 float edge_dist = *std::min_element(edges, edges + 3);
653
654                 if (edge_dist < spacing) {
655                   n -= 1.0;
656                   continue;
657                 }
658
659                 if (object_mask != NULL) {
660                   SGVec2f texCoord = torigin + a*t0 + b*t1;
661
662                   // Check this random point against the object mask
663                   // blue (for buildings) channel.
664                   osg::Image* img = object_mask->getImage();
665                   unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
666                   unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
667
668                   if (mt_rand(&seed) > img->getColor(x, y).b()) {
669                     // Failed object mask check
670                     n -= 1.0;
671                     continue;
672                   }
673
674                   rotation = img->getColor(x,y).r();
675                 }
676
677                 bool close = false;
678
679                 // Check it isn't too close to any other random objects in the triangle
680                 std::vector<std::pair<SGVec3f, float> >::iterator l;
681                 for (l = triangleObjectsList.begin(); l != triangleObjectsList.end(); ++l) {
682                   float min_dist2 = (l->second + object->get_spacing_m()) *
683                                     (l->second + object->get_spacing_m());
684
685                   if (distSqr(l->first, randomPoint) > min_dist2) {
686                     close = true;
687                     continue;
688                   }
689                 }
690
691                 if (!close) {
692                     triangleObjectsList.push_back(std::make_pair(randomPoint, object->get_spacing_m()));
693                     randomModels.insert(randomPoint,
694                                         object,
695                                         (int)object->get_randomized_range_m(&seed),
696                                         rotation);
697                 }
698               }
699             }
700           }
701         }
702
703         // Random objects now generated.  Now generate the random buildings (if any);
704         if (use_random_buildings && (building_coverage > 0) && (building_density > 0)) {
705
706           // Calculate the number of buildings, taking into account building density (which is linear)
707           // and the slope density factor.
708           double num = building_density * building_density * slope_density * area / building_coverage;
709
710           // For partial units of area, use a zombie door method to
711           // create the proper random chance of an object being created
712           // for this triangle.
713           num = num + mt_rand(&seed);
714
715           if (num < 1.0f) {
716             continue;
717           }
718
719           // Cosine of the angle between the two vectors.
720           float cosine = (dot(v0, v1) / (length(v0) * length(v1)));
721
722           // Determine a grid spacing in each vector such that the correct
723           // coverage will result.
724           float stepv0 = (sqrtf(building_coverage) / building_density) / length(v0) / sqrtf(1 - cosine * cosine);
725           float stepv1 = (sqrtf(building_coverage) / building_density) / length(v1);
726
727           stepv0 = std::min(stepv0, 1.0f);
728           stepv1 = std::min(stepv1, 1.0f);
729
730           // Start at a random point. a will be immediately incremented below.
731           float a = -mt_rand(&seed) * stepv0;
732           float b = mt_rand(&seed) * stepv1;
733
734           // Place an object each unit of area
735           while (num > 1.0) {
736             num -= 1.0;
737
738             // Set the next location to place a building
739             a += stepv0;
740
741             if ((a + b) > 1.0f) {
742               // Reached the end of the scan-line on v0. Reset and increment
743               // scan-line on v1
744               a = mt_rand(&seed) * stepv0;
745               b += stepv1;
746             }
747
748             if (b > 1.0f) {
749               // In a degenerate case of a single point, we might be outside the
750               // scanline.  Note that we need to still ensure that a+b < 1.
751               b = mt_rand(&seed) * stepv1 * (1.0f - a);
752             }
753
754             if ((a + b) > 1.0f ) {
755               // Truly degenerate case - simply choose a random point guaranteed
756               // to fulfil the constraing of a+b < 1.
757               a = mt_rand(&seed);
758               b = mt_rand(&seed) * (1.0f - a);
759             }
760
761             SGVec3f randomPoint = vorigin + a*v0 + b*v1;
762             float rotation = mt_rand(&seed);
763
764             if (object_mask != NULL) {
765               SGVec2f texCoord = torigin + a*t0 + b*t1;
766               osg::Image* img = object_mask->getImage();
767               int x = (int) (img->s() * texCoord.x()) % img->s();
768               int y = (int) (img->t() * texCoord.y()) % img->t();
769
770               // In some degenerate cases x or y can be < 1, in which case the mod operand fails
771               while (x < 0) x += img->s();
772               while (y < 0) y += img->t();
773
774               if (mt_rand(&seed) < img->getColor(x, y).b()) {
775                 // Object passes mask. Rotation is taken from the red channel
776                 rotation = img->getColor(x,y).r();
777               } else {
778                 // Fails mask test - try again.
779                 mask_dropped++;
780                 continue;
781               }
782             }
783
784             // Check building isn't too close to the triangle edge.
785             float type_roll = mt_rand(&seed);
786             SGBuildingBin::BuildingType buildingtype = bin->getBuildingType(type_roll);
787             float radius = bin->getBuildingMaxRadius(buildingtype);
788
789             // Determine the actual center of the building, by shifting from the
790             // center of the front face to the true center.
791             osg::Matrix rotationMat = osg::Matrix::rotate(- rotation * M_PI * 2,
792                                                  osg::Vec3f(0.0, 0.0, 1.0));
793             SGVec3f buildingCenter = randomPoint + toSG(osg::Vec3f(-0.5 * bin->getBuildingMaxDepth(buildingtype), 0.0, 0.0) * rotationMat);
794
795             SGVec3f p = buildingCenter - vorigin;
796             float edges[] = { length(cross(p     , p - v0)) / length(v0),
797                               length(cross(p - v0, p - v1)) / length(v1 - v0),
798                               length(cross(p - v1, p     )) / length(v1)      };
799             float edge_dist = *std::min_element(edges, edges + 3);
800
801             if (edge_dist < radius) {
802               triangle_dropped++;
803               continue;
804             }
805
806             // Check building isn't too close to random objects and other buildings.
807             bool close = false;
808             std::vector<std::pair<SGVec3f, float> >::iterator iter;
809
810             for (iter = triangleBuildingList.begin(); iter != triangleBuildingList.end(); ++iter) {
811               float min_dist = iter->second + radius;
812               if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) {
813                 close = true;
814                 continue;
815               }
816             }
817
818             if (close) {
819               building_dropped++;
820               continue;
821             }
822
823             for (iter = triangleObjectsList.begin(); iter != triangleObjectsList.end(); ++iter) {
824               float min_dist = iter->second + radius;
825               if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) {
826                 close = true;
827                 continue;
828               }
829             }
830
831             if (close) {
832               random_dropped++;
833               continue;
834             }
835
836             std::pair<SGVec3f, float> pt = std::make_pair(buildingCenter, radius);
837             triangleBuildingList.push_back(pt);
838             bin->insert(randomPoint, rotation, buildingtype);
839           }
840         }
841
842         triangleObjectsList.clear();
843         triangleBuildingList.clear();
844       }
845
846       SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << ((bin) ? bin->getNumBuildings() : 0));
847       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to mask: " << mask_dropped);
848       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to random object: " << random_dropped);
849       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to other buildings: " << building_dropped);
850     }
851   }
852
853   void computeRandomForest(SGMaterialLib* matlib, float vegetation_density)
854   {
855     SGMaterialTriangleMap::iterator i;
856
857     // generate a repeatable random seed
858     mt seed;
859
860     mt_init(&seed, unsigned(586));
861
862     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
863       SGMaterial *mat = matlib->findCached(i->first);
864       if (!mat)
865         continue;
866
867       float wood_coverage = mat->get_wood_coverage();
868       if ((wood_coverage <= 0) || (vegetation_density <= 0))
869         continue;
870
871       // Attributes that don't vary by tree but do vary by material
872       bool found = false;
873       TreeBin* bin = NULL;
874
875       BOOST_FOREACH(bin, randomForest)
876       {
877         if ((bin->texture           == mat->get_tree_texture()  ) &&
878             (bin->texture_varieties == mat->get_tree_varieties()) &&
879             (bin->range             == mat->get_tree_range()    ) &&
880             (bin->width             == mat->get_tree_width()    ) &&
881             (bin->height            == mat->get_tree_height()   )   ) {
882             found = true;
883             break;
884         }
885       }
886
887       if (!found) {
888         bin = new TreeBin();
889         bin->texture = mat->get_tree_texture();
890           SG_LOG(SG_INPUT, SG_DEBUG, "Tree texture " << bin->texture);
891         bin->range   = mat->get_tree_range();
892         bin->width   = mat->get_tree_width();
893         bin->height  = mat->get_tree_height();
894         bin->texture_varieties = mat->get_tree_varieties();
895         randomForest.push_back(bin);
896       }
897
898       std::vector<SGVec3f> randomPoints;
899       i->second.addRandomTreePoints(wood_coverage,
900                                     mat->get_object_mask(i->second),
901                                     vegetation_density,
902                                     mat->get_cos_tree_max_density_slope_angle(),
903                                     mat->get_cos_tree_zero_density_slope_angle(),
904                                     randomPoints);
905
906       std::vector<SGVec3f>::iterator k;
907       for (k = randomPoints.begin(); k != randomPoints.end(); ++k) {
908         bin->insert(*k);
909       }
910     }
911   }
912
913   bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
914   {
915     if (!insertPtGeometry(obj, matlib))
916       return false;
917     if (!insertSurfaceGeometry(obj, matlib))
918       return false;
919     return true;
920   }
921 };
922
923 typedef std::pair<osg::Node*, int> ModelLOD;
924 struct MakeQuadLeaf {
925     osg::LOD* operator() () const { return new osg::LOD; }
926 };
927 struct AddModelLOD {
928     void operator() (osg::LOD* leaf, ModelLOD& mlod) const
929     {
930         leaf->addChild(mlod.first, 0, mlod.second);
931     }
932 };
933 struct GetModelLODCoord {
934     GetModelLODCoord() {}
935     GetModelLODCoord(const GetModelLODCoord& rhs)
936     {}
937     osg::Vec3 operator() (const ModelLOD& mlod) const
938     {
939         return mlod.first->getBound().center();
940     }
941 };
942
943 typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
944                         GetModelLODCoord>  RandomObjectsQuadtree;
945
946 class RandomObjectCallback : public OptionsReadFileCallback {
947 public:
948     virtual osgDB::ReaderWriter::ReadResult
949     readNode(const std::string&, const osgDB::Options*)
950     {
951         osg::ref_ptr<osg::Group> group = new osg::Group;
952         group->setName("Random Object and Lighting Group");
953         group->setDataVariance(osg::Object::STATIC);
954
955         osg::Node* node = loadTerrain();
956         if (node)
957           group->addChild(node);
958
959         osg::LOD* lightLOD = generateLightingTileObjects();
960         if (lightLOD)
961           group->addChild(lightLOD);
962
963         osg::LOD* objectLOD = generateRandomTileObjects();
964         if (objectLOD)
965           group->addChild(objectLOD);
966
967         return group.release();
968     }
969
970     // Load terrain if required
971     osg::Node* loadTerrain()
972     {
973       if (! _loadterrain)
974         return NULL;
975
976       SGBinObject tile;
977       if (!tile.read_bin(_path))
978         return NULL;
979
980       SGMaterialLibPtr matlib;
981       bool useVBOs = false;
982       bool simplifyNear    = false;
983       double ratio       = SG_SIMPLIFIER_RATIO;
984       double maxLength   = SG_SIMPLIFIER_MAX_LENGTH;
985       double maxError    = SG_SIMPLIFIER_MAX_ERROR;
986
987       if (_options) {
988         matlib = _options->getMaterialLib();
989         useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON");
990         SGPropertyNode* propertyNode = _options->getPropertyNode().get();
991         simplifyNear = propertyNode->getBoolValue("/sim/rendering/terrain/simplifier/enabled-near", simplifyNear);
992         ratio = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/ratio", ratio);
993         maxLength = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-length", maxLength);
994         maxError = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-error", maxError);
995       }
996
997       // PSADRO TODO : we can do this in terragear 
998       // - why not add a bitmask of flags to the btg so we can precompute this?
999       // and only do it if it hasn't been done already
1000       SGVec3d center = tile.get_gbs_center();
1001       SGGeod geodPos = SGGeod::fromCart(center);
1002       SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
1003
1004       // rotate the tiles so that the bounding boxes get nearly axis aligned.
1005       // this will help the collision tree's bounding boxes a bit ...
1006       std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
1007       for (unsigned i = 0; i < nodes.size(); ++i)
1008         nodes[i] = hlOr.transform(nodes[i]);
1009       tile.set_wgs84_nodes(nodes);
1010
1011       SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]);
1012       std::vector<SGVec3f> normals = tile.get_normals();
1013       for (unsigned i = 0; i < normals.size(); ++i)
1014         normals[i] = hlOrf.transform(normals[i]);
1015       tile.set_normals(normals);
1016
1017       osg::ref_ptr<SGTileGeometryBin> tileGeometryBin = new SGTileGeometryBin;
1018
1019       if (!tileGeometryBin->insertBinObj(tile, matlib))
1020         return NULL;
1021
1022       osg::Node* node = tileGeometryBin->getSurfaceGeometry(matlib, useVBOs);
1023       if (node && simplifyNear) {
1024         osgUtil::Simplifier simplifier(ratio, maxError, maxLength);
1025         node->accept(simplifier);
1026       }
1027
1028       return node;
1029     }
1030
1031     // Generate all the lighting objects for the tile.
1032     osg::LOD* generateLightingTileObjects()
1033     {
1034       SGMaterialLibPtr matlib;
1035
1036       if (_options)
1037         matlib = _options->getMaterialLib();
1038
1039       // FIXME: ugly, has a side effect
1040       if (matlib)
1041         _tileGeometryBin->computeRandomSurfaceLights(matlib);
1042
1043       GroundLightManager* lightManager = GroundLightManager::instance();
1044       osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
1045       SGVec3f up(0, 0, 1);
1046
1047       if (_tileGeometryBin->tileLights.getNumLights() > 0
1048           || _tileGeometryBin->randomTileLights.getNumLights() > 0) {
1049         osg::Group* groundLights0 = new osg::Group;
1050         groundLights0->setStateSet(lightManager->getGroundLightStateSet());
1051         groundLights0->setNodeMask(GROUNDLIGHTS0_BIT);
1052         osg::Geode* geode = new osg::Geode;
1053         geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->tileLights));
1054         geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->randomTileLights, 4, -0.3f));
1055         groundLights0->addChild(geode);
1056         lightGroup->addChild(groundLights0);
1057       }
1058
1059       if (_tileGeometryBin->randomTileLights.getNumLights() > 0) {
1060         osg::Group* groundLights1 = new osg::Group;
1061         groundLights1->setStateSet(lightManager->getGroundLightStateSet());
1062         groundLights1->setNodeMask(GROUNDLIGHTS1_BIT);
1063         osg::Group* groundLights2 = new osg::Group;
1064         groundLights2->setStateSet(lightManager->getGroundLightStateSet());
1065         groundLights2->setNodeMask(GROUNDLIGHTS2_BIT);
1066         osg::Geode* geode = new osg::Geode;
1067         geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->randomTileLights, 2, -0.15f));
1068         groundLights1->addChild(geode);
1069         lightGroup->addChild(groundLights1);
1070         geode = new osg::Geode;
1071         geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->randomTileLights));
1072         groundLights2->addChild(geode);
1073         lightGroup->addChild(groundLights2);
1074       }
1075
1076       if (!_tileGeometryBin->vasiLights.empty()) {
1077         EffectGeode* vasiGeode = new EffectGeode;
1078         Effect* vasiEffect
1079             = getLightEffect(24, osg::Vec3(1, 0.0001, 0.000001), 1, 24, true, _options);
1080         vasiGeode->setEffect(vasiEffect);
1081         SGVec4f red(1, 0, 0, 1);
1082         SGMaterial* mat = 0;
1083         if (matlib)
1084           mat = matlib->findCached("RWY_RED_LIGHTS");
1085         if (mat)
1086           red = mat->get_light_color();
1087         SGVec4f white(1, 1, 1, 1);
1088         mat = 0;
1089         if (matlib)
1090           mat = matlib->findCached("RWY_WHITE_LIGHTS");
1091         if (mat)
1092           white = mat->get_light_color();
1093         SGDirectionalLightListBin::const_iterator i;
1094         for (i = _tileGeometryBin->vasiLights.begin();
1095              i != _tileGeometryBin->vasiLights.end(); ++i) {
1096           vasiGeode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
1097         }
1098         vasiGeode->setStateSet(lightManager->getRunwayLightStateSet());
1099         lightGroup->addChild(vasiGeode);
1100       }
1101
1102       Effect* runwayEffect = 0;
1103       if (_tileGeometryBin->runwayLights.getNumLights() > 0
1104           || !_tileGeometryBin->rabitLights.empty()
1105           || !_tileGeometryBin->reilLights.empty()
1106           || !_tileGeometryBin->odalLights.empty()
1107           || _tileGeometryBin->taxiLights.getNumLights() > 0)
1108           runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true, _options);
1109       if (_tileGeometryBin->runwayLights.getNumLights() > 0
1110           || !_tileGeometryBin->rabitLights.empty()
1111           || !_tileGeometryBin->reilLights.empty()
1112           || !_tileGeometryBin->odalLights.empty()
1113           || !_tileGeometryBin->holdshortLights.empty()
1114           || !_tileGeometryBin->guardLights.empty()) {
1115         osg::Group* rwyLights = new osg::Group;
1116         rwyLights->setStateSet(lightManager->getRunwayLightStateSet());
1117         rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
1118         if (_tileGeometryBin->runwayLights.getNumLights() != 0) {
1119           EffectGeode* geode = new EffectGeode;
1120           geode->setEffect(runwayEffect);
1121           geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->runwayLights));
1122           rwyLights->addChild(geode);
1123         }
1124         SGDirectionalLightListBin::const_iterator i;
1125         for (i = _tileGeometryBin->rabitLights.begin();
1126              i != _tileGeometryBin->rabitLights.end(); ++i) {
1127           rwyLights->addChild(SGLightFactory::getSequenced(*i, _options));
1128         }
1129         for (i = _tileGeometryBin->reilLights.begin();
1130              i != _tileGeometryBin->reilLights.end(); ++i) {
1131           rwyLights->addChild(SGLightFactory::getSequenced(*i, _options));
1132         }
1133         for (i = _tileGeometryBin->holdshortLights.begin();
1134              i != _tileGeometryBin->holdshortLights.end(); ++i) {
1135           rwyLights->addChild(SGLightFactory::getHoldShort(*i, _options));
1136         }
1137         for (i = _tileGeometryBin->guardLights.begin();
1138              i != _tileGeometryBin->guardLights.end(); ++i) {
1139           rwyLights->addChild(SGLightFactory::getGuard(*i, _options));
1140         }
1141         SGLightListBin::const_iterator j;
1142         for (j = _tileGeometryBin->odalLights.begin();
1143              j != _tileGeometryBin->odalLights.end(); ++j) {
1144           rwyLights->addChild(SGLightFactory::getOdal(*j, _options));
1145         }
1146         lightGroup->addChild(rwyLights);
1147       }
1148
1149       if (_tileGeometryBin->taxiLights.getNumLights() > 0) {
1150         osg::Group* taxiLights = new osg::Group;
1151         taxiLights->setStateSet(lightManager->getTaxiLightStateSet());
1152         taxiLights->setNodeMask(RUNWAYLIGHTS_BIT);
1153         EffectGeode* geode = new EffectGeode;
1154         geode->setEffect(runwayEffect);
1155         geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->taxiLights));
1156         taxiLights->addChild(geode);
1157         lightGroup->addChild(taxiLights);
1158       }
1159
1160       osg::LOD* lightLOD = NULL;
1161
1162       if (lightGroup->getNumChildren() > 0) {
1163         lightLOD = new osg::LOD;
1164         lightLOD->addChild(lightGroup.get(), 0, 60000);
1165         // VASI is always on, so doesn't use light bits.
1166         lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT | PERMANENTLIGHT_BIT);
1167       }
1168
1169       return lightLOD;
1170     }
1171
1172     // Generate all the random forest, objects and buildings for the tile
1173     osg::LOD* generateRandomTileObjects()
1174     {
1175       SGMaterialLibPtr matlib;
1176       bool use_random_objects = false;
1177       bool use_random_vegetation = false;
1178       bool use_random_buildings = false;
1179       float vegetation_density = 1.0f;
1180       float building_density = 1.0f;
1181       bool useVBOs = false;
1182       
1183       osg::ref_ptr<osg::Group> randomObjects;
1184       osg::ref_ptr<osg::Group> forestNode;
1185       osg::ref_ptr<osg::Group> buildingNode;
1186
1187       if (_options) {
1188         matlib = _options->getMaterialLib();
1189         SGPropertyNode* propertyNode = _options->getPropertyNode().get();
1190         if (propertyNode) {
1191             use_random_objects
1192                 = propertyNode->getBoolValue("/sim/rendering/random-objects",
1193                                              use_random_objects);
1194             use_random_vegetation
1195                 = propertyNode->getBoolValue("/sim/rendering/random-vegetation",
1196                                              use_random_vegetation);
1197             vegetation_density
1198                 = propertyNode->getFloatValue("/sim/rendering/vegetation-density",
1199                                               vegetation_density);
1200             use_random_buildings
1201                 = propertyNode->getBoolValue("/sim/rendering/random-buildings",
1202                                              use_random_buildings);
1203             building_density
1204                 = propertyNode->getFloatValue("/sim/rendering/building-density",
1205                                               building_density);
1206         }
1207         
1208         useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON");
1209       }
1210
1211
1212
1213       if (matlib && (use_random_objects || use_random_buildings)) {
1214         _tileGeometryBin->computeRandomObjectsAndBuildings(matlib,
1215                                                          building_density,
1216                                                          use_random_objects,
1217                                                          use_random_buildings,
1218                                                          useVBOs);
1219       }
1220
1221
1222       if (_tileGeometryBin->randomModels.getNumModels() > 0) {
1223         // Generate a repeatable random seed
1224         mt seed;
1225         mt_init(&seed, unsigned(123));
1226
1227         std::vector<ModelLOD> models;
1228         for (unsigned int i = 0;
1229              i < _tileGeometryBin->randomModels.getNumModels(); i++) {
1230           SGMatModelBin::MatModel obj
1231             = _tileGeometryBin->randomModels.getMatModel(i);
1232
1233           SGPropertyNode* root = _options->getPropertyNode()->getRootNode();
1234           osg::Node* node = obj.model->get_random_model(root, &seed);
1235
1236           // Create a matrix to place the object in the correct
1237           // location, and then apply the rotation matrix created
1238           // above, with an additional random (or taken from
1239           // the object mask) heading rotation if appropriate.
1240           osg::Matrix transformMat;
1241           transformMat = osg::Matrix::translate(toOsg(obj.position));
1242           if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
1243             // Rotate the object around the z axis.
1244             double hdg = mt_rand(&seed) * M_PI * 2;
1245             transformMat.preMult(osg::Matrix::rotate(hdg,
1246                                                      osg::Vec3d(0.0, 0.0, 1.0)));
1247           }
1248
1249           if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) {
1250             // Rotate the object around the z axis.
1251             double hdg =  - obj.rotation * M_PI * 2;
1252             transformMat.preMult(osg::Matrix::rotate(hdg,
1253                                                      osg::Vec3d(0.0, 0.0, 1.0)));
1254           }
1255
1256           osg::MatrixTransform* position =
1257             new osg::MatrixTransform(transformMat);
1258           position->setName("positionRandomModel");
1259           position->addChild(node);
1260           models.push_back(ModelLOD(position, obj.lod));
1261         }
1262         RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
1263         quadtree.buildQuadTree(models.begin(), models.end());
1264         randomObjects = quadtree.getRoot();
1265         randomObjects->setName("Random objects");
1266       }
1267
1268       if (! _tileGeometryBin->randomBuildings.empty()) {
1269         buildingNode = createRandomBuildings(_tileGeometryBin->randomBuildings, osg::Matrix::identity(),
1270                                     _options);
1271         buildingNode->setName("Random buildings");
1272         _tileGeometryBin->randomBuildings.clear();
1273       }
1274
1275       if (use_random_vegetation && matlib) {
1276         // Now add some random forest.
1277         _tileGeometryBin->computeRandomForest(matlib, vegetation_density);
1278
1279         if (! _tileGeometryBin->randomForest.empty()) {
1280           forestNode = createForest(_tileGeometryBin->randomForest, osg::Matrix::identity(),
1281                                     _options);
1282           forestNode->setName("Random trees");
1283         }
1284       }
1285
1286       osg::LOD* objectLOD = NULL;
1287
1288       if (randomObjects.valid() ||  forestNode.valid() || buildingNode.valid()) {
1289         objectLOD = new osg::LOD;
1290
1291         if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
1292         if (forestNode.valid())  objectLOD->addChild(forestNode.get(), 0, 20000);
1293         if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000);
1294
1295         unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
1296         objectLOD->setNodeMask(nodeMask);
1297       }
1298
1299       return objectLOD;
1300     }
1301
1302     /// The original options to use for this bunch of models
1303     osg::ref_ptr<SGReaderWriterOptions> _options;
1304     osg::ref_ptr<SGTileGeometryBin> _tileGeometryBin;
1305     string _path;
1306     bool _loadterrain;
1307 };
1308
1309 osg::Node*
1310 SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options)
1311 {
1312     SGBinObject tile;
1313     if (!tile.read_bin(path))
1314       return NULL;
1315
1316     SGMaterialLibPtr matlib;
1317     bool useVBOs = false;
1318     bool simplifyDistant = false;
1319     bool simplifyNear    = false;
1320     double ratio       = SG_SIMPLIFIER_RATIO;
1321     double maxLength   = SG_SIMPLIFIER_MAX_LENGTH;
1322     double maxError    = SG_SIMPLIFIER_MAX_ERROR;
1323     double object_range = SG_OBJECT_RANGE;
1324
1325     if (options) {
1326       matlib = options->getMaterialLib();
1327       useVBOs = (options->getPluginStringData("SimGear::USE_VBOS") == "ON");
1328       SGPropertyNode* propertyNode = options->getPropertyNode().get();
1329
1330       // We control whether we simplify the nearby terrain and distant terrain separatey.
1331       // However, we don't allow only simplifying the near terrain!
1332       simplifyNear = propertyNode->getBoolValue("/sim/rendering/terrain/simplifier/enabled-near", simplifyNear);
1333       simplifyDistant = simplifyNear || propertyNode->getBoolValue("/sim/rendering/terrain/simplifier/enabled-far", simplifyDistant);
1334       ratio = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/ratio", ratio);
1335       maxLength = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-length", maxLength);
1336       maxError = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-error", maxError);
1337                         object_range = propertyNode->getDoubleValue("/sim/rendering/static-lod/rough", object_range);
1338     }
1339
1340     SGVec3d center = tile.get_gbs_center();
1341     SGGeod geodPos = SGGeod::fromCart(center);
1342     SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
1343
1344     // rotate the tiles so that the bounding boxes get nearly axis aligned.
1345     // this will help the collision tree's bounding boxes a bit ...
1346     std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
1347     for (unsigned i = 0; i < nodes.size(); ++i)
1348       nodes[i] = hlOr.transform(nodes[i]);
1349     tile.set_wgs84_nodes(nodes);
1350
1351     SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]);
1352     std::vector<SGVec3f> normals = tile.get_normals();
1353     for (unsigned i = 0; i < normals.size(); ++i)
1354       normals[i] = hlOrf.transform(normals[i]);
1355     tile.set_normals(normals);
1356
1357     osg::ref_ptr<SGTileGeometryBin> tileGeometryBin = new SGTileGeometryBin;
1358
1359     if (!tileGeometryBin->insertBinObj(tile, matlib))
1360       return NULL;
1361
1362
1363     osg::Node* node = tileGeometryBin->getSurfaceGeometry(matlib, useVBOs);
1364     if (node && simplifyDistant) {
1365       osgUtil::Simplifier simplifier(ratio, maxError, maxLength);
1366       node->accept(simplifier);
1367     }
1368
1369     // The toplevel transform for that tile.
1370     osg::MatrixTransform* transform = new osg::MatrixTransform;
1371     transform->setName(path);
1372     transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
1373                          osg::Matrix::translate(toOsg(center)));
1374
1375     // PagedLOD for the random objects so we don't need to generate
1376     // them all on tile loading.
1377     osg::PagedLOD* pagedLOD = new osg::PagedLOD;
1378     pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER);
1379     pagedLOD->setName("pagedObjectLOD");
1380
1381     if (node) {
1382       if (simplifyNear == simplifyDistant) {
1383         // Same terrain type is used for both near and far distances,
1384         // so add it to the main group.
1385         osg::Group* terrainGroup = new osg::Group;
1386         terrainGroup->setName("BTGTerrainGroup");
1387         terrainGroup->addChild(node);
1388         transform->addChild(terrainGroup);
1389       } else if (simplifyDistant) {
1390         // Simplified terrain is only used in the distance, the
1391         // call-back below will re-generate the closer version
1392         pagedLOD->addChild(node, object_range + SG_TILE_RADIUS, FLT_MAX);
1393       }
1394     }
1395
1396     // we just need to know about the read file callback that itself holds the data
1397     osg::ref_ptr<RandomObjectCallback> randomObjectCallback = new RandomObjectCallback;
1398     randomObjectCallback->_options = SGReaderWriterOptions::copyOrCreate(options);
1399     randomObjectCallback->_tileGeometryBin = tileGeometryBin;
1400     randomObjectCallback->_path = std::string(path);
1401     randomObjectCallback->_loadterrain = ! (simplifyNear == simplifyDistant);
1402
1403     osg::ref_ptr<osgDB::Options> callbackOptions = new osgDB::Options;
1404     callbackOptions->setReadFileCallback(randomObjectCallback.get());
1405     pagedLOD->setDatabaseOptions(callbackOptions.get());
1406
1407     pagedLOD->setFileName(pagedLOD->getNumChildren(), "Dummy name - use the stored data in the read file callback");
1408     pagedLOD->setRange(pagedLOD->getNumChildren(), 0, object_range + SG_TILE_RADIUS);
1409     transform->addChild(pagedLOD);
1410     transform->setNodeMask( ~simgear::MODELLIGHT_BIT );
1411
1412     return transform;
1413 }