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