From d883ab278d10c89580d5bbc4473d2e9fc375b7b9 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sun, 4 Aug 2019 02:27:57 -0500 Subject: [PATCH 01/52] [osgText.getActiveFont] Breaking change within OSG --- simgear/canvas/elements/CanvasText.cxx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/simgear/canvas/elements/CanvasText.cxx b/simgear/canvas/elements/CanvasText.cxx index 3051045f..060436e1 100644 --- a/simgear/canvas/elements/CanvasText.cxx +++ b/simgear/canvas/elements/CanvasText.cxx @@ -53,10 +53,13 @@ namespace canvas TextLine lineAt(size_t i) const; /// Get nearest line to given y-coordinate +#if OSG_VERSION_LESS_THAN(3,6,5) TextLine nearestLine(float pos_y) const; - - SGVec2i sizeForWidth(int w) const; +#else + TextLine nearestLine(float pos_y); + SGVec2i sizeForWidth(int w); +#endif osg::BoundingBox #if OSG_VERSION_LESS_THAN(3,3,2) @@ -319,9 +322,14 @@ namespace canvas } //---------------------------------------------------------------------------- +#if OSG_VERSION_LESS_THAN(3,6,5) TextLine Text::TextOSG::nearestLine(float pos_y) const +#else + TextLine Text::TextOSG::nearestLine(float pos_y) +#endif { - osgText::Font const* font = getActiveFont(); + auto font = getActiveFont(); + if( !font || lineCount() <= 0 ) return TextLine(0, this); @@ -343,12 +351,16 @@ namespace canvas // simplified version of osgText::Text::computeGlyphRepresentation() to // just calculate the size for a given weight. Glpyh calculations/creating // is not necessary for this... +#if OSG_VERSION_LESS_THAN(3,6,5) SGVec2i Text::TextOSG::sizeForWidth(int w) const +#else + SGVec2i Text::TextOSG::sizeForWidth(int w) +#endif { if( _text.empty() ) return SGVec2i(0, 0); - osgText::Font* activefont = const_cast(getActiveFont()); + auto activefont = getActiveFont(); if( !activefont ) return SGVec2i(-1, -1); @@ -727,7 +739,7 @@ namespace canvas } #else - void Text::TextOSG::computePositionsImplementation() + void Text::TextOSG::computePositionsImplementation() { TextBase::computePositionsImplementation(); } From b28f030874d514dfc1393f5a7156f202556f3730 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sun, 4 Aug 2019 10:43:00 -0500 Subject: [PATCH 02/52] [CanvasText] Restore support for OSG 3.4 --- simgear/canvas/elements/CanvasText.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/simgear/canvas/elements/CanvasText.cxx b/simgear/canvas/elements/CanvasText.cxx index 060436e1..9e4e88b9 100644 --- a/simgear/canvas/elements/CanvasText.cxx +++ b/simgear/canvas/elements/CanvasText.cxx @@ -324,11 +324,13 @@ namespace canvas //---------------------------------------------------------------------------- #if OSG_VERSION_LESS_THAN(3,6,5) TextLine Text::TextOSG::nearestLine(float pos_y) const + { + osgText::Font const* font = getActiveFont(); #else TextLine Text::TextOSG::nearestLine(float pos_y) -#endif { auto font = getActiveFont(); +#endif if( !font || lineCount() <= 0 ) return TextLine(0, this); @@ -360,7 +362,12 @@ namespace canvas if( _text.empty() ) return SGVec2i(0, 0); +#if OSG_VERSION_LESS_THAN(3,6,5) + osgText::Font* activefont = const_cast(getActiveFont()); +#else auto activefont = getActiveFont(); +#endif + if( !activefont ) return SGVec2i(-1, -1); From 38f6a5a40a433755f9f48fd53ceef92f0cc9cec4 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Wed, 14 Aug 2019 16:47:25 +0200 Subject: [PATCH 03/52] Remove fairly irrelevant startup log message from NasalMainLoopRecipient This is probably the reason why the tests are failing. --- simgear/nasal/cppbind/NasalEmesaryInterface.hxx | 1 - 1 file changed, 1 deletion(-) diff --git a/simgear/nasal/cppbind/NasalEmesaryInterface.hxx b/simgear/nasal/cppbind/NasalEmesaryInterface.hxx index b6d01ca1..64d24f18 100644 --- a/simgear/nasal/cppbind/NasalEmesaryInterface.hxx +++ b/simgear/nasal/cppbind/NasalEmesaryInterface.hxx @@ -67,7 +67,6 @@ namespace nasal public: NasalMainLoopRecipient() : receiveCount(0) { simgear::Emesary::GlobalTransmitter::instance()->Register(*this); - SG_LOG(SG_NASAL, SG_INFO, "NasalMainLoopRecipient created"); } virtual ~NasalMainLoopRecipient() { simgear::Emesary::GlobalTransmitter::instance()->DeRegister(*this); From 138e28fcbe0972a5955e5670aab30c1657dab5ba Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Tue, 20 Aug 2019 17:00:17 +0100 Subject: [PATCH 04/52] Instance-based random buildings Replace existing random buildings implementation with one using proper instancing. This allows better control of the buildings themselves and allows BUILDING_LIST STG verb to specify individual building dimensions. See README.scenery for details. --- simgear/scene/material/mat.cxx | 69 +- simgear/scene/tgdb/SGBuildingBin.cxx | 1108 +++++++++++--------------- simgear/scene/tgdb/SGBuildingBin.hxx | 296 ++----- 3 files changed, 587 insertions(+), 886 deletions(-) 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); }; From f407785f10ecabfecc89b201162e22adba90f396 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Mon, 12 Aug 2019 17:53:31 +0100 Subject: [PATCH 05/52] Fixes for building on OpenBSD. --- CMakeLists.txt | 4 ++++ simgear/canvas/ShivaVG/src/shConfig.h | 5 +++++ simgear/canvas/ShivaVG/src/shDefs.h | 4 ++-- simgear/misc/strutils.cxx | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a48cb4b..cf5c425c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -521,6 +521,10 @@ include(CheckCXXFeatures) # ahead of system-installed libs include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear) +if(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") + include_directories("/usr/X11R6/include") +endif() + add_definitions(-DHAVE_CONFIG_H) # configure a header file to pass some of the CMake settings diff --git a/simgear/canvas/ShivaVG/src/shConfig.h b/simgear/canvas/ShivaVG/src/shConfig.h index 6ed402e7..77be46ac 100644 --- a/simgear/canvas/ShivaVG/src/shConfig.h +++ b/simgear/canvas/ShivaVG/src/shConfig.h @@ -31,6 +31,11 @@ // FreeBSD #define VG_API_FREEBSD +#elif defined(__OpenBSD__) + + // FreeBSD + #define VG_API_OPENBSD + #else // Unsupported system diff --git a/simgear/canvas/ShivaVG/src/shDefs.h b/simgear/canvas/ShivaVG/src/shDefs.h index ee1774b5..f4355b18 100644 --- a/simgear/canvas/ShivaVG/src/shDefs.h +++ b/simgear/canvas/ShivaVG/src/shDefs.h @@ -34,7 +34,7 @@ #include #include -#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__) +#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) # include #endif @@ -161,7 +161,7 @@ SHfloat getMaxFloat(); /* OpenGL headers */ -#if defined(VG_API_LINUX) || defined(VG_API_FREEBSD) +#if defined(VG_API_LINUX) || defined(VG_API_FREEBSD) || defined(VG_API_OPENBSD) #include #include #elif defined(VG_API_MACOSX) diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index 6d2cbeaa..5062e242 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -1043,7 +1043,7 @@ std::string error_string(int errnum) retcode = strerror_s(buf, sizeof(buf), errnum); #elif defined(_GNU_SOURCE) return std::string(strerror_r(errnum, buf, sizeof(buf))); -#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC) || defined(__FreeBSD__) +#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC) || defined(__FreeBSD__) || defined(__OpenBSD__) int retcode; // POSIX.1-2001 and POSIX.1-2008 retcode = strerror_r(errnum, buf, sizeof(buf)); From 7762bc2cad01426cd8f5402582b8e53e6644b5c3 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Sun, 1 Sep 2019 15:07:07 +0100 Subject: [PATCH 06/52] simgear/scene/model/ModelRegistry.cxx: avoid noisy diagnostic from OSG. --- simgear/scene/model/ModelRegistry.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx index 0087cbbf..e128d390 100644 --- a/simgear/scene/model/ModelRegistry.cxx +++ b/simgear/scene/model/ModelRegistry.cxx @@ -608,7 +608,10 @@ osg::ref_ptr DefaultCachePolicy::find(const string& fileName, const O void DefaultCachePolicy::addToCache(const string& fileName, osg::Node* node) { - Registry::instance()->addEntryToObjectCache(fileName, node); + /* Avoid default options=NULL in this call, as it provokes noisy diagnostic + in OSG. */ + Options options; + Registry::instance()->addEntryToObjectCache(fileName, node, 0 /*timestamp*/, &options); } // Optimizations we don't use: From 20fb7a9ce0c94dacea94b38e1f1181cbc96c59ef Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 14 Aug 2019 12:55:58 +0100 Subject: [PATCH 07/52] simgear/debug/logstream.cxx: Allow runtime control of file:line in diagnostics. New fn logstream::setFileLine() allows other code to control whether log output includes file:line information. --- simgear/debug/logstream.cxx | 19 +++++++++++++++++-- simgear/debug/logstream.hxx | 6 ++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/simgear/debug/logstream.cxx b/simgear/debug/logstream.cxx index d5851001..f2c61531 100644 --- a/simgear/debug/logstream.cxx +++ b/simgear/debug/logstream.cxx @@ -194,7 +194,13 @@ public: { if (!shouldLog(c, p)) return; //fprintf(stderr, "%s\n", aMessage.c_str()); - fprintf(stderr, "%8.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, debugPriorityToString(p), debugClassToString(c), aMessage.c_str()); + + if (file && line != -1) { + fprintf(stderr, "%8.2f %s:%i: [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, file, line, debugPriorityToString(p), debugClassToString(c), aMessage.c_str()); + } + else { + fprintf(stderr, "%8.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, debugPriorityToString(p), debugClassToString(c), aMessage.c_str()); + } // file, line, aMessage.c_str()); //fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p, // file, line, aMessage.c_str()); @@ -407,6 +413,7 @@ public: bool m_stdout_isRedirectedAlready = false; #endif bool m_developerMode = false; + bool m_fileLine = false; // test suite mode. bool m_testMode = false; @@ -438,7 +445,7 @@ public: LogEntry entry(m_entries.pop()); // special marker entry detected, terminate the thread since we are // making a configuration change or quitting the app - if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) { + if ((entry.debugClass == SG_NONE) && entry.file && !strcmp(entry.file, "done")) { return; } { @@ -535,6 +542,10 @@ public: const char* fileName, int line, const std::string& msg) { p = translatePriority(p); + if (!m_fileLine) { + /* This prevents output of file:line. */ + line = -1; + } LogEntry entry(c, p, fileName, line, msg); m_entries.push(entry); } @@ -581,6 +592,10 @@ void logstream::setDeveloperMode(bool devMode) d->m_developerMode = devMode; } +void logstream::setFileLine(bool fileLine) +{ + d->m_fileLine = fileLine; +} void logstream::addCallback(simgear::LogCallback* cb) diff --git a/simgear/debug/logstream.hxx b/simgear/debug/logstream.hxx index 30c656c7..ec101d44 100644 --- a/simgear/debug/logstream.hxx +++ b/simgear/debug/logstream.hxx @@ -112,6 +112,12 @@ public: */ void setDeveloperMode(bool devMode); + /** + * set output of file:line mode on/off. If on, all log messages are + * prefixed by the file:line of the caller of SG_LOG(). + */ + void setFileLine(bool fileLine); + /** * the core logging method */ From 9bea6afb80bbb9e2e350566056f5ba14f3f11acd Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Thu, 5 Sep 2019 14:58:47 +0100 Subject: [PATCH 08/52] Revert "simgear/scene/model/ModelRegistry.cxx: avoid noisy diagnostic from OSG." This reverts commit 7762bc2cad01426cd8f5402582b8e53e6644b5c3. This broke Windows and Mac builds. --- simgear/scene/model/ModelRegistry.cxx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx index e128d390..0087cbbf 100644 --- a/simgear/scene/model/ModelRegistry.cxx +++ b/simgear/scene/model/ModelRegistry.cxx @@ -608,10 +608,7 @@ osg::ref_ptr DefaultCachePolicy::find(const string& fileName, const O void DefaultCachePolicy::addToCache(const string& fileName, osg::Node* node) { - /* Avoid default options=NULL in this call, as it provokes noisy diagnostic - in OSG. */ - Options options; - Registry::instance()->addEntryToObjectCache(fileName, node, 0 /*timestamp*/, &options); + Registry::instance()->addEntryToObjectCache(fileName, node); } // Optimizations we don't use: From 70c564dd2c2edd88779edd3274e509a2522c177a Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Mon, 16 Sep 2019 23:30:59 -0500 Subject: [PATCH 09/52] Fix compile issue on Win10 VS2019 --- simgear/scene/model/BVHPageNodeOSG.hxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/simgear/scene/model/BVHPageNodeOSG.hxx b/simgear/scene/model/BVHPageNodeOSG.hxx index 1a3a5a73..e6a83a4d 100644 --- a/simgear/scene/model/BVHPageNodeOSG.hxx +++ b/simgear/scene/model/BVHPageNodeOSG.hxx @@ -18,6 +18,8 @@ #ifndef BVHPageNodeOSG_hxx #define BVHPageNodeOSG_hxx +#include + #include "../../bvh/BVHPageNode.hxx" #include From 23f378cd9353abe1d0670be37b73802a394b625c Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Tue, 17 Sep 2019 12:51:05 -0500 Subject: [PATCH 10/52] Support VS 2019 - courtesy of Lars Toenning --- CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf5c425c..7710db08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,15 +173,17 @@ if (MSVC AND MSVC_3RDPARTY_ROOT) set( OSG_MSVC "msvc" ) if (${MSVC_VERSION_MAJOR} EQUAL "19") - if (${MSVC_VERSION_MINOR} EQUAL "00") - set( OSG_MSVC ${OSG_MSVC}140 ) - else () + if (${MSVC_VERSION_MINOR} GREATER_EQUAL "20") + set( OSG_MSVC ${OSG_MSVC}142 ) + elseif (${MSVC_VERSION_MINOR} GREATER_EQUAL "10") set( OSG_MSVC ${OSG_MSVC}141 ) + else () + set( OSG_MSVC ${OSG_MSVC}140 ) endif () elseif (${MSVC_VERSION_MAJOR} EQUAL "18") set( OSG_MSVC ${OSG_MSVC}120 ) else () - message(FATAL_ERROR "Visual Studio 2013/15/17 is required") + message(FATAL_ERROR "Visual Studio 2013 or higher is required") endif () if (CMAKE_CL_64) From 93995cd7420570059d2716aa5282140df76f3c79 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 19 Sep 2019 08:05:40 +0100 Subject: [PATCH 11/52] Nasal GC : Fix crash running FG unit-tests When freeing a context, clear its stack so that anything dangling in them (especially the opStack) is GCed. --- simgear/nasal/code.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index 0783b38b..f724c1ba 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -235,6 +235,21 @@ void naFreeContext(naContext c) if(c->callChild) naFreeContext(c->callChild); if(c->callParent) c->callParent->callChild = 0; LOCK(); + + // 2019-09-21 + // James adding this to ensure stray stuff in freed contexts gets GCed + // this shows up when doing a reset / shutdown of all Nasal - we drop + // all our contexts and saved refs, and run a GC pass. We expect *everything* + // to be freed but actually the freed contexts often have a ref in their + // opStack. + // + // The underlying cause is likely some operation which leaves a value on + // the opstack accidently, but tracing that down requires more Nasal-fu + // than I have right now. So instead I'm clearing the stack tops here, so + // a freed context looks the same as a new one returned by initContext. + + c->fTop = c->opTop = c->markTop = 0; + c->nextFree = globals->freeContexts; globals->freeContexts = c; UNLOCK(); From 053bda26a43314a91b01b08cd4617da82f7ab807 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Wed, 25 Sep 2019 21:53:19 +0100 Subject: [PATCH 12/52] Shader buildings - split wall and roof tex index Previously there was very limited texture variations as a given texture index was used for both the wall and ceiling. Now these can be specified separately, allowing for more variation in both STG defined and random buildings. --- simgear/scene/tgdb/SGBuildingBin.cxx | 100 ++++++++++++++++----------- simgear/scene/tgdb/SGBuildingBin.hxx | 27 ++++++-- 2 files changed, 80 insertions(+), 47 deletions(-) diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index 96465d89..5cd24dc3 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -104,7 +104,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof } // Left face @@ -115,7 +115,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof } // Back face @@ -126,7 +126,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof } // Right face @@ -137,7 +137,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof } // MAIN BODY @@ -149,7 +149,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof } // Left face @@ -160,7 +160,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof } // Back face @@ -171,7 +171,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof } // Right face @@ -182,7 +182,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof } // ROOF - assume pitched - will be flattened in shader if required. @@ -194,7 +194,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof } // Left pitched roof @@ -205,7 +205,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof } // Back pitched roof @@ -216,7 +216,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof } // Right pitched roof @@ -227,7 +227,7 @@ struct MakeBuildingLeaf 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 + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof } // The 1024x1024 texture is split into 32x16 blocks. @@ -308,6 +308,7 @@ struct MakeBuildingLeaf 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->setVertexAttribArray(BUILDING_RTEX0X_RTEX0Y_ATTR, new osg::Vec3Array, Array::BIND_PER_VERTEX); geom->addPrimitiveSet( new osg::DrawArrays( GL_QUADS, 0, 48, 0) ); @@ -320,6 +321,7 @@ struct MakeBuildingLeaf 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)); + ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_RTEX0X_RTEX0Y_ATTR, 1)); LOD* result = new LOD; result->addChild(geode, 0, _range); @@ -342,15 +344,15 @@ struct AddBuildingLeafObject 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) + osg::Vec3Array* rot = static_cast (geom->getVertexAttribArray(BUILDING_ROT_PITCH_TEX0X_ATTR)); // (rotation, pitch height, wall texture x offset) + osg::Vec3Array* tex = static_cast (geom->getVertexAttribArray(BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR)); // (wall texture y offset, wall/roof texture x gain, wall/roof texture y gain) + osg::Vec3Array* rtex = static_cast (geom->getVertexAttribArray(BUILDING_RTEX0X_RTEX0Y_ATTR)); // (roof texture x offset, roof texture y offset, unused) 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"; + rot->push_back(osg::Vec3(building.rotation, building.pitch_height, building.walltex0.x())); + tex->push_back(osg::Vec3(building.walltex0.y(), building.tex1.x(), building.tex1.y())); + rtex->push_back(osg::Vec3(building.rooftex0.x(), building.rooftex0.y(), 0.0f)); DrawArrays* primSet = static_cast(geom->getPrimitiveSet(0)); primSet->setNumInstances(positions->size()); @@ -399,7 +401,7 @@ typedef QuadTreeBuilder> x >> y >> z >> r >> b >> w >> d >> h >> p >> s >> o >> f >> t; + int b, s, o, f, wt, rt; + in >> x >> y >> z >> r >> b >> w >> d >> h >> p >> s >> o >> f >> wt >> rt; //SG_LOG(SG_TERRAIN, SG_ALERT, "Building entry " << x << " " << y << " " << z << " " << b ); SGVec3f loc = SGVec3f(x,y,z); @@ -435,7 +438,7 @@ typedef QuadTreeBuilderget_building_large_pitch()) ? 3.0 : 0.0; } - insert(p, r, buildingtype, width, depth, height, pitch_height, floors, (int) (mt_rand(&seed) * 1000.0)); + insert(p, r, buildingtype, width, depth, height, pitch_height, floors, (int) (mt_rand(&seed) * 1000.0), (int) (mt_rand(&seed) * 1000.0)); } int SGBuildingBin::getNumBuildings() { diff --git a/simgear/scene/tgdb/SGBuildingBin.hxx b/simgear/scene/tgdb/SGBuildingBin.hxx index 67e94ce3..666a1f45 100644 --- a/simgear/scene/tgdb/SGBuildingBin.hxx +++ b/simgear/scene/tgdb/SGBuildingBin.hxx @@ -54,8 +54,9 @@ // 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) +const int BUILDING_ROT_PITCH_TEX0X_ATTR = 12; // (rotation, pitch height, wall texture x offset) +const int BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR = 13; // (wall texture y offset, texture x gain, texture y gain) +const int BUILDING_RTEX0X_RTEX0Y_ATTR = 14; // (roof texture x offset, roof texture y offset, unused) using namespace osg; @@ -90,14 +91,15 @@ public: LARGE }; struct BuildingInstance { - BuildingInstance(Vec3f p, float w, float d, float h, float ph, float r, Vec2f t0, Vec2f t1) : + BuildingInstance(Vec3f p, float w, float d, float h, float ph, float r, Vec2f wt0, Vec2f rt0, Vec2f t1) : position(p), width(w), depth(d), height(h), pitch_height(ph), rotation(r), - tex0(t0), + walltex0(wt0), + rooftex0(rt0), tex1(t1) { } @@ -108,7 +110,8 @@ public: height(b.height), pitch_height(b.pitch_height), rotation(b.rotation), - tex0(b.tex0), + walltex0(b.walltex0), + rooftex0(b.rooftex0), tex1(b.tex1) { } @@ -120,7 +123,8 @@ public: float pitch_height; float rotation; - Vec2f tex0; + Vec2f walltex0; + Vec2f rooftex0; Vec2f tex1; // References to allow the QuadTreeBuilder to work @@ -159,7 +163,16 @@ public: ~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); + void insert(SGVec3f p, + float r, + BuildingType buildingtype, + float width, + float depth, + float height, + float pitch_height, + int floors, + int wall_tex_index, + int roof_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); From 7464e17e2254772c04c9daab87ba104daefedafd Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Tue, 1 Oct 2019 14:56:18 +0100 Subject: [PATCH 13/52] Swap width and depth for shader defined buildings Previously the width was set as the x-axis and depth for the y-axis when passed through to the shader. This was incorrect, as the x-axis is actually the depth of the building. --- simgear/scene/tgdb/SGBuildingBin.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index 5cd24dc3..a22be5c9 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -349,7 +349,8 @@ struct AddBuildingLeafObject osg::Vec3Array* rtex = static_cast (geom->getVertexAttribArray(BUILDING_RTEX0X_RTEX0Y_ATTR)); // (roof texture x offset, roof texture y offset, unused) positions->push_back(building.position); - scale->push_back(osg::Vec3(building.width, building.depth, building.height)); + // Depth is the x-axis, width is the y-axis + scale->push_back(osg::Vec3(building.depth, building.width, building.height)); rot->push_back(osg::Vec3(building.rotation, building.pitch_height, building.walltex0.x())); tex->push_back(osg::Vec3(building.walltex0.y(), building.tex1.x(), building.tex1.y())); rtex->push_back(osg::Vec3(building.rooftex0.x(), building.rooftex0.y(), 0.0f)); From 1399cc948223a8846730f6ff79f0b94695a51ee5 Mon Sep 17 00:00:00 2001 From: ThomasS Date: Wed, 29 May 2019 11:32:33 +0200 Subject: [PATCH 14/52] Additional interface to ReaderWriterSTG for adding callbacks for unknown STG token handler --- simgear/scene/tgdb/ReaderWriterSTG.cxx | 41 ++++++++++++++++++++++++-- simgear/scene/tgdb/ReaderWriterSTG.hxx | 7 +++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/simgear/scene/tgdb/ReaderWriterSTG.cxx b/simgear/scene/tgdb/ReaderWriterSTG.cxx index a417df45..73fc9da0 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.cxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.cxx @@ -95,6 +95,13 @@ static SGBucket bucketIndexFromFileName(const std::string& fileName) return SGBucket(index); } +/** + * callback per STG token, with access synced by a lock. + */ +using TokenCallbackMap = std::map; +static TokenCallbackMap globalStgObjectCallbacks = {}; +static OpenThreads::Mutex globalStgObjectCallbackLock; + struct ReaderWriterSTG::_ModelBin { struct _Object { SGPath _errorLocation; @@ -536,8 +543,26 @@ struct ReaderWriterSTG::_ModelBin { _buildingListList.push_back(buildinglist); //SG_LOG(SG_TERRAIN, SG_ALERT, "Building list: " << buildinglist._filename << " " << buildinglist._material_name << " " << buildinglist._lon << " " << buildinglist._lat); } else { - SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName - << ": Unknown token '" << token << "'" ); + // Check registered callback for token. Keep lock until callback completed to make sure it will not be + // executed after a thread successfully executed removeSTGObjectHandler() + { + OpenThreads::ScopedLock lock(globalStgObjectCallbackLock); + STGObjectCallback callback = globalStgObjectCallbacks[token]; + + if (callback != nullptr) { + _ObjectStatic obj; + // pitch and roll are not common, so passed in "restofline" only + in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg; + string_list restofline; + std::string buf; + while (in >> buf) { + restofline.push_back(buf); + } + callback(token,name, SGGeod::fromDegM(obj._lon, obj._lat, obj._elev), obj._hdg,restofline); + } else { + SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName << ": Unknown token '" << token << "'" ); + } + } } } } @@ -711,4 +736,16 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt return modelBin.load(bucket, options); } + +void ReaderWriterSTG::setSTGObjectHandler(const std::string &token, STGObjectCallback callback) +{ + OpenThreads::ScopedLock lock(globalStgObjectCallbackLock); + globalStgObjectCallbacks[token] = callback; +} + +void ReaderWriterSTG::removeSTGObjectHandler(const std::string &token, STGObjectCallback callback) +{ + OpenThreads::ScopedLock lock(globalStgObjectCallbackLock); + globalStgObjectCallbacks.erase(token); +} } diff --git a/simgear/scene/tgdb/ReaderWriterSTG.hxx b/simgear/scene/tgdb/ReaderWriterSTG.hxx index 8e19578e..19a1e7ea 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.hxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.hxx @@ -23,7 +23,9 @@ #define _READERWRITERSTG_HXX #include +#include +class SGGeod; class SGBucket; namespace simgear { @@ -38,6 +40,11 @@ public: virtual ReadResult readNode(const std::string&, const osgDB::Options*) const; + //pitch and roll are not common, so passed in "restofline" only + using STGObjectCallback = std::function; + //add/remove a callback that is invoked for unknown STG token + static void setSTGObjectHandler(const std::string &token, STGObjectCallback callback); + static void removeSTGObjectHandler(const std::string &token, STGObjectCallback callback); private: struct _ModelBin; }; From b742607c769cbb666aa6f34208cb7a7054f8ef92 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Thu, 3 Oct 2019 11:50:07 -0500 Subject: [PATCH 15/52] [ReaderWriteSTG] fix for missing header. --- simgear/scene/tgdb/ReaderWriterSTG.hxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/simgear/scene/tgdb/ReaderWriterSTG.hxx b/simgear/scene/tgdb/ReaderWriterSTG.hxx index 19a1e7ea..74b6c77b 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.hxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.hxx @@ -22,6 +22,8 @@ #ifndef _READERWRITERSTG_HXX #define _READERWRITERSTG_HXX +#include + #include #include From ca97b4371b83fba90eac3872e42d2f0e3d37a789 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sat, 5 Oct 2019 17:19:09 +0100 Subject: [PATCH 16/52] Use alternative name for internal expat_config.h Avoid clashes when system has expat installed. --- 3rdparty/expat/CMakeLists.txt | 2 +- 3rdparty/expat/xmlparse.c | 2 ++ 3rdparty/expat/xmlrole.c | 2 ++ 3rdparty/expat/xmltok.c | 2 ++ CMakeLists.txt | 3 --- simgear/CMakeLists.txt | 32 ++++++++++++++++++++++---------- 6 files changed, 29 insertions(+), 14 deletions(-) diff --git a/3rdparty/expat/CMakeLists.txt b/3rdparty/expat/CMakeLists.txt index 550a52c9..badfd61c 100644 --- a/3rdparty/expat/CMakeLists.txt +++ b/3rdparty/expat/CMakeLists.txt @@ -1,7 +1,7 @@ configure_file ( "${PROJECT_SOURCE_DIR}/3rdparty/expat/expat_config_cmake.in" - "${PROJECT_BINARY_DIR}/3rdparty/expat/expat_config.h" + "${PROJECT_BINARY_DIR}/3rdparty/expat/simgear_expat_config.h" ) set(expat_sources diff --git a/3rdparty/expat/xmlparse.c b/3rdparty/expat/xmlparse.c index e4fb313f..83ab782d 100644 --- a/3rdparty/expat/xmlparse.c +++ b/3rdparty/expat/xmlparse.c @@ -18,6 +18,8 @@ #include "amigaconfig.h" #elif defined(__WATCOMC__) #include "watcomconfig.h" +#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H) +#include "simgear_expat_config.h" #elif defined(HAVE_EXPAT_CONFIG_H) #include "expat_config.h" #endif /* ndef COMPILED_FROM_DSP */ diff --git a/3rdparty/expat/xmlrole.c b/3rdparty/expat/xmlrole.c index 0bb159f5..84ce13dd 100644 --- a/3rdparty/expat/xmlrole.c +++ b/3rdparty/expat/xmlrole.c @@ -12,6 +12,8 @@ #include "amigaconfig.h" #elif defined(__WATCOMC__) #include "watcomconfig.h" +#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H) +#include "simgear_expat_config.h" #else #ifdef HAVE_EXPAT_CONFIG_H #include "expat_config.h" diff --git a/3rdparty/expat/xmltok.c b/3rdparty/expat/xmltok.c index 5a8bd132..674d8cc5 100644 --- a/3rdparty/expat/xmltok.c +++ b/3rdparty/expat/xmltok.c @@ -12,6 +12,8 @@ #include "amigaconfig.h" #elif defined(__WATCOMC__) #include "watcomconfig.h" +#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H) +#include "simgear_expat_config.h" #else #ifdef HAVE_EXPAT_CONFIG_H #include "expat_config.h" diff --git a/CMakeLists.txt b/CMakeLists.txt index 7710db08..705b610d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,9 +273,6 @@ if (SYSTEM_EXPAT) else() message(STATUS "Using built-in expat code") - # XML_STATIC is important to avoid sg_expat_external.h - # declaring symbols as declspec(import) - add_definitions(-DHAVE_EXPAT_CONFIG_H -DXML_STATIC) set(EXPAT_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/3rdparty/expat ${PROJECT_BINARY_DIR}/3rdparty/expat) diff --git a/simgear/CMakeLists.txt b/simgear/CMakeLists.txt index ca26c9d2..7646ac09 100644 --- a/simgear/CMakeLists.txt +++ b/simgear/CMakeLists.txt @@ -115,11 +115,22 @@ target_include_directories(SimGearCore BEFORE PUBLIC $ $) +# so simgear/simgear_config.h is found +target_include_directories(SimGearCore BEFORE PUBLIC + $ + $) + target_include_directories(SimGearCore PUBLIC ${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR}) target_include_directories(SimGearCore PRIVATE ${EXPAT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS}) +if (NOT SYSTEM_EXPAT) + # XML_STATIC is important to avoid sg_expat_external.h + # declaring symbols as declspec(import) + target_compile_definitions(SimGearCore PRIVATE HAVE_SIMGEAR_EXPAT_CONFIG_H XML_STATIC) +endif() + install(TARGETS SimGearCore EXPORT SimGearTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -144,9 +155,10 @@ if (NOT SIMGEAR_HEADLESS) endif() endif() +# we expose ZLib in some of our headers +target_link_libraries(SimGearCore PUBLIC ${ZLIB_LIBRARY}) -target_link_libraries(SimGearCore - ${ZLIB_LIBRARY} +target_link_libraries(SimGearCore PRIVATE ${RT_LIBRARY} ${DL_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} @@ -155,29 +167,29 @@ target_link_libraries(SimGearCore ${WINSOCK_LIBRARY}) if(SYSTEM_EXPAT) - target_link_libraries(SimGearCore - ${EXPAT_LIBRARIES}) + target_link_libraries(SimGearCore PRIVATE ${EXPAT_LIBRARIES}) endif() if(ENABLE_DNS AND SYSTEM_UDNS) - target_link_libraries(SimGearCore - ${UDNS_LIBRARIES}) + target_link_libraries(SimGearCore PRIVATE ${UDNS_LIBRARIES}) endif() if(NOT SIMGEAR_HEADLESS) target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include) - target_link_libraries(SimGearScene + target_link_libraries(SimGearScene PUBLIC SimGearCore - ${ZLIB_LIBRARY} ${OPENSCENEGRAPH_LIBRARIES} + ) + + target_link_libraries(SimGearScene PRIVATE + ${ZLIB_LIBRARY} ${OPENAL_LIBRARY} ${OPENGL_LIBRARY} ${JPEG_LIBRARY}) if(ENABLE_GDAL) - target_link_libraries(SimGearScene - ${GDAL_LIBRARIES}) + target_link_libraries(SimGearScene PRIVATE ${GDAL_LIBRARIES}) endif() # only actually needed by canvas/KeyboardEvent.cxx From 07586e84ce88d5a79e1c8623cfdcd50a160aed40 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sun, 6 Oct 2019 12:52:12 +0100 Subject: [PATCH 17/52] Rotate orientation of pitched roofs by 90 degrees Also add some additional checking of parameters. --- simgear/scene/tgdb/SGBuildingBin.cxx | 183 ++++++++++++++------------- 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index a22be5c9..df01aa81 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -105,6 +105,7 @@ struct MakeBuildingLeaf 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 differentiate wall from roof + t->push_back( osg::Vec2( 0.0, 0.0) ); } // Left face @@ -116,6 +117,7 @@ struct MakeBuildingLeaf for (int i=0; i<4; ++i) { n->push_back( osg::Vec3(0, -1, 0) ); // normal c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof + t->push_back( osg::Vec2( 0.0, 0.0) ); } // Back face @@ -127,6 +129,7 @@ struct MakeBuildingLeaf 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 differentiate wall from roof + t->push_back( osg::Vec2( 0.0, 0.0) ); } // Right face @@ -138,6 +141,7 @@ struct MakeBuildingLeaf for (int i=0; i<4; ++i) { n->push_back( osg::Vec3(0, 1, 0) ); // normal c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof + t->push_back( osg::Vec2( 0.0, 0.0) ); } // MAIN BODY @@ -147,6 +151,11 @@ struct MakeBuildingLeaf 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 + 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 + 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 differentiate wall from roof @@ -158,6 +167,11 @@ struct MakeBuildingLeaf 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 + 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 + for (int i=0; i<4; ++i) { n->push_back( osg::Vec3(0, -1, 0) ); // normal c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof @@ -169,6 +183,11 @@ struct MakeBuildingLeaf 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 + 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 + 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 differentiate wall from roof @@ -180,6 +199,11 @@ struct MakeBuildingLeaf 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 + 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 + for (int i=0; i<4; ++i) { n->push_back( osg::Vec3(0, 1, 0) ); // normal c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof @@ -189,110 +213,67 @@ struct MakeBuildingLeaf // 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 + v->push_back( osg::Vec3( 0.0, 0.0, 1.5) ); // top left + v->push_back( osg::Vec3( 0.0, 0.0, 1.5) ); // top right + + t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right + t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right + t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right + t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom 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, 1, 0, 0) ); // color - used to differentiate wall from roof + n->push_back( osg::Vec3( 1, 0, 0) ); // normal + c->push_back( osg::Vec4( 1, 0, 0, 0) ); // color - used to differentiate wall from roof. This is really an extension of the wall } // 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 + v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // bottom left + v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // bottom right + v->push_back( osg::Vec3( 0.0, 0.0, 1.5) ); // top right + v->push_back( osg::Vec3( -1.0, 0.0, 1.5) ); // top left + + 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 for (int i=0; i<4; ++i) { - n->push_back( osg::Vec3(0, -1, 0) ); // normal + n->push_back( osg::Vec3(0, -0.707, 0.707) ); // normal c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof } // 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 + 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.0, 1.5) ); // top left + v->push_back( osg::Vec3( -1.0, 0.0, 1.5) ); // top right + + t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right + t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right + t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right + t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom 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, 1, 0, 0) ); // color - used to differentiate wall from roof + n->push_back( osg::Vec3(-1.0, 0, 0) ); // normal + c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof. This is really an extension of the wall below. } // 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 + v->push_back( osg::Vec3( 0.0, 0.5, 1.0) ); // bottom left + v->push_back( osg::Vec3( -1.0, 0.5, 1.0) ); // bottom right + v->push_back( osg::Vec3( -1.0, 0.0, 1.5) ); // top right + v->push_back( osg::Vec3( 0.0, 0.0, 1.5) ); // top left + + 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 for (int i=0; i<4; ++i) { - n->push_back( osg::Vec3(0, 1, 0) ); // normal + n->push_back( osg::Vec3(0, 0.707, 0.707) ); // normal c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof } - // 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++)); @@ -350,10 +331,10 @@ struct AddBuildingLeafObject positions->push_back(building.position); // Depth is the x-axis, width is the y-axis - scale->push_back(osg::Vec3(building.depth, building.width, building.height)); - rot->push_back(osg::Vec3(building.rotation, building.pitch_height, building.walltex0.x())); - tex->push_back(osg::Vec3(building.walltex0.y(), building.tex1.x(), building.tex1.y())); - rtex->push_back(osg::Vec3(building.rooftex0.x(), building.rooftex0.y(), 0.0f)); + scale->push_back(osg::Vec3f(building.depth, building.width, building.height)); + rot->push_back(osg::Vec3f(building.rotation, building.pitch_height, building.walltex0.x())); + tex->push_back(osg::Vec3f(building.walltex0.y(), building.tex1.x(), building.tex1.y())); + rtex->push_back(osg::Vec3f(building.rooftex0.x(), building.rooftex0.y(), 0.0f)); DrawArrays* primSet = static_cast(geom->getPrimitiveSet(0)); primSet->setNumInstances(positions->size()); @@ -468,8 +449,8 @@ typedef QuadTreeBuilder 0.5f) || + (wall_offset_y + gain_y > (6.0f * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT))) { + SG_LOG(SG_TERRAIN, SG_ALERT, "Small building texture coordinates out of bounds offset (" << wall_offset_x << ", " << wall_offset_y << ") gain (" << gain_x << ", " << gain_y << ")"); + } + + wall_tex0 = Vec2f(wall_offset_x, wall_offset_y); roof_tex0 = Vec2f(roof_offset_x, roof_offset_y); tex1 = Vec2f(gain_x, gain_y); @@ -507,9 +495,16 @@ typedef QuadTreeBuilder 0.5f) || + (wall_offset_y + gain_y < (6.0f * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT)) || + (wall_offset_y + gain_y > ((6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT))) { + SG_LOG(SG_TERRAIN, SG_ALERT, "Medium building texture coordinates out of bounds offset (" << wall_offset_x << ", " << wall_offset_y << ") gain (" << gain_x << ", " << gain_y << ")"); + } + + wall_tex0 = Vec2f(wall_offset_x, wall_offset_y); roof_tex0 = Vec2f(roof_offset_x, roof_offset_y); tex1 = Vec2f(gain_x, gain_y); @@ -526,9 +521,15 @@ typedef QuadTreeBuilder 0.5f) || + (wall_offset_y + gain_y < ((6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT)) || + (wall_offset_y + gain_y > 1.0) ) { + SG_LOG(SG_TERRAIN, SG_ALERT, "Large building texture coordinates out of bounds offset (" << wall_offset_x << ", " << wall_offset_y << ") gain (" << gain_x << ", " << gain_y << ")"); + } + wall_tex0 = Vec2f(wall_offset_x, wall_offset_y); roof_tex0 = Vec2f(roof_offset_x, roof_offset_y); tex1 = Vec2f(gain_x, gain_y); From 74cd8cf75ccddbf9afb3bdfc2dea3ed80dac0682 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Tue, 8 Oct 2019 21:38:48 +0100 Subject: [PATCH 18/52] Shader building roof types Shader buildings now support multiple roof types: - flat - gabled - hipped - pyramidal --- simgear/scene/tgdb/SGBuildingBin.cxx | 215 +++++++++++++++++++-------- simgear/scene/tgdb/SGBuildingBin.hxx | 13 +- 2 files changed, 166 insertions(+), 62 deletions(-) diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index df01aa81..bcecc1f8 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -85,10 +85,10 @@ struct MakeBuildingLeaf // 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); + v->reserve(52); + t->reserve(52); + n->reserve(52); + c->reserve(52); // Now create an OSG Geometry based on the Building // 0,0,0 is the bottom center of the front @@ -209,78 +209,119 @@ struct MakeBuildingLeaf c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof } - // 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.0, 0.0, 1.5) ); // top left - v->push_back( osg::Vec3( 0.0, 0.0, 1.5) ); // top right - - t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right - t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right - t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right - t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom 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 differentiate wall from roof. This is really an extension of the wall - } - - // Left pitched roof - v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // bottom left - v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // bottom right - v->push_back( osg::Vec3( 0.0, 0.0, 1.5) ); // top right - v->push_back( osg::Vec3( -1.0, 0.0, 1.5) ); // top left + // ROOF 1 - built as a block. The shader will deform it to the correct shape. + // 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, 1.0) ); // top left + v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // top right 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 + t->push_back( osg::Vec2( 0.0, 1.0) ); // top left + t->push_back( osg::Vec2( -1.0, 1.0) ); // top right + + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof for (int i=0; i<4; ++i) { - n->push_back( osg::Vec3(0, -0.707, 0.707) ); // normal - c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + n->push_back( osg::Vec3(0.707, 0, 0.707) ); // normal } - // Back pitched roof + // 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, 1.0) ); // top left + v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // top right + + 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 + + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0, -0.707, 0.707) ); // normal + } + + // 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.0, 1.5) ); // top left - v->push_back( osg::Vec3( -1.0, 0.0, 1.5) ); // top right - - t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right - t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right - t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right - t->push_back( osg::Vec2( 0.5, 1.0) ); // bottom right - - for (int i=0; i<4; ++i) { - n->push_back( osg::Vec3(-1.0, 0, 0) ); // normal - c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof. This is really an extension of the wall below. - } - - // Right pitched roof - v->push_back( osg::Vec3( 0.0, 0.5, 1.0) ); // bottom left - v->push_back( osg::Vec3( -1.0, 0.5, 1.0) ); // bottom right - v->push_back( osg::Vec3( -1.0, 0.0, 1.5) ); // top right - v->push_back( osg::Vec3( 0.0, 0.0, 1.5) ); // top 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 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 + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + for (int i=0; i<4; ++i) { - n->push_back( osg::Vec3(0, 0.707, 0.707) ); // normal - c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + n->push_back( osg::Vec3(-0.707, 0, 0.707) ); // normal } + // 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, 1.0) ); // top left + v->push_back( osg::Vec3( 0.0, 0.5, 1.0) ); // top right + + 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 + + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0, 0.707, 0.707) ); // normal + } + + // Top 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( -1.0, 0.5, 1.0) ); // top left + v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // top right + + 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 + + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof + + for (int i=0; i<4; ++i) { + n->push_back( osg::Vec3(0, 0, -1.0) ); // normal + } + + assert(v->size() == 52); + assert(t->size() == 52); + assert(c->size() == 52); + assert(n->size() == 52); + 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->setColorArray(c, Array::BIND_PER_VERTEX); geom->setUseDisplayList( false ); geom->setUseVertexBufferObjects( true ); geom->setComputeBoundingBoxCallback(new BuildingBoundingBoxCallback); @@ -290,8 +331,10 @@ struct MakeBuildingLeaf 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->setVertexAttribArray(BUILDING_RTEX0X_RTEX0Y_ATTR, new osg::Vec3Array, Array::BIND_PER_VERTEX); + geom->setVertexAttribArray(BUILDING_ROFFTOPSCALE_ATTR, new osg::Vec3Array, Array::BIND_PER_VERTEX); - geom->addPrimitiveSet( new osg::DrawArrays( GL_QUADS, 0, 48, 0) ); + //geom->addPrimitiveSet( new osg::DrawArrays( GL_QUADS, 0, 48, 0) ); + geom->addPrimitiveSet( new osg::DrawArrays( GL_QUADS, 0, 52, 0) ); EffectGeode* geode = new EffectGeode; geode->addDrawable(geom); @@ -303,6 +346,7 @@ struct MakeBuildingLeaf ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_ROT_PITCH_TEX0X_ATTR, 1)); ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR, 1)); ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_RTEX0X_RTEX0Y_ATTR, 1)); + ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_ROFFTOPSCALE_ATTR, 1)); LOD* result = new LOD; result->addChild(geode, 0, _range); @@ -328,6 +372,7 @@ struct AddBuildingLeafObject osg::Vec3Array* rot = static_cast (geom->getVertexAttribArray(BUILDING_ROT_PITCH_TEX0X_ATTR)); // (rotation, pitch height, wall texture x offset) osg::Vec3Array* tex = static_cast (geom->getVertexAttribArray(BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR)); // (wall texture y offset, wall/roof texture x gain, wall/roof texture y gain) osg::Vec3Array* rtex = static_cast (geom->getVertexAttribArray(BUILDING_RTEX0X_RTEX0Y_ATTR)); // (roof texture x offset, roof texture y offset, unused) + osg::Vec3Array* rooftops = static_cast (geom->getVertexAttribArray(BUILDING_ROFFTOPSCALE_ATTR)); // (rooftop scale x, rooftop scale y, unused) positions->push_back(building.position); // Depth is the x-axis, width is the y-axis @@ -335,6 +380,7 @@ struct AddBuildingLeafObject rot->push_back(osg::Vec3f(building.rotation, building.pitch_height, building.walltex0.x())); tex->push_back(osg::Vec3f(building.walltex0.y(), building.tex1.x(), building.tex1.y())); rtex->push_back(osg::Vec3f(building.rooftex0.x(), building.rooftex0.y(), 0.0f)); + rooftops->push_back(osg::Vec3f(building.rooftop_scale.x(), building.rooftop_scale.y(), 0.0f)); DrawArrays* primSet = static_cast(geom->getPrimitiveSet(0)); primSet->setNumInstances(positions->size()); @@ -392,10 +438,10 @@ typedef QuadTreeBuilder 0.0f) + { + // Roof with some pitch height + + if ((roof_shape == 2) || (roof_shape == 6) || (roof_shape == 10) || (roof_shape == 11)) { + // Gabled, gambrel, round, saltbox + if (roof_orientation == 0) rooftop_scale = Vec2f(1.0f, 0.0f); + if (roof_orientation == 1) rooftop_scale = Vec2f(0.0f, 1.0f); + } + + if ((roof_shape == 3) || (roof_shape == 4) || (roof_shape == 7)) { + // Hipped, half-hipped, mansard + // The pitch height expressed as a fraction of the building width/depth such that the hipped + // roof has a pitch of around 45 degrees. + if (roof_orientation == 0) rooftop_scale = Vec2f(min(0.5f,(width - 2*pitch_height) / width), 0.0f); + if (roof_orientation == 1) rooftop_scale = Vec2f(0.0f, min(0.5f,(depth - 2*pitch_height) / width)); + } + + // Pyramidal, dome, onion + if ((roof_shape == 5) || (roof_shape == 8) || (roof_shape == 9)) rooftop_scale = Vec2f(0.0f, 0.0f); + } + + buildingLocations.push_back(BuildingInstance(toOsg(p), width, depth, height, pitch_height, r, wall_tex0, roof_tex0, tex1, rooftop_scale)); } @@ -544,6 +615,8 @@ typedef QuadTreeBuilder width) { depth = width; } pitch_height = (mt_rand(&seed) < material->get_building_small_pitch()) ? 3.0 : 0.0; + + if (pitch_height == 0.0) { + roof_shape = 0; + roof_orientation = 0; + } else { + roof_shape = (int) (mt_rand(&seed) * 10.0); + roof_orientation = (int) std::round((float) mt_rand(&seed)); + } } 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()); @@ -575,6 +656,14 @@ typedef QuadTreeBuilderget_building_medium_pitch()) ? 3.0 : 0.0; + + if (pitch_height == 0.0) { + roof_shape = 0; + roof_orientation = 0; + } else { + roof_shape = (int) (mt_rand(&seed) * 10.0); + roof_orientation = (int) std::round((float) mt_rand(&seed)); + } } 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()); @@ -582,9 +671,17 @@ typedef QuadTreeBuilder::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 (pitch_height == 0.0) { + roof_shape = 0; + roof_orientation = 0; + } else { + roof_shape = (int) (mt_rand(&seed) * 10.0); + roof_orientation = (int) std::round((float) mt_rand(&seed)); + } } - insert(p, r, buildingtype, width, depth, height, pitch_height, floors, (int) (mt_rand(&seed) * 1000.0), (int) (mt_rand(&seed) * 1000.0)); + insert(p, r, buildingtype, width, depth, height, pitch_height, floors, roof_shape, roof_orientation, (int) (mt_rand(&seed) * 1000.0), (int) (mt_rand(&seed) * 1000.0)); } int SGBuildingBin::getNumBuildings() { diff --git a/simgear/scene/tgdb/SGBuildingBin.hxx b/simgear/scene/tgdb/SGBuildingBin.hxx index 666a1f45..d10d1c1d 100644 --- a/simgear/scene/tgdb/SGBuildingBin.hxx +++ b/simgear/scene/tgdb/SGBuildingBin.hxx @@ -57,6 +57,7 @@ const int BUILDING_SCALE_ATTR = 11; // (width, depth, height) const int BUILDING_ROT_PITCH_TEX0X_ATTR = 12; // (rotation, pitch height, wall texture x offset) const int BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR = 13; // (wall texture y offset, texture x gain, texture y gain) const int BUILDING_RTEX0X_RTEX0Y_ATTR = 14; // (roof texture x offset, roof texture y offset, unused) +const int BUILDING_ROFFTOPSCALE_ATTR = 15; // (roof texture x offset, roof texture y offset, unused) using namespace osg; @@ -91,7 +92,7 @@ public: LARGE }; struct BuildingInstance { - BuildingInstance(Vec3f p, float w, float d, float h, float ph, float r, Vec2f wt0, Vec2f rt0, Vec2f t1) : + BuildingInstance(Vec3f p, float w, float d, float h, float ph, float r, Vec2f wt0, Vec2f rt0, Vec2f t1, Vec2f rs) : position(p), width(w), depth(d), @@ -100,7 +101,8 @@ public: rotation(r), walltex0(wt0), rooftex0(rt0), - tex1(t1) + tex1(t1), + rooftop_scale(rs) { } BuildingInstance(Vec3f p, BuildingInstance b) : @@ -112,7 +114,8 @@ public: rotation(b.rotation), walltex0(b.walltex0), rooftex0(b.rooftex0), - tex1(b.tex1) + tex1(b.tex1), + rooftop_scale(b.rooftop_scale) { } @@ -127,6 +130,8 @@ public: Vec2f rooftex0; Vec2f tex1; + Vec2f rooftop_scale; + // References to allow the QuadTreeBuilder to work //const BuildingList* buildingList; //ref_ptr sharedGeometry; @@ -171,6 +176,8 @@ public: float height, float pitch_height, int floors, + int roof_shape, + int roof_orientation, int wall_tex_index, int roof_tex_index); From 65331599ecbbb9ba4546897d967036b7feebaf08 Mon Sep 17 00:00:00 2001 From: Mykola Orliuk Date: Sat, 5 Oct 2019 18:43:50 +0200 Subject: [PATCH 19/52] BoostTestTargets.cmake: support CMake 3.15 Signed-off-by: James Turner --- CMakeLists.txt | 3 +++ CMakeModules/BoostTestTargets.cmake | 16 +++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 705b610d..1bc7b920 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,9 @@ if(COMMAND cmake_policy) if(POLICY CMP0067) cmake_policy(SET CMP0067 NEW) endif() + if(POLICY CMP0093) + cmake_policy(SET CMP0093 NEW) + endif() endif() diff --git a/CMakeModules/BoostTestTargets.cmake b/CMakeModules/BoostTestTargets.cmake index 580bf9d2..f1bd0f99 100644 --- a/CMakeModules/BoostTestTargets.cmake +++ b/CMakeModules/BoostTestTargets.cmake @@ -46,11 +46,17 @@ set(BOOST_TEST_TARGET_PREFIX "test") if(NOT Boost_FOUND) find_package(Boost 1.34.0 QUIET) endif() -if("${Boost_VERSION}0" LESS "1034000") + +if (NOT Boost_VERSION_MACRO) + # Compatibility with pre CMP0093 (CMake 3.15) + set(Boost_VERSION_MACRO ${Boost_VERSION}) +endif() + +if("${Boost_VERSION_MACRO}0" LESS "1034000") set(_shared_msg "NOTE: boost::test-based targets and tests cannot " "be added: boost >= 1.34.0 required but not found. " - "(found: '${Boost_VERSION}'; want >=103400) ") + "(found: '${Boost_VERSION_MACRO}'; want >=103400) ") if(ENABLE_TESTS) message(FATAL_ERROR ${_shared_msg} @@ -66,7 +72,7 @@ endif() include(GetForceIncludeDefinitions) include(CopyResourcesToBuildTree) -if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000") +if(Boost_FOUND AND NOT "${Boost_VERSION_MACRO}0" LESS "1034000") set(_boosttesttargets_libs) set(_boostConfig "BoostTestTargetsIncluded.h") if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY) @@ -129,7 +135,7 @@ function(add_boost_test _name) "Syntax error in use of add_boost_test: at least one source file required!") endif() - if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000") + if(Boost_FOUND AND NOT "${Boost_VERSION_MACRO}0" LESS "1034000") include_directories(${Boost_INCLUDE_DIRS}) @@ -221,7 +227,7 @@ function(add_boost_test _name) set(_test_command ${_target_name}) endif() - if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" )) + if(TESTS AND ( "${Boost_VERSION_MACRO}" VERSION_GREATER "103799" )) foreach(_test ${TESTS}) add_test( ${_name}-${_test} From de3625d992ed123821a559e68317dcc43009d817 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sat, 19 Oct 2019 07:42:28 -0700 Subject: [PATCH 20/52] Fix linkage of OpenAL tests Now we no longer list OpenAL a s apublic dependency of SimGear, we need to explicitly link the tests against OpenAL, since they use it directly, in all cases. --- simgear/sound/CMakeLists.txt | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/simgear/sound/CMakeLists.txt b/simgear/sound/CMakeLists.txt index 884e9520..c63556bb 100644 --- a/simgear/sound/CMakeLists.txt +++ b/simgear/sound/CMakeLists.txt @@ -38,28 +38,13 @@ if(ENABLE_TESTS AND ENABLE_SOUND) COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" ) endfunction() - set( SOUND_TEST_LIBS - ${TEST_LIBS} - ) + set( SOUND_TEST_LIBS ${TEST_LIBS} ) if (USE_AEONWAVE) - if (SIMGEAR_SHARED) - else() - set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS} - ${AAX_LIBRARY} - ) - endif() - + set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS} ${AAX_LIBRARY}) create_test(aeonwave_test1) - else () - if (SIMGEAR_SHARED) - else() - set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS} - ${OPENAL_LIBRARY} - ) - endif() - + set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS} ${OPENAL_LIBRARY}) create_test(openal_test1) endif() From 4b1a53367f3cacc3f82dcb9ed3e441afe9e6a633 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 20 Oct 2019 13:02:30 +0100 Subject: [PATCH 21/52] =?UTF-8?q?Runway=20lights=20improvements=20from=20S?= =?UTF-8?q?ascha=20Rei=C3=9Fner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (Sascha Reißner ) --- simgear/scene/tgdb/SGTileDetailsCallback.hxx | 68 +++++++---------- simgear/scene/tgdb/pt_lights.cxx | 80 +++++++++++++------- simgear/scene/tgdb/pt_lights.hxx | 3 + 3 files changed, 82 insertions(+), 69 deletions(-) diff --git a/simgear/scene/tgdb/SGTileDetailsCallback.hxx b/simgear/scene/tgdb/SGTileDetailsCallback.hxx index 6a16c5da..6e22c8cd 100644 --- a/simgear/scene/tgdb/SGTileDetailsCallback.hxx +++ b/simgear/scene/tgdb/SGTileDetailsCallback.hxx @@ -903,64 +903,48 @@ public: } Effect* runwayEffect = 0; - if (runwayLights.getNumLights() > 0 - || !rabitLights.empty() - || !reilLights.empty() - || !odalLights.empty() - || taxiLights.getNumLights() > 0) { - + if (runwayLights.getNumLights() > 0 || taxiLights.getNumLights() > 0) { runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true, _options); } - + if (runwayLights.getNumLights() > 0 || !rabitLights.empty() || !reilLights.empty() || !odalLights.empty() || !holdshortLights.empty() || !guardLights.empty()) { - osg::Group* rwyLights = new osg::Group; - osg::StateSet* ss = lightManager->getRunwayLightStateSet(); - rwyLights->setStateSet(ss); - rwyLights->setNodeMask(RUNWAYLIGHTS_BIT); - - if (runwayLights.getNumLights() != 0) { - EffectGeode* geode = new EffectGeode; - geode->setEffect(runwayEffect); - - osg::Drawable* rldraw = SGLightFactory::getLights(runwayLights); - geode->addDrawable( rldraw ); - - rwyLights->addChild(geode); - } + osg::Group* rwyLightsGroup = new osg::Group; + rwyLightsGroup->setStateSet(lightManager->getRunwayLightStateSet()); + rwyLightsGroup->setNodeMask(RUNWAYLIGHTS_BIT); + SGDirectionalLightListBin::const_iterator i; - for (i = rabitLights.begin(); - i != rabitLights.end(); ++i) { - osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options); - rwyLights->addChild( seqNode ); + + for (i = rabitLights.begin() ; i != rabitLights.end() ; ++i) { + rwyLightsGroup->addChild(SGLightFactory::getSequenced(*i, _options)); } - for (i = reilLights.begin(); - i != reilLights.end(); ++i) { - osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options); - rwyLights->addChild(seqNode); + for (i = reilLights.begin() ; i != reilLights.end() ; ++i) { + rwyLightsGroup->addChild(SGLightFactory::getReil(*i, _options)); } - for (i = holdshortLights.begin(); - i != holdshortLights.end(); ++i) { - osg::Node* seqNode = SGLightFactory::getHoldShort(*i, _options); - rwyLights->addChild(seqNode); + for (i = holdshortLights.begin() ; i != holdshortLights.end() ; ++i) { + rwyLightsGroup->addChild(SGLightFactory::getHoldShort(*i, _options)); } - for (i = guardLights.begin(); - i != guardLights.end(); ++i) { - osg::Node* seqNode = SGLightFactory::getGuard(*i, _options); - rwyLights->addChild(seqNode); + for (i = guardLights.begin() ; i != guardLights.end() ; ++i) { + rwyLightsGroup->addChild(SGLightFactory::getGuard(*i, _options)); } SGLightListBin::const_iterator j; - for (j = odalLights.begin(); - j != odalLights.end(); ++j) { - osg::Node* seqNode = SGLightFactory::getOdal(*j, _options); - rwyLights->addChild(seqNode); + for (j = odalLights.begin() ; j != odalLights.end() ; ++j) { + rwyLightsGroup->addChild(SGLightFactory::getOdal(*j, _options)); } - lightGroup->addChild(rwyLights); + + if (runwayLights.getNumLights() > 0) { + osg::ref_ptr geode = new EffectGeode; + geode->setEffect(runwayEffect); + geode->addDrawable(SGLightFactory::getLights(runwayLights)); + rwyLightsGroup->addChild(geode); + } + + lightGroup->addChild(rwyLightsGroup); } if (taxiLights.getNumLights() > 0) { diff --git a/simgear/scene/tgdb/pt_lights.cxx b/simgear/scene/tgdb/pt_lights.cxx index be82127e..11ba4539 100644 --- a/simgear/scene/tgdb/pt_lights.cxx +++ b/simgear/scene/tgdb/pt_lights.cxx @@ -214,8 +214,7 @@ SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff geometry->setDataVariance(osg::Object::STATIC); geometry->setVertexArray(vertices); geometry->setNormalBinding(osg::Geometry::BIND_OFF); - geometry->setColorArray(colors); - geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); osg::DrawArrays* drawArrays; drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS, @@ -275,7 +274,7 @@ SGLightFactory::getLights(const SGDirectionalLightBin& lights) //stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); osg::DrawArrays* drawArrays; - drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS, + drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, vertices->size()); geometry->addPrimitiveSet(drawArrays); return geometry; @@ -370,18 +369,46 @@ SGLightFactory::getSequenced(const SGDirectionalLightBin& lights, const SGReader // generate a repeatable random seed sg_srandom(unsigned(lights.getLight(0).position[0])); - float flashTime = 2e-2 + 5e-3*sg_random(); + float flashTime = 0.065 + 0.003 * sg_random(); osg::Sequence* sequence = new osg::Sequence; sequence->setDefaultTime(flashTime); - Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001), - 6.0f, 10.0f, true, options); + Effect* effect = getLightEffect(24.0f, osg::Vec3(1.0, 0.0001, 0.000001), + 1.0f, 24.0f, true, options); for (int i = lights.getNumLights() - 1; 0 <= i; --i) { EffectGeode* egeode = new EffectGeode; egeode->setEffect(effect); egeode->addDrawable(getLightDrawable(lights.getLight(i))); sequence->addChild(egeode, flashTime); } - sequence->addChild(new osg::Group, 1 + 1e-1*sg_random()); + sequence->addChild(new osg::Group, 1.9 + (0.1 * sg_random()) - (lights.getNumLights() * flashTime)); + sequence->setInterval(osg::Sequence::LOOP, 0, -1); + sequence->setDuration(1.0f, -1); + sequence->setMode(osg::Sequence::START); + sequence->setSync(true); + return sequence; +} + +osg::Node* +SGLightFactory::getReil(const SGDirectionalLightBin& lights, const SGReaderWriterOptions* options) +{ + if (lights.getNumLights() <= 0) + return 0; + + // generate a repeatable random seed + sg_srandom(unsigned(lights.getLight(0).position[0])); + float flashTime = 0.065 + 0.003 * sg_random(); + osg::Sequence* sequence = new osg::Sequence; + sequence->setDefaultTime(flashTime); + Effect* effect = getLightEffect(24.0f, osg::Vec3(1.0, 0.0001, 0.000001), + 1.0f, 24.0f, true, options); + EffectGeode* egeode = new EffectGeode; + egeode->setEffect(effect); + + for (int i = lights.getNumLights() - 1; 0 <= i; --i) { + egeode->addDrawable(getLightDrawable(lights.getLight(i))); + } + sequence->addChild(egeode, flashTime); + sequence->addChild(new osg::Group, 1.9 + 0.1 * sg_random() - flashTime); sequence->setInterval(osg::Sequence::LOOP, 0, -1); sequence->setDuration(1.0f, -1); sequence->setMode(osg::Sequence::START); @@ -397,11 +424,11 @@ SGLightFactory::getOdal(const SGLightBin& lights, const SGReaderWriterOptions* o // generate a repeatable random seed sg_srandom(unsigned(lights.getLight(0).position[0])); - float flashTime = 2e-2 + 5e-3*sg_random(); + float flashTime = 0.065 + 0.003 * sg_random(); osg::Sequence* sequence = new osg::Sequence; sequence->setDefaultTime(flashTime); - Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001), - 6.0, 10.0, false, options); + Effect* effect = getLightEffect(20.0f, osg::Vec3(1.0, 0.0001, 0.000001), + 1.0f, 20.0f, false, options); // centerline lights for (int i = lights.getNumLights() - 1; i >= 2; i--) { EffectGeode* egeode = new EffectGeode; @@ -409,18 +436,18 @@ SGLightFactory::getOdal(const SGLightBin& lights, const SGReaderWriterOptions* o egeode->addDrawable(getLightDrawable(lights.getLight(i))); sequence->addChild(egeode, flashTime); } + // add extra empty group for a break + sequence->addChild(new osg::Group, 4 * flashTime); // runway end lights - osg::Group* group = new osg::Group; + EffectGeode* egeode = new EffectGeode; + egeode->setEffect(effect); for (unsigned i = 0; i < 2; ++i) { - EffectGeode* egeode = new EffectGeode; - egeode->setEffect(effect); egeode->addDrawable(getLightDrawable(lights.getLight(i))); - group->addChild(egeode); } - sequence->addChild(group, flashTime); + sequence->addChild(egeode, flashTime); // add an extra empty group for a break - sequence->addChild(new osg::Group, 2 + 1e-1*sg_random()); + sequence->addChild(new osg::Group, 1.9 + (0.1 * sg_random()) - ((lights.getNumLights() + 2) * flashTime)); sequence->setInterval(osg::Sequence::LOOP, 0, -1); sequence->setDuration(1.0f, -1); sequence->setMode(osg::Sequence::START); @@ -437,23 +464,22 @@ SGLightFactory::getHoldShort(const SGDirectionalLightBin& lights, const SGReader return 0; sg_srandom(unsigned(lights.getLight(0).position[0])); - float flashTime = 1 + 0.1 * sg_random(); + float flashTime = 0.9 + 0.2 * sg_random(); osg::Sequence* sequence = new osg::Sequence; // start with lights off - sequence->addChild(new osg::Group, flashTime); + sequence->addChild(new osg::Group, 0.2); // ...and increase the lights in steps - for (int i = 2; i < 7; i+=2) { - Effect* effect = getLightEffect(i, osg::Vec3(1, 0.001, 0.000002), - 0.0f, i, true, options); + for (int i = 0; i < 5; i++) { + Effect* effect = getLightEffect(12.0f + i, osg::Vec3(1, 0.001, 0.0002), + 1.0f, 12.0f + i, true, options); EffectGeode* egeode = new EffectGeode; + egeode->setEffect(effect); for (unsigned int j = 0; j < lights.getNumLights(); ++j) { egeode->addDrawable(getLightDrawable(lights.getLight(j))); - egeode->setEffect(effect); } - sequence->addChild(egeode, (i==6) ? flashTime : 0.1); + sequence->addChild(egeode, (i==4) ? flashTime : 0.1); } - sequence->setInterval(osg::Sequence::SWING, 0, -1); sequence->setDuration(1.0f, -1); sequence->setMode(osg::Sequence::START); @@ -470,11 +496,11 @@ SGLightFactory::getGuard(const SGDirectionalLightBin& lights, const SGReaderWrit // generate a repeatable random seed sg_srandom(unsigned(lights.getLight(0).position[0])); - float flashTime = 1.0f + 1*sg_random(); + float flashTime = 0.9 + 0.2 * sg_random(); osg::Sequence* sequence = new osg::Sequence; sequence->setDefaultTime(flashTime); - Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.001, 0.000002), - 0.0f, 8.0f, true, options); + Effect* effect = getLightEffect(16.0f, osg::Vec3(1.0, 0.001, 0.0002), + 1.0f, 16.0f, true, options); for (unsigned int i = 0; i < lights.getNumLights(); ++i) { EffectGeode* egeode = new EffectGeode; egeode->setEffect(effect); diff --git a/simgear/scene/tgdb/pt_lights.hxx b/simgear/scene/tgdb/pt_lights.hxx index 1097c4b6..005aea1d 100644 --- a/simgear/scene/tgdb/pt_lights.hxx +++ b/simgear/scene/tgdb/pt_lights.hxx @@ -87,6 +87,9 @@ public: static osg::Node* getSequenced(const SGDirectionalLightBin& lights, const simgear::SGReaderWriterOptions* options); + static osg::Node* + getReil(const SGDirectionalLightBin& lights, const simgear::SGReaderWriterOptions* options); + static osg::Node* getOdal(const SGLightBin& lights, const simgear::SGReaderWriterOptions* options); From 0a7514c47b9b97e8e9392af5469f2d80ae3dc8de Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 20 Oct 2019 13:03:15 +0100 Subject: [PATCH 22/52] =?UTF-8?q?Add=20new=20=E2=80=98listener-safe?= =?UTF-8?q?=E2=80=99=20SGProperty=20attribute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Used in FG to make certain tied properties as safe to use with listeners, and hence suppress the warning. --- simgear/props/props.cxx | 2 +- simgear/props/props.hxx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx index 9d941559..c6a84df0 100644 --- a/simgear/props/props.cxx +++ b/simgear/props/props.cxx @@ -849,7 +849,7 @@ SGPropertyNode::trace_read () const * Last used attribute * Update as needed when enum Attribute is changed */ -const int SGPropertyNode::LAST_USED_ATTRIBUTE = PROTECTED; +const int SGPropertyNode::LAST_USED_ATTRIBUTE = LISTENER_SAFE; /** * Default constructor: always creates a root node. diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx index 3afc8796..0d407c99 100644 --- a/simgear/props/props.hxx +++ b/simgear/props/props.hxx @@ -821,6 +821,7 @@ public: USERARCHIVE = 64, PRESERVE = 128, PROTECTED = 1 << 8, + LISTENER_SAFE = 1 << 9, /// it's safe to listen to this property, even if it's tied // beware: if you add another attribute here, // also update value of "LAST_USED_ATTRIBUTE". }; From 02b61b145afb914c6d72e05d506967833e6d08ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Sat, 26 Oct 2019 01:53:30 +0200 Subject: [PATCH 23/52] Compositor: Shaders are looked for in $FG_ROOT/Compositor/Shaders when the compositor is enabled. Fixed Compositor/ always being added to the effect filename. Now it is only added when the base folder is Effects/. --- simgear/scene/material/Effect.cxx | 7 ++++++- simgear/scene/material/makeEffect.cxx | 6 +++++- simgear/scene/viewer/CompositorPass.cxx | 18 +++++++++--------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx index c2240c05..6c486b0b 100644 --- a/simgear/scene/material/Effect.cxx +++ b/simgear/scene/material/Effect.cxx @@ -918,8 +918,13 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass, resolvedKey.attributes = prgKey.attributes; BOOST_FOREACH(const ShaderKey& shaderKey, prgKey.shaders) { - const string& shaderName = shaderKey.first; + // FIXME orig: const string& shaderName = shaderKey.first; + string shaderName = shaderKey.first; Shader::Type stype = (Shader::Type)shaderKey.second; + if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false) && + shaderName.substr(0, shaderName.find("/")) == "Shaders") { + shaderName = "Compositor/" + shaderName; + } string fileName = SGModelLib::findDataFile(shaderName, options); if (fileName.empty()) { diff --git a/simgear/scene/material/makeEffect.cxx b/simgear/scene/material/makeEffect.cxx index 35591c51..7877378e 100644 --- a/simgear/scene/material/makeEffect.cxx +++ b/simgear/scene/material/makeEffect.cxx @@ -130,8 +130,12 @@ Effect* makeEffect(const string& name, string effectFileName; // Use getPropertyRoot() because the SGReaderWriterOptions might not have a // valid property tree - if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false)) + if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false) && + name.substr(0, name.find("/")) == "Effects") { + // Temporarily append the Compositor/ directory to every effect that should + // be inside Effects/. effectFileName += "Compositor/"; + } effectFileName += name; effectFileName += ".eff"; string absFileName diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index 7e82765d..d2d7a6db 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -112,15 +112,15 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, camera->setClearStencil(root->getIntValue("clear-stencil", 0)); GLbitfield clear_mask = 0; - if (root->getBoolValue("clear-color-bit", true)) - clear_mask |= GL_COLOR_BUFFER_BIT; - if (root->getBoolValue("clear-accum-bit", false)) - clear_mask |= GL_ACCUM_BUFFER_BIT; - if (root->getBoolValue("clear-depth-bit", true)) - clear_mask |= GL_DEPTH_BUFFER_BIT; - if (root->getBoolValue("clear-stencil-bit", false)) - clear_mask |= GL_STENCIL_BUFFER_BIT; - // Default clear mask is GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, as in OSG + std::stringstream ss; + std::string bit; + // Default clear mask as in OSG + ss << root->getStringValue("clear-mask", "color depth"); + while (ss >> bit) { + if (bit == "color") clear_mask |= GL_COLOR_BUFFER_BIT; + else if (bit == "depth") clear_mask |= GL_DEPTH_BUFFER_BIT; + else if (bit == "stencil") clear_mask |= GL_STENCIL_BUFFER_BIT; + } camera->setClearMask(clear_mask); PropertyList p_bindings = root->getChildren("binding"); From 4b280a00db19aa5b3eddb0ba4f79cfe8031926ea Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sat, 26 Oct 2019 16:29:37 +0100 Subject: [PATCH 24/52] Shader Buildings - fix roofs, set small tex to 5m Fix a bug in the roof transformation that meant the roof ridges were not sized correctly. Also change the texture "unit" width for small buildings from 6m to 5m following discussions with Rick Gruber-Riemer. --- simgear/scene/tgdb/SGBuildingBin.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index bcecc1f8..157dcccd 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -503,14 +503,14 @@ typedef QuadTreeBuilder Date: Wed, 30 Oct 2019 17:14:38 +0100 Subject: [PATCH 25/52] Only use effect schemes when the Compositor is enabled --- simgear/scene/material/Effect.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx index 6c486b0b..89b33a75 100644 --- a/simgear/scene/material/Effect.cxx +++ b/simgear/scene/material/Effect.cxx @@ -1479,7 +1479,8 @@ static SGMutex realizeTechniques_lock; bool Effect::realizeTechniques(const SGReaderWriterOptions* options) { SGGuard g(realizeTechniques_lock); - mergeSchemesFallbacks(this, options); + if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false)) + mergeSchemesFallbacks(this, options); if (_isRealized) return true; From 5db8e42c69efe5ffe12b3c3cbfcc293a487acbaf Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Thu, 31 Oct 2019 08:37:30 +0000 Subject: [PATCH 26/52] simgear/environment/metar.*: added getDescription(). Returns human-readable descrition of metar. Uses code from flightgear:src/Main/metar_main.cxx. --- simgear/environment/metar.cxx | 314 ++++++++++++++++++++++++++++++++++ simgear/environment/metar.hxx | 5 + 2 files changed, 319 insertions(+) diff --git a/simgear/environment/metar.cxx b/simgear/environment/metar.cxx index 5b497a05..cc865149 100644 --- a/simgear/environment/metar.cxx +++ b/simgear/environment/metar.cxx @@ -38,9 +38,12 @@ # include #endif +#include #include #include #include +#include +#include #include #include @@ -155,6 +158,317 @@ SGMetar::~SGMetar() } +static const char *azimuthName(double d) +{ + const char *dir[] = { + "N", "NNE", "NE", "ENE", + "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", + "W", "WNW", "NW", "NNW" + }; + d += 11.25; + while (d < 0) + d += 360; + while (d >= 360) + d -= 360; + return dir[int(d / 22.5)]; +} + + +// round double to 10^g +static double rnd(double r, int g = 0) +{ + double f = pow(10.0, g); + return f * floor(r / f + 0.5); +} + + +/* A manipulator that can use spaces to emulate tab characters. */ +struct Tab +{ + /* If is 0, we simply insert tab characters. Otherwise we insert + spaces to align with the next column at multiple of . */ + explicit Tab(int stops) + : + _stops(stops) + {} + int _stops; +}; + +std::ostream& operator << (std::ostream& out, const Tab& t) +{ + if (t._stops == 0) { + return out << '\t'; + } + + std::ostringstream& out2 = *(std::ostringstream*) &out; + std::string s = out2.str(); + auto nl = s.rfind('\n'); + if (nl < 0) nl = 0; + int column = 0; + for (auto i = nl+1; i != s.size(); ++i) { + if (s[i] == '\t') + column = (column + t._stops) / t._stops * t._stops; + else + column += 1; + } + int column2 = (column + t._stops) / t._stops * t._stops; + for (int i=column; igetVisibility_m() != NaN) + out << tab << tab << tab << SGMetarVisibilityManip(*dirvis, tab) << "\n"; + + + // vertical visibility + SGMetarVisibility vertvis = getVertVisibility(); + if ((d = vertvis.getVisibility_ft()) != NaN) + out << "Vert. visibility:" << tab << SGMetarVisibilityManip(vertvis, tab) << "\n"; + else if (vertvis.getModifier() == SGMetarVisibility::NOGO) + out << "Vert. visibility:" << tab << "impossible to determine" << "\n"; + + + // wind + d = getWindSpeed_kmh(); + out << "Wind:" << tab << tab << tab; + if (d < .1) + out << "none" << "\n"; + else { + if ((i = getWindDir()) == -1) + out << "from variable directions"; + else + out << "from the " << azimuthName(i) << " (" << i << "°)"; + out << " at " << rnd(d, -1) << " km/h"; + + out << tab << tab << rnd(getWindSpeed_kt(), -1) << " kt"; + out << " = " << rnd(getWindSpeed_mph(), -1) << " mph"; + out << " = " << rnd(getWindSpeed_mps(), -1) << " m/s"; + out << "\n"; + + d = getGustSpeed_kmh(); + if (d != NaN && d != 0) { + out << tab << tab << tab << "with gusts at " << rnd(d, -1) << " km/h"; + out << tab << tab << tab << rnd(getGustSpeed_kt(), -1) << " kt"; + out << " = " << rnd(getGustSpeed_mph(), -1) << " mph"; + out << " = " << rnd(getGustSpeed_mps(), -1) << " m/s"; + out << "\n"; + } + + int from = getWindRangeFrom(); + int to = getWindRangeTo(); + if (from != to) { + out << tab << tab << tab << "variable from " << azimuthName(from); + out << " to " << azimuthName(to); + out << " (" << from << "°--" << to << "°)" << "\n"; + } + } + + + // temperature/humidity/air pressure + if ((d = getTemperature_C()) != NaN) { + out << "Temperature:" << tab << tab << d << "°C" << tab << tab << tab << tab << tab; + out << rnd(getTemperature_F(), -1) << "°F" << "\n"; + + if ((d = getDewpoint_C()) != NaN) { + out << "Dewpoint:" << tab << tab << d << "°C" << tab << tab << tab << tab << tab; + out << rnd(getDewpoint_F(), -1) << "°F" << "\n"; + out << "Rel. Humidity: " << tab << tab << rnd(getRelHumidity()) << "%" << "\n"; + } + } + if ((d = getPressure_hPa()) != NaN) { + out << "Pressure:" << tab << tab << rnd(d) << " hPa" << tab << tab << tab << tab; + out << rnd(getPressure_inHg(), -2) << " in. Hg" << "\n"; + } + + + // weather phenomena + vector wv = getWeather(); + vector::iterator weather; + for (i = 0, weather = wv.begin(); weather != wv.end(); weather++, i++) { + out << (i ? ", " : "Weather:") << tab << tab << weather->c_str(); + } + if (i) + out << "\n"; + + + // cloud layers + const char *coverage_string[5] = { + "clear skies", "few clouds", "scattered clouds", "broken clouds", "sky overcast" + }; + vector cv = getClouds(); + vector::iterator cloud; + for (lineno = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, lineno++) { + if (lineno) out << tab << tab << tab; + else out << "Sky condition:" << tab << tab; + + if ((i = cloud->getCoverage()) != -1) + out << coverage_string[i]; + if ((d = cloud->getAltitude_ft()) != NaN) + out << " at " << rnd(d, 1) << " ft"; + if ((s = cloud->getTypeLongString())) + out << " (" << s << ')'; + if (d != NaN) + out << tab << tab << tab << rnd(cloud->getAltitude_m(), 1) << " m"; + out << "\n"; + } + + + // runways + map rm = getRunways(); + map::iterator runway; + for (runway = rm.begin(); runway != rm.end(); runway++) { + lineno = 0; + if (!strcmp(runway->first.c_str(), "ALL")) + out << "All runways:" << tab << tab; + else + out << "Runway " << runway->first << ":" << tab << tab; + SGMetarRunway rwy = runway->second; + + // assemble surface string + vector surface; + if ((s = rwy.getDepositString()) && strlen(s)) + surface.push_back(s); + if ((s = rwy.getExtentString()) && strlen(s)) + surface.push_back(s); + if ((d = rwy.getDepth()) != NaN) { + sprintf(buf, "%.1lf mm", d * 1000.0); + surface.push_back(buf); + } + if ((s = rwy.getFrictionString()) && strlen(s)) + surface.push_back(s); + if ((d = rwy.getFriction()) != NaN) { + sprintf(buf, "friction: %.2lf", d); + surface.push_back(buf); + } + + if (! surface.empty()) { + vector::iterator rwysurf = surface.begin(); + for (i = 0; rwysurf != surface.end(); rwysurf++, i++) { + if (i) + out << ", "; + out << *rwysurf; + } + lineno++; + } + + // assemble visibility string + SGMetarVisibility minvis = rwy.getMinVisibility(); + SGMetarVisibility maxvis = rwy.getMaxVisibility(); + if ((d = minvis.getVisibility_m()) != NaN) { + if (lineno++) + out << "\n" << tab << tab << tab; + out << SGMetarVisibilityManip(minvis, tab); + } + if (maxvis.getVisibility_m() != d) { + out << "\n" << tab << tab << tab << SGMetarVisibilityManip(maxvis, tab) << "\n"; + lineno++; + } + + if (rwy.getWindShear()) { + if (lineno++) + out << "\n" << tab << tab << tab; + out << "critical wind shear" << "\n"; + } + out << "\n"; + } + out << "\n"; + return out.str(); +} + void SGMetar::useCurrentDate() { struct tm now; diff --git a/simgear/environment/metar.hxx b/simgear/environment/metar.hxx index 2f2e5d02..3b08602b 100644 --- a/simgear/environment/metar.hxx +++ b/simgear/environment/metar.hxx @@ -235,6 +235,11 @@ public: inline const std::vector& getWeather() const { return _weather; } inline const std::vector getWeather2() const { return _weather2; } + /* Returns human-readable description. If tabtops is 0, we use tab + characters, otherwise we use spaces to pad to multiple of . + */ + std::string getDescription(int tabstops) const; + protected: std::string _url; int _grpcount; From d1eb768de6bca8cecdc4a506ae90b4e0cefcabf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Sun, 3 Nov 2019 20:53:55 +0100 Subject: [PATCH 27/52] Disabled CASTSHADOW_BIT on most of the scene graph geometry to optimize performance --- simgear/scene/sky/newcloud.cxx | 2 +- simgear/scene/tgdb/SGOceanTile.cxx | 2 +- simgear/scene/tgdb/SGTileDetailsCallback.hxx | 2 +- simgear/scene/tgdb/obj.cxx | 2 +- simgear/scene/viewer/CompositorPass.cxx | 33 ++++++++++++++++---- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index 6791b370..8e9eb714 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -256,7 +256,7 @@ osg::ref_ptr SGNewCloud::genCloud() { geode->addDrawable(sg); geode->setName("3D cloud"); geode->setEffect(effect.get()); - geode->setNodeMask( ~simgear::MODELLIGHT_BIT ); + geode->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) ); return geode; } diff --git a/simgear/scene/tgdb/SGOceanTile.cxx b/simgear/scene/tgdb/SGOceanTile.cxx index f4636159..29839b29 100644 --- a/simgear/scene/tgdb/SGOceanTile.cxx +++ b/simgear/scene/tgdb/SGOceanTile.cxx @@ -338,7 +338,7 @@ osg::Node* SGOceanTile(const SGBucket& b, SGMaterialLib *matlib, int latPoints, transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))* osg::Matrix::translate(toOsg(cartCenter))); transform->addChild(geode); - transform->setNodeMask( ~simgear::MODELLIGHT_BIT ); + transform->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) ); return transform; } diff --git a/simgear/scene/tgdb/SGTileDetailsCallback.hxx b/simgear/scene/tgdb/SGTileDetailsCallback.hxx index 6e22c8cd..58792a68 100644 --- a/simgear/scene/tgdb/SGTileDetailsCallback.hxx +++ b/simgear/scene/tgdb/SGTileDetailsCallback.hxx @@ -1099,7 +1099,7 @@ public: if (forestNode.valid()) objectLOD->addChild(forestNode.get(), 0, 2.0 * object_range + SG_TILE_RADIUS); if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 2.0 * object_range + SG_TILE_RADIUS); - unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT; + unsigned nodeMask = SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT; objectLOD->setNodeMask(nodeMask); } diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index 2efe814e..5d3461f5 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -159,6 +159,6 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options transform->addChild(pagedLOD); } - transform->setNodeMask( ~simgear::MODELLIGHT_BIT ); + transform->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) ); return transform; } diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index d2d7a6db..2a67a6d4 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -36,6 +36,25 @@ #include "Compositor.hxx" #include "CompositorUtil.hxx" +namespace { +osgUtil::RenderBin::RenderBinList +removeTransparentBins(simgear::EffectCullVisitor *cv) +{ + osgUtil::RenderBin::RenderBinList transparent_bins; + osgUtil::RenderStage *stage = cv->getRenderStage(); + osgUtil::RenderBin::RenderBinList &rbl = stage->getRenderBinList(); + for (auto rbi = rbl.begin(); rbi != rbl.end(); ) { + if (rbi->second->getSortMode() == osgUtil::RenderBin::SORT_BACK_TO_FRONT) { + transparent_bins.insert(std::make_pair(rbi->first, rbi->second)); + rbl.erase(rbi++); + } else { + ++rbi; + } + } + return transparent_bins; +} +} // anonymous namespace + namespace simgear { namespace compositor { @@ -384,9 +403,12 @@ public: virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) { osg::Camera *camera = static_cast(node); + EffectCullVisitor *cv = dynamic_cast(nv); traverse(node, nv); + removeTransparentBins(cv); + // The light matrix uniform is updated after the traverse in case the // OSG near/far plane calculations were enabled osg::Matrixf light_matrix = @@ -497,19 +519,18 @@ public: osg::Vec4 aim4 = osg::Vec4(bs.center(), 1.0) * view_inverse; osg::Vec3 aim(aim4.x(), aim4.y(), aim4.z()); - osg::Vec3 up(0.0f, 1.0f, 0.0f); osg::Matrixd &light_view_matrix = camera->getViewMatrix(); light_view_matrix.makeLookAt( - aim + (light_dir * bs.radius() * 2.0f), + aim + light_dir * (bs.radius() + 10.0f), aim, - aim); + osg::Vec3(0.0f, 0.0f, 1.0f)); osg::Matrixd &light_proj_matrix = camera->getProjectionMatrix(); light_proj_matrix.makeOrtho( -bs.radius(), bs.radius(), -bs.radius(), bs.radius(), - -bs.radius() * 6.0f, bs.radius() * 6.0f); + 1.0, bs.radius() * 10.0); // Do texel snapping to prevent flickering or shimmering. // We are using double precision vectors and matrices because in FG @@ -520,8 +541,8 @@ public: osg::Vec2d shadow_origin(shadow_origin4.x(), shadow_origin4.y()); shadow_origin = osg::Vec2d(shadow_origin.x() * _half_sm_size.x(), shadow_origin.y() * _half_sm_size.y()); - osg::Vec2d rounded_origin(std::round(shadow_origin.x()), - std::round(shadow_origin.y())); + osg::Vec2d rounded_origin(std::floor(shadow_origin.x()), + std::floor(shadow_origin.y())); osg::Vec2d rounding = rounded_origin - shadow_origin; rounding = osg::Vec2d(rounding.x() / _half_sm_size.x(), rounding.y() / _half_sm_size.y()); From 99d5b0dc8ab64da7ceda9ae021a57b7f4e037cf2 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Mon, 4 Nov 2019 14:54:06 +0100 Subject: [PATCH 28/52] Switch to in-place declarations and to the ISO9613 distance attenuation model for AeonWave which also calcualtes the proper sound velocity based on atmospheric properties --- simgear/sound/sample.cxx | 48 +-------------- simgear/sound/sample.hxx | 96 ++++++++++++++++++----------- simgear/sound/sample_group.cxx | 23 ++----- simgear/sound/sample_group.hxx | 38 ++++++++---- simgear/sound/soundmgr.hxx | 4 +- simgear/sound/soundmgr_aeonwave.cxx | 14 ++--- 6 files changed, 101 insertions(+), 122 deletions(-) diff --git a/simgear/sound/sample.cxx b/simgear/sound/sample.cxx index 89c26056..e3db3445 100644 --- a/simgear/sound/sample.cxx +++ b/simgear/sound/sample.cxx @@ -4,7 +4,7 @@ // Modified to match the new SoundSystem by Erik Hofman, October 2009 // // Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt -// Copyright (C) 2009 Erik Hofman +// Copyright (C) 2009-2019 Erik Hofman // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -49,24 +49,6 @@ using std::string; // empty constructor SGSoundSampleInfo::SGSoundSampleInfo() : _refname(random_string()), - _bits(16), - _tracks(1), - _samples(0), - _frequency(22500), - _compressed(false), - _loop(false), - _static_changed(true), - _playing(false), - _pitch(1.0f), - _volume(1.0f), - _master_volume(1.0f), - _use_pos_props(false), - _out_of_range(false), - _inner_angle(360.0f), - _outer_angle(360.0f), - _outer_gain(0.0f), - _reference_dist(500.0f), - _max_dist(3000.0f), _absolute_pos(SGVec3d::zeros()), _relative_pos(SGVec3d::zeros()), _direction(SGVec3d::zeros()), @@ -97,27 +79,9 @@ std::string SGSoundSampleInfo::random_string() // SGSoundSample // -// empty constructor -SGSoundSample::SGSoundSample() : - _is_file(false), - _changed(true), - _valid_source(false), - _source(SGSoundMgr::NO_SOURCE), - _data(NULL), - _valid_buffer(false), - _buffer(SGSoundMgr::NO_BUFFER) -{ -} - // constructor SGSoundSample::SGSoundSample(const char *file, const SGPath& currentDir) : - _is_file(true), - _changed(true), - _valid_source(false), - _source(SGSoundMgr::NO_SOURCE), - _data(NULL), - _valid_buffer(false), - _buffer(SGSoundMgr::NO_BUFFER) + _is_file(true) { SGPath p = simgear::ResourceManager::instance()->findPath(file, currentDir); _refname = p.utf8Str(); @@ -125,13 +89,7 @@ SGSoundSample::SGSoundSample(const char *file, const SGPath& currentDir) : // constructor SGSoundSample::SGSoundSample( const unsigned char** data, - int len, int freq, int format ) : - _is_file(false), - _changed(true), - _valid_source(false), - _source(SGSoundMgr::NO_SOURCE), - _valid_buffer(false), - _buffer(SGSoundMgr::NO_BUFFER) + int len, int freq, int format ) { SG_LOG( SG_SOUND, SG_DEBUG, "In memory sounds sample" ); _data = (unsigned char*)*data; *data = NULL; diff --git a/simgear/sound/sample.hxx b/simgear/sound/sample.hxx index 4329a273..366e4cad 100644 --- a/simgear/sound/sample.hxx +++ b/simgear/sound/sample.hxx @@ -1,10 +1,10 @@ // sample.hxx -- Audio sample encapsulation class -// +// // Written by Curtis Olson, started April 2004. // Modified to match the new SoundSystem by Erik Hofman, October 2009 // // Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt -// Copyright (C) 2009 Erik Hofman +// Copyright (C) 2009-2019 Erik Hofman // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -32,7 +32,8 @@ #include #include - +#include "soundmgr.hxx" + enum { SG_SAMPLE_MONO = 1, SG_SAMPLE_STEREO = 2, @@ -76,7 +77,7 @@ public: * Returns the block alignment of this audio sample. * @return block alignment in bytes */ - inline unsigned int get_block_align() { + inline unsigned int get_block_align() { return _block_align; } @@ -164,10 +165,22 @@ public: /** * Get maximum distance (in meters) of this sound. * This is the distance where this sound is no longer audible. - * @return dist Maximum distance + * @return Maximum distance */ inline float get_max_dist() { return _max_dist; } + /** + * Get the temperature (in degrees Celsius) at the current altitude. + * @return temperature in degrees Celsius + */ + inline float get_temperature() { return _degC; } + + /** + * Get the relative humidity at the current altitude. + * @return Percent relative humidity (0.0 to 1.0) + */ + inline float get_humidity() { return _humidity; } + /** * Test if static data of audio sample configuration has changed. * Calling this function will reset the flag so calling it a second @@ -181,32 +194,34 @@ public: protected: // static sound emitter info std::string _refname; - unsigned int _bits; - unsigned int _tracks; - unsigned int _samples; - unsigned int _frequency; - unsigned int _block_align; - bool _compressed; - bool _loop; + unsigned int _bits = 16; + unsigned int _tracks = 1; + unsigned int _samples = 0; + unsigned int _frequency = 22500; + unsigned int _block_align = 2; + bool _compressed = false; + bool _loop = false; // dynamic sound emitter info (non 3d) - bool _static_changed; - bool _playing; + bool _static_changed = true; + bool _playing = false; - float _pitch; - float _volume; - float _master_volume; + float _pitch = 1.0f; + float _volume = 1.0f; + float _master_volume = 1.0f; // dynamic sound emitter info (3d) - bool _use_pos_props; - bool _out_of_range; + bool _use_pos_props = false; + bool _out_of_range = false; - float _inner_angle; - float _outer_angle; - float _outer_gain; + float _inner_angle = 360.0f; + float _outer_angle = 360.0f; + float _outer_gain = 0.0f; - float _reference_dist; - float _max_dist; + float _reference_dist = 500.0f; + float _max_dist = 3000.0f; + float _humidity = 0.5f; + float _degC = 20.0f; SGPropertyNode_ptr _pos_prop[3]; SGVec3d _absolute_pos; // absolute position @@ -233,7 +248,7 @@ public: * Empty constructor, can be used to read data to the systems * memory and not to the driver. */ - SGSoundSample(); + SGSoundSample() {}; /** * Constructor @@ -307,7 +322,7 @@ public: */ inline void play_once() { play(false); } - /** + /** * Schedule this audio sample to play looped. * @see #play */ @@ -389,7 +404,7 @@ public: */ inline void set_buffer(unsigned int bid) { _buffer = bid; _valid_buffer = true; _changed = true; - } + } /** * Get the buffer-id of this source @@ -409,7 +424,7 @@ public: inline void no_valid_buffer() { _valid_buffer = false; } /** - * Set the playback pitch of this audio sample. + * Set the playback pitch of this audio sample. * Should be between 0.0 and 2.0 for maximum compatibility. * @param p Pitch */ @@ -540,6 +555,17 @@ public: _velocity = vel; _changed = true; } + /** + * Set both the temperature and relative humidity at the current altitude. + * @param temp Temperature in degrees Celsius + * @param hum Percent relative humidity (0.0 to 1.0) + */ + inline void set_atmosphere(float temp, float hum) { + if (fabsf(_degC - temp) > 1.0f || fabsf(_humidity - hum) > 0.1f) { + _degC = temp, _humidity = hum; _static_changed = true; + } + } + /** * Set reference distance (in meters) of this sound. * This is the distance where the gain will be half. @@ -563,19 +589,19 @@ public: void update_pos_and_orientation(); protected: - bool _is_file; - bool _changed; + bool _is_file = false; + bool _changed = true; // Sources are points emitting sound. - bool _valid_source; - unsigned int _source; + bool _valid_source = false; + unsigned int _source = SGSoundMgr::NO_SOURCE; private: - unsigned char* _data; + unsigned char* _data = NULL; // Buffers hold sound data. - bool _valid_buffer; - unsigned int _buffer; + bool _valid_buffer = false; + unsigned int _buffer = SGSoundMgr::NO_BUFFER; }; #endif // _SG_SAMPLE_HXX diff --git a/simgear/sound/sample_group.cxx b/simgear/sound/sample_group.cxx index 12bbd3ba..f25699d4 100644 --- a/simgear/sound/sample_group.cxx +++ b/simgear/sound/sample_group.cxx @@ -2,7 +2,7 @@ // // Written for the new SoundSystem by Erik Hofman, October 2009 // -// Copyright (C) 2009 Erik Hofman +// Copyright (C) 2009-2019 Erik Hofman // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -31,16 +31,7 @@ #include "soundmgr.hxx" #include "sample_group.hxx" -SGSampleGroup::SGSampleGroup () : - _smgr(NULL), - _refname(""), - _active(false), - _changed(false), - _pause(false), - _volume(1.0), - _tied_to_listener(false), - _velocity(SGVec3d::zeros()), - _orientation(SGQuatd::zeros()) +SGSampleGroup::SGSampleGroup () { _samples.clear(); } @@ -48,14 +39,7 @@ SGSampleGroup::SGSampleGroup () : SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr, const std::string &refname ): _smgr(smgr), - _refname(refname), - _active(false), - _changed(false), - _pause(false), - _volume(1.0), - _tied_to_listener(false), - _velocity(SGVec3d::zeros()), - _orientation(SGQuatd::zeros()) + _refname(refname) { _smgr->add(this, refname); _samples.clear(); @@ -318,6 +302,7 @@ void SGSampleGroup::update_pos_and_orientation() { sample->set_rotation( ec2body ); sample->set_position(base_position); sample->set_velocity( velocity ); + sample->set_atmosphere( _degC, _humidity ); // Test if a sample is farther away than max distance, if so // stop the sound playback and free it's source. diff --git a/simgear/sound/sample_group.hxx b/simgear/sound/sample_group.hxx index a40dee47..07aabd36 100644 --- a/simgear/sound/sample_group.hxx +++ b/simgear/sound/sample_group.hxx @@ -6,7 +6,7 @@ // // Written for the new SoundSystem by Erik Hofman, October 2009 // -// Copyright (C) 2009 Erik Hofman +// Copyright (C) 2009-2019 Erik Hofman // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public @@ -131,7 +131,7 @@ public: * @return true if the audio sample exsists and is scheduled for playing */ bool play( const std::string& refname, bool looping = false ); - + /** * Request to start playing the referred audio sample looping. * @param refname Reference name of the audio sample to start playing @@ -173,7 +173,7 @@ public: /** * Set the velocity vector of this sample group. * This is in the local frame coordinate system; x=north, y=east, z=down - * @param vel Velocity vector + * @param vel Velocity vector */ void set_velocity( const SGVec3d& vel ) { _velocity = vel; _changed = true; @@ -196,29 +196,41 @@ public: _orientation = ori; _changed = true; } + /** + * Set both the temperature and relative humidity at the current altitude. + * @param temp Temperature in degrees Celsius + * @param hum Percent relative humidity (0.0 to 1.0) + */ + void set_atmosphere(float temp, float hum ) { + _degC = temp, _humidity = hum; _changed = true; + } + /** * Tie this sample group to the listener position, orientation and velocity */ void tie_to_listener() { _tied_to_listener = true; } protected: - SGSoundMgr *_smgr; - std::string _refname; - bool _active; + SGSoundMgr *_smgr = NULL; + std::string _refname = ""; + bool _active = false; private: void cleanup_removed_samples(); void start_playing_sample(SGSoundSample *sample); void check_playing_sample(SGSoundSample *sample); - - bool _changed; - bool _pause; - float _volume; - bool _tied_to_listener; - SGVec3d _velocity; + bool _changed = false; + bool _pause = false; + float _volume = 1.0f; + float _humidity = 0.5f; + float _degC = 20.0f; + + bool _tied_to_listener = false; + + SGVec3d _velocity = SGVec3d::zeros(); + SGQuatd _orientation = SGQuatd::zeros(); SGGeod _base_pos; - SGQuatd _orientation; sample_map _samples; std::vector< SGSharedPtr > _removed_samples; diff --git a/simgear/sound/soundmgr.hxx b/simgear/sound/soundmgr.hxx index 6837a35f..a3360dfa 100644 --- a/simgear/sound/soundmgr.hxx +++ b/simgear/sound/soundmgr.hxx @@ -11,7 +11,7 @@ // Modified for the new SoundSystem by Erik Hofman, October 2009 // // Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt -// Copyright (C) 2009 Erik Hofman +// Copyright (C) 2009-2019 Erik Hofman // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public @@ -68,7 +68,7 @@ public: * Select a specific sound device. * Requires a init/reinit call before sound is actually switched. */ - inline void select_device(const char* devname) {_device_name = devname;} + inline void select_device(const char* devname) { _device_name = devname; } /** * Test is the sound manager is in a working condition. diff --git a/simgear/sound/soundmgr_aeonwave.cxx b/simgear/sound/soundmgr_aeonwave.cxx index 111fa10e..7a5330de 100644 --- a/simgear/sound/soundmgr_aeonwave.cxx +++ b/simgear/sound/soundmgr_aeonwave.cxx @@ -191,7 +191,7 @@ void SGSoundMgr::init() TRY( d->_aax.set(dsp) ); dsp = aax::dsp(d->_aax, AAX_DISTANCE_FILTER); - TRY( dsp.set(AAX_AL_INVERSE_DISTANCE_CLAMPED) ); + TRY( dsp.set(AAX_ISO9613_DISTANCE) ); TRY( d->_aax.set(dsp) ); dsp = aax::dsp(d->_aax, AAX_VELOCITY_EFFECT); @@ -313,12 +313,6 @@ void SGSoundMgr::update( double dt ) TRY( dsp.set(AAX_GAIN, _volume) ); TRY( d->_aax.set(dsp) ); -#if 0 - // TODO: altitude dependent - dsp = d->_aax.get(AAX_VELOCITY_EFFECT); - TRY( dsp.set(AAX_SOUND_VELOCITY, 340.3f) ); - TRY( d->_aax.set(dsp) ); -#endif aax::Matrix64 mtx = d->_mtx; mtx.inverse(); TRY( d->_aax.sensor_matrix(mtx) ); @@ -568,7 +562,7 @@ void SGSoundMgr::sample_play( SGSoundSample *sample ) aax::dsp dsp = emitter.get(AAX_DISTANCE_FILTER); TRY( dsp.set(AAX_ROLLOFF_FACTOR, 0.3f) ); - TRY( dsp.set(AAX_AL_INVERSE_DISTANCE_CLAMPED) ); + TRY( dsp.set(AAX_ISO9613_DISTANCE) ); TRY( emitter.set(dsp) ); TRY( emitter.set(AAX_LOOPING, sample->is_looping()) ); @@ -666,6 +660,9 @@ void SGSoundMgr::update_sample_config( SGSoundSample *sample, SGVec3d& position, dsp = emitter.get(AAX_DISTANCE_FILTER); TRY( dsp.set(AAX_REF_DISTANCE, sample->get_reference_dist()) ); TRY( dsp.set(AAX_MAX_DISTANCE, sample->get_max_dist()) ); + TRY( dsp.set(AAX_RELATIVE_HUMIDITY, sample->get_humidity()) ); + TRY( dsp.set(AAX_TEMPERATURE, sample->get_temperature(), + AAX_DEGREES_CELSIUS) ); TRY( emitter.set(dsp) ); } } @@ -693,6 +690,7 @@ vector SGSoundMgr::get_available_devices() } } } + testForError("get_available_devices"); #endif return devices; } From 82b1cca247f3ed48834de5a789e2455744432c84 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Mon, 4 Nov 2019 15:18:59 +0100 Subject: [PATCH 29/52] Also add pressure to the mix --- simgear/sound/sample.hxx | 20 +++++++++++++++----- simgear/sound/sample_group.cxx | 2 +- simgear/sound/sample_group.hxx | 10 ++++++---- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/simgear/sound/sample.hxx b/simgear/sound/sample.hxx index 366e4cad..a67524d7 100644 --- a/simgear/sound/sample.hxx +++ b/simgear/sound/sample.hxx @@ -181,6 +181,12 @@ public: */ inline float get_humidity() { return _humidity; } + /** + * Get the pressure at the current altitude. + * @return Pressure in kPa + */ + inline float get_pressure() { return _pressure; } + /** * Test if static data of audio sample configuration has changed. * Calling this function will reset the flag so calling it a second @@ -220,6 +226,7 @@ protected: float _reference_dist = 500.0f; float _max_dist = 3000.0f; + float _pressure = 101.325f; float _humidity = 0.5f; float _degC = 20.0f; @@ -557,12 +564,15 @@ public: /** * Set both the temperature and relative humidity at the current altitude. - * @param temp Temperature in degrees Celsius - * @param hum Percent relative humidity (0.0 to 1.0) + * @param t Temperature in degrees Celsius + * @param h Percent relative humidity (0.0 to 1.0) + * @param p Pressure in kPa; */ - inline void set_atmosphere(float temp, float hum) { - if (fabsf(_degC - temp) > 1.0f || fabsf(_humidity - hum) > 0.1f) { - _degC = temp, _humidity = hum; _static_changed = true; + inline void set_atmosphere(float t, float h, float p) { + if (fabsf(_degC - t) > 1.0f || fabsf(_humidity - h) > 0.1f || + fabsf(_pressure - p) > 1.0f) + { + _degC = t, _humidity = h; _pressure = p; _static_changed = true; } } diff --git a/simgear/sound/sample_group.cxx b/simgear/sound/sample_group.cxx index f25699d4..d303dee5 100644 --- a/simgear/sound/sample_group.cxx +++ b/simgear/sound/sample_group.cxx @@ -302,7 +302,7 @@ void SGSampleGroup::update_pos_and_orientation() { sample->set_rotation( ec2body ); sample->set_position(base_position); sample->set_velocity( velocity ); - sample->set_atmosphere( _degC, _humidity ); + sample->set_atmosphere( _degC, _humidity, _pressure ); // Test if a sample is farther away than max distance, if so // stop the sound playback and free it's source. diff --git a/simgear/sound/sample_group.hxx b/simgear/sound/sample_group.hxx index 07aabd36..be63224d 100644 --- a/simgear/sound/sample_group.hxx +++ b/simgear/sound/sample_group.hxx @@ -198,11 +198,12 @@ public: /** * Set both the temperature and relative humidity at the current altitude. - * @param temp Temperature in degrees Celsius - * @param hum Percent relative humidity (0.0 to 1.0) + * @param t Temperature in degrees Celsius + * @param h Percent relative humidity (0.0 to 1.0) + * @param p Pressure in kPa */ - void set_atmosphere(float temp, float hum ) { - _degC = temp, _humidity = hum; _changed = true; + void set_atmosphere(float t, float h, float p ) { + _degC = t, _humidity = h; _pressure = p; _changed = true; } /** @@ -223,6 +224,7 @@ private: bool _changed = false; bool _pause = false; float _volume = 1.0f; + float _pressure = 101.325f; float _humidity = 0.5f; float _degC = 20.0f; From 0c8949e723ee3aa200c9ac105e7924cc246715b7 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Tue, 5 Nov 2019 18:40:24 +0000 Subject: [PATCH 30/52] simgear/environment/metar.cxx:SGMetar::getDescription(): minor improvements to returned text. remove special characters from output. We used to include 0xb0 characters - small 'o' degree symbol, but this isn't reliable and looks bad in flightgear's weather window. Consistently use space between number and its unit. remove 'METAR Report' header. --- simgear/environment/metar.cxx | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/simgear/environment/metar.cxx b/simgear/environment/metar.cxx index cc865149..898f76d4 100644 --- a/simgear/environment/metar.cxx +++ b/simgear/environment/metar.cxx @@ -270,20 +270,15 @@ std::string SGMetar::getDescription(int tabstops) const int i, lineno; Tab tab(tabstops); - out << "METAR Report"; - if ((i = getReportType()) == SGMetar::AUTO) - out << tab << tab << "(automatically generated)"; + out << "(METAR automatically generated)\n"; else if (i == SGMetar::COR) - out << tab << tab << "(manually corrected)"; + out << "(METAR manually corrected)\n"; else if (i == SGMetar::RTD) - out << tab << tab << "(routine delayed)"; + out << "(METAR routine delayed)\n"; - out << "\n"; - out << "============" << "\n"; out << "Airport-Id:" << tab << tab << getId() << "\n"; - // date/time int year = getYear(); int month = getMonth(); @@ -331,7 +326,7 @@ std::string SGMetar::getDescription(int tabstops) const if ((i = getWindDir()) == -1) out << "from variable directions"; else - out << "from the " << azimuthName(i) << " (" << i << "°)"; + out << "from the " << azimuthName(i) << " (" << i << " deg)"; out << " at " << rnd(d, -1) << " km/h"; out << tab << tab << rnd(getWindSpeed_kt(), -1) << " kt"; @@ -353,20 +348,20 @@ std::string SGMetar::getDescription(int tabstops) const if (from != to) { out << tab << tab << tab << "variable from " << azimuthName(from); out << " to " << azimuthName(to); - out << " (" << from << "°--" << to << "°)" << "\n"; + out << " (" << from << "deg --" << to << " deg)" << "\n"; } } // temperature/humidity/air pressure if ((d = getTemperature_C()) != NaN) { - out << "Temperature:" << tab << tab << d << "°C" << tab << tab << tab << tab << tab; - out << rnd(getTemperature_F(), -1) << "°F" << "\n"; + out << "Temperature:" << tab << tab << d << " C" << tab << tab << tab << tab << tab; + out << rnd(getTemperature_F(), -1) << " F" << "\n"; if ((d = getDewpoint_C()) != NaN) { - out << "Dewpoint:" << tab << tab << d << "°C" << tab << tab << tab << tab << tab; - out << rnd(getDewpoint_F(), -1) << "°F" << "\n"; - out << "Rel. Humidity: " << tab << tab << rnd(getRelHumidity()) << "%" << "\n"; + out << "Dewpoint:" << tab << tab << d << " C" << tab << tab << tab << tab << tab; + out << rnd(getDewpoint_F(), -1) << " F" << "\n"; + out << "Rel. Humidity: " << tab << tab << rnd(getRelHumidity()) << " %" << "\n"; } } if ((d = getPressure_hPa()) != NaN) { From 1a4568175c68f9ea4b8d5b2272832ffbedff1126 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 6 Nov 2019 19:07:50 +0000 Subject: [PATCH 31/52] simgear/environment/metar.*: getDescription() can now also use single spaces. If param is -1, all sequences of tabs are replaced by a single space. --- simgear/environment/metar.cxx | 8 ++++++++ simgear/environment/metar.hxx | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/simgear/environment/metar.cxx b/simgear/environment/metar.cxx index 898f76d4..9d9c944f 100644 --- a/simgear/environment/metar.cxx +++ b/simgear/environment/metar.cxx @@ -203,6 +203,14 @@ std::ostream& operator << (std::ostream& out, const Tab& t) std::ostringstream& out2 = *(std::ostringstream*) &out; std::string s = out2.str(); + + if (t._stops < 0) { + if (!s.size() || s[s.size()-1] != ' ') { + out << ' '; + } + return out; + } + auto nl = s.rfind('\n'); if (nl < 0) nl = 0; int column = 0; diff --git a/simgear/environment/metar.hxx b/simgear/environment/metar.hxx index 3b08602b..a92bf1a5 100644 --- a/simgear/environment/metar.hxx +++ b/simgear/environment/metar.hxx @@ -236,8 +236,8 @@ public: inline const std::vector getWeather2() const { return _weather2; } /* Returns human-readable description. If tabtops is 0, we use tab - characters, otherwise we use spaces to pad to multiple of . - */ + characters. If +ve we use spaces to pad to multiple of . If + -1 all sequences of tabs are represented by a single space. */ std::string getDescription(int tabstops) const; protected: From 2b2ada20372828135b8fa4d39833609a6b306b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Wed, 6 Nov 2019 01:35:02 +0100 Subject: [PATCH 32/52] Compositor: Added support for spotlights in clustered shading --- simgear/scene/model/SGLight.cxx | 61 ++++++----- simgear/scene/viewer/ClusteredShading.cxx | 125 ++++++++++++++++++---- simgear/scene/viewer/ClusteredShading.hxx | 14 ++- 3 files changed, 151 insertions(+), 49 deletions(-) diff --git a/simgear/scene/model/SGLight.cxx b/simgear/scene/model/SGLight.cxx index ab332750..269829e1 100644 --- a/simgear/scene/model/SGLight.cxx +++ b/simgear/scene/model/SGLight.cxx @@ -73,32 +73,39 @@ SGLight::appendLight(const SGPropertyNode *configNode, light->setSpotExponent(configNode->getFloatValue("spot-exponent")); light->setSpotCutoff(configNode->getFloatValue("spot-cutoff")); - osg::Group *group = 0; - if ((p = configNode->getNode("offsets")) == NULL) { - group = new osg::Group; - } else { - // Set up the alignment node ("stolen" from animation.cxx) - // XXX Order of rotations is probably not correct. - osg::MatrixTransform *align = new osg::MatrixTransform; - osg::Matrix res_matrix; - res_matrix.makeRotate( - p->getFloatValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(0, 1, 0), - p->getFloatValue("roll-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(1, 0, 0), - p->getFloatValue("heading-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(0, 0, 1)); + osg::MatrixTransform *align = new osg::MatrixTransform; + align->addChild(light); - osg::Matrix tmat; - tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0), - configNode->getFloatValue("offsets/y-m", 0.0), - configNode->getFloatValue("offsets/z-m", 0.0)); + osg::Matrix t; + osg::Vec3 pos(configNode->getFloatValue("position/x-m"), + configNode->getFloatValue("position/y-m"), + configNode->getFloatValue("position/z-m")); + t.makeTranslate(pos); - align->setMatrix(res_matrix * tmat); - group = align; + osg::Matrix r; + if (const SGPropertyNode *dirNode = configNode->getNode("direction")) { + if (dirNode->hasValue("pitch-deg")) { + r.makeRotate( + dirNode->getFloatValue("pitch-deg")*SG_DEGREES_TO_RADIANS, + osg::Vec3(0, 1, 0), + dirNode->getFloatValue("roll-deg")*SG_DEGREES_TO_RADIANS, + osg::Vec3(1, 0, 0), + dirNode->getFloatValue("heading-deg")*SG_DEGREES_TO_RADIANS, + osg::Vec3(0, 0, 1)); + } else if (dirNode->hasValue("lookat-x-m")) { + osg::Vec3 lookAt(dirNode->getFloatValue("lookat-x-m"), + dirNode->getFloatValue("lookat-y-m"), + dirNode->getFloatValue("lookat-z-m")); + osg::Vec3 dir = lookAt - pos; + r.makeRotate(osg::Vec3(0, 0, -1), dir); + } else { + r.makeRotate(osg::Vec3(0, 0, -1), + osg::Vec3(dirNode->getFloatValue("x"), + dirNode->getFloatValue("y"), + dirNode->getFloatValue("z"))); + } } - - group->addChild(light); + align->setMatrix(r * t); osg::Shape *debug_shape; if (light->getType() == SGLight::Type::POINT) { @@ -125,14 +132,14 @@ SGLight::appendLight(const SGPropertyNode *configNode, debug_switch->addChild(debug_geode); simgear::getPropertyRoot()->getNode("/sim/debug/show-light-volumes", true)-> addChangeListener(new SGLightDebugListener(debug_switch), true); - group->addChild(debug_switch); + align->addChild(debug_switch); if ((p = configNode->getNode("name")) != NULL) - group->setName(p->getStringValue()); + align->setName(p->getStringValue()); else - group->setName("light"); + align->setName("light"); - return group; + return align; } SGLight::SGLight() : diff --git a/simgear/scene/viewer/ClusteredShading.cxx b/simgear/scene/viewer/ClusteredShading.cxx index e42883a2..ba521a4d 100644 --- a/simgear/scene/viewer/ClusteredShading.cxx +++ b/simgear/scene/viewer/ClusteredShading.cxx @@ -27,6 +27,7 @@ #include +#include #include namespace simgear { @@ -40,7 +41,7 @@ const int MAX_SPOTLIGHTS = 256; // It must be a multiple of the size of a vec4 as per the std140 layout rules. // See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt const int POINTLIGHT_BLOCK_SIZE = 20; -const int SPOTLIGHT_BLOCK_SIZE = 8; +const int SPOTLIGHT_BLOCK_SIZE = 28; ClusteredShading::ClusteredShading(osg::Camera *camera, const SGPropertyNode *config) : @@ -180,12 +181,38 @@ ClusteredShading::update(const SGLightList &light_list) PointlightBound point; point.light = light; point.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) * - osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * - _camera->getViewMatrix(); + // The parenthesis are very important: if the vector is + // multiplied by the local to world matrix first we'll have + // precision issues + (osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * + _camera->getViewMatrix()); point.range = light->getRange(); + _point_bounds.push_back(point); } else if (light->getType() == SGLight::Type::SPOT) { + SpotlightBound spot; + spot.light = light; + spot.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) * + (osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * + _camera->getViewMatrix()); + spot.direction = osg::Vec4f(0.0f, 0.0f, -1.0f, 0.0f) * + (osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * + _camera->getViewMatrix()); + float range = light->getRange(); + float angle = light->getSpotCutoff() * SG_DEGREES_TO_RADIANS; + spot.cos_cutoff = cos(angle); + if(angle > SGD_PI_4) { + spot.bounding_sphere.radius = range * tan(angle); + } else { + spot.bounding_sphere.radius = + range * 0.5f / pow(spot.cos_cutoff, 2.0f); + } + + spot.bounding_sphere.center = + spot.position + spot.direction * spot.bounding_sphere.radius; + + _spot_bounds.push_back(spot); } } if (_point_bounds.size() > MAX_POINTLIGHTS || @@ -265,8 +292,9 @@ ClusteredShading::update(const SGLightList &light_list) _light_grid->dirty(); _light_indices->dirty(); - // Upload pointlight data + // Upload pointlight and spotlight data writePointlightData(); + writeSpotlightData(); } void @@ -301,6 +329,7 @@ ClusteredShading::assignLightsToSlice(int slice) GLuint local_point_count = 0; GLuint local_spot_count = 0; + // Test point lights for (GLushort point_iterator = 0; point_iterator < _point_bounds.size(); ++point_iterator) { @@ -328,25 +357,40 @@ ClusteredShading::assignLightsToSlice(int slice) } } + // Test spot lights + for (GLushort spot_iterator = 0; + spot_iterator < _spot_bounds.size(); + ++spot_iterator) { + SpotlightBound spot = _spot_bounds[spot_iterator]; + + // Perform frustum-sphere collision tests + float distance = 0.0f; + for (int j = 0; j < 6; j++) { + distance = subfrustum.plane[j] * spot.bounding_sphere.center + + spot.bounding_sphere.radius; + if (distance <= 0.0f) + break; + } + + if (distance > 0.0f) { + // Update light index list + indices[_global_light_count] = spot_iterator; + ++local_spot_count; + ++_global_light_count; // Atomic increment + } + + if (_global_light_count >= MAX_LIGHT_INDICES) { + throw sg_range_exception( + "Clustered shading light index count is over the hardcoded limit (" + + std::to_string(MAX_LIGHT_INDICES) + ")"); + } + } + // Update light grid grid[(z_offset + i) * 3 + 0] = start_offset; grid[(z_offset + i) * 3 + 1] = local_point_count; grid[(z_offset + i) * 3 + 2] = local_spot_count; } - - // for (int y = 0; y < _n_vtiles; ++y) { - // for (int x = 0; x < _n_htiles; ++x) { - // std::cout << grid[(y * _n_htiles + x) * 3 + 0] << "," - // << grid[(y * _n_htiles + x) * 3 + 1] << " "; - // } - // std::cout << std::endl; - // } - // std::cout << "\n\n"; - - // for (int i = 0; i < n_vtiles * n_htiles; ++i) { - // std::cout << indices[i] << " "; - // } - // std::cout << "\n"; } void @@ -385,6 +429,51 @@ ClusteredShading::writePointlightData() _pointlight_data->dirty(); } +void +ClusteredShading::writeSpotlightData() +{ + GLfloat *data = reinterpret_cast(&(*_spotlight_data)[0]); + + for (const auto &spot : _spot_bounds) { + // vec4 position + *data++ = spot.position.x(); + *data++ = spot.position.y(); + *data++ = spot.position.z(); + *data++ = 1.0f; + // vec4 direction + *data++ = spot.direction.x(); + *data++ = spot.direction.y(); + *data++ = spot.direction.z(); + *data++ = 0.0f; + // vec4 ambient + *data++ = spot.light->getAmbient().x(); + *data++ = spot.light->getAmbient().y(); + *data++ = spot.light->getAmbient().z(); + *data++ = spot.light->getAmbient().w(); + // vec4 diffuse + *data++ = spot.light->getDiffuse().x(); + *data++ = spot.light->getDiffuse().y(); + *data++ = spot.light->getDiffuse().z(); + *data++ = spot.light->getDiffuse().w(); + // vec4 specular + *data++ = spot.light->getSpecular().x(); + *data++ = spot.light->getSpecular().y(); + *data++ = spot.light->getSpecular().z(); + *data++ = spot.light->getSpecular().w(); + // vec4 attenuation (x = constant, y = linear, z = quadratic, w = range) + *data++ = spot.light->getConstantAttenuation(); + *data++ = spot.light->getLinearAttenuation(); + *data++ = spot.light->getQuadraticAttenuation(); + *data++ = spot.light->getRange(); + // float cos_cutoff + *data++ = spot.cos_cutoff; + // float exponent + *data++ = spot.light->getSpotExponent(); + // Needs 2N padding (8 bytes) + } + _spotlight_data->dirty(); +} + float ClusteredShading::getDepthForSlice(int slice) const { diff --git a/simgear/scene/viewer/ClusteredShading.hxx b/simgear/scene/viewer/ClusteredShading.hxx index 4843a102..fed11f1f 100644 --- a/simgear/scene/viewer/ClusteredShading.hxx +++ b/simgear/scene/viewer/ClusteredShading.hxx @@ -42,19 +42,25 @@ protected: }; struct PointlightBound { - SGLight *light; + SGLight *light = nullptr; osg::Vec4f position; - float range; + float range = 0.0f; }; struct SpotlightBound { - SGLight *light; + SGLight *light = nullptr; osg::Vec4f position; - float range; + osg::Vec4f direction; + float cos_cutoff = 0.0f; + struct { + osg::Vec4f center; + float radius = 0.0f; + } bounding_sphere; }; void threadFunc(int thread_id); void assignLightsToSlice(int slice); void writePointlightData(); + void writeSpotlightData(); float getDepthForSlice(int slice) const; osg::observer_ptr _camera; From ece2f913a1aa40a0acb14a059e6dc435c44e9032 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Thu, 7 Nov 2019 15:15:02 +0100 Subject: [PATCH 33/52] Get AeonWave up to par again by removing a number of errors, and fixing a unit type for the audio cone inner and outer angle. --- simgear/sound/soundmgr_aeonwave.cxx | 17 +++++++---------- simgear/sound/soundmgr_test.cxx | 4 ++++ simgear/sound/soundmgr_test2.cxx | 4 ++++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/simgear/sound/soundmgr_aeonwave.cxx b/simgear/sound/soundmgr_aeonwave.cxx index 7a5330de..7a0fa32f 100644 --- a/simgear/sound/soundmgr_aeonwave.cxx +++ b/simgear/sound/soundmgr_aeonwave.cxx @@ -257,7 +257,7 @@ void SGSoundMgr::stop() if (is_working()) { _active = false; - TRY( d->_aax.set(AAX_STOPPED) ); + TRY( d->_aax.set(AAX_PROCESSED) ); _renderer = "unknown"; _vendor = "unknown"; @@ -411,11 +411,8 @@ void SGSoundMgr::release_source( unsigned int source ) if ( source_it != d->_sources.end() ) { aax::Emitter& emitter = source_it->second; - enum aaxState state = emitter.state(); - if (state != AAX_PROCESSED) { - TRY( emitter.set(AAX_PROCESSED) ); - TRY( d->_aax.remove(emitter) ); - } + TRY( emitter.set(AAX_PROCESSED) ); + TRY( d->_aax.remove(emitter) ); TRY( emitter.remove_buffer() ); d->_sources.erase(source_it); } @@ -621,7 +618,7 @@ bool SGSoundMgr::is_sample_stopped(SGSoundSample *sample) assert(sample->is_valid_source()); aax::Emitter& emitter = d->get_emitter(sample->get_source()); int result = emitter.state(); - return (result == AAX_STOPPED); + return (result == AAX_PROCESSED); #else return true; #endif @@ -651,9 +648,9 @@ void SGSoundMgr::update_sample_config( SGSoundSample *sample, SGVec3d& position, TRY( emitter.set(dsp) ); if ( sample->has_static_data_changed() ) { - dsp = emitter.get(AAX_ANGULAR_FILTER); - TRY( dsp.set(AAX_INNER_ANGLE, sample->get_innerangle()) ); - TRY( dsp.set(AAX_OUTER_ANGLE, sample->get_outerangle()) ); + dsp = emitter.get(AAX_DIRECTIONAL_FILTER); + TRY( dsp.set(AAX_INNER_ANGLE, sample->get_innerangle(), AAX_DEGREES) ); + TRY( dsp.set(AAX_OUTER_ANGLE, sample->get_outerangle(), AAX_DEGREES) ); TRY( dsp.set(AAX_OUTER_GAIN, sample->get_outergain()) ); TRY( emitter.set(dsp) ); diff --git a/simgear/sound/soundmgr_test.cxx b/simgear/sound/soundmgr_test.cxx index a9004890..1b8a375f 100644 --- a/simgear/sound/soundmgr_test.cxx +++ b/simgear/sound/soundmgr_test.cxx @@ -29,6 +29,10 @@ int main( int argc, char *argv[] ) { smgr->set_volume(0.9); smgr->activate(); + // prevent NaNs + SGQuatd orient = SGQuatd::fromYawPitchRollDeg(0.0, 0.0, 0.0); + smgr->set_orientation( orient ); + SGPath srcDir(SRC_DIR); SGSoundSample *sample1 = new SGSoundSample("jet.wav", srcDir); diff --git a/simgear/sound/soundmgr_test2.cxx b/simgear/sound/soundmgr_test2.cxx index bab5c39a..3728261d 100644 --- a/simgear/sound/soundmgr_test2.cxx +++ b/simgear/sound/soundmgr_test2.cxx @@ -30,6 +30,10 @@ int main( int argc, char *argv[] ) { SGPath srcDir(SRC_DIR); + // prevent NaNs + SGQuatd orient = SGQuatd::fromYawPitchRollDeg(0.0, 0.0, 0.0); + smgr->set_orientation( orient ); + printf("default position and orientation\n"); SGSoundSample *sample1 = new SGSoundSample("jet.wav", srcDir); sample1->set_volume(1.0); From 94ced66f87d4c7d5633ef7c4a79e14d58c9185c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Thu, 7 Nov 2019 15:46:05 +0100 Subject: [PATCH 34/52] Compositor: Enable more culling for shadow passes --- simgear/scene/viewer/CompositorPass.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index 2a67a6d4..2187a494 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -567,8 +567,7 @@ struct ShadowMapPassBuilder : public PassBuilder { osg::Camera *camera = pass->camera; camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); - camera->setCullingMode(camera->getCullingMode() & - ~osg::CullSettings::SMALL_FEATURE_CULLING); + camera->setCullingMode(osg::CullSettings::ENABLE_ALL_CULLING); //camera->setComputeNearFarMode( // osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); From 15c6131f0e46241afc1fb863db7e821c4a4f355a Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Fri, 8 Nov 2019 10:51:56 +0100 Subject: [PATCH 35/52] Use a better way to set the listener position and orientation --- simgear/sound/soundmgr_test.cxx | 3 +-- simgear/sound/soundmgr_test2.cxx | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/simgear/sound/soundmgr_test.cxx b/simgear/sound/soundmgr_test.cxx index 1b8a375f..68c075f3 100644 --- a/simgear/sound/soundmgr_test.cxx +++ b/simgear/sound/soundmgr_test.cxx @@ -30,8 +30,7 @@ int main( int argc, char *argv[] ) { smgr->activate(); // prevent NaNs - SGQuatd orient = SGQuatd::fromYawPitchRollDeg(0.0, 0.0, 0.0); - smgr->set_orientation( orient ); + smgr->set_position( SGVec3d(0, 0, 0), SGGeod::fromDegFt(0, 0, 0) ); SGPath srcDir(SRC_DIR); diff --git a/simgear/sound/soundmgr_test2.cxx b/simgear/sound/soundmgr_test2.cxx index 3728261d..373f616e 100644 --- a/simgear/sound/soundmgr_test2.cxx +++ b/simgear/sound/soundmgr_test2.cxx @@ -31,8 +31,7 @@ int main( int argc, char *argv[] ) { SGPath srcDir(SRC_DIR); // prevent NaNs - SGQuatd orient = SGQuatd::fromYawPitchRollDeg(0.0, 0.0, 0.0); - smgr->set_orientation( orient ); + smgr->set_position( SGVec3d(0, 0, 0), SGGeod::fromDegFt(0, 0, 0) ); printf("default position and orientation\n"); SGSoundSample *sample1 = new SGSoundSample("jet.wav", srcDir); From b91d4411b53fa0eea555dc924199b41c3df05ed5 Mon Sep 17 00:00:00 2001 From: "James.Hester" Date: Tue, 12 Nov 2019 07:55:09 +1100 Subject: [PATCH 36/52] Added point_in_triangle function. --- simgear/math/SGVec2.hxx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/simgear/math/SGVec2.hxx b/simgear/math/SGVec2.hxx index 93655cb1..0979c0a0 100644 --- a/simgear/math/SGVec2.hxx +++ b/simgear/math/SGVec2.hxx @@ -375,6 +375,29 @@ interpolate(T tau, const SGVec2& v1, const SGVec2& v2) return r; } +// Is the first point inside the triangle formed by the other three points? +// Helper function +template +inline +T +pt_determine(const SGVec2& pt1, const SGVec2& pt2, const SGVec2& pt3) +{ + return (pt1.x()-pt3.x()) * (pt2.y()-pt3.y()) - (pt2.x() - pt3.x()) * (pt1.y() - pt3.y()); +} + +template +inline +bool +point_in_triangle(const SGVec2& testpt, const SGVec2& pt1, const SGVec2& pt2, const SGVec2& pt3) +{ + T d1 = pt_determine(testpt,pt1,pt2); + T d2 = pt_determine(testpt,pt2,pt3); + T d3 = pt_determine(testpt,pt3,pt1); + bool has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0); + bool has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0); + return !(has_neg && has_pos); +} + #ifndef NDEBUG template inline From 0919f5bf9c61593bd75a63e1d0de95406887088b Mon Sep 17 00:00:00 2001 From: "James.Hester" Date: Tue, 12 Nov 2019 08:00:04 +1100 Subject: [PATCH 37/52] Improve comments on previous commit. --- simgear/math/SGVec2.hxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/simgear/math/SGVec2.hxx b/simgear/math/SGVec2.hxx index 0979c0a0..c0c6cb62 100644 --- a/simgear/math/SGVec2.hxx +++ b/simgear/math/SGVec2.hxx @@ -375,8 +375,7 @@ interpolate(T tau, const SGVec2& v1, const SGVec2& v2) return r; } -// Is the first point inside the triangle formed by the other three points? -// Helper function +// Helper function for point_in_triangle template inline T @@ -384,7 +383,8 @@ pt_determine(const SGVec2& pt1, const SGVec2& pt2, const SGVec2& pt3) { return (pt1.x()-pt3.x()) * (pt2.y()-pt3.y()) - (pt2.x() - pt3.x()) * (pt1.y() - pt3.y()); } - + +// Is testpt inside the triangle formed by the other three points? template inline bool From 56933067c05b3558724b5d13b33551c9873a02bb Mon Sep 17 00:00:00 2001 From: "James.Hester" Date: Sat, 16 Nov 2019 11:02:35 +1100 Subject: [PATCH 38/52] Add "is_plantation" property to materials definitions. --- simgear/scene/material/mat.cxx | 1 + simgear/scene/material/mat.hxx | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/simgear/scene/material/mat.cxx b/simgear/scene/material/mat.cxx index 0eaa37a6..0483089e 100644 --- a/simgear/scene/material/mat.cxx +++ b/simgear/scene/material/mat.cxx @@ -328,6 +328,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, // Random vegetation properties wood_coverage = props->getDoubleValue("wood-coverage", 0.0); + is_plantation = props->getBoolValue("plantation",false); tree_effect = props->getStringValue("tree-effect", "Effects/tree"); tree_height = props->getDoubleValue("tree-height-m", 0.0); tree_width = props->getDoubleValue("tree-width-m", 0.0); diff --git a/simgear/scene/material/mat.hxx b/simgear/scene/material/mat.hxx index ca70bd65..8e4647d2 100644 --- a/simgear/scene/material/mat.hxx +++ b/simgear/scene/material/mat.hxx @@ -244,6 +244,13 @@ public: */ inline double get_wood_coverage () const { return wood_coverage; } + /** + * Get whether or not vegetation is regularly spaced + * + * @return flag: if true, vegetation is regularly spaced. + */ + inline bool get_is_plantation () const { return is_plantation; } + /** * Get the tree height. * @@ -455,6 +462,9 @@ private: // coverage of woods double wood_coverage; + // are trees regularly planted? + bool is_plantation; + // Range at which trees become visible double tree_range; From 0f9fe8adef5542949019c363dd8fdb1b6093faaa Mon Sep 17 00:00:00 2001 From: "James.Hester" Date: Sat, 16 Nov 2019 11:16:09 +1100 Subject: [PATCH 39/52] Add generation of plantation vegetation to TileDetailsCallback. Plantations are regularly spaced vegetation. This effect is switched on by the is_plantation material property. Vegetation is laid out at integer spacings in x and y, with the spacing determined by the usual coverage properties. --- simgear/scene/tgdb/SGNodeTriangles.hxx | 152 +++++++++++++------ simgear/scene/tgdb/SGTexturedTriangleBin.hxx | 131 +++++++++++----- simgear/scene/tgdb/SGTileDetailsCallback.hxx | 1 + 3 files changed, 197 insertions(+), 87 deletions(-) diff --git a/simgear/scene/tgdb/SGNodeTriangles.hxx b/simgear/scene/tgdb/SGNodeTriangles.hxx index 1d3f72d2..c32dfd89 100644 --- a/simgear/scene/tgdb/SGNodeTriangles.hxx +++ b/simgear/scene/tgdb/SGNodeTriangles.hxx @@ -285,100 +285,154 @@ public: } } } - + void addRandomTreePoints(float wood_coverage, osg::Texture2D* object_mask, float vegetation_density, float cos_max_density_angle, float cos_zero_density_angle, + bool is_plantation, std::vector& points, std::vector& normals) { if ( !geometries.empty() ) { const osg::Vec3Array* vertices = dynamic_cast(geometries[0]->getVertexArray()); const osg::Vec2Array* texcoords = dynamic_cast(geometries[0]->getTexCoordArray(0)); - + int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); if ( numPrimitiveSets > 0 ) { const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); unsigned int numIndices = ps->getNumIndices(); - + for ( unsigned int i=2; ioperator[](ps->index(i-2))); SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1))); SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0))); - + SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2))); SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1))); SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0))); - + SGVec3f normal = cross(v1 - v0, v2 - v0); - + // Ensure the slope isn't too steep by checking the // cos of the angle between the slope normal and the // vertical (conveniently the z-component of the normalized // normal) and values passed in. float alpha = normalize(normal).z(); float slope_density = 1.0; - + if (alpha < cos_zero_density_angle) continue; // Too steep for any vegetation - + if (alpha < cos_max_density_angle) { slope_density = (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle); } - + // Compute the area float area = 0.5f*length(normal); if (area <= SGLimitsf::min()) continue; - - // Determine the number of trees, taking into account vegetation - // density (which is linear) and the slope density factor. - // Use a zombie door method to create the proper random chance - // of a tree being created for partial values. - int woodcount = (int) (vegetation_density * vegetation_density * - slope_density * - area / wood_coverage + mt_rand(&seed)); - - for (int j = 0; j < woodcount; j++) { - float a = mt_rand(&seed); - float b = mt_rand(&seed); - - if ( a + b > 1.0f ) { - a = 1.0f - a; - b = 1.0f - b; - } - - float c = 1.0f - a - b; - - SGVec3f randomPoint = a*v0 + b*v1 + c*v2; - - if (object_mask != NULL) { - SGVec2f texCoord = a*t0 + b*t1 + c*t2; - - // Check this random point against the object mask - // green (for trees) 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).g()) { - // The red channel contains the rotation for this object - points.push_back(randomPoint); - normals.push_back(normalize(normal)); + + if (!is_plantation) { + // Determine the number of trees, taking into account vegetation + // density (which is linear) and the slope density factor. + // Use a zombie door method to create the proper random chance + // of a tree being created for partial values. + int woodcount = (int) (vegetation_density * vegetation_density * + slope_density * + area / wood_coverage + mt_rand(&seed)); + for (int j = 0; j < woodcount; j++) { + // Use barycentric coordinates + float a = mt_rand(&seed); + float b = mt_rand(&seed); + + if ( a + b > 1.0f ) { + a = 1.0f - a; + b = 1.0f - b; + } + + float c = 1.0f - a - b; + + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // green (for trees) 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).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + + } } - } else { - points.push_back(randomPoint); - normals.push_back(normalize(normal)); - } + } else { // regularly-spaced vegetation + // separate vegetation in integral 1m units + int separation = (int) ceil(sqrt(wood_coverage)); + float max_x = ceil(max(max(v1.x(),v2.x()),v0.x())); + float min_x = floor(min(min(v1.x(),v2.x()),v0.x())); + float max_y = ceil(max(max(v1.y(),v2.y()),v0.y())); + float min_y = floor(min(min(v1.y(),v2.y()),v0.y())); + + /* equation of the plane ax+by+cz+d=0, need d */ + + float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z()); + /* Now loop over a grid, skipping points not in the triangle */ + int x_steps = (int) (max_x - min_x)/separation; + int y_steps = (int) (max_y - min_y)/separation; + SGVec2f v02d = SGVec2f(v0.x(),v0.y()); + SGVec2f v12d = SGVec2f(v1.x(),v1.y()); + SGVec2f v22d = SGVec2f(v2.x(),v2.y()); + + for (int jx = 0; jx < x_steps; jx++) { + float ptx = min_x + jx * separation; + + for (int jy = 0; jy < y_steps; jy++) { + float pty = min_y + jy * separation; + SGVec2f newpt = SGVec2f(ptx,pty); + if (!point_in_triangle(newpt,v02d,v12d,v22d)) + continue; + + // z = (-ax-by-d)/c; c is not zero as + // that would be alpha of 1.0 + + float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z(); + SGVec3f randomPoint = SGVec3f(ptx,pty,ptz); + + if (object_mask != NULL) { + // Check this point against the object mask + // green (for trees) channel. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * newpt.x()) % img->s(); + unsigned int y = (int) (img->t() * newpt.y()) % img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } + } } } } } } - + #if 0 // debug : this will save the tile as a shapefile that can be viewed in QGIS. // NOTE: this is really slow.... diff --git a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx index 47939a75..3c8132e1 100644 --- a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx +++ b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx @@ -216,6 +216,7 @@ public: float vegetation_density, float cos_max_density_angle, float cos_zero_density_angle, + bool is_plantation, std::vector& points, std::vector& normals) { @@ -249,50 +250,104 @@ public: float area = 0.5f*length(normal); if (area <= SGLimitsf::min()) continue; - - // Determine the number of trees, taking into account vegetation - // density (which is linear) and the slope density factor. - // Use a zombie door method to create the proper random chance - // of a tree being created for partial values. - int woodcount = (int) (vegetation_density * vegetation_density * - slope_density * - area / wood_coverage + mt_rand(&seed)); + if (!is_plantation) { + // Determine the number of trees, taking into account vegetation + // density (which is linear) and the slope density factor. + // Use a zombie door method to create the proper random chance + // of a tree being created for partial values. + int woodcount = (int) (vegetation_density * vegetation_density * + slope_density * + area / wood_coverage + mt_rand(&seed)); - for (int j = 0; j < woodcount; j++) { - float a = mt_rand(&seed); - float b = mt_rand(&seed); + for (int j = 0; j < woodcount; j++) { + float a = mt_rand(&seed); + float b = mt_rand(&seed); - if ( a + b > 1.0f ) { - a = 1.0f - a; - b = 1.0f - b; - } - - float c = 1.0f - a - b; - - SGVec3f randomPoint = a*v0 + b*v1 + c*v2; - - if (object_mask != NULL) { - SGVec2f texCoord = a*t0 + b*t1 + c*t2; - - // Check this random point against the object mask - // green (for trees) 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).g()) { - // The red channel contains the rotation for this object - points.push_back(randomPoint); - normals.push_back(normalize(normal)); + if ( a + b > 1.0f ) { + a = 1.0f - a; + b = 1.0f - b; } - } else { - points.push_back(randomPoint); - normals.push_back(normalize(normal)); - } + + float c = 1.0f - a - b; + + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // green (for trees) 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).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } + } else { // regularly-spaced vegetation + + int separation = (int) ceil(sqrt(wood_coverage)); + float max_x = ceil(max(max(v1.x(),v2.x()),v0.x())); + float min_x = floor(min(min(v1.x(),v2.x()),v0.x())); + float max_y = ceil(max(max(v1.y(),v2.y()),v0.y())); + float min_y = floor(min(min(v1.y(),v2.y()),v0.y())); + + // equation of the plane ax+by+cz+d=0, need d + + float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z()); + + // Now loop over a grid, skipping points not in the triangle + + int x_steps = (int) (max_x - min_x)/separation; + int y_steps = (int) (max_y - min_y)/separation; + SGVec2f v02d = SGVec2f(v0.x(),v0.y()); + SGVec2f v12d = SGVec2f(v1.x(),v1.y()); + SGVec2f v22d = SGVec2f(v2.x(),v2.y()); + + for (int jx = 0; jx < x_steps; jx++) { + float ptx = min_x + jx * separation; + + for (int jy = 0; jy < y_steps; jy++) { + float pty = min_y + jy * separation; + SGVec2f newpt = SGVec2f(ptx,pty); + if (!point_in_triangle(newpt,v02d,v12d,v22d)) + continue; + + // z = (-ax-by-d)/c; c is not zero as + // that would be alpha of 1.0 + + float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z(); + SGVec3f randomPoint = SGVec3f(ptx,pty,ptz); + + if (object_mask != NULL) { + // Check this point against the object mask + // green (for trees) channel. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * newpt.x()) % img->s(); + unsigned int y = (int) (img->t() * newpt.y()) % img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } + } } } } - + void addRandomPoints(double coverage, double spacing, osg::Texture2D* object_mask, diff --git a/simgear/scene/tgdb/SGTileDetailsCallback.hxx b/simgear/scene/tgdb/SGTileDetailsCallback.hxx index 6a16c5da..2c46aef5 100644 --- a/simgear/scene/tgdb/SGTileDetailsCallback.hxx +++ b/simgear/scene/tgdb/SGTileDetailsCallback.hxx @@ -753,6 +753,7 @@ public: vegetation_density, mat->get_cos_tree_max_density_slope_angle(), mat->get_cos_tree_zero_density_slope_angle(), + mat->get_is_plantation(), randomPoints, randomPointNormals); From 595eb3efea71eeaaddd5fe2f789c335b2c35a21f Mon Sep 17 00:00:00 2001 From: "James.Hester" Date: Thu, 21 Nov 2019 20:36:56 +1100 Subject: [PATCH 40/52] Change order of test for is_plantation as per feedback. --- simgear/scene/tgdb/SGNodeTriangles.hxx | 84 ++++++++++---------- simgear/scene/tgdb/SGTexturedTriangleBin.hxx | 84 ++++++++++---------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/simgear/scene/tgdb/SGNodeTriangles.hxx b/simgear/scene/tgdb/SGNodeTriangles.hxx index c32dfd89..128b242d 100644 --- a/simgear/scene/tgdb/SGNodeTriangles.hxx +++ b/simgear/scene/tgdb/SGNodeTriangles.hxx @@ -335,48 +335,7 @@ public: if (area <= SGLimitsf::min()) continue; - if (!is_plantation) { - // Determine the number of trees, taking into account vegetation - // density (which is linear) and the slope density factor. - // Use a zombie door method to create the proper random chance - // of a tree being created for partial values. - int woodcount = (int) (vegetation_density * vegetation_density * - slope_density * - area / wood_coverage + mt_rand(&seed)); - for (int j = 0; j < woodcount; j++) { - // Use barycentric coordinates - float a = mt_rand(&seed); - float b = mt_rand(&seed); - - if ( a + b > 1.0f ) { - a = 1.0f - a; - b = 1.0f - b; - } - - float c = 1.0f - a - b; - - SGVec3f randomPoint = a*v0 + b*v1 + c*v2; - if (object_mask != NULL) { - SGVec2f texCoord = a*t0 + b*t1 + c*t2; - - // Check this random point against the object mask - // green (for trees) 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).g()) { - // The red channel contains the rotation for this object - points.push_back(randomPoint); - normals.push_back(normalize(normal)); - } - } else { - points.push_back(randomPoint); - normals.push_back(normalize(normal)); - - } - } - } else { // regularly-spaced vegetation + if (is_plantation) { // regularly-spaced vegetation // separate vegetation in integral 1m units int separation = (int) ceil(sqrt(wood_coverage)); float max_x = ceil(max(max(v1.x(),v2.x()),v0.x())); @@ -427,6 +386,47 @@ public: } } } + } else { + // Determine the number of trees, taking into account vegetation + // density (which is linear) and the slope density factor. + // Use a zombie door method to create the proper random chance + // of a tree being created for partial values. + int woodcount = (int) (vegetation_density * vegetation_density * + slope_density * + area / wood_coverage + mt_rand(&seed)); + for (int j = 0; j < woodcount; j++) { + // Use barycentric coordinates + float a = mt_rand(&seed); + float b = mt_rand(&seed); + + if ( a + b > 1.0f ) { + a = 1.0f - a; + b = 1.0f - b; + } + + float c = 1.0f - a - b; + + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // green (for trees) 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).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + + } + } } } } diff --git a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx index 3c8132e1..8f5cc314 100644 --- a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx +++ b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx @@ -250,48 +250,7 @@ public: float area = 0.5f*length(normal); if (area <= SGLimitsf::min()) continue; - if (!is_plantation) { - // Determine the number of trees, taking into account vegetation - // density (which is linear) and the slope density factor. - // Use a zombie door method to create the proper random chance - // of a tree being created for partial values. - int woodcount = (int) (vegetation_density * vegetation_density * - slope_density * - area / wood_coverage + mt_rand(&seed)); - - for (int j = 0; j < woodcount; j++) { - float a = mt_rand(&seed); - float b = mt_rand(&seed); - - if ( a + b > 1.0f ) { - a = 1.0f - a; - b = 1.0f - b; - } - - float c = 1.0f - a - b; - - SGVec3f randomPoint = a*v0 + b*v1 + c*v2; - - if (object_mask != NULL) { - SGVec2f texCoord = a*t0 + b*t1 + c*t2; - - // Check this random point against the object mask - // green (for trees) 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).g()) { - // The red channel contains the rotation for this object - points.push_back(randomPoint); - normals.push_back(normalize(normal)); - } - } else { - points.push_back(randomPoint); - normals.push_back(normalize(normal)); - } - } - } else { // regularly-spaced vegetation + if (is_plantation) { // regularly-spaced vegetation int separation = (int) ceil(sqrt(wood_coverage)); float max_x = ceil(max(max(v1.x(),v2.x()),v0.x())); @@ -344,6 +303,47 @@ public: } } } + } else { + // Determine the number of trees, taking into account vegetation + // density (which is linear) and the slope density factor. + // Use a zombie door method to create the proper random chance + // of a tree being created for partial values. + int woodcount = (int) (vegetation_density * vegetation_density * + slope_density * + area / wood_coverage + mt_rand(&seed)); + + for (int j = 0; j < woodcount; j++) { + float a = mt_rand(&seed); + float b = mt_rand(&seed); + + if ( a + b > 1.0f ) { + a = 1.0f - a; + b = 1.0f - b; + } + + float c = 1.0f - a - b; + + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // green (for trees) 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).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } } } } From d5957b8c5f2188674eb6b3d480bd8eb70d253f79 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sat, 23 Nov 2019 17:05:20 +0000 Subject: [PATCH 41/52] Random Buildings - improved texture mapping Previously the front and side faces of random/OSM buildings had identical texture coordinates. This resulted in the sides of buildings texture mapping being squeezed or stretched. This change generates a separate texture mapping for the sides of the buildings. --- simgear/scene/tgdb/SGBuildingBin.cxx | 99 +++++++++++++++------------- simgear/scene/tgdb/SGBuildingBin.hxx | 4 +- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index 157dcccd..2208f1e8 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -83,7 +83,8 @@ struct MakeBuildingLeaf 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. + // vertex shader for texture mapping: + // (front, roof, roof top vertex, side) v->reserve(52); t->reserve(52); @@ -174,7 +175,7 @@ struct MakeBuildingLeaf for (int i=0; i<4; ++i) { n->push_back( osg::Vec3(0, -1, 0) ); // normal - c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 0, 0, 1) ); // color - used to differentiate wall from roof } // Back face @@ -206,7 +207,7 @@ struct MakeBuildingLeaf for (int i=0; i<4; ++i) { n->push_back( osg::Vec3(0, 1, 0) ); // normal - c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof + c->push_back( osg::Vec4(0, 0, 0, 1) ); // color - used to differentiate wall from roof } // ROOF 1 - built as a block. The shader will deform it to the correct shape. @@ -369,9 +370,9 @@ struct AddBuildingLeafObject 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, wall texture x offset) - osg::Vec3Array* tex = static_cast (geom->getVertexAttribArray(BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR)); // (wall texture y offset, wall/roof texture x gain, wall/roof texture y gain) - osg::Vec3Array* rtex = static_cast (geom->getVertexAttribArray(BUILDING_RTEX0X_RTEX0Y_ATTR)); // (roof texture x offset, roof texture y offset, unused) + osg::Vec3Array* rot = static_cast (geom->getVertexAttribArray(BUILDING_ROT_PITCH_TEX0X_ATTR)); // (rotation, pitch height, wall texture x0) + osg::Vec3Array* tex = static_cast (geom->getVertexAttribArray(BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR)); // (wall texture y0, front/roof texture x1, front/side/roof texture y1) + osg::Vec3Array* rtex = static_cast (geom->getVertexAttribArray(BUILDING_RTEX0X_RTEX0Y_ATTR)); // (roof texture x0, roof texture y0, side texture x1) osg::Vec3Array* rooftops = static_cast (geom->getVertexAttribArray(BUILDING_ROFFTOPSCALE_ATTR)); // (rooftop scale x, rooftop scale y, unused) positions->push_back(building.position); @@ -379,7 +380,7 @@ struct AddBuildingLeafObject scale->push_back(osg::Vec3f(building.depth, building.width, building.height)); rot->push_back(osg::Vec3f(building.rotation, building.pitch_height, building.walltex0.x())); tex->push_back(osg::Vec3f(building.walltex0.y(), building.tex1.x(), building.tex1.y())); - rtex->push_back(osg::Vec3f(building.rooftex0.x(), building.rooftex0.y(), 0.0f)); + rtex->push_back(osg::Vec3f(building.rooftex0.x(), building.rooftex0.y(), building.tex1.z())); rooftops->push_back(osg::Vec3f(building.rooftop_scale.x(), building.rooftop_scale.y(), 0.0f)); DrawArrays* primSet = static_cast(geom->getPrimitiveSet(0)); @@ -439,7 +440,7 @@ typedef QuadTreeBuilder 0.5f) || - (wall_offset_y + gain_y > (6.0f * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT))) { - SG_LOG(SG_TERRAIN, SG_ALERT, "Small building texture coordinates out of bounds offset (" << wall_offset_x << ", " << wall_offset_y << ") gain (" << gain_x << ", " << gain_y << ")"); + if ((wall_x0 + wall_roof_x1 > 0.5f) || + (wall_y0 + y1 > (6.0f * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT))) { + SG_LOG(SG_TERRAIN, SG_ALERT, "Small building texture coordinates out of bounds offset (" << wall_x0 << ", " << wall_y0 << ") gain (" << wall_roof_x1 << ", " << y1 << ")"); } - wall_tex0 = Vec2f(wall_offset_x, wall_offset_y); - roof_tex0 = Vec2f(roof_offset_x, roof_offset_y); - tex1 = Vec2f(gain_x, gain_y); + wall_tex0 = Vec2f(wall_x0, wall_y0); + roof_tex0 = Vec2f(roof_x0, roof_y0); + tex1 = Vec3f(wall_roof_x1, y1, side_x1); } else if (buildingtype == SGBuildingBin::MEDIUM) { // MEDIUM BUILDING // Maximum texture height is 8 stories. @@ -533,27 +536,28 @@ typedef QuadTreeBuilder 0.5f) || - (wall_offset_y + gain_y < (6.0f * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT)) || - (wall_offset_y + gain_y > ((6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT))) { - SG_LOG(SG_TERRAIN, SG_ALERT, "Medium building texture coordinates out of bounds offset (" << wall_offset_x << ", " << wall_offset_y << ") gain (" << gain_x << ", " << gain_y << ")"); + if ((wall_x0 + wall_roof_x1 > 0.5f) || + (wall_y0 + y1 < (6.0f * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT)) || + (wall_y0 + y1 > ((6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT))) { + SG_LOG(SG_TERRAIN, SG_ALERT, "Medium building texture coordinates out of bounds offset (" << wall_x0 << ", " << wall_y0 << ") gain (" << wall_roof_x1 << ", " << y1 << ")"); } - wall_tex0 = Vec2f(wall_offset_x, wall_offset_y); - roof_tex0 = Vec2f(roof_offset_x, roof_offset_y); - tex1 = Vec2f(gain_x, gain_y); + wall_tex0 = Vec2f(wall_x0, wall_y0); + roof_tex0 = Vec2f(roof_x0, roof_y0); + tex1 = Vec3f(wall_roof_x1, y1, side_x1); } else { // LARGE BUILDING // Maximum texture height is 22 stories. @@ -563,22 +567,23 @@ typedef QuadTreeBuilder 0.5f) || - (wall_offset_y + gain_y < ((6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT)) || - (wall_offset_y + gain_y > 1.0) ) { - SG_LOG(SG_TERRAIN, SG_ALERT, "Large building texture coordinates out of bounds offset (" << wall_offset_x << ", " << wall_offset_y << ") gain (" << gain_x << ", " << gain_y << ")"); + if ((wall_x0 + wall_roof_x1 > 0.5f) || + (wall_y0 + y1 < ((6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT)) || + (wall_y0 + y1 > 1.0) ) { + SG_LOG(SG_TERRAIN, SG_ALERT, "Large building texture coordinates out of bounds offset (" << wall_x0 << ", " << wall_y0 << ") gain (" << wall_roof_x1 << ", " << y1 << ")"); } - wall_tex0 = Vec2f(wall_offset_x, wall_offset_y); - roof_tex0 = Vec2f(roof_offset_x, roof_offset_y); - tex1 = Vec2f(gain_x, gain_y); + wall_tex0 = Vec2f(wall_x0, wall_y0); + roof_tex0 = Vec2f(roof_x0, roof_y0); + tex1 = Vec3f(wall_roof_x1, y1, side_x1); } // Build a scaling factor in the x,y axes for the top of the roof. This allows us to create gabled, hipped, pyramidal roofs. diff --git a/simgear/scene/tgdb/SGBuildingBin.hxx b/simgear/scene/tgdb/SGBuildingBin.hxx index d10d1c1d..6e99d45b 100644 --- a/simgear/scene/tgdb/SGBuildingBin.hxx +++ b/simgear/scene/tgdb/SGBuildingBin.hxx @@ -92,7 +92,7 @@ public: LARGE }; struct BuildingInstance { - BuildingInstance(Vec3f p, float w, float d, float h, float ph, float r, Vec2f wt0, Vec2f rt0, Vec2f t1, Vec2f rs) : + BuildingInstance(Vec3f p, float w, float d, float h, float ph, float r, Vec2f wt0, Vec2f rt0, Vec3f t1, Vec2f rs) : position(p), width(w), depth(d), @@ -128,7 +128,7 @@ public: Vec2f walltex0; Vec2f rooftex0; - Vec2f tex1; + Vec3f tex1; // Texture gains for the front, roof and sides Vec2f rooftop_scale; From 86f3169e4bc6f5ab2eae7587c377bab1fd237c22 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sun, 8 Dec 2019 17:36:23 +0000 Subject: [PATCH 42/52] Shader Buildings - fix roof orientation Correct transformation to make roof ridges match orientation in documentation. --- simgear/scene/tgdb/SGBuildingBin.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index 2208f1e8..2a56f309 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -595,16 +595,16 @@ typedef QuadTreeBuilder Date: Sat, 14 Dec 2019 23:31:13 -0600 Subject: [PATCH 43/52] [CanvasText] Fix conditional compiles --- simgear/canvas/elements/CanvasText.cxx | 46 ++++++++++++-------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/simgear/canvas/elements/CanvasText.cxx b/simgear/canvas/elements/CanvasText.cxx index 9e4e88b9..68d554bf 100644 --- a/simgear/canvas/elements/CanvasText.cxx +++ b/simgear/canvas/elements/CanvasText.cxx @@ -61,13 +61,11 @@ namespace canvas SGVec2i sizeForWidth(int w); #endif - osg::BoundingBox #if OSG_VERSION_LESS_THAN(3,3,2) - computeBound() + osg::BoundingBox computeBound() const override; #else - computeBoundingBox() + osg::BoundingBox computeBoundingBox() const override; #endif - const override; protected: friend class TextLine; @@ -129,7 +127,6 @@ namespace canvas _quads = &text->_textureGlyphQuadMap.begin()->second; - #if OSG_VERSION_LESS_THAN(3,5,6) GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers; GlyphQuads::LineNumbers::const_iterator begin_it = @@ -143,8 +140,7 @@ namespace canvas _end = std::upper_bound(begin_it, line_numbers.end(), _line) - line_numbers.begin(); #else -//OSG:TODO: Need 3.5.6 version of this - + // TODO: Need 3.5.6 version of this #endif } @@ -174,11 +170,17 @@ namespace canvas if( empty() ) return pos; + #if OSG_VERSION_LESS_THAN(3,3,5) GlyphQuads::Coords2 const& coords = _quads->_coords; #elif OSG_VERSION_LESS_THAN(3,5,6) GlyphQuads::Coords2 refCoords = _quads->_coords; GlyphQuads::Coords2::element_type &coords = *refCoords.get(); +#else + // TODO: need 3.5.7 version of this. + return pos; +#endif + size_t global_i = _begin + i; if (global_i == _begin) @@ -201,9 +203,6 @@ namespace canvas // position at center between characters pos.x() = 0.5 * (prev_r + cur_l); } -#else -//OSG:TODO: need 3.5.7 version of this. -#endif return pos; } @@ -211,16 +210,20 @@ namespace canvas //---------------------------------------------------------------------------- osg::Vec2 TextLine::nearestCursor(float x) const { - if( empty() ) + if (empty()) return cursorPos(0); - GlyphQuads::Glyphs const& glyphs = _quads->_glyphs; #if OSG_VERSION_LESS_THAN(3,3,5) GlyphQuads::Coords2 const& coords = _quads->_coords; #elif OSG_VERSION_LESS_THAN(3,5,6) - GlyphQuads::Coords2 refCoords = _quads->_coords; GlyphQuads::Coords2::element_type &coords = *refCoords.get(); +#else + // TODO: need 3.5.7 version of this. + return cursorPos(0); +#endif + + GlyphQuads::Glyphs const& glyphs = _quads->_glyphs; float const HIT_FRACTION = 0.6; float const character_width = _text->getCharacterHeight() @@ -240,10 +243,6 @@ namespace canvas } return cursorPos(i - _begin); -#else -//OSG:TODO: need 3.5.7 version of this. - return cursorPos(0); -#endif } //---------------------------------------------------------------------------- @@ -647,19 +646,16 @@ namespace canvas } //---------------------------------------------------------------------------- - osg::BoundingBox #if OSG_VERSION_LESS_THAN(3,3,2) - Text::TextOSG::computeBound() + osg::BoundingBox Text::TextOSG::computeBound() const #else - Text::TextOSG::computeBoundingBox() + osg::BoundingBox Text::TextOSG::computeBoundingBox() const #endif - const { - osg::BoundingBox bb = #if OSG_VERSION_LESS_THAN(3,3,2) - osgText::Text::computeBound(); + osg::BoundingBox bb = osgText::Text::computeBound(); #else - osgText::Text::computeBoundingBox(); + osg::BoundingBox bb = osgText::Text::computeBoundingBox(); #endif #if OSG_VERSION_LESS_THAN(3,1,0) @@ -748,7 +744,7 @@ namespace canvas #else void Text::TextOSG::computePositionsImplementation() { - TextBase::computePositionsImplementation(); + TextBase::computePositionsImplementation(); } #endif //---------------------------------------------------------------------------- From dc886118baec64f9e86e45c1e2713bbf245f972e Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sun, 15 Dec 2019 00:13:46 -0600 Subject: [PATCH 44/52] [CanvasText] Fix conditional compiles --- simgear/canvas/elements/CanvasText.cxx | 49 +++++++++++++------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/simgear/canvas/elements/CanvasText.cxx b/simgear/canvas/elements/CanvasText.cxx index 9e4e88b9..9f7a54d7 100644 --- a/simgear/canvas/elements/CanvasText.cxx +++ b/simgear/canvas/elements/CanvasText.cxx @@ -61,13 +61,11 @@ namespace canvas SGVec2i sizeForWidth(int w); #endif - osg::BoundingBox #if OSG_VERSION_LESS_THAN(3,3,2) - computeBound() + osg::BoundingBox computeBound() const override; #else - computeBoundingBox() + osg::BoundingBox computeBoundingBox() const override; #endif - const override; protected: friend class TextLine; @@ -129,7 +127,6 @@ namespace canvas _quads = &text->_textureGlyphQuadMap.begin()->second; - #if OSG_VERSION_LESS_THAN(3,5,6) GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers; GlyphQuads::LineNumbers::const_iterator begin_it = @@ -143,8 +140,7 @@ namespace canvas _end = std::upper_bound(begin_it, line_numbers.end(), _line) - line_numbers.begin(); #else -//OSG:TODO: Need 3.5.6 version of this - + // TODO: Need 3.5.6 version of this #endif } @@ -174,11 +170,17 @@ namespace canvas if( empty() ) return pos; + +#if OSG_VERSION_GREATER_OR_EQUAL(3,5,6) + // TODO: need 3.5.6 version of this. +#else #if OSG_VERSION_LESS_THAN(3,3,5) GlyphQuads::Coords2 const& coords = _quads->_coords; -#elif OSG_VERSION_LESS_THAN(3,5,6) +#else GlyphQuads::Coords2 refCoords = _quads->_coords; GlyphQuads::Coords2::element_type &coords = *refCoords.get(); +#endif + size_t global_i = _begin + i; if (global_i == _begin) @@ -201,8 +203,6 @@ namespace canvas // position at center between characters pos.x() = 0.5 * (prev_r + cur_l); } -#else -//OSG:TODO: need 3.5.7 version of this. #endif return pos; @@ -211,16 +211,21 @@ namespace canvas //---------------------------------------------------------------------------- osg::Vec2 TextLine::nearestCursor(float x) const { - if( empty() ) + if (empty()) return cursorPos(0); - GlyphQuads::Glyphs const& glyphs = _quads->_glyphs; +#if OSG_VERSION_GREATER_OR_EQUAL(3,5,6) + // TODO: need 3.5.7 version of this. + return cursorPos(0); +#else #if OSG_VERSION_LESS_THAN(3,3,5) GlyphQuads::Coords2 const& coords = _quads->_coords; -#elif OSG_VERSION_LESS_THAN(3,5,6) - +#else GlyphQuads::Coords2 refCoords = _quads->_coords; GlyphQuads::Coords2::element_type &coords = *refCoords.get(); +#endif + + GlyphQuads::Glyphs const& glyphs = _quads->_glyphs; float const HIT_FRACTION = 0.6; float const character_width = _text->getCharacterHeight() @@ -240,9 +245,6 @@ namespace canvas } return cursorPos(i - _begin); -#else -//OSG:TODO: need 3.5.7 version of this. - return cursorPos(0); #endif } @@ -647,19 +649,16 @@ namespace canvas } //---------------------------------------------------------------------------- - osg::BoundingBox #if OSG_VERSION_LESS_THAN(3,3,2) - Text::TextOSG::computeBound() + osg::BoundingBox Text::TextOSG::computeBound() const #else - Text::TextOSG::computeBoundingBox() + osg::BoundingBox Text::TextOSG::computeBoundingBox() const #endif - const { - osg::BoundingBox bb = #if OSG_VERSION_LESS_THAN(3,3,2) - osgText::Text::computeBound(); + osg::BoundingBox bb = osgText::Text::computeBound(); #else - osgText::Text::computeBoundingBox(); + osg::BoundingBox bb = osgText::Text::computeBoundingBox(); #endif #if OSG_VERSION_LESS_THAN(3,1,0) @@ -748,7 +747,7 @@ namespace canvas #else void Text::TextOSG::computePositionsImplementation() { - TextBase::computePositionsImplementation(); + TextBase::computePositionsImplementation(); } #endif //---------------------------------------------------------------------------- From c653a82619f3db992881e1f2a877236f01bf4a4d Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sun, 15 Dec 2019 02:00:16 -0600 Subject: [PATCH 45/52] Eliminate unitialized warning --- simgear/scene/model/SGLight.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/simgear/scene/model/SGLight.cxx b/simgear/scene/model/SGLight.cxx index 269829e1..03b659fa 100644 --- a/simgear/scene/model/SGLight.cxx +++ b/simgear/scene/model/SGLight.cxx @@ -14,6 +14,8 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +#include + #include "SGLight.hxx" #include @@ -107,7 +109,7 @@ SGLight::appendLight(const SGPropertyNode *configNode, } align->setMatrix(r * t); - osg::Shape *debug_shape; + osg::Shape *debug_shape = nullptr; if (light->getType() == SGLight::Type::POINT) { debug_shape = new osg::Sphere(osg::Vec3(0, 0, 0), light->getRange()); } else if (light->getType() == SGLight::Type::SPOT) { @@ -116,7 +118,10 @@ SGLight::appendLight(const SGPropertyNode *configNode, osg::Vec3(0, 0, -0.75 * light->getRange()), tan(light->getSpotCutoff() * SG_DEGREES_TO_RADIANS) * light->getRange(), light->getRange()); + } else { + throw std::domain_error("Unsupported SGLight::Type"); } + osg::ShapeDrawable *debug_drawable = new osg::ShapeDrawable(debug_shape); debug_drawable->setColor(osg::Vec4(1.0, 0.0, 0.0, 1.0)); osg::Geode *debug_geode = new osg::Geode; From 1619df97f8a905bedf126f54331d3e47d93fb5d2 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sun, 15 Dec 2019 02:19:13 -0600 Subject: [PATCH 46/52] Potential unterminated string --- simgear/nasal/parse.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/simgear/nasal/parse.c b/simgear/nasal/parse.c index e5b012fa..419bf5b3 100644 --- a/simgear/nasal/parse.c +++ b/simgear/nasal/parse.c @@ -420,10 +420,14 @@ naRef naParseCode(struct Context* c, naRef srcFile, int firstLine, // Catch parser errors here. p.errLine = *errLine = 1; - if(setjmp(p.jumpHandle)) { - strncpy(c->error, p.err, sizeof(c->error)); + if (setjmp(p.jumpHandle)) { + size_t end_ = sizeof(c->error) - 1; + strncpy(c->error, p.err, end_); + c->error[end_] = '\0'; + *errLine = p.errLine; naParseDestroy(&p); + return naNil(); } From eb1afec22b126f48b5cba77cd38c203ab0031cbf Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sun, 15 Dec 2019 02:39:43 -0600 Subject: [PATCH 47/52] Remove unused object creation --- simgear/emesary/test_emesary.cxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/simgear/emesary/test_emesary.cxx b/simgear/emesary/test_emesary.cxx index 852fa55b..55acd622 100644 --- a/simgear/emesary/test_emesary.cxx +++ b/simgear/emesary/test_emesary.cxx @@ -32,20 +32,23 @@ class TestThreadRecipient : public simgear::Emesary::IReceiver public: TestThreadRecipient() : receiveCount(0) { - } std::atomic receiveCount; + virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n) { if (n.GetType() == (const char*)this) { - TestThreadNotification *tn = dynamic_cast(&n); + // Unused: TestThreadNotification *tn = dynamic_cast(&n); receiveCount++; + TestThreadNotification onwardNotification("AL"); simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(onwardNotification); + return simgear::Emesary::ReceiptStatusOK; } + return simgear::Emesary::ReceiptStatusOK; } }; From 02496da4d29eaa952273b922c8f4f719034a4894 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sun, 15 Dec 2019 03:08:06 -0600 Subject: [PATCH 48/52] Logic error --- simgear/structure/SGExpression.cxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/simgear/structure/SGExpression.cxx b/simgear/structure/SGExpression.cxx index e6b38500..4e2a4f83 100644 --- a/simgear/structure/SGExpression.cxx +++ b/simgear/structure/SGExpression.cxx @@ -95,9 +95,11 @@ SGReadValueFromString(const char* str, bool& value) SG_LOG(SG_IO, SG_ALERT, "Cannot read string content."); return false; } + std::stringstream s; s.str(std::string(str)); s >> value; + if (!s.fail()) return true; @@ -109,10 +111,12 @@ SGReadValueFromString(const char* str, bool& value) value = true; return true; } + if (stdstr == "false" || stdstr == "False" || stdstr == "FALSE") { - value = true; + value = false; return true; } + return false; } From fb57fe9da78a6f14ace0a7e3323e1354fa59cb15 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sun, 15 Dec 2019 03:12:54 -0600 Subject: [PATCH 49/52] Catching an exception by value --- simgear/io/iostreams/zlibstream_test.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simgear/io/iostreams/zlibstream_test.cxx b/simgear/io/iostreams/zlibstream_test.cxx index b62ec160..20ae1c10 100644 --- a/simgear/io/iostreams/zlibstream_test.cxx +++ b/simgear/io/iostreams/zlibstream_test.cxx @@ -431,7 +431,7 @@ void test_ZlibDecompressorIStream_readPutbackEtc() try { // 'Z' is not the last character read from the stream decompressor.putback('Z'); - } catch (std::ios_base::failure) { + } catch (const std::ios_base::failure&) { gotException = true; } catch (const std::exception& e) { // gcc fails to catch std::ios_base::failure due to an inconsistent C++11 From c39a5fd8c0238f45fa56609f0e602b3793862e94 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 25 Nov 2019 11:35:07 +0000 Subject: [PATCH 50/52] Fix some Clang warnings --- simgear/canvas/CanvasWindow.hxx | 8 ++++---- simgear/io/untar.cxx | 7 +++---- simgear/io/untar.hxx | 2 +- simgear/scene/model/SGPickAnimation.cxx | 15 ++++++--------- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/simgear/canvas/CanvasWindow.hxx b/simgear/canvas/CanvasWindow.hxx index db24bb9c..665a660a 100644 --- a/simgear/canvas/CanvasWindow.hxx +++ b/simgear/canvas/CanvasWindow.hxx @@ -68,8 +68,8 @@ namespace canvas Element* parent = 0 ); virtual ~Window(); - virtual void update(double delta_time_sec); - virtual void valueChanged(SGPropertyNode* node); + void update(double delta_time_sec) override; + void valueChanged(SGPropertyNode* node) override; const SGVec2 getPosition() const; const SGRect getScreenRegion() const; @@ -84,8 +84,8 @@ namespace canvas bool isResizable() const; bool isCapturingEvents() const; - virtual void setVisible(bool visible); - virtual bool isVisible() const; + void setVisible(bool visible) override; + bool isVisible() const override; /** * Moves window on top of all other windows with the same z-index. diff --git a/simgear/io/untar.cxx b/simgear/io/untar.cxx index 4d825efe..cc70c62f 100644 --- a/simgear/io/untar.cxx +++ b/simgear/io/untar.cxx @@ -48,6 +48,8 @@ namespace simgear assert(outer); } + virtual ~ArchiveExtractorPrivate() = default; + typedef enum { INVALID = 0, READING_HEADER, @@ -614,10 +616,7 @@ ArchiveExtractor::ArchiveExtractor(const SGPath& rootPath) : { } -ArchiveExtractor::~ArchiveExtractor() -{ - -} +ArchiveExtractor::~ArchiveExtractor() = default; void ArchiveExtractor::extractBytes(const uint8_t* bytes, size_t count) { diff --git a/simgear/io/untar.hxx b/simgear/io/untar.hxx index 1b3d9ccc..9760544c 100644 --- a/simgear/io/untar.hxx +++ b/simgear/io/untar.hxx @@ -34,7 +34,7 @@ class ArchiveExtractor { public: ArchiveExtractor(const SGPath& rootPath); - ~ArchiveExtractor(); + virtual ~ArchiveExtractor(); enum DetermineResult { diff --git a/simgear/scene/model/SGPickAnimation.cxx b/simgear/scene/model/SGPickAnimation.cxx index 9b9f5249..9f2a9eac 100644 --- a/simgear/scene/model/SGPickAnimation.cxx +++ b/simgear/scene/model/SGPickAnimation.cxx @@ -615,9 +615,8 @@ public: return false; } - virtual void buttonReleased( int keyModState, - const osgGA::GUIEventAdapter&, - const Info* ) + void buttonReleased( int keyModState, const osgGA::GUIEventAdapter&, + const Info* ) override { if (!_condition || _condition->test()) { @@ -641,8 +640,7 @@ public: return _dragDirection; } - virtual void mouseMoved( const osgGA::GUIEventAdapter& ea, - const Info* ) + void mouseMoved( const osgGA::GUIEventAdapter& ea, const Info* ) override { if (!_condition || _condition->test()) { _mousePos = eventToWindowCoords(ea); @@ -675,7 +673,7 @@ public: } } - virtual void update(double dt, int keyModState) + void update(double dt, int keyModState) override { if (_hasDragged) { return; @@ -688,8 +686,7 @@ public: } // of repeat iteration } - virtual bool hover( const osg::Vec2d& windowPos, - const Info& ) + bool hover( const osg::Vec2d& windowPos, const Info& ) override { if (!_condition || _condition->test()) { @@ -711,7 +708,7 @@ public: _cursorName = aName; } - virtual std::string getCursor() const + std::string getCursor() const override { return _cursorName; } private: From da45c26e7d9d987bbe654d6547286d7e1710608c Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 25 Nov 2019 11:35:30 +0000 Subject: [PATCH 51/52] Adding pixel manipulators to canvas::Image --- simgear/canvas/elements/CanvasImage.cxx | 93 +++++++++++++++++++ simgear/canvas/elements/CanvasImage.hxx | 16 ++++ simgear/math/SGVec2.hxx | 4 +- .../cppbind/detail/from_nasal_helper.hxx | 9 ++ simgear/nasal/cppbind/detail/nasal_traits.hxx | 12 +++ 5 files changed, 132 insertions(+), 2 deletions(-) diff --git a/simgear/canvas/elements/CanvasImage.cxx b/simgear/canvas/elements/CanvasImage.cxx index fbd2cf9f..c13f25ab 100644 --- a/simgear/canvas/elements/CanvasImage.cxx +++ b/simgear/canvas/elements/CanvasImage.cxx @@ -838,5 +838,98 @@ namespace canvas return false; } + void Image::fillRect(const SGRect& rect, const std::string& c) + { + osg::Vec4 color(1,1,1,1); + if(!c.empty() && !parseColor(c, color)) + return; + + fillRect(rect, color); + } + +void fillRow(GLubyte* row, GLuint pixel, GLuint width, GLuint pixelBytes) +{ + GLubyte* dst = row; + for (int x = 0; x < width; ++x) { + memcpy(dst, &pixel, pixelBytes); + dst += pixelBytes; + } +} + +SGRect intersectRect(const SGRect& a, const SGRect& b) +{ + SGVec2 m1 = max(a.getMin(), b.getMin()); + SGVec2 m2 = min(a.getMax(), b.getMax()); + return SGRect(m1, m2); +} + + void Image::fillRect(const SGRect& rect, const osg::Vec4& color) + { + osg::ref_ptr image = _texture->getImage(); + const auto format = image->getInternalTextureFormat(); + + auto clippedRect = intersectRect(rect, SGRect(0, 0, image->s(), image->t())); + if ((clippedRect.width() == 0) || (clippedRect.height() == 0)) { + return; + } + + GLubyte* rowData = nullptr; + size_t rowByteSize = 0; + GLuint pixelWidth = clippedRect.width(); + GLuint pixel = 0; + GLuint pixelBytes = 0; + + switch (format) { + case GL_RGBA8: + rowByteSize = pixelWidth * 4; + rowData = static_cast(alloca(rowByteSize)); + + // assume litte-endian, so read out backwards, hence when we memcpy + // the data, it ends up in RGBA order + pixel = color.asABGR(); + pixelBytes = 4; + fillRow(rowData, pixel, pixelWidth, pixelBytes); + break; + + case GL_RGB8: + rowByteSize = pixelWidth * 3; + rowData = static_cast(alloca(rowByteSize)); + pixel = color.asABGR(); + pixelBytes = 3; + fillRow(rowData, pixel, pixelWidth, pixelBytes); + break; + + default: + SG_LOG(SG_IO, SG_WARN, __PRETTY_FUNCTION__ << ": unsupported internal image format:" << format); + return; + } + + for (int row=clippedRect.t(); row < clippedRect.b(); ++row) { + GLubyte* imageData = image->data(clippedRect.l(), row); + memcpy(imageData, rowData, rowByteSize); + } + + setImage(image); + } + + void Image::setPixel(int x, int y, const std::string& c) + { + osg::Vec4 color(1,1,1,1); + if(!c.empty() && !parseColor(c, color)) + return; + + setPixel(x, y, color); + } + + void Image::setPixel(int x, int y, const osg::Vec4& color) + { + osg::ref_ptr image = _texture->getImage(); + image->setColor(color, x, y); + + // is this needed, or does OSG track modifications to the data + // automatically? + setImage(image); + } + } // namespace canvas } // namespace simgear diff --git a/simgear/canvas/elements/CanvasImage.hxx b/simgear/canvas/elements/CanvasImage.hxx index 9641c489..dedcaf01 100644 --- a/simgear/canvas/elements/CanvasImage.hxx +++ b/simgear/canvas/elements/CanvasImage.hxx @@ -101,6 +101,22 @@ namespace canvas */ void setSourceRect(const SGRect& sourceRect); + /** + * fill the specified rectangle of the image, with an RGB value + */ + void fillRect(const SGRect& rect, const std::string& color); + + /** + * fill the specified rectangle of the image, with an RGB value + */ + void fillRect(const SGRect& rect, const osg::Vec4& color); + + void setPixel(int x, int y, const std::string& color); + + void setPixel(int x, int y, const osg::Vec4& color); + + + // void setRow(int row, int offset, ) protected: enum ImageAttributes { diff --git a/simgear/math/SGVec2.hxx b/simgear/math/SGVec2.hxx index c0c6cb62..d9e4439f 100644 --- a/simgear/math/SGVec2.hxx +++ b/simgear/math/SGVec2.hxx @@ -193,8 +193,8 @@ min(S s, SGVec2 v) template inline SGVec2 -max(const SGVec2& v1, const SGVec2& v2) -{ v1 = simd4::max(v1.simd2(), v2.simd2()); return v1; } +max(SGVec2 v1, const SGVec2& v2) +{ v1.simd2() = simd4::max(v1.simd2(), v2.simd2()); return v1; } template inline SGVec2 diff --git a/simgear/nasal/cppbind/detail/from_nasal_helper.hxx b/simgear/nasal/cppbind/detail/from_nasal_helper.hxx index a582bfe6..afda9f24 100644 --- a/simgear/nasal/cppbind/detail/from_nasal_helper.hxx +++ b/simgear/nasal/cppbind/detail/from_nasal_helper.hxx @@ -207,6 +207,15 @@ namespace nasal return Vec2(vec[0], vec[1]); } + template + std::enable_if_t::value, Vec4> + from_nasal_helper(naContext c, naRef ref, const Vec4*) + { + auto vec = from_nasal_helper(c, ref, static_cast*>(0)); + return Vec4(vec[0], vec[1], vec[2], vec[3]); + } + + /** * Convert a Nasal vector with 4 elements ([x, y, w, h]) to an SGRect */ diff --git a/simgear/nasal/cppbind/detail/nasal_traits.hxx b/simgear/nasal/cppbind/detail/nasal_traits.hxx index 17e18ecd..228b2556 100644 --- a/simgear/nasal/cppbind/detail/nasal_traits.hxx +++ b/simgear/nasal/cppbind/detail/nasal_traits.hxx @@ -28,6 +28,7 @@ class SGWeakReferenced; template class SGSharedPtr; template class SGWeakPtr; template class SGVec2; +template class SGVec4; namespace boost { @@ -47,6 +48,9 @@ namespace osg class Vec2d; class Vec2f; class Vec2s; + + class Vec4f; + class Vec4d; } // The actual traits... @@ -55,6 +59,10 @@ namespace nasal template struct is_vec2: public std::false_type {}; + template + struct is_vec4: public std::false_type {}; + + #define SG_MAKE_TRAIT(templ,type,attr)\ template templ\ struct attr< type >: public std::true_type {}; @@ -65,6 +73,10 @@ SG_MAKE_TRAIT(<>, osg::Vec2d, is_vec2) SG_MAKE_TRAIT(<>, osg::Vec2f, is_vec2) SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2) +SG_MAKE_TRAIT(, SGVec4, is_vec4) +SG_MAKE_TRAIT(<>, osg::Vec4d, is_vec4) +SG_MAKE_TRAIT(<>, osg::Vec4f, is_vec4) + #undef SG_MAKE_TRAIT template From ec19dfc2f29222b734bca65d5b2fa2b2d992e51b Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 18 Dec 2019 23:43:40 +0000 Subject: [PATCH 52/52] Fix Windows build __PRETTY_FUNCTION__ is not portable, alas. --- simgear/canvas/elements/CanvasImage.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simgear/canvas/elements/CanvasImage.cxx b/simgear/canvas/elements/CanvasImage.cxx index c13f25ab..3964653b 100644 --- a/simgear/canvas/elements/CanvasImage.cxx +++ b/simgear/canvas/elements/CanvasImage.cxx @@ -900,7 +900,7 @@ SGRect intersectRect(const SGRect& a, const SGRect& b) break; default: - SG_LOG(SG_IO, SG_WARN, __PRETTY_FUNCTION__ << ": unsupported internal image format:" << format); + SG_LOG(SG_IO, SG_WARN, "Image::fillRect: unsupported internal image format:" << format); return; }