]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/obj.cxx
Revert "Use simgear internal stuff for the singleton class."
[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/StateSet>
41 #include <osg/Switch>
42
43 #include <boost/foreach.hpp>
44
45 #include <algorithm>
46
47 #include <simgear/debug/logstream.hxx>
48 #include <simgear/io/sg_binobj.hxx>
49 #include <simgear/math/sg_geodesy.hxx>
50 #include <simgear/math/sg_random.h>
51 #include <simgear/scene/material/Effect.hxx>
52 #include <simgear/scene/material/EffectGeode.hxx>
53 #include <simgear/scene/material/mat.hxx>
54 #include <simgear/scene/material/matlib.hxx>
55 #include <simgear/scene/model/SGOffsetTransform.hxx>
56 #include <simgear/scene/util/SGUpdateVisitor.hxx>
57 #include <simgear/scene/util/SGNodeMasks.hxx>
58 #include <simgear/scene/util/QuadTreeBuilder.hxx>
59
60 #include "SGTexturedTriangleBin.hxx"
61 #include "SGLightBin.hxx"
62 #include "SGModelBin.hxx"
63 #include "TreeBin.hxx"
64 #include "SGDirectionalLightBin.hxx"
65 #include "GroundLightManager.hxx"
66
67
68 #include "pt_lights.hxx"
69
70 using namespace simgear;
71
72 typedef std::map<std::string,SGTexturedTriangleBin> SGMaterialTriangleMap;
73 typedef std::list<SGLightBin> SGLightListBin;
74 typedef std::list<SGDirectionalLightBin> SGDirectionalLightListBin;
75
76 struct SGTileGeometryBin {
77   SGMaterialTriangleMap materialTriangleMap;
78   SGLightBin tileLights;
79   SGLightBin randomTileLights;
80   SGTreeBinList randomForest;
81   SGDirectionalLightBin runwayLights;
82   SGDirectionalLightBin taxiLights;
83   SGDirectionalLightListBin vasiLights;
84   SGDirectionalLightListBin rabitLights;
85   SGLightListBin odalLights;
86   SGDirectionalLightListBin reilLights;
87   SGMatModelBin randomModels;
88
89   static SGVec4f
90   getMaterialLightColor(const SGMaterial* material)
91   {
92     if (!material)
93       return SGVec4f(1, 1, 1, 0.8);
94     return material->get_light_color();
95   }
96
97   static void
98   addPointGeometry(SGLightBin& lights,
99                    const std::vector<SGVec3d>& vertices,
100                    const SGVec4f& color,
101                    const int_list& pts_v)
102   {
103     for (unsigned i = 0; i < pts_v.size(); ++i)
104       lights.insert(toVec3f(vertices[pts_v[i]]), color);
105   }
106
107   static void
108   addPointGeometry(SGDirectionalLightBin& lights,
109                    const std::vector<SGVec3d>& vertices,
110                    const std::vector<SGVec3f>& normals,
111                    const SGVec4f& color,
112                    const int_list& pts_v,
113                    const int_list& pts_n)
114   {
115     // If the normal indices match the vertex indices, use seperate
116     // normal indices. Else reuse the vertex indices for the normals.
117     if (pts_v.size() == pts_n.size()) {
118       for (unsigned i = 0; i < pts_v.size(); ++i)
119         lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_n[i]], color);
120     } else {
121       for (unsigned i = 0; i < pts_v.size(); ++i)
122         lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_v[i]], color);
123     }
124   }
125
126   bool
127   insertPtGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
128   {
129     if (obj.get_pts_v().size() != obj.get_pts_n().size()) {
130       SG_LOG(SG_TERRAIN, SG_ALERT,
131              "Group list sizes for points do not match!");
132       return false;
133     }
134
135     for (unsigned grp = 0; grp < obj.get_pts_v().size(); ++grp) {
136       std::string materialName = obj.get_pt_materials()[grp];
137       SGMaterial* material = 0;
138       if (matlib)
139           material = matlib->find(materialName);
140       SGVec4f color = getMaterialLightColor(material);
141
142       if (3 <= materialName.size() && materialName.substr(0, 3) != "RWY") {
143         // Just plain lights. Not something for the runway.
144         addPointGeometry(tileLights, obj.get_wgs84_nodes(), color,
145                          obj.get_pts_v()[grp]);
146       } else if (materialName == "RWY_BLUE_TAXIWAY_LIGHTS"
147                  || materialName == "RWY_GREEN_TAXIWAY_LIGHTS") {
148         addPointGeometry(taxiLights, obj.get_wgs84_nodes(), obj.get_normals(),
149                          color, obj.get_pts_v()[grp], obj.get_pts_n()[grp]);
150       } else if (materialName == "RWY_VASI_LIGHTS") {
151         vasiLights.push_back(SGDirectionalLightBin());
152         addPointGeometry(vasiLights.back(), obj.get_wgs84_nodes(),
153                          obj.get_normals(), color, obj.get_pts_v()[grp],
154                          obj.get_pts_n()[grp]);
155       } else if (materialName == "RWY_SEQUENCED_LIGHTS") {
156         rabitLights.push_back(SGDirectionalLightBin());
157         addPointGeometry(rabitLights.back(), obj.get_wgs84_nodes(),
158                          obj.get_normals(), color, obj.get_pts_v()[grp],
159                          obj.get_pts_n()[grp]);
160       } else if (materialName == "RWY_ODALS_LIGHTS") {
161         odalLights.push_back(SGLightBin());
162         addPointGeometry(odalLights.back(), obj.get_wgs84_nodes(),
163                          color, obj.get_pts_v()[grp]);
164       } else if (materialName == "RWY_REIL_LIGHTS") {
165         reilLights.push_back(SGDirectionalLightBin());
166         addPointGeometry(reilLights.back(), obj.get_wgs84_nodes(),
167                          obj.get_normals(), color, obj.get_pts_v()[grp],
168                          obj.get_pts_n()[grp]);
169       } else {
170         // what is left must be runway lights
171         addPointGeometry(runwayLights, obj.get_wgs84_nodes(),
172                          obj.get_normals(), color, obj.get_pts_v()[grp],
173                          obj.get_pts_n()[grp]);
174       }
175     }
176
177     return true;
178   }
179
180
181   static SGVec2f
182   getTexCoord(const std::vector<SGVec2f>& texCoords, const int_list& tc,
183               const SGVec2f& tcScale, unsigned i)
184   {
185     if (tc.empty())
186       return tcScale;
187     else if (tc.size() == 1)
188       return mult(texCoords[tc[0]], tcScale);
189     else
190       return mult(texCoords[tc[i]], tcScale);
191   }
192
193   static void
194   addTriangleGeometry(SGTexturedTriangleBin& triangles,
195                       const std::vector<SGVec3d>& vertices,
196                       const std::vector<SGVec3f>& normals,
197                       const std::vector<SGVec2f>& texCoords,
198                       const int_list& tris_v,
199                       const int_list& tris_n,
200                       const int_list& tris_tc,
201                       const SGVec2f& tcScale)
202   {
203     if (tris_v.size() != tris_n.size()) {
204       // If the normal indices do not match, they should be inmplicitly
205       // the same than the vertex indices. So just call ourselves again
206       // with the matching index vector.
207       addTriangleGeometry(triangles, vertices, normals, texCoords,
208                           tris_v, tris_v, tris_tc, tcScale);
209       return;
210     }
211
212     for (unsigned i = 2; i < tris_v.size(); i += 3) {
213       SGVertNormTex v0;
214       v0.vertex = toVec3f(vertices[tris_v[i-2]]);
215       v0.normal = normals[tris_n[i-2]];
216       v0.texCoord = getTexCoord(texCoords, tris_tc, tcScale, i-2);
217       SGVertNormTex v1;
218       v1.vertex = toVec3f(vertices[tris_v[i-1]]);
219       v1.normal = normals[tris_n[i-1]];
220       v1.texCoord = getTexCoord(texCoords, tris_tc, tcScale, i-1);
221       SGVertNormTex v2;
222       v2.vertex = toVec3f(vertices[tris_v[i]]);
223       v2.normal = normals[tris_n[i]];
224       v2.texCoord = getTexCoord(texCoords, tris_tc, tcScale, i);
225       triangles.insert(v0, v1, v2);
226     }
227   }
228
229   static void
230   addStripGeometry(SGTexturedTriangleBin& triangles,
231                    const std::vector<SGVec3d>& vertices,
232                    const std::vector<SGVec3f>& normals,
233                    const std::vector<SGVec2f>& texCoords,
234                    const int_list& strips_v,
235                    const int_list& strips_n,
236                    const int_list& strips_tc,
237                    const SGVec2f& tcScale)
238   {
239     if (strips_v.size() != strips_n.size()) {
240       // If the normal indices do not match, they should be inmplicitly
241       // the same than the vertex indices. So just call ourselves again
242       // with the matching index vector.
243       addStripGeometry(triangles, vertices, normals, texCoords,
244                        strips_v, strips_v, strips_tc, tcScale);
245       return;
246     }
247
248     for (unsigned i = 2; i < strips_v.size(); ++i) {
249       SGVertNormTex v0;
250       v0.vertex = toVec3f(vertices[strips_v[i-2]]);
251       v0.normal = normals[strips_n[i-2]];
252       v0.texCoord = getTexCoord(texCoords, strips_tc, tcScale, i-2);
253       SGVertNormTex v1;
254       v1.vertex = toVec3f(vertices[strips_v[i-1]]);
255       v1.normal = normals[strips_n[i-1]];
256       v1.texCoord = getTexCoord(texCoords, strips_tc, tcScale, i-1);
257       SGVertNormTex v2;
258       v2.vertex = toVec3f(vertices[strips_v[i]]);
259       v2.normal = normals[strips_n[i]];
260       v2.texCoord = getTexCoord(texCoords, strips_tc, tcScale, i);
261       if (i%2)
262         triangles.insert(v1, v0, v2);
263       else
264         triangles.insert(v0, v1, v2);
265     }
266   }
267   
268   static void
269   addFanGeometry(SGTexturedTriangleBin& triangles,
270                  const std::vector<SGVec3d>& vertices,
271                  const std::vector<SGVec3f>& normals,
272                  const std::vector<SGVec2f>& texCoords,
273                  const int_list& fans_v,
274                  const int_list& fans_n,
275                  const int_list& fans_tc,
276                  const SGVec2f& tcScale)
277   {
278     if (fans_v.size() != fans_n.size()) {
279       // If the normal indices do not match, they should be implicitly
280       // the same than the vertex indices. So just call ourselves again
281       // with the matching index vector.
282       addFanGeometry(triangles, vertices, normals, texCoords,
283                      fans_v, fans_v, fans_tc, tcScale);
284       return;
285     }
286
287     SGVertNormTex v0;
288     v0.vertex = toVec3f(vertices[fans_v[0]]);
289     v0.normal = normals[fans_n[0]];
290     v0.texCoord = getTexCoord(texCoords, fans_tc, tcScale, 0);
291     SGVertNormTex v1;
292     v1.vertex = toVec3f(vertices[fans_v[1]]);
293     v1.normal = normals[fans_n[1]];
294     v1.texCoord = getTexCoord(texCoords, fans_tc, tcScale, 1);
295     for (unsigned i = 2; i < fans_v.size(); ++i) {
296       SGVertNormTex v2;
297       v2.vertex = toVec3f(vertices[fans_v[i]]);
298       v2.normal = normals[fans_n[i]];
299       v2.texCoord = getTexCoord(texCoords, fans_tc, tcScale, i);
300       triangles.insert(v0, v1, v2);
301       v1 = v2;
302     }
303   }
304
305   SGVec2f getTexCoordScale(const std::string& name, SGMaterialLib* matlib)
306   {
307     if (!matlib)
308       return SGVec2f(1, 1);
309     SGMaterial* material = matlib->find(name);
310     if (!material)
311       return SGVec2f(1, 1);
312
313     return material->get_tex_coord_scale();
314   }
315
316   bool
317   insertSurfaceGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
318   {
319     if (obj.get_tris_n().size() < obj.get_tris_v().size() ||
320         obj.get_tris_tc().size() < obj.get_tris_v().size()) {
321       SG_LOG(SG_TERRAIN, SG_ALERT,
322              "Group list sizes for triangles do not match!");
323       return false;
324     }
325
326     for (unsigned grp = 0; grp < obj.get_tris_v().size(); ++grp) {
327       std::string materialName = obj.get_tri_materials()[grp];
328       SGVec2f tcScale = getTexCoordScale(materialName, matlib);
329       addTriangleGeometry(materialTriangleMap[materialName],
330                           obj.get_wgs84_nodes(), obj.get_normals(),
331                           obj.get_texcoords(), obj.get_tris_v()[grp],
332                           obj.get_tris_n()[grp], obj.get_tris_tc()[grp],
333                           tcScale);
334     }
335
336     if (obj.get_strips_n().size() < obj.get_strips_v().size() ||
337         obj.get_strips_tc().size() < obj.get_strips_v().size()) {
338       SG_LOG(SG_TERRAIN, SG_ALERT,
339              "Group list sizes for strips do not match!");
340       return false;
341     }
342     for (unsigned grp = 0; grp < obj.get_strips_v().size(); ++grp) {
343       std::string materialName = obj.get_strip_materials()[grp];
344       SGVec2f tcScale = getTexCoordScale(materialName, matlib);
345       addStripGeometry(materialTriangleMap[materialName],
346                        obj.get_wgs84_nodes(), obj.get_normals(),
347                        obj.get_texcoords(), obj.get_strips_v()[grp],
348                        obj.get_strips_n()[grp], obj.get_strips_tc()[grp],
349                        tcScale);
350     }
351
352     if (obj.get_fans_n().size() < obj.get_fans_v().size() ||
353         obj.get_fans_tc().size() < obj.get_fans_v().size()) {
354       SG_LOG(SG_TERRAIN, SG_ALERT,
355              "Group list sizes for fans do not match!");
356       return false;
357     }
358     for (unsigned grp = 0; grp < obj.get_fans_v().size(); ++grp) {
359       std::string materialName = obj.get_fan_materials()[grp];
360       SGVec2f tcScale = getTexCoordScale(materialName, matlib);
361       addFanGeometry(materialTriangleMap[materialName],
362                      obj.get_wgs84_nodes(), obj.get_normals(),
363                      obj.get_texcoords(), obj.get_fans_v()[grp],
364                      obj.get_fans_n()[grp], obj.get_fans_tc()[grp],
365                      tcScale);
366     }
367     return true;
368   }
369
370   osg::Node* getSurfaceGeometry(SGMaterialLib* matlib) const
371   {
372     if (materialTriangleMap.empty())
373       return 0;
374
375     EffectGeode* eg = 0;
376     osg::Group* group = (materialTriangleMap.size() > 1 ? new osg::Group : 0);
377     //osg::Geode* geode = new osg::Geode;
378     SGMaterialTriangleMap::const_iterator i;
379     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
380       osg::Geometry* geometry = i->second.buildGeometry();
381       SGMaterial *mat = 0;
382       if (matlib)
383         mat = matlib->find(i->first);
384       eg = new EffectGeode;
385       if (mat)
386         eg->setEffect(mat->get_effect(i->second));
387       eg->addDrawable(geometry);
388       eg->runGenerators(geometry);  // Generate extra data needed by effect
389       if (group)
390         group->addChild(eg);
391     }
392     if (group)
393         return group;
394     else
395         return eg;
396   }
397
398   void computeRandomSurfaceLights(SGMaterialLib* matlib)
399   {
400     SGMaterialTriangleMap::iterator i;
401         
402     // generate a repeatable random seed
403     mt seed;
404     mt_init(&seed, unsigned(123));
405     
406     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
407       SGMaterial *mat = matlib->find(i->first);
408       if (!mat)
409         continue;
410
411       float coverage = mat->get_light_coverage();
412       if (coverage <= 0)
413         continue;
414       if (coverage < 10000.0) {
415         SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
416                << coverage << ", pushing up to 10000");
417         coverage = 10000;
418       }
419       
420       std::vector<SGVec3f> randomPoints;
421       i->second.addRandomSurfacePoints(coverage, 3, mat->get_object_mask(i->second), randomPoints);
422       std::vector<SGVec3f>::iterator j;
423       for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
424         float zombie = mt_rand(&seed);
425         // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
426         float factor = mt_rand(&seed);
427         factor *= factor;
428
429         float bright = 1;
430         SGVec4f color;
431         if ( zombie > 0.5 ) {
432           // 50% chance of yellowish
433           color = SGVec4f(0.9f, 0.9f, 0.3f, bright - factor * 0.2f);
434         } else if (zombie > 0.15f) {
435           // 35% chance of whitish
436           color = SGVec4f(0.9, 0.9f, 0.8f, bright - factor * 0.2f);
437         } else if (zombie > 0.05f) {
438           // 10% chance of orangish
439           color = SGVec4f(0.9f, 0.6f, 0.2f, bright - factor * 0.2f);
440         } else {
441           // 5% chance of redish
442           color = SGVec4f(0.9f, 0.2f, 0.2f, bright - factor * 0.2f);
443         }
444         randomTileLights.insert(*j, color);
445       }
446     }
447   }
448
449   void computeRandomForest(SGMaterialLib* matlib, float vegetation_density)
450   {
451     SGMaterialTriangleMap::iterator i;
452
453     // generate a repeatable random seed
454     mt seed;
455     mt_init(&seed, unsigned(586));
456
457     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
458       SGMaterial *mat = matlib->find(i->first);
459       if (!mat)
460         continue;
461
462       float wood_coverage = mat->get_wood_coverage();
463       if (wood_coverage <= 0)
464         continue;
465               
466       // Attributes that don't vary by tree but do vary by material
467       bool found = false;
468       TreeBin* bin = NULL;
469       
470       BOOST_FOREACH(bin, randomForest)
471       {
472         if ((bin->texture           == mat->get_tree_texture()  ) &&
473             (bin->texture_varieties == mat->get_tree_varieties()) &&
474             (bin->range             == mat->get_tree_range()    ) &&
475             (bin->width             == mat->get_tree_width()    ) &&
476             (bin->height            == mat->get_tree_height()   )   ) {
477             found = true;
478             break;
479         }
480       }
481       
482       if (!found) {
483         bin = new TreeBin();
484         bin->texture = mat->get_tree_texture();
485           SG_LOG(SG_INPUT, SG_DEBUG, "Tree texture " << bin->texture);
486         bin->range   = mat->get_tree_range();
487         bin->width   = mat->get_tree_width();
488         bin->height  = mat->get_tree_height();
489         bin->texture_varieties = mat->get_tree_varieties();
490         randomForest.push_back(bin);
491       }
492
493       std::vector<SGVec3f> randomPoints;
494       i->second.addRandomTreePoints(wood_coverage,
495                                     mat->get_object_mask(i->second),
496                                     vegetation_density,
497                                     randomPoints);
498       
499       std::vector<SGVec3f>::iterator k;
500       for (k = randomPoints.begin(); k != randomPoints.end(); ++k) {
501         bin->insert(*k);
502       }
503     }
504   }
505
506   void computeRandomObjects(SGMaterialLib* matlib)
507   {
508     SGMaterialTriangleMap::iterator i;
509
510     // generate a repeatable random seed
511     mt seed;
512     mt_init(&seed, unsigned(123));
513
514     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
515       SGMaterial *mat = matlib->find(i->first);
516       if (!mat)
517         continue;
518
519       int group_count = mat->get_object_group_count();
520
521       if (group_count > 0)
522       {
523         for (int j = 0; j < group_count; j++)
524         {
525           SGMatModelGroup *object_group =  mat->get_object_group(j);
526           int nObjects = object_group->get_object_count();
527
528           if (nObjects > 0)
529           {
530             // For each of the random models in the group, determine an appropriate
531             // number of random placements and insert them.
532             for (int k = 0; k < nObjects; k++) {
533               SGMatModel * object = object_group->get_object(k);
534
535               std::vector<std::pair<SGVec3f, float> > randomPoints;
536
537               i->second.addRandomPoints(object->get_coverage_m2(), 
538                                         object->get_spacing_m(),
539                                         mat->get_object_mask(i->second), 
540                                         randomPoints);
541               std::vector<std::pair<SGVec3f, float> >::iterator l;
542               for (l = randomPoints.begin(); l != randomPoints.end(); ++l) {
543                 
544                 // Only add the model if it is sufficiently far from the
545                 // other models
546                 bool close = false;                
547                 
548                 for (unsigned i = 0; i < randomModels.getNumModels(); i++) {
549                   float spacing = randomModels.getMatModel(i).model->get_spacing_m() + object->get_spacing_m();
550                   spacing = spacing * spacing;
551                   
552                   if (distSqr(randomModels.getMatModel(i).position, l->first) < spacing) {
553                     close = true;                
554                   }              
555                 }            
556                 if (!close) { 
557                   randomModels.insert(l->first, object, (int)object->get_randomized_range_m(&seed), l->second);
558                 }
559               }
560             }
561           }
562         }
563       }
564     }
565   }
566   
567
568   bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
569   {
570     if (!insertPtGeometry(obj, matlib))
571       return false;
572     if (!insertSurfaceGeometry(obj, matlib))
573       return false;
574     return true;
575   }
576 };
577
578 typedef std::pair<osg::Node*, int> ModelLOD;
579 struct MakeQuadLeaf {
580     osg::LOD* operator() () const { return new osg::LOD; }
581 };
582 struct AddModelLOD {
583     void operator() (osg::LOD* leaf, ModelLOD& mlod) const
584     {
585         leaf->addChild(mlod.first, 0, mlod.second);
586     }
587 };
588 struct GetModelLODCoord {
589     GetModelLODCoord() {}
590     GetModelLODCoord(const GetModelLODCoord& rhs)
591     {}
592     osg::Vec3 operator() (const ModelLOD& mlod) const
593     {
594         return mlod.first->getBound().center();
595     }
596 };
597
598 typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
599                         GetModelLODCoord>  RandomObjectsQuadtree;
600
601 osg::Node*
602 SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options)
603 {
604   SGBinObject tile;
605   if (!tile.read_bin(path))
606     return NULL;
607
608   SGMaterialLib* matlib = 0;
609   bool use_random_objects = false;
610   bool use_random_vegetation = false;
611   float vegetation_density = 1.0f;
612   if (options) {
613     matlib = options->getMaterialLib();
614     SGPropertyNode* propertyNode = options->getPropertyNode().get();
615     if (propertyNode) {
616         use_random_objects
617             = propertyNode->getBoolValue("/sim/rendering/random-objects",
618                                          use_random_objects);
619         use_random_vegetation
620             = propertyNode->getBoolValue("/sim/rendering/random-vegetation",
621                                          use_random_vegetation);
622         vegetation_density
623             = propertyNode->getFloatValue("/sim/rendering/vegetation-density",
624                                           vegetation_density);
625     }
626   }
627
628   SGVec3d center = tile.get_gbs_center();
629   SGGeod geodPos = SGGeod::fromCart(center);
630   SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
631
632   // rotate the tiles so that the bounding boxes get nearly axis aligned.
633   // this will help the collision tree's bounding boxes a bit ...
634   std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
635   for (unsigned i = 0; i < nodes.size(); ++i)
636     nodes[i] = hlOr.transform(nodes[i]);
637   tile.set_wgs84_nodes(nodes);
638
639   SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]);
640   std::vector<SGVec3f> normals = tile.get_normals();
641   for (unsigned i = 0; i < normals.size(); ++i)
642     normals[i] = hlOrf.transform(normals[i]);
643   tile.set_normals(normals);
644
645   SGTileGeometryBin tileGeometryBin;
646   if (!tileGeometryBin.insertBinObj(tile, matlib))
647     return NULL;
648
649   SGVec3f up(0, 0, 1);
650   GroundLightManager* lightManager = GroundLightManager::instance();
651
652   osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
653   osg::ref_ptr<osg::Group> randomObjects;
654   osg::ref_ptr<osg::Group> forestNode;
655   osg::Group* terrainGroup = new osg::Group;
656
657   osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
658   if (node)
659     terrainGroup->addChild(node);
660
661   if (use_random_objects || use_random_vegetation) {
662     if (use_random_objects) {
663       if (matlib)
664         tileGeometryBin.computeRandomObjects(matlib);
665     
666       if (tileGeometryBin.randomModels.getNumModels() > 0) {
667         // Generate a repeatable random seed
668         mt seed;
669         mt_init(&seed, unsigned(123));
670
671         std::vector<ModelLOD> models;
672         for (unsigned int i = 0;
673              i < tileGeometryBin.randomModels.getNumModels(); i++) {
674           SGMatModelBin::MatModel obj
675             = tileGeometryBin.randomModels.getMatModel(i);          
676
677           SGPropertyNode* root = options->getPropertyNode()->getRootNode();
678           osg::Node* node = obj.model->get_random_model(root, &seed);
679         
680           // Create a matrix to place the object in the correct
681           // location, and then apply the rotation matrix created
682           // above, with an additional random (or taken from
683           // the object mask) heading rotation if appropriate.
684           osg::Matrix transformMat;
685           transformMat = osg::Matrix::translate(toOsg(obj.position));
686           if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
687             // Rotate the object around the z axis.
688             double hdg = mt_rand(&seed) * M_PI * 2;
689             transformMat.preMult(osg::Matrix::rotate(hdg,
690                                                      osg::Vec3d(0.0, 0.0, 1.0)));
691           }
692
693           if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) {
694             // Rotate the object around the z axis.
695             double hdg =  - obj.rotation * M_PI * 2;
696             transformMat.preMult(osg::Matrix::rotate(hdg,
697                                                      osg::Vec3d(0.0, 0.0, 1.0)));
698           }
699           
700           osg::MatrixTransform* position =
701             new osg::MatrixTransform(transformMat);
702           position->addChild(node);
703           models.push_back(ModelLOD(position, obj.lod));
704         }
705         RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
706         quadtree.buildQuadTree(models.begin(), models.end());
707         randomObjects = quadtree.getRoot();
708         randomObjects->setName("random objects");
709       }
710     }
711
712     if (use_random_vegetation && matlib) {
713       // Now add some random forest.
714       tileGeometryBin.computeRandomForest(matlib, vegetation_density);
715       
716       if (tileGeometryBin.randomForest.size() > 0) {
717         forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity(),
718                                   options);
719         forestNode->setName("Random trees");
720       }
721     } 
722   }
723
724   // FIXME: ugly, has a side effect
725   if (matlib)
726     tileGeometryBin.computeRandomSurfaceLights(matlib);
727
728   if (tileGeometryBin.tileLights.getNumLights() > 0
729       || tileGeometryBin.randomTileLights.getNumLights() > 0) {
730     osg::Group* groundLights0 = new osg::Group;
731     groundLights0->setStateSet(lightManager->getGroundLightStateSet());
732     groundLights0->setNodeMask(GROUNDLIGHTS0_BIT);
733     osg::Geode* geode = new osg::Geode;
734     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.tileLights));
735     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 4, -0.3f));
736     groundLights0->addChild(geode);
737     lightGroup->addChild(groundLights0);
738   }
739   if (tileGeometryBin.randomTileLights.getNumLights() > 0) {
740     osg::Group* groundLights1 = new osg::Group;
741     groundLights1->setStateSet(lightManager->getGroundLightStateSet());
742     groundLights1->setNodeMask(GROUNDLIGHTS1_BIT);
743     osg::Group* groundLights2 = new osg::Group;
744     groundLights2->setStateSet(lightManager->getGroundLightStateSet());
745     groundLights2->setNodeMask(GROUNDLIGHTS2_BIT);
746     osg::Geode* geode = new osg::Geode;
747     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 2, -0.15f));
748     groundLights1->addChild(geode);
749     lightGroup->addChild(groundLights1);
750     geode = new osg::Geode;
751     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights));
752     groundLights2->addChild(geode);
753     lightGroup->addChild(groundLights2);
754   }
755
756   if (!tileGeometryBin.vasiLights.empty()) {
757     EffectGeode* vasiGeode = new EffectGeode;
758     Effect* vasiEffect
759         = getLightEffect(6, osg::Vec3(1, 0.0001, 0.000001), 1, 6, true);
760     vasiGeode->setEffect(vasiEffect);
761     SGVec4f red(1, 0, 0, 1);
762     SGMaterial* mat = 0;
763     if (matlib)
764       mat = matlib->find("RWY_RED_LIGHTS");
765     if (mat)
766       red = mat->get_light_color();
767     SGVec4f white(1, 1, 1, 1);
768     mat = 0;
769     if (matlib)
770       mat = matlib->find("RWY_WHITE_LIGHTS");
771     if (mat)
772       white = mat->get_light_color();
773     SGDirectionalLightListBin::const_iterator i;
774     for (i = tileGeometryBin.vasiLights.begin();
775          i != tileGeometryBin.vasiLights.end(); ++i) {
776       vasiGeode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
777     }
778     vasiGeode->setStateSet(lightManager->getRunwayLightStateSet());
779     lightGroup->addChild(vasiGeode);
780   }
781   Effect* runwayEffect = 0;
782   if (tileGeometryBin.runwayLights.getNumLights() > 0
783       || !tileGeometryBin.rabitLights.empty()
784       || !tileGeometryBin.reilLights.empty()
785       || !tileGeometryBin.odalLights.empty()
786       || tileGeometryBin.taxiLights.getNumLights() > 0)
787       runwayEffect = getLightEffect(4, osg::Vec3(1, 0.001, 0.0002), 1, 4, true);
788   if (tileGeometryBin.runwayLights.getNumLights() > 0
789       || !tileGeometryBin.rabitLights.empty()
790       || !tileGeometryBin.reilLights.empty()
791       || !tileGeometryBin.odalLights.empty()) {
792     osg::Group* rwyLights = new osg::Group;
793     rwyLights->setStateSet(lightManager->getRunwayLightStateSet());
794     rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
795     if (tileGeometryBin.runwayLights.getNumLights() != 0) {
796       EffectGeode* geode = new EffectGeode;
797       geode->setEffect(runwayEffect);
798       geode->addDrawable(SGLightFactory::getLights(tileGeometryBin
799                                                    .runwayLights));
800       rwyLights->addChild(geode);
801     }
802     SGDirectionalLightListBin::const_iterator i;
803     for (i = tileGeometryBin.rabitLights.begin();
804          i != tileGeometryBin.rabitLights.end(); ++i) {
805       rwyLights->addChild(SGLightFactory::getSequenced(*i));
806     }
807     for (i = tileGeometryBin.reilLights.begin();
808          i != tileGeometryBin.reilLights.end(); ++i) {
809       rwyLights->addChild(SGLightFactory::getSequenced(*i));
810     }
811     SGLightListBin::const_iterator j;
812     for (j = tileGeometryBin.odalLights.begin();
813          j != tileGeometryBin.odalLights.end(); ++j) {
814       rwyLights->addChild(SGLightFactory::getOdal(*j));
815     }
816     lightGroup->addChild(rwyLights);
817   }
818
819   if (tileGeometryBin.taxiLights.getNumLights() > 0) {
820     osg::Group* taxiLights = new osg::Group;
821     taxiLights->setStateSet(lightManager->getTaxiLightStateSet());
822     taxiLights->setNodeMask(RUNWAYLIGHTS_BIT);
823     EffectGeode* geode = new EffectGeode;
824     geode->setEffect(runwayEffect);
825     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.taxiLights));
826     taxiLights->addChild(geode);
827     lightGroup->addChild(taxiLights);
828   }
829
830   // The toplevel transform for that tile.
831   osg::MatrixTransform* transform = new osg::MatrixTransform;
832   transform->setName(path);
833   transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
834                        osg::Matrix::translate(toOsg(center)));
835   transform->addChild(terrainGroup);
836   if (lightGroup->getNumChildren() > 0) {
837     osg::LOD* lightLOD = new osg::LOD;
838     lightLOD->addChild(lightGroup.get(), 0, 30000);
839     // VASI is always on, so doesn't use light bits.
840     lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT); 
841     transform->addChild(lightLOD);
842   }
843   
844   if (randomObjects.valid() || forestNode.valid()) {
845   
846     // Add a LoD node, so we don't try to display anything when the tile center
847     // is more than 20km away.
848     osg::LOD* objectLOD = new osg::LOD;
849     
850     if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
851     if (forestNode.valid())  objectLOD->addChild(forestNode.get(), 0, 20000);
852     
853     unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
854     objectLOD->setNodeMask(nodeMask);
855     transform->addChild(objectLOD);
856   }
857   
858   return transform;
859 }