# include <simgear_config.h>
#endif
-#include <simgear/compiler.h>
-
-#include <list>
+#include "obj.hxx"
-#include STL_STRING
+#include <simgear/compiler.h>
-#include <osg/StateSet>
+#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 <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_types.hxx>
-#include <simgear/misc/texcoord.hxx>
+#include <simgear/math/sg_random.h>
+#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/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;
-};
+#include <simgear/scene/model/SGOffsetTransform.hxx>
+#include <simgear/scene/util/SGUpdateVisitor.hxx>
+#include <simgear/scene/util/SGNodeMasks.hxx>
+#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();
+ }
+
+ 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);
+ }
+
+ 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 {
+ for (unsigned i = 0; i < pts_v.size(); ++i)
+ lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_v[i]], color);
+ }
+ }
+
+ 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;
+ }
+ 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]);
+ }
+ }
-// Generate an ocean tile
-bool SGGenTile( const string& path, SGBucket b,
- Point3D *center, double *bounding_radius,
- SGMaterialLib *matlib, osg::Group* group )
-{
- osg::StateSet *state = 0;
-
- double tex_width = 1000.0;
- // double tex_height;
-
- // 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();
- // tex_height = newmat->get_ysize();
-
- // set ssgState
- state = mat->get_state();
- } else {
- SG_LOG( SG_TERRAIN, SG_ALERT,
- "Ack! unknown usemtl name = " << "Ocean"
- << " in " << path );
+ return true;
+ }
+
+
+ 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);
+ }
+
+ 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 (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;
}
- // Calculate center point
- double clon = b.get_center_lon();
- double clat = b.get_center_lat();
- double height = b.get_height();
- double width = b.get_width();
+ 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);
+ }
+ }
+
+ 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 (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;
+ }
- *center = sgGeodToCart( Point3D(clon*SGD_DEGREES_TO_RADIANS,
- clat*SGD_DEGREES_TO_RADIANS,
- 0.0) );
- // cout << "center = " << center << endl;;
-
- // 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 = 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);
+ }
+ }
+
+ 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;
}
- 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;
+ 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;
+ }
+ }
+
+ 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();
+ }
+
+ 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;
}
- // Calculate bounding radius
- *bounding_radius = center->distance3D( cart[0] );
- // cout << "bounding radius = " << t->bounding_radius << endl;
+ 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);
+ }
- // 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;
+ 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;
+ }
+ 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);
}
- // 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 );
+ 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;
}
- 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()));
+ 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;
+ }
+
+ void computeRandomSurfaceLights(SGMaterialLib* matlib)
+ {
+ SGMaterialTriangleMap::iterator i;
+
+ // generate a repeatable random seed
+ mt seed;
+ mt_init(&seed, unsigned(123));
- 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);
-
+ 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);
+ }
+ }
+ }
+
+ void computeRandomForest(SGMaterialLib* matlib)
+ {
+ SGMaterialTriangleMap::iterator i;
+
+ // 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);
+ }
+ }
+ }
+
+ void computeRandomObjects(SGMaterialLib* matlib)
+ {
+ 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));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
+ {
+ if (!insertPtGeometry(obj, matlib))
+ return false;
+ if (!insertSurfaceGeometry(obj, matlib))
+ return false;
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::Node *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
-}
+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;
-\f
-////////////////////////////////////////////////////////////////////////
-// Scenery loaders.
-////////////////////////////////////////////////////////////////////////
-
-// Load an Binary obj file
-bool SGBinObjLoad( const string& path, const bool is_base,
- Point3D *center,
- double *bounding_radius,
- SGMaterialLib *matlib,
- bool use_random_objects,
- osg::Group *geometry,
- osg::Group *vasi_lights,
- osg::Group *rwy_lights,
- osg::Group *taxi_lights,
- osg::Vec3Array *ground_lights )
+osg::Node*
+SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects, bool use_random_vegetation)
{
- SGBinObject obj;
-
- if ( ! obj.read_bin( path ) ) {
- return false;
- }
-
- osg::Group *local_terrain = new osg::Group;
- local_terrain->setName( "LocalTerrain" );
- geometry->addChild( local_terrain );
-
- geometry->setName(path);
-
- // reference point (center offset/bounding sphere)
- *center = obj.get_gbs_center();
- *bounding_radius = obj.get_gbs_radius();
-
- point_list const& nodes = obj.get_wgs84_nodes();
- // point_list const& colors = obj.get_colors();
- 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;
-
- // 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
- material = pt_materials[i];
- tex_index.clear();
- osg::Node *leaf = SGMakeLeaf( path, GL_POINTS, matlib, material,
- nodes, normals, texcoords,
- pts_v[i], pts_n[i], tex_index,
- false, ground_lights );
- local_terrain->addChild( leaf );
+ 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::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
+ if (node)
+ terrainGroup->addChild(node);
+
+ if (use_random_objects || use_random_vegetation) {
+ if (use_random_objects) {
+ if (matlib)
+ tileGeometryBin.computeRandomObjects(matlib);
+
+ 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");
+ }
}
- // 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);
+ 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);
}
-
- 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 );
+ 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);
}
- 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 (!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));
}
- 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 );
+ 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);
}
-
- LeafMap::iterator lmi = leafMap.begin();
- while ( lmi != leafMap.end() ) {
- 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::Node *leaf = SGMakeLeaf( path, GL_TRIANGLES, matlib,
- tri_materials[ind],
- nodes, normals, texcoords,
- tris_v[ind], tris_n[ind], tris_tc[ind],
- is_base, ground_lights );
- if ( use_random_objects ) {
- SGMaterial *mat = matlib->find( tri_materials[ind] );
- if ( mat == NULL ) {
- SG_LOG( SG_INPUT, SG_ALERT,
- "Unknown material for random surface objects = "
- << tri_materials[ind] );
- } else {
- gen_random_surface_objects( leaf, random_object_branch,
- center, mat );
- }
- }
- local_terrain->addChild( leaf );
- } else if ( leaf.type == GL_TRIANGLE_STRIP ) {
- osg::Node *leaf = SGMakeLeaf( path, GL_TRIANGLE_STRIP,
- matlib, strip_materials[ind],
- nodes, normals, texcoords,
- strips_v[ind], strips_n[ind], strips_tc[ind],
- is_base, ground_lights );
- if ( use_random_objects ) {
- SGMaterial *mat = matlib->find( strip_materials[ind] );
- if ( mat == NULL ) {
- SG_LOG( SG_INPUT, SG_ALERT,
- "Unknown material for random surface objects = "
- << strip_materials[ind] );
- } else {
- gen_random_surface_objects( leaf, random_object_branch,
- center, mat );
- }
- }
- local_terrain->addChild( leaf );
- } else {
- osg::Node *leaf = SGMakeLeaf( path, GL_TRIANGLE_FAN,
- matlib, fan_materials[ind],
- nodes, normals, texcoords,
- fans_v[ind], fans_n[ind], fans_tc[ind],
- is_base, ground_lights );
- if ( use_random_objects ) {
- SGMaterial *mat = matlib->find( fan_materials[ind] );
- if ( mat == NULL ) {
- SG_LOG( SG_INPUT, SG_ALERT,
- "Unknown material for random surface objects = "
- << fan_materials[ind] );
- } else {
- gen_random_surface_objects( leaf, random_object_branch,
- center, mat );
- }
- }
- local_terrain->addChild( leaf );
- }
- ++li;
- }
- ++lmi;
+ SGDirectionalLightListBin::const_iterator i;
+ for (i = tileGeometryBin.rabitLights.begin();
+ i != tileGeometryBin.rabitLights.end(); ++i) {
+ rwyLights->addChild(SGLightFactory::getSequenced(*i));
}
-
- return true;
+ 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::rotate(toOsg(hlOr))*
+ osg::Matrix::translate(toOsg(center)));
+ transform->addChild(terrainGroup);
+ 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;
}