]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/SGTexturedTriangleBin.hxx
Smaller point light for vasi
[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                            std::vector<SGVec3f>& points)
177   {
178     unsigned num = getNumTriangles();
179     for (unsigned i = 0; i < num; ++i) {
180       triangle_ref triangleRef = getTriangleRef(i);
181       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
182       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
183       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
184       SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
185       SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
186       SGVec2f t2 = getVertex(triangleRef[2]).texCoord;
187       SGVec3f normal = cross(v1 - v0, v2 - v0);
188       
189       // Compute the area
190       float area = 0.5f*length(normal);
191       if (area <= SGLimitsf::min())
192         continue;
193
194       // For partial units of area, use a zombie door method to
195       // create the proper random chance of a point being created
196       // for this triangle
197       float unit = area + mt_rand(&seed)*wood_coverage;
198       
199       // Vegetation density is linear, while we're creating woodland
200       // by area.
201       int woodcount = (int) (vegetation_density * 
202                              vegetation_density * 
203                              unit / wood_coverage);
204       
205       for (int j = 0; j < woodcount; j++) {
206         float a = mt_rand(&seed);
207         float b = mt_rand(&seed);
208
209         if ( a + b > 1.0f ) {
210           a = 1.0f - a;
211           b = 1.0f - b;
212         }
213
214         float c = 1.0f - a - b;
215
216         SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
217         
218         if (object_mask != NULL) {
219           SGVec2f texCoord = a*t0 + b*t1 + c*t2;
220           
221           // Check this random point against the object mask
222           // green (for trees) channel. 
223           osg::Image* img = object_mask->getImage();            
224           unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
225           unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
226           
227           if (mt_rand(&seed) < img->getColor(x, y).g()) {  
228             // The red channel contains the rotation for this object                                  
229             points.push_back(randomPoint);
230           }
231         } else {
232           points.push_back(randomPoint);
233         }                
234       }
235     }
236   }
237   
238    void addRandomPoints(double coverage, 
239                         double spacing,
240                         osg::Texture2D* object_mask,
241                         std::vector<std::pair<SGVec3f, float> >& points)
242   {
243     unsigned num = getNumTriangles();
244     for (unsigned i = 0; i < num; ++i) {
245       triangle_ref triangleRef = getTriangleRef(i);
246       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
247       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
248       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
249       SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
250       SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
251       SGVec2f t2 = getVertex(triangleRef[2]).texCoord;
252       SGVec3f normal = cross(v1 - v0, v2 - v0);
253       
254       // Compute the area
255       float area = 0.5f*length(normal);
256       if (area <= SGLimitsf::min())
257         continue;
258       
259       // for partial units of area, use a zombie door method to
260       // create the proper random chance of an object being created
261       // for this triangle.
262       double num = area / coverage + mt_rand(&seed);
263
264       // place an object each unit of area
265       while ( num > 1.0 ) {
266         float a = mt_rand(&seed);
267         float b = mt_rand(&seed);
268         if ( a + b > 1 ) {
269           a = 1 - a;
270           b = 1 - b;
271         }
272         float c = 1 - a - b;
273         SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
274         
275         // Check that the point is sufficiently far from
276         // the edge of the triangle by measuring the distance
277         // from the three lines that make up the triangle.        
278         if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) > spacing) &&
279             ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) > spacing) &&
280             ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) > spacing)   )
281         {
282           if (object_mask != NULL) {
283             SGVec2f texCoord = a*t0 + b*t1 + c*t2;
284             
285             // Check this random point against the object mask
286             // blue (for buildings) channel. 
287             osg::Image* img = object_mask->getImage();            
288             unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
289             unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
290             
291             if (mt_rand(&seed) < img->getColor(x, y).b()) {  
292               // The red channel contains the rotation for this object                                  
293               points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r()));
294             }
295           } else {
296             points.push_back(std::make_pair(randomPoint, mt_rand(&seed)));
297           }        
298         }
299         num -= 1.0;
300       }
301     }
302   }
303
304   osg::Geometry* buildGeometry(const TriangleVector& triangles) const
305   {
306     // Do not build anything if there is nothing in here ...
307     if (empty() || triangles.empty())
308       return 0;
309
310     // FIXME: do not include all values here ...
311     osg::Vec3Array* vertices = new osg::Vec3Array;
312     osg::Vec3Array* normals = new osg::Vec3Array;
313     osg::Vec2Array* texCoords = new osg::Vec2Array;
314
315     osg::Vec4Array* colors = new osg::Vec4Array;
316     colors->push_back(osg::Vec4(1, 1, 1, 1));
317
318     osg::Geometry* geometry = new osg::Geometry;
319     geometry->setVertexArray(vertices);
320     geometry->setNormalArray(normals);
321     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
322     geometry->setColorArray(colors);
323     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
324     geometry->setTexCoordArray(0, texCoords);
325
326     const unsigned invalid = ~unsigned(0);
327     std::vector<unsigned> indexMap(getNumVertices(), invalid);
328
329     DrawElementsFacade deFacade(vertices->size());
330     for (index_type i = 0; i < triangles.size(); ++i) {
331       triangle_ref triangle = triangles[i];
332       if (indexMap[triangle[0]] == invalid) {
333         indexMap[triangle[0]] = vertices->size();
334         vertices->push_back(toOsg(getVertex(triangle[0]).vertex));
335         normals->push_back(toOsg(getVertex(triangle[0]).normal));
336         texCoords->push_back(toOsg(getVertex(triangle[0]).texCoord));
337       }
338       deFacade.push_back(indexMap[triangle[0]]);
339
340       if (indexMap[triangle[1]] == invalid) {
341         indexMap[triangle[1]] = vertices->size();
342         vertices->push_back(toOsg(getVertex(triangle[1]).vertex));
343         normals->push_back(toOsg(getVertex(triangle[1]).normal));
344         texCoords->push_back(toOsg(getVertex(triangle[1]).texCoord));
345       }
346       deFacade.push_back(indexMap[triangle[1]]);
347
348       if (indexMap[triangle[2]] == invalid) {
349         indexMap[triangle[2]] = vertices->size();
350         vertices->push_back(toOsg(getVertex(triangle[2]).vertex));
351         normals->push_back(toOsg(getVertex(triangle[2]).normal));
352         texCoords->push_back(toOsg(getVertex(triangle[2]).texCoord));
353       }
354       deFacade.push_back(indexMap[triangle[2]]);
355     }
356     geometry->addPrimitiveSet(deFacade.getDrawElements());
357
358     return geometry;
359   }
360
361   osg::Geometry* buildGeometry() const
362   { return buildGeometry(getTriangles()); }
363   
364   int getTextureIndex() {
365     if (empty() || getNumTriangles() == 0)
366       return 0;
367
368     triangle_ref triangleRef = getTriangleRef(0);
369     SGVec3f v0 = getVertex(triangleRef[0]).vertex;
370       
371     return floor(v0.x());
372   }
373
374 private:
375   // Random seed for the triangle.
376   mt seed;
377 };
378
379 #endif