diff --git a/simgear/scene/tgdb/SGNodeTriangles.hxx b/simgear/scene/tgdb/SGNodeTriangles.hxx index 1d3f72d2..c32dfd89 100644 --- a/simgear/scene/tgdb/SGNodeTriangles.hxx +++ b/simgear/scene/tgdb/SGNodeTriangles.hxx @@ -285,100 +285,154 @@ public: } } } - + void addRandomTreePoints(float wood_coverage, osg::Texture2D* object_mask, float vegetation_density, float cos_max_density_angle, float cos_zero_density_angle, + bool is_plantation, std::vector& points, std::vector& normals) { if ( !geometries.empty() ) { const osg::Vec3Array* vertices = dynamic_cast(geometries[0]->getVertexArray()); const osg::Vec2Array* texcoords = dynamic_cast(geometries[0]->getTexCoordArray(0)); - + int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); if ( numPrimitiveSets > 0 ) { const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); unsigned int numIndices = ps->getNumIndices(); - + for ( unsigned int i=2; ioperator[](ps->index(i-2))); SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1))); SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0))); - + SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2))); SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1))); SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0))); - + SGVec3f normal = cross(v1 - v0, v2 - v0); - + // Ensure the slope isn't too steep by checking the // cos of the angle between the slope normal and the // vertical (conveniently the z-component of the normalized // normal) and values passed in. float alpha = normalize(normal).z(); float slope_density = 1.0; - + if (alpha < cos_zero_density_angle) continue; // Too steep for any vegetation - + if (alpha < cos_max_density_angle) { slope_density = (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle); } - + // Compute the area float area = 0.5f*length(normal); if (area <= SGLimitsf::min()) continue; - - // Determine the number of trees, taking into account vegetation - // density (which is linear) and the slope density factor. - // Use a zombie door method to create the proper random chance - // of a tree being created for partial values. - int woodcount = (int) (vegetation_density * vegetation_density * - slope_density * - area / wood_coverage + mt_rand(&seed)); - - for (int j = 0; j < woodcount; j++) { - float a = mt_rand(&seed); - float b = mt_rand(&seed); - - if ( a + b > 1.0f ) { - a = 1.0f - a; - b = 1.0f - b; - } - - float c = 1.0f - a - b; - - SGVec3f randomPoint = a*v0 + b*v1 + c*v2; - - if (object_mask != NULL) { - SGVec2f texCoord = a*t0 + b*t1 + c*t2; - - // Check this random point against the object mask - // green (for trees) channel. - osg::Image* img = object_mask->getImage(); - unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); - unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); - - if (mt_rand(&seed) < img->getColor(x, y).g()) { - // The red channel contains the rotation for this object - points.push_back(randomPoint); - normals.push_back(normalize(normal)); + + if (!is_plantation) { + // Determine the number of trees, taking into account vegetation + // density (which is linear) and the slope density factor. + // Use a zombie door method to create the proper random chance + // of a tree being created for partial values. + int woodcount = (int) (vegetation_density * vegetation_density * + slope_density * + area / wood_coverage + mt_rand(&seed)); + for (int j = 0; j < woodcount; j++) { + // Use barycentric coordinates + float a = mt_rand(&seed); + float b = mt_rand(&seed); + + if ( a + b > 1.0f ) { + a = 1.0f - a; + b = 1.0f - b; + } + + float c = 1.0f - a - b; + + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // green (for trees) channel. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); + unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + + } } - } else { - points.push_back(randomPoint); - normals.push_back(normalize(normal)); - } + } else { // regularly-spaced vegetation + // separate vegetation in integral 1m units + int separation = (int) ceil(sqrt(wood_coverage)); + float max_x = ceil(max(max(v1.x(),v2.x()),v0.x())); + float min_x = floor(min(min(v1.x(),v2.x()),v0.x())); + float max_y = ceil(max(max(v1.y(),v2.y()),v0.y())); + float min_y = floor(min(min(v1.y(),v2.y()),v0.y())); + + /* equation of the plane ax+by+cz+d=0, need d */ + + float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z()); + /* Now loop over a grid, skipping points not in the triangle */ + int x_steps = (int) (max_x - min_x)/separation; + int y_steps = (int) (max_y - min_y)/separation; + SGVec2f v02d = SGVec2f(v0.x(),v0.y()); + SGVec2f v12d = SGVec2f(v1.x(),v1.y()); + SGVec2f v22d = SGVec2f(v2.x(),v2.y()); + + for (int jx = 0; jx < x_steps; jx++) { + float ptx = min_x + jx * separation; + + for (int jy = 0; jy < y_steps; jy++) { + float pty = min_y + jy * separation; + SGVec2f newpt = SGVec2f(ptx,pty); + if (!point_in_triangle(newpt,v02d,v12d,v22d)) + continue; + + // z = (-ax-by-d)/c; c is not zero as + // that would be alpha of 1.0 + + float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z(); + SGVec3f randomPoint = SGVec3f(ptx,pty,ptz); + + if (object_mask != NULL) { + // Check this point against the object mask + // green (for trees) channel. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * newpt.x()) % img->s(); + unsigned int y = (int) (img->t() * newpt.y()) % img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } + } } } } } } - + #if 0 // debug : this will save the tile as a shapefile that can be viewed in QGIS. // NOTE: this is really slow.... diff --git a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx index 47939a75..3c8132e1 100644 --- a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx +++ b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx @@ -216,6 +216,7 @@ public: float vegetation_density, float cos_max_density_angle, float cos_zero_density_angle, + bool is_plantation, std::vector& points, std::vector& normals) { @@ -249,50 +250,104 @@ public: float area = 0.5f*length(normal); if (area <= SGLimitsf::min()) continue; - - // Determine the number of trees, taking into account vegetation - // density (which is linear) and the slope density factor. - // Use a zombie door method to create the proper random chance - // of a tree being created for partial values. - int woodcount = (int) (vegetation_density * vegetation_density * - slope_density * - area / wood_coverage + mt_rand(&seed)); + if (!is_plantation) { + // Determine the number of trees, taking into account vegetation + // density (which is linear) and the slope density factor. + // Use a zombie door method to create the proper random chance + // of a tree being created for partial values. + int woodcount = (int) (vegetation_density * vegetation_density * + slope_density * + area / wood_coverage + mt_rand(&seed)); - for (int j = 0; j < woodcount; j++) { - float a = mt_rand(&seed); - float b = mt_rand(&seed); + for (int j = 0; j < woodcount; j++) { + float a = mt_rand(&seed); + float b = mt_rand(&seed); - if ( a + b > 1.0f ) { - a = 1.0f - a; - b = 1.0f - b; - } - - float c = 1.0f - a - b; - - SGVec3f randomPoint = a*v0 + b*v1 + c*v2; - - if (object_mask != NULL) { - SGVec2f texCoord = a*t0 + b*t1 + c*t2; - - // Check this random point against the object mask - // green (for trees) channel. - osg::Image* img = object_mask->getImage(); - unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); - unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); - - if (mt_rand(&seed) < img->getColor(x, y).g()) { - // The red channel contains the rotation for this object - points.push_back(randomPoint); - normals.push_back(normalize(normal)); + if ( a + b > 1.0f ) { + a = 1.0f - a; + b = 1.0f - b; } - } else { - points.push_back(randomPoint); - normals.push_back(normalize(normal)); - } + + float c = 1.0f - a - b; + + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // green (for trees) channel. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); + unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } + } else { // regularly-spaced vegetation + + int separation = (int) ceil(sqrt(wood_coverage)); + float max_x = ceil(max(max(v1.x(),v2.x()),v0.x())); + float min_x = floor(min(min(v1.x(),v2.x()),v0.x())); + float max_y = ceil(max(max(v1.y(),v2.y()),v0.y())); + float min_y = floor(min(min(v1.y(),v2.y()),v0.y())); + + // equation of the plane ax+by+cz+d=0, need d + + float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z()); + + // Now loop over a grid, skipping points not in the triangle + + int x_steps = (int) (max_x - min_x)/separation; + int y_steps = (int) (max_y - min_y)/separation; + SGVec2f v02d = SGVec2f(v0.x(),v0.y()); + SGVec2f v12d = SGVec2f(v1.x(),v1.y()); + SGVec2f v22d = SGVec2f(v2.x(),v2.y()); + + for (int jx = 0; jx < x_steps; jx++) { + float ptx = min_x + jx * separation; + + for (int jy = 0; jy < y_steps; jy++) { + float pty = min_y + jy * separation; + SGVec2f newpt = SGVec2f(ptx,pty); + if (!point_in_triangle(newpt,v02d,v12d,v22d)) + continue; + + // z = (-ax-by-d)/c; c is not zero as + // that would be alpha of 1.0 + + float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z(); + SGVec3f randomPoint = SGVec3f(ptx,pty,ptz); + + if (object_mask != NULL) { + // Check this point against the object mask + // green (for trees) channel. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * newpt.x()) % img->s(); + unsigned int y = (int) (img->t() * newpt.y()) % img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } else { + points.push_back(randomPoint); + normals.push_back(normalize(normal)); + } + } + } } } } - + void addRandomPoints(double coverage, double spacing, osg::Texture2D* object_mask, diff --git a/simgear/scene/tgdb/SGTileDetailsCallback.hxx b/simgear/scene/tgdb/SGTileDetailsCallback.hxx index 6a16c5da..2c46aef5 100644 --- a/simgear/scene/tgdb/SGTileDetailsCallback.hxx +++ b/simgear/scene/tgdb/SGTileDetailsCallback.hxx @@ -753,6 +753,7 @@ public: vegetation_density, mat->get_cos_tree_max_density_slope_angle(), mat->get_cos_tree_zero_density_slope_angle(), + mat->get_is_plantation(), randomPoints, randomPointNormals);