Random buildings - initial commit.

This commit is contained in:
Stuart Buchanan 2012-04-24 22:00:35 +01:00
parent 0c13fb7ae4
commit f9bf403fc0
6 changed files with 1212 additions and 61 deletions

View File

@ -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;

View File

@ -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;

View File

@ -25,6 +25,7 @@ set(SOURCES
GroundLightManager.cxx
ReaderWriterSPT.cxx
ReaderWriterSTG.cxx
SGBuildingBin.cxx
SGOceanTile.cxx
SGReaderWriterBTG.cxx
SGVasiDrawable.cxx

View File

@ -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 <simgear_config.h>
#endif
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/Matrix>
#include <osg/ShadeModel>
#include <osg/Material>
#include <osg/CullFace>
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/util/QuadTreeBuilder.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx>
#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<std::string, osg::observer_ptr<osg::StateSet> > 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<osg::Geometry> 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<EffectGeode> geode = new EffectGeode();
//geode->setEffect(_effect);
osg::ref_ptr<osg::Geode> 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<Geode*>(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<LOD*, SGBuildingBin::Building, MakeBuildingLeaf, AddBuildingLeafObject,
GetBuildingCoord> 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<SGBuildingBin::Building> 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> 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;
}
}

View File

@ -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 <math.h>
#include <vector>
#include <string>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Matrix>
#include <simgear/scene/util/OsgMath.hxx>
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<Building> 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<SGBuildingBin*> SGBuildingBinList;
osg::Group* createRandomBuildings(SGBuildingBinList buildinglist, const osg::Matrix& transform,
const SGReaderWriterOptions* options);
}
#endif

View File

@ -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<std::pair<SGVec3f, float> > 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<std::pair<SGVec3f, float> >::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<osg::Group> lightGroup = new SGOffsetTransform(0.94);
osg::ref_ptr<osg::Group> randomObjects;
osg::ref_ptr<osg::Group> forestNode;
osg::ref_ptr<osg::Group> 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<ModelLOD> 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<ModelLOD> 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);