From f9bf403fc0026f461090fc55e1e37f28f8359cea Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Tue, 24 Apr 2012 22:00:35 +0100 Subject: [PATCH] Random buildings - initial commit. --- simgear/scene/material/mat.cxx | 44 ++ simgear/scene/material/mat.hxx | 104 +++++ simgear/scene/tgdb/CMakeLists.txt | 1 + simgear/scene/tgdb/SGBuildingBin.cxx | 636 +++++++++++++++++++++++++++ simgear/scene/tgdb/SGBuildingBin.hxx | 110 +++++ simgear/scene/tgdb/obj.cxx | 378 +++++++++++++--- 6 files changed, 1212 insertions(+), 61 deletions(-) create mode 100644 simgear/scene/tgdb/SGBuildingBin.cxx create mode 100644 simgear/scene/tgdb/SGBuildingBin.hxx diff --git a/simgear/scene/material/mat.cxx b/simgear/scene/material/mat.cxx index ec5164bd..2082fd47 100644 --- a/simgear/scene/material/mat.cxx +++ b/simgear/scene/material/mat.cxx @@ -264,6 +264,49 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, wrapv = props->getBoolValue("wrapv", true); mipmap = props->getBoolValue("mipmap", true); light_coverage = props->getDoubleValue("light-coverage", 0.0); + + // Building properties + building_coverage = props->getDoubleValue("building-coverage", 0.0); + building_spacing = props->getDoubleValue("building-spacing-m", 5.0); + + string bt = props->getStringValue("building-texture", "Textures/buildings.png"); + building_texture = SGModelLib::findDataFile(bt, options); + + if (building_texture.empty()) { + SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt); + } + + building_small_ratio = props->getDoubleValue("building-small-ratio", 0.8); + building_medium_ratio = props->getDoubleValue("building-medium-ratio", 0.15); + building_large_ratio = props->getDoubleValue("building-large-ratio", 0.05); + + building_small_pitch = props->getDoubleValue("building-small-pitch", 0.8); + building_medium_pitch = props->getDoubleValue("building-medium-pitch", 0.2); + building_large_pitch = props->getDoubleValue("building-large-pitch", 0.1); + + building_small_min_floors = props->getIntValue("building-small-min-floors", 1); + building_small_max_floors = props->getIntValue("building-small-max-floors", 3); + building_medium_min_floors = props->getIntValue("building-medium-min-floors", 3); + building_medium_max_floors = props->getIntValue("building-medium-max-floors", 8); + building_large_min_floors = props->getIntValue("building-large-min-floors", 5); + building_large_max_floors = props->getIntValue("building-large-max-floors", 20); + + building_small_min_width = props->getFloatValue("building-small-min-width-m", 15.0); + building_small_max_width = props->getFloatValue("building-small-max-width-m", 60.0); + building_small_min_depth = props->getFloatValue("building-small-min-depth-m", 10.0); + building_small_max_depth = props->getFloatValue("building-small-max-depth-m", 20.0); + + building_medium_min_width = props->getFloatValue("building-medium-min-width-m", 25.0); + building_medium_max_width = props->getFloatValue("building-medium-max-width-m", 50.0); + building_medium_min_depth = props->getFloatValue("building-medium-min-depth-m", 20.0); + building_medium_max_depth = props->getFloatValue("building-medium-max-depth-m", 50.0); + + building_large_min_width = props->getFloatValue("building-large-min-width-m", 50.0); + building_large_max_width = props->getFloatValue("building-large-max-width-m", 75.0); + building_large_min_depth = props->getFloatValue("building-large-min-depth-m", 50.0); + building_large_max_depth = props->getFloatValue("building-large-max-depth-m", 75.0); + + // Random vegetation properties wood_coverage = props->getDoubleValue("wood-coverage", 0.0); tree_height = props->getDoubleValue("tree-height-m", 0.0); tree_width = props->getDoubleValue("tree-width-m", 0.0); @@ -358,6 +401,7 @@ SGMaterial::init () mipmap = true; light_coverage = 0.0; + building_coverage = 0.0; solid = true; friction_factor = 1; diff --git a/simgear/scene/material/mat.hxx b/simgear/scene/material/mat.hxx index 292df681..f607f7fb 100644 --- a/simgear/scene/material/mat.hxx +++ b/simgear/scene/material/mat.hxx @@ -145,6 +145,67 @@ public: * @return The area (m^2) covered by each light. */ inline double get_light_coverage () const { return light_coverage; } + + /** + * Get the building coverage. + * + * A smaller number means more generated buildings. + * + * @return The area (m^2) covered by each light. + */ + inline double get_building_coverage () const { return building_coverage; } + + /** + * Get the building spacing. + * + * This is the minimum spacing between buildings + * + * @return The minimum distance between buildings + */ + inline double get_building_spacing () const { return building_spacing; } + + /** + * Get the building texture. + * + * This is the texture used for auto-generated buildings. + * + * @return The texture for auto-generated buildings. + */ + inline std::string get_building_texture () const { return building_texture; } + + // Ratio of the 3 random building sizes + inline double get_building_small_fraction () const { return building_small_ratio / (building_small_ratio + building_medium_ratio + building_large_ratio); } + inline double get_building_medium_fraction () const { return building_medium_ratio / (building_small_ratio + building_medium_ratio + building_large_ratio); } + inline double get_building_large_fraction () const { return building_large_ratio / (building_small_ratio + building_medium_ratio + building_large_ratio); } + + // Proportion of buildings with pitched roofs + inline double get_building_small_pitch () const { return building_small_pitch; } + inline double get_building_medium_pitch () const { return building_medium_pitch; } + inline double get_building_large_pitch () const { return building_large_pitch; } + + // Min/Max number of floors for each size + inline int get_building_small_min_floors () const { return building_small_min_floors; } + inline int get_building_small_max_floors () const { return building_small_max_floors; } + inline int get_building_medium_min_floors () const { return building_medium_min_floors; } + inline int get_building_medium_max_floors () const { return building_medium_max_floors; } + inline int get_building_large_min_floors () const { return building_large_min_floors; } + inline int get_building_large_max_floors () const { return building_large_max_floors; } + + // Minimum width and depth for each size + inline double get_building_small_min_width () const { return building_small_min_width; } + inline double get_building_small_max_width () const { return building_small_max_width; } + inline double get_building_small_min_depth () const { return building_small_min_depth; } + inline double get_building_small_max_depth () const { return building_small_max_depth; } + + inline double get_building_medium_min_width () const { return building_medium_min_width; } + inline double get_building_medium_max_width () const { return building_medium_max_width; } + inline double get_building_medium_min_depth () const { return building_medium_min_depth; } + inline double get_building_medium_max_depth () const { return building_medium_max_depth; } + + inline double get_building_large_min_width () const { return building_large_min_width; } + inline double get_building_large_max_width () const { return building_large_max_width; } + inline double get_building_large_min_depth () const { return building_large_min_depth; } + inline double get_building_large_max_depth () const { return building_large_max_depth; } /** * Get the wood coverage. @@ -317,6 +378,49 @@ private: // coverage of night lighting. double light_coverage; + // coverage of buildings + double building_coverage; + + // building spacing + double building_spacing; + + // building texture + std::string building_texture; + + // Ratio of the 3 random building sizes + double building_small_ratio; + double building_medium_ratio; + double building_large_ratio; + + // Proportion of buildings with pitched roofs + double building_small_pitch; + double building_medium_pitch; + double building_large_pitch; + + // Min/Max number of floors for each size + int building_small_min_floors; + int building_small_max_floors; + int building_medium_min_floors; + int building_medium_max_floors; + int building_large_min_floors; + int building_large_max_floors; + + // Minimum width and depth for each size + double building_small_min_width; + double building_small_max_width; + double building_small_min_depth; + double building_small_max_depth; + + double building_medium_min_width; + double building_medium_max_width; + double building_medium_min_depth; + double building_medium_max_depth; + + double building_large_min_width; + double building_large_max_width; + double building_large_min_depth; + double building_large_max_depth; + // coverage of woods double wood_coverage; diff --git a/simgear/scene/tgdb/CMakeLists.txt b/simgear/scene/tgdb/CMakeLists.txt index 63cce41d..f340727d 100644 --- a/simgear/scene/tgdb/CMakeLists.txt +++ b/simgear/scene/tgdb/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES GroundLightManager.cxx ReaderWriterSPT.cxx ReaderWriterSTG.cxx + SGBuildingBin.cxx SGOceanTile.cxx SGReaderWriterBTG.cxx SGVasiDrawable.cxx diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx new file mode 100644 index 00000000..51694bae --- /dev/null +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -0,0 +1,636 @@ +/* -*-c++-*- + * + * Copyright (C) 2012 Stuart Buchanan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#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 +#include + + +#include "ShaderGeometry.hxx" +#include "SGBuildingBin.hxx" + +#define SG_BUILDING_QUAD_TREE_DEPTH 3 +#define SG_BUILDING_FADE_OUT_LEVELS 10 + +using namespace osg; + +namespace simgear +{ + +typedef std::map > BuildingStateSetMap; +static BuildingStateSetMap statesetmap; + +void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& building) +{ + // Generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(building.position.x())); + + // Get or create geometry. + osg::ref_ptr geom; + osg::Vec3Array* v = new osg::Vec3Array; + osg::Vec2Array* t = new osg::Vec2Array; + + // Color and Normal will be per QUAD + osg::Vec4Array* c = new osg::Vec4Array; + osg::Vec3Array* n = new osg::Vec3Array; + + if (geode->getNumDrawables() == 0) { + geom = new osg::Geometry; + v = new osg::Vec3Array; + t = new osg::Vec2Array; + c = new osg::Vec4Array; + n = new osg::Vec3Array; + + // Set the color, which is bound overall, and simply white + c->push_back( osg::Vec4( 1, 1, 1, 1) ); + geom->setColorArray(c); + geom->setColorBinding(osg::Geometry::BIND_OVERALL); + + geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE); + // Temporary primitive set. Will be over-written later. + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,1)); + geode->addDrawable(geom); + } else { + geom = (osg::Geometry*) geode->getDrawable(0); + v = (osg::Vec3Array*) geom->getVertexArray(); + t = (osg::Vec2Array*) geom->getTexCoordArray(0); + c = (osg::Vec4Array*) geom->getColorArray(); + n = (osg::Vec3Array*) geom->getNormalArray(); + } + + // For the moment we'll create a simple box with 5 sides (no need + // for a base). + int num_quads = 5; + + if (building.pitched) { + // If it's a pitched roof, we add another 3 quads (we'll be + // removing the flat top). + num_quads+=3; + } + + // Set up the rotation and translation matrix, which we apply to + // vertices as they are created as we'll be adding buildings later. + osg::Matrix transformMat; + transformMat = osg::Matrix::translate(toOsg(building.position)); + double hdg = - building.rotation * M_PI * 2; + osg::Matrix rotationMat = osg::Matrix::rotate(hdg, + osg::Vec3d(0.0, 0.0, 1.0)); + transformMat.preMult(rotationMat); + + // Create the vertices + 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 + + + // BASEMENT + // This exteds 10m below the main section + // Front face + v->push_back( osg::Vec3( 0, cw, -10) * transformMat ); // bottom right + v->push_back( osg::Vec3( 0, -cw, -10) * transformMat ); // bottom left + v->push_back( osg::Vec3( 0, -cw, 0) * transformMat ); // top left + v->push_back( osg::Vec3( 0, cw, 0) * transformMat ); // top right + + n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal + + // Left face + v->push_back( osg::Vec3( 0, -cw, -10) * transformMat ); // bottom right + v->push_back( osg::Vec3( cd, -cw, -10) * transformMat ); // bottom left + v->push_back( osg::Vec3( cd, -cw, 0) * transformMat ); // top left + v->push_back( osg::Vec3( 0, -cw, 0) * transformMat ); // top right + + n->push_back( osg::Vec3(0, -1, 0) * rotationMat ); // normal + + // Back face + v->push_back( osg::Vec3( cd, -cw, -10) * transformMat ); // bottom right + v->push_back( osg::Vec3( cd, cw, -10) * transformMat ); // bottom left + v->push_back( osg::Vec3( cd, cw, 0) * transformMat ); // top left + v->push_back( osg::Vec3( cd, -cw, 0) * transformMat ); // top right + + n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal + + // Right face + v->push_back( osg::Vec3( cd, cw, -10) * transformMat ); // bottom right + v->push_back( osg::Vec3( 0, cw, -10) * transformMat ); // bottom left + v->push_back( osg::Vec3( 0, cw, 0) * transformMat ); // top left + v->push_back( osg::Vec3( cd, cw, 0) * transformMat ); // top right + + n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal + + // MAIN BODY + // Front face + v->push_back( osg::Vec3( 0, cw, 0) * transformMat ); // bottom right + v->push_back( osg::Vec3( 0, -cw, 0) * transformMat ); // bottom left + v->push_back( osg::Vec3( 0, -cw, ch) * transformMat ); // top left + v->push_back( osg::Vec3( 0, cw, ch) * transformMat ); // top right + + n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal + + // Left face + v->push_back( osg::Vec3( 0, -cw, 0) * transformMat ); // bottom right + v->push_back( osg::Vec3( cd, -cw, 0) * transformMat ); // bottom left + v->push_back( osg::Vec3( cd, -cw, ch) * transformMat ); // top left + v->push_back( osg::Vec3( 0, -cw, ch) * transformMat ); // top right + + n->push_back( osg::Vec3(0, -1, 0) * rotationMat ); // normal + + // Back face + v->push_back( osg::Vec3( cd, -cw, 0) * transformMat ); // bottom right + v->push_back( osg::Vec3( cd, cw, 0) * transformMat ); // bottom left + v->push_back( osg::Vec3( cd, cw, ch) * transformMat ); // top left + v->push_back( osg::Vec3( cd, -cw, ch) * transformMat ); // top right + + n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal + + // Right face + v->push_back( osg::Vec3( cd, cw, 0) * transformMat ); // bottom right + v->push_back( osg::Vec3( 0, cw, 0) * transformMat ); // bottom left + v->push_back( osg::Vec3( 0, cw, ch) * transformMat ); // top left + v->push_back( osg::Vec3( cd, cw, ch) * transformMat ); // top right + + n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal + + // ROOF + if (building.pitched) { + + // Front pitched roof + v->push_back( osg::Vec3( 0, cw, ch) * transformMat ); // bottom right + v->push_back( osg::Vec3( 0, -cw, ch) * transformMat ); // bottom left + v->push_back( osg::Vec3(0.5*cd, -cw, ch+3) * transformMat ); // top left + v->push_back( osg::Vec3(0.5*cd, cw, ch+3) * transformMat ); // top right + n->push_back( osg::Vec3(-0.707, 0, 0.707) * rotationMat ); // normal + + // Left pitched roof + v->push_back( osg::Vec3( 0, -cw, ch) * transformMat ); // bottom right + v->push_back( osg::Vec3( cd, -cw, ch) * transformMat ); // bottom left + v->push_back( osg::Vec3(0.5*cd, -cw, ch+3) * transformMat ); // top left + v->push_back( osg::Vec3(0.5*cd, -cw, ch+3) * transformMat ); // top right + n->push_back( osg::Vec3(0, -1, 0) * rotationMat ); // normal + + // Back pitched roof + v->push_back( osg::Vec3( cd, -cw, ch) * transformMat ); // bottom right + v->push_back( osg::Vec3( cd, cw, ch) * transformMat ); // bottom left + v->push_back( osg::Vec3(0.5*cd, cw, ch+3) * transformMat ); // top left + v->push_back( osg::Vec3(0.5*cd, -cw, ch+3) * transformMat ); // top right + n->push_back( osg::Vec3(0.707, 0, 0.707) * rotationMat ); // normal + + // Right pitched roof + v->push_back( osg::Vec3( cd, cw, ch) * transformMat ); // bottom right + v->push_back( osg::Vec3( 0, cw, ch) * transformMat ); // bottom left + v->push_back( osg::Vec3(0.5*cd, cw, ch+3) * transformMat ); // top left + v->push_back( osg::Vec3(0.5*cd, cw, ch+3) * transformMat ); // top right + n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal + } else { + // Top face + v->push_back( osg::Vec3( 0, cw, ch) * transformMat ); // bottom right + v->push_back( osg::Vec3( 0, -cw, ch) * transformMat ); // bottom left + v->push_back( osg::Vec3( cd, -cw, ch) * transformMat ); // top left + v->push_back( osg::Vec3( cd, cw, ch) * transformMat ); // top right + n->push_back( osg::Vec3( 0, 0, 1) * rotationMat ); // 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; + float base_y = (float) row * 16.0 * 3.0 / 1024.0; + float top_y = base_y + 16.0 * (float) building.floors / 1024.0; + float left_x = 0.0f; + float right_x = 32.0 / 1024.0 * round((float) building.width / 6.0f); + float front_x = 384.0/1024.0; + 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) ); + } + // MAIN BODY + // 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 + t->push_back( osg::Vec2( back_x, top_y ) ); // top left + t->push_back( osg::Vec2( front_x, top_y ) ); // top right + + // ROOF + if (building.pitched) { + // Use the entire height of the roof texture + top_y = base_y + 16.0 * 3.0 / 1024.0; + left_x = 512/1024.0; + right_x = 512/1024.0 + 32.0 / 1024.0 * round(building.width / 6.0f); + 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 + t->push_back( osg::Vec2( back_x, top_y ) ); // top left + t->push_back( osg::Vec2( front_x, top_y ) ); // top right + } else { + // Flat roof + left_x = 512.0/1024.0; + right_x = 640.0/1024.0; + // Use the entire height of the roof texture + top_y = base_y + 16.0 * 3.0 / 1024.0; + + 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) + { + 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; + float right_x = left_x + 32.0 / 1024.0 * round((float) building.width / 10.0f); + + // BASEMENT - uses the baseline texture + for (unsigned int i = 0; i < 16; i++) { + t->push_back( osg::Vec2( left_x, base_y) ); + } + + // MAIN BODY + // 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 + t->push_back( osg::Vec2( left_x, top_y ) ); // top left + t->push_back( osg::Vec2( right_x, top_y ) ); // top right + + // ROOF + 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 + t->push_back( osg::Vec2( left_x, top_y ) ); // top left + t->push_back( osg::Vec2( right_x, top_y ) ); // top right + } else { + // Flat roof + base_y = 416/1024.0; + top_y = 576.0/1024.0; + right_x = left_x + 32.0 / 1024.0 * 6.0; + + 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::LARGE) + { + 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; + float right_x = left_x + 32.0 / 1024.0 * round((float) building.width / 20.0f); + + // BASEMENT - uses the baseline texture + for (unsigned int i = 0; i < 16; i++) { + t->push_back( osg::Vec2( left_x, base_y) ); + } + + // MAIN BODY + // 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 + t->push_back( osg::Vec2( left_x, top_y ) ); // top left + t->push_back( osg::Vec2( right_x, top_y ) ); // top right + + // ROOF + if (building.pitched) { + base_y = 896/1024.0; + top_y = 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 + t->push_back( osg::Vec2( left_x, top_y ) ); // top left + t->push_back( osg::Vec2( right_x, top_y ) ); // top right + } else { + // Flat roof + base_y = 896/1024.0; + top_y = 1.0; + + 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 + } + + } + + // Set the vertex, texture and normals back. + geom->setVertexArray(v); + geom->setTexCoordArray(0, t); + geom->setNormalArray(n); + + geom->setPrimitiveSet(0, new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,v->size())); + geode->setDrawable(0, geom); +} + +// Helper classes for creating the quad tree +namespace +{ +struct MakeBuildingLeaf +{ + MakeBuildingLeaf(float range, Effect* effect) : + _range(range), _effect(effect) {} + + MakeBuildingLeaf(const MakeBuildingLeaf& rhs) : + _range(rhs._range), _effect(rhs._effect) + {} + + LOD* operator() () const + { + LOD* result = new LOD; + + // Create a series of LOD nodes so trees cover decreases slightly + // gradually with distance from _range to 2*_range + for (float i = 0.0; i < SG_BUILDING_FADE_OUT_LEVELS; i++) + { + //osg::ref_ptr geode = new EffectGeode(); + //geode->setEffect(_effect); + osg::ref_ptr geode = new osg::Geode(); + result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0))); + } + return result; + } + + float _range; + Effect* _effect; +}; + +struct AddBuildingLeafObject +{ + void operator() (LOD* lod, const SGBuildingBin::Building& building) const + { + Geode* geode = static_cast(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren())); + addBuildingToLeafGeode(geode, building); + } +}; + +struct GetBuildingCoord +{ + Vec3 operator() (const SGBuildingBin::Building& building) const + { + return toOsg(building.position); + } +}; + +typedef QuadTreeBuilder BuildingGeometryQuadtree; +} + +struct BuildingTransformer +{ + BuildingTransformer(Matrix& mat_) : mat(mat_) {} + SGBuildingBin::Building operator()(const SGBuildingBin::Building& building) const + { + Vec3 pos = toOsg(building.position); + return SGBuildingBin::Building(toSG(pos * mat), building); + } + Matrix mat; +}; + + + +// 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, + const SGReaderWriterOptions* options) +{ + Matrix transInv = Matrix::inverse(transform); + static Matrix ident; + // Set up some shared structures. + MatrixTransform* mt = new MatrixTransform(transform); + Effect* effect = makeEffect("Effects/model-default", true); + + SGBuildingBin* bin = NULL; + + BOOST_FOREACH(bin, buildings) + { + // Now, create a quadbuilding for the buildings. + BuildingGeometryQuadtree + quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(), + SG_BUILDING_QUAD_TREE_DEPTH, + MakeBuildingLeaf(20000.0f, effect)); // FIXME - tie to property + + // Transform building positions from the "geocentric" positions we + // get from the scenery polys into the local Z-up coordinate + // system. + std::vector rotatedBuildings; + rotatedBuildings.reserve(bin->buildings.size()); + std::transform(bin->buildings.begin(), bin->buildings.end(), + std::back_inserter(rotatedBuildings), + BuildingTransformer(transInv)); + quadbuilding.buildQuadTree(rotatedBuildings.begin(), rotatedBuildings.end()); + + ref_ptr group = quadbuilding.getRoot(); + + // Set up the stateset for this building bin and the texture to use. + osg::StateSet* stateSet = group->getOrCreateStateSet(); + const std::string texturename = bin->texture; + osg::Texture2D* texture = SGLoadTexture2D(texturename); + texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE); + texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE); + stateSet->setTextureAttributeAndModes(0, texture); + + osg::ShadeModel* shadeModel = new osg::ShadeModel; + shadeModel->setMode(osg::ShadeModel::FLAT); + stateSet->setAttributeAndModes(shadeModel); + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); + stateSet->setMode(GL_FOG, osg::StateAttribute::ON); + stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON); + stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON); + stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF); + stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF); + stateSet->setAttribute(new osg::CullFace(osg::CullFace::BACK)); + + osg::Material* material = new osg::Material; + material->setAmbient(osg::Material::FRONT, osg::Vec4(0.3,0.3,0.3,1.0)); + material->setDiffuse(osg::Material::FRONT, osg::Vec4(1.0,1.0,1.0,1.0)); + material->setSpecular(osg::Material::FRONT, osg::Vec4(0,0,0,1.0)); + material->setShininess(osg::Material::FRONT, 0.0); + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + stateSet->setAttribute(material); + mt->addChild(group); + } + + return mt; +} + +} diff --git a/simgear/scene/tgdb/SGBuildingBin.hxx b/simgear/scene/tgdb/SGBuildingBin.hxx new file mode 100644 index 00000000..9c6d2619 --- /dev/null +++ b/simgear/scene/tgdb/SGBuildingBin.hxx @@ -0,0 +1,110 @@ +/* -*-c++-*- + * + * Copyright (C) 2011 Stuart Buchanan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#ifndef SG_BUILDING_BIN_HXX +#define SG_BUILDING_BIN_HXX + +#include + +#include +#include + +#include +#include +#include + +#include + +namespace simgear +{ +class SGBuildingBin { +public: + + enum BuildingType { + SMALL = 0, + MEDIUM, + LARGE }; + + struct Building { + Building(BuildingType t, const SGVec3f& p, float w, float d, float h, int f, float rot, bool pitch) : + type(t), + position(p), + width(w), + depth(d), + height(h), + floors(f), + rotation(rot), + pitched(pitch), + radius(std::max(d, 0.5f*w)) + { } + Building(const SGVec3f& p, Building b) : + type(b.type), + position(p), + width(b.width), + depth(b.depth), + height(b.height), + floors(b.floors), + rotation(b.rotation), + pitched(b.pitched), + radius(std::max(b.depth, 0.5f*b.width)) + { } + + BuildingType type; + SGVec3f position; + float width; + float depth; + float height; + int floors; + float rotation; + bool pitched; + float radius; + + float getFootprint() { + return radius; + } + }; + + typedef std::vector BuildingList; + BuildingList buildings; + + std::string texture; + + void insert(const Building& model) + { + buildings.push_back(model); + } + + void insert(BuildingType t, const SGVec3f& p, float w, float d, float h, int f, float rot, bool pitch) + { insert(Building(t, p, w, d, h, f, rot, pitch)); } + + unsigned getNumBuildings() const + { return buildings.size(); } + const Building& getBuilding(unsigned i) const + { return buildings[i]; } +}; + +// List of buildings +typedef std::list SGBuildingBinList; + +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 c899d8ae..cb29aa74 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -60,6 +60,7 @@ #include "SGTexturedTriangleBin.hxx" #include "SGLightBin.hxx" #include "SGModelBin.hxx" +#include "SGBuildingBin.hxx" #include "TreeBin.hxx" #include "SGDirectionalLightBin.hxx" #include "GroundLightManager.hxx" @@ -85,6 +86,7 @@ struct SGTileGeometryBin { SGLightListBin odalLights; SGDirectionalLightListBin reilLights; SGMatModelBin randomModels; + SGBuildingBinList randomBuildings; static SGVec4f getMaterialLightColor(const SGMaterial* material) @@ -445,7 +447,242 @@ struct SGTileGeometryBin { } } } + + void computeRandomBuildings(SGMaterialLib* matlib, float building_density) + { + 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); + + float coverage = mat->get_building_coverage(); + + // Minimum spacing needs to include the maximum footprint of a building. + // As the 0,0,0 point is the center of the front of the building, we need + // to consider the full depth, but only half the possible width. + float min_spacing = mat->get_building_spacing(); + + if (coverage <= 0) + continue; + + bool found = false; + SGBuildingBin* bin = NULL; + + BOOST_FOREACH(bin, randomBuildings) + { + if (bin->texture == mat->get_building_texture()) { + found = true; + break; + } + } + + if (!found) { + bin = new SGBuildingBin(); + bin->texture = mat->get_building_texture(); + SG_LOG(SG_INPUT, SG_DEBUG, "Building texture " << bin->texture); + randomBuildings.push_back(bin); + } + + std::vector > randomPoints; + + unsigned num = i->second.getNumTriangles(); + int triangle_dropped = 0; + int building_dropped = 0; + int random_dropped = 0; + + for (unsigned i = 0; i < num; ++i) { + SGBuildingBin::BuildingList triangle_buildings; + SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i); + + SGVec3f v0 = triangleBin.getVertex(triangleRef[0]).vertex; + SGVec3f v1 = triangleBin.getVertex(triangleRef[1]).vertex; + SGVec3f v2 = triangleBin.getVertex(triangleRef[2]).vertex; + SGVec2f t0 = triangleBin.getVertex(triangleRef[0]).texCoord; + SGVec2f t1 = triangleBin.getVertex(triangleRef[1]).texCoord; + SGVec2f t2 = triangleBin.getVertex(triangleRef[2]).texCoord; + SGVec3f normal = cross(v1 - v0, v2 - v0); + + // Compute the area + float area = 0.5f*length(normal); + if (area <= SGLimitsf::min()) + continue; + + // for partial units of area, use a zombie door method to + // create the proper random chance of an object being created + // for this triangle. + double num = area / coverage + mt_rand(&seed); + + // Apply density. + num = num * building_density; + + // place an object each unit of area + while ( num > 1.0 ) { + float a = mt_rand(&seed); + float b = mt_rand(&seed); + if ( a + b > 1 ) { + a = 1 - a; + b = 1 - b; + } + + float c = 1 - a - b; + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + float rotation = mt_rand(&seed); + + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + 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()) { + // Object passes mask. Rotation is taken from the red channel + rotation = img->getColor(x,y).r(); + } else { + // Fails mask test - try again. + num -= 1.0; + continue; + } + } + + // Now create the building, so we have an idea of its footprint + // and therefore appropriate spacing. + SGBuildingBin::BuildingType buildingtype; + float width; + float depth; + int floors; + float height; + bool pitched; + + // Determine the building type, and hence dimensions. + float type = mt_rand(&seed); + + if (type < mat->get_building_small_fraction()) { + // Small building + buildingtype = SGBuildingBin::SMALL; + 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 = 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)); + + if (depth > width) { depth = width; } + + pitched = (mt_rand(&seed) < mat->get_building_small_pitch()); + } else if (type < (mat->get_building_small_fraction() + mat->get_building_medium_fraction())) { + 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 = 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)); + pitched = (mt_rand(&seed) < mat->get_building_medium_pitch()); + } else { + buildingtype = SGBuildingBin::LARGE; + 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 = 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()); + } + + // Determine an appropriate minimum spacing for the object. Note that the + // origin of the building model is the center of the front face, hence we + // consider the full depth. We choose _not_ to use the diagonal distance + // to one of the rear corners, as we assume that terrain masking will + // make the buildings place in some sort of grid. + float radius = std::max(depth, 0.5f*width); + + // 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. + if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) < radius) || + ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) < radius) || + ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) < radius) ) + { + triangle_dropped++; + num -= 1.0; + continue; + } + + // Check against the generic random objects. TODO - make this more efficient by + // masking ahead of time objects outside of the triangle. + bool too_close = false; + for (unsigned int i = 0; i < randomModels.getNumModels(); ++i) { + float min_dist = randomModels.getMatModel(i).model->get_spacing_m() + radius + min_spacing; + min_dist = min_dist * min_dist; + + if (distSqr(randomModels.getMatModel(i).position, randomPoint) < min_dist) { + too_close = true; + random_dropped++; + continue; + } + } + + if (too_close) { + // Too close to a random model - drop and try again + num -= 1.0; + continue; + } + + SGBuildingBin::BuildingList::iterator l; + + // Check that the building is sufficiently far from any other building within the triangle. + for (l = triangle_buildings.begin(); l != triangle_buildings.end(); ++l) { + + float min_dist = l->radius + radius + min_spacing; + min_dist = min_dist * min_dist; + + if (distSqr(randomPoint, l->position) < min_dist) { + building_dropped++; + too_close = true; + continue; + } + } + + if (too_close) { + // Too close to another building - drop and try again + num -= 1.0; + continue; + } + + // If we've passed all of the above tests we have a valid + // building, so create it! + SGBuildingBin::Building building = + SGBuildingBin::Building(buildingtype, + randomPoint, + width, + depth, + height, + floors, + rotation, + pitched); + triangle_buildings.push_back(building); + num -= 1.0; + } + + // Add the buildings from this triangle to the overall list. + SGBuildingBin::BuildingList::iterator l; + + for (l = triangle_buildings.begin(); l != triangle_buildings.end(); ++l) { + bin->insert(*l); + } + } + + SG_LOG(SG_INPUT, SG_DEBUG, "Random Buildings: " << bin->getNumBuildings()); + SG_LOG(SG_INPUT, SG_DEBUG, " Dropped due to triangle edge: " << triangle_dropped); + SG_LOG(SG_INPUT, SG_DEBUG, " Dropped due to random object: " << random_dropped); + SG_LOG(SG_INPUT, SG_DEBUG, " Dropped due to other building: " << building_dropped); + } + } + void computeRandomForest(SGMaterialLib* matlib, float vegetation_density) { SGMaterialTriangleMap::iterator i; @@ -538,9 +775,9 @@ struct SGTileGeometryBin { object->get_spacing_m(), mat->get_object_mask(i->second), randomPoints); + std::vector >::iterator l; for (l = randomPoints.begin(); l != randomPoints.end(); ++l) { - // Only add the model if it is sufficiently far from the // other models bool close = false; @@ -551,8 +788,10 @@ struct SGTileGeometryBin { if (distSqr(randomModels.getMatModel(i).position, l->first) < spacing) { close = true; + continue; } } + if (!close) { randomModels.insert(l->first, object, (int)object->get_randomized_range_m(&seed), l->second); } @@ -563,7 +802,6 @@ struct SGTileGeometryBin { } } } - bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib) { @@ -608,7 +846,9 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options SGMaterialLib* matlib = 0; bool use_random_objects = false; bool use_random_vegetation = false; - float vegetation_density = 1.0f; + 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(); @@ -618,10 +858,16 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options use_random_objects); use_random_vegetation = propertyNode->getBoolValue("/sim/rendering/random-vegetation", - use_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); } } @@ -652,75 +898,82 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options 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; 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)); + if (use_random_objects && matlib) { + tileGeometryBin.computeRandomObjects(matlib); - std::vector models; - for (unsigned int i = 0; - i < tileGeometryBin.randomModels.getNumModels(); i++) { - SGMatModelBin::MatModel obj - = tileGeometryBin.randomModels.getMatModel(i); + if (tileGeometryBin.randomModels.getNumModels() > 0) { + // Generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(123)); - 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))); - } + std::vector models; + for (unsigned int i = 0; + i < tileGeometryBin.randomModels.getNumModels(); i++) { + SGMatModelBin::MatModel obj + = tileGeometryBin.randomModels.getMatModel(i); - 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->addChild(node); - models.push_back(ModelLOD(position, obj.lod)); - } - RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD())); - quadtree.buildQuadTree(models.begin(), models.end()); - randomObjects = quadtree.getRoot(); - randomObjects->setName("random objects"); - } - } - - if (use_random_vegetation && matlib) { - // Now add some random forest. - tileGeometryBin.computeRandomForest(matlib, vegetation_density); + SGPropertyNode* root = options->getPropertyNode()->getRootNode(); + osg::Node* node = obj.model->get_random_model(root, &seed); - if (tileGeometryBin.randomForest.size() > 0) { - forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity(), - options); - forestNode->setName("Random trees"); + // 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->addChild(node); + models.push_back(ModelLOD(position, obj.lod)); } - } + RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD())); + quadtree.buildQuadTree(models.begin(), models.end()); + randomObjects = quadtree.getRoot(); + randomObjects->setName("random objects"); + } } + if (use_random_vegetation && matlib) { + // Now add some random forest. + tileGeometryBin.computeRandomForest(matlib, vegetation_density); + + if (tileGeometryBin.randomForest.size() > 0) { + forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity(), + options); + forestNode->setName("Random trees"); + } + } + + if (use_random_buildings && matlib) { + tileGeometryBin.computeRandomBuildings(matlib, building_density); + if (tileGeometryBin.randomBuildings.size() > 0) { + buildingNode = createRandomBuildings(tileGeometryBin.randomBuildings, osg::Matrix::identity(), + options); + buildingNode->setName("Random buildings"); + } + } + // FIXME: ugly, has a side effect if (matlib) tileGeometryBin.computeRandomSurfaceLights(matlib); @@ -736,6 +989,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options groundLights0->addChild(geode); lightGroup->addChild(groundLights0); } + if (tileGeometryBin.randomTileLights.getNumLights() > 0) { osg::Group* groundLights1 = new osg::Group; groundLights1->setStateSet(lightManager->getGroundLightStateSet()); @@ -778,6 +1032,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options vasiGeode->setStateSet(lightManager->getRunwayLightStateSet()); lightGroup->addChild(vasiGeode); } + Effect* runwayEffect = 0; if (tileGeometryBin.runwayLights.getNumLights() > 0 || !tileGeometryBin.rabitLights.empty() @@ -841,7 +1096,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options transform->addChild(lightLOD); } - if (randomObjects.valid() || forestNode.valid()) { + 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. @@ -849,6 +1104,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options 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_RECIEVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT; objectLOD->setNodeMask(nodeMask);