]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/obj.cxx
No round function for MSVC
[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/math/SGMisc.hxx>
52 #include <simgear/scene/material/Effect.hxx>
53 #include <simgear/scene/material/EffectGeode.hxx>
54 #include <simgear/scene/material/mat.hxx>
55 #include <simgear/scene/material/matlib.hxx>
56 #include <simgear/scene/model/SGOffsetTransform.hxx>
57 #include <simgear/scene/util/SGUpdateVisitor.hxx>
58 #include <simgear/scene/util/SGNodeMasks.hxx>
59 #include <simgear/scene/util/QuadTreeBuilder.hxx>
60
61 #include "SGTexturedTriangleBin.hxx"
62 #include "SGLightBin.hxx"
63 #include "SGModelBin.hxx"
64 #include "SGBuildingBin.hxx"
65 #include "TreeBin.hxx"
66 #include "SGDirectionalLightBin.hxx"
67 #include "GroundLightManager.hxx"
68
69
70 #include "pt_lights.hxx"
71
72 using namespace simgear;
73
74 typedef std::map<std::string,SGTexturedTriangleBin> SGMaterialTriangleMap;
75 typedef std::list<SGLightBin> SGLightListBin;
76 typedef std::list<SGDirectionalLightBin> SGDirectionalLightListBin;
77
78 struct SGTileGeometryBin {
79   SGMaterialTriangleMap materialTriangleMap;
80   SGLightBin tileLights;
81   SGLightBin randomTileLights;
82   SGTreeBinList randomForest;
83   SGDirectionalLightBin runwayLights;
84   SGDirectionalLightBin taxiLights;
85   SGDirectionalLightListBin vasiLights;
86   SGDirectionalLightListBin rabitLights;
87   SGLightListBin odalLights;
88   SGDirectionalLightListBin reilLights;
89   SGMatModelBin randomModels;
90   SGBuildingBinList randomBuildings;
91
92   static SGVec4f
93   getMaterialLightColor(const SGMaterial* material)
94   {
95     if (!material)
96       return SGVec4f(1, 1, 1, 0.8);
97     return material->get_light_color();
98   }
99
100   static void
101   addPointGeometry(SGLightBin& lights,
102                    const std::vector<SGVec3d>& vertices,
103                    const SGVec4f& color,
104                    const int_list& pts_v)
105   {
106     for (unsigned i = 0; i < pts_v.size(); ++i)
107       lights.insert(toVec3f(vertices[pts_v[i]]), color);
108   }
109
110   static void
111   addPointGeometry(SGDirectionalLightBin& lights,
112                    const std::vector<SGVec3d>& vertices,
113                    const std::vector<SGVec3f>& normals,
114                    const SGVec4f& color,
115                    const int_list& pts_v,
116                    const int_list& pts_n)
117   {
118     // If the normal indices match the vertex indices, use seperate
119     // normal indices. Else reuse the vertex indices for the normals.
120     if (pts_v.size() == pts_n.size()) {
121       for (unsigned i = 0; i < pts_v.size(); ++i)
122         lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_n[i]], color);
123     } else {
124       for (unsigned i = 0; i < pts_v.size(); ++i)
125         lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_v[i]], color);
126     }
127   }
128
129   bool
130   insertPtGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
131   {
132     if (obj.get_pts_v().size() != obj.get_pts_n().size()) {
133       SG_LOG(SG_TERRAIN, SG_ALERT,
134              "Group list sizes for points do not match!");
135       return false;
136     }
137
138     for (unsigned grp = 0; grp < obj.get_pts_v().size(); ++grp) {
139       std::string materialName = obj.get_pt_materials()[grp];
140       SGMaterial* material = 0;
141       if (matlib)
142           material = matlib->find(materialName);
143       SGVec4f color = getMaterialLightColor(material);
144
145       if (3 <= materialName.size() && materialName.substr(0, 3) != "RWY") {
146         // Just plain lights. Not something for the runway.
147         addPointGeometry(tileLights, obj.get_wgs84_nodes(), color,
148                          obj.get_pts_v()[grp]);
149       } else if (materialName == "RWY_BLUE_TAXIWAY_LIGHTS"
150                  || materialName == "RWY_GREEN_TAXIWAY_LIGHTS") {
151         addPointGeometry(taxiLights, obj.get_wgs84_nodes(), obj.get_normals(),
152                          color, obj.get_pts_v()[grp], obj.get_pts_n()[grp]);
153       } else if (materialName == "RWY_VASI_LIGHTS") {
154         vasiLights.push_back(SGDirectionalLightBin());
155         addPointGeometry(vasiLights.back(), obj.get_wgs84_nodes(),
156                          obj.get_normals(), color, obj.get_pts_v()[grp],
157                          obj.get_pts_n()[grp]);
158       } else if (materialName == "RWY_SEQUENCED_LIGHTS") {
159         rabitLights.push_back(SGDirectionalLightBin());
160         addPointGeometry(rabitLights.back(), obj.get_wgs84_nodes(),
161                          obj.get_normals(), color, obj.get_pts_v()[grp],
162                          obj.get_pts_n()[grp]);
163       } else if (materialName == "RWY_ODALS_LIGHTS") {
164         odalLights.push_back(SGLightBin());
165         addPointGeometry(odalLights.back(), obj.get_wgs84_nodes(),
166                          color, obj.get_pts_v()[grp]);
167       } else if (materialName == "RWY_REIL_LIGHTS") {
168         reilLights.push_back(SGDirectionalLightBin());
169         addPointGeometry(reilLights.back(), obj.get_wgs84_nodes(),
170                          obj.get_normals(), color, obj.get_pts_v()[grp],
171                          obj.get_pts_n()[grp]);
172       } else {
173         // what is left must be runway lights
174         addPointGeometry(runwayLights, obj.get_wgs84_nodes(),
175                          obj.get_normals(), color, obj.get_pts_v()[grp],
176                          obj.get_pts_n()[grp]);
177       }
178     }
179
180     return true;
181   }
182
183
184   static SGVec2f
185   getTexCoord(const std::vector<SGVec2f>& texCoords, const int_list& tc,
186               const SGVec2f& tcScale, unsigned i)
187   {
188     if (tc.empty())
189       return tcScale;
190     else if (tc.size() == 1)
191       return mult(texCoords[tc[0]], tcScale);
192     else
193       return mult(texCoords[tc[i]], tcScale);
194   }
195
196   static void
197   addTriangleGeometry(SGTexturedTriangleBin& triangles,
198                       const std::vector<SGVec3d>& vertices,
199                       const std::vector<SGVec3f>& normals,
200                       const std::vector<SGVec2f>& texCoords,
201                       const int_list& tris_v,
202                       const int_list& tris_n,
203                       const int_list& tris_tc,
204                       const SGVec2f& tcScale)
205   {
206     if (tris_v.size() != tris_n.size()) {
207       // If the normal indices do not match, they should be inmplicitly
208       // the same than the vertex indices. So just call ourselves again
209       // with the matching index vector.
210       addTriangleGeometry(triangles, vertices, normals, texCoords,
211                           tris_v, tris_v, tris_tc, tcScale);
212       return;
213     }
214
215     for (unsigned i = 2; i < tris_v.size(); i += 3) {
216       SGVertNormTex v0;
217       v0.vertex = toVec3f(vertices[tris_v[i-2]]);
218       v0.normal = normals[tris_n[i-2]];
219       v0.texCoord = getTexCoord(texCoords, tris_tc, tcScale, i-2);
220       SGVertNormTex v1;
221       v1.vertex = toVec3f(vertices[tris_v[i-1]]);
222       v1.normal = normals[tris_n[i-1]];
223       v1.texCoord = getTexCoord(texCoords, tris_tc, tcScale, i-1);
224       SGVertNormTex v2;
225       v2.vertex = toVec3f(vertices[tris_v[i]]);
226       v2.normal = normals[tris_n[i]];
227       v2.texCoord = getTexCoord(texCoords, tris_tc, tcScale, i);
228       triangles.insert(v0, v1, v2);
229     }
230   }
231
232   static void
233   addStripGeometry(SGTexturedTriangleBin& triangles,
234                    const std::vector<SGVec3d>& vertices,
235                    const std::vector<SGVec3f>& normals,
236                    const std::vector<SGVec2f>& texCoords,
237                    const int_list& strips_v,
238                    const int_list& strips_n,
239                    const int_list& strips_tc,
240                    const SGVec2f& tcScale)
241   {
242     if (strips_v.size() != strips_n.size()) {
243       // If the normal indices do not match, they should be inmplicitly
244       // the same than the vertex indices. So just call ourselves again
245       // with the matching index vector.
246       addStripGeometry(triangles, vertices, normals, texCoords,
247                        strips_v, strips_v, strips_tc, tcScale);
248       return;
249     }
250
251     for (unsigned i = 2; i < strips_v.size(); ++i) {
252       SGVertNormTex v0;
253       v0.vertex = toVec3f(vertices[strips_v[i-2]]);
254       v0.normal = normals[strips_n[i-2]];
255       v0.texCoord = getTexCoord(texCoords, strips_tc, tcScale, i-2);
256       SGVertNormTex v1;
257       v1.vertex = toVec3f(vertices[strips_v[i-1]]);
258       v1.normal = normals[strips_n[i-1]];
259       v1.texCoord = getTexCoord(texCoords, strips_tc, tcScale, i-1);
260       SGVertNormTex v2;
261       v2.vertex = toVec3f(vertices[strips_v[i]]);
262       v2.normal = normals[strips_n[i]];
263       v2.texCoord = getTexCoord(texCoords, strips_tc, tcScale, i);
264       if (i%2)
265         triangles.insert(v1, v0, v2);
266       else
267         triangles.insert(v0, v1, v2);
268     }
269   }
270   
271   static void
272   addFanGeometry(SGTexturedTriangleBin& triangles,
273                  const std::vector<SGVec3d>& vertices,
274                  const std::vector<SGVec3f>& normals,
275                  const std::vector<SGVec2f>& texCoords,
276                  const int_list& fans_v,
277                  const int_list& fans_n,
278                  const int_list& fans_tc,
279                  const SGVec2f& tcScale)
280   {
281     if (fans_v.size() != fans_n.size()) {
282       // If the normal indices do not match, they should be implicitly
283       // the same than the vertex indices. So just call ourselves again
284       // with the matching index vector.
285       addFanGeometry(triangles, vertices, normals, texCoords,
286                      fans_v, fans_v, fans_tc, tcScale);
287       return;
288     }
289
290     SGVertNormTex v0;
291     v0.vertex = toVec3f(vertices[fans_v[0]]);
292     v0.normal = normals[fans_n[0]];
293     v0.texCoord = getTexCoord(texCoords, fans_tc, tcScale, 0);
294     SGVertNormTex v1;
295     v1.vertex = toVec3f(vertices[fans_v[1]]);
296     v1.normal = normals[fans_n[1]];
297     v1.texCoord = getTexCoord(texCoords, fans_tc, tcScale, 1);
298     for (unsigned i = 2; i < fans_v.size(); ++i) {
299       SGVertNormTex v2;
300       v2.vertex = toVec3f(vertices[fans_v[i]]);
301       v2.normal = normals[fans_n[i]];
302       v2.texCoord = getTexCoord(texCoords, fans_tc, tcScale, i);
303       triangles.insert(v0, v1, v2);
304       v1 = v2;
305     }
306   }
307
308   SGVec2f getTexCoordScale(const std::string& name, SGMaterialLib* matlib)
309   {
310     if (!matlib)
311       return SGVec2f(1, 1);
312     SGMaterial* material = matlib->find(name);
313     if (!material)
314       return SGVec2f(1, 1);
315
316     return material->get_tex_coord_scale();
317   }
318
319   bool
320   insertSurfaceGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
321   {
322     if (obj.get_tris_n().size() < obj.get_tris_v().size() ||
323         obj.get_tris_tc().size() < obj.get_tris_v().size()) {
324       SG_LOG(SG_TERRAIN, SG_ALERT,
325              "Group list sizes for triangles do not match!");
326       return false;
327     }
328
329     for (unsigned grp = 0; grp < obj.get_tris_v().size(); ++grp) {
330       std::string materialName = obj.get_tri_materials()[grp];
331       SGVec2f tcScale = getTexCoordScale(materialName, matlib);
332       addTriangleGeometry(materialTriangleMap[materialName],
333                           obj.get_wgs84_nodes(), obj.get_normals(),
334                           obj.get_texcoords(), obj.get_tris_v()[grp],
335                           obj.get_tris_n()[grp], obj.get_tris_tc()[grp],
336                           tcScale);
337     }
338
339     if (obj.get_strips_n().size() < obj.get_strips_v().size() ||
340         obj.get_strips_tc().size() < obj.get_strips_v().size()) {
341       SG_LOG(SG_TERRAIN, SG_ALERT,
342              "Group list sizes for strips do not match!");
343       return false;
344     }
345     for (unsigned grp = 0; grp < obj.get_strips_v().size(); ++grp) {
346       std::string materialName = obj.get_strip_materials()[grp];
347       SGVec2f tcScale = getTexCoordScale(materialName, matlib);
348       addStripGeometry(materialTriangleMap[materialName],
349                        obj.get_wgs84_nodes(), obj.get_normals(),
350                        obj.get_texcoords(), obj.get_strips_v()[grp],
351                        obj.get_strips_n()[grp], obj.get_strips_tc()[grp],
352                        tcScale);
353     }
354
355     if (obj.get_fans_n().size() < obj.get_fans_v().size() ||
356         obj.get_fans_tc().size() < obj.get_fans_v().size()) {
357       SG_LOG(SG_TERRAIN, SG_ALERT,
358              "Group list sizes for fans do not match!");
359       return false;
360     }
361     for (unsigned grp = 0; grp < obj.get_fans_v().size(); ++grp) {
362       std::string materialName = obj.get_fan_materials()[grp];
363       SGVec2f tcScale = getTexCoordScale(materialName, matlib);
364       addFanGeometry(materialTriangleMap[materialName],
365                      obj.get_wgs84_nodes(), obj.get_normals(),
366                      obj.get_texcoords(), obj.get_fans_v()[grp],
367                      obj.get_fans_n()[grp], obj.get_fans_tc()[grp],
368                      tcScale);
369     }
370     return true;
371   }
372
373   osg::Node* getSurfaceGeometry(SGMaterialLib* matlib) const
374   {
375     if (materialTriangleMap.empty())
376       return 0;
377
378     EffectGeode* eg = 0;
379     osg::Group* group = (materialTriangleMap.size() > 1 ? new osg::Group : 0);
380     //osg::Geode* geode = new osg::Geode;
381     SGMaterialTriangleMap::const_iterator i;
382     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
383       osg::Geometry* geometry = i->second.buildGeometry();
384       SGMaterial *mat = 0;
385       if (matlib)
386         mat = matlib->find(i->first);
387       eg = new EffectGeode;
388       if (mat)
389         eg->setEffect(mat->get_effect(i->second));
390       eg->addDrawable(geometry);
391       eg->runGenerators(geometry);  // Generate extra data needed by effect
392       if (group)
393         group->addChild(eg);
394     }
395     if (group)
396         return group;
397     else
398         return eg;
399   }
400
401   void computeRandomSurfaceLights(SGMaterialLib* matlib)
402   {
403     SGMaterialTriangleMap::iterator i;
404         
405     // generate a repeatable random seed
406     mt seed;
407     mt_init(&seed, unsigned(123));
408     
409     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
410       SGMaterial *mat = matlib->find(i->first);
411       if (!mat)
412         continue;
413
414       float coverage = mat->get_light_coverage();
415       if (coverage <= 0)
416         continue;
417       if (coverage < 10000.0) {
418         SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
419                << coverage << ", pushing up to 10000");
420         coverage = 10000;
421       }
422       
423       std::vector<SGVec3f> randomPoints;
424       i->second.addRandomSurfacePoints(coverage, 3, mat->get_object_mask(i->second), randomPoints);
425       std::vector<SGVec3f>::iterator j;
426       for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
427         float zombie = mt_rand(&seed);
428         // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
429         float factor = mt_rand(&seed);
430         factor *= factor;
431
432         float bright = 1;
433         SGVec4f color;
434         if ( zombie > 0.5 ) {
435           // 50% chance of yellowish
436           color = SGVec4f(0.9f, 0.9f, 0.3f, bright - factor * 0.2f);
437         } else if (zombie > 0.15f) {
438           // 35% chance of whitish
439           color = SGVec4f(0.9, 0.9f, 0.8f, bright - factor * 0.2f);
440         } else if (zombie > 0.05f) {
441           // 10% chance of orangish
442           color = SGVec4f(0.9f, 0.6f, 0.2f, bright - factor * 0.2f);
443         } else {
444           // 5% chance of redish
445           color = SGVec4f(0.9f, 0.2f, 0.2f, bright - factor * 0.2f);
446         }
447         randomTileLights.insert(*j, color);
448       }
449     }
450   }
451   
452   void computeRandomBuildings(SGMaterialLib* matlib, float building_density)
453   {
454     SGMaterialTriangleMap::iterator i;
455         
456     // generate a repeatable random seed
457     mt seed;
458     mt_init(&seed, unsigned(123));
459     
460     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
461       SGMaterial *mat = matlib->find(i->first);
462       SGTexturedTriangleBin triangleBin = i->second;
463       
464       if (!mat)
465         continue;
466
467       osg::Texture2D* object_mask = mat->get_object_mask(triangleBin);
468
469       float coverage = mat->get_building_coverage();
470       
471       // Minimum spacing needs to include the maximum footprint of a building.
472       // As the 0,0,0 point is the center of the front of the building, we need
473       // to consider the full depth, but only half the possible width.          
474       float min_spacing = mat->get_building_spacing();
475
476       if (coverage <= 0)
477         continue;        
478         
479       bool found = false;
480       SGBuildingBin* bin = NULL;
481       
482       BOOST_FOREACH(bin, randomBuildings)
483       {
484         if (bin->texture == mat->get_building_texture()) {
485             found = true;
486             break;
487         }
488       }
489       
490       if (!found) {
491         bin = new SGBuildingBin();
492         bin->texture = mat->get_building_texture();
493         SG_LOG(SG_INPUT, SG_DEBUG, "Building texture " << bin->texture);
494         randomBuildings.push_back(bin);
495       }       
496       
497       std::vector<std::pair<SGVec3f, float> > randomPoints;
498       
499       unsigned num = i->second.getNumTriangles();
500       int triangle_dropped = 0;
501       int building_dropped = 0;
502       int random_dropped = 0;
503
504       for (unsigned i = 0; i < num; ++i) {
505         SGBuildingBin::BuildingList triangle_buildings;
506         SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i);
507         
508         SGVec3f v0 = triangleBin.getVertex(triangleRef[0]).vertex;
509         SGVec3f v1 = triangleBin.getVertex(triangleRef[1]).vertex;
510         SGVec3f v2 = triangleBin.getVertex(triangleRef[2]).vertex;
511         SGVec2f t0 = triangleBin.getVertex(triangleRef[0]).texCoord;
512         SGVec2f t1 = triangleBin.getVertex(triangleRef[1]).texCoord;
513         SGVec2f t2 = triangleBin.getVertex(triangleRef[2]).texCoord;
514         SGVec3f normal = cross(v1 - v0, v2 - v0);
515         
516         // Compute the area
517         float area = 0.5f*length(normal);
518         if (area <= SGLimitsf::min())
519           continue;
520
521         // for partial units of area, use a zombie door method to
522         // create the proper random chance of an object being created
523         // for this triangle.
524         double num = area / coverage + mt_rand(&seed);
525         
526         // Apply density.
527         num = num * building_density;
528
529         // place an object each unit of area
530         while ( num > 1.0 ) {
531           float a = mt_rand(&seed);
532           float b = mt_rand(&seed);
533           if ( a + b > 1 ) {
534             a = 1 - a;
535             b = 1 - b;
536           }
537           
538           float c = 1 - a - b;
539           SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
540           float rotation = mt_rand(&seed);
541           
542           if (object_mask != NULL) {
543             SGVec2f texCoord = a*t0 + b*t1 + c*t2;
544             osg::Image* img = object_mask->getImage();            
545             unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
546             unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
547             
548             if (mt_rand(&seed) < img->getColor(x, y).b()) {  
549               // Object passes mask. Rotation is taken from the red channel
550               rotation = img->getColor(x,y).r();
551             } else {
552               // Fails mask test - try again.
553               num -= 1.0;
554               continue;
555             }  
556           }
557           
558           // Now create the building, so we have an idea of its footprint
559           // and therefore appropriate spacing.
560           SGBuildingBin::BuildingType buildingtype;
561           float width;
562           float depth;
563           int floors;
564           float height;
565           bool pitched;
566                                   
567           // Determine the building type, and hence dimensions.
568           float type = mt_rand(&seed);
569           
570           if (type < mat->get_building_small_fraction()) {
571             // Small building
572             buildingtype = SGBuildingBin::SMALL;
573             width = mat->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_width() - mat->get_building_small_min_width());
574             depth = mat->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_depth() - mat->get_building_small_min_depth());
575             floors = SGMisc::round(mat->get_building_small_min_floors() + mt_rand(&seed) * (mat->get_building_small_max_floors() - mat->get_building_small_min_floors()));
576             height = floors * (2.8 + mt_rand(&seed));
577             
578             if (depth > width) { depth = width; }
579             
580             pitched = (mt_rand(&seed) < mat->get_building_small_pitch());
581           } else if (type < (mat->get_building_small_fraction() + mat->get_building_medium_fraction())) {
582             buildingtype = SGBuildingBin::MEDIUM;
583             width = mat->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_width() - mat->get_building_medium_min_width());
584             depth = mat->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_depth() - mat->get_building_medium_min_depth());
585             floors = SGMisc::round(mat->get_building_medium_min_floors() + mt_rand(&seed) * (mat->get_building_medium_max_floors() - mat->get_building_medium_min_floors()));
586             height = floors * (2.8 + mt_rand(&seed));
587             pitched = (mt_rand(&seed) < mat->get_building_medium_pitch());         
588           } else {
589             buildingtype = SGBuildingBin::LARGE;
590             width = mat->get_building_large_min_width() + mt_rand(&seed) * (mat->get_building_large_max_width() - mat->get_building_large_min_width());
591             depth = mat->get_building_large_min_depth() + mt_rand(&seed) * (mat->get_building_large_max_depth() - mat->get_building_large_min_depth());
592             floors = SGMisc::round(mat->get_building_large_min_floors() + mt_rand(&seed) * (mat->get_building_large_max_floors() - mat->get_building_large_min_floors())); 
593             height = floors * (2.8 + mt_rand(&seed));
594             pitched = (mt_rand(&seed) < mat->get_building_large_pitch());                   
595           }
596           
597           // Determine an appropriate minimum spacing for the object.  Note that the
598           // origin of the building model is the center of the front face, hence we
599           // consider the full depth.  We choose _not_ to use the diagonal distance
600           // to one of the rear corners, as we assume that terrain masking will
601           // make the buildings place in some sort of grid.
602           float radius = std::max(depth, 0.5f*width);
603
604           // Check that the point is sufficiently far from
605           // the edge of the triangle by measuring the distance
606           // from the three lines that make up the triangle.        
607           if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) < radius) ||
608               ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) < radius) ||
609               ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) < radius)   )
610           {
611             triangle_dropped++;
612             num -= 1.0;
613             continue;
614           }
615
616           // Check against the generic random objects.  TODO - make this more efficient by
617           // masking ahead of time objects outside of the triangle.
618           bool too_close = false;
619           for (unsigned int i = 0; i < randomModels.getNumModels(); ++i) {
620             float min_dist = randomModels.getMatModel(i).model->get_spacing_m() + radius + min_spacing;
621             min_dist = min_dist * min_dist;
622             
623             if (distSqr(randomModels.getMatModel(i).position, randomPoint) < min_dist) {
624               too_close = true;
625               random_dropped++;
626               continue;
627             }          
628           }
629           
630           if (too_close) {
631             // Too close to a random model - drop and try again
632             num -= 1.0;
633             continue;            
634           }
635           
636           SGBuildingBin::BuildingList::iterator l;       
637           
638           // Check that the building is sufficiently far from any other building within the triangle.
639           for (l = triangle_buildings.begin(); l != triangle_buildings.end(); ++l) {
640             
641             float min_dist = l->radius + radius + min_spacing;
642             min_dist = min_dist * min_dist;
643             
644             if (distSqr(randomPoint, l->position) < min_dist) {
645               building_dropped++;
646               too_close = true;
647               continue;
648             }
649           }
650           
651           if (too_close) {
652             // Too close to another building - drop and try again
653             num -= 1.0;
654             continue;            
655           }
656           
657           // If we've passed all of the above tests we have a valid
658           // building, so create it!          
659           SGBuildingBin::Building building = 
660                     SGBuildingBin::Building(buildingtype, 
661                                             randomPoint, 
662                                             width, 
663                                             depth, 
664                                             height, 
665                                             floors,
666                                             rotation,
667                                             pitched);                                                            
668           triangle_buildings.push_back(building);
669           num -= 1.0;
670         }
671         
672         // Add the buildings from this triangle to the overall list.
673         SGBuildingBin::BuildingList::iterator l;  
674
675         for (l = triangle_buildings.begin(); l != triangle_buildings.end(); ++l) {
676           bin->insert(*l);
677         }
678       }
679
680       SG_LOG(SG_INPUT, SG_DEBUG, "Random Buildings: " << bin->getNumBuildings());
681       SG_LOG(SG_INPUT, SG_DEBUG, "  Dropped due to triangle edge: " << triangle_dropped);
682       SG_LOG(SG_INPUT, SG_DEBUG, "  Dropped due to random object: " << random_dropped);
683       SG_LOG(SG_INPUT, SG_DEBUG, "  Dropped due to other building: " << building_dropped);
684     }
685   }
686   
687   void computeRandomForest(SGMaterialLib* matlib, float vegetation_density)
688   {
689     SGMaterialTriangleMap::iterator i;
690
691     // generate a repeatable random seed
692     mt seed;
693     mt_init(&seed, unsigned(586));
694
695     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
696       SGMaterial *mat = matlib->find(i->first);
697       if (!mat)
698         continue;
699
700       float wood_coverage = mat->get_wood_coverage();
701       if (wood_coverage <= 0)
702         continue;
703               
704       // Attributes that don't vary by tree but do vary by material
705       bool found = false;
706       TreeBin* bin = NULL;
707       
708       BOOST_FOREACH(bin, randomForest)
709       {
710         if ((bin->texture           == mat->get_tree_texture()  ) &&
711             (bin->texture_varieties == mat->get_tree_varieties()) &&
712             (bin->range             == mat->get_tree_range()    ) &&
713             (bin->width             == mat->get_tree_width()    ) &&
714             (bin->height            == mat->get_tree_height()   )   ) {
715             found = true;
716             break;
717         }
718       }
719       
720       if (!found) {
721         bin = new TreeBin();
722         bin->texture = mat->get_tree_texture();
723           SG_LOG(SG_INPUT, SG_DEBUG, "Tree texture " << bin->texture);
724         bin->range   = mat->get_tree_range();
725         bin->width   = mat->get_tree_width();
726         bin->height  = mat->get_tree_height();
727         bin->texture_varieties = mat->get_tree_varieties();
728         randomForest.push_back(bin);
729       }
730
731       std::vector<SGVec3f> randomPoints;
732       i->second.addRandomTreePoints(wood_coverage,
733                                     mat->get_object_mask(i->second),
734                                     vegetation_density,
735                                     randomPoints);
736       
737       std::vector<SGVec3f>::iterator k;
738       for (k = randomPoints.begin(); k != randomPoints.end(); ++k) {
739         bin->insert(*k);
740       }
741     }
742   }
743
744   void computeRandomObjects(SGMaterialLib* matlib)
745   {
746     SGMaterialTriangleMap::iterator i;
747
748     // generate a repeatable random seed
749     mt seed;
750     mt_init(&seed, unsigned(123));
751
752     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
753       SGMaterial *mat = matlib->find(i->first);
754       if (!mat)
755         continue;
756
757       int group_count = mat->get_object_group_count();
758
759       if (group_count > 0)
760       {
761         for (int j = 0; j < group_count; j++)
762         {
763           SGMatModelGroup *object_group =  mat->get_object_group(j);
764           int nObjects = object_group->get_object_count();
765
766           if (nObjects > 0)
767           {
768             // For each of the random models in the group, determine an appropriate
769             // number of random placements and insert them.
770             for (int k = 0; k < nObjects; k++) {
771               SGMatModel * object = object_group->get_object(k);
772
773               std::vector<std::pair<SGVec3f, float> > randomPoints;
774
775               i->second.addRandomPoints(object->get_coverage_m2(), 
776                                         object->get_spacing_m(),
777                                         mat->get_object_mask(i->second), 
778                                         randomPoints);
779                                         
780               std::vector<std::pair<SGVec3f, float> >::iterator l;
781               for (l = randomPoints.begin(); l != randomPoints.end(); ++l) {
782                 // Only add the model if it is sufficiently far from the
783                 // other models
784                 bool close = false;                
785                 
786                 for (unsigned i = 0; i < randomModels.getNumModels(); i++) {
787                   float spacing = randomModels.getMatModel(i).model->get_spacing_m() + object->get_spacing_m();
788                   spacing = spacing * spacing;
789                   
790                   if (distSqr(randomModels.getMatModel(i).position, l->first) < spacing) {
791                     close = true;                
792                     continue;
793                   }              
794                 }            
795                 
796                 if (!close) { 
797                   randomModels.insert(l->first, object, (int)object->get_randomized_range_m(&seed), l->second);
798                 }
799               }
800             }
801           }
802         }
803       }
804     }
805   }
806
807   bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
808   {
809     if (!insertPtGeometry(obj, matlib))
810       return false;
811     if (!insertSurfaceGeometry(obj, matlib))
812       return false;
813     return true;
814   }
815 };
816
817 typedef std::pair<osg::Node*, int> ModelLOD;
818 struct MakeQuadLeaf {
819     osg::LOD* operator() () const { return new osg::LOD; }
820 };
821 struct AddModelLOD {
822     void operator() (osg::LOD* leaf, ModelLOD& mlod) const
823     {
824         leaf->addChild(mlod.first, 0, mlod.second);
825     }
826 };
827 struct GetModelLODCoord {
828     GetModelLODCoord() {}
829     GetModelLODCoord(const GetModelLODCoord& rhs)
830     {}
831     osg::Vec3 operator() (const ModelLOD& mlod) const
832     {
833         return mlod.first->getBound().center();
834     }
835 };
836
837 typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
838                         GetModelLODCoord>  RandomObjectsQuadtree;
839
840 osg::Node*
841 SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options)
842 {
843   SGBinObject tile;
844   if (!tile.read_bin(path))
845     return NULL;
846
847   SGMaterialLib* matlib = 0;
848   bool use_random_objects = false;
849   bool use_random_vegetation = false;
850   bool use_random_buildings = false;
851   float vegetation_density = 1.0f;  
852   float building_density = 1.0f;
853   if (options) {
854     matlib = options->getMaterialLib();
855     SGPropertyNode* propertyNode = options->getPropertyNode().get();
856     if (propertyNode) {
857         use_random_objects
858             = propertyNode->getBoolValue("/sim/rendering/random-objects",
859                                          use_random_objects);
860         use_random_vegetation
861             = propertyNode->getBoolValue("/sim/rendering/random-vegetation",
862                                          use_random_vegetation);        
863         vegetation_density
864             = propertyNode->getFloatValue("/sim/rendering/vegetation-density",
865                                           vegetation_density);
866         use_random_buildings
867             = propertyNode->getBoolValue("/sim/rendering/random-buildings",
868                                          use_random_buildings);        
869         building_density
870             = propertyNode->getFloatValue("/sim/rendering/building-density",
871                                           building_density);
872     }
873   }
874
875   SGVec3d center = tile.get_gbs_center();
876   SGGeod geodPos = SGGeod::fromCart(center);
877   SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
878
879   // rotate the tiles so that the bounding boxes get nearly axis aligned.
880   // this will help the collision tree's bounding boxes a bit ...
881   std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
882   for (unsigned i = 0; i < nodes.size(); ++i)
883     nodes[i] = hlOr.transform(nodes[i]);
884   tile.set_wgs84_nodes(nodes);
885
886   SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]);
887   std::vector<SGVec3f> normals = tile.get_normals();
888   for (unsigned i = 0; i < normals.size(); ++i)
889     normals[i] = hlOrf.transform(normals[i]);
890   tile.set_normals(normals);
891
892   SGTileGeometryBin tileGeometryBin;
893   if (!tileGeometryBin.insertBinObj(tile, matlib))
894     return NULL;
895
896   SGVec3f up(0, 0, 1);
897   GroundLightManager* lightManager = GroundLightManager::instance();
898
899   osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
900   osg::ref_ptr<osg::Group> randomObjects;
901   osg::ref_ptr<osg::Group> forestNode;
902   osg::ref_ptr<osg::Group> buildingNode;  
903   osg::Group* terrainGroup = new osg::Group;
904
905   osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
906   if (node)
907     terrainGroup->addChild(node);
908
909   if (use_random_objects && matlib) {
910     tileGeometryBin.computeRandomObjects(matlib);
911
912     if (tileGeometryBin.randomModels.getNumModels() > 0) {
913       // Generate a repeatable random seed
914       mt seed;
915       mt_init(&seed, unsigned(123));
916
917       std::vector<ModelLOD> models;
918       for (unsigned int i = 0;
919            i < tileGeometryBin.randomModels.getNumModels(); i++) {
920         SGMatModelBin::MatModel obj
921           = tileGeometryBin.randomModels.getMatModel(i);          
922
923         SGPropertyNode* root = options->getPropertyNode()->getRootNode();
924         osg::Node* node = obj.model->get_random_model(root, &seed);
925       
926         // Create a matrix to place the object in the correct
927         // location, and then apply the rotation matrix created
928         // above, with an additional random (or taken from
929         // the object mask) heading rotation if appropriate.
930         osg::Matrix transformMat;
931         transformMat = osg::Matrix::translate(toOsg(obj.position));
932         if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
933           // Rotate the object around the z axis.
934           double hdg = mt_rand(&seed) * M_PI * 2;
935           transformMat.preMult(osg::Matrix::rotate(hdg,
936                                                    osg::Vec3d(0.0, 0.0, 1.0)));
937         }
938
939         if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) {
940           // Rotate the object around the z axis.
941           double hdg =  - obj.rotation * M_PI * 2;
942           transformMat.preMult(osg::Matrix::rotate(hdg,
943                                                    osg::Vec3d(0.0, 0.0, 1.0)));
944         }
945         
946         osg::MatrixTransform* position =
947           new osg::MatrixTransform(transformMat);
948         position->addChild(node);
949         models.push_back(ModelLOD(position, obj.lod));
950       }
951       RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
952       quadtree.buildQuadTree(models.begin(), models.end());
953       randomObjects = quadtree.getRoot();
954       randomObjects->setName("random objects");
955     }
956   }
957
958   if (use_random_vegetation && matlib) {
959     // Now add some random forest.
960     tileGeometryBin.computeRandomForest(matlib, vegetation_density);
961     
962     if (tileGeometryBin.randomForest.size() > 0) {
963       forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity(),
964                                 options);
965       forestNode->setName("Random trees");
966     }
967   } 
968
969   if (use_random_buildings && matlib) {
970     tileGeometryBin.computeRandomBuildings(matlib, building_density);
971     if (tileGeometryBin.randomBuildings.size() > 0) {
972       buildingNode = createRandomBuildings(tileGeometryBin.randomBuildings, osg::Matrix::identity(),
973                                   options);                                
974       buildingNode->setName("Random buildings");
975     }
976   }  
977
978   // FIXME: ugly, has a side effect
979   if (matlib)
980     tileGeometryBin.computeRandomSurfaceLights(matlib);
981
982   if (tileGeometryBin.tileLights.getNumLights() > 0
983       || tileGeometryBin.randomTileLights.getNumLights() > 0) {
984     osg::Group* groundLights0 = new osg::Group;
985     groundLights0->setStateSet(lightManager->getGroundLightStateSet());
986     groundLights0->setNodeMask(GROUNDLIGHTS0_BIT);
987     osg::Geode* geode = new osg::Geode;
988     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.tileLights));
989     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 4, -0.3f));
990     groundLights0->addChild(geode);
991     lightGroup->addChild(groundLights0);
992   }
993   
994   if (tileGeometryBin.randomTileLights.getNumLights() > 0) {
995     osg::Group* groundLights1 = new osg::Group;
996     groundLights1->setStateSet(lightManager->getGroundLightStateSet());
997     groundLights1->setNodeMask(GROUNDLIGHTS1_BIT);
998     osg::Group* groundLights2 = new osg::Group;
999     groundLights2->setStateSet(lightManager->getGroundLightStateSet());
1000     groundLights2->setNodeMask(GROUNDLIGHTS2_BIT);
1001     osg::Geode* geode = new osg::Geode;
1002     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 2, -0.15f));
1003     groundLights1->addChild(geode);
1004     lightGroup->addChild(groundLights1);
1005     geode = new osg::Geode;
1006     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights));
1007     groundLights2->addChild(geode);
1008     lightGroup->addChild(groundLights2);
1009   }
1010
1011   if (!tileGeometryBin.vasiLights.empty()) {
1012     EffectGeode* vasiGeode = new EffectGeode;
1013     Effect* vasiEffect
1014         = getLightEffect(24, osg::Vec3(1, 0.0001, 0.000001), 1, 24, true);
1015     vasiGeode->setEffect(vasiEffect);
1016     SGVec4f red(1, 0, 0, 1);
1017     SGMaterial* mat = 0;
1018     if (matlib)
1019       mat = matlib->find("RWY_RED_LIGHTS");
1020     if (mat)
1021       red = mat->get_light_color();
1022     SGVec4f white(1, 1, 1, 1);
1023     mat = 0;
1024     if (matlib)
1025       mat = matlib->find("RWY_WHITE_LIGHTS");
1026     if (mat)
1027       white = mat->get_light_color();
1028     SGDirectionalLightListBin::const_iterator i;
1029     for (i = tileGeometryBin.vasiLights.begin();
1030          i != tileGeometryBin.vasiLights.end(); ++i) {
1031       vasiGeode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
1032     }
1033     vasiGeode->setStateSet(lightManager->getRunwayLightStateSet());
1034     lightGroup->addChild(vasiGeode);
1035   }
1036   
1037   Effect* runwayEffect = 0;
1038   if (tileGeometryBin.runwayLights.getNumLights() > 0
1039       || !tileGeometryBin.rabitLights.empty()
1040       || !tileGeometryBin.reilLights.empty()
1041       || !tileGeometryBin.odalLights.empty()
1042       || tileGeometryBin.taxiLights.getNumLights() > 0)
1043       runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true);
1044   if (tileGeometryBin.runwayLights.getNumLights() > 0
1045       || !tileGeometryBin.rabitLights.empty()
1046       || !tileGeometryBin.reilLights.empty()
1047       || !tileGeometryBin.odalLights.empty()) {
1048     osg::Group* rwyLights = new osg::Group;
1049     rwyLights->setStateSet(lightManager->getRunwayLightStateSet());
1050     rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
1051     if (tileGeometryBin.runwayLights.getNumLights() != 0) {
1052       EffectGeode* geode = new EffectGeode;
1053       geode->setEffect(runwayEffect);
1054       geode->addDrawable(SGLightFactory::getLights(tileGeometryBin
1055                                                    .runwayLights));
1056       rwyLights->addChild(geode);
1057     }
1058     SGDirectionalLightListBin::const_iterator i;
1059     for (i = tileGeometryBin.rabitLights.begin();
1060          i != tileGeometryBin.rabitLights.end(); ++i) {
1061       rwyLights->addChild(SGLightFactory::getSequenced(*i));
1062     }
1063     for (i = tileGeometryBin.reilLights.begin();
1064          i != tileGeometryBin.reilLights.end(); ++i) {
1065       rwyLights->addChild(SGLightFactory::getSequenced(*i));
1066     }
1067     SGLightListBin::const_iterator j;
1068     for (j = tileGeometryBin.odalLights.begin();
1069          j != tileGeometryBin.odalLights.end(); ++j) {
1070       rwyLights->addChild(SGLightFactory::getOdal(*j));
1071     }
1072     lightGroup->addChild(rwyLights);
1073   }
1074
1075   if (tileGeometryBin.taxiLights.getNumLights() > 0) {
1076     osg::Group* taxiLights = new osg::Group;
1077     taxiLights->setStateSet(lightManager->getTaxiLightStateSet());
1078     taxiLights->setNodeMask(RUNWAYLIGHTS_BIT);
1079     EffectGeode* geode = new EffectGeode;
1080     geode->setEffect(runwayEffect);
1081     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.taxiLights));
1082     taxiLights->addChild(geode);
1083     lightGroup->addChild(taxiLights);
1084   }
1085
1086   // The toplevel transform for that tile.
1087   osg::MatrixTransform* transform = new osg::MatrixTransform;
1088   transform->setName(path);
1089   transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
1090                        osg::Matrix::translate(toOsg(center)));
1091   transform->addChild(terrainGroup);
1092   if (lightGroup->getNumChildren() > 0) {
1093     osg::LOD* lightLOD = new osg::LOD;
1094     lightLOD->addChild(lightGroup.get(), 0, 30000);
1095     // VASI is always on, so doesn't use light bits.
1096     lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT); 
1097     transform->addChild(lightLOD);
1098   }
1099   
1100   if (randomObjects.valid() || forestNode.valid() || buildingNode.valid()) {
1101   
1102     // Add a LoD node, so we don't try to display anything when the tile center
1103     // is more than 20km away.
1104     osg::LOD* objectLOD = new osg::LOD;
1105     
1106     if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
1107     if (forestNode.valid())  objectLOD->addChild(forestNode.get(), 0, 20000);
1108     if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000);
1109     
1110     unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
1111     objectLOD->setNodeMask(nodeMask);
1112     transform->addChild(objectLOD);
1113   }
1114   
1115   return transform;
1116 }