]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/SGTexturedTriangleBin.hxx
Fix #1783: repeated error message on console
[simgear.git] / simgear / scene / tgdb / SGTexturedTriangleBin.hxx
1 /* -*-c++-*-
2  *
3  * Copyright (C) 2006-2007 Mathias Froehlich 
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  * MA 02110-1301, USA.
19  *
20  */
21
22 #ifndef SG_TEXTURED_TRIANGLE_BIN_HXX
23 #define SG_TEXTURED_TRIANGLE_BIN_HXX
24
25 #define MAX_RANDOM_OBJECTS 100.0
26
27 #include <osg/Array>
28 #include <osg/Geometry>
29 #include <osg/PrimitiveSet>
30 #include <osg/Texture2D>
31 #include <osg/ref_ptr>
32 #include <stdio.h>
33
34 #include <simgear/math/sg_random.h>
35 #include <simgear/scene/util/OsgMath.hxx>
36 #include "SGTriangleBin.hxx"
37
38
39
40 struct SGVertNormTex {
41   SGVertNormTex() { 
42       tc_mask = 0;
43   }
44
45   struct less
46   {
47     inline bool tc_is_less ( const SGVertNormTex& l,
48                              const SGVertNormTex& r,
49                              int   idx ) const
50     {
51         if ( r.tc_mask & 1<<idx ) {
52             if ( l.tc_mask & 1<<idx ) {
53                 if (l.texCoord[idx] < r.texCoord[idx]) {
54                     return true;
55                 }
56             }
57         } 
58         
59         return false;
60     };
61     
62     inline bool operator() (const SGVertNormTex& l,
63                             const SGVertNormTex& r) const
64     {
65       if (l.vertex < r.vertex) return true;
66       else if (r.vertex < l.vertex) return false;
67       else if (l.normal < r.normal) return true;
68       else if (r.normal < l.normal) return false;
69       else if ( tc_is_less( l, r, 0 ) ) return true;
70       else if ( tc_is_less( r, l, 0 ) ) return false;
71       else if ( tc_is_less( l, r, 1 ) ) return true;
72       else if ( tc_is_less( r, l, 1 ) ) return false;
73       else if ( tc_is_less( l, r, 2 ) ) return true;
74       else if ( tc_is_less( r, l, 2 ) ) return false;
75       else if ( tc_is_less( l, r, 3 ) ) return true;
76       else return false;
77     }
78   };
79
80   void SetVertex( const SGVec3f& v )          { vertex = v; }
81   const SGVec3f& GetVertex( void ) const      { return vertex; }
82   
83   void SetNormal( const SGVec3f& n )          { normal = n; }
84   const SGVec3f& GetNormal( void ) const      { return normal; }
85   
86   void SetTexCoord( unsigned idx, const SGVec2f& tc ) { 
87       texCoord[idx] = tc; 
88       tc_mask |= 1 << idx; 
89   }
90   const SGVec2f& GetTexCoord( unsigned idx ) const { return texCoord[idx]; }
91
92 private:  
93   SGVec3f vertex;
94   SGVec3f normal;
95   SGVec2f texCoord[4];
96   
97   unsigned tc_mask;
98 };
99
100 // Use a DrawElementsUShort if there are few enough vertices,
101 // otherwise fallback to DrawElementsUInt. Hide the differences
102 // between the two from the rest of the code.
103 //
104 // We don't bother with DrawElementsUByte because that is generally
105 // not an advantage on modern hardware.
106 class DrawElementsFacade {
107 public:
108     DrawElementsFacade(void) : count(0)
109     {
110         _uintElements = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
111         _ushortElements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES);
112     }
113     
114     void push_back(unsigned val)
115     {
116         count++;
117         if (count < 65536) {
118             _ushortElements->push_back(val);
119         }
120         _uintElements->push_back(val);
121     }
122
123     osg::DrawElements* getDrawElements()
124     {
125         if (count > 65535) {
126             _ushortElements = 0;
127             return _uintElements;
128         } else {
129             _uintElements = 0;
130             return _ushortElements;
131         }
132     }
133 protected:
134     osg::ref_ptr<osg::DrawElementsUShort> _ushortElements;
135     osg::ref_ptr<osg::DrawElementsUInt> _uintElements;
136     unsigned count;
137 };
138
139 class SGTexturedTriangleBin : public SGTriangleBin<SGVertNormTex> {
140 public:
141   SGTexturedTriangleBin()
142   {
143     mt_init(&seed, 123);
144     has_sec_tcs = false;
145   }
146
147   // Computes and adds random surface points to the points list.
148   // The random points are computed with a density of (coverage points)/1
149   // The points are offsetted away from the triangles in
150   // offset * positive normal direction.
151   void addRandomSurfacePoints(float coverage, float offset,
152                               osg::Texture2D* object_mask,
153                               std::vector<SGVec3f>& points)
154   {
155     unsigned num = getNumTriangles();
156     for (unsigned i = 0; i < num; ++i) {
157       triangle_ref triangleRef = getTriangleRef(i);
158       SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
159       SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
160       SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
161       SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
162       SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
163       SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
164       SGVec3f normal = cross(v1 - v0, v2 - v0);
165       
166       // Compute the area
167       float area = 0.5f*length(normal);
168       if (area <= SGLimitsf::min())
169         continue;
170       
171       // For partial units of area, use a zombie door method to
172       // create the proper random chance of a light being created
173       // for this triangle
174       float unit = area + mt_rand(&seed)*coverage;
175       
176       SGVec3f offsetVector = offset*normalize(normal);
177       // generate a light point for each unit of area
178
179       while ( coverage < unit ) {
180
181         float a = mt_rand(&seed);
182         float b = mt_rand(&seed);
183
184         if ( a + b > 1 ) {
185           a = 1 - a;
186           b = 1 - b;
187         }
188         float c = 1 - a - b;
189         SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
190         
191         if (object_mask != NULL) {
192           SGVec2f texCoord = a*t0 + b*t1 + c*t2;
193           
194           // Check this random point against the object mask
195           // red channel.
196           osg::Image* img = object_mask->getImage();            
197           unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
198           unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
199           
200           if (mt_rand(&seed) < img->getColor(x, y).r()) {                
201             points.push_back(randomPoint);        
202           }                    
203         } else {      
204           // No object mask, so simply place the object  
205           points.push_back(randomPoint);        
206         }
207         unit -= coverage;        
208       }
209     }
210   }
211
212   // Computes and adds random surface points to the points list for tree
213   // coverage.
214   void addRandomTreePoints(float wood_coverage, 
215                            osg::Texture2D* object_mask,
216                            float vegetation_density,
217                            float cos_max_density_angle,
218                            float cos_zero_density_angle,
219                            std::vector<SGVec3f>& points,
220                            std::vector<SGVec3f>& normals)
221   {
222     unsigned num = getNumTriangles();
223     for (unsigned i = 0; i < num; ++i) {
224       triangle_ref triangleRef = getTriangleRef(i);
225       SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
226       SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
227       SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
228       SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
229       SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
230       SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
231       SGVec3f normal = cross(v1 - v0, v2 - v0);
232       
233       // Ensure the slope isn't too steep by checking the
234       // cos of the angle between the slope normal and the
235       // vertical (conveniently the z-component of the normalized
236       // normal) and values passed in.                   
237       float alpha = normalize(normal).z();
238       float slope_density = 1.0;
239       
240       if (alpha < cos_zero_density_angle) 
241         continue; // Too steep for any vegetation      
242       
243       if (alpha < cos_max_density_angle) {
244         slope_density = 
245           (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
246       }
247       
248       // Compute the area
249       float area = 0.5f*length(normal);
250       if (area <= SGLimitsf::min())
251         continue;
252         
253       // Determine the number of trees, taking into account vegetation
254       // density (which is linear) and the slope density factor.
255       // Use a zombie door method to create the proper random chance 
256       // of a tree being created for partial values.
257       int woodcount = (int) (vegetation_density * vegetation_density * 
258                              slope_density *
259                              area / wood_coverage + mt_rand(&seed));
260       
261       for (int j = 0; j < woodcount; j++) {
262         float a = mt_rand(&seed);
263         float b = mt_rand(&seed);
264
265         if ( a + b > 1.0f ) {
266           a = 1.0f - a;
267           b = 1.0f - b;
268         }
269
270         float c = 1.0f - a - b;
271
272         SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
273         
274         if (object_mask != NULL) {
275           SGVec2f texCoord = a*t0 + b*t1 + c*t2;
276           
277           // Check this random point against the object mask
278           // green (for trees) channel. 
279           osg::Image* img = object_mask->getImage();            
280           unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
281           unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
282           
283           if (mt_rand(&seed) < img->getColor(x, y).g()) {  
284             // The red channel contains the rotation for this object                                  
285             points.push_back(randomPoint);
286             normals.push_back(normalize(normal));
287           }
288         } else {
289           points.push_back(randomPoint);
290           normals.push_back(normalize(normal));
291         }                
292       }
293     }
294   }
295   
296    void addRandomPoints(double coverage, 
297                         double spacing,
298                         osg::Texture2D* object_mask,
299                         std::vector<std::pair<SGVec3f, float> >& points)
300   {
301     unsigned numtriangles = getNumTriangles();
302     for (unsigned i = 0; i < numtriangles; ++i) {
303       triangle_ref triangleRef = getTriangleRef(i);
304       SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
305       SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
306       SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
307       SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
308       SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
309       SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
310       SGVec3f normal = cross(v1 - v0, v2 - v0);
311       
312       // Compute the area
313       float area = 0.5f*length(normal);
314       if (area <= SGLimitsf::min())
315         continue;
316       
317       // for partial units of area, use a zombie door method to
318       // create the proper random chance of an object being created
319       // for this triangle.
320       double num = area / coverage + mt_rand(&seed);
321
322       if (num > MAX_RANDOM_OBJECTS) {
323           SG_LOG(SG_TERRAIN, SG_ALERT,
324                  "Per-triangle random object count exceeded limits ("
325                           << MAX_RANDOM_OBJECTS << ") " << num);
326           num = MAX_RANDOM_OBJECTS;
327       }
328
329       // place an object each unit of area
330       while ( num > 1.0 ) {
331         float a = mt_rand(&seed);
332         float b = mt_rand(&seed);
333         if ( a + b > 1 ) {
334           a = 1 - a;
335           b = 1 - b;
336         }
337         float c = 1 - a - b;
338         SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
339         
340         // Check that the point is sufficiently far from
341         // the edge of the triangle by measuring the distance
342         // from the three lines that make up the triangle.        
343         if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) > spacing) &&
344             ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) > spacing) &&
345             ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) > spacing)   )
346         {
347           if (object_mask != NULL) {
348             SGVec2f texCoord = a*t0 + b*t1 + c*t2;
349             
350             // Check this random point against the object mask
351             // blue (for buildings) channel. 
352             osg::Image* img = object_mask->getImage();            
353             unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
354             unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
355             
356             if (mt_rand(&seed) < img->getColor(x, y).b()) {  
357               // The red channel contains the rotation for this object                                  
358               points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r()));
359             }
360           } else {
361             points.push_back(std::make_pair(randomPoint, static_cast<float>(mt_rand(&seed))));
362           }        
363         }
364         num -= 1.0;
365       }
366     }
367   }
368
369   osg::Geometry* buildGeometry(const TriangleVector& triangles, bool useVBOs) const
370   {
371     // Do not build anything if there is nothing in here ...
372     if (empty() || triangles.empty())
373       return 0;
374
375     // FIXME: do not include all values here ...
376     osg::Vec3Array* vertices = new osg::Vec3Array;
377     osg::Vec3Array* normals = new osg::Vec3Array;
378     osg::Vec2Array* priTexCoords = new osg::Vec2Array;
379     osg::Vec2Array* secTexCoords = new osg::Vec2Array;
380
381     osg::Vec4Array* colors = new osg::Vec4Array;
382     colors->push_back(osg::Vec4(1, 1, 1, 1));
383
384     osg::Geometry* geometry = new osg::Geometry;
385     if (useVBOs) {
386         geometry->setUseDisplayList(false);
387         geometry->setUseVertexBufferObjects(true);
388     }
389     
390     geometry->setDataVariance(osg::Object::STATIC);
391     geometry->setVertexArray(vertices);
392     geometry->setNormalArray(normals);
393     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
394     geometry->setColorArray(colors);
395     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
396     if ( has_sec_tcs ) {
397         geometry->setTexCoordArray(0, priTexCoords);
398         geometry->setTexCoordArray(1, secTexCoords);
399     } else {
400         geometry->setTexCoordArray(0, priTexCoords);
401     }        
402
403     const unsigned invalid = ~unsigned(0);
404     std::vector<unsigned> indexMap(getNumVertices(), invalid);
405
406     DrawElementsFacade deFacade;
407     for (index_type i = 0; i < triangles.size(); ++i) {
408       triangle_ref triangle = triangles[i];
409       if (indexMap[triangle[0]] == invalid) {
410         indexMap[triangle[0]] = vertices->size();
411         vertices->push_back(toOsg(getVertex(triangle[0]).GetVertex()));
412         normals->push_back(toOsg(getVertex(triangle[0]).GetNormal()));
413         priTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(0)));
414         if ( has_sec_tcs ) {
415             secTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(1)));
416         }
417       }
418       deFacade.push_back(indexMap[triangle[0]]);
419
420       if (indexMap[triangle[1]] == invalid) {
421         indexMap[triangle[1]] = vertices->size();
422         vertices->push_back(toOsg(getVertex(triangle[1]).GetVertex()));
423         normals->push_back(toOsg(getVertex(triangle[1]).GetNormal()));
424         priTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(0)));
425         if ( has_sec_tcs ) {
426             secTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(1)));
427         }
428       }
429       deFacade.push_back(indexMap[triangle[1]]);
430
431       if (indexMap[triangle[2]] == invalid) {
432         indexMap[triangle[2]] = vertices->size();
433         vertices->push_back(toOsg(getVertex(triangle[2]).GetVertex()));
434         normals->push_back(toOsg(getVertex(triangle[2]).GetNormal()));
435         priTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(0)));
436         if ( has_sec_tcs ) {
437             secTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(1)));
438         }
439       }
440       deFacade.push_back(indexMap[triangle[2]]);
441     }
442     geometry->addPrimitiveSet(deFacade.getDrawElements());
443
444     return geometry;
445   }
446
447   osg::Geometry* buildGeometry(bool useVBOs) const
448   { return buildGeometry(getTriangles(), useVBOs); }
449   
450   int getTextureIndex() const
451   {
452     if (empty() || getNumTriangles() == 0)
453       return 0;
454
455     triangle_ref triangleRef = getTriangleRef(0);
456     SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
457       
458     return floor(v0.x());
459   }
460   
461   void hasSecondaryTexCoord( bool sec_tc ) { has_sec_tcs = sec_tc; }
462
463 private:
464   // Random seed for the triangle.
465   mt seed;
466   
467   // does the triangle array have secondary texture coordinates
468   bool has_sec_tcs;
469 };
470
471 #endif