From 593c884f144ff593d46c031d5bb5d46abab545c6 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sun, 5 Feb 2012 23:05:57 +0000 Subject: [PATCH] Random object and vegetation masking based on bitmap file. Also adds a property controlling vegetation density. --- simgear/scene/material/mat.cxx | 111 +++++++-- simgear/scene/material/mat.hxx | 22 +- simgear/scene/material/matmodel.cxx | 12 +- simgear/scene/material/matmodel.hxx | 15 +- simgear/scene/tgdb/SGModelBin.hxx | 9 +- simgear/scene/tgdb/SGReaderWriterBTG.cxx | 7 +- simgear/scene/tgdb/SGTexturedTriangleBin.hxx | 230 ++++++++++++++----- simgear/scene/tgdb/obj.cxx | 56 ++++- simgear/scene/tgdb/obj.hxx | 6 +- simgear/scene/tgdb/userdata.cxx | 2 +- simgear/scene/tgdb/userdata.hxx | 2 +- 11 files changed, 366 insertions(+), 106 deletions(-) diff --git a/simgear/scene/material/mat.cxx b/simgear/scene/material/mat.cxx index f70a9a3b..0431fbff 100644 --- a/simgear/scene/material/mat.cxx +++ b/simgear/scene/material/mat.cxx @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include "mat.hxx" @@ -74,7 +74,7 @@ SGMaterial::_internal_state::_internal_state(Effect *e, bool l, { } -SGMaterial::_internal_state::_internal_state(Effect *e, const string &t, bool l, +SGMaterial::_internal_state::_internal_state(Effect *e, const string &t, bool l, const SGReaderWriterOptions* o) : effect(e), effect_realized(l), options(o) { @@ -118,14 +118,22 @@ void SGMaterial::read_properties(const SGReaderWriterOptions* options, const SGPropertyNode *props) { - // Gather the path(s) to the texture(s) + std::vector dds; std::vector textures = props->getChildren("texture"); for (unsigned int i = 0; i < textures.size(); i++) { string tname = textures[i]->getStringValue(); + if (tname.empty()) { tname = "unknown.rgb"; } + + if (tname.rfind(".dds") == (tname.length() - 4)) { + dds.push_back(true); + } else { + dds.push_back(false); + } + SGPath tpath("Textures.high"); tpath.append(tname); string fullTexPath = SGModelLib::findDataFile(tpath.str(), options); @@ -134,7 +142,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, tpath.append(tname); fullTexPath = SGModelLib::findDataFile(tpath.str(), options); } - + if (!fullTexPath.empty() ) { _internal_state st( NULL, fullTexPath, false, options ); _status.push_back( st ); @@ -152,6 +160,15 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, if (tname.empty()) { tname = "unknown.rgb"; } + + if (j == 0) { + if (tname.rfind(".dds") == (tname.length() - 4)) { + dds.push_back(true); + } else { + dds.push_back(false); + } + } + SGPath tpath("Textures.high"); tpath.append(tname); string fullTexPath = SGModelLib::findDataFile(tpath.str(), options); @@ -160,6 +177,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, tpath.append(tname); fullTexPath = SGModelLib::findDataFile(tpath.str(), options); } + st.add_texture(fullTexPath, textures[j]->getIndex()); } @@ -175,6 +193,43 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, _internal_state st( NULL, tpath.str(), true, options ); _status.push_back( st ); } + + std::vector masks = props->getChildren("object-mask"); + for (unsigned int i = 0; i < masks.size(); i++) + { + string omname = masks[i]->getStringValue(); + + if (! omname.empty()) { + SGPath ompath("Textures.high"); + ompath.append(omname); + string fullMaskPath = SGModelLib::findDataFile(ompath.str(), options); + + if (fullMaskPath.empty()) { + ompath = SGPath("Textures"); + ompath.append(omname); + fullMaskPath = SGModelLib::findDataFile(ompath.str(), options); + } + + osg::Image* image = osgDB::readImageFile(fullMaskPath, options); + if (image->valid()) + { + osg::Texture2D* object_mask = new osg::Texture2D; + + if (dds[i]) { + // Texture is a DDS. This is relevant for the object mask, as DDS + // textures have an origin at the bottom left rather than top + // left, therefore we flip the object mask vertically. + image->flipVertical(); + } + + object_mask->setImage(image); + object_mask->setDataVariance(osg::Object::STATIC); + object_mask->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + object_mask->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + _masks.push_back(object_mask); + } + } + } xsize = props->getDoubleValue("xsize", 0.0); ysize = props->getDoubleValue("ysize", 0.0); @@ -195,7 +250,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, string treeTexPath = props->getStringValue("tree-texture"); tree_texture = SGModelLib::findDataFile(treeTexPath, options); } - + // surface values for use with ground reactions solid = props->getBoolValue("solid", true); friction_factor = props->getDoubleValue("friction-factor", 1.0); @@ -253,7 +308,6 @@ void SGMaterial::init () { _status.clear(); - _current_ptr = 0; xsize = 0; ysize = 0; wrapu = true; @@ -278,22 +332,47 @@ SGMaterial::init () effect = "Effects/terrain-default"; } -Effect* SGMaterial::get_effect(int n) +Effect* SGMaterial::get_effect(int i) +{ + if(!_status[i].effect_realized) { + _status[i].effect->realizeTechniques(_status[i].options.get()); + _status[i].effect_realized = true; + } + return _status[i].effect.get(); +} + +Effect* SGMaterial::get_effect(SGTexturedTriangleBin triangleBin) { if (_status.size() == 0) { SG_LOG( SG_GENERAL, SG_WARN, "No effect available."); return 0; } - int i = n >= 0 ? n : _current_ptr; - if(!_status[i].effect_realized) { - _status[i].effect->realizeTechniques(_status[i].options.get()); - _status[i].effect_realized = true; + + int i = triangleBin.getTextureIndex() % _status.size(); + return get_effect(i); +} + +Effect* SGMaterial::get_effect() +{ + return get_effect(0); +} + + +osg::Texture2D* SGMaterial::get_object_mask(SGTexturedTriangleBin triangleBin) +{ + if (_status.size() == 0) { + 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, + unsigned int i = triangleBin.getTextureIndex() % _status.size(); + if (i < _masks.size()) { + return _masks[i]; + } else { + return 0; } - // XXX This business of returning a "random" alternate texture is - // really bogus. It means that the appearance of the terrain - // depends on the order in which it is paged in! - _current_ptr = (_current_ptr + 1) % _status.size(); - return _status[i].effect.get(); } void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options) diff --git a/simgear/scene/material/mat.hxx b/simgear/scene/material/mat.hxx index 06e72b72..26d78333 100644 --- a/simgear/scene/material/mat.hxx +++ b/simgear/scene/material/mat.hxx @@ -37,8 +37,11 @@ #include #include +#include "Effect.hxx" +#include #include +#include namespace osg { @@ -103,7 +106,14 @@ public: /** * Get the textured state. */ - simgear::Effect *get_effect(int n = -1); + simgear::Effect* get_effect(SGTexturedTriangleBin triangleBin); + simgear::Effect* get_effect(); + + /** + * Get the textured state. + */ + osg::Texture2D* get_object_mask(SGTexturedTriangleBin triangleBin); + /** * Get the number of textures assigned to this material. @@ -189,7 +199,7 @@ public: * @return the texture to use for trees. */ inline std::string get_tree_texture () const { return tree_texture; } - + /** * Return if the surface material is solid, if it is not solid, a fluid * can be assumed, that is usually water. @@ -293,9 +303,6 @@ private: // texture status std::vector<_internal_state> _status; - // Round-robin counter - mutable unsigned int _current_ptr; - // texture size double xsize, ysize; @@ -361,6 +368,10 @@ private: // Tree texture, typically a strip of applicable tree textures std::string tree_texture; + + // Object mask, a simple RGB texture used as a mask when placing + // random vegetation, objects and buildings + std::vector _masks; //////////////////////////////////////////////////////////////////// // Internal constructors and methods. @@ -369,6 +380,7 @@ private: void read_properties(const simgear::SGReaderWriterOptions* options, const SGPropertyNode *props); void buildEffectProperties(const simgear::SGReaderWriterOptions* options); + simgear::Effect* get_effect(int i); }; diff --git a/simgear/scene/material/matmodel.cxx b/simgear/scene/material/matmodel.cxx index 6bd1e189..2ff665d9 100644 --- a/simgear/scene/material/matmodel.cxx +++ b/simgear/scene/material/matmodel.cxx @@ -55,6 +55,7 @@ using std::map; SGMatModel::SGMatModel (const SGPropertyNode * node, double range_m) : _models_loaded(false), _coverage_m2(node->getDoubleValue("coverage-m2", 1000000)), + _spacing_m(node->getDoubleValue("spacing-m", 20)), _range_m(range_m) { // Sanity check @@ -77,6 +78,8 @@ SGMatModel::SGMatModel (const SGPropertyNode * node, double range_m) _heading_type = HEADING_BILLBOARD; } else if (hdg == "random") { _heading_type = HEADING_RANDOM; + } else if (hdg == "mask") { + _heading_type = HEADING_MASK; } else { _heading_type = HEADING_FIXED; SG_LOG(SG_INPUT, SG_ALERT, "Unknown heading type: " << hdg @@ -135,11 +138,11 @@ SGMatModel::load_models( SGPropertyNode *prop_root ) } osg::Node* -SGMatModel::get_random_model( SGPropertyNode *prop_root, mt seed ) +SGMatModel::get_random_model( SGPropertyNode *prop_root, mt* seed ) { load_models( prop_root ); // comment this out if preloading models int nModels = _models.size(); - return _models[mt_rand(&seed) * nModels].get(); + return _models[mt_rand(seed) * nModels].get(); } double @@ -153,6 +156,11 @@ double SGMatModel::get_range_m() const return _range_m; } +double SGMatModel::get_spacing_m() const +{ + return _spacing_m; +} + double SGMatModel::get_randomized_range_m(mt* seed) const { double lrand = mt_rand(seed); diff --git a/simgear/scene/material/matmodel.hxx b/simgear/scene/material/matmodel.hxx index 3e7d84a9..1687302e 100644 --- a/simgear/scene/material/matmodel.hxx +++ b/simgear/scene/material/matmodel.hxx @@ -66,7 +66,8 @@ public: enum HeadingType { HEADING_FIXED, HEADING_BILLBOARD, - HEADING_RANDOM + HEADING_RANDOM, + HEADING_MASK }; /** @@ -82,7 +83,7 @@ public: * * @return A randomly select model from the variants. */ - osg::Node *get_random_model( SGPropertyNode *prop_root, mt seed ); + osg::Node *get_random_model( SGPropertyNode *prop_root, mt *seed ); /** @@ -98,6 +99,15 @@ public: * @return The visual range. */ double get_range_m () const; + + /** + * Get the minimum spacing between this and any + * other objects in m + * + * @return The spacing in m. + */ + double get_spacing_m () const; + /** * Get a randomized visual range @@ -136,6 +146,7 @@ private: mutable std::vector > _models; mutable bool _models_loaded; double _coverage_m2; + double _spacing_m; double _range_m; HeadingType _heading_type; }; diff --git a/simgear/scene/tgdb/SGModelBin.hxx b/simgear/scene/tgdb/SGModelBin.hxx index bd61a948..914b52a6 100644 --- a/simgear/scene/tgdb/SGModelBin.hxx +++ b/simgear/scene/tgdb/SGModelBin.hxx @@ -27,12 +27,13 @@ class SGMatModelBin { public: struct MatModel { - MatModel(const SGVec3f& p, SGMatModel *m, int l) : - position(p), model(m), lod(l) + MatModel(const SGVec3f& p, SGMatModel *m, int l, float rot) : + position(p), model(m), lod(l), rotation(rot) { } SGVec3f position; SGMatModel *model; int lod; + float rotation; }; typedef std::vector MatModelList; @@ -41,8 +42,8 @@ public: _models.push_back(model); } - void insert(const SGVec3f& p, SGMatModel *m, int l) - { insert(MatModel(p, m, l)); } + void insert(const SGVec3f& p, SGMatModel *m, int l, float rot) + { insert(MatModel(p, m, l, rot)); } unsigned getNumModels() const { return _models.size(); } diff --git a/simgear/scene/tgdb/SGReaderWriterBTG.cxx b/simgear/scene/tgdb/SGReaderWriterBTG.cxx index a3a0fe5c..71d2a00d 100644 --- a/simgear/scene/tgdb/SGReaderWriterBTG.cxx +++ b/simgear/scene/tgdb/SGReaderWriterBTG.cxx @@ -62,6 +62,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName, SGMaterialLib* matlib = 0; bool useRandomObjects = false; bool useRandomVegetation = false; + float vegetation_density = 1.0f; const SGReaderWriterOptions* sgOptions; sgOptions = dynamic_cast(options); if (sgOptions) { @@ -74,12 +75,16 @@ SGReaderWriterBTG::readNode(const std::string& fileName, useRandomVegetation = propertyNode->getBoolValue("/sim/rendering/random-vegetation", useRandomVegetation); + vegetation_density + = propertyNode->getFloatValue("/sim/rendering/vegetation-density", + vegetation_density); } } osg::Node* result = SGLoadBTG(fileName, matlib, useRandomObjects, - useRandomVegetation); + useRandomVegetation, + vegetation_density); if (result) return result; else diff --git a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx index 3df4280a..999a64fc 100644 --- a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx +++ b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -106,6 +108,7 @@ public: // The points are offsetted away from the triangles in // offset * positive normal direction. void addRandomSurfacePoints(float coverage, float offset, + osg::Texture2D* object_mask, std::vector& points) { unsigned num = getNumTriangles(); @@ -114,6 +117,9 @@ public: SGVec3f v0 = getVertex(triangleRef[0]).vertex; SGVec3f v1 = getVertex(triangleRef[1]).vertex; SGVec3f v2 = getVertex(triangleRef[2]).vertex; + SGVec2f t0 = getVertex(triangleRef[0]).texCoord; + SGVec2f t1 = getVertex(triangleRef[1]).texCoord; + SGVec2f t2 = getVertex(triangleRef[2]).texCoord; SGVec3f normal = cross(v1 - v0, v2 - v0); // Compute the area @@ -140,8 +146,24 @@ public: } float c = 1 - a - b; SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2; - points.push_back(randomPoint); - unit -= coverage; + + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // red 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).r()) { + points.push_back(randomPoint); + } + } else { + // No object mask, so simply place the object + points.push_back(randomPoint); + } + unit -= coverage; } } } @@ -151,6 +173,8 @@ public: void addRandomTreePoints(float wood_coverage, float tree_density, float wood_size, + osg::Texture2D* object_mask, + float vegetation_density, std::vector& points) { unsigned num = getNumTriangles(); @@ -159,83 +183,135 @@ public: SGVec3f v0 = getVertex(triangleRef[0]).vertex; SGVec3f v1 = getVertex(triangleRef[1]).vertex; SGVec3f v2 = getVertex(triangleRef[2]).vertex; + SGVec2f t0 = getVertex(triangleRef[0]).texCoord; + SGVec2f t1 = getVertex(triangleRef[1]).texCoord; + SGVec2f t2 = getVertex(triangleRef[2]).texCoord; SGVec3f normal = cross(v1 - v0, v2 - v0); // Compute the area float area = 0.5f*length(normal); if (area <= SGLimitsf::min()) continue; - - // For partial units of area, use a zombie door method to - // create the proper random chance of a point being created - // for this triangle - float unit = area + mt_rand(&seed)*wood_coverage; - int woodcount = (int) (unit / wood_coverage); - - for (int j = 0; j < woodcount; j++) { - - if (wood_size < area) { - // We need to place a wood within the triangle and populate it - - // Determine the center of the wood - float x = mt_rand(&seed); - float y = mt_rand(&seed); + if (object_mask != NULL) { + // For partial units of area, use a zombie door method to + // create the proper random chance of a point being created + // for this triangle + float unit = area + mt_rand(&seed)*wood_coverage; + + // Vegetation density is linear, while we're creating woodland + // by area. + int woodcount = (int) (vegetation_density * + vegetation_density * + unit / wood_coverage); + + 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; + } - // Determine the size of this wood in m^2, and the number - // of trees in the wood - float ws = wood_size + wood_size * (mt_rand(&seed) - 0.5f); - unsigned total_trees = ws / tree_density; - float wood_length = sqrt(ws); + float c = 1.0f - a - b; + + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // green 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()) { + points.push_back(randomPoint); + } + } + } else { + // For partial units of area, use a zombie door method to + // create the proper random chance of a point being created + // for this triangle + float unit = area + mt_rand(&seed)*wood_coverage; + int woodcount = (int) (unit / wood_coverage); + + if (wood_size < 1.0) { + // A wood size of 0 is used for an even spread of woodland, + // where each wood contains a single tree. In this case we + // need to apply the vegetation_density to the wood count rather + // than the tree density. + woodcount = woodcount * vegetation_density; + } + + for (int j = 0; j < woodcount; j++) { + + if (wood_size < area) { + // We need to place a wood within the triangle and populate it + + // Determine the center of the wood + float x = mt_rand(&seed); + float y = mt_rand(&seed); + + // Determine the size of this wood in m^2, and the number + // of trees in the wood + float ws = wood_size + wood_size * (mt_rand(&seed) - 0.5f); + unsigned total_trees = ws / tree_density; + + if (wood_size >= 1.0) { + total_trees = total_trees * vegetation_density; + } + + float wood_length = sqrt(ws); - // From our wood size, work out the fraction on the two axis. - // This will be used as a factor when placing trees in the wood. - float x_tree_factor = wood_length / length(v1 -v0); - float y_tree_factor = wood_length / length(v2 -v0); + // From our wood size, work out the fraction on the two axis. + // This will be used as a factor when placing trees in the wood. + float x_tree_factor = wood_length / length(v1 -v0); + float y_tree_factor = wood_length / length(v2 -v0); - for (unsigned k = 0; k <= total_trees; k++) { + for (unsigned k = 0; k <= total_trees; k++) { - float a = x + x_tree_factor * (mt_rand(&seed) - 0.5f); - float b = y + y_tree_factor * (mt_rand(&seed) - 0.5f); + float a = x + x_tree_factor * (mt_rand(&seed) - 0.5f); + float b = y + y_tree_factor * (mt_rand(&seed) - 0.5f); - // In some cases, the triangle side lengths are so small that the - // tree_factors become so large as to make placing the tree within - // the triangle almost impossible. In this case, we place them - // randomly across the triangle. - if (a < 0.0f || a > 1.0f) a = mt_rand(&seed); - if (b < 0.0f || b > 1.0f) b = mt_rand(&seed); - - if ( a + b > 1.0f ) { - a = 1.0f - a; - b = 1.0f - b; - } + // In some cases, the triangle side lengths are so small that the + // tree_factors become so large as to make placing the tree within + // the triangle almost impossible. In this case, we place them + // randomly across the triangle. + if (a < 0.0f || a > 1.0f) a = mt_rand(&seed); + if (b < 0.0f || b > 1.0f) b = mt_rand(&seed); - float c = 1.0f - a - b; + 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; + points.push_back(randomPoint); + } + } else { + // This triangle is too small to contain a complete wood, so just + // distribute trees across it. + unsigned total_trees = area / tree_density; - SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + for (unsigned k = 0; k <= total_trees; k++) { - points.push_back(randomPoint); - } - } else { - // This triangle is too small to contain a complete wood, so just - // distribute trees across it. - unsigned total_trees = area / tree_density; + float a = mt_rand(&seed); + float b = mt_rand(&seed); - for (unsigned k = 0; k <= total_trees; k++) { + if ( a + b > 1.0f ) { + a = 1.0f - a; + b = 1.0f - b; + } - float a = mt_rand(&seed); - float b = mt_rand(&seed); + float c = 1.0f - a - b; - if ( a + b > 1.0f ) { - a = 1.0f - a; - b = 1.0f - b; + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + points.push_back(randomPoint); } - - float c = 1.0f - a - b; - - SGVec3f randomPoint = a*v0 + b*v1 + c*v2; - points.push_back(randomPoint); } } } @@ -243,7 +319,8 @@ public: } void addRandomPoints(float coverage, - std::vector& points) + osg::Texture2D* object_mask, + std::vector >& points) { unsigned num = getNumTriangles(); for (unsigned i = 0; i < num; ++i) { @@ -251,6 +328,9 @@ public: SGVec3f v0 = getVertex(triangleRef[0]).vertex; SGVec3f v1 = getVertex(triangleRef[1]).vertex; SGVec3f v2 = getVertex(triangleRef[2]).vertex; + SGVec2f t0 = getVertex(triangleRef[0]).texCoord; + SGVec2f t1 = getVertex(triangleRef[1]).texCoord; + SGVec2f t2 = getVertex(triangleRef[2]).texCoord; SGVec3f normal = cross(v1 - v0, v2 - v0); // Compute the area @@ -273,7 +353,25 @@ public: } float c = 1 - a - b; SGVec3f randomPoint = a*v0 + b*v1 + c*v2; - points.push_back(randomPoint); + + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // blue (for buildings) channel. Also check + // that they are more than spacing metres away from + // any other point. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); + unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).b()) { + // The red channel contains the rotation for this object + points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r())); + } + } else { + points.push_back(std::make_pair(randomPoint, mt_rand(&seed))); + } num -= 1.0; } } @@ -338,6 +436,16 @@ public: osg::Geometry* buildGeometry() const { return buildGeometry(getTriangles()); } + + int getTextureIndex() { + if (empty() || getNumTriangles() == 0) + return 0; + + triangle_ref triangleRef = getTriangleRef(0); + SGVec3f v0 = getVertex(triangleRef[0]).vertex; + + return floor(v0.x()); + } private: // Random seed for the triangle. diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index d080ff33..b7721b6c 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -42,6 +42,8 @@ #include +#include + #include #include #include @@ -382,7 +384,7 @@ struct SGTileGeometryBin { mat = matlib->find(i->first); eg = new EffectGeode; if (mat) - eg->setEffect(mat->get_effect()); + eg->setEffect(mat->get_effect(i->second)); eg->addDrawable(geometry); eg->runGenerators(geometry); // Generate extra data needed by effect if (group) @@ -417,7 +419,7 @@ struct SGTileGeometryBin { } std::vector randomPoints; - i->second.addRandomSurfacePoints(coverage, 3, randomPoints); + i->second.addRandomSurfacePoints(coverage, 3, mat->get_object_mask(i->second), randomPoints); std::vector::iterator j; for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { float zombie = mt_rand(&seed); @@ -445,7 +447,7 @@ struct SGTileGeometryBin { } } - void computeRandomForest(SGMaterialLib* matlib) + void computeRandomForest(SGMaterialLib* matlib, float vegetation_density) { SGMaterialTriangleMap::iterator i; @@ -493,6 +495,8 @@ struct SGTileGeometryBin { i->second.addRandomTreePoints(wood_coverage, mat->get_tree_density(), mat->get_wood_size(), + mat->get_object_mask(i->second), + vegetation_density, randomPoints); std::vector::iterator k; @@ -531,12 +535,29 @@ struct SGTileGeometryBin { for (int k = 0; k < nObjects; k++) { SGMatModel * object = object_group->get_object(k); - std::vector randomPoints; + std::vector > randomPoints; - i->second.addRandomPoints(object->get_coverage_m2(), randomPoints); - std::vector::iterator l; + i->second.addRandomPoints(object->get_coverage_m2(), + mat->get_object_mask(i->second), + randomPoints); + std::vector >::iterator l; for (l = randomPoints.begin(); l != randomPoints.end(); ++l) { - randomModels.insert(*l, object, (int)object->get_randomized_range_m(&seed)); + + // Only add the model if it is sufficiently far from the + // other models + bool close = false; + + for (unsigned i = 0; i < randomModels.getNumModels(); i++) { + float spacing = std::max(randomModels.getMatModel(i).model->get_spacing_m(), object->get_spacing_m()); + spacing = spacing * spacing; + + if (distSqr(randomModels.getMatModel(i).position, l->first) < spacing) { + close = true; + } + } + if (!close) { + randomModels.insert(l->first, object, (int)object->get_randomized_range_m(&seed), l->second); + } } } } @@ -544,6 +565,7 @@ struct SGTileGeometryBin { } } } + bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib) { @@ -579,7 +601,7 @@ typedef QuadTreeBuilder RandomObjectsQuadtree; osg::Node* -SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_objects, bool use_random_vegetation) +SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_objects, bool use_random_vegetation, float vegetation_density) { SGBinObject tile; if (!tile.read_bin(path)) @@ -632,12 +654,14 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_object for (unsigned int i = 0; i < tileGeometryBin.randomModels.getNumModels(); i++) { SGMatModelBin::MatModel obj - = tileGeometryBin.randomModels.getMatModel(i); - osg::Node* node = sgGetRandomModel(obj.model, seed); + = tileGeometryBin.randomModels.getMatModel(i); + + osg::Node* node = sgGetRandomModel(obj.model, &seed); // Create a matrix to place the object in the correct // location, and then apply the rotation matrix created - // above, with an additional random heading rotation if appropriate. + // above, with an additional random (or taken from + // the object mask) heading rotation if appropriate. osg::Matrix transformMat; transformMat = osg::Matrix::translate(toOsg(obj.position)); if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) { @@ -646,6 +670,14 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_object transformMat.preMult(osg::Matrix::rotate(hdg, osg::Vec3d(0.0, 0.0, 1.0))); } + + if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) { + // Rotate the object around the z axis. + double hdg = obj.rotation * M_PI * 2; + transformMat.preMult(osg::Matrix::rotate(hdg, + osg::Vec3d(0.0, 0.0, 1.0))); + } + osg::MatrixTransform* position = new osg::MatrixTransform(transformMat); position->addChild(node); @@ -660,7 +692,7 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_object if (use_random_vegetation && matlib) { // Now add some random forest. - tileGeometryBin.computeRandomForest(matlib); + tileGeometryBin.computeRandomForest(matlib, vegetation_density); if (tileGeometryBin.randomForest.size() > 0) { forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity()); diff --git a/simgear/scene/tgdb/obj.hxx b/simgear/scene/tgdb/obj.hxx index cdced8b6..66054a19 100644 --- a/simgear/scene/tgdb/obj.hxx +++ b/simgear/scene/tgdb/obj.hxx @@ -56,6 +56,10 @@ inline bool SGGenTile( const std::string&, const SGBucket& b, } osg::Node* -SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_objects, bool use_random_vegetation); +SGLoadBTG(const std::string& path, + SGMaterialLib *matlib, + bool use_random_objects, + bool use_random_vegetation, + float vegetation_density); #endif // _SG_OBJ_HXX diff --git a/simgear/scene/tgdb/userdata.cxx b/simgear/scene/tgdb/userdata.cxx index bf5823c9..10fa8f7e 100644 --- a/simgear/scene/tgdb/userdata.cxx +++ b/simgear/scene/tgdb/userdata.cxx @@ -59,7 +59,7 @@ void sgUserDataInit( SGPropertyNode *p ) { root_props = p; } -osg::Node* sgGetRandomModel(SGMatModel *obj, mt seed) { +osg::Node* sgGetRandomModel(SGMatModel *obj, mt *seed) { return obj->get_random_model( root_props, seed ); } diff --git a/simgear/scene/tgdb/userdata.hxx b/simgear/scene/tgdb/userdata.hxx index fa5209e6..553a8c59 100644 --- a/simgear/scene/tgdb/userdata.hxx +++ b/simgear/scene/tgdb/userdata.hxx @@ -43,7 +43,7 @@ void sgUserDataInit(SGPropertyNode *p); /** * Get a random model. */ -osg::Node* sgGetRandomModel(SGMatModel *obj, mt seed); +osg::Node* sgGetRandomModel(SGMatModel *obj, mt *seed); namespace simgear { -- 2.39.5