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