//
// Written by Curtis Olson, started October 1997.
//
-// Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com
+// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
-// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
# include <simgear_config.h>
#endif
+#include "obj.hxx"
+
#include <simgear/compiler.h>
-#include STL_STRING
+#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 <boost/foreach.hpp>
-#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"
+#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;
+ SGTreeBinList 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();
+ }
-SG_USING_STD(string);
+ 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);
+ }
+ }
-// Generate an ocean tile
-bool sgGenTile( const string& path, SGBucket b,
- Point3D *center, double *bounding_radius,
- SGMaterialLib *matlib, ssgBranch* geometry )
-{
- ssgSimpleState *state = NULL;
+ 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;
+ }
- geometry->setName( (char *)path.c_str() );
+ 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]);
+ }
+ }
- double tex_width = 1000.0;
- // double tex_height;
+ return true;
+ }
- // 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 );
- }
- // 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();
+ 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);
+ }
- *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() );
+ 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;
}
- 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;
+ 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);
}
+ }
- // Calculate bounding radius
- *bounding_radius = center->distance3D( cart[0] );
- // cout << "bounding radius = " << t->bounding_radius << endl;
-
- // 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;
+ 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;
}
- // 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 );
+ 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);
}
- point_list texs = sgCalcTexCoords( b, geod_nodes, rectangle,
- 1000.0 / tex_width );
-
- // Allocate ssg structure
- ssgVertexArray *vl = new ssgVertexArray( 4 );
- ssgNormalArray *nl = new ssgNormalArray( 4 );
- ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
- ssgColourArray *cl = new ssgColourArray( 1 );
-
- sgVec4 color;
- sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
- cl->add( color );
-
- // sgVec3 *vtlist = new sgVec3 [ 4 ];
- // t->vec3_ptrs.push_back( vtlist );
- // sgVec3 *vnlist = new sgVec3 [ 4 ];
- // t->vec3_ptrs.push_back( vnlist );
- // sgVec2 *tclist = new sgVec2 [ 4 ];
- // t->vec2_ptrs.push_back( tclist );
-
- sgVec2 tmp2;
- sgVec3 tmp3;
- for ( i = 0; i < 4; ++i ) {
- sgSetVec3( tmp3,
- rel[i].x(), rel[i].y(), rel[i].z() );
- vl->add( tmp3 );
-
- sgSetVec3( tmp3,
- normals[i].x(), normals[i].y(), normals[i].z() );
- nl->add( tmp3 );
-
- sgSetVec2( tmp2, texs[i].x(), texs[i].y());
- tl->add( tmp2 );
+ }
+
+ 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;
}
-
- ssgLeaf *leaf =
- new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
-
- leaf->setState( state );
- geometry->addKid( leaf );
-
- 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);
+ 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;
}
- data->is_filled_in = true;
}
- return 1;
-}
+ 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);
-/**
- * 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 material->get_tex_coord_scale();
}
- return 0;
-}
+ 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;
+ }
-/**
- * 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 (ssgLeaf *leaf,
- ssgBranch *branch,
- Point3D *center,
- SGMaterial *mat )
-{
- // 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
- float ranges[] = { 0, 20000, 1000000 };
- ssgRangeSelector * lod = new ssgRangeSelector;
- lod->setRanges(ranges, 3);
- branch->addKid(lod);
-
- // Create the in-range and out-of-range
- // branches.
- ssgBranch * in_range = new ssgBranch;
- ssgBranch * out_of_range = new ssgBranch;
- lod->addKid(in_range);
- lod->addKid(out_of_range);
-
- 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);
- in_range->setTravCallback(SSG_CALLBACK_PRETRAV, leaf_in_range_callback);
- out_of_range->setUserData(data);
- out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
- leaf_out_of_range_callback);
- out_of_range
- ->addKid(new SGDummyBSphereEntity(leaf->getBSphere()->getRadius()));
-}
+ 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);
+ }
+ 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);
+ }
-\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,
- ssgBranch *geometry,
- ssgBranch *vasi_lights,
- ssgBranch *rwy_lights,
- ssgBranch *taxi_lights,
- ssgVertexArray *ground_lights )
-{
- SGBinObject obj;
+ 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;
+ }
- if ( ! obj.read_bin( path ) ) {
- return false;
+ 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;
+ }
- geometry->setName( (char *)path.c_str() );
-
- // 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
- sgVec3 up;
- sgSetVec3( up, center->x(), center->y(), center->z() );
- // returns a transform -> lod -> leaf structure
- ssgBranch *branch = sgMakeDirectionalLights( nodes, normals,
- pts_v[i], pts_n[i],
- matlib,
- pt_materials[i], up );
- if ( pt_materials[i] == "RWY_VASI_LIGHTS" ) {
- vasi_lights->addKid( branch );
- } else if ( pt_materials[i] == "RWY_BLUE_TAXIWAY_LIGHTS"
- || pt_materials[i] == "RWY_GREEN_TAXIWAY_LIGHTS" )
- {
- taxi_lights->addKid( branch );
- } else {
- rwy_lights->addKid( branch );
- }
+ void computeRandomSurfaceLights(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;
+
+ 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 {
- // other geometry
- material = pt_materials[i];
- tex_index.clear();
- ssgLeaf *leaf = sgMakeLeaf( path, GL_POINTS, matlib, material,
- nodes, normals, texcoords,
- pts_v[i], pts_n[i], tex_index,
- false, ground_lights );
- geometry->addKid( leaf );
+ // 5% chance of redish
+ color = SGVec4f(0.9f, 0.2f, 0.2f, bright - factor * 0.2f);
}
+ randomTileLights.insert(*j, color);
+ }
}
+ }
- // Put all randomly-placed objects under a separate branch
- // (actually an ssgRangeSelector) named "random-models".
- ssgBranch * random_object_branch = 0;
- if (use_random_objects) {
- float ranges[] = { 0, 20000 }; // Maximum 20km range for random objects
- ssgRangeSelector * object_lod = new ssgRangeSelector;
- object_lod->setRanges(ranges, 2);
- object_lod->setName("random-models");
- geometry->addKid(object_lod);
- random_object_branch = new ssgBranch;
- object_lod->addKid(random_object_branch);
+ 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 but do vary by material
+ bool found = false;
+ TreeBin* bin = NULL;
+
+ BOOST_FOREACH(bin, randomForest)
+ {
+ if ((bin->texture == mat->get_tree_texture() ) &&
+ (bin->texture_varieties == mat->get_tree_varieties()) &&
+ (bin->range == mat->get_tree_range() ) &&
+ (bin->width == mat->get_tree_width() ) &&
+ (bin->height == mat->get_tree_height() ) ) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ bin = new TreeBin();
+ bin->texture = mat->get_tree_texture();
+ SG_LOG(SG_INPUT, SG_DEBUG, "Tree texture " << bin->texture);
+ bin->range = mat->get_tree_range();
+ bin->width = mat->get_tree_width();
+ bin->height = mat->get_tree_height();
+ bin->texture_varieties = mat->get_tree_varieties();
+ randomForest.push_back(bin);
+ }
+
+ std::vector<SGVec3f> randomPoints;
+ i->second.addRandomTreePoints(wood_coverage,
+ mat->get_tree_density(),
+ mat->get_wood_size(),
+ randomPoints);
+
+ std::vector<SGVec3f>::iterator k;
+ for (k = randomPoints.begin(); k != randomPoints.end(); ++k) {
+ bin->insert(*k);
+ }
}
+ }
- // generate 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 ) {
- ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLES, matlib,
- tri_materials[i],
- nodes, normals, texcoords,
- tris_v[i], tris_n[i], tris_tc[i],
- is_base, ground_lights );
-
- if ( use_random_objects ) {
- SGMaterial *mat = matlib->find( tri_materials[i] );
- if ( mat == NULL ) {
- SG_LOG( SG_INPUT, SG_ALERT,
- "Unknown material for random surface objects = "
- << tri_materials[i] );
- } else {
- gen_random_surface_objects( leaf, random_object_branch,
- center, mat );
+ 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));
+ }
}
+ }
}
- geometry->addKid( leaf );
+ }
}
+ }
- // generate strips
- 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 ) {
- ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_STRIP,
- matlib, strip_materials[i],
- nodes, normals, texcoords,
- strips_v[i], strips_n[i], strips_tc[i],
- is_base, ground_lights );
-
- if ( use_random_objects ) {
- SGMaterial *mat = matlib->find( strip_materials[i] );
- if ( mat == NULL ) {
- SG_LOG( SG_INPUT, SG_ALERT,
- "Unknown material for random surface objects = "
- << strip_materials[i] );
- } else {
- gen_random_surface_objects( leaf, random_object_branch,
- center, mat );
- }
- }
- geometry->addKid( leaf );
+ bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
+ {
+ if (!insertPtGeometry(obj, matlib))
+ return false;
+ if (!insertSurfaceGeometry(obj, matlib))
+ return false;
+ return true;
+ }
+};
+
+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();
}
+};
- // generate fans
- 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 ) {
- ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_FAN,
- matlib, fan_materials[i],
- nodes, normals, texcoords,
- fans_v[i], fans_n[i], fans_tc[i],
- is_base, ground_lights );
- if ( use_random_objects ) {
- SGMaterial *mat = matlib->find( fan_materials[i] );
- if ( mat == NULL ) {
- SG_LOG( SG_INPUT, SG_ALERT,
- "Unknown material for random surface objects = "
- << fan_materials[i] );
- } else {
- gen_random_surface_objects( leaf, random_object_branch,
- center, mat );
- }
+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, bool use_random_vegetation)
+{
+ SGBinObject tile;
+ if (!tile.read_bin(path))
+ return false;
+
+ SGVec3d center = tile.get_gbs_center();
+ 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> forestNode;
+ 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");
+ }
+ }
+
+ if (use_random_vegetation && matlib) {
+ // Now add some random forest.
+ tileGeometryBin.computeRandomForest(matlib);
+
+ if (tileGeometryBin.randomForest.size() > 0) {
+ forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity());
+ forestNode->setName("Random trees");
+ }
+ }
+ }
- geometry->addKid( leaf );
+ 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);
}
+ }
- return true;
+ 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::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() || forestNode.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 (forestNode.valid()) objectLOD->addChild(forestNode.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;
}