X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Ftgdb%2Fobj.cxx;h=fd6d42e29e13d5c5d6d6f077123622057514e8d4;hb=2cc2a857a2cc2daff30601e0f8f697c768dd5b30;hp=a5bfcda9b346694155499e469e0c0e0786e09e38;hpb=84dd54b33a6d8b35e57c32194b025f79245f35c4;p=simgear.git diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index a5bfcda9..fd6d42e2 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -26,474 +26,763 @@ # include #endif -#include - -#include +#include "obj.hxx" -#include STL_STRING +#include -#include +#include #include #include #include #include +#include +#include +#include +#include -#include +#include #include #include -#include -#include +#include +#include +#include #include #include -#include -#include -#include - -#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, 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(); +#include +#include +#include +#include + +#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 SGMaterialTriangleMap; +typedef std::list SGLightListBin; +typedef std::list 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& 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& vertices, + const std::vector& 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 ); + 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; } - // 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(); - - *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 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]); + } } - 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; + return true; + } + + + static SGVec2f + getTexCoord(const std::vector& 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& vertices, + const std::vector& normals, + const std::vector& 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 bounding radius - *bounding_radius = center->distance3D( cart[0] ); - // cout << "bounding radius = " << t->bounding_radius << 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); + } + } + + static void + addStripGeometry(SGTexturedTriangleBin& triangles, + const std::vector& vertices, + const std::vector& normals, + const std::vector& 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 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 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& vertices, + const std::vector& normals, + const std::vector& 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; } - // 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 ); + 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; } - 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())); + } + + 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; } - - osg::Geometry* geometry = new osg::Geometry; - geometry->setVertexArray(vl); - geometry->setNormalArray(nl); - geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); - geometry->setColorBinding(osg::Geometry::BIND_OFF); - 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); + 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); + } - group->addChild(geode); + 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); + } + 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; -} - - -/** - * 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 -} - - - -//////////////////////////////////////////////////////////////////////// -// 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 ) -{ - SGBinObject obj; - - 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); } - - 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 ); - } + 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)); + + 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 randomPoints; + i->second.addRandomSurfacePoints(coverage, 3, randomPoints); + std::vector::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(); - 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 ); + // 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". - 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); + } + + 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 randomPoints; + i->second.addRandomTreePoints(wood_coverage, + mat->get_tree_density(), + mat->get_wood_size(), + randomPoints); + + std::vector::iterator j; + for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { + randomForest.insert(*j); + } } - - typedef map > 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 ); + } + + 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 randomPoints; + + i->second.addRandomPoints(object->get_coverage_m2(), randomPoints); + std::vector::iterator l; + for (l = randomPoints.begin(); l != randomPoints.end(); ++l) { + randomModels.insert(*l, object, (int)object->get_randomized_range_m(&seed)); + } + } + } + } + } } - 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 ); + } + + bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib) + { + if (!insertPtGeometry(obj, matlib)) + return false; + if (!insertSurfaceGeometry(obj, matlib)) + return false; + return true; + } +}; + +typedef std::pair 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); } - 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 ); +}; +struct GetModelLODCoord { + GetModelLODCoord() {} + GetModelLODCoord(const GetModelLODCoord& rhs) + {} + osg::Vec3 operator() (const ModelLOD& mlod) const + { + return mlod.first->getBound().center(); } +}; - LeafMap::iterator lmi = leafMap.begin(); - while ( lmi != leafMap.end() ) { - list &leaf_list = lmi->second; - list::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; +typedef QuadTreeBuilder 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_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 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 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 lightGroup = new SGOffsetTransform(0.94); + osg::ref_ptr randomObjects; + osg::ref_ptr 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 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)); } - ++lmi; + RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD())); + quadtree.buildQuadTree(models.begin(), models.end()); + randomObjects = quadtree.getRoot(); + randomObjects->setName("random objects"); + } } - return true; + 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::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; }