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.
This commit is contained in:
@@ -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<SGVec3f>& points,
|
||||
std::vector<SGVec3f>& normals)
|
||||
{
|
||||
if ( !geometries.empty() ) {
|
||||
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
|
||||
const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(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; i<numIndices; i+= 3 ) {
|
||||
SGVec3f v0 = toSG(vertices->operator[](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....
|
||||
|
||||
@@ -216,6 +216,7 @@ public:
|
||||
float vegetation_density,
|
||||
float cos_max_density_angle,
|
||||
float cos_zero_density_angle,
|
||||
bool is_plantation,
|
||||
std::vector<SGVec3f>& points,
|
||||
std::vector<SGVec3f>& 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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user