#include <osgDB/FileUtils>
#include <simgear/debug/logstream.hxx>
+#include <simgear/math/SGLimits.hxx>
+#include <simgear/math/SGMisc.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 2
-#define SG_BUILDING_FADE_OUT_LEVELS 4
using namespace osg;
static BuildingStateSetMap statesetmap;
static int numBuildings;
-void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& building)
+typedef std::map<std::string, osg::observer_ptr<Effect> > EffectMap;
+static EffectMap buildingEffectMap;
+
+// Building instance scheme:
+// vertex - local position of vertices, with 0,0,0 being the center front.
+// fog coord - rotation
+// color - xyz of tree quad origin, replicated 4 times.
+
+struct BuildingBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback
{
- // 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::ref_ptr<osg::Vec3Array> v;
- osg::ref_ptr<osg::Vec2Array> t;
- osg::ref_ptr<osg::Vec4Array> c;
- osg::ref_ptr<osg::Vec3Array> n;
-
- if (geode->getNumDrawables() == 0) {
- geom = new osg::Geometry;
- v = new osg::Vec3Array;
- t = new osg::Vec2Array;
- c = new osg::Vec4Array;
- n = new osg::Vec3Array;
+ BuildingBoundingBoxCallback() {}
+ BuildingBoundingBoxCallback(const BuildingBoundingBoxCallback&, const CopyOp&) {}
+ META_Object(simgear, BuildingBoundingBoxCallback);
+ virtual BoundingBox computeBound(const Drawable&) const;
+};
+
+BoundingBox
+BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
+{
+ BoundingBox bb;
+ const Geometry* geom = static_cast<const Geometry*>(&drawable);
+ const Vec3Array* v = static_cast<const Vec3Array*>(geom->getVertexArray());
+ const Vec4Array* pos = static_cast<const Vec4Array*>(geom->getColorArray());
+
+ Geometry::PrimitiveSetList primSets = geom->getPrimitiveSetList();
+ for (Geometry::PrimitiveSetList::const_iterator psitr = primSets.begin(), psend = primSets.end();
+ psitr != psend;
+ ++psitr) {
+ DrawArrays* da = static_cast<DrawArrays*>(psitr->get());
+ GLint psFirst = da->getFirst();
+ GLint psEndVert = psFirst + da->getCount();
+ for (GLint i = psFirst;i < psEndVert; ++i) {
+ Vec3 pt = (*v)[i];
+ Matrixd trnsfrm = Matrixd::rotate(- M_PI * 2 * (*pos)[i].a(), Vec3(0.0f, 0.0f, 1.0f));
+ pt = pt * trnsfrm;
+ pt += Vec3((*pos)[i].x(), (*pos)[i].y(), (*pos)[i].z());
+ bb.expandBy(pt);
+ }
+ }
+ return bb;
+}
+
+ // Set up the building set based on the material definitions
+ SGBuildingBin::SGBuildingBin(const SGMaterial *mat) {
+
+ material_name = mat->get_names()[0];
+ SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << material_name);
+ texture = mat->get_building_texture();
+ lightMap = mat->get_building_lightmap();
+ SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << texture);
+
+ // Generate a random seed for the building generation.
+ mt seed;
+ mt_init(&seed, unsigned(123));
+
+ smallSharedGeometry = new osg::Geometry();
+ mediumSharedGeometry = new osg::Geometry();
+ largeSharedGeometry = new osg::Geometry();
+
+ smallBuildingMaxRadius = std::max(mat->get_building_small_max_depth() * 0.5, mat->get_building_small_max_width() * 0.5);
+ mediumBuildingMaxRadius = std::max(mat->get_building_medium_max_depth() * 0.5, mat->get_building_medium_max_width() * 0.5);
+ largeBuildingMaxRadius = std::max(mat->get_building_large_max_depth() * 0.5, mat->get_building_large_max_width() * 0.5);
+
+ smallBuildingMaxDepth = mat->get_building_small_max_depth();
+ mediumBuildingMaxDepth = mat->get_building_medium_max_depth();
+ largeBuildingMaxDepth = mat->get_building_large_max_depth();
+
+ smallBuildingFraction = mat->get_building_small_fraction();
+ mediumBuildingFraction = mat->get_building_medium_fraction();
+
+ SG_LOG(SG_TERRAIN, SG_DEBUG, "Building fractions " << smallBuildingFraction << " " << mediumBuildingFraction);
+
+
+ // TODO: Reverse this - otherwise we never get any large buildings!
+ BuildingType types[] = { SGBuildingBin::SMALL, SGBuildingBin::MEDIUM, SGBuildingBin::LARGE };
+ BuildingList lists[] = { SGBuildingBin::smallBuildings, SGBuildingBin::mediumBuildings, SGBuildingBin::largeBuildings };
+ ref_ptr<Geometry> geometries[] = { smallSharedGeometry, mediumSharedGeometry, largeSharedGeometry };
+
+ for (int bt=0; bt < 3; bt++) {
+ SGBuildingBin::BuildingType buildingtype = types[bt];
+ ref_ptr<Geometry> sharedGeometry = geometries[bt];
+ BuildingList buildings = lists[bt];
- // 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_VERTEX);
- // 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;
+ osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
+ osg::ref_ptr<osg::Vec2Array> t = new osg::Vec2Array;
+ osg::ref_ptr<osg::Vec3Array> n = new osg::Vec3Array;
- 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;
+ v->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
+ t->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
+ n->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
- // 0,0,0 is the bottom center of the front
- // face, e.g. where the front door would be
+ sharedGeometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
+ sharedGeometry->setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
+ sharedGeometry->setComputeBoundingBoxCallback(new BuildingBoundingBoxCallback);
+ sharedGeometry->setUseDisplayList(false);
- // 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
-
- for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal
-
- // Left 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
-
- for (int i=0; i<4; ++i)
- 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
-
- for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal
-
- // Right 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
-
- for (int i=0; i<4; ++i)
- 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
-
- for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal
-
- // Left 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
-
- for (int i=0; i<4; ++i)
- 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
-
- for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal
-
- // Right 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
-
- for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal
-
- // ROOF
- if (building.pitched) {
+ for (unsigned int j = 0; j < BUILDING_SET_SIZE; j++) {
+ float width;
+ float depth;
+ int floors;
+ float height;
+ bool pitched;
+
+ if (buildingtype == SGBuildingBin::SMALL) {
+ // Small building
+ width = mat->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_width() - mat->get_building_small_min_width());
+ depth = mat->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_depth() - mat->get_building_small_min_depth());
+ floors = SGMisc<double>::round(mat->get_building_small_min_floors() + mt_rand(&seed) * (mat->get_building_small_max_floors() - mat->get_building_small_min_floors()));
+ height = floors * (2.8 + mt_rand(&seed));
+
+ // Small buildings are never deeper than they are wide.
+ if (depth > width) { depth = width; }
+
+ pitched = (mt_rand(&seed) < mat->get_building_small_pitch());
+ } else if (buildingtype == SGBuildingBin::MEDIUM) {
+ width = mat->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_width() - mat->get_building_medium_min_width());
+ depth = mat->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_depth() - mat->get_building_medium_min_depth());
+ floors = SGMisc<double>::round(mat->get_building_medium_min_floors() + mt_rand(&seed) * (mat->get_building_medium_max_floors() - mat->get_building_medium_min_floors()));
+ height = floors * (2.8 + mt_rand(&seed));
+
+ while ((height > width) && (floors > mat->get_building_medium_min_floors())) {
+ // Ensure that medium buildings aren't taller than they are wide
+ floors--;
+ height = floors * (2.8 + mt_rand(&seed));
+ }
+
+ pitched = (mt_rand(&seed) < mat->get_building_medium_pitch());
+ } else {
+ width = mat->get_building_large_min_width() + mt_rand(&seed) * (mat->get_building_large_max_width() - mat->get_building_large_min_width());
+ depth = mat->get_building_large_min_depth() + mt_rand(&seed) * (mat->get_building_large_max_depth() - mat->get_building_large_min_depth());
+ floors = SGMisc<double>::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());
+ }
- // 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
+ Building building = Building(buildingtype,
+ width,
+ depth,
+ height,
+ floors,
+ pitched);
- for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3(0.707, 0, 0.707) * rotationMat ); // normal
+ buildings.push_back(building);
+
+ // Now create an OSG Geometry based on the Building
+ float cw = 0.5f * building.width;
+ float cd = building.depth;
+ float ch = building.height;
+
+ // 0,0,0 is the bottom center of the front
+ // face, e.g. where the front door would be
- // Left 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
+ // BASEMENT
+ // This exteds 10m below the main section
+ // Front face
+ v->push_back( osg::Vec3( 0, -cw, -10) ); // bottom right
+ v->push_back( osg::Vec3( 0, cw, -10) ); // bottom left
+ v->push_back( osg::Vec3( 0, cw, 0) ); // top left
+ v->push_back( osg::Vec3( 0, -cw, 0) ); // top right
for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3(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(1, 0, 0) ); // normal
+ // Left face
+ v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom right
+ v->push_back( osg::Vec3( 0, -cw, -10) ); // bottom left
+ v->push_back( osg::Vec3( 0, -cw, 0) ); // top left
+ v->push_back( osg::Vec3( -cd, -cw, 0) ); // top right
+
for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3(-0.707, 0, 0.707) * rotationMat ); // normal
+ n->push_back( osg::Vec3(0, -1, 0) ); // normal
- // Right 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
+ // Back face
+ v->push_back( osg::Vec3( -cd, cw, -10) ); // bottom right
+ v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom left
+ v->push_back( osg::Vec3( -cd, -cw, 0) ); // top left
+ v->push_back( osg::Vec3( -cd, cw, 0) ); // top right
for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3(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(-1, 0, 0) ); // normal
+ // Right face
+ v->push_back( osg::Vec3( 0, cw, -10) ); // bottom right
+ v->push_back( osg::Vec3( -cd, cw, -10) ); // bottom left
+ v->push_back( osg::Vec3( -cd, cw, 0) ); // top left
+ v->push_back( osg::Vec3( 0, cw, 0) ); // top right
+
for (int i=0; i<4; ++i)
- n->push_back( osg::Vec3( 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 = 32.0 / 1024.0 * round((float) building.width / 6.0f);
- float right_x = 0.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) );
- }
+ n->push_back( osg::Vec3(0, 1, 0) ); // normal
+
// 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
+ // Front face
+ v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom right
+ v->push_back( osg::Vec3( 0, cw, 0) ); // bottom left
+ v->push_back( osg::Vec3( 0, cw, ch) ); // top left
+ v->push_back( osg::Vec3( 0, -cw, ch) ); // top right
- // 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
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(1, 0, 0) ); // normal
- // 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
+ // Left face
+ v->push_back( osg::Vec3( -cd, -cw, 0) ); // bottom right
+ v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom left
+ v->push_back( osg::Vec3( 0, -cw, ch) ); // top left
+ v->push_back( osg::Vec3( -cd, -cw, ch) ); // top right
+
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(0, -1, 0) ); // normal
+
+ // Back face
+ v->push_back( osg::Vec3( -cd, cw, 0) ); // bottom right
+ v->push_back( osg::Vec3( -cd, -cw, 0) ); // bottom left
+ v->push_back( osg::Vec3( -cd, -cw, ch) ); // top left
+ v->push_back( osg::Vec3( -cd, cw, ch) ); // top right
+
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(-1, 0, 0) ); // normal
- // Right (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
+ // Right face
+ v->push_back( osg::Vec3( 0, cw, 0) ); // bottom right
+ v->push_back( osg::Vec3( -cd, cw, 0) ); // bottom left
+ v->push_back( osg::Vec3( -cd, cw, ch) ); // top left
+ v->push_back( osg::Vec3( 0, cw, ch) ); // top right
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(0, 1, 0) ); // normal
+
// 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 + 32.0 / 1024.0 * round(building.width / 6.0f);
- right_x = 512/1024.0;
- front_x = 480.0/1024.0;
- back_x = 512.0/1024.0;
+ if (building.pitched) {
+
+ // Front pitched roof
+ v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom right
+ v->push_back( osg::Vec3( 0, cw, ch) ); // bottom left
+ v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top left
+ v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right
+
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(0.707, 0, 0.707) ); // normal
+
+ // Left pitched roof
+ v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom right
+ v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom left
+ v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left
+ v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right
+
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(0, -1, 0) ); // normal
+
+ // Back pitched roof
+ v->push_back( osg::Vec3( -cd, cw, ch) ); // bottom right
+ v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom left
+ v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left
+ v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top right
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(-0.707, 0, 0.707) ); // normal
+
+ // Right pitched roof
+ v->push_back( osg::Vec3( 0, cw, ch) ); // bottom right
+ v->push_back( osg::Vec3( -cd, cw, ch) ); // bottom left
+ v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top left
+ v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top right
+
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(0, 1, 0) ); // normal
+ } else {
+ // If the roof isn't pitched, we still generate the
+ // vertices for simplicity later.
+
+ // Top of the roof
+ v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom right
+ v->push_back( osg::Vec3( 0, cw, ch) ); // bottom left
+ v->push_back( osg::Vec3(-cd, cw, ch) ); // top left
+ v->push_back( osg::Vec3(-cd, -cw, ch) ); // top right
+
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(0, 0, 1) ); // normal
+
+ // Left non-pitched roof
+ v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom right
+ v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom left
+ v->push_back( osg::Vec3( 0, -cw, ch) ); // top left
+ v->push_back( osg::Vec3( -cd, -cw, ch) ); // top right
+
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(0, -1, 0) ); // normal
+
+ // Back pitched roof
+ v->push_back( osg::Vec3(-cd, cw, ch) ); // bottom right
+ v->push_back( osg::Vec3(-cd, -cw, ch) ); // bottom left
+ v->push_back( osg::Vec3(-cd, -cw, ch) ); // top left
+ v->push_back( osg::Vec3(-cd, cw, ch) ); // top right
+
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(1, 0, 0) ); // normal
+
+ // Right pitched roof
+ v->push_back( osg::Vec3( 0, cw, ch) ); // bottom right
+ v->push_back( osg::Vec3(-cd, cw, ch) ); // bottom left
+ v->push_back( osg::Vec3(-cd, cw, ch) ); // top left
+ v->push_back( osg::Vec3( 0, cw, ch) ); // top right
+
+ for (int i=0; i<4; ++i)
+ n->push_back( osg::Vec3(0, 1, 0) ); // normal
+ }
+
+ // The 1024x1024 texture is split into 32x16 blocks.
+ // For a small building, each block is 6m wide and 3m high.
+ // For a medium building, each block is 10m wide and 3m high.
+ // For a large building, each block is 20m wide and 3m high
+
+ if (building.type == SGBuildingBin::SMALL) {
+ // Small buildings are represented on the bottom 5 rows of 3 floors
+ int row = ((int) (mt_rand(&seed) * 1000)) % 5;
+ 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 = 32.0 / 1024.0 * round((float) building.width / 6.0f);
+ float right_x = 0.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( 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 = 640.0/1024.0;
- right_x = 512.0/1024.0;
- // Use the entire height of the roof texture
- top_y = base_y + 16.0 * 3.0 / 1024.0;
+
+ // 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 + 32.0 / 1024.0 * round(building.width / 6.0f);
+ right_x = 512/1024.0;
+ front_x = 480.0/1024.0;
+ back_x = 512.0/1024.0;
+
+ // Front
+ t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
+ t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
+ t->push_back( osg::Vec2( left_x, top_y ) ); // top left
+ t->push_back( osg::Vec2( right_x, top_y ) ); // top right
+
+ // Left
+ t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
+ t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
+ t->push_back( osg::Vec2( back_x, top_y ) ); // top left
+ t->push_back( osg::Vec2( front_x, top_y ) ); // top right
+
+ // Back (same as front for the moment)
+ t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
+ t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
+ t->push_back( osg::Vec2( left_x, top_y ) ); // top left
+ t->push_back( osg::Vec2( right_x, top_y ) ); // top right
+
+ // Right (same as left for the moment)
+ t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
+ t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
+ 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 = 640.0/1024.0;
+ right_x = 512.0/1024.0;
+ // Use the entire height of the roof texture
+ top_y = base_y + 16.0 * 3.0 / 1024.0;
+
+ // Flat roofs still have 4 surfaces, so we need to set the textures
+ for (int i=0; i<4; ++i) {
+ t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
+ t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
+ t->push_back( osg::Vec2( left_x, top_y ) ); // top left
+ t->push_back( osg::Vec2( right_x, top_y ) ); // top right
+ }
+ }
- 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 + 32.0 / 1024.0 * round((float) building.width / 10.0f);
- float right_x = column * 192.0 /1024.0;
-
- // BASEMENT - uses the baseline texture
- for (unsigned int i = 0; i < 16; i++) {
- t->push_back( osg::Vec2( left_x, base_y) );
- }
-
- // 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;
-
+ 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 + 32.0 / 1024.0 * round((float) building.width / 10.0f);
+ float right_x = column * 192.0 /1024.0;
+
+ // BASEMENT - uses the baseline texture
+ for (unsigned int i = 0; i < 16; i++) {
+ t->push_back( osg::Vec2( left_x, base_y) );
+ }
+
+ // 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, 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, 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;
- left_x = column * 192.0 /1024.0;
- right_x = (column + 1)* 192.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::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 + 32.0 / 1024.0 * round((float) building.width / 20.0f);
- float right_x = column * 128.0 /1024.0;
-
- // BASEMENT - uses the baseline texture
- for (unsigned int i = 0; i < 16; i++) {
- t->push_back( osg::Vec2( left_x, base_y) );
- }
- // 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;
+ left_x = column * 192.0 /1024.0;
+ right_x = (column + 1)* 192.0 /1024.0;
+
+ // Flat roofs still have 4 surfaces
+ for (int i=0; i<4; ++i) {
+ 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;
+ 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 + 32.0 / 1024.0 * round((float) building.width / 20.0f);
+ float right_x = column * 128.0 /1024.0;
+
+ // BASEMENT - uses the baseline texture
+ for (unsigned int i = 0; i < 16; i++) {
+ t->push_back( osg::Vec2( left_x, base_y) );
+ }
+
+ // 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, 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, 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
- }
+ // 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;
+
+ // Flat roofs still have 4 surfaces
+ for (int i=0; i<4; ++i) {
+ t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
+ t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
+ t->push_back( osg::Vec2( left_x, top_y ) ); // top left
+ t->push_back( osg::Vec2( right_x, top_y ) ); // top right
+ }
+ }
+ }
}
-
- // 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);
-}
-
-typedef std::map<std::string, osg::observer_ptr<Effect> > EffectMap;
+ // Set the vertex, texture and normals. Colors will be set per-instance
+ // later.
+ sharedGeometry->setVertexArray(v);
+ sharedGeometry->setTexCoordArray(0, t);
+ sharedGeometry->setNormalArray(n);
+ }
+ }
+
+ void SGBuildingBin::insert(SGVec3f p, float r, BuildingType type) {
+
+ if (type == SGBuildingBin::SMALL) {
+ smallBuildingLocations.push_back(BuildingInstance(p, r, &smallBuildings, smallSharedGeometry));
+ }
+
+ if (type == SGBuildingBin::MEDIUM) {
+ mediumBuildingLocations.push_back(BuildingInstance(p, r, &mediumBuildings, mediumSharedGeometry));
+ }
-static EffectMap buildingEffectMap;
+ if (type == SGBuildingBin::LARGE) {
+ largeBuildingLocations.push_back(BuildingInstance(p, r, &largeBuildings, largeSharedGeometry));
+ }
+ }
-// Helper classes for creating the quad tree
-namespace
-{
-struct MakeBuildingLeaf
-{
- MakeBuildingLeaf(float range, Effect* effect) :
- _range(range), _effect(effect) {}
+ int SGBuildingBin::getNumBuildings() {
+ return smallBuildingLocations.size() + mediumBuildingLocations.size() + largeBuildingLocations.size();
+ }
+
+ bool SGBuildingBin::checkMinDist (SGVec3f p, float radius) {
+ BuildingInstanceList::iterator iter;
- 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++)
- {
- EffectGeode* geode = new EffectGeode;
- geode->setEffect(_effect.get());
- result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0)));
- }
- return result;
+ float r = (radius + smallBuildingMaxRadius) * (radius + smallBuildingMaxRadius);
+ for (iter = smallBuildingLocations.begin(); iter != smallBuildingLocations.end(); ++iter) {
+ if (iter->getDistSqr(p) < r) {
+ return false;
+ }
}
- float _range;
- ref_ptr<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);
+ r = (radius + mediumBuildingMaxRadius) * (radius + mediumBuildingMaxRadius);
+ for (iter = mediumBuildingLocations.begin(); iter != mediumBuildingLocations.end(); ++iter) {
+ if (iter->getDistSqr(p) < r) {
+ return false;
+ }
}
-};
-
-struct GetBuildingCoord
-{
- Vec3 operator() (const SGBuildingBin::Building& building) const
- {
- return toOsg(building.position);
+
+ r = (radius + largeBuildingMaxRadius) * (radius + largeBuildingMaxRadius);
+ for (iter = largeBuildingLocations.begin(); iter != largeBuildingLocations.end(); ++iter) {
+ if (iter->getDistSqr(p) < r) {
+ return false;
+ }
}
-};
+
+ return true;
+ }
+
+ SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) {
+
+ if (roll < smallBuildingFraction) {
+ return SGBuildingBin::SMALL;
+ }
+
+ if (roll < (smallBuildingFraction + mediumBuildingFraction)) {
+ return SGBuildingBin::MEDIUM;
+ }
+
+ return SGBuildingBin::LARGE;
+ }
-typedef QuadTreeBuilder<LOD*, SGBuildingBin::Building, MakeBuildingLeaf, AddBuildingLeafObject,
- GetBuildingCoord> BuildingGeometryQuadtree;
-}
+ float SGBuildingBin::getBuildingMaxRadius(BuildingType type) {
+
+ if (type == SGBuildingBin::SMALL) return smallBuildingMaxRadius;
+ if (type == SGBuildingBin::MEDIUM) return mediumBuildingMaxRadius;
+ if (type == SGBuildingBin::LARGE) return largeBuildingMaxRadius;
+
+ return 0;
+ }
+
+ float SGBuildingBin::getBuildingMaxDepth(BuildingType type) {
+
+ if (type == SGBuildingBin::SMALL) return smallBuildingMaxDepth;
+ if (type == SGBuildingBin::MEDIUM) return mediumBuildingMaxDepth;
+ if (type == SGBuildingBin::LARGE) return largeBuildingMaxDepth;
+
+ return 0;
+ }
+
+ ref_ptr<Group> SGBuildingBin::createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options)
+ {
+ ref_ptr<Effect> effect;
+ EffectMap::iterator iter = buildingEffectMap.find(texture);
-struct BuildingTransformer
-{
- BuildingTransformer(Matrix& mat_) : mat(mat_) {}
- SGBuildingBin::Building operator()(const SGBuildingBin::Building& building) const
+ if ((iter == buildingEffectMap.end())||
+ (!iter->second.lock(effect)))
{
- Vec3 pos = toOsg(building.position);
- return SGBuildingBin::Building(toSG(pos * mat), building);
+ SGPropertyNode_ptr effectProp = new SGPropertyNode;
+ makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
+ SGPropertyNode* params = makeChild(effectProp, "parameters");
+ // Main texture - n=0
+ params->getChild("texture", 0, true)->getChild("image", 0, true)
+ ->setStringValue(texture);
+
+ // Light map - n=3
+ params->getChild("texture", 3, true)->getChild("image", 0, true)
+ ->setStringValue(lightMap);
+
+ effect = makeEffect(effectProp, true, options);
+ if (iter == buildingEffectMap.end())
+ buildingEffectMap.insert(EffectMap::value_type(texture, effect));
+ else
+ iter->second = effect; // update existing, but empty observer
+ }
+
+ ref_ptr<Group> group = new osg::Group();
+
+ // Now, create a quadbuilding for the buildings.
+
+ BuildingInstanceList locs[] = { smallBuildingLocations,
+ SGBuildingBin::mediumBuildingLocations,
+ SGBuildingBin::largeBuildingLocations };
+
+ for (int i = 0; i < 3; i++)
+ {
+ BuildingGeometryQuadtree
+ quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(),
+ SG_BUILDING_QUAD_TREE_DEPTH,
+ MakeBuildingLeaf(20000.0, effect));
+
+ // Transform building positions from the "geocentric" positions we
+ // get from the scenery polys into the local Z-up coordinate
+ // system.
+ std::vector<BuildingInstance> rotatedBuildings;
+ rotatedBuildings.reserve(locs[i].size());
+ std::transform(locs[i].begin(), locs[i].end(),
+ std::back_inserter(rotatedBuildings),
+ BuildingInstanceTransformer(transInv));
+ quadbuilding.buildQuadTree(rotatedBuildings.begin(), rotatedBuildings.end());
+
+ for (size_t i = 0; i < quadbuilding.getRoot()->getNumChildren(); ++i)
+ group->addChild(quadbuilding.getRoot()->getChild(i));
}
- 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);
-
- SGBuildingBin* bin = NULL;
-
- BOOST_FOREACH(bin, buildings)
- {
- numBuildings = numBuildings + bin->getNumBuildings();
- SG_LOG(SG_TERRAIN, SG_DEBUG, "Total random buildings generated: " << numBuildings);
-
- ref_ptr<Effect> effect;
- EffectMap::iterator iter = buildingEffectMap.find(bin->texture);
-
- if ((iter == buildingEffectMap.end())||
- (!iter->second.lock(effect)))
- {
- SGPropertyNode_ptr effectProp = new SGPropertyNode;
- makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
- SGPropertyNode* params = makeChild(effectProp, "parameters");
- // Main texture - n=0
- params->getChild("texture", 0, true)->getChild("image", 0, true)
- ->setStringValue(bin->texture);
-
- // Light map - n=3
- params->getChild("texture", 3, true)->getChild("image", 0, true)
- ->setStringValue(bin->lightMap);
-
- effect = makeEffect(effectProp, true, options);
- if (iter == buildingEffectMap.end())
- buildingEffectMap.insert(EffectMap::value_type(bin->texture, effect));
- else
- iter->second = effect; // update existing, but empty observer
- }
- // 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();
- mt->addChild(group);
- delete bin;
- }
+ return group;
+ }
- buildings.clear();
+ // We may end up with a quadtree with many empty leaves. One might say
+ // that we should avoid constructing the leaves in the first place,
+ // but this node visitor tries to clean up after the fact.
+ struct QuadTreeCleaner : public osg::NodeVisitor
+ {
+ QuadTreeCleaner() : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
+ {
+ }
+ void apply(LOD& lod)
+ {
+ for (int i = lod.getNumChildren() - 1; i >= 0; --i) {
+ EffectGeode* geode = dynamic_cast<EffectGeode*>(lod.getChild(i));
+ if (!geode)
+ continue;
+ bool geodeEmpty = true;
+ if (geode->getNumDrawables() > 1) {
+ SG_LOG(SG_TERRAIN, SG_DEBUG, "Building LOD Drawables: " << geode->getNumDrawables());
+ }
+
+ for (unsigned j = 0; j < geode->getNumDrawables(); ++j) {
+ const Geometry* geom = dynamic_cast<Geometry*>(geode->getDrawable(j));
+ if (!geom) {
+ geodeEmpty = false;
+ break;
+ }
+ for (unsigned k = 0; k < geom->getNumPrimitiveSets(); k++) {
+ const PrimitiveSet* ps = geom->getPrimitiveSet(k);
+ if (ps->getNumIndices() > 0) {
+ geodeEmpty = false;
+ break;
+ }
+ }
+ }
+ if (geodeEmpty)
+ lod.removeChildren(i, 1);
+ }
+ }
+ };
- return mt;
-}
-
+ // 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);
+
+ SGBuildingBin* bin = NULL;
+
+ BOOST_FOREACH(bin, buildings)
+ {
+ numBuildings = numBuildings + bin->getNumBuildings();
+ ref_ptr<Group> group = bin->createBuildingsGroup(transInv, options);
+
+ for (size_t i = 0; i < group->getNumChildren(); ++i)
+ mt->addChild(group->getChild(i));
+
+ delete bin;
+ }
+
+ buildings.clear();
+ QuadTreeCleaner cleaner;
+ mt->accept(cleaner);
+ return mt;
+ }
}
}
}
- void computeRandomBuildings(SGMaterialLib* matlib, float building_density)
+ void computeRandomObjectsAndBuildings(
+ SGMaterialLib* matlib,
+ float building_density,
+ bool use_random_objects,
+ bool use_random_buildings)
{
SGMaterialTriangleMap::iterator i;
osg::Texture2D* object_mask = mat->get_object_mask(triangleBin);
- float coverage = mat->get_building_coverage();
+ int group_count = mat->get_object_group_count();
+ float building_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 (building_coverage > 0) {
+ BOOST_FOREACH(bin, randomBuildings)
+ {
+ if (bin->getMaterialName() == mat->get_names()[0]) {
+ found = true;
+ break;
+ }
}
- }
-
- if (!found) {
- bin = new SGBuildingBin();
- bin->texture = mat->get_building_texture();
- bin->lightMap = mat->get_building_lightmap();
- SG_LOG(SG_INPUT, SG_DEBUG, "Building texture " << bin->texture);
- randomBuildings.push_back(bin);
- }
-
- std::vector<std::pair<SGVec3f, float> > randomPoints;
+
+ if (!found) {
+ bin = new SGBuildingBin(mat);
+ randomBuildings.push_back(bin);
+ }
+ }
unsigned num = i->second.getNumTriangles();
- int triangle_dropped = 0;
- int building_dropped = 0;
int random_dropped = 0;
int mask_dropped = 0;
+ int building_dropped = 0;
+ int triangle_dropped = 0;
for (unsigned i = 0; i < num; ++i) {
- SGBuildingBin::BuildingList triangle_buildings;
SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i);
SGVec3f vorigin = triangleBin.getVertex(triangleRef[0]).vertex;
SGVec2f t1 = triangleBin.getVertex(triangleRef[2]).texCoord - torigin;
SGVec3f normal = cross(v0, v1);
+ // Containers to hold the random buildings and objects generated
+ // for this triangle for collision detection purposes.
+ std::vector< std::pair< SGVec3f, float> > triangleObjectsList;
+ std::vector< std::pair< SGVec3f, float> > triangleBuildingList;
+
// Compute the area
float area = 0.5f*length(normal);
if (area <= SGLimitsf::min())
continue;
- // 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);
- if (num < 1.0f) {
- continue;
- }
-
- // Apply density, which is linear, while we're dealing in areas
- num = num * building_density * building_density;
-
- // Cosine of the angle between the two vectors.
- float cosine = (dot(v0, v1) / (length(v0) * length(v1)));
-
- // Determine a grid spacing in each vector such that the correct
- // coverage will result.
- float stepv0 = (sqrtf(coverage) / building_density) / length(v0) / sqrtf(1 - cosine * cosine);
- float stepv1 = (sqrtf(coverage) / building_density) / length(v1);
-
- stepv0 = std::min(stepv0, 1.0f);
- stepv1 = std::min(stepv1, 1.0f);
-
- // Start at a random point. a will be immediately incremented below.
- float a = -mt_rand(&seed) * stepv0;
- float b = mt_rand(&seed) * stepv1;
+ // Generate any random objects
+ if (use_random_objects && (group_count > 0))
+ {
+ for (int j = 0; j < group_count; j++)
+ {
+ SGMatModelGroup *object_group = mat->get_object_group(j);
+ int nObjects = object_group->get_object_count();
+
+ if (nObjects == 0) continue;
+
+ // For each of the random models in the group, determine an appropriate
+ // number of random placements and insert them.
+ for (int k = 0; k < nObjects; k++) {
+ SGMatModel * object = object_group->get_object(k);
+
+ // Use the zombie door method to determine fractional object placement.
+ double n = area / object->get_coverage_m2() + mt_rand(&seed);
+
+ // place an object each unit of area
+ while ( n > 1.0 ) {
+ float a = mt_rand(&seed);
+ float b = mt_rand(&seed);
+ if ( a + b > 1 ) {
+ a = 1 - a;
+ b = 1 - b;
+ }
- // Place an object each unit of area
- while (num > 1.0) {
+ SGVec3f randomPoint = vorigin + a*v0 + b*v1;
+ float rotation = static_cast<float>(mt_rand(&seed));
+
+ // Check that the point is sufficiently far from
+ // the edge of the triangle by measuring the distance
+ // from the three lines that make up the triangle.
+ float spacing = object->get_spacing_m();
+
+ SGVec3f p = randomPoint - vorigin;
+ float edges[] = { length(cross(p , p - v0)) / length(v0),
+ length(cross(p - v0, p - v1)) / length(v1 - v0),
+ length(cross(p - v1, p )) / length(v1) };
+ float edge_dist = *std::min_element(edges, edges + 3);
+
+ if (edge_dist < spacing) {
+ n -= 1.0;
+ continue;
+ }
+
+ if (object_mask != NULL) {
+ SGVec2f texCoord = torigin + a*t0 + b*t1;
+
+ // Check this random point against the object mask
+ // blue (for buildings) channel.
+ osg::Image* img = object_mask->getImage();
+ 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()) {
+ // Failed object mask check
+ n -= 1.0;
+ continue;
+ }
+
+ rotation = img->getColor(x,y).r();
+ }
+
+ bool close = false;
- // Set the next location to place a building
- a += stepv0;
-
- if ((a + b) > 1.0f) {
- // Reached the end of the scan-line on v0. Reset and increment
- // scan-line on v1
- a = mt_rand(&seed) * stepv0;
- b += stepv1;
- }
-
- if (b > 1.0f) {
- // In a degenerate case of a single point, we might be outside the
- // scanline. Note that we need to still ensure that a+b < 1.
- b = mt_rand(&seed) * stepv1 * (1.0f - a);
+ // Check it isn't too close to any other random objects in the triangle
+ std::vector<std::pair<SGVec3f, float> >::iterator l;
+ for (l = triangleObjectsList.begin(); l != triangleObjectsList.end(); ++l) {
+ float min_dist2 = (l->second + object->get_spacing_m()) *
+ (l->second + object->get_spacing_m());
+
+ if (distSqr(l->first, randomPoint) > min_dist2) {
+ close = true;
+ continue;
+ }
+ }
+
+ if (!close) {
+ triangleObjectsList.push_back(std::make_pair(randomPoint, object->get_spacing_m()));
+ randomModels.insert(randomPoint,
+ object,
+ (int)object->get_randomized_range_m(&seed),
+ rotation);
+ }
+
+ n -= 1.0;
+ }
+ }
}
-
- if ((a + b) > 1.0f ) {
- // Truly degenerate case - simply choose a random point guaranteed
- // to fulfil the constraing of a+b < 1.
- a = mt_rand(&seed);
- b = mt_rand(&seed) * (1.0f - a);
+ }
+
+ // Random objects now generated. Now generate the random buildings (if any);
+ if (use_random_buildings && (building_coverage > 0)) {
+ // 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 / building_coverage + mt_rand(&seed);
+ if (num < 1.0f) {
+ continue;
}
+
+ // Apply density, which is linear, while we're dealing in areas
+ num = num * building_density * building_density;
- SGVec3f randomPoint = vorigin + a*v0 + b*v1;
- float rotation = mt_rand(&seed);
-
- if (object_mask != NULL) {
- SGVec2f texCoord = torigin + a*t0 + b*t1;
- osg::Image* img = object_mask->getImage();
- int x = (int) (img->s() * texCoord.x()) % img->s();
- int y = (int) (img->t() * texCoord.y()) % img->t();
-
- // In some degenerate cases x or y can be < 1, in which case the mod operand fails
- while (x < 0) x += img->s();
- while (y < 0) y += img->t();
-
- if (mt_rand(&seed) < img->getColor(x, y).b()) {
- // Object passes mask. Rotation is taken from the red channel
- rotation = img->getColor(x,y).r();
- } else {
- // Fails mask test - try again.
- mask_dropped++;
- num -= 1.0;
- continue;
- }
- }
+ // Cosine of the angle between the two vectors.
+ float cosine = (dot(v0, v1) / (length(v0) * length(v1)));
+
+ // Determine a grid spacing in each vector such that the correct
+ // coverage will result.
+ float stepv0 = (sqrtf(building_coverage) / building_density) / length(v0) / sqrtf(1 - cosine * cosine);
+ float stepv1 = (sqrtf(building_coverage) / building_density) / length(v1);
- // 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);
+ stepv0 = std::min(stepv0, 1.0f);
+ stepv1 = std::min(stepv1, 1.0f);
- 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 = SGMisc<double>::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));
+ // Start at a random point. a will be immediately incremented below.
+ float a = -mt_rand(&seed) * stepv0;
+ float b = mt_rand(&seed) * stepv1;
+
+ // Place an object each unit of area
+ while (num > 1.0) {
+
+ // Set the next location to place a building
+ a += stepv0;
- // Small buildings are never deeper than they are wide.
- if (depth > width) { depth = width; }
+ if ((a + b) > 1.0f) {
+ // Reached the end of the scan-line on v0. Reset and increment
+ // scan-line on v1
+ a = mt_rand(&seed) * stepv0;
+ b += stepv1;
+ }
- 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 = SGMisc<double>::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));
+ if (b > 1.0f) {
+ // In a degenerate case of a single point, we might be outside the
+ // scanline. Note that we need to still ensure that a+b < 1.
+ b = mt_rand(&seed) * stepv1 * (1.0f - a);
+ }
- while ((height > width) && (floors > mat->get_building_medium_min_floors())) {
- // Ensure that medium buildings aren't taller than they are wide
- floors--;
- height = floors * (2.8 + mt_rand(&seed));
+ if ((a + b) > 1.0f ) {
+ // Truly degenerate case - simply choose a random point guaranteed
+ // to fulfil the constraing of a+b < 1.
+ a = mt_rand(&seed);
+ b = mt_rand(&seed) * (1.0f - a);
}
- 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 = SGMisc<double>::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.
- SGVec3f p = randomPoint - vorigin;
-
- if (((length(cross(p , p - v0)) / length(v0)) < radius) ||
- ((length(cross(p - v0, p - v1)) / length(v1 - v0)) < radius) ||
- ((length(cross(p - v1, p )) / length(v1)) < radius) )
- {
- triangle_dropped++;
- num -= 1.0;
- continue;
- }
+ SGVec3f randomPoint = vorigin + a*v0 + b*v1;
+ float rotation = mt_rand(&seed);
+
+ if (object_mask != NULL) {
+ SGVec2f texCoord = torigin + a*t0 + b*t1;
+ osg::Image* img = object_mask->getImage();
+ int x = (int) (img->s() * texCoord.x()) % img->s();
+ int y = (int) (img->t() * texCoord.y()) % img->t();
+
+ // In some degenerate cases x or y can be < 1, in which case the mod operand fails
+ while (x < 0) x += img->s();
+ while (y < 0) y += img->t();
+
+ if (mt_rand(&seed) < img->getColor(x, y).b()) {
+ // Object passes mask. Rotation is taken from the red channel
+ rotation = img->getColor(x,y).r();
+ } else {
+ // Fails mask test - try again.
+ mask_dropped++;
+ num -= 1.0;
+ continue;
+ }
+ }
- // Check 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;
+ // Check building isn't too close to the triangle edge.
+ float type_roll = mt_rand(&seed);
+ SGBuildingBin::BuildingType buildingtype = bin->getBuildingType(type_roll);
+ float radius = bin->getBuildingMaxRadius(buildingtype);
+
+ // Determine the actual center of the building, by shifting from the
+ // center of the front face to the true center.
+ osg::Matrix rotationMat = osg::Matrix::rotate(- rotation * M_PI * 2,
+ osg::Vec3f(0.0, 0.0, 1.0));
+ SGVec3f buildingCenter = randomPoint + toSG(osg::Vec3f(-0.5 * bin->getBuildingMaxDepth(buildingtype), 0.0, 0.0) * rotationMat);
+
+ SGVec3f p = buildingCenter - vorigin;
+ float edges[] = { length(cross(p , p - v0)) / length(v0),
+ length(cross(p - v0, p - v1)) / length(v1 - v0),
+ length(cross(p - v1, p )) / length(v1) };
+ float edge_dist = *std::min_element(edges, edges + 3);
- if (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) {
+ if (edge_dist < radius) {
+ num -= 1.0;
+ triangle_dropped++;
+ continue;
+ }
+
+ // Check building isn't too close to random objects and other buildings.
+ bool close = false;
+ std::vector<std::pair<SGVec3f, float> >::iterator iter;
- float min_dist = l->radius + radius + min_spacing;
- min_dist = min_dist * min_dist;
+ for (iter = triangleBuildingList.begin(); iter != triangleBuildingList.end(); ++iter) {
+ float min_dist = iter->second + radius;
+ if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) {
+ close = true;
+ continue;
+ }
+ }
- if (distSqr(randomPoint, l->position) < min_dist) {
+ if (close) {
+ num -= 1.0;
building_dropped++;
- too_close = true;
- continue;
+ continue;
}
- }
-
- if (too_close) {
- // Too close to another building - drop and try again
+
+ for (iter = triangleObjectsList.begin(); iter != triangleObjectsList.end(); ++iter) {
+ float min_dist = iter->second + radius;
+ if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) {
+ close = true;
+ continue;
+ }
+ }
+
+ if (close) {
+ num -= 1.0;
+ random_dropped++;
+ continue;
+ }
+
+ std::pair<SGVec3f, float> pt = std::make_pair(buildingCenter, radius);
+ triangleBuildingList.push_back(pt);
+ bin->insert(randomPoint, rotation, buildingtype);
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);
}
- triangle_buildings.clear();
+ triangleObjectsList.clear();
+ triangleBuildingList.clear();
}
SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << bin->getNumBuildings());
SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to mask: " << mask_dropped);
- SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to triangle edge: " << triangle_dropped);
SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to random object: " << random_dropped);
- SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to other building: " << building_dropped);
+ SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to other buildings: " << building_dropped);
}
}
}
}
- void computeRandomObjects(SGMaterialLib* matlib)
- {
- SGMaterialTriangleMap::iterator i;
-
- // generate a repeatable random seed
- mt seed;
- mt_init(&seed, unsigned(123));
-
- for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
- SGMaterial *mat = matlib->find(i->first);
- if (!mat)
- continue;
-
- int group_count = mat->get_object_group_count();
-
- if (group_count > 0)
- {
- for (int j = 0; j < group_count; j++)
- {
- SGMatModelGroup *object_group = mat->get_object_group(j);
- int nObjects = object_group->get_object_count();
-
- if (nObjects > 0)
- {
- // For each of the random models in the group, determine an appropriate
- // number of random placements and insert them.
- for (int k = 0; k < nObjects; k++) {
- SGMatModel * object = object_group->get_object(k);
-
- std::vector<std::pair<SGVec3f, float> > randomPoints;
-
- i->second.addRandomPoints(object->get_coverage_m2(),
- 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;
-
- for (unsigned i = 0; i < randomModels.getNumModels(); i++) {
- float spacing = randomModels.getMatModel(i).model->get_spacing_m() + object->get_spacing_m();
- spacing = spacing * spacing;
-
- 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);
- }
- }
- }
- }
- }
- }
- }
- }
-
bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
{
if (!insertPtGeometry(obj, matlib))
osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
if (node)
terrainGroup->addChild(node);
+
+ if (matlib && (use_random_objects || use_random_buildings)) {
+ tileGeometryBin.computeRandomObjectsAndBuildings(matlib,
+ building_density,
+ use_random_objects,
+ use_random_buildings);
+ }
- if (use_random_objects && matlib) {
- tileGeometryBin.computeRandomObjects(matlib);
-
- if (tileGeometryBin.randomModels.getNumModels() > 0) {
- // Generate a repeatable random seed
- mt seed;
- mt_init(&seed, unsigned(123));
+ if (tileGeometryBin.randomModels.getNumModels() > 0) {
+ // Generate a repeatable random seed
+ mt seed;
+ mt_init(&seed, unsigned(123));
- std::vector<ModelLOD> models;
- for (unsigned int i = 0;
- i < tileGeometryBin.randomModels.getNumModels(); i++) {
- SGMatModelBin::MatModel obj
- = tileGeometryBin.randomModels.getMatModel(i);
+ std::vector<ModelLOD> models;
+ for (unsigned int i = 0;
+ i < tileGeometryBin.randomModels.getNumModels(); i++) {
+ SGMatModelBin::MatModel obj
+ = tileGeometryBin.randomModels.getMatModel(i);
- SGPropertyNode* root = options->getPropertyNode()->getRootNode();
- osg::Node* node = obj.model->get_random_model(root, &seed);
-
- // Create a matrix to place the object in the correct
- // location, and then apply the rotation matrix created
- // above, with an additional random (or taken from
- // the object mask) heading rotation if appropriate.
- osg::Matrix transformMat;
- transformMat = osg::Matrix::translate(toOsg(obj.position));
- if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
- // Rotate the object around the z axis.
- double hdg = mt_rand(&seed) * M_PI * 2;
- transformMat.preMult(osg::Matrix::rotate(hdg,
- osg::Vec3d(0.0, 0.0, 1.0)));
- }
+ SGPropertyNode* root = options->getPropertyNode()->getRootNode();
+ osg::Node* node = obj.model->get_random_model(root, &seed);
+
+ // Create a matrix to place the object in the correct
+ // location, and then apply the rotation matrix created
+ // above, with an additional random (or taken from
+ // the object mask) heading rotation if appropriate.
+ osg::Matrix transformMat;
+ transformMat = osg::Matrix::translate(toOsg(obj.position));
+ if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
+ // Rotate the object around the z axis.
+ double hdg = mt_rand(&seed) * M_PI * 2;
+ transformMat.preMult(osg::Matrix::rotate(hdg,
+ osg::Vec3d(0.0, 0.0, 1.0)));
+ }
- if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) {
- // Rotate the object around the z axis.
- double hdg = - obj.rotation * M_PI * 2;
- transformMat.preMult(osg::Matrix::rotate(hdg,
- osg::Vec3d(0.0, 0.0, 1.0)));
- }
-
- osg::MatrixTransform* position =
- new osg::MatrixTransform(transformMat);
- position->addChild(node);
- models.push_back(ModelLOD(position, obj.lod));
+ 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)));
}
- RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
- quadtree.buildQuadTree(models.begin(), models.end());
- randomObjects = quadtree.getRoot();
- randomObjects->setName("random objects");
+
+ 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 (tileGeometryBin.randomBuildings.size() > 0) {
+ buildingNode = createRandomBuildings(tileGeometryBin.randomBuildings, osg::Matrix::identity(),
+ options);
+ buildingNode->setName("Random buildings");
}
if (use_random_vegetation && matlib) {
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