Random object and vegetation masking based on bitmap file.

Also adds a property controlling vegetation density.
This commit is contained in:
Stuart Buchanan
2012-02-05 23:05:57 +00:00
parent 9d20de068c
commit 593c884f14
11 changed files with 368 additions and 108 deletions
+92 -13
View File
@@ -30,7 +30,7 @@
#include <string.h> #include <string.h>
#include <map> #include <map>
#include <vector> #include <vector>
#include<string> #include <string>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include "mat.hxx" #include "mat.hxx"
@@ -118,14 +118,22 @@ void
SGMaterial::read_properties(const SGReaderWriterOptions* options, SGMaterial::read_properties(const SGReaderWriterOptions* options,
const SGPropertyNode *props) const SGPropertyNode *props)
{ {
// Gather the path(s) to the texture(s) std::vector<bool> dds;
std::vector<SGPropertyNode_ptr> textures = props->getChildren("texture"); std::vector<SGPropertyNode_ptr> textures = props->getChildren("texture");
for (unsigned int i = 0; i < textures.size(); i++) for (unsigned int i = 0; i < textures.size(); i++)
{ {
string tname = textures[i]->getStringValue(); string tname = textures[i]->getStringValue();
if (tname.empty()) { if (tname.empty()) {
tname = "unknown.rgb"; tname = "unknown.rgb";
} }
if (tname.rfind(".dds") == (tname.length() - 4)) {
dds.push_back(true);
} else {
dds.push_back(false);
}
SGPath tpath("Textures.high"); SGPath tpath("Textures.high");
tpath.append(tname); tpath.append(tname);
string fullTexPath = SGModelLib::findDataFile(tpath.str(), options); string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
@@ -152,6 +160,15 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
if (tname.empty()) { if (tname.empty()) {
tname = "unknown.rgb"; 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"); SGPath tpath("Textures.high");
tpath.append(tname); tpath.append(tname);
string fullTexPath = SGModelLib::findDataFile(tpath.str(), options); string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
@@ -160,6 +177,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
tpath.append(tname); tpath.append(tname);
fullTexPath = SGModelLib::findDataFile(tpath.str(), options); fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
} }
st.add_texture(fullTexPath, textures[j]->getIndex()); st.add_texture(fullTexPath, textures[j]->getIndex());
} }
@@ -176,6 +194,43 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
_status.push_back( st ); _status.push_back( st );
} }
std::vector<SGPropertyNode_ptr> 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); xsize = props->getDoubleValue("xsize", 0.0);
ysize = props->getDoubleValue("ysize", 0.0); ysize = props->getDoubleValue("ysize", 0.0);
wrapu = props->getBoolValue("wrapu", true); wrapu = props->getBoolValue("wrapu", true);
@@ -253,7 +308,6 @@ void
SGMaterial::init () SGMaterial::init ()
{ {
_status.clear(); _status.clear();
_current_ptr = 0;
xsize = 0; xsize = 0;
ysize = 0; ysize = 0;
wrapu = true; wrapu = true;
@@ -278,22 +332,47 @@ SGMaterial::init ()
effect = "Effects/terrain-default"; 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) { if (_status.size() == 0) {
SG_LOG( SG_GENERAL, SG_WARN, "No effect available."); SG_LOG( SG_GENERAL, SG_WARN, "No effect available.");
return 0; return 0;
} }
int i = n >= 0 ? n : _current_ptr;
if(!_status[i].effect_realized) { int i = triangleBin.getTextureIndex() % _status.size();
_status[i].effect->realizeTechniques(_status[i].options.get()); return get_effect(i);
_status[i].effect_realized = true; }
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) void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options)
+16 -4
View File
@@ -37,8 +37,11 @@
#include <map> #include <map>
#include <simgear/math/SGMath.hxx> #include <simgear/math/SGMath.hxx>
#include "Effect.hxx"
#include <simgear/scene/tgdb/SGTexturedTriangleBin.hxx>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osg/Texture2D>
namespace osg namespace osg
{ {
@@ -103,7 +106,14 @@ public:
/** /**
* Get the textured state. * 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. * Get the number of textures assigned to this material.
@@ -293,9 +303,6 @@ private:
// texture status // texture status
std::vector<_internal_state> _status; std::vector<_internal_state> _status;
// Round-robin counter
mutable unsigned int _current_ptr;
// texture size // texture size
double xsize, ysize; double xsize, ysize;
@@ -361,6 +368,10 @@ private:
// Tree texture, typically a strip of applicable tree textures // Tree texture, typically a strip of applicable tree textures
std::string tree_texture; std::string tree_texture;
// Object mask, a simple RGB texture used as a mask when placing
// random vegetation, objects and buildings
std::vector<osg::Texture2D*> _masks;
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Internal constructors and methods. // Internal constructors and methods.
@@ -369,6 +380,7 @@ private:
void read_properties(const simgear::SGReaderWriterOptions* options, void read_properties(const simgear::SGReaderWriterOptions* options,
const SGPropertyNode *props); const SGPropertyNode *props);
void buildEffectProperties(const simgear::SGReaderWriterOptions* options); void buildEffectProperties(const simgear::SGReaderWriterOptions* options);
simgear::Effect* get_effect(int i);
}; };
+10 -2
View File
@@ -55,6 +55,7 @@ using std::map;
SGMatModel::SGMatModel (const SGPropertyNode * node, double range_m) SGMatModel::SGMatModel (const SGPropertyNode * node, double range_m)
: _models_loaded(false), : _models_loaded(false),
_coverage_m2(node->getDoubleValue("coverage-m2", 1000000)), _coverage_m2(node->getDoubleValue("coverage-m2", 1000000)),
_spacing_m(node->getDoubleValue("spacing-m", 20)),
_range_m(range_m) _range_m(range_m)
{ {
// Sanity check // Sanity check
@@ -77,6 +78,8 @@ SGMatModel::SGMatModel (const SGPropertyNode * node, double range_m)
_heading_type = HEADING_BILLBOARD; _heading_type = HEADING_BILLBOARD;
} else if (hdg == "random") { } else if (hdg == "random") {
_heading_type = HEADING_RANDOM; _heading_type = HEADING_RANDOM;
} else if (hdg == "mask") {
_heading_type = HEADING_MASK;
} else { } else {
_heading_type = HEADING_FIXED; _heading_type = HEADING_FIXED;
SG_LOG(SG_INPUT, SG_ALERT, "Unknown heading type: " << hdg SG_LOG(SG_INPUT, SG_ALERT, "Unknown heading type: " << hdg
@@ -135,11 +138,11 @@ SGMatModel::load_models( SGPropertyNode *prop_root )
} }
osg::Node* 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 load_models( prop_root ); // comment this out if preloading models
int nModels = _models.size(); int nModels = _models.size();
return _models[mt_rand(&seed) * nModels].get(); return _models[mt_rand(seed) * nModels].get();
} }
double double
@@ -153,6 +156,11 @@ double SGMatModel::get_range_m() const
return _range_m; return _range_m;
} }
double SGMatModel::get_spacing_m() const
{
return _spacing_m;
}
double SGMatModel::get_randomized_range_m(mt* seed) const double SGMatModel::get_randomized_range_m(mt* seed) const
{ {
double lrand = mt_rand(seed); double lrand = mt_rand(seed);
+13 -2
View File
@@ -66,7 +66,8 @@ public:
enum HeadingType { enum HeadingType {
HEADING_FIXED, HEADING_FIXED,
HEADING_BILLBOARD, HEADING_BILLBOARD,
HEADING_RANDOM HEADING_RANDOM,
HEADING_MASK
}; };
/** /**
@@ -82,7 +83,7 @@ public:
* *
* @return A randomly select model from the variants. * @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 );
/** /**
@@ -99,6 +100,15 @@ public:
*/ */
double get_range_m () const; 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 * Get a randomized visual range
* *
@@ -136,6 +146,7 @@ private:
mutable std::vector<osg::ref_ptr<osg::Node> > _models; mutable std::vector<osg::ref_ptr<osg::Node> > _models;
mutable bool _models_loaded; mutable bool _models_loaded;
double _coverage_m2; double _coverage_m2;
double _spacing_m;
double _range_m; double _range_m;
HeadingType _heading_type; HeadingType _heading_type;
}; };
+5 -4
View File
@@ -27,12 +27,13 @@
class SGMatModelBin { class SGMatModelBin {
public: public:
struct MatModel { struct MatModel {
MatModel(const SGVec3f& p, SGMatModel *m, int l) : MatModel(const SGVec3f& p, SGMatModel *m, int l, float rot) :
position(p), model(m), lod(l) position(p), model(m), lod(l), rotation(rot)
{ } { }
SGVec3f position; SGVec3f position;
SGMatModel *model; SGMatModel *model;
int lod; int lod;
float rotation;
}; };
typedef std::vector<MatModel> MatModelList; typedef std::vector<MatModel> MatModelList;
@@ -41,8 +42,8 @@ public:
_models.push_back(model); _models.push_back(model);
} }
void insert(const SGVec3f& p, SGMatModel *m, int l) void insert(const SGVec3f& p, SGMatModel *m, int l, float rot)
{ insert(MatModel(p, m, l)); } { insert(MatModel(p, m, l, rot)); }
unsigned getNumModels() const unsigned getNumModels() const
{ return _models.size(); } { return _models.size(); }
+6 -1
View File
@@ -62,6 +62,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
SGMaterialLib* matlib = 0; SGMaterialLib* matlib = 0;
bool useRandomObjects = false; bool useRandomObjects = false;
bool useRandomVegetation = false; bool useRandomVegetation = false;
float vegetation_density = 1.0f;
const SGReaderWriterOptions* sgOptions; const SGReaderWriterOptions* sgOptions;
sgOptions = dynamic_cast<const SGReaderWriterOptions*>(options); sgOptions = dynamic_cast<const SGReaderWriterOptions*>(options);
if (sgOptions) { if (sgOptions) {
@@ -74,12 +75,16 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
useRandomVegetation useRandomVegetation
= propertyNode->getBoolValue("/sim/rendering/random-vegetation", = propertyNode->getBoolValue("/sim/rendering/random-vegetation",
useRandomVegetation); useRandomVegetation);
vegetation_density
= propertyNode->getFloatValue("/sim/rendering/vegetation-density",
vegetation_density);
} }
} }
osg::Node* result = SGLoadBTG(fileName, matlib, osg::Node* result = SGLoadBTG(fileName, matlib,
useRandomObjects, useRandomObjects,
useRandomVegetation); useRandomVegetation,
vegetation_density);
if (result) if (result)
return result; return result;
else else
+166 -58
View File
@@ -25,6 +25,8 @@
#include <osg/Array> #include <osg/Array>
#include <osg/Geometry> #include <osg/Geometry>
#include <osg/PrimitiveSet> #include <osg/PrimitiveSet>
#include <osg/Texture2D>
#include <stdio.h>
#include <simgear/math/sg_random.h> #include <simgear/math/sg_random.h>
#include <simgear/math/SGMath.hxx> #include <simgear/math/SGMath.hxx>
@@ -106,6 +108,7 @@ public:
// The points are offsetted away from the triangles in // The points are offsetted away from the triangles in
// offset * positive normal direction. // offset * positive normal direction.
void addRandomSurfacePoints(float coverage, float offset, void addRandomSurfacePoints(float coverage, float offset,
osg::Texture2D* object_mask,
std::vector<SGVec3f>& points) std::vector<SGVec3f>& points)
{ {
unsigned num = getNumTriangles(); unsigned num = getNumTriangles();
@@ -114,6 +117,9 @@ public:
SGVec3f v0 = getVertex(triangleRef[0]).vertex; SGVec3f v0 = getVertex(triangleRef[0]).vertex;
SGVec3f v1 = getVertex(triangleRef[1]).vertex; SGVec3f v1 = getVertex(triangleRef[1]).vertex;
SGVec3f v2 = getVertex(triangleRef[2]).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); SGVec3f normal = cross(v1 - v0, v2 - v0);
// Compute the area // Compute the area
@@ -140,7 +146,23 @@ public:
} }
float c = 1 - a - b; float c = 1 - a - b;
SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2; SGVec3f randomPoint = offsetVector + 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
// 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; unit -= coverage;
} }
} }
@@ -151,6 +173,8 @@ public:
void addRandomTreePoints(float wood_coverage, void addRandomTreePoints(float wood_coverage,
float tree_density, float tree_density,
float wood_size, float wood_size,
osg::Texture2D* object_mask,
float vegetation_density,
std::vector<SGVec3f>& points) std::vector<SGVec3f>& points)
{ {
unsigned num = getNumTriangles(); unsigned num = getNumTriangles();
@@ -159,6 +183,9 @@ public:
SGVec3f v0 = getVertex(triangleRef[0]).vertex; SGVec3f v0 = getVertex(triangleRef[0]).vertex;
SGVec3f v1 = getVertex(triangleRef[1]).vertex; SGVec3f v1 = getVertex(triangleRef[1]).vertex;
SGVec3f v2 = getVertex(triangleRef[2]).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); SGVec3f normal = cross(v1 - v0, v2 - v0);
// Compute the area // Compute the area
@@ -166,76 +193,125 @@ public:
if (area <= SGLimitsf::min()) if (area <= SGLimitsf::min())
continue; continue;
// For partial units of area, use a zombie door method to if (object_mask != NULL) {
// create the proper random chance of a point being created // For partial units of area, use a zombie door method to
// for this triangle // create the proper random chance of a point being created
float unit = area + mt_rand(&seed)*wood_coverage; // for this triangle
float unit = area + mt_rand(&seed)*wood_coverage;
int woodcount = (int) (unit / 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++) { for (int j = 0; j < woodcount; j++) {
float a = mt_rand(&seed);
float b = mt_rand(&seed);
if (wood_size < area) { if ( a + b > 1.0f ) {
// We need to place a wood within the triangle and populate it a = 1.0f - a;
b = 1.0f - b;
}
// Determine the center of the wood float c = 1.0f - a - b;
float x = mt_rand(&seed);
float y = mt_rand(&seed);
// Determine the size of this wood in m^2, and the number SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
// of trees in the wood SGVec2f texCoord = a*t0 + b*t1 + c*t2;
float ws = wood_size + wood_size * (mt_rand(&seed) - 0.5f);
unsigned total_trees = ws / tree_density;
float wood_length = sqrt(ws);
// From our wood size, work out the fraction on the two axis. // Check this random point against the object mask
// This will be used as a factor when placing trees in the wood. // green channel.
float x_tree_factor = wood_length / length(v1 -v0); osg::Image* img = object_mask->getImage();
float y_tree_factor = wood_length / length(v2 -v0); unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
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);
// 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;
}
float c = 1.0f - a - b;
SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
if (mt_rand(&seed) < img->getColor(x, y).g()) {
points.push_back(randomPoint); points.push_back(randomPoint);
} }
} else { }
// This triangle is too small to contain a complete wood, so just } else {
// distribute trees across it. // For partial units of area, use a zombie door method to
unsigned total_trees = area / tree_density; // 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 (unsigned k = 0; k <= total_trees; k++) { 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;
}
float a = mt_rand(&seed); for (int j = 0; j < woodcount; j++) {
float b = mt_rand(&seed);
if ( a + b > 1.0f ) { if (wood_size < area) {
a = 1.0f - a; // We need to place a wood within the triangle and populate it
b = 1.0f - b;
// 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 c = 1.0f - a - b; float wood_length = sqrt(ws);
SGVec3f randomPoint = a*v0 + b*v1 + c*v2; // From our wood size, work out the fraction on the two axis.
points.push_back(randomPoint); // 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++) {
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;
}
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;
for (unsigned k = 0; k <= total_trees; k++) {
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;
SGVec2f texCoord = a*t0 + b*t1 + c*t2;
points.push_back(randomPoint);
}
} }
} }
} }
@@ -243,7 +319,8 @@ public:
} }
void addRandomPoints(float coverage, void addRandomPoints(float coverage,
std::vector<SGVec3f>& points) osg::Texture2D* object_mask,
std::vector<std::pair<SGVec3f, float> >& points)
{ {
unsigned num = getNumTriangles(); unsigned num = getNumTriangles();
for (unsigned i = 0; i < num; ++i) { for (unsigned i = 0; i < num; ++i) {
@@ -251,6 +328,9 @@ public:
SGVec3f v0 = getVertex(triangleRef[0]).vertex; SGVec3f v0 = getVertex(triangleRef[0]).vertex;
SGVec3f v1 = getVertex(triangleRef[1]).vertex; SGVec3f v1 = getVertex(triangleRef[1]).vertex;
SGVec3f v2 = getVertex(triangleRef[2]).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); SGVec3f normal = cross(v1 - v0, v2 - v0);
// Compute the area // Compute the area
@@ -273,7 +353,25 @@ public:
} }
float c = 1 - a - b; float c = 1 - a - b;
SGVec3f randomPoint = a*v0 + b*v1 + c*v2; 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; num -= 1.0;
} }
} }
@@ -339,6 +437,16 @@ public:
osg::Geometry* buildGeometry() const osg::Geometry* buildGeometry() const
{ return buildGeometry(getTriangles()); } { 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: private:
// Random seed for the triangle. // Random seed for the triangle.
mt seed; mt seed;
+43 -11
View File
@@ -42,6 +42,8 @@
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <algorithm>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/io/sg_binobj.hxx> #include <simgear/io/sg_binobj.hxx>
#include <simgear/math/sg_geodesy.hxx> #include <simgear/math/sg_geodesy.hxx>
@@ -382,7 +384,7 @@ struct SGTileGeometryBin {
mat = matlib->find(i->first); mat = matlib->find(i->first);
eg = new EffectGeode; eg = new EffectGeode;
if (mat) if (mat)
eg->setEffect(mat->get_effect()); eg->setEffect(mat->get_effect(i->second));
eg->addDrawable(geometry); eg->addDrawable(geometry);
eg->runGenerators(geometry); // Generate extra data needed by effect eg->runGenerators(geometry); // Generate extra data needed by effect
if (group) if (group)
@@ -417,7 +419,7 @@ struct SGTileGeometryBin {
} }
std::vector<SGVec3f> randomPoints; std::vector<SGVec3f> randomPoints;
i->second.addRandomSurfacePoints(coverage, 3, randomPoints); i->second.addRandomSurfacePoints(coverage, 3, mat->get_object_mask(i->second), randomPoints);
std::vector<SGVec3f>::iterator j; std::vector<SGVec3f>::iterator j;
for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
float zombie = mt_rand(&seed); 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; SGMaterialTriangleMap::iterator i;
@@ -493,6 +495,8 @@ struct SGTileGeometryBin {
i->second.addRandomTreePoints(wood_coverage, i->second.addRandomTreePoints(wood_coverage,
mat->get_tree_density(), mat->get_tree_density(),
mat->get_wood_size(), mat->get_wood_size(),
mat->get_object_mask(i->second),
vegetation_density,
randomPoints); randomPoints);
std::vector<SGVec3f>::iterator k; std::vector<SGVec3f>::iterator k;
@@ -531,12 +535,29 @@ struct SGTileGeometryBin {
for (int k = 0; k < nObjects; k++) { for (int k = 0; k < nObjects; k++) {
SGMatModel * object = object_group->get_object(k); SGMatModel * object = object_group->get_object(k);
std::vector<SGVec3f> randomPoints; std::vector<std::pair<SGVec3f, float> > randomPoints;
i->second.addRandomPoints(object->get_coverage_m2(), randomPoints); i->second.addRandomPoints(object->get_coverage_m2(),
std::vector<SGVec3f>::iterator l; mat->get_object_mask(i->second),
randomPoints);
std::vector<std::pair<SGVec3f, float> >::iterator l;
for (l = randomPoints.begin(); l != randomPoints.end(); ++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);
}
} }
} }
} }
@@ -545,6 +566,7 @@ struct SGTileGeometryBin {
} }
} }
bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib) bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
{ {
if (!insertPtGeometry(obj, matlib)) if (!insertPtGeometry(obj, matlib))
@@ -579,7 +601,7 @@ typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
GetModelLODCoord> RandomObjectsQuadtree; GetModelLODCoord> RandomObjectsQuadtree;
osg::Node* 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; SGBinObject tile;
if (!tile.read_bin(path)) if (!tile.read_bin(path))
@@ -633,11 +655,13 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_object
i < tileGeometryBin.randomModels.getNumModels(); i++) { i < tileGeometryBin.randomModels.getNumModels(); i++) {
SGMatModelBin::MatModel obj SGMatModelBin::MatModel obj
= tileGeometryBin.randomModels.getMatModel(i); = tileGeometryBin.randomModels.getMatModel(i);
osg::Node* node = sgGetRandomModel(obj.model, seed);
osg::Node* node = sgGetRandomModel(obj.model, &seed);
// Create a matrix to place the object in the correct // Create a matrix to place the object in the correct
// location, and then apply the rotation matrix created // 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; osg::Matrix transformMat;
transformMat = osg::Matrix::translate(toOsg(obj.position)); transformMat = osg::Matrix::translate(toOsg(obj.position));
if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) { 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, transformMat.preMult(osg::Matrix::rotate(hdg,
osg::Vec3d(0.0, 0.0, 1.0))); 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 = osg::MatrixTransform* position =
new osg::MatrixTransform(transformMat); new osg::MatrixTransform(transformMat);
position->addChild(node); position->addChild(node);
@@ -660,7 +692,7 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_object
if (use_random_vegetation && matlib) { if (use_random_vegetation && matlib) {
// Now add some random forest. // Now add some random forest.
tileGeometryBin.computeRandomForest(matlib); tileGeometryBin.computeRandomForest(matlib, vegetation_density);
if (tileGeometryBin.randomForest.size() > 0) { if (tileGeometryBin.randomForest.size() > 0) {
forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity()); forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity());
+5 -1
View File
@@ -56,6 +56,10 @@ inline bool SGGenTile( const std::string&, const SGBucket& b,
} }
osg::Node* 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 #endif // _SG_OBJ_HXX
+1 -1
View File
@@ -59,7 +59,7 @@ void sgUserDataInit( SGPropertyNode *p ) {
root_props = 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 ); return obj->get_random_model( root_props, seed );
} }
+1 -1
View File
@@ -43,7 +43,7 @@ void sgUserDataInit(SGPropertyNode *p);
/** /**
* Get a random model. * Get a random model.
*/ */
osg::Node* sgGetRandomModel(SGMatModel *obj, mt seed); osg::Node* sgGetRandomModel(SGMatModel *obj, mt *seed);
namespace simgear namespace simgear
{ {