+}
+
+
+osg::Node*
+FGTileEntry::gen_lights( SGMaterialLib *matlib, osg::Vec3Array *lights,
+ int inc, float bright )
+{
+ // generate a repeatable random seed
+ sg_srandom( (unsigned)(*lights)[0][0] );
+
+ // Allocate ssg structure
+ osg::Vec3Array *vl = new osg::Vec3Array;
+ osg::Vec4Array *cl = new osg::Vec4Array;
+
+ for ( unsigned i = 0; i < lights->size(); ++i ) {
+ // this loop is slightly less efficient than it otherwise
+ // could be, but we want a red light to always be red, and a
+ // yellow light to always be yellow, etc. so we are trying to
+ // preserve the random sequence.
+ float zombie = sg_random();
+ if ( i % inc == 0 ) {
+ vl->push_back( (*lights)[i] );
+
+ // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
+ float factor = sg_random();
+ factor *= factor;
+
+ osg::Vec4 color;
+ if ( zombie > 0.5 ) {
+ // 50% chance of yellowish
+ color = osg::Vec4( 0.9, 0.9, 0.3, bright - factor * 0.2 );
+ } else if ( zombie > 0.15 ) {
+ // 35% chance of whitish
+ color = osg::Vec4( 0.9, 0.9, 0.8, bright - factor * 0.2 );
+ } else if ( zombie > 0.05 ) {
+ // 10% chance of orangish
+ color = osg::Vec4( 0.9, 0.6, 0.2, bright - factor * 0.2 );
+ } else {
+ // 5% chance of redish
+ color = osg::Vec4( 0.9, 0.2, 0.2, bright - factor * 0.2 );
+ }
+ cl->push_back( color );
+ }
+ }
+
+ // create ssg leaf
+ osg::Geometry* geometry = new osg::Geometry;
+ geometry->setVertexArray(vl);
+ geometry->setColorArray(cl);
+ geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
+ geometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, vl->size()));
+ osg::Geode* geode = new osg::Geode;
+ geode->addDrawable(geometry);
+
+ // assign state
+ SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
+ geode->setStateSet(mat->get_state());
+
+ return geode;
+}
+
+
+bool FGTileEntry::obj_load( const string& path,
+ osg::Group *geometry,
+ osg::Group *vasi_lights,
+ osg::Group *rwy_lights,
+ osg::Group *taxi_lights,
+ osg::Vec3Array *ground_lights, bool is_base )
+{
+ Point3D c; // returned center point
+ double br; // returned bounding radius
+
+ bool use_random_objects =
+ fgGetBool("/sim/rendering/random-objects", true);
+
+ // try loading binary format
+ if ( SGBinObjLoad( path, is_base,
+ &c, &br, globals->get_matlib(), use_random_objects,
+ geometry, vasi_lights, rwy_lights, taxi_lights,
+ ground_lights ) )
+ {
+ if ( is_base ) {
+ center = c;
+ bounding_radius = br;
+ }
+ }
+
+ return (geometry != NULL);
+}
+
+
+typedef enum {
+ OBJECT,
+ OBJECT_SHARED,
+ OBJECT_STATIC,
+ OBJECT_SIGN,
+ OBJECT_RUNWAY_SIGN
+} object_type;
+
+
+// storage class for deferred object processing in FGTileEntry::load()
+struct Object {
+ Object(object_type t, const string& token, const SGPath& p, istream& in)
+ : type(t), path(p)
+ {
+ in >> name;
+ if (type != OBJECT)
+ in >> lon >> lat >> elev >> hdg;
+ in >> ::skipeol;
+
+ if (type == OBJECT)
+ SG_LOG(SG_TERRAIN, SG_INFO, " " << token << " " << name);
+ else
+ SG_LOG(SG_TERRAIN, SG_INFO, " " << token << " " << name << " lon=" <<
+ lon << " lat=" << lat << " elev=" << elev << " hdg=" << hdg);
+ }
+ object_type type;
+ string name;
+ SGPath path;
+ double lon, lat, elev, hdg;
+};
+
+
+void
+FGTileEntry::load( const string_list &path_list, bool is_base )
+{
+ bool found_tile_base = false;
+
+ SGPath object_base;
+ vector<const Object*> objects;
+
+ string index_str = tile_bucket.gen_index_str();
+ SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << index_str );
+
+ // scan and parse all files and store information
+ for (unsigned int i = 0; i < path_list.size(); i++) {
+ // If we found a terrain tile in Terrain/, we have to process the
+ // Objects/ dir in the same group, too, before we can stop scanning.
+ // FGGlobals::set_fg_scenery() inserts an empty string to path_list
+ // as marker.
+ if (path_list[i].empty()) {
+ if (found_tile_base)
+ break;
+ else
+ continue;
+ }
+
+ bool has_base = false;
+
+ SGPath tile_path = path_list[i];
+ tile_path.append( tile_bucket.gen_base_path() );
+
+ SGPath basename = tile_path;
+ basename.append( index_str );
+
+ SG_LOG( SG_TERRAIN, SG_INFO, " Trying " << basename.str() );
+
+
+ // Check for master .stg (scene terra gear) file
+ SGPath stg_name = basename;
+ stg_name.concat( ".stg" );
+
+ sg_gzifstream in( stg_name.str() );
+ if ( !in.is_open() )
+ continue;
+
+ while ( ! in.eof() ) {
+ string token;
+ in >> token;
+
+ if ( token[0] == '#' ) {
+ in >> ::skipeol;
+ continue;
+ }
+ // Load only once (first found)
+ if ( token == "OBJECT_BASE" ) {
+ string name;
+ in >> name >> ::skipws;
+ SG_LOG( SG_TERRAIN, SG_INFO, " " << token << " " << name );
+
+ if (!found_tile_base) {
+ found_tile_base = true;
+ has_base = true;
+
+ object_base = tile_path;
+ object_base.append(name);
+
+ } else
+ SG_LOG(SG_TERRAIN, SG_INFO, " (skipped)");
+
+ // Load only if base is not in another file
+ } else if ( token == "OBJECT" ) {
+ if (!found_tile_base || has_base)
+ objects.push_back(new Object(OBJECT, token, tile_path, in));
+ else {
+ string name;
+ in >> name >> ::skipeol;
+ SG_LOG(SG_TERRAIN, SG_INFO, " " << token << " "
+ << name << " (skipped)");
+ }
+
+ // Always OK to load
+ } else if ( token == "OBJECT_STATIC" ) {
+ objects.push_back(new Object(OBJECT_STATIC, token, tile_path, in));
+
+ } else if ( token == "OBJECT_SHARED" ) {
+ objects.push_back(new Object(OBJECT_SHARED, token, tile_path, in));
+
+ } else if ( token == "OBJECT_SIGN" ) {
+ objects.push_back(new Object(OBJECT_SIGN, token, tile_path, in));
+
+ } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
+ objects.push_back(new Object(OBJECT_RUNWAY_SIGN, token, tile_path, in));
+
+ } else {
+ SG_LOG( SG_TERRAIN, SG_DEBUG,
+ "Unknown token '" << token << "' in " << stg_name.str() );
+ in >> ::skipws;
+ }
+ }
+ }
+
+
+ // obj_load() will generate ground lighting for us ...
+ osg::ref_ptr<osg::Vec3Array> light_pts = new osg::Vec3Array;
+ osg::Group* new_tile = new osg::Group;
+
+
+ if (found_tile_base) {
+ // load tile if found ...
+ osg::ref_ptr<osg::Group> geometry = new osg::Group;
+ if ( obj_load( object_base.str(), geometry.get(),
+ NULL, NULL, NULL, light_pts.get(), true ) ) {
+ new_tile -> addChild( geometry.get() );
+ }
+
+ } else {
+ // ... or generate an ocean tile on the fly
+ SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
+ osg::ref_ptr<osg::Group> geometry = new osg::Group;
+ Point3D c;
+ double br;
+ if ( SGGenTile( path_list[0], tile_bucket, &c, &br,
+ globals->get_matlib(), geometry.get() ) ) {
+ center = c;
+ bounding_radius = br;
+ new_tile -> addChild( geometry.get() );
+ } else {
+ SG_LOG( SG_TERRAIN, SG_ALERT,
+ "Warning: failed to generate ocean tile!" );
+ }
+ }
+
+
+ // now that we have a valid center, process all the objects
+ for (unsigned int j = 0; j < objects.size(); j++) {
+ const Object *obj = objects[j];
+
+ if (obj->type == OBJECT) {
+ SGPath custom_path = obj->path;
+ custom_path.append( obj->name );
+
+ osg::ref_ptr<osg::Group> geometry = new osg::Group;
+ osg::ref_ptr<osg::Group> vasi_lights = new osg::Group;
+ osg::ref_ptr<osg::Group> rwy_lights = new osg::Group;
+ osg::ref_ptr<osg::Group> taxi_lights = new osg::Group;