]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/SGTexturedTriangleBin.hxx
Make tsync part of libSimGearCore when building shared libraries
[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/math/SGMath.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(float coverage, 
239                         osg::Texture2D* object_mask,
240                         std::vector<std::pair<SGVec3f, float> >& points)
241   {
242     unsigned num = getNumTriangles();
243     for (unsigned i = 0; i < num; ++i) {
244       triangle_ref triangleRef = getTriangleRef(i);
245       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
246       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
247       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
248       SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
249       SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
250       SGVec2f t2 = getVertex(triangleRef[2]).texCoord;
251       SGVec3f normal = cross(v1 - v0, v2 - v0);
252       
253       // Compute the area
254       float area = 0.5f*length(normal);
255       if (area <= SGLimitsf::min())
256         continue;
257       
258       // for partial units of area, use a zombie door method to
259       // create the proper random chance of an object being created
260       // for this triangle.
261       double num = area / coverage + mt_rand(&seed);
262
263       // place an object each unit of area
264       while ( num > 1.0 ) {
265         float a = mt_rand(&seed);
266         float b = mt_rand(&seed);
267         if ( a + b > 1 ) {
268           a = 1 - a;
269           b = 1 - b;
270         }
271         float c = 1 - a - b;
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           // blue (for buildings) 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).b()) {  
284             // The red channel contains the rotation for this object                                  
285             points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r()));
286           }
287         } else {
288           points.push_back(std::make_pair(randomPoint, mt_rand(&seed)));
289         }        
290         num -= 1.0;
291       }
292     }
293   }
294
295   osg::Geometry* buildGeometry(const TriangleVector& triangles) const
296   {
297     // Do not build anything if there is nothing in here ...
298     if (empty() || triangles.empty())
299       return 0;
300
301     // FIXME: do not include all values here ...
302     osg::Vec3Array* vertices = new osg::Vec3Array;
303     osg::Vec3Array* normals = new osg::Vec3Array;
304     osg::Vec2Array* texCoords = new osg::Vec2Array;
305
306     osg::Vec4Array* colors = new osg::Vec4Array;
307     colors->push_back(osg::Vec4(1, 1, 1, 1));
308
309     osg::Geometry* geometry = new osg::Geometry;
310     geometry->setVertexArray(vertices);
311     geometry->setNormalArray(normals);
312     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
313     geometry->setColorArray(colors);
314     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
315     geometry->setTexCoordArray(0, texCoords);
316
317     const unsigned invalid = ~unsigned(0);
318     std::vector<unsigned> indexMap(getNumVertices(), invalid);
319
320     DrawElementsFacade deFacade(vertices->size());
321     for (index_type i = 0; i < triangles.size(); ++i) {
322       triangle_ref triangle = triangles[i];
323       if (indexMap[triangle[0]] == invalid) {
324         indexMap[triangle[0]] = vertices->size();
325         vertices->push_back(toOsg(getVertex(triangle[0]).vertex));
326         normals->push_back(toOsg(getVertex(triangle[0]).normal));
327         texCoords->push_back(toOsg(getVertex(triangle[0]).texCoord));
328       }
329       deFacade.push_back(indexMap[triangle[0]]);
330
331       if (indexMap[triangle[1]] == invalid) {
332         indexMap[triangle[1]] = vertices->size();
333         vertices->push_back(toOsg(getVertex(triangle[1]).vertex));
334         normals->push_back(toOsg(getVertex(triangle[1]).normal));
335         texCoords->push_back(toOsg(getVertex(triangle[1]).texCoord));
336       }
337       deFacade.push_back(indexMap[triangle[1]]);
338
339       if (indexMap[triangle[2]] == invalid) {
340         indexMap[triangle[2]] = vertices->size();
341         vertices->push_back(toOsg(getVertex(triangle[2]).vertex));
342         normals->push_back(toOsg(getVertex(triangle[2]).normal));
343         texCoords->push_back(toOsg(getVertex(triangle[2]).texCoord));
344       }
345       deFacade.push_back(indexMap[triangle[2]]);
346     }
347     geometry->addPrimitiveSet(deFacade.getDrawElements());
348
349     return geometry;
350   }
351
352   osg::Geometry* buildGeometry() const
353   { return buildGeometry(getTriangles()); }
354   
355   int getTextureIndex() {
356     if (empty() || getNumTriangles() == 0)
357       return 0;
358
359     triangle_ref triangleRef = getTriangleRef(0);
360     SGVec3f v0 = getVertex(triangleRef[0]).vertex;
361       
362     return floor(v0.x());
363   }
364
365 private:
366   // Random seed for the triangle.
367   mt seed;
368 };
369
370 #endif