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 {
SGVertNormTexSGVertNormTex41   SGVertNormTex() {
42       tc_mask = 0;
43   }
44 
45   struct less
46   {
tc_is_lessSGVertNormTex::less47     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 
operator ()SGVertNormTex::less62     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 
SetVertexSGVertNormTex80   void SetVertex( const SGVec3f& v )          { vertex = v; }
GetVertexSGVertNormTex81   const SGVec3f& GetVertex( void ) const      { return vertex; }
82 
SetNormalSGVertNormTex83   void SetNormal( const SGVec3f& n )          { normal = n; }
GetNormalSGVertNormTex84   const SGVec3f& GetNormal( void ) const      { return normal; }
85 
SetTexCoordSGVertNormTex86   void SetTexCoord( unsigned idx, const SGVec2f& tc ) {
87       texCoord[idx] = tc;
88       tc_mask |= 1 << idx;
89   }
GetTexCoordSGVertNormTex90   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:
DrawElementsFacade(void)108     DrawElementsFacade(void) : count(0)
109     {
110         _uintElements = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
111         _ushortElements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES);
112     }
113 
push_back(unsigned val)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 
getDrawElements()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:
SGTexturedTriangleBin()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.
addRandomSurfacePoints(float coverage,float offset,osg::Texture2D * object_mask,std::vector<SGVec3f> & points)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.
addRandomTreePoints(float wood_coverage,osg::Texture2D * object_mask,float vegetation_density,float cos_max_density_angle,float cos_zero_density_angle,bool is_plantation,std::vector<SGVec3f> & points,std::vector<SGVec3f> & normals)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                            bool is_plantation,
220                            std::vector<SGVec3f>& points,
221 			   std::vector<SGVec3f>& normals)
222   {
223     unsigned num = getNumTriangles();
224     for (unsigned i = 0; i < num; ++i) {
225       triangle_ref triangleRef = getTriangleRef(i);
226       SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
227       SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
228       SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
229       SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
230       SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
231       SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
232       SGVec3f normal = cross(v1 - v0, v2 - v0);
233 
234       // Ensure the slope isn't too steep by checking the
235       // cos of the angle between the slope normal and the
236       // vertical (conveniently the z-component of the normalized
237       // normal) and values passed in.
238       float alpha = normalize(normal).z();
239       float slope_density = 1.0;
240 
241       if (alpha < cos_zero_density_angle)
242         continue; // Too steep for any vegetation
243 
244       if (alpha < cos_max_density_angle) {
245         slope_density =
246           (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
247       }
248 
249       // Compute the area
250       float area = 0.5f*length(normal);
251       if (area <= SGLimitsf::min())
252         continue;
253       if (is_plantation) {          // regularly-spaced vegetation
254 
255         int separation = (int) ceil(sqrt(wood_coverage));
256         float max_x = ceil(max(max(v1.x(),v2.x()),v0.x()));
257         float min_x = floor(min(min(v1.x(),v2.x()),v0.x()));
258         float max_y = ceil(max(max(v1.y(),v2.y()),v0.y()));
259         float min_y = floor(min(min(v1.y(),v2.y()),v0.y()));
260 
261         // equation of the plane ax+by+cz+d=0, need d
262 
263         float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z());
264 
265         // Now loop over a grid, skipping points not in the triangle
266 
267         int x_steps = (int) (max_x - min_x)/separation;
268         int y_steps = (int) (max_y - min_y)/separation;
269         SGVec2f v02d = SGVec2f(v0.x(),v0.y());
270         SGVec2f v12d = SGVec2f(v1.x(),v1.y());
271         SGVec2f v22d = SGVec2f(v2.x(),v2.y());
272 
273         for (int jx = 0; jx < x_steps; jx++) {
274           float ptx = min_x + jx * separation;
275 
276           for (int jy = 0; jy < y_steps; jy++) {
277             float pty = min_y + jy * separation;
278             SGVec2f newpt = SGVec2f(ptx,pty);
279             if (!point_in_triangle(newpt,v02d,v12d,v22d))
280               continue;
281 
282             // z = (-ax-by-d)/c; c is not zero as
283             // that would be alpha of 1.0
284 
285             float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z();
286             SGVec3f randomPoint = SGVec3f(ptx,pty,ptz);
287 
288             if (object_mask != NULL) {
289               // Check this point against the object mask
290               // green (for trees) channel.
291               osg::Image* img = object_mask->getImage();
292               unsigned int x = (int) (img->s() * newpt.x()) % img->s();
293               unsigned int y = (int) (img->t() * newpt.y()) % img->t();
294 
295               if (mt_rand(&seed) < img->getColor(x, y).g()) {
296                 // The red channel contains the rotation for this object
297                 points.push_back(randomPoint);
298                 normals.push_back(normalize(normal));
299               }
300             } else {
301               points.push_back(randomPoint);
302               normals.push_back(normalize(normal));
303             }
304           }
305         }
306       } else {
307         // Determine the number of trees, taking into account vegetation
308         // density (which is linear) and the slope density factor.
309         // Use a zombie door method to create the proper random chance
310         // of a tree being created for partial values.
311         int woodcount = (int) (vegetation_density * vegetation_density *
312                                slope_density *
313                                area / wood_coverage + mt_rand(&seed));
314 
315         for (int j = 0; j < woodcount; j++) {
316           float a = mt_rand(&seed);
317           float b = mt_rand(&seed);
318 
319           if ( a + b > 1.0f ) {
320             a = 1.0f - a;
321             b = 1.0f - b;
322           }
323 
324           float c = 1.0f - a - b;
325 
326           SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
327 
328           if (object_mask != NULL) {
329             SGVec2f texCoord = a*t0 + b*t1 + c*t2;
330 
331             // Check this random point against the object mask
332             // green (for trees) channel.
333             osg::Image* img = object_mask->getImage();
334             unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
335             unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
336 
337             if (mt_rand(&seed) < img->getColor(x, y).g()) {
338               // The red channel contains the rotation for this object
339               points.push_back(randomPoint);
340               normals.push_back(normalize(normal));
341             }
342           } else {
343             points.push_back(randomPoint);
344             normals.push_back(normalize(normal));
345           }
346         }
347       }
348     }
349   }
350 
addRandomPoints(double coverage,double spacing,osg::Texture2D * object_mask,std::vector<std::pair<SGVec3f,float>> & points)351    void addRandomPoints(double coverage,
352                         double spacing,
353                         osg::Texture2D* object_mask,
354                         std::vector<std::pair<SGVec3f, float> >& points)
355   {
356     unsigned numtriangles = getNumTriangles();
357     for (unsigned i = 0; i < numtriangles; ++i) {
358       triangle_ref triangleRef = getTriangleRef(i);
359       SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
360       SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
361       SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
362       SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
363       SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
364       SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
365       SGVec3f normal = cross(v1 - v0, v2 - v0);
366 
367       // Compute the area
368       float area = 0.5f*length(normal);
369       if (area <= SGLimitsf::min())
370         continue;
371 
372       // for partial units of area, use a zombie door method to
373       // create the proper random chance of an object being created
374       // for this triangle.
375       double num = area / coverage + mt_rand(&seed);
376 
377       if (num > MAX_RANDOM_OBJECTS) {
378           SG_LOG(SG_TERRAIN, SG_ALERT,
379                  "Per-triangle random object count exceeded limits ("
380         		  << MAX_RANDOM_OBJECTS << ") " << num);
381           num = MAX_RANDOM_OBJECTS;
382       }
383 
384       // place an object each unit of area
385       while ( num > 1.0 ) {
386         float a = mt_rand(&seed);
387         float b = mt_rand(&seed);
388         if ( a + b > 1 ) {
389           a = 1 - a;
390           b = 1 - b;
391         }
392         float c = 1 - a - b;
393         SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
394 
395         // Check that the point is sufficiently far from
396         // the edge of the triangle by measuring the distance
397         // from the three lines that make up the triangle.
398         if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) > spacing) &&
399             ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) > spacing) &&
400             ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) > spacing)   )
401         {
402           if (object_mask != NULL) {
403             SGVec2f texCoord = a*t0 + b*t1 + c*t2;
404 
405             // Check this random point against the object mask
406             // blue (for buildings) channel.
407             osg::Image* img = object_mask->getImage();
408             unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
409             unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
410 
411             if (mt_rand(&seed) < img->getColor(x, y).b()) {
412               // The red channel contains the rotation for this object
413               points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r()));
414             }
415           } else {
416             points.push_back(std::make_pair(randomPoint, static_cast<float>(mt_rand(&seed))));
417           }
418         }
419         num -= 1.0;
420       }
421     }
422   }
423 
buildGeometry(const TriangleVector & triangles,bool useVBOs) const424   osg::Geometry* buildGeometry(const TriangleVector& triangles, bool useVBOs) const
425   {
426     // Do not build anything if there is nothing in here ...
427     if (empty() || triangles.empty())
428       return 0;
429 
430     // FIXME: do not include all values here ...
431     osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
432     osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
433     osg::ref_ptr<osg::Vec2Array> priTexCoords = new osg::Vec2Array;
434     osg::ref_ptr<osg::Vec2Array> secTexCoords = new osg::Vec2Array;
435 
436     osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
437     colors->push_back(osg::Vec4(1, 1, 1, 1));
438 
439     osg::Geometry* geometry = new osg::Geometry;
440     if (useVBOs) {
441         geometry->setUseDisplayList(false);
442         geometry->setUseVertexBufferObjects(true);
443     }
444 
445     geometry->setDataVariance(osg::Object::STATIC);
446     geometry->setVertexArray(vertices);
447     geometry->setNormalArray(normals);
448     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
449     geometry->setColorArray(colors);
450     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
451     if ( has_sec_tcs ) {
452         geometry->setTexCoordArray(0, priTexCoords);
453         geometry->setTexCoordArray(1, secTexCoords);
454     } else {
455         geometry->setTexCoordArray(0, priTexCoords);
456     }
457 
458     const unsigned invalid = ~unsigned(0);
459     std::vector<unsigned> indexMap(getNumVertices(), invalid);
460 
461     DrawElementsFacade deFacade;
462     for (index_type i = 0; i < triangles.size(); ++i) {
463       triangle_ref triangle = triangles[i];
464       if (indexMap[triangle[0]] == invalid) {
465         indexMap[triangle[0]] = vertices->size();
466         vertices->push_back(toOsg(getVertex(triangle[0]).GetVertex()));
467         normals->push_back(toOsg(getVertex(triangle[0]).GetNormal()));
468         priTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(0)));
469         if ( has_sec_tcs ) {
470             secTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(1)));
471         }
472       }
473       deFacade.push_back(indexMap[triangle[0]]);
474 
475       if (indexMap[triangle[1]] == invalid) {
476         indexMap[triangle[1]] = vertices->size();
477         vertices->push_back(toOsg(getVertex(triangle[1]).GetVertex()));
478         normals->push_back(toOsg(getVertex(triangle[1]).GetNormal()));
479         priTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(0)));
480         if ( has_sec_tcs ) {
481             secTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(1)));
482         }
483       }
484       deFacade.push_back(indexMap[triangle[1]]);
485 
486       if (indexMap[triangle[2]] == invalid) {
487         indexMap[triangle[2]] = vertices->size();
488         vertices->push_back(toOsg(getVertex(triangle[2]).GetVertex()));
489         normals->push_back(toOsg(getVertex(triangle[2]).GetNormal()));
490         priTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(0)));
491         if ( has_sec_tcs ) {
492             secTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(1)));
493         }
494       }
495       deFacade.push_back(indexMap[triangle[2]]);
496     }
497     geometry->addPrimitiveSet(deFacade.getDrawElements());
498 
499     return geometry;
500   }
501 
buildGeometry(bool useVBOs) const502   osg::Geometry* buildGeometry(bool useVBOs) const
503   { return buildGeometry(getTriangles(), useVBOs); }
504 
getTextureIndex() const505   int getTextureIndex() const
506   {
507     if (empty() || getNumTriangles() == 0)
508       return 0;
509 
510     triangle_ref triangleRef = getTriangleRef(0);
511     SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
512 
513     return floor(v0.x());
514   }
515 
hasSecondaryTexCoord(bool sec_tc)516   void hasSecondaryTexCoord( bool sec_tc ) { has_sec_tcs = sec_tc; }
517 
518 private:
519   // Random seed for the triangle.
520   mt seed;
521 
522   // does the triangle array have secondary texture coordinates
523   bool has_sec_tcs;
524 };
525 
526 #endif
527