Random buildings - initial commit.
This commit is contained in:
parent
0c13fb7ae4
commit
f9bf403fc0
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -25,6 +25,7 @@ set(SOURCES
|
||||
GroundLightManager.cxx
|
||||
ReaderWriterSPT.cxx
|
||||
ReaderWriterSTG.cxx
|
||||
SGBuildingBin.cxx
|
||||
SGOceanTile.cxx
|
||||
SGReaderWriterBTG.cxx
|
||||
SGVasiDrawable.cxx
|
||||
|
636
simgear/scene/tgdb/SGBuildingBin.cxx
Normal file
636
simgear/scene/tgdb/SGBuildingBin.cxx
Normal 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;
|
||||
}
|
||||
|
||||
}
|
110
simgear/scene/tgdb/SGBuildingBin.hxx
Normal file
110
simgear/scene/tgdb/SGBuildingBin.hxx
Normal 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
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user