diff --git a/simgear/scene/material/mat.cxx b/simgear/scene/material/mat.cxx index 0eaa37a6..f62adbef 100644 --- a/simgear/scene/material/mat.cxx +++ b/simgear/scene/material/mat.cxx @@ -108,7 +108,7 @@ SGMaterial::SGMaterial( const SGReaderWriterOptions* options, } SGMaterial::SGMaterial( const osgDB::Options* options, - const SGPropertyNode *props, + const SGPropertyNode *props, SGPropertyNode *prop_root, AreaList *a, SGSharedPtr c) @@ -210,7 +210,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, _internal_state st( NULL, tpath.local8BitStr(), true, options ); _status.push_back( st ); } - + std::vector masks = props->getChildren("object-mask"); for (unsigned int i = 0; i < masks.size(); i++) { @@ -243,8 +243,8 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, // the object mask, as DDS textures have an origin at the bottom // left rather than top left. Therefore we flip a copy of the image // (otherwise a second reference to the object mask would flip it - // back!). - SG_LOG(SG_GENERAL, SG_DEBUG, "Flipping object mask" << omname); + // back!). + SG_LOG(SG_GENERAL, SG_DEBUG, "Flipping object mask" << omname); image = (osg::Image* ) image->clone(osg::CopyOp::SHALLOW_COPY); image->flipVertical(); } @@ -271,30 +271,30 @@ 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); - + std::string bt = props->getStringValue( "building-texture", "Textures/buildings.png" ); - building_texture = SGModelLib::findDataFile(bt, options); - + building_texture = SGModelLib::findDataFile(bt, options); + if (building_texture.empty()) { SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt); } - + bt = props->getStringValue("building-lightmap", "Textures/buildings-lightmap.png"); - building_lightmap = SGModelLib::findDataFile(bt, options); - + building_lightmap = SGModelLib::findDataFile(bt, options); + if (building_lightmap.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); @@ -305,27 +305,40 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, 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); building_range = props->getDoubleValue("building-range-m", default_object_range); - + + // There are some constraints on the maximum building size that we can sensibly render. + // Using values outside these ranges will result in the texture being stretched to fit, + // which may not be desireable. We will allow it, but display warnings. + // We do not display warnings for large buildings as we assume the textures are sufficiently + // generic to be stretched without problems. + if (building_small_max_floors > 3) SG_LOG(SG_GENERAL, SG_ALERT, "building-small-max-floors exceeds maximum (3). Texture will be stretched to fit."); + if (building_medium_max_floors > 8) SG_LOG(SG_GENERAL, SG_ALERT, "building-medium-max-floors exceeds maximum (8). Texture will be stretched to fit."); + if (building_large_max_floors > 22) SG_LOG(SG_GENERAL, SG_ALERT, "building-large-max-floors exceeds maximum (22). Texture will be stretched to fit."); + if (building_small_max_width > 192.0) SG_LOG(SG_GENERAL, SG_ALERT, "building-small-max-width-m exceeds maximum (192). Texture will be stretched to fit."); + if (building_small_max_depth > 192.0) SG_LOG(SG_GENERAL, SG_ALERT, "building-small-max-depth-m exceeds maximum (192). Texture will be stretched to fit."); + if (building_medium_max_width > 80.0) SG_LOG(SG_GENERAL, SG_ALERT, "building-medium-max-width-m exceeds maximum (80). Texture will be stretched to fit."); + if (building_medium_max_depth > 80.0) SG_LOG(SG_GENERAL, SG_ALERT, "building-medium-max-depth-m exceeds maximum (80). Texture will be stretched to fit."); + cos_object_max_density_slope_angle = cos(props->getFloatValue("object-max-density-angle-deg", 20.0) * osg::PI/180.0); cos_object_zero_density_slope_angle = cos(props->getFloatValue("object-zero-density-angle-deg", 30.0) * osg::PI/180.0); - + // Random vegetation properties wood_coverage = props->getDoubleValue("wood-coverage", 0.0); tree_effect = props->getStringValue("tree-effect", "Effects/tree"); @@ -337,7 +350,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, cos_tree_zero_density_slope_angle = cos(props->getFloatValue("tree-zero-density-angle-deg", 45.0) * osg::PI/180.0); const SGPropertyNode* treeTexNode = props->getChild("tree-texture"); - + if (treeTexNode) { std::string treeTexPath = props->getStringValue("tree-texture"); @@ -398,7 +411,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, if (name) glyphs[name] = new SGMaterialGlyph(glyph_nodes[i]); } - + // Read parameters entry, which is passed into the effect if (props->hasChild("parameters")) { parameters = props->getChild("parameters"); @@ -413,7 +426,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, // Private methods. //////////////////////////////////////////////////////////////////////// -void +void SGMaterial::init () { _status.clear(); @@ -437,7 +450,7 @@ SGMaterial::init () } Effect* SGMaterial::get_effect(int i) -{ +{ if(!_status[i].effect_realized) { if (!_status[i].effect.valid()) return 0; @@ -454,7 +467,7 @@ Effect* SGMaterial::get_one_effect(int texIndex) SG_LOG( SG_GENERAL, SG_WARN, "No effect available."); return 0; } - + int i = texIndex % _status.size(); return get_effect(i); } @@ -472,9 +485,9 @@ osg::Texture2D* SGMaterial::get_one_object_mask(int texIndex) SG_LOG( SG_GENERAL, SG_WARN, "No mask available."); return 0; } - + // Note that the object mask is closely linked to the texture/effect - // so we index based on the texture index, + // so we index based on the texture index, unsigned int i = texIndex % _status.size(); if (i < _masks.size()) { return _masks[i].get(); @@ -489,10 +502,10 @@ void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options) ref_ptr user = new SGMaterialUserData(this); SGPropertyNode_ptr propRoot = new SGPropertyNode(); makeChild(propRoot, "inherits-from")->setStringValue(effect); - + SGPropertyNode* paramProp = makeChild(propRoot, "parameters"); copyProperties(parameters, paramProp); - + SGPropertyNode* materialProp = makeChild(paramProp, "material"); makeChild(materialProp, "ambient")->setValue(SGVec4d(ambient)); makeChild(materialProp, "diffuse")->setValue(SGVec4d(diffuse)); diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index 10158a5d..96465d89 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -55,7 +55,6 @@ #include "ShaderGeometry.hxx" #include "SGBuildingBin.hxx" - using namespace osg; namespace simgear @@ -67,44 +66,308 @@ static BuildingStateSetMap statesetmap; typedef std::map > EffectMap; static EffectMap buildingEffectMap; -// Building instance scheme: -// vertex - local position of vertices, with 0,0,0 being the center front. -// fog coord - rotation -// color - xyz of tree quad origin, replicated 4 times. - -struct BuildingBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback +// Helper classes for creating the quad tree +struct MakeBuildingLeaf { - BuildingBoundingBoxCallback() {} - BuildingBoundingBoxCallback(const BuildingBoundingBoxCallback&, const CopyOp&) {} - META_Object(simgear, BuildingBoundingBoxCallback); - virtual BoundingBox computeBound(const Drawable&) const; + MakeBuildingLeaf(float range, Effect* effect, bool fade) : + _range(range), _effect(effect), _fade_out(fade) {} + + MakeBuildingLeaf(const MakeBuildingLeaf& rhs) : + _range(rhs._range), _effect(rhs._effect), _fade_out(rhs._fade_out) + {} + + LOD* operator() () const + { + osg::Vec3Array* v = new osg::Vec3Array; + osg::Vec2Array* t = new osg::Vec2Array; + osg::Vec3Array* n = new osg::Vec3Array; + osg::Vec4Array* c = new osg::Vec4Array; + // Color array is used to identify the different building faces by the + // vertex shader for texture mapping. + + v->reserve(48); + t->reserve(48); + n->reserve(48); + c->reserve(48); + + // Now create an OSG Geometry based on the Building + // 0,0,0 is the bottom center of the front + // face, e.g. where the front door would be + + // BASEMENT + // This extends 10m below the main section + // Front face + v->push_back( osg::Vec3( 0.0, -0.5, -1.0) ); // bottom right + v->push_back( osg::Vec3( 0.0, 0.5, -1.0) ); // bottom left + v->push_back( osg::Vec3( 0.0, 0.5, 0.0) ); // top left + v->push_back( osg::Vec3( 0.0, -0.5, 0.0) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(1, 0, 0) ); // normal + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to identify faces + } + + // Left face + v->push_back( osg::Vec3( -1.0, -0.5, -1.0) ); // bottom right + v->push_back( osg::Vec3( 0.0, -0.5, -1.0) ); // bottom left + v->push_back( osg::Vec3( 0.0, -0.5, 0.0) ); // top left + v->push_back( osg::Vec3( -1.0, -0.5, 0.0) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0, -1, 0) ); // normal + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to identify faces + } + + // Back face + v->push_back( osg::Vec3( -1.0, 0.5, -1.0) ); // bottom right + v->push_back( osg::Vec3( -1.0, -0.5, -1.0) ); // bottom left + v->push_back( osg::Vec3( -1.0, -0.5, 0.0) ); // top left + v->push_back( osg::Vec3( -1.0, 0.5, 0.0) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(-1, 0, 0) ); // normal + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to identify faces + } + + // Right face + v->push_back( osg::Vec3( 0.0, 0.5, -1.0) ); // bottom right + v->push_back( osg::Vec3( -1.0, 0.5, -1.0) ); // bottom left + v->push_back( osg::Vec3( -1.0, 0.5, 0.0) ); // top left + v->push_back( osg::Vec3( 0.0, 0.5, 0.0) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0, 1, 0) ); // normal + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to identify faces + } + + // MAIN BODY + // Front face + v->push_back( osg::Vec3( 0.0, -0.5, 0.0) ); // bottom right + v->push_back( osg::Vec3( 0.0, 0.5, 0.0) ); // bottom left + v->push_back( osg::Vec3( 0.0, 0.5, 1.0) ); // top left + v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(1, 0, 0) ); // normal + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to identify faces + } + + // Left face + v->push_back( osg::Vec3( -1.0, -0.5, 0.0) ); // bottom right + v->push_back( osg::Vec3( 0.0, -0.5, 0.0) ); // bottom left + v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // top left + v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0, -1, 0) ); // normal + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to identify faces + } + + // Back face + v->push_back( osg::Vec3( -1.0, 0.5, 0.0) ); // bottom right + v->push_back( osg::Vec3( -1.0, -0.5, 0.0) ); // bottom left + v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // top left + v->push_back( osg::Vec3( -1.0, 0.5, 1.0) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(-1, 0, 0) ); // normal + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to identify faces + } + + // Right face + v->push_back( osg::Vec3( 0.0, 0.5, 0.0) ); // bottom right + v->push_back( osg::Vec3( -1.0, 0.5, 0.0) ); // bottom left + v->push_back( osg::Vec3( -1.0, 0.5, 1.0) ); // top left + v->push_back( osg::Vec3( 0.0, 0.5, 1.0) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0, 1, 0) ); // normal + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to identify faces + } + + // ROOF - assume pitched - will be flattened in shader if required. + // Front pitched roof + v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // bottom right + v->push_back( osg::Vec3( 0.0, 0.5, 1.0) ); // bottom left + v->push_back( osg::Vec3( -0.5, 0.5, 1.5) ); // top left + v->push_back( osg::Vec3( -0.5, -0.5, 1.5) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0.707, 0, 0.707) ); // normal + c->push_back( osg::Vec4(0, 0, 1, 0) ); // color - used to identify faces + } + + // Left pitched roof + v->push_back( osg::Vec3(-1.0, -0.5, 1.0) ); // bottom right + v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // bottom left + v->push_back( osg::Vec3(-0.5, -0.5, 1.5) ); // top left + v->push_back( osg::Vec3(-0.5, -0.5, 1.5) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0, -1, 0) ); // normal + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to identify faces + } + + // Back pitched roof + v->push_back( osg::Vec3(-1.0, 0.5, 1.0) ); // bottom right + v->push_back( osg::Vec3(-1.0, -0.5, 1.0) ); // bottom left + v->push_back( osg::Vec3(-0.5, -0.5, 1.5) ); // top left + v->push_back( osg::Vec3(-0.5, 0.5, 1.5) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(-0.707, 0, 0.707) ); // normal + c->push_back( osg::Vec4(0, 0, 1, 0) ); // color - used to identify faces + } + + // Right pitched roof + v->push_back( osg::Vec3( 0.0, 0.5, 1.0) ); // bottom right + v->push_back( osg::Vec3(-1.0, 0.5, 1.0) ); // bottom left + v->push_back( osg::Vec3(-0.5, 0.5, 1.5) ); // top left + v->push_back( osg::Vec3(-0.5, 0.5, 1.5) ); // top right + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0, 1, 0) ); // normal + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to identify faces + } + + // 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 + + // Texture coordinates are adjusted in the shader. Here we just use + // the full [0,1] range. + + // BASEMENT - uses the baseline texture + for (unsigned int i = 0; i < 16; i++) { + t->push_back( osg::Vec2( 0.0, 0.0) ); + } + + // MAIN BODY + // Front + t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom right + t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom left + t->push_back( osg::Vec2( 0.0, 1.0) ); // top left + t->push_back( osg::Vec2( 1.0, 1.0) ); // top right + + // Left + t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom right + t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom left + t->push_back( osg::Vec2( 1.0, 1.0) ); // top left + t->push_back( osg::Vec2( 0.0, 1.0) ); // top right + + // Back (same as front for the moment) + t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom right + t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom left + t->push_back( osg::Vec2( 0.0, 1.0 ) ); // top left + t->push_back( osg::Vec2( 1.0, 1.0 ) ); // top right + + // Right (same as left for the moment) + t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom right + t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom left + t->push_back( osg::Vec2( 1.0, 1.0 ) ); // top left + t->push_back( osg::Vec2( 0.0, 1.0 ) ); // top right + + // ROOF + // ROOF - Front top surface. Negative x-coord will wrap texture + t->push_back( osg::Vec2( -1.0, 0.0) ); // bottom right + t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom left + t->push_back( osg::Vec2( 0.0, 1.0 ) ); // top left + t->push_back( osg::Vec2( -1.0, 1.0 ) ); // top right + + // ROOF - Left + t->push_back( osg::Vec2( 1.0, 1.0) ); // bottom right + t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom left + t->push_back( osg::Vec2( 1.0, 0.0 ) ); // top left + t->push_back( osg::Vec2( 1.0, 1.0 ) ); // top right + + // ROOF - Back (same as front for the moment) + t->push_back( osg::Vec2( -1.0, 0.0) ); // bottom right + t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom left + t->push_back( osg::Vec2( 0.0, 1.0 ) ); // top left + t->push_back( osg::Vec2( -1.0, 1.0 ) ); // top right + + // ROOF - Right (same as left for the moment) + t->push_back( osg::Vec2( 1.0, 1.0) ); // bottom right + t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom left + t->push_back( osg::Vec2( 1.0, 0.0 ) ); // top left + t->push_back( osg::Vec2( 1.0, 1.0 ) ); // top right + + Geometry* geom = new Geometry; + static int buildingCounter = 0; + geom->setName("BuildingGeometry_" + std::to_string(buildingCounter++)); + geom->setVertexArray(v); + geom->setTexCoordArray(0, t, Array::BIND_PER_VERTEX); + geom->setNormalArray(n, Array::BIND_PER_VERTEX); + geom->setColorArray(c); + geom->setUseDisplayList( false ); + geom->setUseVertexBufferObjects( true ); + geom->setComputeBoundingBoxCallback(new BuildingBoundingBoxCallback); + + geom->setVertexAttribArray(BUILDING_POSITION_ATTR, new osg::Vec3Array, Array::BIND_PER_VERTEX); + geom->setVertexAttribArray(BUILDING_SCALE_ATTR, new osg::Vec3Array, Array::BIND_PER_VERTEX); + geom->setVertexAttribArray(BUILDING_ROT_PITCH_TEX0X_ATTR, new osg::Vec3Array, Array::BIND_PER_VERTEX); + geom->setVertexAttribArray(BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR, new osg::Vec3Array, Array::BIND_PER_VERTEX); + + geom->addPrimitiveSet( new osg::DrawArrays( GL_QUADS, 0, 48, 0) ); + + EffectGeode* geode = new EffectGeode; + geode->addDrawable(geom); + geode->setEffect(_effect.get()); + + StateSet* ss = geode->getOrCreateStateSet(); + ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_POSITION_ATTR, 1)); + ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_SCALE_ATTR, 1)); + ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_ROT_PITCH_TEX0X_ATTR, 1)); + ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR, 1)); + + LOD* result = new LOD; + result->addChild(geode, 0, _range); + return result; + } + + float _range; + ref_ptr _effect; + bool _fade_out; }; -BoundingBox -BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const +struct AddBuildingLeafObject { - BoundingBox bb; - const Geometry* geom = static_cast(&drawable); - const Vec3Array* v = static_cast(geom->getVertexArray()); - const Vec4Array* pos = static_cast(geom->getColorArray()); + void operator() (LOD* lod, const SGBuildingBin::BuildingInstance building) const + { + //Geode* geode = static_cast(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren())); + Geode* geode = static_cast(lod->getChild(0)); - Geometry::PrimitiveSetList primSets = geom->getPrimitiveSetList(); - for (Geometry::PrimitiveSetList::const_iterator psitr = primSets.begin(), psend = primSets.end(); - psitr != psend; - ++psitr) { - DrawArrays* da = static_cast(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); - } + Geometry* geom = static_cast(geode->getDrawable(0)); + + osg::Vec3Array* positions = static_cast (geom->getVertexAttribArray(BUILDING_POSITION_ATTR)); // (x,y,z) + osg::Vec3Array* scale = static_cast (geom->getVertexAttribArray(BUILDING_SCALE_ATTR)); // (width, depth, height) + osg::Vec3Array* rot = static_cast (geom->getVertexAttribArray(BUILDING_ROT_PITCH_TEX0X_ATTR)); // (rotation, pitch height, texture x offset) + osg::Vec3Array* tex = static_cast (geom->getVertexAttribArray(BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR)); // (texture y offset, texture x gain, texture y gain) + + positions->push_back(building.position); + scale->push_back(osg::Vec3(building.width, building.depth, building.height)); + rot->push_back(osg::Vec3(building.rotation, building.pitch_height, building.tex0.x())); + tex->push_back(osg::Vec3(building.tex0.y(), building.tex1.x(), building.tex1.y())); + + if ((building.tex0.y() > (1470.0f / 2048.0f)) && (building.tex1.x() > 0.25f)) cerr << "Building texture out of bounds " << building.tex0.y() << " " << building.tex1.x() << "\n"; + + DrawArrays* primSet = static_cast(geom->getPrimitiveSet(0)); + primSet->setNumInstances(positions->size()); } - return bb; -} +}; + +struct GetBuildingCoord +{ + Vec3 operator() (const SGBuildingBin::BuildingInstance& building) const + { + return building.position; + } +}; + +typedef QuadTreeBuilder BuildingGeometryQuadtree; + // Set up a BuildingBin from a file containing a list of individual building // positions. @@ -118,12 +381,12 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const sg_gzifstream stream(absoluteFileName); if (!stream.is_open()) { - SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to open " << absoluteFileName << " does not exist."); + SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to open " << absoluteFileName); return; } while (!stream.eof()) { - // read a line. Each line defines a single builing position, and may have + // read a line. Each line defines a single building position, and may have // a comment, starting with # std::string line; std::getline(stream, line); @@ -136,24 +399,44 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const // and process further std::stringstream in(line); - // Line format is X Y Z R T + // Line format is X Y Z R B W D H P S O F T // where: - // X,Y,Z are the cartesian coordinates of the bottom SW corner of the building. +X is East, +Y is North - // R is the building rotation in degrees centered on the SW corner - // T is the building type [0, 1, 2] for SMALL, MEDIUM, LARGE - float x, y, z, r; - int t; - in >> x >> y >> z >> r >> t; + // X,Y,Z are the cartesian coordinates of the center of the front face. +X is East, +Y is North + // R is the building rotation in degrees centered on the middle of the front face. + // B is the building type [0, 1, 2] for SMALL, MEDIUM, LARGE + // W is the building width in meters + // D is the building depth in meters + // H is the building height in meters, excluding any pitched roof + // P is the pitch height in meters. 0 for a flat roof + // S is the roof shape (currently unused - all roofs are flat or gabled depending on pitch height) : + // 0=flat 1=skillion 2=gabled 3=half-hipped 4=hipped 5=pyramidal 6=gambled + // 7=mansard 8=dome 9=onion 10=round 11=saltbox + // O is the roof ridge orientation (currently unused, all roofs are assumed orthogonal) : + // 0 = parallel to the front face of the building + // 1 = orthogonal to the front face of the building + // F is the number of floors (integer) + // T is the texture index to use (integer). Buildings with the same T value will have the same texture assigned. There are 6 small, 6 medium and 4 large textures. + float x, y, z, r, w, d, h, p; + int b, s, o, f, t; + in >> x >> y >> z >> r >> b >> w >> d >> h >> p >> s >> o >> f >> t; - //SG_LOG(SG_TERRAIN, SG_ALERT, "Building entry " << x << " " << y << " " << z << " " << t ); - SGVec3f p = SGVec3f(x,y,z); + //SG_LOG(SG_TERRAIN, SG_ALERT, "Building entry " << x << " " << y << " " << z << " " << b ); + SGVec3f loc = SGVec3f(x,y,z); BuildingType type = BuildingType::SMALL; - if (t == 1) type = BuildingType::MEDIUM; - if (t == 2) type = BuildingType::LARGE; + if (b == 1) type = BuildingType::MEDIUM; + if (b == 2) type = BuildingType::LARGE; // Rotation is in the file as degrees, but in the datastructure normalized // to 0.0 - 1.0 - insert(p, (float) (r / 360.0f), type); + float rot = (float) (r / 360.0f); + + if (w == 0.0f) { + // If width is not defined, then we assume we don't have a full set of + // data for the buildings so just use the random building definitions + insert(loc, rot, type); + } else { + insert(loc, rot, type, w, d, h, p, f, t); + } } stream.close(); @@ -161,594 +444,148 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const // Set up the building set based on the material definitions SGBuildingBin::SGBuildingBin(const SGMaterial *mat, bool useVBOs) { - material_name = new std::string(mat->get_names()[0]); SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << material_name); + material = mat; texture = new std::string(mat->get_building_texture()); lightMap = new std::string(mat->get_building_lightmap()); + buildingRange = mat->get_building_range(); SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << texture); + } + + SGBuildingBin::~SGBuildingBin() { + buildingLocations.clear(); + } + + // Generate a building specifying the exact position, dimensions and texture index. + void SGBuildingBin::insert(SGVec3f p, float r, BuildingType buildingtype, float width, float depth, float height, float pitch_height, int floors, int tex_index) { + + // The 2048x2048 texture is split into 64x32 blocks. So there are 64 on + // the x-axis and 128 on the y-axis. + // The leftmost 32 are used for the sides of the building, and the rightmost + // 32 for the roof. + const float BUILDING_TEXTURE_BLOCK_HEIGHT = 1.0f / 64.0f; // The height of a single block within the random building texture + const float BUILDING_TEXTURE_BLOCK_WIDTH = 1.0f / 32.0f; // The width of a single block within the random building texture + Vec2f tex0, tex1; + + if (buildingtype == SGBuildingBin::SMALL) { + // SMALL BUILDINGS + // Maximum texture height is 3 stories. + // Small buildings are represented on the bottom 18 rows + // Each block is 6m wide and 3m high. + int row = tex_index % 6; + float offset_x = 0.0f; + float offset_y = (float) row * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT; + float gain_x = min(0.5f, std::round(width / 6.0f) * BUILDING_TEXTURE_BLOCK_WIDTH); + float gain_y = (float) (min(3, floors)) * BUILDING_TEXTURE_BLOCK_HEIGHT; + + tex0 = Vec2f(offset_x, offset_y); + tex1 = Vec2f(gain_x, gain_y); + } else if (buildingtype == SGBuildingBin::MEDIUM) { + // MEDIUM BUILDING + // Maximum texture height is 8 stories. + // Medium buildings are arranged on the texture in a 2x3 pattern. + // For a medium building, each block is 10m wide and 3m high. + int column = tex_index % 2; + int row = tex_index % 3; + float offset_x = column * 0.25f; + // Counting from the bottom, we have 6 rows of small buildings, each 3 blocks high + float offset_y = (6.0f * 3.0f + row * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT; + float gain_x = min(0.25f, std::round(width / 10.0f) * BUILDING_TEXTURE_BLOCK_WIDTH); + float gain_y = (float) (min(8, floors)) * BUILDING_TEXTURE_BLOCK_HEIGHT; + + tex0 = Vec2f(offset_x, offset_y); + tex1 = Vec2f(gain_x, gain_y); + } else { + // LARGE BUILDING + // Maximum texture height is 22 stories. + // Large buildings are arranged in a 4x1 pattern + // Each block is 20m wide and 3m high. + int column = tex_index % 4; + // Counting from the bottom we have 6 rows of small buildings (3 blocks high), + // then 3 rows of medium buildings (8 blocks high). Then the large building texture + float offset_x = column * 0.125f; + float offset_y = (6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT; + float gain_x = min(0.125f, std::round(width / 20.0f) * BUILDING_TEXTURE_BLOCK_WIDTH); + float gain_y = (float) min(22, floors) * BUILDING_TEXTURE_BLOCK_HEIGHT; + + tex0 = Vec2f(offset_x, offset_y); + tex1 = Vec2f(gain_x, gain_y); + } + + buildingLocations.push_back(BuildingInstance(toOsg(p), width, depth, height, pitch_height, r, tex0, tex1)); + } + + + // Generate a building of a given type at a specified position, using the random building material definition to determine the dimensions and texture index. + void SGBuildingBin::insert(SGVec3f p, float r, BuildingType buildingtype) { + + float width, depth, height, pitch_height; + int floors; // Generate a random seed for the building generation. mt seed; - mt_init(&seed, unsigned(123)); + mt_init(&seed, unsigned(p.x() + p.y() + p.z())); - smallSharedGeometry = new osg::Geometry(); - mediumSharedGeometry = new osg::Geometry(); - largeSharedGeometry = new osg::Geometry(); + if (buildingtype == SGBuildingBin::SMALL) { + // Small building + // Maximum number of floors is 3, and maximum width/depth is 192m. + width = material->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_small_max_width() - material->get_building_small_min_width()); + depth = material->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_small_max_depth() - material->get_building_small_min_depth()); + floors = SGMisc::round(material->get_building_small_min_floors() + mt_rand(&seed) * (material->get_building_small_max_floors() - material->get_building_small_min_floors())); + height = floors * (2.8 + mt_rand(&seed)); - 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); + // Small buildings are never deeper than they are wide. + if (depth > width) { depth = width; } - smallBuildingMaxDepth = mat->get_building_small_max_depth(); - mediumBuildingMaxDepth = mat->get_building_medium_max_depth(); - largeBuildingMaxDepth = mat->get_building_large_max_depth(); + pitch_height = (mt_rand(&seed) < material->get_building_small_pitch()) ? 3.0 : 0.0; + } else if (buildingtype == SGBuildingBin::MEDIUM) { + // MEDIUM BUILDING + width = material->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_medium_max_width() - material->get_building_medium_min_width()); + depth = material->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_medium_max_depth() - material->get_building_medium_min_depth()); + floors = SGMisc::round(material->get_building_medium_min_floors() + mt_rand(&seed) * (material->get_building_medium_max_floors() - material->get_building_medium_min_floors())); + height = floors * (2.8 + mt_rand(&seed)); - smallBuildingFraction = mat->get_building_small_fraction(); - mediumBuildingFraction = mat->get_building_medium_fraction(); - - buildingRange = mat->get_building_range(); - - SG_LOG(SG_TERRAIN, SG_DEBUG, "Building fractions " << smallBuildingFraction << " " << mediumBuildingFraction); - - - // TODO: Reverse this - otherwise we never get any large buildings! - BuildingType types[] = { SGBuildingBin::SMALL, SGBuildingBin::MEDIUM, SGBuildingBin::LARGE }; - BuildingList lists[] = { SGBuildingBin::smallBuildings, SGBuildingBin::mediumBuildings, SGBuildingBin::largeBuildings }; - ref_ptr geometries[] = { smallSharedGeometry, mediumSharedGeometry, largeSharedGeometry }; - - for (int bt=0; bt < 3; bt++) { - SGBuildingBin::BuildingType buildingtype = types[bt]; - ref_ptr sharedGeometry = geometries[bt]; - BuildingList buildings = lists[bt]; - - osg::ref_ptr v = new osg::Vec3Array; - osg::ref_ptr t = new osg::Vec2Array; - osg::ref_ptr n = new osg::Vec3Array; - - v->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING); - t->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING); - n->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING); - - sharedGeometry->setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX); - sharedGeometry->setComputeBoundingBoxCallback(new BuildingBoundingBoxCallback); - sharedGeometry->setUseDisplayList(false); - sharedGeometry->setDataVariance(osg::Object::STATIC); - if (useVBOs) { - sharedGeometry->setUseVertexBufferObjects(true); + while ((height > width) && (floors > material->get_building_medium_min_floors())) { + // Ensure that medium buildings aren't taller than they are wide + floors--; + height = floors * (2.8 + mt_rand(&seed)); } - for (unsigned int j = 0; j < BUILDING_SET_SIZE; j++) { - float width; - float depth; - int floors; - float height; - bool pitched; - - if (buildingtype == SGBuildingBin::SMALL) { - // Small building - width = mat->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_width() - mat->get_building_small_min_width()); - depth = mat->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_depth() - mat->get_building_small_min_depth()); - floors = SGMisc::round(mat->get_building_small_min_floors() + mt_rand(&seed) * (mat->get_building_small_max_floors() - mat->get_building_small_min_floors())); - height = floors * (2.8 + mt_rand(&seed)); - - // Small buildings are never deeper than they are wide. - if (depth > width) { depth = width; } - - pitched = (mt_rand(&seed) < mat->get_building_small_pitch()); - } else if (buildingtype == SGBuildingBin::MEDIUM) { - width = mat->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_width() - mat->get_building_medium_min_width()); - depth = mat->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_depth() - mat->get_building_medium_min_depth()); - floors = SGMisc::round(mat->get_building_medium_min_floors() + mt_rand(&seed) * (mat->get_building_medium_max_floors() - mat->get_building_medium_min_floors())); - height = floors * (2.8 + mt_rand(&seed)); - - while ((height > width) && (floors > mat->get_building_medium_min_floors())) { - // Ensure that medium buildings aren't taller than they are wide - floors--; - height = floors * (2.8 + mt_rand(&seed)); - } - - pitched = (mt_rand(&seed) < mat->get_building_medium_pitch()); - } else { - width = mat->get_building_large_min_width() + mt_rand(&seed) * (mat->get_building_large_max_width() - mat->get_building_large_min_width()); - depth = mat->get_building_large_min_depth() + mt_rand(&seed) * (mat->get_building_large_max_depth() - mat->get_building_large_min_depth()); - floors = SGMisc::round(mat->get_building_large_min_floors() + mt_rand(&seed) * (mat->get_building_large_max_floors() - mat->get_building_large_min_floors())); - height = floors * (2.8 + mt_rand(&seed)); - pitched = (mt_rand(&seed) < mat->get_building_large_pitch()); - } - - Building building = Building(buildingtype, - width, - depth, - height, - floors, - pitched); - - buildings.push_back(building); - - // Now create an OSG Geometry based on the Building - float cw = 0.5f * building.width; - float cd = building.depth; - float ch = building.height; - - // 0,0,0 is the bottom center of the front - // face, e.g. where the front door would be - - // 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(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, -1, 0) ); // normal - - // 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(-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, 1, 0) ); // normal - - // MAIN BODY - // Front face - v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom right - v->push_back( osg::Vec3( 0, cw, 0) ); // bottom left - v->push_back( osg::Vec3( 0, cw, ch) ); // top left - v->push_back( osg::Vec3( 0, -cw, ch) ); // top right - - for (int i=0; i<4; ++i) - n->push_back( osg::Vec3(1, 0, 0) ); // normal - - // Left face - v->push_back( osg::Vec3( -cd, -cw, 0) ); // bottom right - v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom left - 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 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) { - - // 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 * SGMisc::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 * SGMisc::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 + 32.0 / 1024.0 * SGMisc::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 - } - } - - } - - 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 * SGMisc::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; - - // 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 - } - } - } - - 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 * SGMisc::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 = 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. Colors will be set per-instance - // later. - sharedGeometry->setVertexArray(v); - sharedGeometry->setTexCoordArray(0, t, Array::BIND_PER_VERTEX); - sharedGeometry->setNormalArray(n, Array::BIND_PER_VERTEX); - } - } - - void SGBuildingBin::insert(SGVec3f p, float r, BuildingType type) { - - if (type == SGBuildingBin::SMALL) { - smallBuildingLocations.push_back(BuildingInstance(p, r, &smallBuildings, smallSharedGeometry)); + pitch_height = (mt_rand(&seed) < material->get_building_medium_pitch()) ? 3.0 : 0.0; + } else { + // LARGE BUILDING + width = material->get_building_large_min_width() + mt_rand(&seed) * (material->get_building_large_max_width() - material->get_building_large_min_width()); + depth = material->get_building_large_min_depth() + mt_rand(&seed) * (material->get_building_large_max_depth() - material->get_building_large_min_depth()); + floors = SGMisc::round(material->get_building_large_min_floors() + mt_rand(&seed) * (material->get_building_large_max_floors() - material->get_building_large_min_floors())); + height = floors * (2.8 + mt_rand(&seed)); + pitch_height = (mt_rand(&seed) < material->get_building_large_pitch()) ? 3.0 : 0.0; } - if (type == SGBuildingBin::MEDIUM) { - mediumBuildingLocations.push_back(BuildingInstance(p, r, &mediumBuildings, mediumSharedGeometry)); - } - - if (type == SGBuildingBin::LARGE) { - largeBuildingLocations.push_back(BuildingInstance(p, r, &largeBuildings, largeSharedGeometry)); - } + insert(p, r, buildingtype, width, depth, height, pitch_height, floors, (int) (mt_rand(&seed) * 1000.0)); } int SGBuildingBin::getNumBuildings() { - return smallBuildingLocations.size() + mediumBuildingLocations.size() + largeBuildingLocations.size(); + return buildingLocations.size(); } bool SGBuildingBin::checkMinDist (SGVec3f p, float radius) { BuildingInstanceList::iterator iter; - - float r = (radius + smallBuildingMaxRadius) * (radius + smallBuildingMaxRadius); - for (iter = smallBuildingLocations.begin(); iter != smallBuildingLocations.end(); ++iter) { - if (iter->getDistSqr(p) < r) { + for (iter = buildingLocations.begin(); iter != buildingLocations.end(); ++iter) { + if (iter->getDistSqr(toOsg(p)) < radius) { return false; } } - - r = (radius + mediumBuildingMaxRadius) * (radius + mediumBuildingMaxRadius); - for (iter = mediumBuildingLocations.begin(); iter != mediumBuildingLocations.end(); ++iter) { - if (iter->getDistSqr(p) < r) { - return false; - } - } - - 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) { + if (roll < material->get_building_small_fraction()) { return SGBuildingBin::SMALL; } - if (roll < (smallBuildingFraction + mediumBuildingFraction)) { + if (roll < (material->get_building_small_fraction() + material->get_building_medium_fraction())) { return SGBuildingBin::MEDIUM; } @@ -756,20 +593,16 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const } float SGBuildingBin::getBuildingMaxRadius(BuildingType type) { - - if (type == SGBuildingBin::SMALL) return smallBuildingMaxRadius; - if (type == SGBuildingBin::MEDIUM) return mediumBuildingMaxRadius; - if (type == SGBuildingBin::LARGE) return largeBuildingMaxRadius; - + if (type == SGBuildingBin::SMALL) return material->get_building_small_max_width(); + if (type == SGBuildingBin::MEDIUM) return material->get_building_medium_max_width(); + if (type == SGBuildingBin::LARGE) return material->get_building_large_max_width(); 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; - + if (type == SGBuildingBin::SMALL) return material->get_building_small_max_depth(); + if (type == SGBuildingBin::MEDIUM) return material->get_building_medium_max_depth(); + if (type == SGBuildingBin::LARGE) return material->get_building_large_max_depth(); return 0; } @@ -799,35 +632,33 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const iter->second = effect; // update existing, but empty observer } - ref_ptr group = new osg::Group(); + // Transform building positions from the "geocentric" positions we + // get from the scenery polys into the local Z-up coordinate + // system. + std::vector rotatedBuildings; + rotatedBuildings.reserve(buildingLocations.size()); + for (const auto &b : buildingLocations) { + rotatedBuildings.emplace_back(BuildingInstance( + b.position * transInv, + b + )); + } // Now, create a quadbuilding for the buildings. + BuildingGeometryQuadtree + quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(), + SG_BUILDING_QUAD_TREE_DEPTH, + MakeBuildingLeaf(buildingRange, effect, false)); - BuildingInstanceList locs[] = { smallBuildingLocations, - SGBuildingBin::mediumBuildingLocations, - SGBuildingBin::largeBuildingLocations }; + quadbuilding.buildQuadTree(rotatedBuildings.begin(), rotatedBuildings.end()); - for (int i = 0; i < 3; i++) - { - // Create a quad tree. Only small and medium buildings are faded out. - BuildingGeometryQuadtree - quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(), - SG_BUILDING_QUAD_TREE_DEPTH, - MakeBuildingLeaf(buildingRange, effect, (i != 2))); + ref_ptr group = new osg::Group(); - // Transform building positions from the "geocentric" positions we - // get from the scenery polys into the local Z-up coordinate - // system. - std::vector rotatedBuildings; - rotatedBuildings.reserve(locs[i].size()); - std::transform(locs[i].begin(), locs[i].end(), - std::back_inserter(rotatedBuildings), - BuildingInstanceTransformer(transInv)); - quadbuilding.buildQuadTree(rotatedBuildings.begin(), rotatedBuildings.end()); + static int buildingGroupCounter = 0; + group->setName("BuildingsGroup_" + std::to_string(buildingGroupCounter++)); - for (size_t j = 0; j < quadbuilding.getRoot()->getNumChildren(); ++j) - group->addChild(quadbuilding.getRoot()->getChild(j)); - } + for (size_t j = 0; j < quadbuilding.getRoot()->getNumChildren(); ++j) + group->addChild(quadbuilding.getRoot()->getChild(j)); return group; } @@ -847,10 +678,6 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const 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(geode->getDrawable(j)); if (!geom) { @@ -878,7 +705,6 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const const SGReaderWriterOptions* options) { Matrix transInv = Matrix::inverse(transform); - static Matrix ident; // Set up some shared structures. MatrixTransform* mt = new MatrixTransform(transform); SGBuildingBinList::iterator i; @@ -890,12 +716,8 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const for (size_t j = 0; j < group->getNumChildren(); ++j) { mt->addChild(group->getChild(j)); } - - delete bin; } - buildings.clear(); - QuadTreeCleaner cleaner; mt->accept(cleaner); return mt; diff --git a/simgear/scene/tgdb/SGBuildingBin.hxx b/simgear/scene/tgdb/SGBuildingBin.hxx index d6774ca0..67e94ce3 100644 --- a/simgear/scene/tgdb/SGBuildingBin.hxx +++ b/simgear/scene/tgdb/SGBuildingBin.hxx @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -47,141 +48,120 @@ #include #include -#define SG_BUILDING_QUAD_TREE_DEPTH 4 +#define SG_BUILDING_QUAD_TREE_DEPTH 2 #define SG_BUILDING_FADE_OUT_LEVELS 4 +// these correspond to building.eff +const int BUILDING_POSITION_ATTR = 10; // (x,y,z) +const int BUILDING_SCALE_ATTR = 11; // (width, depth, height) +const int BUILDING_ROT_PITCH_TEX0X_ATTR = 12; // (rotation, pitch height, texture x offset) +const int BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR = 13; // (texture y offset, texture x gain, texture y gain) + using namespace osg; namespace simgear { + +struct BuildingBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback +{ + BuildingBoundingBoxCallback() {} + BuildingBoundingBoxCallback(const BuildingBoundingBoxCallback&, const CopyOp&) {} + META_Object(simgear, BuildingBoundingBoxCallback); + virtual BoundingBox computeBound(const Drawable& drawable) const + { + BoundingBox bb; + const Geometry* geom = static_cast(&drawable); + const Vec3Array* pos = static_cast(geom->getVertexAttribArray(BUILDING_POSITION_ATTR)); + + for (unsigned int v=0; vsize(); ++v) { + Vec3 pt = (*pos)[v]; + bb.expandBy(pt); + } + return bb; + } +}; + class SGBuildingBin { public: - // Number of buildings to auto-generate. Individual - // building instances are taken from this set. - static const unsigned int BUILDING_SET_SIZE = 200; - - static const unsigned int QUADS_PER_BUILDING = 12; - static const unsigned int VERTICES_PER_BUILDING = 4 * QUADS_PER_BUILDING; - static const unsigned int VERTICES_PER_BUILDING_SET = BUILDING_SET_SIZE * VERTICES_PER_BUILDING; - enum BuildingType { SMALL = 0, MEDIUM, LARGE }; + struct BuildingInstance { + BuildingInstance(Vec3f p, float w, float d, float h, float ph, float r, Vec2f t0, Vec2f t1) : + position(p), + width(w), + depth(d), + height(h), + pitch_height(ph), + rotation(r), + tex0(t0), + tex1(t1) + { } + + BuildingInstance(Vec3f p, BuildingInstance b) : + position(p), + width(b.width), + depth(b.depth), + height(b.height), + pitch_height(b.pitch_height), + rotation(b.rotation), + tex0(b.tex0), + tex1(b.tex1) + { } + + + Vec3f position; + float width; + float depth; + float height; + float pitch_height; + float rotation; + + Vec2f tex0; + Vec2f tex1; + + // References to allow the QuadTreeBuilder to work + //const BuildingList* buildingList; + //ref_ptr sharedGeometry; + + Vec3f getPosition() { return position; } + float getRotation() { return rotation; } + + float getDistSqr(Vec3f p) { + return (p - position) * (p - position); + } + }; + private: - struct Building { - Building(BuildingType t, float w, float d, float h, int f, bool pitch) : - type(t), - width(w), - depth(d), - height(h), - floors(f), - pitched(pitch), - radius(std::max(d, 0.5f*w)) - { } - - BuildingType type; - float width; - float depth; - float height; - int floors; - bool pitched; - float radius; - - float getFootprint() { - return radius; - } - }; - - // The set of buildings that are instantiated - typedef std::vector BuildingList; - BuildingList smallBuildings; - BuildingList mediumBuildings; - BuildingList largeBuildings; + const SGMaterial *material; std::string* material_name; std::string* texture; std::string* lightMap; - // Fraction of buildings of this type - float smallBuildingFraction; - float mediumBuildingFraction; - - // The maximum radius of each building type - float smallBuildingMaxRadius; - float mediumBuildingMaxRadius; - float largeBuildingMaxRadius; - - // The maximum depth of each building type - float smallBuildingMaxDepth; - float mediumBuildingMaxDepth; - float largeBuildingMaxDepth; - // Visibility range for buildings float buildingRange; - // Shared geometries of the building set - ref_ptr smallSharedGeometry; - ref_ptr mediumSharedGeometry; - ref_ptr largeSharedGeometry; - - struct BuildingInstance { - BuildingInstance(SGVec3f p, float r, const BuildingList* bl, ref_ptr sg) : - position(p), - rotation(r), - buildingList(bl), - sharedGeometry(sg) - { } - - BuildingInstance(SGVec3f p, BuildingInstance b) : - position(p), - rotation(b.rotation), - buildingList(b.buildingList), - sharedGeometry(b.sharedGeometry) - { } - - SGVec3f position; - float rotation; - - // References to allow the QuadTreeBuilder to work - const BuildingList* buildingList; - ref_ptr sharedGeometry; - - SGVec3f getPosition() { return position; } - float getRotation() { return rotation; } - - float getDistSqr(SGVec3f p) { - return distSqr(p, position); - } - - const osg::Vec4f getColorValue() { - return osg::Vec4f(toOsg(position), rotation); - } - }; // Information for an instance of a building - position and orientation typedef std::vector BuildingInstanceList; - BuildingInstanceList smallBuildingLocations; - BuildingInstanceList mediumBuildingLocations; - BuildingInstanceList largeBuildingLocations; + BuildingInstanceList buildingLocations; public: SGBuildingBin(const SGMaterial *mat, bool useVBOs); SGBuildingBin(const SGPath& absoluteFileName, const SGMaterial *mat, bool useVBOs); - ~SGBuildingBin() { - smallBuildings.clear(); - mediumBuildings.clear(); - largeBuildings.clear(); - smallBuildingLocations.clear(); - mediumBuildingLocations.clear(); - largeBuildingLocations.clear(); - } + ~SGBuildingBin(); + // Generate a building specifying the exact position, dimensions and texture index. + void insert(SGVec3f p, float r, BuildingType buildingtype, float width, float depth, float height, float pitch_height, int floors, int tex_index); + + // Generate a building of a given type at a specified position, using the random building material definition to determine the dimensions and texture index. void insert(SGVec3f p, float r, BuildingType type); int getNumBuildings(); @@ -190,123 +170,9 @@ public: std::string* getMaterialName() { return material_name; } BuildingType getBuildingType(float roll); - float getBuildingMaxRadius(BuildingType); float getBuildingMaxDepth(BuildingType); - // Helper classes for creating the quad tree - struct MakeBuildingLeaf - { - MakeBuildingLeaf(float range, Effect* effect, bool fade) : - _range(range), _effect(effect), _fade_out(fade) {} - - MakeBuildingLeaf(const MakeBuildingLeaf& rhs) : - _range(rhs._range), _effect(rhs._effect), _fade_out(rhs._fade_out) - {} - - LOD* operator() () const - { - LOD* result = new LOD; - - if (_fade_out) { - // Create a series of LOD nodes so building cover decreases - // gradually with distance from _range to 2*_range - for (float i = 0.0; i < SG_BUILDING_FADE_OUT_LEVELS; i++) - { - EffectGeode* geode = new EffectGeode; - geode->setEffect(_effect.get()); - result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0))); - } - } else { - // No fade-out, so all are visible for 2X range - EffectGeode* geode = new EffectGeode; - geode->setEffect(_effect.get()); - result->addChild(geode, 0, 2.0 * _range); - } - return result; - } - - float _range; - ref_ptr _effect; - bool _fade_out; - }; - - struct AddBuildingLeafObject - { - Geometry* createNewBuildingGeometryInstance(const BuildingInstance& building) const - { - Geometry* geom = simgear::clone(building.sharedGeometry.get(), CopyOp::SHALLOW_COPY); - geom->setColorArray(new Vec4Array, Array::BIND_PER_VERTEX); - geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS)); - return geom; - } - - void operator() (LOD* lod, const BuildingInstance& building) const - { - Geode* geode = static_cast(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren())); - unsigned int numDrawables = geode->getNumDrawables(); - - // Get the last geometry of to be added and check if there is space for - // another building instance within it. This is done by checking - // if the number of Color values matches the number of vertices. - // The color array is used to store the position of a particular - // instance. - Geometry* geom; - - if (numDrawables == 0) { - // Create a new copy of the shared geometry to instantiate - geom = createNewBuildingGeometryInstance(building); - geode->addDrawable(geom); - } else { - geom = static_cast(geode->getDrawable(numDrawables - 1)); - } - - // Check if this building is too close to any other others. - DrawArrays* primSet = static_cast(geom->getPrimitiveSet(0)); - Vec4Array* posArray = static_cast(geom->getColorArray()); - - // Now check if this geometry is full. - if (posArray->size() >= static_cast(geom->getVertexArray())->size()) { - // This particular geometry is full, so we generate another - // by taking a shallow copy of the shared Geomety. - geom = createNewBuildingGeometryInstance(building); - geode->addDrawable(geom); - posArray = static_cast(geom->getColorArray()); - SG_LOG(SG_TERRAIN, SG_DEBUG, "Added new geometry to building geod: " << geode->getNumDrawables()); - } - - // We now have a geometry with space for this new building. - // Set the position and rotation - osg::Vec4f c = osg::Vec4f(toOsg(building.position), building.rotation); - posArray->insert(posArray->end(), VERTICES_PER_BUILDING, c); - size_t numVerts = posArray->size(); - primSet = static_cast(geom->getPrimitiveSet(0)); - primSet->setCount(numVerts); - } - }; - - struct GetBuildingCoord - { - Vec3 operator() (const BuildingInstance& building) const - { - return toOsg(building.position); - } - }; - - typedef QuadTreeBuilder BuildingGeometryQuadtree; - - struct BuildingInstanceTransformer - { - BuildingInstanceTransformer(Matrix& mat_) : mat(mat_) {} - BuildingInstance operator()(const BuildingInstance& buildingInstance) const - { - Vec3 pos = toOsg(buildingInstance.position) * mat; - return BuildingInstance(toSG(pos), buildingInstance); - } - Matrix mat; - }; - ref_ptr createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options); };