]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/SGTileDetailsCallback.hxx
Fix #1783: repeated error message on console
[simgear.git] / simgear / scene / tgdb / SGTileDetailsCallback.hxx
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 <osg/LOD>
30 #include <osgUtil/Simplifier>
31
32 #include <boost/foreach.hpp>
33
34 #include <simgear/scene/material/matmodel.hxx>
35 #include <simgear/scene/model/SGOffsetTransform.hxx>
36 #include <simgear/scene/util/QuadTreeBuilder.hxx>
37 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
38 #include <simgear/scene/util/OptionsReadFileCallback.hxx>
39 #include <simgear/scene/util/SGNodeMasks.hxx>
40
41 #include "SGNodeTriangles.hxx"
42 #include "GroundLightManager.hxx"
43 #include "SGLightBin.hxx"
44 #include "SGDirectionalLightBin.hxx"
45 #include "SGModelBin.hxx"
46 #include "SGBuildingBin.hxx"
47 #include "TreeBin.hxx"
48
49 #include "pt_lights.hxx"
50
51
52 typedef std::list<SGLightBin> SGLightListBin;
53 typedef std::list<SGDirectionalLightBin> SGDirectionalLightListBin;
54
55 #define SG_SIMPLIFIER_RATIO         (0.001)
56 #define SG_SIMPLIFIER_MAX_LENGTH    (1000.0)
57 #define SG_SIMPLIFIER_MAX_ERROR     (2000.0)
58 #define SG_OBJECT_RANGE             (9000.0)
59 #define SG_TILE_RADIUS              (14000.0)
60 #define SG_TILE_MIN_EXPIRY          (180.0)
61
62 using namespace simgear;
63
64 // QuadTreeBuilder is used by Random Objects Generator
65 typedef std::pair<osg::Node*, int> ModelLOD;
66 struct MakeQuadLeaf {
67     osg::LOD* operator() () const { return new osg::LOD; }
68 };
69 struct AddModelLOD {
70     void operator() (osg::LOD* leaf, ModelLOD& mlod) const
71     {
72         leaf->addChild(mlod.first, 0, mlod.second);
73     }
74 };
75 struct GetModelLODCoord {
76     GetModelLODCoord() {}
77     GetModelLODCoord(const GetModelLODCoord& rhs)
78     {}
79     osg::Vec3 operator() (const ModelLOD& mlod) const
80     {
81         return mlod.first->getBound().center();
82     }
83 };
84 typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
85                         GetModelLODCoord>  RandomObjectsQuadtree;
86
87
88 // needs constructor
89 static unsigned int num_tdcb = 0;
90 class SGTileDetailsCallback : public OptionsReadFileCallback {
91 public:
92     SGTileDetailsCallback() 
93     {
94         num_tdcb++;
95     }
96
97     virtual ~SGTileDetailsCallback() 
98     {
99         num_tdcb--;
100         SG_LOG( SG_GENERAL, SG_INFO, "SGTileDetailsCallback::~SGTileDetailsCallback() num cbs left " << num_tdcb  );
101     }
102     
103     virtual osgDB::ReaderWriter::ReadResult readNode(
104         const std::string&, const osgDB::Options*)
105     {
106         SGMaterialLibPtr matlib;
107         osg::ref_ptr<SGMaterialCache> matcache; 
108         
109         osg::ref_ptr<osg::Group> group = new osg::Group;
110         group->setDataVariance(osg::Object::STATIC);
111
112         // generate textured triangle list
113         std::vector<SGTriangleInfo> matTris;
114         GetNodeTriangles nodeTris(_gbs_center, &matTris);
115         _rootNode->accept( nodeTris );
116
117         // build matcache
118         matlib = _options->getMaterialLib();
119         if (matlib) {
120             SGGeod geodPos = SGGeod::fromCart(_gbs_center);            
121             matcache = matlib->generateMatCache(geodPos);
122         }
123         
124 #if 0
125         // TEST : See if we can regenerate landclass shapes from node
126         for ( unsigned int i=0; i<matTris.size(); i++ ) {
127             matTris[i].dumpBorder(_gbs_center);
128         }
129 #endif
130
131         osg::Node* node = loadTerrain();
132         if (node) {
133             group->addChild(node);
134         }
135
136         osg::LOD* lightLOD = generateLightingTileObjects(matTris, matcache);
137         if (lightLOD) {
138             group->addChild(lightLOD);
139         }
140
141         osg::LOD* objectLOD = generateRandomTileObjects(matTris, matcache);
142         if (objectLOD) {
143             group->addChild(objectLOD);
144         }
145         
146         return group.release();
147     }
148
149     static SGVec4f getMaterialLightColor(const SGMaterial* material)
150     {
151         if (!material) {
152             return SGVec4f(1, 1, 1, 0.8);
153         }
154         
155         return material->get_light_color();
156     }
157     
158     static void
159     addPointGeometry(SGLightBin& lights,
160                      const std::vector<SGVec3d>& vertices,
161                      const SGVec4f& color,
162                      const int_list& pts_v)
163     {
164         for (unsigned i = 0; i < pts_v.size(); ++i)
165             lights.insert(toVec3f(vertices[pts_v[i]]), color);
166     }
167     
168     static void
169     addPointGeometry(SGDirectionalLightBin& lights,
170                      const std::vector<SGVec3d>& vertices,
171                      const std::vector<SGVec3f>& normals,
172                      const SGVec4f& color,
173                      const int_list& pts_v,
174                      const int_list& pts_n)
175     {
176         // If the normal indices match the vertex indices, use seperate
177         // normal indices. Else reuse the vertex indices for the normals.
178         if (pts_v.size() == pts_n.size()) {
179             for (unsigned i = 0; i < pts_v.size(); ++i)
180                 lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_n[i]], color);
181         } else {
182             for (unsigned i = 0; i < pts_v.size(); ++i)
183                 lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_v[i]], color);
184         }
185     }
186     
187     bool insertPtGeometry(const SGBinObject& obj, SGMaterialCache* matcache)
188     {
189         if (obj.get_pts_v().size() != obj.get_pts_n().size()) {
190             SG_LOG(SG_TERRAIN, SG_ALERT,
191                    "Group list sizes for points do not match!");
192             return false;
193         }
194         
195         for (unsigned grp = 0; grp < obj.get_pts_v().size(); ++grp) {
196             std::string materialName = obj.get_pt_materials()[grp];
197             SGMaterial* material = matcache->find(materialName);
198             SGVec4f color = getMaterialLightColor(material);
199             
200             if (3 <= materialName.size() && materialName.substr(0, 3) != "RWY") {
201                 // Just plain lights. Not something for the runway.
202                 addPointGeometry(tileLights, obj.get_wgs84_nodes(), color,
203                                  obj.get_pts_v()[grp]);
204             } else if (materialName == "RWY_BLUE_TAXIWAY_LIGHTS"
205                 || materialName == "RWY_GREEN_TAXIWAY_LIGHTS") {
206                 addPointGeometry(taxiLights, obj.get_wgs84_nodes(), obj.get_normals(),
207                                  color, obj.get_pts_v()[grp], obj.get_pts_n()[grp]);
208                 } else if (materialName == "RWY_VASI_LIGHTS") {
209                     vasiLights.push_back(SGDirectionalLightBin());
210                     addPointGeometry(vasiLights.back(), obj.get_wgs84_nodes(),
211                                      obj.get_normals(), color, obj.get_pts_v()[grp],
212                                      obj.get_pts_n()[grp]);
213                 } else if (materialName == "RWY_SEQUENCED_LIGHTS") {
214                     rabitLights.push_back(SGDirectionalLightBin());
215                     addPointGeometry(rabitLights.back(), obj.get_wgs84_nodes(),
216                                      obj.get_normals(), color, obj.get_pts_v()[grp],
217                                      obj.get_pts_n()[grp]);
218                 } else if (materialName == "RWY_ODALS_LIGHTS") {
219                     odalLights.push_back(SGLightBin());
220                     addPointGeometry(odalLights.back(), obj.get_wgs84_nodes(),
221                                      color, obj.get_pts_v()[grp]);
222                 } else if (materialName == "RWY_YELLOW_PULSE_LIGHTS") {
223                     holdshortLights.push_back(SGDirectionalLightBin());
224                     addPointGeometry(holdshortLights.back(), obj.get_wgs84_nodes(),
225                                      obj.get_normals(), color, obj.get_pts_v()[grp],
226                                      obj.get_pts_n()[grp]);
227                 } else if (materialName == "RWY_GUARD_LIGHTS") {
228                     guardLights.push_back(SGDirectionalLightBin());
229                     addPointGeometry(guardLights.back(), obj.get_wgs84_nodes(),
230                                      obj.get_normals(), color, obj.get_pts_v()[grp],
231                                      obj.get_pts_n()[grp]);
232                 } else if (materialName == "RWY_REIL_LIGHTS") {
233                     reilLights.push_back(SGDirectionalLightBin());
234                     addPointGeometry(reilLights.back(), obj.get_wgs84_nodes(),
235                                      obj.get_normals(), color, obj.get_pts_v()[grp],
236                                      obj.get_pts_n()[grp]);
237                 } else {
238                     // what is left must be runway lights
239                     addPointGeometry(runwayLights, obj.get_wgs84_nodes(),
240                                      obj.get_normals(), color, obj.get_pts_v()[grp],
241                                      obj.get_pts_n()[grp]);
242                 }
243         }
244         
245         return true;
246     }
247     
248     
249     
250     // Load terrain if required
251     // todo - this is the same code as when we load a btg from the .STG - can we combine?
252     osg::Node* loadTerrain()
253     {
254       if (! _loadterrain)
255         return NULL;
256
257       SGBinObject tile;
258       if (!tile.read_bin(_path))
259         return NULL;
260
261       SGMaterialLibPtr matlib;
262       SGMaterialCache* matcache = 0;
263       bool useVBOs = false;
264       bool simplifyNear    = false;
265       double ratio       = SG_SIMPLIFIER_RATIO;
266       double maxLength   = SG_SIMPLIFIER_MAX_LENGTH;
267       double maxError    = SG_SIMPLIFIER_MAX_ERROR;
268
269       if (_options) {
270         matlib = _options->getMaterialLib();
271         useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON");
272         SGPropertyNode* propertyNode = _options->getPropertyNode().get();
273         simplifyNear = propertyNode->getBoolValue("/sim/rendering/terrain/simplifier/enabled-near", simplifyNear);
274         ratio = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/ratio", ratio);
275         maxLength = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-length", maxLength);
276         maxError = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-error", maxError);
277       }
278
279       // PSADRO TODO : we can do this in terragear 
280       // - why not add a bitmask of flags to the btg so we can precompute this?
281       // and only do it if it hasn't been done already
282       SGVec3d center = tile.get_gbs_center();
283       SGGeod geodPos = SGGeod::fromCart(center);
284       SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
285
286       // Generate a materials cache
287       if (matlib) {
288           matcache = matlib->generateMatCache(geodPos);
289       }
290       
291       // rotate the tiles so that the bounding boxes get nearly axis aligned.
292       // this will help the collision tree's bounding boxes a bit ...
293       std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
294       for (unsigned i = 0; i < nodes.size(); ++i) {
295         nodes[i] = hlOr.transform(nodes[i]);
296       }
297       tile.set_wgs84_nodes(nodes);
298
299       SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]);
300       std::vector<SGVec3f> normals = tile.get_normals();
301       for (unsigned i = 0; i < normals.size(); ++i) {
302         normals[i] = hlOrf.transform(normals[i]);
303       }
304       tile.set_normals(normals);
305
306       osg::ref_ptr<SGTileGeometryBin> tileGeometryBin = new SGTileGeometryBin;
307
308       if (!tileGeometryBin->insertSurfaceGeometry(tile, matcache)) {
309         return NULL;
310       }
311       
312       osg::Node* node = tileGeometryBin->getSurfaceGeometry(matcache, useVBOs);
313       if (node && simplifyNear) {
314         osgUtil::Simplifier simplifier(ratio, maxError, maxLength);
315         node->accept(simplifier);
316       }
317
318       return node;
319     }
320
321     float min_dist_to_seg_squared( const SGVec3f p, const SGVec3d& a, const SGVec3d& b )
322     {
323         const float l2 = distSqr(a, b);
324         SGVec3d pd = toVec3d( p );
325         if (l2 == 0.0) {
326             return distSqr(pd, a); // if a == b, just return distance to A
327         }
328         
329         // Consider the line extending the segment, parameterized as a + t (b - a).
330         // We find projection of pt onto the line. 
331         // It falls where t = [(p-a) . (b-a)] / |b-a|^2
332         const float t = dot(pd-a, b-a) / l2;
333         
334         if (t < 0.0) {
335             return distSqr(pd, a);
336         } else if (t > 1.0) {
337             return distSqr(pd, b);
338         } else {
339             const SGVec3d proj = a + t * (b-a);
340             return distSqr(pd, proj);
341         }
342     }
343     
344     float min_dist_from_borders( SGVec3f p, const std::vector<SGBorderContour>& bsegs )
345     {
346         // calc min dist to each line 
347         // calc distance squared to keep this as fast as we can
348         // first, we must be able to project the point onto the segment
349         std::vector<float> distances;
350         for ( unsigned int b=0; b<bsegs.size(); b++ )
351         {
352             distances.push_back( min_dist_to_seg_squared( p, bsegs[b].start, bsegs[b].end ) );
353         }
354         
355         float min_dist_sq = *std::min_element( distances.begin(), distances.end() );        
356         return sqrt( min_dist_sq );
357     }
358     
359     // let's break random objects from randomBuildings
360     void computeRandomObjectsAndBuildings(
361         std::vector<SGTriangleInfo>& matTris, 
362         float building_density,
363         bool use_random_objects,
364         bool use_random_buildings,
365         bool useVBOs,
366         SGMatModelBin&     randomModels,
367         SGBuildingBinList& randomBuildings )
368     {
369         unsigned int m;
370         
371         // Only compute the random objects if we haven't already done so
372         if (_tileRandomObjectsComputed) {
373             return;
374         }
375         _tileRandomObjectsComputed = true;
376         
377         // generate a repeatable random seed
378         mt seed;
379         mt_init(&seed, unsigned(123));
380         
381         for ( m=0; m<matTris.size(); m++ ) {
382             SGMaterial *mat = matTris[m].getMaterial();
383             if (!mat)
384                 continue;
385                         
386             osg::Texture2D* object_mask  = mat->get_one_object_mask(matTris[m].getTextureIndex());
387             
388             int   group_count            = mat->get_object_group_count();
389             float building_coverage      = mat->get_building_coverage();
390             float cos_zero_density_angle = mat->get_cos_object_zero_density_slope_angle();
391             float cos_max_density_angle  = mat->get_cos_object_max_density_slope_angle();
392             
393             if (building_coverage == 0)
394                 continue;
395             
396             SGBuildingBin* bin = NULL;
397             
398             if (building_coverage > 0) {
399                 bin = new SGBuildingBin(mat, useVBOs);                
400                 randomBuildings.push_back(bin);
401             }
402             
403             unsigned num = matTris[m].getNumTriangles();
404             int random_dropped = 0;
405             int mask_dropped = 0;
406             int building_dropped = 0;
407             int triangle_dropped = 0;
408             
409             // get the polygon border segments
410 //            std::vector<SGBorderContour> borderSegs;
411 //            matTris[m].getBorderContours( borderSegs );
412             
413             for (unsigned i = 0; i < num; ++i) {
414                 std::vector<SGVec3f> triVerts;
415                 std::vector<SGVec2f> triTCs;
416                 matTris[m].getTriangle(i, triVerts, triTCs);
417                 
418                 SGVec3f vorigin = triVerts[0];
419                 SGVec3f v0 = triVerts[1] - vorigin;
420                 SGVec3f v1 = triVerts[2] - vorigin;
421                 SGVec2f torigin = triTCs[0];
422                 SGVec2f t0 = triTCs[1] - torigin;
423                 SGVec2f t1 = triTCs[2] - torigin;
424                 SGVec3f normal = cross(v0, v1);
425                 
426                 // Ensure the slope isn't too steep by checking the
427                 // cos of the angle between the slope normal and the
428                 // vertical (conveniently the z-component of the normalized
429                 // normal) and values passed in.
430                 float cos = normalize(normal).z();
431                 float slope_density = 1.0;
432                 if (cos < cos_zero_density_angle) continue; // Too steep for any objects
433                 if (cos < cos_max_density_angle) {
434                     slope_density =
435                     (cos - cos_zero_density_angle) /
436                     (cos_max_density_angle - cos_zero_density_angle);
437                 }
438                 
439                 // Containers to hold the random buildings and objects generated
440                 // for this triangle for collision detection purposes.
441                 std::vector< std::pair< SGVec3f, float> > triangleObjectsList;
442                 std::vector< std::pair< SGVec3f, float> > triangleBuildingList;
443                 
444                 // Compute the area : todo - we only want to stop if the area of the POLY
445                 // is too small
446                 // so we need to know area of each poly....
447                 float area = 0.5f*length(normal);
448                 if (area <= SGLimitsf::min())
449                     continue;
450                 
451                 // Generate any random objects
452                 if (use_random_objects && (group_count > 0))
453                 {
454                     for (int j = 0; j < group_count; j++)
455                     {
456                         SGMatModelGroup *object_group =  mat->get_object_group(j);
457                         int nObjects = object_group->get_object_count();
458                         
459                         if (nObjects == 0) continue;
460                         
461                         // For each of the random models in the group, determine an appropriate
462                         // number of random placements and insert them.
463                         for (int k = 0; k < nObjects; k++) {
464                             SGMatModel * object = object_group->get_object(k);
465                             
466                             // Determine the number of objecst to place, taking into account
467                             // the slope density factor.
468                             double n = slope_density * area / object->get_coverage_m2();
469                             
470                             // Use the zombie door method to determine fractional object placement.
471                             n = n + mt_rand(&seed);
472                             
473                             // place an object each unit of area
474                             while ( n > 1.0 ) {
475                                 n -= 1.0;
476                                 
477                                 float a = mt_rand(&seed);
478                                 float b = mt_rand(&seed);
479                                 if ( a + b > 1 ) {
480                                     a = 1 - a;
481                                     b = 1 - b;
482                                 }
483                                 
484                                 SGVec3f randomPoint = vorigin + a*v0 + b*v1;
485                                 float rotation = static_cast<float>(mt_rand(&seed));
486                                 
487                                 // Check that the point is sufficiently far from
488                                 // the edge of the triangle by measuring the distance
489                                 // from the three lines that make up the triangle.
490                                 float spacing = object->get_spacing_m();
491                                 
492                                 SGVec3f p = randomPoint - vorigin;
493 #if 1
494                                 float edges[] = { 
495                                     length(cross(p     , p - v0)) / length(v0),
496                                     length(cross(p - v0, p - v1)) / length(v1 - v0),
497                                     length(cross(p - v1, p     )) / length(v1)      };
498                                     float edge_dist = *std::min_element(edges, edges + 3);
499 #else
500                                     float edge_dist = min_dist_from_borders( randomPoint, borderSegs );
501 #endif
502                                     if (edge_dist < spacing) {
503                                         continue;
504                                     }
505                                     
506                                     if (object_mask != NULL) {
507                                         SGVec2f texCoord = torigin + a*t0 + b*t1;
508                                         
509                                         // Check this random point against the object mask
510                                         // blue (for buildings) channel.
511                                         osg::Image* img = object_mask->getImage();
512                                         unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
513                                         unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
514                                         
515                                         if (mt_rand(&seed) > img->getColor(x, y).b()) {
516                                             // Failed object mask check
517                                             continue;
518                                         }
519                                         
520                                         rotation = img->getColor(x,y).r();
521                                     }
522                                     
523                                     bool close = false;
524                                     
525                                     // Check it isn't too close to any other random objects in the triangle
526                                     std::vector<std::pair<SGVec3f, float> >::iterator l;
527                                     for (l = triangleObjectsList.begin(); l != triangleObjectsList.end(); ++l) {
528                                         float min_dist2 = (l->second + object->get_spacing_m()) *
529                                         (l->second + object->get_spacing_m());
530                                         
531                                         if (distSqr(l->first, randomPoint) < min_dist2) {
532                                             close = true;
533                                             continue;
534                                         }
535                                     }
536                                     
537                                     if (!close) {
538                                         triangleObjectsList.push_back(std::make_pair(randomPoint, object->get_spacing_m()));
539                                         randomModels.insert(randomPoint,
540                                                             object,
541                                                             (int)object->get_randomized_range_m(&seed),
542                                                             rotation);
543                                     }
544                             }
545                         }
546                     }
547                 }
548                 
549                 // Random objects now generated.  Now generate the random buildings (if any);
550                 if (use_random_buildings && (building_coverage > 0) && (building_density > 0)) {
551                     
552                     // Calculate the number of buildings, taking into account building density (which is linear)
553                     // and the slope density factor.
554                     double num = building_density * building_density * slope_density * area / building_coverage;
555                     
556                     // For partial units of area, use a zombie door method to
557                     // create the proper random chance of an object being created
558                     // for this triangle.
559                     num = num + mt_rand(&seed);
560                     
561                     if (num < 1.0f) {
562                         continue;
563                     }
564                     
565                     // Cosine of the angle between the two vectors.
566                     float cosine = (dot(v0, v1) / (length(v0) * length(v1)));
567                     
568                     // Determine a grid spacing in each vector such that the correct
569                     // coverage will result.
570                     float stepv0 = (sqrtf(building_coverage) / building_density) / length(v0) / sqrtf(1 - cosine * cosine);
571                     float stepv1 = (sqrtf(building_coverage) / building_density) / length(v1);
572                     
573                     stepv0 = std::min(stepv0, 1.0f);
574                     stepv1 = std::min(stepv1, 1.0f);
575                     
576                     // Start at a random point. a will be immediately incremented below.
577                     float a = -mt_rand(&seed) * stepv0;
578                     float b = mt_rand(&seed) * stepv1;
579                     
580                     // Place an object each unit of area
581                     while (num > 1.0) {
582                         num -= 1.0;
583                         
584                         // Set the next location to place a building
585                         a += stepv0;
586                         
587                         if ((a + b) > 1.0f) {
588                             // Reached the end of the scan-line on v0. Reset and increment
589                             // scan-line on v1
590                             a = mt_rand(&seed) * stepv0;
591                             b += stepv1;
592                         }
593                         
594                         if (b > 1.0f) {
595                             // In a degenerate case of a single point, we might be outside the
596                             // scanline.  Note that we need to still ensure that a+b < 1.
597                             b = mt_rand(&seed) * stepv1 * (1.0f - a);
598                         }
599                         
600                         if ((a + b) > 1.0f ) {
601                             // Truly degenerate case - simply choose a random point guaranteed
602                             // to fulfil the constraing of a+b < 1.
603                             a = mt_rand(&seed);
604                             b = mt_rand(&seed) * (1.0f - a);
605                         }
606                         
607                         SGVec3f randomPoint = vorigin + a*v0 + b*v1;
608                         float rotation = mt_rand(&seed);
609                         
610                         if (object_mask != NULL) {
611                             SGVec2f texCoord = torigin + a*t0 + b*t1;
612                             osg::Image* img = object_mask->getImage();
613                             int x = (int) (img->s() * texCoord.x()) % img->s();
614                             int y = (int) (img->t() * texCoord.y()) % img->t();
615                             
616                             // In some degenerate cases x or y can be < 1, in which case the mod operand fails
617                             while (x < 0) x += img->s();
618                             while (y < 0) y += img->t();
619                             
620                             if (mt_rand(&seed) < img->getColor(x, y).b()) {
621                                 // Object passes mask. Rotation is taken from the red channel
622                                 rotation = img->getColor(x,y).r();
623                             } else {
624                                 // Fails mask test - try again.
625                                 mask_dropped++;
626                                 continue;
627                             }
628                         }
629                         
630                         // Check building isn't too close to the triangle edge.
631                         float type_roll = mt_rand(&seed);
632                         SGBuildingBin::BuildingType buildingtype = bin->getBuildingType(type_roll);
633                         float radius = bin->getBuildingMaxRadius(buildingtype);
634                         
635                         // Determine the actual center of the building, by shifting from the
636                         // center of the front face to the true center.
637                         osg::Matrix rotationMat = osg::Matrix::rotate(- rotation * M_PI * 2,
638                                                                       osg::Vec3f(0.0, 0.0, 1.0));
639                         SGVec3f buildingCenter = randomPoint + toSG(osg::Vec3f(-0.5 * bin->getBuildingMaxDepth(buildingtype), 0.0, 0.0) * rotationMat);
640                         
641                         SGVec3f p = buildingCenter - vorigin;
642 #if 1
643                         float edges[] = { length(cross(p     , p - v0)) / length(v0),
644                             length(cross(p - v0, p - v1)) / length(v1 - v0),
645                             length(cross(p - v1, p     )) / length(v1)      };
646                             float edge_dist = *std::min_element(edges, edges + 3);
647 #else
648                             float edge_dist = min_dist_from_borders(randomPoint, borderSegs);
649 #endif
650                             if (edge_dist < radius) {
651                                 triangle_dropped++;
652                                 continue;
653                             }
654                             
655                             // Check building isn't too close to random objects and other buildings.
656                             bool close = false;
657                             std::vector<std::pair<SGVec3f, float> >::iterator iter;
658                             
659                             for (iter = triangleBuildingList.begin(); iter != triangleBuildingList.end(); ++iter) {
660                                 float min_dist = iter->second + radius;
661                                 if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) {
662                                     close = true;
663                                     continue;
664                                 }
665                             }
666                             
667                             if (close) {
668                                 building_dropped++;
669                                 continue;
670                             }
671                             
672                             for (iter = triangleObjectsList.begin(); iter != triangleObjectsList.end(); ++iter) {
673                                 float min_dist = iter->second + radius;
674                                 if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) {
675                                     close = true;
676                                     continue;
677                                 }
678                             }
679                             
680                             if (close) {
681                                 random_dropped++;
682                                 continue;
683                             }
684                             
685                             std::pair<SGVec3f, float> pt = std::make_pair(buildingCenter, radius);
686                             triangleBuildingList.push_back(pt);
687                             bin->insert(randomPoint, rotation, buildingtype);
688                     }
689                 }
690                 
691                 triangleObjectsList.clear();
692                 triangleBuildingList.clear();
693             }
694             
695             SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << ((bin) ? bin->getNumBuildings() : 0));
696             SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to mask: " << mask_dropped);
697             SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to random object: " << random_dropped);
698             SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to other buildings: " << building_dropped);
699         }
700     }
701     
702     void computeRandomForest(std::vector<SGTriangleInfo>& matTris, float vegetation_density, SGTreeBinList& randomForest)
703     {        
704         unsigned int i;
705         
706         // generate a repeatable random seed
707         mt seed;
708         mt_init(&seed, unsigned(586));
709         
710         for ( i=0; i<matTris.size(); i++ ) {
711             SGMaterial *mat = matTris[i].getMaterial();
712             if (!mat)
713                 continue;
714             
715             float wood_coverage = mat->get_wood_coverage();
716             if ((wood_coverage <= 0) || (vegetation_density <= 0))
717                 continue;
718             
719             // Attributes that don't vary by tree but do vary by material
720             bool found = false;
721             TreeBin* bin = NULL;
722             
723             BOOST_FOREACH(bin, randomForest)
724             {
725                 if ((bin->texture           == mat->get_tree_texture()  ) &&
726                     (bin->teffect           == mat->get_tree_effect()   ) &&
727                     (bin->texture_varieties == mat->get_tree_varieties()) &&
728                     (bin->range             == mat->get_tree_range()    ) &&
729                     (bin->width             == mat->get_tree_width()    ) &&
730                     (bin->height            == mat->get_tree_height()   )   ) {
731                     found = true;
732                 break;
733                     }
734             }
735             
736             if (!found) {
737                 bin = new TreeBin();
738                 bin->texture = mat->get_tree_texture();
739                 SG_LOG(SG_INPUT, SG_DEBUG, "Tree texture " << bin->texture);
740                 bin->teffect = mat->get_tree_effect();
741                 SG_LOG(SG_INPUT, SG_DEBUG, "Tree effect " << bin->teffect);
742                 bin->range   = mat->get_tree_range();
743                 bin->width   = mat->get_tree_width();
744                 bin->height  = mat->get_tree_height();
745                 bin->texture_varieties = mat->get_tree_varieties();
746                 randomForest.push_back(bin);
747             }
748             
749             std::vector<SGVec3f> randomPoints;
750             std::vector<SGVec3f> randomPointNormals;
751             matTris[i].addRandomTreePoints(wood_coverage,
752                                            mat->get_one_object_mask(matTris[i].getTextureIndex()),
753                                            vegetation_density,
754                                            mat->get_cos_tree_max_density_slope_angle(),
755                                            mat->get_cos_tree_zero_density_slope_angle(),
756                                            randomPoints,
757                                            randomPointNormals);
758             
759             std::vector<SGVec3f>::iterator k;
760             std::vector<SGVec3f>::iterator j;
761             for (k = randomPoints.begin(), j = randomPointNormals.begin(); k != randomPoints.end(); ++k, ++j) {
762                       bin->insert(*k, *j);
763             }
764         }
765     }
766     
767     void computeRandomSurfaceLights(std::vector<SGTriangleInfo>& matTris, SGLightBin& randomTileLights )
768     {
769         unsigned int i;
770         
771         // Only compute the lights if we haven't already done so.
772         // For example, the light data will still exist if the
773         // PagedLOD expires.
774         if ( _randomSurfaceLightsComputed ) 
775         {
776             return;
777         } 
778         _randomSurfaceLightsComputed = true;
779         
780         // generate a repeatable random seed
781         mt seed;
782         mt_init(&seed, unsigned(123));
783
784         for ( i=0; i<matTris.size(); i++ ) {
785             SGMaterial *mat = matTris[i].getMaterial();
786             if (!mat)
787                 continue;
788             
789             float coverage = mat->get_light_coverage();
790             if (coverage <= 0)
791                 continue;
792             if (coverage < 10000.0) {
793                 SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
794                 << coverage << ", pushing up to 10000");
795                 coverage = 10000;
796             }
797                         
798             int texIndex = matTris[i].getTextureIndex();
799             
800             std::vector<SGVec3f> randomPoints;
801             matTris[i].addRandomSurfacePoints(coverage, 3, mat->get_one_object_mask(texIndex), randomPoints);
802             std::vector<SGVec3f>::iterator j;
803             for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
804                 float zombie = mt_rand(&seed);
805                 // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
806                 float factor = mt_rand(&seed);
807                 factor *= factor;
808                 
809                 float bright = 1;
810                 SGVec4f color;
811                 if ( zombie > 0.5 ) {
812                     // 50% chance of yellowish
813                     color = SGVec4f(0.9f, 0.9f, 0.3f, bright - factor * 0.2f);
814                 } else if (zombie > 0.15f) {
815                     // 35% chance of whitish
816                     color = SGVec4f(0.9, 0.9f, 0.8f, bright - factor * 0.2f);
817                 } else if (zombie > 0.05f) {
818                     // 10% chance of orangish
819                     color = SGVec4f(0.9f, 0.6f, 0.2f, bright - factor * 0.2f);
820                 } else {
821                     // 5% chance of redish
822                     color = SGVec4f(0.9f, 0.2f, 0.2f, bright - factor * 0.2f);
823                 }
824                 randomTileLights.insert(*j, color);
825             }
826         }
827     }
828     
829     // Generate all the lighting objects for the tile.
830     osg::LOD* generateLightingTileObjects(std::vector<SGTriangleInfo>& matTris, const SGMaterialCache* matcache)
831     {
832       SGLightBin randomTileLights;
833       computeRandomSurfaceLights(matTris, randomTileLights);
834       
835       GroundLightManager* lightManager = GroundLightManager::instance();
836       osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
837       SGVec3f up(0, 0, 1);
838
839       if (tileLights.getNumLights() > 0 || randomTileLights.getNumLights() > 0) {
840         osg::ref_ptr<osg::Group> groundLights0 = new osg::Group;
841
842         groundLights0->setStateSet(lightManager->getGroundLightStateSet());
843         groundLights0->setNodeMask(GROUNDLIGHTS0_BIT);
844
845         osg::ref_ptr<EffectGeode> geode = new EffectGeode;        
846         osg::ref_ptr<Effect> lightEffect = getLightEffect(24, osg::Vec3(1, 0.001, 0.00001), 1, 8, false, _options);
847         
848         geode->setEffect(lightEffect);                
849         geode->addDrawable(SGLightFactory::getLights(tileLights));
850         geode->addDrawable(SGLightFactory::getLights(randomTileLights, 4, -0.3f));
851         groundLights0->addChild(geode);
852         lightGroup->addChild(groundLights0);
853       }
854
855       if (randomTileLights.getNumLights() > 0) {
856         osg::ref_ptr<osg::Group> groundLights1 = new osg::Group;
857         groundLights1->setStateSet(lightManager->getGroundLightStateSet());
858         groundLights1->setNodeMask(GROUNDLIGHTS1_BIT);
859         
860         osg::ref_ptr<osg::Group> groundLights2 = new osg::Group;
861         groundLights2->setStateSet(lightManager->getGroundLightStateSet());
862         groundLights2->setNodeMask(GROUNDLIGHTS2_BIT);
863
864         osg::ref_ptr<EffectGeode> geode1 = new EffectGeode;
865         
866         osg::ref_ptr<Effect> lightEffect = getLightEffect(24, osg::Vec3(1, 0.001, 0.00001), 1, 8, false, _options);        
867         geode1->setEffect(lightEffect);        
868         geode1->addDrawable(SGLightFactory::getLights(randomTileLights, 2, -0.15f));
869         groundLights1->addChild(geode1);
870         lightGroup->addChild(groundLights1);
871         
872         osg::ref_ptr<EffectGeode> geode2 = new EffectGeode;
873         
874         geode2->setEffect(lightEffect);
875         geode2->addDrawable(SGLightFactory::getLights(randomTileLights));
876         groundLights2->addChild(geode2);
877         lightGroup->addChild(groundLights2);
878       }
879
880       if (! vasiLights.empty()) {
881         EffectGeode* vasiGeode = new EffectGeode;        
882         Effect* vasiEffect = getLightEffect(24, osg::Vec3(1, 0.0001, 0.000001), 1, 24, true, _options);
883         vasiGeode->setEffect(vasiEffect);
884         SGVec4f red(1, 0, 0, 1);
885         SGMaterial* mat = 0;
886         if (matcache)
887           mat = matcache->find("RWY_RED_LIGHTS");
888         if (mat) {
889           red = mat->get_light_color();
890         }
891         
892         SGVec4f white(1, 1, 1, 1);
893         mat = 0;
894         if (matcache)
895           mat = matcache->find("RWY_WHITE_LIGHTS");
896         if (mat) {
897           white = mat->get_light_color();
898         }
899         SGDirectionalLightListBin::const_iterator i;
900         for (i = vasiLights.begin();
901              i != vasiLights.end(); ++i) {
902             osg::Drawable* vasiDraw = SGLightFactory::getVasi(up, *i, red, white);
903             vasiGeode->addDrawable( vasiDraw );
904         }
905         osg::StateSet* ss = lightManager->getRunwayLightStateSet();
906         vasiGeode->setStateSet( ss );
907         lightGroup->addChild(vasiGeode);
908       }
909
910       Effect* runwayEffect = 0;
911       if (runwayLights.getNumLights() > 0
912           || !rabitLights.empty()
913           || !reilLights.empty()
914           || !odalLights.empty()
915           || taxiLights.getNumLights() > 0) {
916           
917           runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true, _options);
918       }
919       
920       if (runwayLights.getNumLights() > 0
921           || !rabitLights.empty()
922           || !reilLights.empty()
923           || !odalLights.empty()
924           || !holdshortLights.empty()
925           || !guardLights.empty()) {
926         osg::Group* rwyLights = new osg::Group;
927
928         osg::StateSet* ss = lightManager->getRunwayLightStateSet();      
929         rwyLights->setStateSet(ss);      
930         rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
931         
932         if (runwayLights.getNumLights() != 0) {
933           EffectGeode* geode = new EffectGeode;
934           geode->setEffect(runwayEffect);
935           
936           osg::Drawable* rldraw = SGLightFactory::getLights(runwayLights);
937           geode->addDrawable( rldraw );
938           
939           rwyLights->addChild(geode);
940         }
941         SGDirectionalLightListBin::const_iterator i;
942         for (i = rabitLights.begin();
943              i != rabitLights.end(); ++i) {
944             osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options);
945             rwyLights->addChild( seqNode );
946         }
947         for (i = reilLights.begin();
948              i != reilLights.end(); ++i) {
949             osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options);
950             rwyLights->addChild(seqNode);
951         }
952         for (i = holdshortLights.begin();
953              i != holdshortLights.end(); ++i) {
954             osg::Node* seqNode = SGLightFactory::getHoldShort(*i, _options);
955             rwyLights->addChild(seqNode);
956         }
957         for (i = guardLights.begin();
958              i != guardLights.end(); ++i) {
959             osg::Node* seqNode = SGLightFactory::getGuard(*i, _options);
960             rwyLights->addChild(seqNode);
961         }
962         SGLightListBin::const_iterator j;
963         for (j = odalLights.begin();
964              j != odalLights.end(); ++j) {
965             osg::Node* seqNode = SGLightFactory::getOdal(*j, _options);
966             rwyLights->addChild(seqNode);
967         }
968         lightGroup->addChild(rwyLights);
969       }
970
971       if (taxiLights.getNumLights() > 0) {
972         osg::Group* taxiLightsGroup = new osg::Group;
973         taxiLightsGroup->setStateSet(lightManager->getTaxiLightStateSet());
974         taxiLightsGroup->setNodeMask(RUNWAYLIGHTS_BIT);
975         EffectGeode* geode = new EffectGeode;
976         geode->setEffect(runwayEffect);
977         geode->addDrawable(SGLightFactory::getLights(taxiLights));
978         taxiLightsGroup->addChild(geode);
979         lightGroup->addChild(taxiLightsGroup);
980       }
981
982       osg::LOD* lightLOD = NULL;
983
984       if (lightGroup->getNumChildren() > 0) {
985         lightLOD = new osg::LOD;
986         lightLOD->addChild(lightGroup.get(), 0, 60000);
987         // VASI is always on, so doesn't use light bits.
988         lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT | PERMANENTLIGHT_BIT);
989       }
990
991       return lightLOD;
992     }
993
994     // Generate all the random forest, objects and buildings for the tile
995     osg::LOD* generateRandomTileObjects(std::vector<SGTriangleInfo>& matTris, const SGMaterialCache* matcache)
996     {
997       SGMaterialLibPtr matlib;
998       bool use_random_objects = false;
999       bool use_random_vegetation = false;
1000       bool use_random_buildings = false;
1001       float vegetation_density = 1.0f;
1002       float building_density = 1.0f;
1003       bool useVBOs = false;
1004       
1005       osg::ref_ptr<osg::Group> randomObjects;
1006       osg::ref_ptr<osg::Group> forestNode;
1007       osg::ref_ptr<osg::Group> buildingNode;
1008
1009       if (_options) {
1010         matlib = _options->getMaterialLib();
1011         SGPropertyNode* propertyNode = _options->getPropertyNode().get();
1012         if (propertyNode) {
1013             use_random_objects
1014                 = propertyNode->getBoolValue("/sim/rendering/random-objects",
1015                                              use_random_objects);
1016             use_random_vegetation
1017                 = propertyNode->getBoolValue("/sim/rendering/random-vegetation",
1018                                              use_random_vegetation);
1019             vegetation_density
1020                 = propertyNode->getFloatValue("/sim/rendering/vegetation-density",
1021                                               vegetation_density);
1022             use_random_buildings
1023                 = propertyNode->getBoolValue("/sim/rendering/random-buildings",
1024                                              use_random_buildings);
1025             building_density
1026                 = propertyNode->getFloatValue("/sim/rendering/building-density",
1027                                               building_density);
1028         }
1029         
1030         useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON");
1031       }
1032
1033       SGMatModelBin     randomModels;
1034       
1035       SGBuildingBinList randomBuildings;
1036       
1037       if (matlib && (use_random_objects || use_random_buildings)) {
1038           computeRandomObjectsAndBuildings( matTris, 
1039                                             building_density,
1040                                             use_random_objects,
1041                                             use_random_buildings,
1042                                             useVBOs,
1043                                             randomModels,
1044                                             randomBuildings
1045                                           );
1046       }
1047
1048       if (randomModels.getNumModels() > 0) {
1049         // Generate a repeatable random seed
1050         mt seed;
1051         mt_init(&seed, unsigned(123));
1052
1053         std::vector<ModelLOD> models;
1054         for (unsigned int i = 0; i < randomModels.getNumModels(); i++) {
1055           SGMatModelBin::MatModel obj = randomModels.getMatModel(i);
1056
1057           SGPropertyNode* root = _options->getPropertyNode()->getRootNode();
1058           osg::Node* node = obj.model->get_random_model(root, &seed);
1059
1060           // Create a matrix to place the object in the correct
1061           // location, and then apply the rotation matrix created
1062           // above, with an additional random (or taken from
1063           // the object mask) heading rotation if appropriate.
1064           osg::Matrix transformMat;
1065           transformMat = osg::Matrix::translate(toOsg(obj.position));
1066           if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
1067             // Rotate the object around the z axis.
1068             double hdg = mt_rand(&seed) * M_PI * 2;
1069             transformMat.preMult(osg::Matrix::rotate(hdg,
1070                                                      osg::Vec3d(0.0, 0.0, 1.0)));
1071           }
1072
1073           if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) {
1074             // Rotate the object around the z axis.
1075             double hdg =  - obj.rotation * M_PI * 2;
1076             transformMat.preMult(osg::Matrix::rotate(hdg,
1077                                                      osg::Vec3d(0.0, 0.0, 1.0)));
1078           }
1079
1080           osg::MatrixTransform* position =
1081             new osg::MatrixTransform(transformMat);
1082           position->setName("positionRandomModel");
1083           position->addChild(node);
1084           models.push_back(ModelLOD(position, obj.lod));
1085         }
1086         RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
1087         quadtree.buildQuadTree(models.begin(), models.end());
1088         randomObjects = quadtree.getRoot();
1089         randomObjects->setName("Random objects");
1090       }
1091
1092       if (!randomBuildings.empty()) {
1093         buildingNode = createRandomBuildings(randomBuildings, osg::Matrix::identity(), _options);
1094         buildingNode->setName("Random buildings");
1095         randomBuildings.clear();
1096       }
1097
1098       if (use_random_vegetation && matlib) {
1099         // Now add some random forest.
1100         SGTreeBinList randomForest;
1101         computeRandomForest(matTris, vegetation_density, randomForest);
1102
1103         if (!randomForest.empty()) {
1104           forestNode = createForest(randomForest, osg::Matrix::identity(),_options);
1105           forestNode->setName("Random trees");
1106         }
1107       }
1108
1109       osg::LOD* objectLOD = NULL;
1110
1111       if (randomObjects.valid() ||  forestNode.valid() || buildingNode.valid()) {
1112         objectLOD = new osg::LOD;
1113
1114         if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
1115         if (forestNode.valid())  objectLOD->addChild(forestNode.get(), 0, 20000);
1116         if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000);
1117
1118         unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
1119         objectLOD->setNodeMask(nodeMask);
1120       }
1121
1122       return objectLOD;
1123     }
1124
1125     /// The original options to use for this bunch of models
1126     osg::ref_ptr<SGReaderWriterOptions>     _options;
1127     string                                  _path;
1128     bool                                    _loadterrain;
1129     osg::ref_ptr<osg::Node>                 _rootNode;
1130     SGVec3d                                 _gbs_center;
1131     bool                                    _randomSurfaceLightsComputed;
1132     bool                                    _tileRandomObjectsComputed;
1133     
1134     // most of these are just point and color arrays - extracted from the 
1135     // .BTG PointGeometry at tile load time.
1136     // It shouldn't be too much to keep this in memory even if we don't use it.
1137     SGLightBin                              tileLights;
1138     SGDirectionalLightBin                   runwayLights;
1139     SGDirectionalLightBin                   taxiLights;
1140     SGDirectionalLightListBin               vasiLights;
1141     SGDirectionalLightListBin               rabitLights;
1142     SGLightListBin                          odalLights;
1143     SGDirectionalLightListBin               holdshortLights;
1144     SGDirectionalLightListBin               guardLights;
1145     SGDirectionalLightListBin               reilLights;    
1146 };