1 // future API - just run through once to convert from OSG to SG
2 // then we can use these triangle lists for random
3 // trees/lights/buildings/objects
4 struct SGTexturedTriangle
7 std::vector<SGVec3f> vertices;
8 std::vector<SGVec2f> texcoords;
11 struct SGBorderContour
21 SGTriangleInfo( const SGVec3d& center ) {
26 // API used to build the Info by the visitor
27 void addGeometry( osg::Geometry* g ) {
28 geometries.push_back(g);
31 void setMaterial( SGMaterial* m ) {
35 SGMaterial* getMaterial( void ) const {
39 // API used to get a specific texture or effect from a material. Materials can have
40 // multiple textures - use the floor of the x coordinate of the first vertes to select it.
41 // This will be constant, and give the same result each time to select one effect/texture per drawable.
42 int getTextureIndex( void ) const {
44 const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
46 const osg::Vec3 *v0 = &vertices->operator[](0);
47 texInfo = floor(v0->x());
53 void getTriangles( std::vector<SGTexturedTriangle>& tris )
55 const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
56 const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
58 int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
59 if ( numPrimitiveSets > 0 ) {
60 const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
61 unsigned int numIndices = ps->getNumIndices();
63 for ( unsigned int i=2; i<numIndices; i+= 3 ) {
64 SGTexturedTriangle tri;
66 tri.vertices.push_back( toSG(vertices->operator[](ps->index(i-2))) );
67 tri.vertices.push_back( toSG(vertices->operator[](ps->index(i-1))) );
68 tri.vertices.push_back( toSG(vertices->operator[](ps->index(i-0))) );
70 tri.texcoords.push_back( toSG(texcoords->operator[](ps->index(i-2))) );
71 tri.texcoords.push_back( toSG(texcoords->operator[](ps->index(i-1))) );
72 tri.texcoords.push_back( toSG(texcoords->operator[](ps->index(i-0))) );
77 void getBorderContours( std::vector<SGBorderContour>& border )
79 // each structure contains a list of target indexes and a count
80 int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
81 if ( numPrimitiveSets > 0 ) {
82 const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
84 const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
85 unsigned int numTriangles = ps->getNumIndices()/3;
87 // use a map for fast lookup map the segment as a 64 bit int
88 std::map<uint64_t, int> segCounter;
92 for ( unsigned int i=0; i<numTriangles; i+= 3 ) {
94 if ( ps->index(i+0) < ps->index(i+1) ) {
95 idx1 = ps->index(i+0);
96 idx2 = ps->index(i+1);
98 idx1 = ps->index(i+1);
99 idx2 = ps->index(i+0);
102 key=( (uint64_t)idx1<<32) | (uint64_t)idx2;
103 SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << key << std::dec << " count is " << segCounter[key] );
105 SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] );
108 if ( ps->index(i+1) < ps->index(i+2) ) {
109 idx1 = ps->index(i+1);
110 idx2 = ps->index(i+2);
112 idx1 = ps->index(i+2);
113 idx2 = ps->index(i+1);
116 key=( (uint64_t)idx1<<32) | (uint64_t)idx2;
117 SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << key << std::dec << " count is " << segCounter[key] );
119 SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] );
122 if ( ps->index(i+2) < ps->index(i+0) ) {
123 idx1 = ps->index(i+2);
124 idx2 = ps->index(i+0);
126 idx1 = ps->index(i+0);
127 idx2 = ps->index(i+2);
130 key=( (uint64_t)idx1<<32) | (uint64_t)idx2;
131 SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << key << std::dec << " count is " << segCounter[key] );
133 SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] );
136 // return all segments with count = 1 ( border )
137 std::map<uint64_t, int>::iterator segIt = segCounter.begin();
138 while ( segIt != segCounter.end() ) {
139 if ( segIt->second == 1 ) {
140 SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << segIt->first << std::dec << " count is " << segIt->second );
142 unsigned int iStart = segIt->first >> 32;
143 unsigned int iEnd = segIt->first & 0x00000000FFFFFFFF;
147 bc.start = toVec3d(toSG(vertices->operator[](iStart)));
148 bc.end = toVec3d(toSG(vertices->operator[](iEnd)));
149 border.push_back( bc );
155 // debug out - requires GDAL
159 SGGeod geodPos = SGGeod::fromCart(gbs_center);
160 SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
162 for ( unsigned int i=0; i<border.size(); i++ ){
163 // de-rotate and translate : todo - create a paralell vertex list so we just do this
164 // once per vertex, not for every triangle's use of the vertex
165 SGVec3d sgVStart = hlOr.backTransform( border[i].start) + gbs_center;
166 SGVec3d sgVEnd = hlOr.backTransform( border[i].end) + gbs_center;
168 // convert from cartesian to Geodetic, and save as a list of Geods for output
169 SGGeod gStart = SGGeod::fromCart(sgVStart);
170 SGGeod gEnd = SGGeod::fromCart(sgVEnd);
172 SGShapefile::FromSegment( gStart, gEnd, true, "./borders", mat->get_names()[0], "border" );
178 // Random buildings API - get num triangles, then get a triangle at index
179 unsigned int getNumTriangles( void ) const {
180 unsigned int num_triangles = 0;
182 if ( !geometries.empty() ) {
183 int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
184 if ( numPrimitiveSets > 0 ) {
185 const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
186 unsigned int numIndices = ps->getNumIndices();
187 num_triangles = numIndices/3;
191 return num_triangles;
194 void getTriangle(unsigned int i, std::vector<SGVec3f>& triVerts, std::vector<SGVec2f>& triTCs) const {
195 const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
196 const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
198 if ( !geometries.empty() ) {
199 int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
200 if ( numPrimitiveSets > 0 ) {
201 const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
204 triVerts.push_back( toSG(vertices->operator[](ps->index(idxStart+0))) );
205 triVerts.push_back( toSG(vertices->operator[](ps->index(idxStart+1))) );
206 triVerts.push_back( toSG(vertices->operator[](ps->index(idxStart+2))) );
208 triTCs.push_back( toSG(texcoords->operator[](ps->index(idxStart+0))) );
209 triTCs.push_back( toSG(texcoords->operator[](ps->index(idxStart+1))) );
210 triTCs.push_back( toSG(texcoords->operator[](ps->index(idxStart+2))) );
215 // random lights and trees - just get a list of points on where to add the light / tree
216 // TODO move this out - and handle in the random light / tree code
217 // just use generic triangle API.
218 void addRandomSurfacePoints(float coverage, float offset,
219 osg::Texture2D* object_mask,
220 std::vector<SGVec3f>& points)
222 if ( !geometries.empty() ) {
223 const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
224 const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
226 int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
227 if ( numPrimitiveSets > 0 ) {
228 const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
229 unsigned int numIndices = ps->getNumIndices();
231 for ( unsigned int i=2; i<numIndices; i+= 3 ) {
232 SGVec3f v0 = toSG(vertices->operator[](ps->index(i-2)));
233 SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1)));
234 SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0)));
236 SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2)));
237 SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1)));
238 SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0)));
240 SGVec3f normal = cross(v1 - v0, v2 - v0);
243 float area = 0.5f*length(normal);
244 if (area <= SGLimitsf::min())
247 // For partial units of area, use a zombie door method to
248 // create the proper random chance of a light being created
250 float unit = area + mt_rand(&seed)*coverage;
252 SGVec3f offsetVector = offset*normalize(normal);
253 // generate a light point for each unit of area
255 while ( coverage < unit ) {
256 float a = mt_rand(&seed);
257 float b = mt_rand(&seed);
264 SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
266 if (object_mask != NULL) {
267 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
269 // Check this random point against the object mask
271 osg::Image* img = object_mask->getImage();
272 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
273 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
275 if (mt_rand(&seed) < img->getColor(x, y).r()) {
276 points.push_back(randomPoint);
279 // No object mask, so simply place the object
280 points.push_back(randomPoint);
289 void addRandomTreePoints(float wood_coverage,
290 osg::Texture2D* object_mask,
291 float vegetation_density,
292 float cos_max_density_angle,
293 float cos_zero_density_angle,
294 std::vector<SGVec3f>& points,
295 std::vector<SGVec3f>& normals)
297 if ( !geometries.empty() ) {
298 const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
299 const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
301 int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
302 if ( numPrimitiveSets > 0 ) {
303 const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
304 unsigned int numIndices = ps->getNumIndices();
306 for ( unsigned int i=2; i<numIndices; i+= 3 ) {
307 SGVec3f v0 = toSG(vertices->operator[](ps->index(i-2)));
308 SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1)));
309 SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0)));
311 SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2)));
312 SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1)));
313 SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0)));
315 SGVec3f normal = cross(v1 - v0, v2 - v0);
317 // Ensure the slope isn't too steep by checking the
318 // cos of the angle between the slope normal and the
319 // vertical (conveniently the z-component of the normalized
320 // normal) and values passed in.
321 float alpha = normalize(normal).z();
322 float slope_density = 1.0;
324 if (alpha < cos_zero_density_angle)
325 continue; // Too steep for any vegetation
327 if (alpha < cos_max_density_angle) {
329 (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
333 float area = 0.5f*length(normal);
334 if (area <= SGLimitsf::min())
337 // Determine the number of trees, taking into account vegetation
338 // density (which is linear) and the slope density factor.
339 // Use a zombie door method to create the proper random chance
340 // of a tree being created for partial values.
341 int woodcount = (int) (vegetation_density * vegetation_density *
343 area / wood_coverage + mt_rand(&seed));
345 for (int j = 0; j < woodcount; j++) {
346 float a = mt_rand(&seed);
347 float b = mt_rand(&seed);
349 if ( a + b > 1.0f ) {
354 float c = 1.0f - a - b;
356 SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
358 if (object_mask != NULL) {
359 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
361 // Check this random point against the object mask
362 // green (for trees) channel.
363 osg::Image* img = object_mask->getImage();
364 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
365 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
367 if (mt_rand(&seed) < img->getColor(x, y).g()) {
368 // The red channel contains the rotation for this object
369 points.push_back(randomPoint);
370 normals.push_back(normalize(normal));
373 points.push_back(randomPoint);
374 normals.push_back(normalize(normal));
383 // debug : this will save the tile as a shapefile that can be viewed in QGIS.
384 // NOTE: this is really slow....
385 // remember - we need to de-rotate the tile, then translate back to gbs_center.
387 //dump the first triangle only of the first geometry, for now...
388 SG_LOG(SG_TERRAIN, SG_ALERT, "effect geode has " << geometries.size() << " geometries" );
390 const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
392 SG_LOG(SG_TERRAIN, SG_ALERT, " geometry has " << vertices->getNumElements() << " vertices" );
395 if ( !geometries.empty() ) {
396 int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
397 SG_LOG(SG_TERRAIN, SG_ALERT, " geometry has " << numPrimitiveSets << " primitive sets" );
399 if ( numPrimitiveSets > 0 ) {
400 const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
401 unsigned int numIndices = ps->getNumIndices();
403 // create the same quat we used to rotate here
404 // - use backTransform to go back to original node location
405 SGGeod geodPos = SGGeod::fromCart(gbs_center);
406 SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
408 SG_LOG(SG_TERRAIN, SG_ALERT, " primitive set has has " << numIndices << " indices" );
409 for ( unsigned int i=2; i<numIndices; i+= 3 ) {
410 if ( numIndices >= 3 ) {
411 unsigned int v0i = ps->index(i-2);
412 unsigned int v1i = ps->index(i-1);
413 unsigned int v2i = ps->index(i-0);
415 const osg::Vec3 *v0 = &vertices->operator[](v0i);
416 const osg::Vec3 *v1 = &vertices->operator[](v1i);
417 const osg::Vec3 *v2 = &vertices->operator[](v2i);
419 // de-rotate and translate : todo - create a paralell vertex list so we just do this
420 // once per vertex, not for every triangle's use of the vertex
421 SGVec3d vec0 = hlOr.backTransform( toVec3d(toSG(*v0))) + gbs_center;
422 SGVec3d vec1 = hlOr.backTransform( toVec3d(toSG(*v1))) + gbs_center;
423 SGVec3d vec2 = hlOr.backTransform( toVec3d(toSG(*v2))) + gbs_center;
425 // convert from cartesian to Geodetic, and save as a list of Geods for output
426 std::vector<SGGeod> triangle;
427 triangle.push_back( SGGeod::fromCart(vec0) );
428 triangle.push_back( SGGeod::fromCart(vec1) );
429 triangle.push_back( SGGeod::fromCart(vec2) );
431 SGShapefile::FromGeodList( triangle, true, "./triangles", mat->get_names()[0], "tri" );
444 std::vector<osg::Geometry*> geometries;
445 std::vector<int> polygon_border; // TODO
448 // This visitor will generate an SGTriangleInfo.
449 // currently, it looks like it could save multiple lists, which could be the case
450 // if multiple osg::geods are found with osg::Geometry.
451 // But right now, we store a single PrimitiveSet under a single EffectGeod.
452 // so the traversal should only find a single EffectGeod - building a single SGTriangleInfo
453 class GetNodeTriangles : public osg::NodeVisitor
456 GetNodeTriangles(const SGVec3d& c, std::vector<SGTriangleInfo>* nt) : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ), center(c), nodeTris(nt) {}
458 // This method gets called for every node in the scene
459 // graph. Check each node to see if it has user
460 // out target. If so, save the node's address.
461 virtual void apply( osg::Node& node )
463 EffectGeode* eg = dynamic_cast<EffectGeode*>(&node);
465 // get the material from the user info
466 SGTriangleInfo triInfo( center );
467 triInfo.setMaterial( eg->getMaterial() );
469 // let's find the drawables for this node
470 int numDrawables = eg->getNumDrawables();
471 for ( int i=0; i<numDrawables; i++ ) {
472 triInfo.addGeometry( eg->getDrawable(i)->asGeometry() );
475 nodeTris->push_back( triInfo );
478 // Keep traversing the rest of the scene graph.
484 std::vector<SGTriangleInfo>* nodeTris;