From 5bc535a8d2dfa5e93a58f7fbc47f38d9836a3d46 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Fri, 6 Dec 2013 20:20:46 +0000 Subject: [PATCH] PagedLOD for random trees, buildings and objects. --- simgear/scene/tgdb/SGBuildingBin.cxx | 369 ++++++------ simgear/scene/tgdb/SGBuildingBin.hxx | 124 ++-- simgear/scene/tgdb/obj.cxx | 863 +++++++++++++++------------ 3 files changed, 712 insertions(+), 644 deletions(-) diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index 8a29f35b..3149f844 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -61,13 +61,13 @@ using namespace osg; namespace simgear { - + typedef std::map > BuildingStateSetMap; static BuildingStateSetMap statesetmap; typedef std::map > EffectMap; static EffectMap buildingEffectMap; - + // Building instance scheme: // vertex - local position of vertices, with 0,0,0 being the center front. // fog coord - rotation @@ -88,7 +88,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const const Geometry* geom = static_cast(&drawable); const Vec3Array* v = static_cast(geom->getVertexArray()); const Vec4Array* pos = static_cast(geom->getColorArray()); - + Geometry::PrimitiveSetList primSets = geom->getPrimitiveSetList(); for (Geometry::PrimitiveSetList::const_iterator psitr = primSets.begin(), psend = primSets.end(); psitr != psend; @@ -104,132 +104,132 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const bb.expandBy(pt); } } - return bb; + return bb; } // Set up the building set based on the material definitions SGBuildingBin::SGBuildingBin(const SGMaterial *mat) { - - material_name = mat->get_names()[0]; + + material_name = new std::string(mat->get_names()[0]); SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << material_name); - texture = mat->get_building_texture(); - lightMap = mat->get_building_lightmap(); + texture = new std::string(mat->get_building_texture()); + lightMap = new std::string(mat->get_building_lightmap()); SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << texture); - + // Generate a random seed for the building generation. mt seed; mt_init(&seed, unsigned(123)); - + smallSharedGeometry = new osg::Geometry(); mediumSharedGeometry = new osg::Geometry(); largeSharedGeometry = new osg::Geometry(); - + smallBuildingMaxRadius = std::max(mat->get_building_small_max_depth() * 0.5, mat->get_building_small_max_width() * 0.5); mediumBuildingMaxRadius = std::max(mat->get_building_medium_max_depth() * 0.5, mat->get_building_medium_max_width() * 0.5); largeBuildingMaxRadius = std::max(mat->get_building_large_max_depth() * 0.5, mat->get_building_large_max_width() * 0.5); - + smallBuildingMaxDepth = mat->get_building_small_max_depth(); mediumBuildingMaxDepth = mat->get_building_medium_max_depth(); - largeBuildingMaxDepth = mat->get_building_large_max_depth(); - + largeBuildingMaxDepth = mat->get_building_large_max_depth(); + smallBuildingFraction = mat->get_building_small_fraction(); mediumBuildingFraction = mat->get_building_medium_fraction(); - + buildingRange = mat->get_building_range(); - + SG_LOG(SG_TERRAIN, SG_DEBUG, "Building fractions " << smallBuildingFraction << " " << mediumBuildingFraction); - - + + // TODO: Reverse this - otherwise we never get any large buildings! - BuildingType types[] = { SGBuildingBin::SMALL, SGBuildingBin::MEDIUM, SGBuildingBin::LARGE }; + BuildingType types[] = { SGBuildingBin::SMALL, SGBuildingBin::MEDIUM, SGBuildingBin::LARGE }; BuildingList lists[] = { SGBuildingBin::smallBuildings, SGBuildingBin::mediumBuildings, SGBuildingBin::largeBuildings }; ref_ptr geometries[] = { smallSharedGeometry, mediumSharedGeometry, largeSharedGeometry }; - + for (int bt=0; bt < 3; bt++) { SGBuildingBin::BuildingType buildingtype = types[bt]; ref_ptr sharedGeometry = geometries[bt]; BuildingList buildings = lists[bt]; - + osg::ref_ptr v = new osg::Vec3Array; osg::ref_ptr t = new osg::Vec2Array; osg::ref_ptr n = new osg::Vec3Array; - + v->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING); t->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING); n->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING); - + sharedGeometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); sharedGeometry->setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX); sharedGeometry->setComputeBoundingBoxCallback(new BuildingBoundingBoxCallback); sharedGeometry->setUseDisplayList(false); - - for (unsigned int j = 0; j < BUILDING_SET_SIZE; j++) { + + for (unsigned int j = 0; j < BUILDING_SET_SIZE; j++) { float width; float depth; int floors; float height; bool pitched; - + if (buildingtype == SGBuildingBin::SMALL) { // Small building width = mat->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_width() - mat->get_building_small_min_width()); depth = mat->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_depth() - mat->get_building_small_min_depth()); floors = SGMisc::round(mat->get_building_small_min_floors() + mt_rand(&seed) * (mat->get_building_small_max_floors() - mat->get_building_small_min_floors())); height = floors * (2.8 + mt_rand(&seed)); - + // Small buildings are never deeper than they are wide. if (depth > width) { depth = width; } - + pitched = (mt_rand(&seed) < mat->get_building_small_pitch()); } else if (buildingtype == SGBuildingBin::MEDIUM) { width = mat->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_width() - mat->get_building_medium_min_width()); depth = mat->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_depth() - mat->get_building_medium_min_depth()); floors = SGMisc::round(mat->get_building_medium_min_floors() + mt_rand(&seed) * (mat->get_building_medium_max_floors() - mat->get_building_medium_min_floors())); height = floors * (2.8 + mt_rand(&seed)); - + while ((height > width) && (floors > mat->get_building_medium_min_floors())) { // Ensure that medium buildings aren't taller than they are wide floors--; - height = floors * (2.8 + mt_rand(&seed)); + height = floors * (2.8 + mt_rand(&seed)); } - - pitched = (mt_rand(&seed) < mat->get_building_medium_pitch()); + + pitched = (mt_rand(&seed) < mat->get_building_medium_pitch()); } else { width = mat->get_building_large_min_width() + mt_rand(&seed) * (mat->get_building_large_max_width() - mat->get_building_large_min_width()); depth = mat->get_building_large_min_depth() + mt_rand(&seed) * (mat->get_building_large_max_depth() - mat->get_building_large_min_depth()); - floors = SGMisc::round(mat->get_building_large_min_floors() + mt_rand(&seed) * (mat->get_building_large_max_floors() - mat->get_building_large_min_floors())); + floors = SGMisc::round(mat->get_building_large_min_floors() + mt_rand(&seed) * (mat->get_building_large_max_floors() - mat->get_building_large_min_floors())); height = floors * (2.8 + mt_rand(&seed)); - pitched = (mt_rand(&seed) < mat->get_building_large_pitch()); + pitched = (mt_rand(&seed) < mat->get_building_large_pitch()); } - - Building building = Building(buildingtype, - width, - depth, - height, + + Building building = Building(buildingtype, + width, + depth, + height, floors, - pitched); - + pitched); + buildings.push_back(building); // Now create an OSG Geometry based on the Building float cw = 0.5f * building.width; float cd = building.depth; float ch = building.height; - + // 0,0,0 is the bottom center of the front - // face, e.g. where the front door would be - + // face, e.g. where the front door would be + // BASEMENT // This exteds 10m below the main section - // Front face + // Front face v->push_back( osg::Vec3( 0, -cw, -10) ); // bottom right v->push_back( osg::Vec3( 0, cw, -10) ); // bottom left v->push_back( osg::Vec3( 0, cw, 0) ); // top left v->push_back( osg::Vec3( 0, -cw, 0) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(1, 0, 0) ); // normal - + // Left face v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom right v->push_back( osg::Vec3( 0, -cw, -10) ); // bottom left @@ -244,10 +244,10 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom left v->push_back( osg::Vec3( -cd, -cw, 0) ); // top left v->push_back( osg::Vec3( -cd, cw, 0) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(-1, 0, 0) ); // normal - + // Right face v->push_back( osg::Vec3( 0, cw, -10) ); // bottom right v->push_back( osg::Vec3( -cd, cw, -10) ); // bottom left @@ -255,18 +255,18 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const v->push_back( osg::Vec3( 0, cw, 0) ); // top right for (int i=0; i<4; ++i) - n->push_back( osg::Vec3(0, 1, 0) ); // normal - + n->push_back( osg::Vec3(0, 1, 0) ); // normal + // MAIN BODY - // Front face + // Front face v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom right v->push_back( osg::Vec3( 0, cw, 0) ); // bottom left v->push_back( osg::Vec3( 0, cw, ch) ); // top left v->push_back( osg::Vec3( 0, -cw, ch) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(1, 0, 0) ); // normal - + // Left face v->push_back( osg::Vec3( -cd, -cw, 0) ); // bottom right v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom left @@ -281,10 +281,10 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const v->push_back( osg::Vec3( -cd, -cw, 0) ); // bottom left v->push_back( osg::Vec3( -cd, -cw, ch) ); // top left v->push_back( osg::Vec3( -cd, cw, ch) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(-1, 0, 0) ); // normal - + // Right face v->push_back( osg::Vec3( 0, cw, 0) ); // bottom right v->push_back( osg::Vec3( -cd, cw, 0) ); // bottom left @@ -293,25 +293,25 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const for (int i=0; i<4; ++i) n->push_back( osg::Vec3(0, 1, 0) ); // normal - + // ROOF - if (building.pitched) { - + if (building.pitched) { + // Front pitched roof v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom right v->push_back( osg::Vec3( 0, cw, ch) ); // bottom left v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top left v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(0.707, 0, 0.707) ); // normal - + // Left pitched roof v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom right v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom left v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(0, -1, 0) ); // normal @@ -320,37 +320,37 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom left v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top right - + for (int i=0; i<4; ++i) - n->push_back( osg::Vec3(-0.707, 0, 0.707) ); // normal + n->push_back( osg::Vec3(-0.707, 0, 0.707) ); // normal // Right pitched roof v->push_back( osg::Vec3( 0, cw, ch) ); // bottom right v->push_back( osg::Vec3( -cd, cw, ch) ); // bottom left v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top left v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(0, 1, 0) ); // normal - } else { - // If the roof isn't pitched, we still generate the + } else { + // If the roof isn't pitched, we still generate the // vertices for simplicity later. - + // Top of the roof v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom right v->push_back( osg::Vec3( 0, cw, ch) ); // bottom left v->push_back( osg::Vec3(-cd, cw, ch) ); // top left v->push_back( osg::Vec3(-cd, -cw, ch) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(0, 0, 1) ); // normal - + // Left non-pitched roof v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom right v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom left v->push_back( osg::Vec3( 0, -cw, ch) ); // top left v->push_back( osg::Vec3( -cd, -cw, ch) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(0, -1, 0) ); // normal @@ -359,25 +359,25 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const v->push_back( osg::Vec3(-cd, -cw, ch) ); // bottom left v->push_back( osg::Vec3(-cd, -cw, ch) ); // top left v->push_back( osg::Vec3(-cd, cw, ch) ); // top right - + for (int i=0; i<4; ++i) - n->push_back( osg::Vec3(1, 0, 0) ); // normal + n->push_back( osg::Vec3(1, 0, 0) ); // normal // Right pitched roof v->push_back( osg::Vec3( 0, cw, ch) ); // bottom right v->push_back( osg::Vec3(-cd, cw, ch) ); // bottom left v->push_back( osg::Vec3(-cd, cw, ch) ); // top left v->push_back( osg::Vec3( 0, cw, ch) ); // top right - + for (int i=0; i<4; ++i) n->push_back( osg::Vec3(0, 1, 0) ); // normal } - + // The 1024x1024 texture is split into 32x16 blocks. // For a small building, each block is 6m wide and 3m high. // For a medium building, each block is 10m wide and 3m high. // For a large building, each block is 20m wide and 3m high - + if (building.type == SGBuildingBin::SMALL) { // Small buildings are represented on the bottom 5 rows of 3 floors int row = ((int) (mt_rand(&seed) * 1000)) % 5; @@ -389,8 +389,8 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const float back_x = 384.0/1024.0 + 32.0 / 1024.0 * round((float) building.depth/ 6.0f); // BASEMENT - uses the baseline texture - for (unsigned int i = 0; i < 16; i++) { - t->push_back( osg::Vec2( left_x, base_y) ); + for (unsigned int i = 0; i < 16; i++) { + t->push_back( osg::Vec2( left_x, base_y) ); } // MAIN BODY // Front @@ -398,19 +398,19 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Left t->push_back( osg::Vec2( front_x, base_y) ); // bottom right t->push_back( osg::Vec2( back_x, base_y) ); // bottom left t->push_back( osg::Vec2( back_x, top_y ) ); // top left t->push_back( osg::Vec2( front_x, top_y ) ); // top right - + // Back (same as front for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Right (same as left for the moment) t->push_back( osg::Vec2( front_x, base_y) ); // bottom right t->push_back( osg::Vec2( back_x, base_y) ); // bottom left @@ -418,32 +418,32 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const t->push_back( osg::Vec2( front_x, top_y ) ); // top right // ROOF - if (building.pitched) { + if (building.pitched) { // Use the entire height of the roof texture - top_y = base_y + 16.0 * 3.0 / 1024.0; + top_y = base_y + 16.0 * 3.0 / 1024.0; left_x = 512/1024.0 + 32.0 / 1024.0 * round(building.width / 6.0f); right_x = 512/1024.0; front_x = 480.0/1024.0; back_x = 512.0/1024.0; - + // Front t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Left t->push_back( osg::Vec2( front_x, base_y) ); // bottom right t->push_back( osg::Vec2( back_x, base_y) ); // bottom left t->push_back( osg::Vec2( back_x, top_y ) ); // top left t->push_back( osg::Vec2( front_x, top_y ) ); // top right - + // Back (same as front for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Right (same as left for the moment) t->push_back( osg::Vec2( front_x, base_y) ); // bottom right t->push_back( osg::Vec2( back_x, base_y) ); // bottom left @@ -454,31 +454,31 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const left_x = 640.0/1024.0; right_x = 512.0/1024.0; // Use the entire height of the roof texture - top_y = base_y + 16.0 * 3.0 / 1024.0; - + top_y = base_y + 16.0 * 3.0 / 1024.0; + // Flat roofs still have 4 surfaces, so we need to set the textures - for (int i=0; i<4; ++i) { + for (int i=0; i<4; ++i) { t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right } } - + } - - if (building.type == SGBuildingBin::MEDIUM) + + if (building.type == SGBuildingBin::MEDIUM) { - int column = ((int) (mt_rand(&seed) * 1000)) % 5; + int column = ((int) (mt_rand(&seed) * 1000)) % 5; float base_y = 288 / 1024.0; float top_y = base_y + 16.0 * (float) building.floors / 1024.0; float left_x = column * 192.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 10.0f); float right_x = column * 192.0 /1024.0; // BASEMENT - uses the baseline texture - for (unsigned int i = 0; i < 16; i++) { - t->push_back( osg::Vec2( left_x, base_y) ); - } + for (unsigned int i = 0; i < 16; i++) { + t->push_back( osg::Vec2( left_x, base_y) ); + } // MAIN BODY // Front @@ -486,19 +486,19 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Left t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Back (same as front for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Right (same as left for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left @@ -506,30 +506,30 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const t->push_back( osg::Vec2( right_x, top_y ) ); // top right // ROOF - if (building.pitched) { + if (building.pitched) { base_y = 288.0/1024.0; top_y = 576.0/1024.0; left_x = 960.0/1024.0; right_x = 1.0; - + // Front t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Left t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Back (same as front for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Right (same as left for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left @@ -541,9 +541,9 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const top_y = 576.0/1024.0; left_x = column * 192.0 /1024.0; right_x = (column + 1)* 192.0 /1024.0; - + // Flat roofs still have 4 surfaces - for (int i=0; i<4; ++i) { + for (int i=0; i<4; ++i) { t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left @@ -554,16 +554,16 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const if (building.type == SGBuildingBin::LARGE) { - int column = ((int) (mt_rand(&seed) * 1000)) % 8; + int column = ((int) (mt_rand(&seed) * 1000)) % 8; float base_y = 576 / 1024.0; float top_y = base_y + 16.0 * (float) building.floors / 1024.0; float left_x = column * 128.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 20.0f); - float right_x = column * 128.0 /1024.0; + float right_x = column * 128.0 /1024.0; // BASEMENT - uses the baseline texture - for (unsigned int i = 0; i < 16; i++) { - t->push_back( osg::Vec2( left_x, base_y) ); - } + for (unsigned int i = 0; i < 16; i++) { + t->push_back( osg::Vec2( left_x, base_y) ); + } // MAIN BODY // Front @@ -571,19 +571,19 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Left t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Back (same as front for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Right (same as left for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left @@ -591,7 +591,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const t->push_back( osg::Vec2( right_x, top_y ) ); // top right // ROOF - if (building.pitched) { + if (building.pitched) { base_y = 896/1024.0; top_y = 1.0; // Front @@ -599,19 +599,19 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Left t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Back (same as front for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left t->push_back( osg::Vec2( right_x, top_y ) ); // top right - + // Right (same as left for the moment) t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left @@ -621,9 +621,9 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const // Flat roof base_y = 896/1024.0; top_y = 1.0; - + // Flat roofs still have 4 surfaces - for (int i=0; i<4; ++i) { + for (int i=0; i<4; ++i) { t->push_back( osg::Vec2( right_x, base_y) ); // bottom right t->push_back( osg::Vec2( left_x, base_y) ); // bottom left t->push_back( osg::Vec2( left_x, top_y ) ); // top left @@ -632,7 +632,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const } } } - + // Set the vertex, texture and normals. Colors will be set per-instance // later. sharedGeometry->setVertexArray(v); @@ -640,88 +640,88 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const sharedGeometry->setNormalArray(n); } } - - void SGBuildingBin::insert(SGVec3f p, float r, BuildingType type) { - + + void SGBuildingBin::insert(SGVec3f p, float r, BuildingType type) { + if (type == SGBuildingBin::SMALL) { - smallBuildingLocations.push_back(BuildingInstance(p, r, &smallBuildings, smallSharedGeometry)); + smallBuildingLocations.push_back(BuildingInstance(p, r, &smallBuildings, smallSharedGeometry)); } - + if (type == SGBuildingBin::MEDIUM) { - mediumBuildingLocations.push_back(BuildingInstance(p, r, &mediumBuildings, mediumSharedGeometry)); + mediumBuildingLocations.push_back(BuildingInstance(p, r, &mediumBuildings, mediumSharedGeometry)); } if (type == SGBuildingBin::LARGE) { - largeBuildingLocations.push_back(BuildingInstance(p, r, &largeBuildings, largeSharedGeometry)); - } + largeBuildingLocations.push_back(BuildingInstance(p, r, &largeBuildings, largeSharedGeometry)); + } } int SGBuildingBin::getNumBuildings() { - return smallBuildingLocations.size() + mediumBuildingLocations.size() + largeBuildingLocations.size(); + return smallBuildingLocations.size() + mediumBuildingLocations.size() + largeBuildingLocations.size(); } - - bool SGBuildingBin::checkMinDist (SGVec3f p, float radius) { + + bool SGBuildingBin::checkMinDist (SGVec3f p, float radius) { BuildingInstanceList::iterator iter; - + float r = (radius + smallBuildingMaxRadius) * (radius + smallBuildingMaxRadius); for (iter = smallBuildingLocations.begin(); iter != smallBuildingLocations.end(); ++iter) { if (iter->getDistSqr(p) < r) { - return false; - } + return false; + } } - + r = (radius + mediumBuildingMaxRadius) * (radius + mediumBuildingMaxRadius); for (iter = mediumBuildingLocations.begin(); iter != mediumBuildingLocations.end(); ++iter) { if (iter->getDistSqr(p) < r) { - return false; - } + return false; + } } - + r = (radius + largeBuildingMaxRadius) * (radius + largeBuildingMaxRadius); for (iter = largeBuildingLocations.begin(); iter != largeBuildingLocations.end(); ++iter) { if (iter->getDistSqr(p) < r) { - return false; - } + return false; + } } - + return true; } - + SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) { - + if (roll < smallBuildingFraction) { - return SGBuildingBin::SMALL; + return SGBuildingBin::SMALL; } - + if (roll < (smallBuildingFraction + mediumBuildingFraction)) { return SGBuildingBin::MEDIUM; } - - return SGBuildingBin::LARGE; + + return SGBuildingBin::LARGE; } float SGBuildingBin::getBuildingMaxRadius(BuildingType type) { - + if (type == SGBuildingBin::SMALL) return smallBuildingMaxRadius; if (type == SGBuildingBin::MEDIUM) return mediumBuildingMaxRadius; if (type == SGBuildingBin::LARGE) return largeBuildingMaxRadius; - + return 0; } - + float SGBuildingBin::getBuildingMaxDepth(BuildingType type) { - + if (type == SGBuildingBin::SMALL) return smallBuildingMaxDepth; if (type == SGBuildingBin::MEDIUM) return mediumBuildingMaxDepth; if (type == SGBuildingBin::LARGE) return largeBuildingMaxDepth; - + return 0; } - + ref_ptr SGBuildingBin::createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options) { ref_ptr effect; - EffectMap::iterator iter = buildingEffectMap.find(texture); + EffectMap::iterator iter = buildingEffectMap.find(*texture); if ((iter == buildingEffectMap.end())|| (!iter->second.lock(effect))) @@ -731,27 +731,27 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const SGPropertyNode* params = makeChild(effectProp, "parameters"); // Main texture - n=0 params->getChild("texture", 0, true)->getChild("image", 0, true) - ->setStringValue(texture); + ->setStringValue(*texture); // Light map - n=3 params->getChild("texture", 3, true)->getChild("image", 0, true) - ->setStringValue(lightMap); - + ->setStringValue(*lightMap); + effect = makeEffect(effectProp, true, options); if (iter == buildingEffectMap.end()) - buildingEffectMap.insert(EffectMap::value_type(texture, effect)); + buildingEffectMap.insert(EffectMap::value_type(*texture, effect)); else iter->second = effect; // update existing, but empty observer } - + ref_ptr group = new osg::Group(); - - // Now, create a quadbuilding for the buildings. - - BuildingInstanceList locs[] = { smallBuildingLocations, - SGBuildingBin::mediumBuildingLocations, + + // Now, create a quadbuilding for the buildings. + + BuildingInstanceList locs[] = { smallBuildingLocations, + SGBuildingBin::mediumBuildingLocations, SGBuildingBin::largeBuildingLocations }; - + for (int i = 0; i < 3; i++) { // Create a quad tree. Only small and medium buildings are faded out. @@ -759,7 +759,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(), SG_BUILDING_QUAD_TREE_DEPTH, MakeBuildingLeaf(buildingRange, effect, (i != 2))); - + // Transform building positions from the "geocentric" positions we // get from the scenery polys into the local Z-up coordinate // system. @@ -773,15 +773,15 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const for (size_t j = 0; j < quadbuilding.getRoot()->getNumChildren(); ++j) group->addChild(quadbuilding.getRoot()->getChild(j)); } - + return group; } - + // We may end up with a quadtree with many empty leaves. One might say // that we should avoid constructing the leaves in the first place, // but this node visitor tries to clean up after the fact. struct QuadTreeCleaner : public osg::NodeVisitor - { + { QuadTreeCleaner() : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN) { } @@ -815,31 +815,32 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const } } }; - + // This actually returns a MatrixTransform node. If we rotate the whole // forest into the local Z-up coordinate system we can reuse the // primitive building geometry for all the forests of the same type. - osg::Group* createRandomBuildings(SGBuildingBinList buildings, const osg::Matrix& transform, + osg::Group* createRandomBuildings(SGBuildingBinList& buildings, const osg::Matrix& transform, const SGReaderWriterOptions* options) { Matrix transInv = Matrix::inverse(transform); static Matrix ident; // Set up some shared structures. MatrixTransform* mt = new MatrixTransform(transform); + SGBuildingBinList::iterator i; - SGBuildingBin* bin = NULL; - - BOOST_FOREACH(bin, buildings) - { + for (i = buildings.begin(); i != buildings.end(); ++i) { + SGBuildingBin* bin = *i; ref_ptr group = bin->createBuildingsGroup(transInv, options); - - for (size_t i = 0; i < group->getNumChildren(); ++i) - mt->addChild(group->getChild(i)); - - delete bin; - } - + + for (size_t j = 0; j < group->getNumChildren(); ++j) { + mt->addChild(group->getChild(j)); + } + + delete bin; + } + buildings.clear(); + QuadTreeCleaner cleaner; mt->accept(cleaner); return mt; diff --git a/simgear/scene/tgdb/SGBuildingBin.hxx b/simgear/scene/tgdb/SGBuildingBin.hxx index 43a4fcd5..23d47fcb 100644 --- a/simgear/scene/tgdb/SGBuildingBin.hxx +++ b/simgear/scene/tgdb/SGBuildingBin.hxx @@ -56,9 +56,9 @@ class SGBuildingBin { public: // Number of buildings to auto-generate. Individual - // building instances are taken from this set. + // building instances are taken from this set. static const unsigned int BUILDING_SET_SIZE = 200; - + static const unsigned int QUADS_PER_BUILDING = 12; static const unsigned int VERTICES_PER_BUILDING = 4 * QUADS_PER_BUILDING; static const unsigned int VERTICES_PER_BUILDING_SET = BUILDING_SET_SIZE * VERTICES_PER_BUILDING; @@ -66,21 +66,21 @@ public: enum BuildingType { SMALL = 0, MEDIUM, - LARGE }; - + LARGE }; + private: struct Building { Building(BuildingType t, float w, float d, float h, int f, bool pitch) : - type(t), - width(w), - depth(d), - height(h), + type(t), + width(w), + depth(d), + height(h), floors(f), - pitched(pitch), + pitched(pitch), radius(std::max(d, 0.5f*w)) { } - + BuildingType type; float width; float depth; @@ -88,22 +88,22 @@ private: int floors; bool pitched; float radius; - + float getFootprint() { return radius; } }; - + // The set of buildings that are instantiated typedef std::vector BuildingList; BuildingList smallBuildings; BuildingList mediumBuildings; BuildingList largeBuildings; - - std::string material_name; - std::string texture; - std::string lightMap; - + + std::string* material_name; + std::string* texture; + std::string* lightMap; + // Fraction of buildings of this type float smallBuildingFraction; float mediumBuildingFraction; @@ -112,20 +112,20 @@ private: float smallBuildingMaxRadius; float mediumBuildingMaxRadius; float largeBuildingMaxRadius; - + // The maximum depth of each building type float smallBuildingMaxDepth; float mediumBuildingMaxDepth; float largeBuildingMaxDepth; - + // Visibility range for buildings float buildingRange; - + // Shared geometries of the building set ref_ptr smallSharedGeometry; ref_ptr mediumSharedGeometry; ref_ptr largeSharedGeometry; - + struct BuildingInstance { BuildingInstance(SGVec3f p, float r, const BuildingList* bl, ref_ptr sg) : position(p), @@ -133,26 +133,26 @@ private: buildingList(bl), sharedGeometry(sg) { } - + BuildingInstance(SGVec3f p, BuildingInstance b) : position(p), rotation(b.rotation), buildingList(b.buildingList), sharedGeometry(b.sharedGeometry) - { } - + { } + SGVec3f position; float rotation; - + // References to allow the QuadTreeBuilder to work const BuildingList* buildingList; - ref_ptr sharedGeometry; - + ref_ptr sharedGeometry; + SGVec3f getPosition() { return position; } float getRotation() { return rotation; } - + float getDistSqr(SGVec3f p) { - return distSqr(p, position); + return distSqr(p, position); } const osg::Vec4f getColorValue() { @@ -161,42 +161,42 @@ private: }; // Information for an instance of a building - position and orientation - typedef std::vector BuildingInstanceList; + typedef std::vector BuildingInstanceList; BuildingInstanceList smallBuildingLocations; BuildingInstanceList mediumBuildingLocations; BuildingInstanceList largeBuildingLocations; - -public: + +public: SGBuildingBin(const SGMaterial *mat); - + ~SGBuildingBin() { - smallBuildings.clear(); + smallBuildings.clear(); mediumBuildings.clear(); largeBuildings.clear(); smallBuildingLocations.clear(); mediumBuildingLocations.clear(); largeBuildingLocations.clear(); } - + void insert(SGVec3f p, float r, BuildingType type); int getNumBuildings(); - + bool checkMinDist (SGVec3f p, float radius); - - std::string getMaterialName() { return material_name; } - + + std::string* getMaterialName() { return material_name; } + BuildingType getBuildingType(float roll); - + float getBuildingMaxRadius(BuildingType); float getBuildingMaxDepth(BuildingType); - + // Helper classes for creating the quad tree struct MakeBuildingLeaf { MakeBuildingLeaf(float range, Effect* effect, bool fade) : _range(range), _effect(effect), _fade_out(fade) {} - + MakeBuildingLeaf(const MakeBuildingLeaf& rhs) : _range(rhs._range), _effect(rhs._effect), _fade_out(rhs._fade_out) {} @@ -204,30 +204,30 @@ public: LOD* operator() () const { LOD* result = new LOD; - - if (_fade_out) { + + if (_fade_out) { // Create a series of LOD nodes so buidling cover decreases // gradually with distance from _range to 2*_range for (float i = 0.0; i < SG_BUILDING_FADE_OUT_LEVELS; i++) - { + { EffectGeode* geode = new EffectGeode; geode->setEffect(_effect.get()); - result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0))); + result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0))); } } else { // No fade-out, so all are visible for 2X range EffectGeode* geode = new EffectGeode; geode->setEffect(_effect.get()); - result->addChild(geode, 0, 2.0 * _range); + result->addChild(geode, 0, 2.0 * _range); } return result; } - + float _range; ref_ptr _effect; bool _fade_out; }; - + struct AddBuildingLeafObject { Geometry* createNewBuildingGeometryInstance(const BuildingInstance& building) const @@ -237,32 +237,32 @@ public: geom->setColorBinding(Geometry::BIND_PER_VERTEX); geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS)); return geom; - } - + } + void operator() (LOD* lod, const BuildingInstance& building) const { Geode* geode = static_cast(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren())); - unsigned int numDrawables = geode->getNumDrawables(); - + unsigned int numDrawables = geode->getNumDrawables(); + // Get the last geometry of to be added and check if there is space for // another building instance within it. This is done by checking // if the number of Color values matches the number of vertices. // The color array is used to store the position of a particular // instance. Geometry* geom; - + if (numDrawables == 0) { // Create a new copy of the shared geometry to instantiate geom = createNewBuildingGeometryInstance(building); geode->addDrawable(geom); } else { - geom = static_cast(geode->getDrawable(numDrawables - 1)); + geom = static_cast(geode->getDrawable(numDrawables - 1)); } - + // Check if this building is too close to any other others. DrawArrays* primSet = static_cast(geom->getPrimitiveSet(0)); Vec4Array* posArray = static_cast(geom->getColorArray()); - + // Now check if this geometry is full. if (posArray->size() >= static_cast(geom->getVertexArray())->size()) { // This particular geometry is full, so we generate another @@ -272,7 +272,7 @@ public: posArray = static_cast(geom->getColorArray()); SG_LOG(SG_TERRAIN, SG_DEBUG, "Added new geometry to building geod: " << geode->getNumDrawables()); } - + // We now have a geometry with space for this new building. // Set the position and rotation osg::Vec4f c = osg::Vec4f(toOsg(building.position), building.rotation); @@ -293,7 +293,7 @@ public: typedef QuadTreeBuilder BuildingGeometryQuadtree; - + struct BuildingInstanceTransformer { BuildingInstanceTransformer(Matrix& mat_) : mat(mat_) {} @@ -304,8 +304,8 @@ public: } Matrix mat; }; - - ref_ptr createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options); + + ref_ptr createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options); }; @@ -313,7 +313,7 @@ public: typedef std::list SGBuildingBinList; -osg::Group* createRandomBuildings(SGBuildingBinList buildinglist, const osg::Matrix& transform, +osg::Group* createRandomBuildings(SGBuildingBinList& buildinglist, const osg::Matrix& transform, const SGReaderWriterOptions* options); } #endif diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index b388d4e7..5b484060 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,7 @@ #include #include #include +#include #include "SGTexturedTriangleBin.hxx" #include "SGLightBin.hxx" @@ -77,7 +79,8 @@ typedef std::map SGMaterialTriangleMap; typedef std::list SGLightListBin; typedef std::list SGDirectionalLightListBin; -struct SGTileGeometryBin { +class SGTileGeometryBin : public osg::Referenced { +public: SGMaterialTriangleMap materialTriangleMap; SGLightBin tileLights; SGLightBin randomTileLights; @@ -281,7 +284,7 @@ struct SGTileGeometryBin { triangles.insert(v0, v1, v2); } } - + static void addFanGeometry(SGTexturedTriangleBin& triangles, const std::vector& vertices, @@ -418,11 +421,11 @@ struct SGTileGeometryBin { 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) @@ -436,7 +439,7 @@ struct SGTileGeometryBin { << coverage << ", pushing up to 10000"); coverage = 10000; } - + std::vector randomPoints; i->second.addRandomSurfacePoints(coverage, 3, mat->get_object_mask(i->second), randomPoints); std::vector::iterator j; @@ -465,50 +468,47 @@ struct SGTileGeometryBin { } } } - + void computeRandomObjectsAndBuildings( - SGMaterialLib* matlib, - float building_density, - bool use_random_objects, + SGMaterialLib* matlib, + float building_density, + bool use_random_objects, bool use_random_buildings) { 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); SGTexturedTriangleBin triangleBin = i->second; - + if (!mat) continue; osg::Texture2D* object_mask = mat->get_object_mask(triangleBin); + osg::Image* img; + if (object_mask != NULL) { + img = object_mask->getImage(); + } + int group_count = mat->get_object_group_count(); - float building_coverage = mat->get_building_coverage(); + float building_coverage = mat->get_building_coverage(); float cos_zero_density_angle = mat->get_cos_object_zero_density_slope_angle(); float cos_max_density_angle = mat->get_cos_object_max_density_slope_angle(); - - bool found = false; - SGBuildingBin* bin = NULL; - - if (building_coverage > 0) { - BOOST_FOREACH(bin, randomBuildings) - { - if (bin->getMaterialName() == mat->get_names()[0]) { - found = true; - break; - } - } - - if (!found) { - bin = new SGBuildingBin(mat); - randomBuildings.push_back(bin); - } - } - + + if (building_coverage == 0) + continue; + + SGBuildingBin* bin; + + if (building_coverage > 0) { + bin = new SGBuildingBin(mat); + randomBuildings.push_back(bin); + } + unsigned num = i->second.getNumTriangles(); int random_dropped = 0; int mask_dropped = 0; @@ -517,7 +517,7 @@ struct SGTileGeometryBin { for (unsigned i = 0; i < num; ++i) { SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i); - + SGVec3f vorigin = triangleBin.getVertex(triangleRef[0]).vertex; SGVec3f v0 = triangleBin.getVertex(triangleRef[1]).vertex - vorigin; SGVec3f v1 = triangleBin.getVertex(triangleRef[2]).vertex - vorigin; @@ -525,49 +525,49 @@ struct SGTileGeometryBin { SGVec2f t0 = triangleBin.getVertex(triangleRef[1]).texCoord - torigin; SGVec2f t1 = triangleBin.getVertex(triangleRef[2]).texCoord - torigin; SGVec3f normal = cross(v0, v1); - + // Ensure the slope isn't too steep by checking the // cos of the angle between the slope normal and the // vertical (conveniently the z-component of the normalized - // normal) and values passed in. + // normal) and values passed in. float cos = normalize(normal).z(); float slope_density = 1.0; if (cos < cos_zero_density_angle) continue; // Too steep for any objects if (cos < cos_max_density_angle) { - slope_density = - (cos - cos_zero_density_angle) / + slope_density = + (cos - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle); } - + // Containers to hold the random buildings and objects generated // for this triangle for collision detection purposes. std::vector< std::pair< SGVec3f, float> > triangleObjectsList; std::vector< std::pair< SGVec3f, float> > triangleBuildingList; - + // Compute the area float area = 0.5f*length(normal); if (area <= SGLimitsf::min()) continue; - // Generate any random objects + // Generate any random objects if (use_random_objects && (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) continue; - + // 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); - + // Determine the number of objecst to place, taking into account // the slope density factor. double n = slope_density * area / object->get_coverage_m2(); - + // Use the zombie door method to determine fractional object placement. n = n + mt_rand(&seed); @@ -582,85 +582,83 @@ struct SGTileGeometryBin { SGVec3f randomPoint = vorigin + a*v0 + b*v1; float rotation = static_cast(mt_rand(&seed)); - + // Check that the point is sufficiently far from // the edge of the triangle by measuring the distance - // from the three lines that make up the triangle. + // from the three lines that make up the triangle. float spacing = object->get_spacing_m(); - - SGVec3f p = randomPoint - vorigin; + + SGVec3f p = randomPoint - vorigin; float edges[] = { length(cross(p , p - v0)) / length(v0), length(cross(p - v0, p - v1)) / length(v1 - v0), length(cross(p - v1, p )) / length(v1) }; - float edge_dist = *std::min_element(edges, edges + 3); - - if (edge_dist < spacing) { + float edge_dist = *std::min_element(edges, edges + 3); + + if (edge_dist < spacing) { n -= 1.0; - continue; - } - + continue; + } + if (object_mask != NULL) { SGVec2f texCoord = torigin + a*t0 + b*t1; - + // Check this random point against the object mask - // blue (for buildings) channel. - osg::Image* img = object_mask->getImage(); + // blue (for buildings) channel. + osg::Image* img = object_mask->getImage(); unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); - - if (mt_rand(&seed) > img->getColor(x, y).b()) { + + if (mt_rand(&seed) > img->getColor(x, y).b()) { // Failed object mask check n -= 1.0; - continue; + continue; } - + rotation = img->getColor(x,y).r(); } - + bool close = false; // Check it isn't too close to any other random objects in the triangle std::vector >::iterator l; for (l = triangleObjectsList.begin(); l != triangleObjectsList.end(); ++l) { - float min_dist2 = (l->second + object->get_spacing_m()) * + float min_dist2 = (l->second + object->get_spacing_m()) * (l->second + object->get_spacing_m()); - + if (distSqr(l->first, randomPoint) > min_dist2) { close = true; continue; } } - + if (!close) { triangleObjectsList.push_back(std::make_pair(randomPoint, object->get_spacing_m())); - randomModels.insert(randomPoint, - object, - (int)object->get_randomized_range_m(&seed), + randomModels.insert(randomPoint, + object, + (int)object->get_randomized_range_m(&seed), rotation); } - - n -= 1.0; } } } - } - + } + // Random objects now generated. Now generate the random buildings (if any); if (use_random_buildings && (building_coverage > 0) && (building_density > 0)) { - + // Calculate the number of buildings, taking into account building density (which is linear) - // and the slope density factor. + // and the slope density factor. double num = building_density * building_density * slope_density * area / building_coverage; - + // For partial units of area, use a zombie door method to // create the proper random chance of an object being created // for this triangle. num = num + mt_rand(&seed); if (num < 1.0f) { - continue; + continue; } - + // Cosine of the angle between the two vectors. float cosine = (dot(v0, v1) / (length(v0) * length(v1))); @@ -668,137 +666,133 @@ struct SGTileGeometryBin { // coverage will result. float stepv0 = (sqrtf(building_coverage) / building_density) / length(v0) / sqrtf(1 - cosine * cosine); float stepv1 = (sqrtf(building_coverage) / building_density) / length(v1); - + stepv0 = std::min(stepv0, 1.0f); stepv1 = std::min(stepv1, 1.0f); - + // Start at a random point. a will be immediately incremented below. - float a = -mt_rand(&seed) * stepv0; + float a = -mt_rand(&seed) * stepv0; float b = mt_rand(&seed) * stepv1; // Place an object each unit of area while (num > 1.0) { + num -= 1.0; - // Set the next location to place a building + // Set the next location to place a building a += stepv0; - + if ((a + b) > 1.0f) { // Reached the end of the scan-line on v0. Reset and increment // scan-line on v1 a = mt_rand(&seed) * stepv0; b += stepv1; } - + if (b > 1.0f) { - // In a degenerate case of a single point, we might be outside the + // In a degenerate case of a single point, we might be outside the // scanline. Note that we need to still ensure that a+b < 1. b = mt_rand(&seed) * stepv1 * (1.0f - a); } - + if ((a + b) > 1.0f ) { // Truly degenerate case - simply choose a random point guaranteed // to fulfil the constraing of a+b < 1. a = mt_rand(&seed); b = mt_rand(&seed) * (1.0f - a); } - + SGVec3f randomPoint = vorigin + a*v0 + b*v1; float rotation = mt_rand(&seed); - + if (object_mask != NULL) { SGVec2f texCoord = torigin + a*t0 + b*t1; - osg::Image* img = object_mask->getImage(); + osg::Image* img = object_mask->getImage(); int x = (int) (img->s() * texCoord.x()) % img->s(); int y = (int) (img->t() * texCoord.y()) % img->t(); - + // In some degenerate cases x or y can be < 1, in which case the mod operand fails while (x < 0) x += img->s(); while (y < 0) y += img->t(); - if (mt_rand(&seed) < img->getColor(x, y).b()) { + if (mt_rand(&seed) < img->getColor(x, y).b()) { // Object passes mask. Rotation is taken from the red channel rotation = img->getColor(x,y).r(); } else { // Fails mask test - try again. mask_dropped++; - num -= 1.0; continue; - } + } } // Check building isn't too close to the triangle edge. float type_roll = mt_rand(&seed); SGBuildingBin::BuildingType buildingtype = bin->getBuildingType(type_roll); float radius = bin->getBuildingMaxRadius(buildingtype); - - // Determine the actual center of the building, by shifting from the + + // Determine the actual center of the building, by shifting from the // center of the front face to the true center. osg::Matrix rotationMat = osg::Matrix::rotate(- rotation * M_PI * 2, osg::Vec3f(0.0, 0.0, 1.0)); SGVec3f buildingCenter = randomPoint + toSG(osg::Vec3f(-0.5 * bin->getBuildingMaxDepth(buildingtype), 0.0, 0.0) * rotationMat); - SGVec3f p = buildingCenter - vorigin; + SGVec3f p = buildingCenter - vorigin; float edges[] = { length(cross(p , p - v0)) / length(v0), length(cross(p - v0, p - v1)) / length(v1 - v0), length(cross(p - v1, p )) / length(v1) }; - float edge_dist = *std::min_element(edges, edges + 3); - - if (edge_dist < radius) { - num -= 1.0; + float edge_dist = *std::min_element(edges, edges + 3); + + if (edge_dist < radius) { triangle_dropped++; - continue; + continue; } - - // Check building isn't too close to random objects and other buildings. + + // Check building isn't too close to random objects and other buildings. bool close = false; std::vector >::iterator iter; - + for (iter = triangleBuildingList.begin(); iter != triangleBuildingList.end(); ++iter) { float min_dist = iter->second + radius; if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) { close = true; continue; - } + } } - + if (close) { - num -= 1.0; building_dropped++; - continue; + continue; } - + for (iter = triangleObjectsList.begin(); iter != triangleObjectsList.end(); ++iter) { float min_dist = iter->second + radius; if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) { close = true; continue; - } + } } - + if (close) { - num -= 1.0; random_dropped++; - continue; - } + continue; + } - std::pair pt = std::make_pair(buildingCenter, radius); + std::pair pt = std::make_pair(buildingCenter, radius); triangleBuildingList.push_back(pt); bin->insert(randomPoint, rotation, buildingtype); - num -= 1.0; } } - + triangleObjectsList.clear(); - triangleBuildingList.clear(); + triangleBuildingList.clear(); } - + SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << ((bin) ? bin->getNumBuildings() : 0)); SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to mask: " << mask_dropped); SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to random object: " << random_dropped); SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to other buildings: " << building_dropped); } } - + void computeRandomForest(SGMaterialLib* matlib, float vegetation_density) { SGMaterialTriangleMap::iterator i; @@ -816,11 +810,11 @@ struct SGTileGeometryBin { float wood_coverage = mat->get_wood_coverage(); if ((wood_coverage <= 0) || (vegetation_density <= 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() ) && @@ -832,7 +826,7 @@ struct SGTileGeometryBin { break; } } - + if (!found) { bin = new TreeBin(); bin->texture = mat->get_tree_texture(); @@ -851,7 +845,7 @@ struct SGTileGeometryBin { mat->get_cos_tree_max_density_slope_angle(), mat->get_cos_tree_zero_density_slope_angle(), randomPoints); - + std::vector::iterator k; for (k = randomPoints.begin(); k != randomPoints.end(); ++k) { bin->insert(*k); @@ -892,293 +886,366 @@ struct GetModelLODCoord { typedef QuadTreeBuilder RandomObjectsQuadtree; -osg::Node* -SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options) -{ - SGBinObject tile; - if (!tile.read_bin(path)) - return NULL; - - SGMaterialLib* matlib = 0; - bool use_random_objects = false; - bool use_random_vegetation = false; - bool use_random_buildings = false; - float vegetation_density = 1.0f; - float building_density = 1.0f; - if (options) { - matlib = options->getMaterialLib(); - SGPropertyNode* propertyNode = options->getPropertyNode().get(); - if (propertyNode) { - use_random_objects - = propertyNode->getBoolValue("/sim/rendering/random-objects", - use_random_objects); - use_random_vegetation - = propertyNode->getBoolValue("/sim/rendering/random-vegetation", - use_random_vegetation); - vegetation_density - = propertyNode->getFloatValue("/sim/rendering/vegetation-density", - vegetation_density); - use_random_buildings - = propertyNode->getBoolValue("/sim/rendering/random-buildings", - use_random_buildings); - building_density - = propertyNode->getFloatValue("/sim/rendering/building-density", - building_density); +class RandomObjectCallback : public OptionsReadFileCallback { +public: + virtual osgDB::ReaderWriter::ReadResult + readNode(const std::string&, const osgDB::Options*) + { + osg::ref_ptr group = new osg::Group; + group->setName("Random Object and Lighting Group"); + group->setDataVariance(osg::Object::STATIC); + + osg::LOD* lightLOD = generateLightingTileObjects(); + if (lightLOD) + group->addChild(lightLOD); + + osg::LOD* objectLOD = generateRandomTileObjects(); + if (objectLOD) + group->addChild(objectLOD); + + return group.release(); } - } - 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 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 NULL; - - SGVec3f up(0, 0, 1); - GroundLightManager* lightManager = GroundLightManager::instance(); - - osg::ref_ptr lightGroup = new SGOffsetTransform(0.94); - osg::ref_ptr randomObjects; - osg::ref_ptr forestNode; - osg::ref_ptr buildingNode; - osg::Group* terrainGroup = new osg::Group; - terrainGroup->setName("BTGTerrainGroup"); - - osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib); - if (node) - terrainGroup->addChild(node); - - if (matlib && (use_random_objects || use_random_buildings)) { - tileGeometryBin.computeRandomObjectsAndBuildings(matlib, - building_density, - use_random_objects, - use_random_buildings); - } + // Generate all the lighting objects for the tile. + osg::LOD* generateLightingTileObjects() + { + SGMaterialLib* matlib; - if (tileGeometryBin.randomModels.getNumModels() > 0) { - // Generate a repeatable random seed - mt seed; - mt_init(&seed, unsigned(123)); + if (_options) + matlib = _options->getMaterialLib(); - std::vector models; - for (unsigned int i = 0; - i < tileGeometryBin.randomModels.getNumModels(); i++) { - SGMatModelBin::MatModel obj - = tileGeometryBin.randomModels.getMatModel(i); - - SGPropertyNode* root = options->getPropertyNode()->getRootNode(); - osg::Node* node = obj.model->get_random_model(root, &seed); - - // Create a matrix to place the object in the correct - // location, and then apply the rotation matrix created - // above, with an additional random (or taken from - // the object mask) 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))); + // FIXME: ugly, has a side effect + if (matlib) + _tileGeometryBin->computeRandomSurfaceLights(matlib); + + GroundLightManager* lightManager = GroundLightManager::instance(); + osg::ref_ptr lightGroup = new SGOffsetTransform(0.94); + SGVec3f up(0, 0, 1); + + 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 (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) { - // Rotate the object around the z axis. - double hdg = - obj.rotation * M_PI * 2; - transformMat.preMult(osg::Matrix::rotate(hdg, - osg::Vec3d(0.0, 0.0, 1.0))); + 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); } - - osg::MatrixTransform* position = - new osg::MatrixTransform(transformMat); - position->setName("positionRandomeModel"); - 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 (! tileGeometryBin.randomBuildings.empty()) { - buildingNode = createRandomBuildings(tileGeometryBin.randomBuildings, osg::Matrix::identity(), - options); - buildingNode->setName("Random buildings"); - } + if (!_tileGeometryBin->vasiLights.empty()) { + EffectGeode* vasiGeode = new EffectGeode; + Effect* vasiEffect + = getLightEffect(24, osg::Vec3(1, 0.0001, 0.000001), 1, 24, 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); + } - if (use_random_vegetation && matlib) { - // Now add some random forest. - tileGeometryBin.computeRandomForest(matlib, vegetation_density); - - if (! tileGeometryBin.randomForest.empty()) { - forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity(), - options); - forestNode->setName("Random trees"); - } - } - - // 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); - } + Effect* runwayEffect = 0; + if (_tileGeometryBin->runwayLights.getNumLights() > 0 + || !_tileGeometryBin->rabitLights.empty() + || !_tileGeometryBin->reilLights.empty() + || !_tileGeometryBin->odalLights.empty() + || _tileGeometryBin->taxiLights.getNumLights() > 0) + runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true); + if (_tileGeometryBin->runwayLights.getNumLights() > 0 + || !_tileGeometryBin->rabitLights.empty() + || !_tileGeometryBin->reilLights.empty() + || !_tileGeometryBin->odalLights.empty() + || !_tileGeometryBin->holdshortLights.empty() + || !_tileGeometryBin->guardLights.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)); + } + for (i = _tileGeometryBin->holdshortLights.begin(); + i != _tileGeometryBin->holdshortLights.end(); ++i) { + rwyLights->addChild(SGLightFactory::getHoldShort(*i)); + } + for (i = _tileGeometryBin->guardLights.begin(); + i != _tileGeometryBin->guardLights.end(); ++i) { + rwyLights->addChild(SGLightFactory::getGuard(*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.vasiLights.empty()) { - EffectGeode* vasiGeode = new EffectGeode; - Effect* vasiEffect - = getLightEffect(24, osg::Vec3(1, 0.0001, 0.000001), 1, 24, 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(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true); - if (tileGeometryBin.runwayLights.getNumLights() > 0 - || !tileGeometryBin.rabitLights.empty() - || !tileGeometryBin.reilLights.empty() - || !tileGeometryBin.odalLights.empty() - || !tileGeometryBin.holdshortLights.empty() - || !tileGeometryBin.guardLights.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)); - } - for (i = tileGeometryBin.holdshortLights.begin(); - i != tileGeometryBin.holdshortLights.end(); ++i) { - rwyLights->addChild(SGLightFactory::getHoldShort(*i)); - } - for (i = tileGeometryBin.guardLights.begin(); - i != tileGeometryBin.guardLights.end(); ++i) { - rwyLights->addChild(SGLightFactory::getGuard(*i)); + 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); + } + + osg::LOD* lightLOD = NULL; + + if (lightGroup->getNumChildren() > 0) { + lightLOD = new osg::LOD; + lightLOD->addChild(lightGroup.get(), 0, 60000); + // VASI is always on, so doesn't use light bits. + lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT | PERMANENTLIGHT_BIT); + } + + return lightLOD; } - SGLightListBin::const_iterator j; - for (j = tileGeometryBin.odalLights.begin(); - j != tileGeometryBin.odalLights.end(); ++j) { - rwyLights->addChild(SGLightFactory::getOdal(*j)); + + // Generate all the random forest, objects and buildings for the tile + osg::LOD* generateRandomTileObjects() + { + SGMaterialLib* matlib; + bool use_random_objects = false; + bool use_random_vegetation = false; + bool use_random_buildings = false; + float vegetation_density = 1.0f; + float building_density = 1.0f; + + osg::ref_ptr randomObjects; + osg::ref_ptr forestNode; + osg::ref_ptr buildingNode; + + if (_options) { + matlib = _options->getMaterialLib(); + SGPropertyNode* propertyNode = _options->getPropertyNode().get(); + if (propertyNode) { + use_random_objects + = propertyNode->getBoolValue("/sim/rendering/random-objects", + use_random_objects); + use_random_vegetation + = propertyNode->getBoolValue("/sim/rendering/random-vegetation", + use_random_vegetation); + vegetation_density + = propertyNode->getFloatValue("/sim/rendering/vegetation-density", + vegetation_density); + use_random_buildings + = propertyNode->getBoolValue("/sim/rendering/random-buildings", + use_random_buildings); + building_density + = propertyNode->getFloatValue("/sim/rendering/building-density", + building_density); + } + } + + + + if (matlib && (use_random_objects || use_random_buildings)) { + _tileGeometryBin->computeRandomObjectsAndBuildings(matlib, + building_density, + use_random_objects, + use_random_buildings); + } + + + 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); + + SGPropertyNode* root = _options->getPropertyNode()->getRootNode(); + osg::Node* node = obj.model->get_random_model(root, &seed); + + // Create a matrix to place the object in the correct + // location, and then apply the rotation matrix created + // above, with an additional random (or taken from + // the object mask) 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))); + } + + if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) { + // Rotate the object around the z axis. + double hdg = - obj.rotation * 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->setName("positionRandomModel"); + 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 (! _tileGeometryBin->randomBuildings.empty()) { + buildingNode = createRandomBuildings(_tileGeometryBin->randomBuildings, osg::Matrix::identity(), + _options); + buildingNode->setName("Random buildings"); + _tileGeometryBin->randomBuildings.clear(); + } + + if (use_random_vegetation && matlib) { + // Now add some random forest. + _tileGeometryBin->computeRandomForest(matlib, vegetation_density); + + if (! _tileGeometryBin->randomForest.empty()) { + forestNode = createForest(_tileGeometryBin->randomForest, osg::Matrix::identity(), + _options); + forestNode->setName("Random trees"); + } + } + + osg::LOD* objectLOD = NULL; + + if (randomObjects.valid() || forestNode.valid() || buildingNode.valid()) { + objectLOD = new osg::LOD; + + if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000); + if (forestNode.valid()) objectLOD->addChild(forestNode.get(), 0, 20000); + if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000); + + unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT; + objectLOD->setNodeMask(nodeMask); + } + + return objectLOD; } - 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 original options to use for this bunch of models + osg::ref_ptr _options; + osg::ref_ptr _tileGeometryBin; +}; - // 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, 60000); - // VASI is always on, so doesn't use light bits. - lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT | PERMANENTLIGHT_BIT); - transform->addChild(lightLOD); - } - - if (randomObjects.valid() || forestNode.valid() || buildingNode.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); - if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000); - - unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT; - objectLOD->setNodeMask(nodeMask); - transform->addChild(objectLOD); - } - transform->setNodeMask( ~simgear::MODELLIGHT_BIT ); - - return transform; +osg::Node* +SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options) +{ + SGBinObject tile; + if (!tile.read_bin(path)) + return NULL; + + SGMaterialLib* matlib = 0; + + if (options) { + matlib = options->getMaterialLib(); + } + + 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 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); + + osg::ref_ptr tileGeometryBin = new SGTileGeometryBin; + + if (!tileGeometryBin->insertBinObj(tile, matlib)) + return NULL; + + osg::Group* terrainGroup = new osg::Group; + terrainGroup->setName("BTGTerrainGroup"); + + osg::Node* node = tileGeometryBin->getSurfaceGeometry(matlib); + if (node) + terrainGroup->addChild(node); + + // 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); + + // PagedLOD for the random objects so we don't need to generate + // them all on tile loading. + osg::PagedLOD* pagedLOD = new osg::PagedLOD; + pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER); + pagedLOD->setName("pagedObjectLOD"); + + // we just need to know about the read file callback that itself holds the data + osg::ref_ptr randomObjectCallback = new RandomObjectCallback; + randomObjectCallback->_options = SGReaderWriterOptions::copyOrCreate(options); + randomObjectCallback->_tileGeometryBin = tileGeometryBin; + + osg::ref_ptr callbackOptions = new osgDB::Options; + callbackOptions->setReadFileCallback(randomObjectCallback.get()); + pagedLOD->setDatabaseOptions(callbackOptions.get()); + + pagedLOD->setFileName(pagedLOD->getNumChildren(), "Dummy name - use the stored data in the read file callback"); + pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 35000); + transform->addChild(pagedLOD); + transform->setNodeMask( ~simgear::MODELLIGHT_BIT ); + + return transform; } -- 2.39.5