# include <simgear_config.h>
#endif
-#include <simgear/compiler.h>
-
-#include <list>
+#include "obj.hxx"
-#include STL_STRING
+#include <simgear/compiler.h>
-#include <osg/Depth>
#include <osg/Fog>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/LOD>
#include <osg/MatrixTransform>
+#include <osg/Point>
#include <osg/StateSet>
+#include <osg/Switch>
-#include <osgUtil/Optimizer>
-#include <osgDB/WriteFile>
-
-#include <simgear/bucket/newbucket.hxx>
+#include <simgear/debug/logstream.hxx>
#include <simgear/io/sg_binobj.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/math/sg_random.h>
-#include <simgear/math/sg_types.hxx>
-#include <simgear/misc/texcoord.hxx>
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/material/matlib.hxx>
+#include <simgear/scene/model/SGOffsetTransform.hxx>
#include <simgear/scene/util/SGUpdateVisitor.hxx>
#include <simgear/scene/util/SGNodeMasks.hxx>
-#include <simgear/scene/tgdb/leaf.hxx>
-#include <simgear/scene/tgdb/pt_lights.hxx>
-#include <simgear/scene/tgdb/userdata.hxx>
-
-#include "obj.hxx"
-
-SG_USING_STD(string);
-SG_USING_STD(list);
-
-struct Leaf {
- GLenum type;
- int index;
-};
-
-
-// Generate an ocean tile
-bool SGGenTile( const string& path, const SGBucket& b,
- SGMaterialLib *matlib, osg::Group* group )
-{
- osg::StateSet *state = 0;
+#include <simgear/scene/util/QuadTreeBuilder.hxx>
+
+#include "SGTexturedTriangleBin.hxx"
+#include "SGLightBin.hxx"
+#include "SGModelBin.hxx"
+#include "TreeBin.hxx"
+#include "SGDirectionalLightBin.hxx"
+#include "GroundLightManager.hxx"
+
+
+#include "userdata.hxx"
+#include "pt_lights.hxx"
+
+using namespace simgear;
+
+typedef std::map<std::string,SGTexturedTriangleBin> SGMaterialTriangleMap;
+typedef std::list<SGLightBin> SGLightListBin;
+typedef std::list<SGDirectionalLightBin> SGDirectionalLightListBin;
+
+struct SGTileGeometryBin {
+ SGMaterialTriangleMap materialTriangleMap;
+ SGLightBin tileLights;
+ SGLightBin randomTileLights;
+ TreeBin randomForest;
+ SGDirectionalLightBin runwayLights;
+ SGDirectionalLightBin taxiLights;
+ SGDirectionalLightListBin vasiLights;
+ SGDirectionalLightListBin rabitLights;
+ SGLightListBin odalLights;
+ SGDirectionalLightListBin reilLights;
+ SGMatModelBin randomModels;
+
+ static SGVec4f
+ getMaterialLightColor(const SGMaterial* material)
+ {
+ if (!material)
+ return SGVec4f(1, 1, 1, 0.8);
+ return material->get_light_color();
+ }
- double tex_width = 1000.0;
+ static void
+ addPointGeometry(SGLightBin& lights,
+ const std::vector<SGVec3d>& vertices,
+ const SGVec4f& color,
+ const int_list& pts_v)
+ {
+ for (unsigned i = 0; i < pts_v.size(); ++i)
+ lights.insert(toVec3f(vertices[pts_v[i]]), color);
+ }
- // find Ocean material in the properties list
- SGMaterial *mat = matlib->find( "Ocean" );
- if ( mat != NULL ) {
- // set the texture width and height values for this
- // material
- tex_width = mat->get_xsize();
-
- // set ssgState
- state = mat->get_state();
+ static void
+ addPointGeometry(SGDirectionalLightBin& lights,
+ const std::vector<SGVec3d>& vertices,
+ const std::vector<SGVec3f>& normals,
+ const SGVec4f& color,
+ const int_list& pts_v,
+ const int_list& pts_n)
+ {
+ // If the normal indices match the vertex indices, use seperate
+ // normal indices. Else reuse the vertex indices for the normals.
+ if (pts_v.size() == pts_n.size()) {
+ for (unsigned i = 0; i < pts_v.size(); ++i)
+ lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_n[i]], color);
} else {
- SG_LOG( SG_TERRAIN, SG_ALERT,
- "Ack! unknown usemtl name = " << "Ocean"
- << " in " << path );
- }
-
- // Calculate center point
- SGVec3d cartCenter = SGVec3d::fromGeod(b.get_center());
- Point3D center = Point3D(cartCenter[0], cartCenter[1], cartCenter[2]);
-
- double clon = b.get_center_lon();
- double clat = b.get_center_lat();
- double height = b.get_height();
- double width = b.get_width();
-
- // Caculate corner vertices
- Point3D geod[4];
- geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
- geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
- geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
- geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
-
- Point3D rad[4];
- int i;
- for ( i = 0; i < 4; ++i ) {
- rad[i] = Point3D( geod[i].x() * SGD_DEGREES_TO_RADIANS,
- geod[i].y() * SGD_DEGREES_TO_RADIANS,
- geod[i].z() );
+ for (unsigned i = 0; i < pts_v.size(); ++i)
+ lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_v[i]], color);
}
+ }
- Point3D cart[4], rel[4];
- for ( i = 0; i < 4; ++i ) {
- cart[i] = sgGeodToCart(rad[i]);
- rel[i] = cart[i] - center;
- // cout << "corner " << i << " = " << cart[i] << endl;
+ bool
+ insertPtGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
+ {
+ if (obj.get_pts_v().size() != obj.get_pts_n().size()) {
+ SG_LOG(SG_TERRAIN, SG_ALERT,
+ "Group list sizes for points do not match!");
+ return false;
}
- // Calculate normals
- Point3D normals[4];
- for ( i = 0; i < 4; ++i ) {
- double length = cart[i].distance3D( Point3D(0.0) );
- normals[i] = cart[i] / length;
- // cout << "normal = " << normals[i] << endl;
+ for (unsigned grp = 0; grp < obj.get_pts_v().size(); ++grp) {
+ std::string materialName = obj.get_pt_materials()[grp];
+ SGMaterial* material = 0;
+ if (matlib)
+ material = matlib->find(materialName);
+ SGVec4f color = getMaterialLightColor(material);
+
+ if (3 <= materialName.size() && materialName.substr(0, 3) != "RWY") {
+ // Just plain lights. Not something for the runway.
+ addPointGeometry(tileLights, obj.get_wgs84_nodes(), color,
+ obj.get_pts_v()[grp]);
+ } else if (materialName == "RWY_BLUE_TAXIWAY_LIGHTS"
+ || materialName == "RWY_GREEN_TAXIWAY_LIGHTS") {
+ addPointGeometry(taxiLights, obj.get_wgs84_nodes(), obj.get_normals(),
+ color, obj.get_pts_v()[grp], obj.get_pts_n()[grp]);
+ } else if (materialName == "RWY_VASI_LIGHTS") {
+ vasiLights.push_back(SGDirectionalLightBin());
+ addPointGeometry(vasiLights.back(), obj.get_wgs84_nodes(),
+ obj.get_normals(), color, obj.get_pts_v()[grp],
+ obj.get_pts_n()[grp]);
+ } else if (materialName == "RWY_SEQUENCED_LIGHTS") {
+ rabitLights.push_back(SGDirectionalLightBin());
+ addPointGeometry(rabitLights.back(), obj.get_wgs84_nodes(),
+ obj.get_normals(), color, obj.get_pts_v()[grp],
+ obj.get_pts_n()[grp]);
+ } else if (materialName == "RWY_ODALS_LIGHTS") {
+ odalLights.push_back(SGLightBin());
+ addPointGeometry(odalLights.back(), obj.get_wgs84_nodes(),
+ color, obj.get_pts_v()[grp]);
+ } else if (materialName == "RWY_REIL_LIGHTS") {
+ reilLights.push_back(SGDirectionalLightBin());
+ addPointGeometry(reilLights.back(), obj.get_wgs84_nodes(),
+ obj.get_normals(), color, obj.get_pts_v()[grp],
+ obj.get_pts_n()[grp]);
+ } else {
+ // what is left must be runway lights
+ addPointGeometry(runwayLights, obj.get_wgs84_nodes(),
+ obj.get_normals(), color, obj.get_pts_v()[grp],
+ obj.get_pts_n()[grp]);
+ }
}
- // Calculate texture coordinates
- point_list geod_nodes;
- geod_nodes.clear();
- geod_nodes.reserve(4);
- int_list rectangle;
- rectangle.clear();
- rectangle.reserve(4);
- for ( i = 0; i < 4; ++i ) {
- geod_nodes.push_back( geod[i] );
- rectangle.push_back( i );
- }
- point_list texs = sgCalcTexCoords( b, geod_nodes, rectangle,
- 1000.0 / tex_width );
-
- // Allocate ssg structure
- osg::Vec3Array *vl = new osg::Vec3Array;
- osg::Vec3Array *nl = new osg::Vec3Array;
- osg::Vec2Array *tl = new osg::Vec2Array;
-
- for ( i = 0; i < 4; ++i ) {
- vl->push_back(osg::Vec3(rel[i].x(), rel[i].y(), rel[i].z()));
- nl->push_back(osg::Vec3(normals[i].x(), normals[i].y(), normals[i].z()));
- tl->push_back(osg::Vec2(texs[i].x(), texs[i].y()));
- }
-
- osg::Vec4Array* cl = new osg::Vec4Array;
- cl->push_back(osg::Vec4(1, 1, 1, 1));
-
- osg::Geometry* geometry = new osg::Geometry;
- geometry->setVertexArray(vl);
- geometry->setNormalArray(nl);
- geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
- geometry->setColorArray(cl);
- geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
- geometry->setTexCoordArray(0, tl);
- geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_FAN, 0, vl->size()));
- osg::Geode* geode = new osg::Geode;
- geode->setName(path);
- geode->addDrawable(geometry);
- geode->setStateSet(state);
-
- group->addChild(geode);
-
return true;
-}
-
+ }
-/**
- * SSG callback for an in-range leaf of randomly-placed objects.
- *
- * This pretraversal callback is attached to a branch that is
- * traversed only when a leaf is in range. If the leaf is not
- * currently prepared to be populated with randomly-placed objects,
- * this callback will prepare it (actual population is handled by
- * the tri_in_range_callback for individual triangles).
- *
- * @param entity The entity to which the callback is attached (not used).
- * @param mask The entity's traversal mask (not used).
- * @return Always 1, to allow traversal and culling to continue.
- */
-// static int
-// leaf_in_range_callback (ssgEntity * entity, int mask)
-// {
-// SGLeafUserData * data = (SGLeafUserData *)entity->getUserData();
-
-// if (!data->is_filled_in) {
-// // Iterate through all the triangles
-// // and populate them.
-// int num_tris = data->leaf->getNumTriangles();
-// for ( int i = 0; i < num_tris; ++i ) {
-// data->setup_triangle(i);
-// }
-// data->is_filled_in = true;
-// }
-// return 1;
-// }
-
-
-/**
- * SSG callback for an out-of-range leaf of randomly-placed objects.
- *
- * This pretraversal callback is attached to a branch that is
- * traversed only when a leaf is out of range. If the leaf is
- * currently prepared to be populated with randomly-placed objects (or
- * is actually populated), the objects will be removed.
- *
- * @param entity The entity to which the callback is attached (not used).
- * @param mask The entity's traversal mask (not used).
- * @return Always 0, to prevent any further traversal or culling.
- */
-// static int
-// leaf_out_of_range_callback (ssgEntity * entity, int mask)
-// {
-// SGLeafUserData * data = (SGLeafUserData *)entity->getUserData();
-// if (data->is_filled_in) {
-// data->branch->removeAllKids();
-// data->is_filled_in = false;
-// }
-// return 0;
-// }
-
-
-/**
- * Randomly place objects on a surface.
- *
- * The leaf node provides the geometry of the surface, while the
- * material provides the objects and placement density. Latitude
- * and longitude are required so that the objects can be rotated
- * to the world-up vector. This function does not actually add
- * any objects; instead, it attaches an ssgRangeSelector to the
- * branch with callbacks to generate the objects when needed.
- *
- * @param leaf The surface where the objects should be placed.
- * @param branch The branch that will hold the randomly-placed objects.
- * @param center The center of the leaf in FlightGear coordinates.
- * @param material_name The name of the surface's material.
- */
-static void
-gen_random_surface_objects (osg::Drawable *leaf,
- osg::Group *branch,
- Point3D *center,
- SGMaterial *mat )
-{
- // OSGFIXME
-#if 0
- // If the surface has no triangles, return
- // now.
- int num_tris = leaf->getNumTriangles();
- if (num_tris < 1)
- return;
-
- // If the material has no randomly-placed
- // objects, return now.
- if (mat->get_object_group_count() < 1)
- return;
-
- // Calculate the geodetic centre of
- // the tile, for aligning automatic
- // objects.
- double xyz[3], lon_rad, lat_rad, alt_m;
- xyz[0] = center->x(); xyz[1] = center->y(); xyz[2] = center->z();
- sgCartToGeod(xyz, &lat_rad, &lon_rad, &alt_m);
-
- // LOD for the leaf
- // max random object range: 20000m
- osg::LOD * lod = new osg::LOD;
- branch->addChild(lod);
-
- // Create the in-range and out-of-range
- // branches.
- osg::Group * in_range = new osg::Group;
-// osg::Group * out_of_range = new osg::Group;
- lod->addChild(in_range, 0, 20000 /*OSGFIXME hardcoded visibility ???*/);
-// lod->addChild(out_of_range, 20000, 1e30);
-
- SGLeafUserData * data = new SGLeafUserData;
- data->is_filled_in = false;
- data->leaf = leaf;
- data->mat = mat;
- data->branch = in_range;
- data->sin_lat = sin(lat_rad);
- data->cos_lat = cos(lat_rad);
- data->sin_lon = sin(lon_rad);
- data->cos_lon = cos(lon_rad);
-
- in_range->setUserData(data);
- // OSGFIXME: implement random objects to be loaded when in sight
-// in_range->setTravCallback(SSG_CALLBACK_PRETRAV, leaf_in_range_callback);
-
- // OSGFIXME: implement deletion of tiles that are no longer used
-// out_of_range->setUserData(data);
-// out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
-// leaf_out_of_range_callback);
-// out_of_range
-// ->addChild(new SGDummyBSphereEntity(leaf->getBSphere()->getRadius()));
-#endif
-}
+ static SGVec2f
+ getTexCoord(const std::vector<SGVec2f>& texCoords, const int_list& tc,
+ const SGVec2f& tcScale, unsigned i)
+ {
+ if (tc.empty())
+ return tcScale;
+ else if (tc.size() == 1)
+ return mult(texCoords[tc[0]], tcScale);
+ else
+ return mult(texCoords[tc[i]], tcScale);
+ }
-// Ok, somehow polygon offset for lights ...
-// Could never make the polygon offset for our lights get right.
-// So, why not in this way ...
-class SGLightOffsetTransform : public osg::Transform {
-public:
-#define SCALE_FACTOR 0.94
- virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
- osg::NodeVisitor* nv) const
+ static void
+ addTriangleGeometry(SGTexturedTriangleBin& triangles,
+ const std::vector<SGVec3d>& vertices,
+ const std::vector<SGVec3f>& normals,
+ const std::vector<SGVec2f>& texCoords,
+ const int_list& tris_v,
+ const int_list& tris_n,
+ const int_list& tris_tc,
+ const SGVec2f& tcScale)
{
- if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) {
- double scaleFactor = SCALE_FACTOR;
- osg::Vec3 center = nv->getEyePoint();
- osg::Matrix transform;
- transform(0,0) = scaleFactor;
- transform(1,1) = scaleFactor;
- transform(2,2) = scaleFactor;
- transform(3,0) = center[0]*(1 - scaleFactor);
- transform(3,1) = center[1]*(1 - scaleFactor);
- transform(3,2) = center[2]*(1 - scaleFactor);
- matrix.preMult(transform);
+ if (tris_v.size() != tris_n.size()) {
+ // If the normal indices do not match, they should be inmplicitly
+ // the same than the vertex indices. So just call ourselves again
+ // with the matching index vector.
+ addTriangleGeometry(triangles, vertices, normals, texCoords,
+ tris_v, tris_v, tris_tc, tcScale);
+ return;
+ }
+
+ for (unsigned i = 2; i < tris_v.size(); i += 3) {
+ SGVertNormTex v0;
+ v0.vertex = toVec3f(vertices[tris_v[i-2]]);
+ v0.normal = normals[tris_n[i-2]];
+ v0.texCoord = getTexCoord(texCoords, tris_tc, tcScale, i-2);
+ SGVertNormTex v1;
+ v1.vertex = toVec3f(vertices[tris_v[i-1]]);
+ v1.normal = normals[tris_n[i-1]];
+ v1.texCoord = getTexCoord(texCoords, tris_tc, tcScale, i-1);
+ SGVertNormTex v2;
+ v2.vertex = toVec3f(vertices[tris_v[i]]);
+ v2.normal = normals[tris_n[i]];
+ v2.texCoord = getTexCoord(texCoords, tris_tc, tcScale, i);
+ triangles.insert(v0, v1, v2);
}
- return true;
}
- virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
- osg::NodeVisitor* nv) const
+
+ static void
+ addStripGeometry(SGTexturedTriangleBin& triangles,
+ const std::vector<SGVec3d>& vertices,
+ const std::vector<SGVec3f>& normals,
+ const std::vector<SGVec2f>& texCoords,
+ const int_list& strips_v,
+ const int_list& strips_n,
+ const int_list& strips_tc,
+ const SGVec2f& tcScale)
{
- if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) {
- double scaleFactor = 1/SCALE_FACTOR;
- osg::Vec3 center = nv->getEyePoint();
- osg::Matrix transform;
- transform(0,0) = scaleFactor;
- transform(1,1) = scaleFactor;
- transform(2,2) = scaleFactor;
- transform(3,0) = center[0]*(1 - scaleFactor);
- transform(3,1) = center[1]*(1 - scaleFactor);
- transform(3,2) = center[2]*(1 - scaleFactor);
- matrix.postMult(transform);
+ if (strips_v.size() != strips_n.size()) {
+ // If the normal indices do not match, they should be inmplicitly
+ // the same than the vertex indices. So just call ourselves again
+ // with the matching index vector.
+ addStripGeometry(triangles, vertices, normals, texCoords,
+ strips_v, strips_v, strips_tc, tcScale);
+ return;
}
- return true;
- }
-#undef SCALE_FACTOR
-};
-static SGMaterial* findMaterial(const std::string& material,
- const std::string& path,
- SGMaterialLib *matlib)
-{
- SGMaterial *mat = matlib->find( material );
- if (mat)
- return mat;
-
- // see if this is an on the fly texture
- string file = path;
- string::size_type pos = file.rfind( "/" );
- file = file.substr( 0, pos );
- // cout << "current file = " << file << endl;
- file += "/";
- file += material;
- // cout << "current file = " << file << endl;
- if ( ! matlib->add_item( file ) ) {
- SG_LOG( SG_TERRAIN, SG_ALERT,
- "Ack! unknown usemtl name = " << material
- << " in " << path );
- } else {
- // locate our newly created material
- mat = matlib->find( material );
- if ( mat == NULL ) {
- SG_LOG( SG_TERRAIN, SG_ALERT,
- "Ack! bad on the fly material create = "
- << material << " in " << path );
+ for (unsigned i = 2; i < strips_v.size(); ++i) {
+ SGVertNormTex v0;
+ v0.vertex = toVec3f(vertices[strips_v[i-2]]);
+ v0.normal = normals[strips_n[i-2]];
+ v0.texCoord = getTexCoord(texCoords, strips_tc, tcScale, i-2);
+ SGVertNormTex v1;
+ v1.vertex = toVec3f(vertices[strips_v[i-1]]);
+ v1.normal = normals[strips_n[i-1]];
+ v1.texCoord = getTexCoord(texCoords, strips_tc, tcScale, i-1);
+ SGVertNormTex v2;
+ v2.vertex = toVec3f(vertices[strips_v[i]]);
+ v2.normal = normals[strips_n[i]];
+ v2.texCoord = getTexCoord(texCoords, strips_tc, tcScale, i);
+ if (i%2)
+ triangles.insert(v1, v0, v2);
+ else
+ triangles.insert(v0, v1, v2);
}
}
- return mat;
-}
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Scenery loaders.
-////////////////////////////////////////////////////////////////////////
-
-// Load an Binary obj file
-static bool SGBinObjLoad( const string& path, const bool is_base,
- Point3D& center,
- SGMaterialLib *matlib,
- bool use_random_objects,
- osg::Group *local_terrain,
- osg::Group *vasi_lights,
- osg::Group *rwy_lights,
- osg::Group *taxi_lights,
- osg::Vec3Array *ground_lights )
-{
- SGBinObject obj;
+
+ static void
+ addFanGeometry(SGTexturedTriangleBin& triangles,
+ const std::vector<SGVec3d>& vertices,
+ const std::vector<SGVec3f>& normals,
+ const std::vector<SGVec2f>& texCoords,
+ const int_list& fans_v,
+ const int_list& fans_n,
+ const int_list& fans_tc,
+ const SGVec2f& tcScale)
+ {
+ if (fans_v.size() != fans_n.size()) {
+ // If the normal indices do not match, they should be implicitly
+ // the same than the vertex indices. So just call ourselves again
+ // with the matching index vector.
+ addFanGeometry(triangles, vertices, normals, texCoords,
+ fans_v, fans_v, fans_tc, tcScale);
+ return;
+ }
- if ( ! obj.read_bin( path ) ) {
- return false;
+ SGVertNormTex v0;
+ v0.vertex = toVec3f(vertices[fans_v[0]]);
+ v0.normal = normals[fans_n[0]];
+ v0.texCoord = getTexCoord(texCoords, fans_tc, tcScale, 0);
+ SGVertNormTex v1;
+ v1.vertex = toVec3f(vertices[fans_v[1]]);
+ v1.normal = normals[fans_n[1]];
+ v1.texCoord = getTexCoord(texCoords, fans_tc, tcScale, 1);
+ for (unsigned i = 2; i < fans_v.size(); ++i) {
+ SGVertNormTex v2;
+ v2.vertex = toVec3f(vertices[fans_v[i]]);
+ v2.normal = normals[fans_n[i]];
+ v2.texCoord = getTexCoord(texCoords, fans_tc, tcScale, i);
+ triangles.insert(v0, v1, v2);
+ v1 = v2;
}
+ }
- // reference point (center offset/bounding sphere)
- center = obj.get_gbs_center();
-
- point_list const& nodes = obj.get_wgs84_nodes();
- point_list const& normals = obj.get_normals();
- point_list const& texcoords = obj.get_texcoords();
-
- string material;
- int_list tex_index;
-
- group_list::size_type i;
-
- osg::Geode* geode = new osg::Geode;
- local_terrain->addChild( geode );
-
- // generate points
- string_list const& pt_materials = obj.get_pt_materials();
- group_list const& pts_v = obj.get_pts_v();
- group_list const& pts_n = obj.get_pts_n();
- for ( i = 0; i < pts_v.size(); ++i ) {
- // cout << "pts_v.size() = " << pts_v.size() << endl;
- if ( pt_materials[i].substr(0, 3) == "RWY" ) {
- // airport environment lighting
- SGVec3d up(center.x(), center.y(), center.z());
- // returns a transform -> lod -> leaf structure
- osg::Node *branch = SGMakeDirectionalLights( nodes, normals,
- pts_v[i], pts_n[i],
- matlib,
- pt_materials[i], up );
- if ( pt_materials[i] == "RWY_VASI_LIGHTS" ) {
- vasi_lights->addChild( branch );
- } else if ( pt_materials[i] == "RWY_BLUE_TAXIWAY_LIGHTS"
- || pt_materials[i] == "RWY_GREEN_TAXIWAY_LIGHTS" )
- {
- taxi_lights->addChild( branch );
- } else {
- rwy_lights->addChild( branch );
- }
- } else {
- // other geometry
- SGMaterial *mat = findMaterial( pt_materials[i], path, matlib );
- tex_index.clear();
- osg::Drawable *leaf = SGMakeLeaf( path, GL_POINTS, mat,
- nodes, normals, texcoords,
- pts_v[i], pts_n[i], tex_index,
- false, ground_lights );
-
+ SGVec2f getTexCoordScale(const std::string& name, SGMaterialLib* matlib)
+ {
+ if (!matlib)
+ return SGVec2f(1, 1);
+ SGMaterial* material = matlib->find(name);
+ if (!material)
+ return SGVec2f(1, 1);
+ return material->get_tex_coord_scale();
+ }
- geode->addDrawable( leaf );
- }
+ bool
+ insertSurfaceGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
+ {
+ if (obj.get_tris_n().size() < obj.get_tris_v().size() ||
+ obj.get_tris_tc().size() < obj.get_tris_v().size()) {
+ SG_LOG(SG_TERRAIN, SG_ALERT,
+ "Group list sizes for triangles do not match!");
+ return false;
}
- // Put all randomly-placed objects under a separate branch
- // (actually an ssgRangeSelector) named "random-models".
- osg::Group * random_object_branch = 0;
-// if (use_random_objects) {
-// osg::LOD* object_lod = new osg::LOD;
-// object_lod->setName("random-models");
-// geometry->addChild(object_lod);
-// random_object_branch = new osg::Group;
-// // Maximum 20km range for random objects
-// object_lod->addChild(random_object_branch, 0, 20000);
-// }
-
- typedef map<string,list<Leaf> > LeafMap;
- LeafMap leafMap;
- Leaf leaf;
- leaf.type = GL_TRIANGLES;
- string_list const& tri_materials = obj.get_tri_materials();
- group_list const& tris_v = obj.get_tris_v();
- group_list const& tris_n = obj.get_tris_n();
- group_list const& tris_tc = obj.get_tris_tc();
- for ( i = 0; i < tris_v.size(); i++ ) {
- leaf.index = i;
- leafMap[ tri_materials[i] ].push_back( leaf );
+ for (unsigned grp = 0; grp < obj.get_tris_v().size(); ++grp) {
+ std::string materialName = obj.get_tri_materials()[grp];
+ SGVec2f tcScale = getTexCoordScale(materialName, matlib);
+ addTriangleGeometry(materialTriangleMap[materialName],
+ obj.get_wgs84_nodes(), obj.get_normals(),
+ obj.get_texcoords(), obj.get_tris_v()[grp],
+ obj.get_tris_n()[grp], obj.get_tris_tc()[grp],
+ tcScale);
}
- leaf.type = GL_TRIANGLE_STRIP;
- string_list const& strip_materials = obj.get_strip_materials();
- group_list const& strips_v = obj.get_strips_v();
- group_list const& strips_n = obj.get_strips_n();
- group_list const& strips_tc = obj.get_strips_tc();
- for ( i = 0; i < strips_v.size(); i++ ) {
- leaf.index = i;
- leafMap[ strip_materials[i] ].push_back( leaf );
+
+ if (obj.get_strips_n().size() < obj.get_strips_v().size() ||
+ obj.get_strips_tc().size() < obj.get_strips_v().size()) {
+ SG_LOG(SG_TERRAIN, SG_ALERT,
+ "Group list sizes for strips do not match!");
+ return false;
}
- leaf.type = GL_TRIANGLE_FAN;
- string_list const& fan_materials = obj.get_fan_materials();
- group_list const& fans_v = obj.get_fans_v();
- group_list const& fans_n = obj.get_fans_n();
- group_list const& fans_tc = obj.get_fans_tc();
- for ( i = 0; i < fans_v.size(); i++ ) {
- leaf.index = i;
- leafMap[ fan_materials[i] ].push_back( leaf );
+ for (unsigned grp = 0; grp < obj.get_strips_v().size(); ++grp) {
+ std::string materialName = obj.get_strip_materials()[grp];
+ SGVec2f tcScale = getTexCoordScale(materialName, matlib);
+ addStripGeometry(materialTriangleMap[materialName],
+ obj.get_wgs84_nodes(), obj.get_normals(),
+ obj.get_texcoords(), obj.get_strips_v()[grp],
+ obj.get_strips_n()[grp], obj.get_strips_tc()[grp],
+ tcScale);
}
- LeafMap::iterator lmi = leafMap.begin();
- while ( lmi != leafMap.end() ) {
- SGMaterial *mat = findMaterial( lmi->first, path, matlib );
- list<Leaf> &leaf_list = lmi->second;
- list<Leaf>::iterator li = leaf_list.begin();
- while ( li != leaf_list.end() ) {
- Leaf &leaf = *li;
- int ind = leaf.index;
- if ( leaf.type == GL_TRIANGLES ) {
- osg::Drawable *leaf = SGMakeLeaf( path, GL_TRIANGLES, mat,
- nodes, normals, texcoords,
- tris_v[ind], tris_n[ind], tris_tc[ind],
- is_base, ground_lights );
- if ( random_object_branch ) {
- if ( mat ) {
- gen_random_surface_objects( leaf, random_object_branch,
- ¢er, mat );
- }
- }
- geode->addDrawable( leaf );
- } else if ( leaf.type == GL_TRIANGLE_STRIP ) {
- osg::Drawable *leaf = SGMakeLeaf( path, GL_TRIANGLE_STRIP, mat,
- nodes, normals, texcoords,
- strips_v[ind], strips_n[ind], strips_tc[ind],
- is_base, ground_lights );
- if ( random_object_branch ) {
- if ( mat ) {
- gen_random_surface_objects( leaf, random_object_branch,
- ¢er, mat );
- }
- }
- geode->addDrawable( leaf );
- } else {
- osg::Drawable *leaf = SGMakeLeaf( path, GL_TRIANGLE_FAN, mat,
- nodes, normals, texcoords,
- fans_v[ind], fans_n[ind], fans_tc[ind],
- is_base, ground_lights );
- if ( random_object_branch ) {
- if ( mat ) {
- gen_random_surface_objects( leaf, random_object_branch,
- ¢er, mat );
- }
- }
- geode->addDrawable( leaf );
- }
- ++li;
- }
- ++lmi;
+ if (obj.get_fans_n().size() < obj.get_fans_v().size() ||
+ obj.get_fans_tc().size() < obj.get_fans_v().size()) {
+ SG_LOG(SG_TERRAIN, SG_ALERT,
+ "Group list sizes for fans do not match!");
+ return false;
+ }
+ for (unsigned grp = 0; grp < obj.get_fans_v().size(); ++grp) {
+ std::string materialName = obj.get_fan_materials()[grp];
+ SGVec2f tcScale = getTexCoordScale(materialName, matlib);
+ addFanGeometry(materialTriangleMap[materialName],
+ obj.get_wgs84_nodes(), obj.get_normals(),
+ obj.get_texcoords(), obj.get_fans_v()[grp],
+ obj.get_fans_n()[grp], obj.get_fans_tc()[grp],
+ tcScale);
}
-
return true;
-}
-
-
+ }
+ osg::Node* getSurfaceGeometry(SGMaterialLib* matlib) const
+ {
+ if (materialTriangleMap.empty())
+ return 0;
+
+ EffectGeode* eg = 0;
+ osg::Group* group = (materialTriangleMap.size() > 1 ? new osg::Group : 0);
+ //osg::Geode* geode = new osg::Geode;
+ SGMaterialTriangleMap::const_iterator i;
+ for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+ osg::Geometry* geometry = i->second.buildGeometry();
+ SGMaterial *mat = 0;
+ if (matlib)
+ mat = matlib->find(i->first);
+ eg = new EffectGeode;
+ if (mat)
+ eg->setEffect(mat->get_effect());
+ eg->addDrawable(geometry);
+ eg->runGenerators(geometry); // Generate extra data needed by effect
+ if (group)
+ group->addChild(eg);
+ }
+ if (group)
+ return group;
+ else
+ return eg;
+ }
-static osg::Node*
-gen_lights( SGMaterialLib *matlib, osg::Vec3Array *lights, int inc, float bright )
-{
+ void computeRandomSurfaceLights(SGMaterialLib* matlib)
+ {
+ SGMaterialTriangleMap::iterator i;
+
// 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 );
+ mt seed;
+ mt_init(&seed, unsigned(123));
+
+ for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+ SGMaterial *mat = matlib->find(i->first);
+ if (!mat)
+ continue;
+
+ float coverage = mat->get_light_coverage();
+ if (coverage <= 0)
+ continue;
+ if (coverage < 10000.0) {
+ SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
+ << coverage << ", pushing up to 10000");
+ coverage = 10000;
+ }
+
+ std::vector<SGVec3f> randomPoints;
+ i->second.addRandomSurfacePoints(coverage, 3, randomPoints);
+ std::vector<SGVec3f>::iterator j;
+ for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
+ float zombie = mt_rand(&seed);
+ // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
+ float factor = mt_rand(&seed);
+ factor *= factor;
+
+ float bright = 1;
+ SGVec4f color;
+ if ( zombie > 0.5 ) {
+ // 50% chance of yellowish
+ color = SGVec4f(0.9f, 0.9f, 0.3f, bright - factor * 0.2f);
+ } else if (zombie > 0.15f) {
+ // 35% chance of whitish
+ color = SGVec4f(0.9, 0.9f, 0.8f, bright - factor * 0.2f);
+ } else if (zombie > 0.05f) {
+ // 10% chance of orangish
+ color = SGVec4f(0.9f, 0.6f, 0.2f, bright - factor * 0.2f);
+ } else {
+ // 5% chance of redish
+ color = SGVec4f(0.9f, 0.2f, 0.2f, bright - factor * 0.2f);
}
+ randomTileLights.insert(*j, 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" );
- geometry->setStateSet(mat->get_state());
-
- return geode;
-}
-
-class SGTileUpdateCallback : public osg::NodeCallback {
-public:
- virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+ void computeRandomForest(SGMaterialLib* matlib)
{
- assert(dynamic_cast<osg::Switch*>(node));
- assert(dynamic_cast<SGUpdateVisitor*>(nv));
-
- osg::Switch* lightSwitch = static_cast<osg::Switch*>(node);
- SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
+ SGMaterialTriangleMap::iterator i;
- // The current sun angle in degree
- float sun_angle = updateVisitor->getSunAngleDeg();
-
- // vasi is always on
- lightSwitch->setValue(0, true);
- if (sun_angle > 85 || updateVisitor->getVisibility() < 5000) {
- // runway and taxi
- lightSwitch->setValue(1, true);
- lightSwitch->setValue(2, true);
- } else {
- // runway and taxi
- lightSwitch->setValue(1, false);
- lightSwitch->setValue(2, false);
+ // generate a repeatable random seed
+ mt seed;
+ mt_init(&seed, unsigned(586));
+
+ for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+ SGMaterial *mat = matlib->find(i->first);
+ if (!mat)
+ continue;
+
+ float wood_coverage = mat->get_wood_coverage();
+ if (wood_coverage <= 0)
+ continue;
+
+ // Attributes that don't vary by tree
+ randomForest.texture = mat->get_tree_texture();
+ randomForest.range = mat->get_tree_range();
+ randomForest.width = mat->get_tree_width();
+ randomForest.height = mat->get_tree_height();
+ randomForest.texture_varieties = mat->get_tree_varieties();
+
+ std::vector<SGVec3f> randomPoints;
+ i->second.addRandomTreePoints(wood_coverage,
+ mat->get_tree_density(),
+ mat->get_wood_size(),
+ randomPoints);
+
+ std::vector<SGVec3f>::iterator j;
+ for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
+ randomForest.insert(*j);
+ }
}
-
- // ground lights
- if ( sun_angle > 95 )
- lightSwitch->setValue(5, true);
- else
- lightSwitch->setValue(5, false);
- if ( sun_angle > 92 )
- lightSwitch->setValue(4, true);
- else
- lightSwitch->setValue(4, false);
- if ( sun_angle > 89 )
- lightSwitch->setValue(3, true);
- else
- lightSwitch->setValue(3, false);
-
- traverse(node, nv);
}
-};
-class SGRunwayLightFogUpdateCallback : public osg::StateAttribute::Callback {
-public:
- virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
+ void computeRandomObjects(SGMaterialLib* matlib)
{
- assert(dynamic_cast<SGUpdateVisitor*>(nv));
- assert(dynamic_cast<osg::Fog*>(sa));
- SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
- osg::Fog* fog = static_cast<osg::Fog*>(sa);
- fog->setMode(osg::Fog::EXP2);
- fog->setColor(updateVisitor->getFogColor().osg());
- fog->setDensity(updateVisitor->getRunwayFogExp2Density());
+ SGMaterialTriangleMap::iterator i;
+
+ // generate a repeatable random seed
+ mt seed;
+ mt_init(&seed, unsigned(123));
+
+ for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+ SGMaterial *mat = matlib->find(i->first);
+ if (!mat)
+ continue;
+
+ int group_count = mat->get_object_group_count();
+
+ if (group_count > 0)
+ {
+ for (int j = 0; j < group_count; j++)
+ {
+ SGMatModelGroup *object_group = mat->get_object_group(j);
+ int nObjects = object_group->get_object_count();
+
+ if (nObjects > 0)
+ {
+ // For each of the random models in the group, determine an appropriate
+ // number of random placements and insert them.
+ for (int k = 0; k < nObjects; k++) {
+ SGMatModel * object = object_group->get_object(k);
+
+ std::vector<SGVec3f> randomPoints;
+
+ i->second.addRandomPoints(object->get_coverage_m2(), randomPoints);
+ std::vector<SGVec3f>::iterator l;
+ for (l = randomPoints.begin(); l != randomPoints.end(); ++l) {
+ randomModels.insert(*l, object, (int)object->get_randomized_range_m(&seed));
+ }
+ }
+ }
+ }
+ }
+ }
}
-};
-class SGTaxiLightFogUpdateCallback : public osg::StateAttribute::Callback {
-public:
- virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
+ bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
{
- assert(dynamic_cast<SGUpdateVisitor*>(nv));
- assert(dynamic_cast<osg::Fog*>(sa));
- SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
- osg::Fog* fog = static_cast<osg::Fog*>(sa);
- fog->setMode(osg::Fog::EXP2);
- fog->setColor(updateVisitor->getFogColor().osg());
- fog->setDensity(updateVisitor->getTaxiFogExp2Density());
+ if (!insertPtGeometry(obj, matlib))
+ return false;
+ if (!insertSurfaceGeometry(obj, matlib))
+ return false;
+ return true;
}
};
-class SGGroundLightFogUpdateCallback : public osg::StateAttribute::Callback {
-public:
- virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
- {
- assert(dynamic_cast<SGUpdateVisitor*>(nv));
- assert(dynamic_cast<osg::Fog*>(sa));
- SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
- osg::Fog* fog = static_cast<osg::Fog*>(sa);
- fog->setMode(osg::Fog::EXP2);
- fog->setColor(updateVisitor->getFogColor().osg());
- fog->setDensity(updateVisitor->getGroundLightsFogExp2Density());
- }
+typedef std::pair<osg::Node*, int> ModelLOD;
+struct MakeQuadLeaf {
+ osg::LOD* operator() () const { return new osg::LOD; }
+};
+struct AddModelLOD {
+ void operator() (osg::LOD* leaf, ModelLOD& mlod) const
+ {
+ leaf->addChild(mlod.first, 0, mlod.second);
+ }
+};
+struct GetModelLODCoord {
+ GetModelLODCoord() {}
+ GetModelLODCoord(const GetModelLODCoord& rhs)
+ {}
+ osg::Vec3 operator() (const ModelLOD& mlod) const
+ {
+ return mlod.first->getBound().center();
+ }
};
+typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
+ GetModelLODCoord> RandomObjectsQuadtree;
osg::Node*
-SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects)
+SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects, bool use_random_vegetation)
{
- osg::Group* vasiLights = new osg::Group;
- osg::StateSet* stateSet = vasiLights->getOrCreateStateSet();
- osg::Fog* fog = new osg::Fog;
- fog->setUpdateCallback(new SGRunwayLightFogUpdateCallback);
- stateSet->setAttribute(fog);
-
- osg::Group* rwyLights = new osg::Group;
- stateSet = rwyLights->getOrCreateStateSet();
- fog = new osg::Fog;
- fog->setUpdateCallback(new SGRunwayLightFogUpdateCallback);
- stateSet->setAttribute(fog);
-
- osg::Group* taxiLights = new osg::Group;
- stateSet = taxiLights->getOrCreateStateSet();
- fog = new osg::Fog;
- fog->setUpdateCallback(new SGTaxiLightFogUpdateCallback);
- stateSet->setAttribute(fog);
-
- osg::Group* groundLights0 = new osg::Group;
- stateSet = groundLights0->getOrCreateStateSet();
- fog = new osg::Fog;
- fog->setUpdateCallback(new SGGroundLightFogUpdateCallback);
- stateSet->setAttribute(fog);
-
- osg::Group* groundLights1 = new osg::Group;
- stateSet = groundLights1->getOrCreateStateSet();
- fog = new osg::Fog;
- fog->setUpdateCallback(new SGGroundLightFogUpdateCallback);
- stateSet->setAttribute(fog);
-
- osg::Group* groundLights2 = new osg::Group;
- stateSet = groundLights2->getOrCreateStateSet();
- fog = new osg::Fog;
- fog->setUpdateCallback(new SGGroundLightFogUpdateCallback);
- stateSet->setAttribute(fog);
-
- osg::Switch* lightSwitch = new osg::Switch;
- lightSwitch->setUpdateCallback(new SGTileUpdateCallback);
- lightSwitch->addChild(vasiLights, true);
- lightSwitch->addChild(rwyLights, true);
- lightSwitch->addChild(taxiLights, true);
- lightSwitch->addChild(groundLights0, true);
- lightSwitch->addChild(groundLights1, true);
- lightSwitch->addChild(groundLights2, true);
-
- osg::Group* lightGroup = new SGLightOffsetTransform;
- lightGroup->addChild(lightSwitch);
- unsigned nodeMask = ~0u;
- nodeMask &= ~SG_NODEMASK_CASTSHADOW_BIT;
- nodeMask &= ~SG_NODEMASK_RECIEVESHADOW_BIT;
- nodeMask &= ~SG_NODEMASK_PICK_BIT;
- nodeMask &= ~SG_NODEMASK_TERRAIN_BIT;
- lightGroup->setNodeMask(nodeMask);
-
+ SGBinObject tile;
+ if (!tile.read_bin(path))
+ return false;
+
+ SGVec3d center = tile.get_gbs_center2();
+ SGGeod geodPos = SGGeod::fromCart(center);
+ SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
+
+ // rotate the tiles so that the bounding boxes get nearly axis aligned.
+ // this will help the collision tree's bounding boxes a bit ...
+ std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
+ for (unsigned i = 0; i < nodes.size(); ++i)
+ nodes[i] = hlOr.transform(nodes[i]);
+ tile.set_wgs84_nodes(nodes);
+
+ SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]);
+ std::vector<SGVec3f> normals = tile.get_normals();
+ for (unsigned i = 0; i < normals.size(); ++i)
+ normals[i] = hlOrf.transform(normals[i]);
+ tile.set_normals(normals);
+
+ SGTileGeometryBin tileGeometryBin;
+ if (!tileGeometryBin.insertBinObj(tile, matlib))
+ return false;
+
+ SGVec3f up(0, 0, 1);
+ GroundLightManager* lightManager = GroundLightManager::instance();
+
+ osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
+ osg::ref_ptr<osg::Group> randomObjects;
+ osg::ref_ptr<osg::Group> randomForest;
osg::Group* terrainGroup = new osg::Group;
- osg::ref_ptr<osg::Vec3Array> light_pts = new osg::Vec3Array;
- Point3D center;
- SGBinObjLoad(path, calc_lights, center, matlib, use_random_objects,
- terrainGroup, vasiLights, rwyLights, taxiLights, light_pts.get());
+ osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
+ if (node)
+ terrainGroup->addChild(node);
- if ( light_pts->size() ) {
- osg::Node *lights;
-
- lights = gen_lights( matlib, light_pts.get(), 4, 0.7 );
- groundLights0->addChild( lights );
+ if (use_random_objects || use_random_vegetation) {
+ if (use_random_objects) {
+ if (matlib)
+ tileGeometryBin.computeRandomObjects(matlib);
- lights = gen_lights( matlib, light_pts.get(), 2, 0.85 );
- groundLights1->addChild( lights );
-
- lights = gen_lights( matlib, light_pts.get(), 1, 1.0 );
- groundLights2->addChild( lights );
+ if (tileGeometryBin.randomModels.getNumModels() > 0) {
+ // Generate a repeatable random seed
+ mt seed;
+ mt_init(&seed, unsigned(123));
+
+ std::vector<ModelLOD> models;
+ for (unsigned int i = 0;
+ i < tileGeometryBin.randomModels.getNumModels(); i++) {
+ SGMatModelBin::MatModel obj
+ = tileGeometryBin.randomModels.getMatModel(i);
+ osg::Node* node = sgGetRandomModel(obj.model, seed);
+
+ // Create a matrix to place the object in the correct
+ // location, and then apply the rotation matrix created
+ // above, with an additional random heading rotation if appropriate.
+ osg::Matrix transformMat;
+ transformMat = osg::Matrix::translate(toOsg(obj.position));
+ if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
+ // Rotate the object around the z axis.
+ double hdg = mt_rand(&seed) * M_PI * 2;
+ transformMat.preMult(osg::Matrix::rotate(hdg,
+ osg::Vec3d(0.0, 0.0, 1.0)));
+ }
+ osg::MatrixTransform* position =
+ new osg::MatrixTransform(transformMat);
+ position->addChild(node);
+ models.push_back(ModelLOD(position, obj.lod));
+ }
+ RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
+ quadtree.buildQuadTree(models.begin(), models.end());
+ randomObjects = quadtree.getRoot();
+ randomObjects->setName("random objects");
+ }
+ }
+
+ if (use_random_vegetation && matlib) {
+ // Now add some random forest.
+ tileGeometryBin.computeRandomForest(matlib);
+
+ if (tileGeometryBin.randomForest.getNumTrees() > 0) {
+ randomForest = createForest(tileGeometryBin.randomForest,
+ osg::Matrix::identity());
+ randomForest->setName("random trees");
+ }
+ }
+ }
+
+ if (calc_lights) {
+ // FIXME: ugly, has a side effect
+ if (matlib)
+ tileGeometryBin.computeRandomSurfaceLights(matlib);
+
+ if (tileGeometryBin.tileLights.getNumLights() > 0
+ || tileGeometryBin.randomTileLights.getNumLights() > 0) {
+ osg::Group* groundLights0 = new osg::Group;
+ groundLights0->setStateSet(lightManager->getGroundLightStateSet());
+ groundLights0->setNodeMask(GROUNDLIGHTS0_BIT);
+ osg::Geode* geode = new osg::Geode;
+ geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.tileLights));
+ geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 4, -0.3f));
+ groundLights0->addChild(geode);
+ lightGroup->addChild(groundLights0);
+ }
+ if (tileGeometryBin.randomTileLights.getNumLights() > 0) {
+ osg::Group* groundLights1 = new osg::Group;
+ groundLights1->setStateSet(lightManager->getGroundLightStateSet());
+ groundLights1->setNodeMask(GROUNDLIGHTS1_BIT);
+ osg::Group* groundLights2 = new osg::Group;
+ groundLights2->setStateSet(lightManager->getGroundLightStateSet());
+ groundLights2->setNodeMask(GROUNDLIGHTS2_BIT);
+ osg::Geode* geode = new osg::Geode;
+ geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 2, -0.15f));
+ groundLights1->addChild(geode);
+ lightGroup->addChild(groundLights1);
+ geode = new osg::Geode;
+ geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights));
+ groundLights2->addChild(geode);
+ lightGroup->addChild(groundLights2);
+ }
+ }
+
+ if (!tileGeometryBin.vasiLights.empty()) {
+ EffectGeode* vasiGeode = new EffectGeode;
+ Effect* vasiEffect
+ = getLightEffect(6, osg::Vec3(1, 0.0001, 0.000001), 1, 6, true);
+ vasiGeode->setEffect(vasiEffect);
+ SGVec4f red(1, 0, 0, 1);
+ SGMaterial* mat = 0;
+ if (matlib)
+ mat = matlib->find("RWY_RED_LIGHTS");
+ if (mat)
+ red = mat->get_light_color();
+ SGVec4f white(1, 1, 1, 1);
+ mat = 0;
+ if (matlib)
+ mat = matlib->find("RWY_WHITE_LIGHTS");
+ if (mat)
+ white = mat->get_light_color();
+ SGDirectionalLightListBin::const_iterator i;
+ for (i = tileGeometryBin.vasiLights.begin();
+ i != tileGeometryBin.vasiLights.end(); ++i) {
+ vasiGeode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
+ }
+ vasiGeode->setStateSet(lightManager->getRunwayLightStateSet());
+ lightGroup->addChild(vasiGeode);
+ }
+ Effect* runwayEffect = 0;
+ if (tileGeometryBin.runwayLights.getNumLights() > 0
+ || !tileGeometryBin.rabitLights.empty()
+ || !tileGeometryBin.reilLights.empty()
+ || !tileGeometryBin.odalLights.empty()
+ || tileGeometryBin.taxiLights.getNumLights() > 0)
+ runwayEffect = getLightEffect(4, osg::Vec3(1, 0.001, 0.0002), 1, 4, true);
+ if (tileGeometryBin.runwayLights.getNumLights() > 0
+ || !tileGeometryBin.rabitLights.empty()
+ || !tileGeometryBin.reilLights.empty()
+ || !tileGeometryBin.odalLights.empty()) {
+ osg::Group* rwyLights = new osg::Group;
+ rwyLights->setStateSet(lightManager->getRunwayLightStateSet());
+ rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
+ if (tileGeometryBin.runwayLights.getNumLights() != 0) {
+ EffectGeode* geode = new EffectGeode;
+ geode->setEffect(runwayEffect);
+ geode->addDrawable(SGLightFactory::getLights(tileGeometryBin
+ .runwayLights));
+ rwyLights->addChild(geode);
+ }
+ SGDirectionalLightListBin::const_iterator i;
+ for (i = tileGeometryBin.rabitLights.begin();
+ i != tileGeometryBin.rabitLights.end(); ++i) {
+ rwyLights->addChild(SGLightFactory::getSequenced(*i));
+ }
+ for (i = tileGeometryBin.reilLights.begin();
+ i != tileGeometryBin.reilLights.end(); ++i) {
+ rwyLights->addChild(SGLightFactory::getSequenced(*i));
+ }
+ SGLightListBin::const_iterator j;
+ for (j = tileGeometryBin.odalLights.begin();
+ j != tileGeometryBin.odalLights.end(); ++j) {
+ rwyLights->addChild(SGLightFactory::getOdal(*j));
+ }
+ lightGroup->addChild(rwyLights);
+ }
+
+ if (tileGeometryBin.taxiLights.getNumLights() > 0) {
+ osg::Group* taxiLights = new osg::Group;
+ taxiLights->setStateSet(lightManager->getTaxiLightStateSet());
+ taxiLights->setNodeMask(RUNWAYLIGHTS_BIT);
+ EffectGeode* geode = new EffectGeode;
+ geode->setEffect(runwayEffect);
+ geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.taxiLights));
+ taxiLights->addChild(geode);
+ lightGroup->addChild(taxiLights);
}
// The toplevel transform for that tile.
osg::MatrixTransform* transform = new osg::MatrixTransform;
transform->setName(path);
- transform->setMatrix(osg::Matrix::translate(osg::Vec3d(center[0], center[1], center[2])));
+ transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
+ osg::Matrix::translate(toOsg(center)));
transform->addChild(terrainGroup);
- transform->addChild(lightGroup);
-
+ if (lightGroup->getNumChildren() > 0) {
+ osg::LOD* lightLOD = new osg::LOD;
+ lightLOD->addChild(lightGroup.get(), 0, 30000);
+ // VASI is always on, so doesn't use light bits.
+ lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT);
+ transform->addChild(lightLOD);
+ }
+
+ if (randomObjects.valid() || randomForest.valid()) {
+
+ // Add a LoD node, so we don't try to display anything when the tile center
+ // is more than 20km away.
+ osg::LOD* objectLOD = new osg::LOD;
+
+ if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
+ if (randomForest.valid()) objectLOD->addChild(randomForest.get(), 0, 20000);
+
+ unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
+ objectLOD->setNodeMask(nodeMask);
+ transform->addChild(objectLOD);
+ }
+
return transform;
}