]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/SGTexturedTriangleBin.hxx
Merge branch 'jmt/ref_ptr-conv'
[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
29 #include <simgear/math/sg_random.h>
30 #include <simgear/math/SGMath.hxx>
31 #include "SGTriangleBin.hxx"
32
33
34
35 struct SGVertNormTex {
36   SGVertNormTex()
37   { }
38   SGVertNormTex(const SGVec3f& v, const SGVec3f& n, const SGVec2f& t) :
39     vertex(v), normal(n), texCoord(t)
40   { }
41   struct less
42   {
43     inline bool operator() (const SGVertNormTex& l,
44                             const SGVertNormTex& r) const
45     {
46       if (l.vertex < r.vertex) return true;
47       else if (r.vertex < l.vertex) return false;
48       else if (l.normal < r.normal) return true;
49       else if (r.normal < l.normal) return false;
50       else return l.texCoord < r.texCoord;
51     }
52   };
53
54   SGVec3f vertex;
55   SGVec3f normal;
56   SGVec2f texCoord;
57 };
58
59 // Use a DrawElementsUShort if there are few enough vertices,
60 // otherwise fallback to DrawElementsUInt. Hide the differences
61 // between the two from the rest of the code.
62 //
63 // We don't bother with DrawElementsUByte because that is generally
64 // not an advantage on modern hardware.
65 class DrawElementsFacade {
66 public:
67     DrawElementsFacade(unsigned numVerts) :
68         _ushortElements(0), _uintElements(0)
69     {
70         if (numVerts > 65535)
71             _uintElements
72                 = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
73         else
74             _ushortElements
75                 = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES);
76     }
77     
78     void push_back(unsigned val)
79     {
80         if (_uintElements)
81             _uintElements->push_back(val);
82         else
83             _ushortElements->push_back(val);
84     }
85
86     osg::DrawElements* getDrawElements()
87     {
88         if (_uintElements)
89             return _uintElements;
90         return _ushortElements;
91     }
92 protected:
93     osg::DrawElementsUShort* _ushortElements;
94     osg::DrawElementsUInt* _uintElements;
95 };
96
97 class SGTexturedTriangleBin : public SGTriangleBin<SGVertNormTex> {
98 public:
99   SGTexturedTriangleBin()
100   {
101     mt_init(&seed, 123);
102   }
103
104   // Computes and adds random surface points to the points list.
105   // The random points are computed with a density of (coverage points)/1
106   // The points are offsetted away from the triangles in
107   // offset * positive normal direction.
108   void addRandomSurfacePoints(float coverage, float offset,
109                               std::vector<SGVec3f>& points)
110   {
111     unsigned num = getNumTriangles();
112     for (unsigned i = 0; i < num; ++i) {
113       triangle_ref triangleRef = getTriangleRef(i);
114       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
115       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
116       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
117       SGVec3f normal = cross(v1 - v0, v2 - v0);
118       
119       // Compute the area
120       float area = 0.5f*length(normal);
121       if (area <= SGLimitsf::min())
122         continue;
123       
124       // For partial units of area, use a zombie door method to
125       // create the proper random chance of a light being created
126       // for this triangle
127       float unit = area + mt_rand(&seed)*coverage;
128       
129       SGVec3f offsetVector = offset*normalize(normal);
130       // generate a light point for each unit of area
131
132       while ( coverage < unit ) {
133
134         float a = mt_rand(&seed);
135         float b = mt_rand(&seed);
136
137         if ( a + b > 1 ) {
138           a = 1 - a;
139           b = 1 - b;
140         }
141         float c = 1 - a - b;
142         SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
143         points.push_back(randomPoint);
144         unit -= coverage;
145       }
146     }
147   }
148
149   // Computes and adds random surface points to the points list for tree
150   // coverage.
151   void addRandomTreePoints(float wood_coverage, 
152                            float tree_density,
153                            float wood_size,
154                            std::vector<SGVec3f>& points)
155   {
156     unsigned num = getNumTriangles();
157     for (unsigned i = 0; i < num; ++i) {
158       triangle_ref triangleRef = getTriangleRef(i);
159       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
160       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
161       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
162       SGVec3f normal = cross(v1 - v0, v2 - v0);
163       
164       // Compute the area
165       float area = 0.5f*length(normal);
166       if (area <= SGLimitsf::min())
167         continue;
168       
169       // For partial units of area, use a zombie door method to
170       // create the proper random chance of a point being created
171       // for this triangle
172       float unit = area + mt_rand(&seed)*wood_coverage;
173
174       int woodcount = (int) (unit / wood_coverage);
175
176       for (int j = 0; j < woodcount; j++) {
177
178         if (wood_size < area) {
179           // We need to place a wood within the triangle and populate it
180
181           // Determine the center of the wood
182           float x = mt_rand(&seed);
183           float y = mt_rand(&seed);
184
185           // Determine the size of this wood in m^2, and the number
186           // of trees in the wood
187           float ws = wood_size + wood_size * (mt_rand(&seed) - 0.5f);
188           unsigned total_trees = ws / tree_density;
189           float wood_length = sqrt(ws);
190
191           // From our wood size, work out the fraction on the two axis.
192           // This will be used as a factor when placing trees in the wood.
193           float x_tree_factor = wood_length / length(v1 -v0);
194           float y_tree_factor = wood_length / length(v2 -v0);
195
196           for (unsigned k = 0; k <= total_trees; k++) {
197
198             float a = x + x_tree_factor * (mt_rand(&seed) - 0.5f);
199             float b = y + y_tree_factor * (mt_rand(&seed) - 0.5f);
200
201
202             // In some cases, the triangle side lengths are so small that the
203             // tree_factors become so large as to make placing the tree within
204             // the triangle almost impossible. In this case, we place them
205             // randomly across the triangle.
206             if (a < 0.0f || a > 1.0f) a = mt_rand(&seed);
207             if (b < 0.0f || b > 1.0f) 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             points.push_back(randomPoint);
219           }
220         } else {
221           // This triangle is too small to contain a complete wood, so just
222           // distribute trees across it.
223           unsigned total_trees = area / tree_density;
224
225           for (unsigned k = 0; k <= total_trees; k++) {
226
227             float a = mt_rand(&seed);
228             float b = mt_rand(&seed);
229
230             if ( a + b > 1.0f ) {
231               a = 1.0f - a;
232               b = 1.0f - b;
233             }
234
235             float c = 1.0f - a - b;
236
237             SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
238             points.push_back(randomPoint);
239           }
240         }
241       }
242     }
243   }
244   
245    void addRandomPoints(float coverage, 
246                         std::vector<SGVec3f>& points)
247   {
248     unsigned num = getNumTriangles();
249     for (unsigned i = 0; i < num; ++i) {
250       triangle_ref triangleRef = getTriangleRef(i);
251       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
252       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
253       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
254       SGVec3f normal = cross(v1 - v0, v2 - v0);
255       
256       // Compute the area
257       float area = 0.5f*length(normal);
258       if (area <= SGLimitsf::min())
259         continue;
260       
261       // for partial units of area, use a zombie door method to
262       // create the proper random chance of an object being created
263       // for this triangle.
264       double num = area / coverage + mt_rand(&seed);
265
266       // place an object each unit of area
267       while ( num > 1.0 ) {
268         float a = mt_rand(&seed);
269         float b = mt_rand(&seed);
270         if ( a + b > 1 ) {
271           a = 1 - a;
272           b = 1 - b;
273         }
274         float c = 1 - a - b;
275         SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
276         points.push_back(randomPoint);
277         num -= 1.0;
278       }
279     }
280   }
281
282   osg::Geometry* buildGeometry(const TriangleVector& triangles) const
283   {
284     // Do not build anything if there is nothing in here ...
285     if (empty() || triangles.empty())
286       return 0;
287
288     // FIXME: do not include all values here ...
289     osg::Vec3Array* vertices = new osg::Vec3Array;
290     osg::Vec3Array* normals = new osg::Vec3Array;
291     osg::Vec2Array* texCoords = new osg::Vec2Array;
292
293     osg::Vec4Array* colors = new osg::Vec4Array;
294     colors->push_back(osg::Vec4(1, 1, 1, 1));
295
296     osg::Geometry* geometry = new osg::Geometry;
297     geometry->setVertexArray(vertices);
298     geometry->setNormalArray(normals);
299     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
300     geometry->setColorArray(colors);
301     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
302     geometry->setTexCoordArray(0, texCoords);
303
304     const unsigned invalid = ~unsigned(0);
305     std::vector<unsigned> indexMap(getNumVertices(), invalid);
306
307     DrawElementsFacade deFacade(vertices->size());
308     for (index_type i = 0; i < triangles.size(); ++i) {
309       triangle_ref triangle = triangles[i];
310       if (indexMap[triangle[0]] == invalid) {
311         indexMap[triangle[0]] = vertices->size();
312         vertices->push_back(toOsg(getVertex(triangle[0]).vertex));
313         normals->push_back(toOsg(getVertex(triangle[0]).normal));
314         texCoords->push_back(toOsg(getVertex(triangle[0]).texCoord));
315       }
316       deFacade.push_back(indexMap[triangle[0]]);
317
318       if (indexMap[triangle[1]] == invalid) {
319         indexMap[triangle[1]] = vertices->size();
320         vertices->push_back(toOsg(getVertex(triangle[1]).vertex));
321         normals->push_back(toOsg(getVertex(triangle[1]).normal));
322         texCoords->push_back(toOsg(getVertex(triangle[1]).texCoord));
323       }
324       deFacade.push_back(indexMap[triangle[1]]);
325
326       if (indexMap[triangle[2]] == invalid) {
327         indexMap[triangle[2]] = vertices->size();
328         vertices->push_back(toOsg(getVertex(triangle[2]).vertex));
329         normals->push_back(toOsg(getVertex(triangle[2]).normal));
330         texCoords->push_back(toOsg(getVertex(triangle[2]).texCoord));
331       }
332       deFacade.push_back(indexMap[triangle[2]]);
333     }
334     geometry->addPrimitiveSet(deFacade.getDrawElements());
335
336     return geometry;
337   }
338
339   osg::Geometry* buildGeometry() const
340   { return buildGeometry(getTriangles()); }
341
342 private:
343   // Random seed for the triangle.
344   mt seed;
345 };
346
347 #endif