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