Random object and vegetation masking based on bitmap file.
Also adds a property controlling vegetation density.
This commit is contained in:
@@ -30,7 +30,7 @@
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include<string>
|
||||
#include <string>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#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<bool> dds;
|
||||
std::vector<SGPropertyNode_ptr> 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<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);
|
||||
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)
|
||||
|
||||
@@ -37,8 +37,11 @@
|
||||
#include <map>
|
||||
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include "Effect.hxx"
|
||||
#include <simgear/scene/tgdb/SGTexturedTriangleBin.hxx>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Texture2D>
|
||||
|
||||
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<osg::Texture2D*> _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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<osg::ref_ptr<osg::Node> > _models;
|
||||
mutable bool _models_loaded;
|
||||
double _coverage_m2;
|
||||
double _spacing_m;
|
||||
double _range_m;
|
||||
HeadingType _heading_type;
|
||||
};
|
||||
|
||||
@@ -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<MatModel> 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(); }
|
||||
|
||||
@@ -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<const SGReaderWriterOptions*>(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
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <osg/Array>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/Texture2D>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <simgear/math/sg_random.h>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
@@ -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<SGVec3f>& 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<SGVec3f>& 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);
|
||||
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);
|
||||
|
||||
for (int j = 0; j < woodcount; j++) {
|
||||
if ( a + b > 1.0f ) {
|
||||
a = 1.0f - a;
|
||||
b = 1.0f - b;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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;
|
||||
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 {
|
||||
// This triangle is too small to contain a complete wood, so just
|
||||
// distribute trees across it.
|
||||
unsigned total_trees = area / tree_density;
|
||||
}
|
||||
} 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++) {
|
||||
|
||||
for (unsigned k = 0; k <= total_trees; k++) {
|
||||
if (wood_size < area) {
|
||||
// We need to place a wood within the triangle and populate it
|
||||
|
||||
float a = mt_rand(&seed);
|
||||
float b = mt_rand(&seed);
|
||||
// Determine the center of the wood
|
||||
float x = mt_rand(&seed);
|
||||
float y = 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;
|
||||
|
||||
if (wood_size >= 1.0) {
|
||||
total_trees = total_trees * vegetation_density;
|
||||
}
|
||||
|
||||
float wood_length = sqrt(ws);
|
||||
|
||||
float c = 1.0f - a - b;
|
||||
// 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);
|
||||
|
||||
SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
|
||||
points.push_back(randomPoint);
|
||||
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,
|
||||
std::vector<SGVec3f>& points)
|
||||
osg::Texture2D* object_mask,
|
||||
std::vector<std::pair<SGVec3f, float> >& 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.
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/io/sg_binobj.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
@@ -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<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;
|
||||
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<SGVec3f>::iterator k;
|
||||
@@ -531,12 +535,29 @@ struct SGTileGeometryBin {
|
||||
for (int k = 0; k < nObjects; 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);
|
||||
std::vector<SGVec3f>::iterator l;
|
||||
i->second.addRandomPoints(object->get_coverage_m2(),
|
||||
mat->get_object_mask(i->second),
|
||||
randomPoints);
|
||||
std::vector<std::pair<SGVec3f, float> >::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<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
|
||||
GetModelLODCoord> 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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user