]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/SGNodeTriangles.hxx
Fix #1783: repeated error message on console
[simgear.git] / simgear / scene / tgdb / SGNodeTriangles.hxx
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
5 {
6 public:
7     std::vector<SGVec3f> vertices;
8     std::vector<SGVec2f> texcoords;
9 };
10
11 struct SGBorderContour
12 {
13 public:
14     SGVec3d start;
15     SGVec3d end;
16 };
17
18 class SGTriangleInfo
19 {
20 public:
21     SGTriangleInfo( const SGVec3d& center ) {
22         gbs_center = center;
23         mt_init(&seed, 123);
24     }
25
26     // API used to build the Info by the visitor
27     void addGeometry( osg::Geometry* g ) { 
28         geometries.push_back(g); 
29     }
30     
31     void setMaterial( SGMaterial* m ) { 
32         mat = m; 
33     }
34
35     SGMaterial* getMaterial( void ) const {
36         return mat; 
37     }
38     
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 {
43         int texInfo = 0;
44         const osg::Vec3Array* vertices =  dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
45         if ( vertices ) {
46             const osg::Vec3 *v0 = &vertices->operator[](0);
47             texInfo = floor(v0->x()); 
48         }
49         return texInfo;
50     }
51     
52     // new API - TODO
53     void getTriangles( std::vector<SGTexturedTriangle>& tris )
54     {
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));
57         
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();
62             
63             for ( unsigned int i=2; i<numIndices; i+= 3 ) {
64                 SGTexturedTriangle tri;
65                 
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))) );
69                 
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))) );
73             }
74         }
75     }
76     
77     void getBorderContours( std::vector<SGBorderContour>& border )
78     {
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());
83
84             const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
85             unsigned int numTriangles = ps->getNumIndices()/3;
86
87             // use a map for fast lookup map the segment as a 64 bit int
88             std::map<uint64_t, int> segCounter;
89             uint32_t idx1, idx2;
90             uint64_t key;
91             
92             for ( unsigned int i=0; i<numTriangles; i+= 3 ) {
93                 // first seg
94                 if ( ps->index(i+0) < ps->index(i+1) ) {
95                     idx1 = ps->index(i+0);
96                     idx2 = ps->index(i+1);
97                 } else {
98                     idx1 = ps->index(i+1);
99                     idx2 = ps->index(i+0);                    
100                 }
101                 
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] );
104                 segCounter[key]++;
105                 SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] );
106                 
107                 // second seg
108                 if ( ps->index(i+1) < ps->index(i+2) ) {
109                     idx1 = ps->index(i+1);
110                     idx2 = ps->index(i+2);
111                 } else {
112                     idx1 = ps->index(i+2);
113                     idx2 = ps->index(i+1);             
114                 }
115                 
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] );
118                 segCounter[key]++;
119                 SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] );
120                 
121                 // third seg
122                 if ( ps->index(i+2) < ps->index(i+0) ) {
123                     idx1 = ps->index(i+2);
124                     idx2 = ps->index(i+0);
125                 } else {
126                     idx1 = ps->index(i+0);
127                     idx2 = ps->index(i+2);                    
128                 }
129                 
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] );
132                 segCounter[key]++;
133                 SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] );
134             }
135             
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 );
141                     
142                     unsigned int iStart = segIt->first >> 32;
143                     unsigned int iEnd   = segIt->first & 0x00000000FFFFFFFF;
144                     
145                     SGBorderContour bc;
146                     
147                     bc.start = toVec3d(toSG(vertices->operator[](iStart)));
148                     bc.end   = toVec3d(toSG(vertices->operator[](iEnd)));
149                     border.push_back( bc );
150                 }
151                 segIt++;
152             }
153             
154 #if 0
155             // debug out - requires GDAL
156             //
157             //
158             //
159             SGGeod  geodPos = SGGeod::fromCart(gbs_center);
160             SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
161
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;
167                     
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);
171                 
172                 SGShapefile::FromSegment( gStart, gEnd, true, "./borders", mat->get_names()[0], "border" );
173             }
174 #endif            
175         }
176     }
177         
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;
181
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;
188             }
189         }
190         
191         return num_triangles;
192     }
193     
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));
197
198         if ( !geometries.empty() ) {
199             int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
200             if ( numPrimitiveSets > 0 ) {
201                 const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
202                 int idxStart = i*3;
203                 
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))) );
207                 
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))) );
211             }
212         }
213     }
214     
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)
221     {
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));
225             
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();
230                 
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)));
235                     
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)));
239                     
240                     SGVec3f normal = cross(v1 - v0, v2 - v0);
241             
242                     // Compute the area
243                     float area = 0.5f*length(normal);
244                     if (area <= SGLimitsf::min())
245                         continue;
246             
247                     // For partial units of area, use a zombie door method to
248                     // create the proper random chance of a light being created
249                     // for this triangle
250                     float unit = area + mt_rand(&seed)*coverage;
251             
252                     SGVec3f offsetVector = offset*normalize(normal);
253                     // generate a light point for each unit of area
254             
255                     while ( coverage < unit ) {
256                         float a = mt_rand(&seed);
257                         float b = mt_rand(&seed);
258                 
259                         if ( a + b > 1 ) {
260                             a = 1 - a;
261                             b = 1 - b;
262                         }
263                         float c = 1 - a - b;
264                         SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
265                 
266                         if (object_mask != NULL) {
267                             SGVec2f texCoord = a*t0 + b*t1 + c*t2;
268                     
269                             // Check this random point against the object mask
270                             // red channel.
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();
274                     
275                             if (mt_rand(&seed) < img->getColor(x, y).r()) {                
276                                 points.push_back(randomPoint);        
277                             }                    
278                         } else {      
279                             // No object mask, so simply place the object  
280                             points.push_back(randomPoint);        
281                         }
282                         unit -= coverage;        
283                     }
284                 }
285             }
286         }
287     }
288     
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)
296     {
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));
300             
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();
305                 
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)));
310                     
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)));
314                     
315                     SGVec3f normal = cross(v1 - v0, v2 - v0);
316             
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;
323             
324                     if (alpha < cos_zero_density_angle) 
325                         continue; // Too steep for any vegetation      
326                         
327                     if (alpha < cos_max_density_angle) {
328                         slope_density = 
329                         (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
330                     }
331                     
332                     // Compute the area
333                     float area = 0.5f*length(normal);
334                     if (area <= SGLimitsf::min())
335                         continue;
336                 
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 * 
342                     slope_density *
343                     area / wood_coverage + mt_rand(&seed));
344                     
345                     for (int j = 0; j < woodcount; j++) {
346                         float a = mt_rand(&seed);
347                         float b = mt_rand(&seed);
348                         
349                         if ( a + b > 1.0f ) {
350                             a = 1.0f - a;
351                             b = 1.0f - b;
352                         }
353                         
354                         float c = 1.0f - a - b;
355                         
356                         SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
357                         
358                         if (object_mask != NULL) {
359                             SGVec2f texCoord = a*t0 + b*t1 + c*t2;
360                             
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();
366                             
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));
371                             }
372                         } else {
373                             points.push_back(randomPoint);
374                             normals.push_back(normalize(normal));
375                         }                
376                     }
377                 }
378             }
379         }
380     }
381     
382 #if 0    
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.
386     void dumpBorder() {
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" );
389         
390         const osg::Vec3Array* vertices =  dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
391         if ( vertices ) {
392             SG_LOG(SG_TERRAIN, SG_ALERT, " geometry has " << vertices->getNumElements() << " vertices" );
393         }
394         
395         if ( !geometries.empty() ) {
396             int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
397             SG_LOG(SG_TERRAIN, SG_ALERT, " geometry has " << numPrimitiveSets << " primitive sets" );
398             
399             if ( numPrimitiveSets > 0 ) {
400                 const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
401                 unsigned int numIndices = ps->getNumIndices();
402
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);
407                 
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);
414                     
415                         const osg::Vec3 *v0 = &vertices->operator[](v0i);
416                         const osg::Vec3 *v1 = &vertices->operator[](v1i);
417                         const osg::Vec3 *v2 = &vertices->operator[](v2i);
418                    
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;
424                     
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) );
430                     
431                         SGShapefile::FromGeodList( triangle, true, "./triangles", mat->get_names()[0], "tri" );
432                     }
433                 }
434             }            
435             
436         }
437     }
438 #endif    
439     
440 private:
441     mt seed;
442     SGMaterial* mat;
443     SGVec3d gbs_center;
444     std::vector<osg::Geometry*> geometries;
445     std::vector<int> polygon_border;    // TODO
446 };
447
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
454 {
455 public:
456     GetNodeTriangles(const SGVec3d& c, std::vector<SGTriangleInfo>* nt) : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ), center(c), nodeTris(nt) {}
457     
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 )
462     {
463         EffectGeode* eg = dynamic_cast<EffectGeode*>(&node);
464         if ( eg ) {
465             // get the material from the user info
466             SGTriangleInfo triInfo( center );
467             triInfo.setMaterial( eg->getMaterial() );
468
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() );
473             }
474             
475             nodeTris->push_back( triInfo );
476         }
477         
478         // Keep traversing the rest of the scene graph.
479         traverse( node );
480     }
481     
482 protected:
483     SGVec3d                         center;
484     std::vector<SGTriangleInfo>*    nodeTris;
485 };