3 * Copyright (C) 2006-2007 Mathias Froehlich
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.
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.
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,
22 #ifndef SG_TEXTURED_TRIANGLE_BIN_HXX
23 #define SG_TEXTURED_TRIANGLE_BIN_HXX
26 #include <osg/Geometry>
27 #include <osg/PrimitiveSet>
28 #include <osg/Texture2D>
31 #include <simgear/math/sg_random.h>
32 #include <simgear/scene/util/OsgMath.hxx>
33 #include "SGTriangleBin.hxx"
37 struct SGVertNormTex {
44 inline bool tc_is_less ( const SGVertNormTex& l,
45 const SGVertNormTex& r,
48 if ( r.tc_mask & 1<<idx ) {
49 if ( l.tc_mask & 1<<idx ) {
50 if (l.texCoord[idx] < r.texCoord[idx]) {
59 inline bool operator() (const SGVertNormTex& l,
60 const SGVertNormTex& r) const
62 if (l.vertex < r.vertex) return true;
63 else if (r.vertex < l.vertex) return false;
64 else if (l.normal < r.normal) return true;
65 else if (r.normal < l.normal) return false;
66 else if ( tc_is_less( l, r, 0 ) ) return true;
67 else if ( tc_is_less( r, l, 0 ) ) return false;
68 else if ( tc_is_less( l, r, 1 ) ) return true;
69 else if ( tc_is_less( r, l, 1 ) ) return false;
70 else if ( tc_is_less( l, r, 2 ) ) return true;
71 else if ( tc_is_less( r, l, 2 ) ) return false;
72 else if ( tc_is_less( l, r, 3 ) ) return true;
77 void SetVertex( const SGVec3f& v ) { vertex = v; }
78 const SGVec3f& GetVertex( void ) const { return vertex; }
80 void SetNormal( const SGVec3f& n ) { normal = n; }
81 const SGVec3f& GetNormal( void ) const { return normal; }
83 void SetTexCoord( unsigned idx, const SGVec2f& tc ) {
87 const SGVec2f& GetTexCoord( unsigned idx ) const { return texCoord[idx]; }
97 // Use a DrawElementsUShort if there are few enough vertices,
98 // otherwise fallback to DrawElementsUInt. Hide the differences
99 // between the two from the rest of the code.
101 // We don't bother with DrawElementsUByte because that is generally
102 // not an advantage on modern hardware.
103 class DrawElementsFacade {
105 DrawElementsFacade(unsigned numVerts) :
106 _ushortElements(0), _uintElements(0)
108 if (numVerts > 65535)
110 = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
113 = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES);
116 void push_back(unsigned val)
119 _uintElements->push_back(val);
121 _ushortElements->push_back(val);
124 osg::DrawElements* getDrawElements()
127 return _uintElements;
128 return _ushortElements;
131 osg::DrawElementsUShort* _ushortElements;
132 osg::DrawElementsUInt* _uintElements;
135 class SGTexturedTriangleBin : public SGTriangleBin<SGVertNormTex> {
137 SGTexturedTriangleBin()
143 // Computes and adds random surface points to the points list.
144 // The random points are computed with a density of (coverage points)/1
145 // The points are offsetted away from the triangles in
146 // offset * positive normal direction.
147 void addRandomSurfacePoints(float coverage, float offset,
148 osg::Texture2D* object_mask,
149 std::vector<SGVec3f>& points)
151 unsigned num = getNumTriangles();
152 for (unsigned i = 0; i < num; ++i) {
153 triangle_ref triangleRef = getTriangleRef(i);
154 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
155 SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
156 SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
157 SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
158 SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
159 SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
160 SGVec3f normal = cross(v1 - v0, v2 - v0);
163 float area = 0.5f*length(normal);
164 if (area <= SGLimitsf::min())
167 // For partial units of area, use a zombie door method to
168 // create the proper random chance of a light being created
170 float unit = area + mt_rand(&seed)*coverage;
172 SGVec3f offsetVector = offset*normalize(normal);
173 // generate a light point for each unit of area
175 while ( coverage < unit ) {
177 float a = mt_rand(&seed);
178 float b = mt_rand(&seed);
185 SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
187 if (object_mask != NULL) {
188 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
190 // Check this random point against the object mask
192 osg::Image* img = object_mask->getImage();
193 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
194 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
196 if (mt_rand(&seed) < img->getColor(x, y).r()) {
197 points.push_back(randomPoint);
200 // No object mask, so simply place the object
201 points.push_back(randomPoint);
208 // Computes and adds random surface points to the points list for tree
210 void addRandomTreePoints(float wood_coverage,
211 osg::Texture2D* object_mask,
212 float vegetation_density,
213 float cos_max_density_angle,
214 float cos_zero_density_angle,
215 std::vector<SGVec3f>& points)
217 unsigned num = getNumTriangles();
218 for (unsigned i = 0; i < num; ++i) {
219 triangle_ref triangleRef = getTriangleRef(i);
220 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
221 SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
222 SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
223 SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
224 SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
225 SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
226 SGVec3f normal = cross(v1 - v0, v2 - v0);
228 // Ensure the slope isn't too steep by checking the
229 // cos of the angle between the slope normal and the
230 // vertical (conveniently the z-component of the normalized
231 // normal) and values passed in.
232 float alpha = normalize(normal).z();
233 float slope_density = 1.0;
235 if (alpha < cos_zero_density_angle)
236 continue; // Too steep for any vegetation
238 if (alpha < cos_max_density_angle) {
240 (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
244 float area = 0.5f*length(normal);
245 if (area <= SGLimitsf::min())
248 // Determine the number of trees, taking into account vegetation
249 // density (which is linear) and the slope density factor.
250 // Use a zombie door method to create the proper random chance
251 // of a tree being created for partial values.
252 int woodcount = (int) (vegetation_density * vegetation_density *
254 area / wood_coverage + mt_rand(&seed));
256 for (int j = 0; j < woodcount; j++) {
257 float a = mt_rand(&seed);
258 float b = mt_rand(&seed);
260 if ( a + b > 1.0f ) {
265 float c = 1.0f - a - b;
267 SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
269 if (object_mask != NULL) {
270 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
272 // Check this random point against the object mask
273 // green (for trees) channel.
274 osg::Image* img = object_mask->getImage();
275 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
276 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
278 if (mt_rand(&seed) < img->getColor(x, y).g()) {
279 // The red channel contains the rotation for this object
280 points.push_back(randomPoint);
283 points.push_back(randomPoint);
289 void addRandomPoints(double coverage,
291 osg::Texture2D* object_mask,
292 std::vector<std::pair<SGVec3f, float> >& points)
294 unsigned num = getNumTriangles();
295 for (unsigned i = 0; i < num; ++i) {
296 triangle_ref triangleRef = getTriangleRef(i);
297 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
298 SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
299 SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
300 SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
301 SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
302 SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
303 SGVec3f normal = cross(v1 - v0, v2 - v0);
306 float area = 0.5f*length(normal);
307 if (area <= SGLimitsf::min())
310 // for partial units of area, use a zombie door method to
311 // create the proper random chance of an object being created
312 // for this triangle.
313 double num = area / coverage + mt_rand(&seed);
315 // place an object each unit of area
316 while ( num > 1.0 ) {
317 float a = mt_rand(&seed);
318 float b = mt_rand(&seed);
324 SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
326 // Check that the point is sufficiently far from
327 // the edge of the triangle by measuring the distance
328 // from the three lines that make up the triangle.
329 if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) > spacing) &&
330 ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) > spacing) &&
331 ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) > spacing) )
333 if (object_mask != NULL) {
334 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
336 // Check this random point against the object mask
337 // blue (for buildings) channel.
338 osg::Image* img = object_mask->getImage();
339 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
340 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
342 if (mt_rand(&seed) < img->getColor(x, y).b()) {
343 // The red channel contains the rotation for this object
344 points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r()));
347 points.push_back(std::make_pair(randomPoint, static_cast<float>(mt_rand(&seed))));
355 osg::Geometry* buildGeometry(const TriangleVector& triangles, bool useVBOs) const
357 // Do not build anything if there is nothing in here ...
358 if (empty() || triangles.empty())
361 // FIXME: do not include all values here ...
362 osg::Vec3Array* vertices = new osg::Vec3Array;
363 osg::Vec3Array* normals = new osg::Vec3Array;
364 osg::Vec2Array* priTexCoords = new osg::Vec2Array;
365 osg::Vec2Array* secTexCoords = new osg::Vec2Array;
367 osg::Vec4Array* colors = new osg::Vec4Array;
368 colors->push_back(osg::Vec4(1, 1, 1, 1));
370 osg::Geometry* geometry = new osg::Geometry;
372 geometry->setUseDisplayList(false);
373 geometry->setUseVertexBufferObjects(true);
376 geometry->setDataVariance(osg::Object::STATIC);
377 geometry->setVertexArray(vertices);
378 geometry->setNormalArray(normals);
379 geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
380 geometry->setColorArray(colors);
381 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
383 geometry->setTexCoordArray(0, priTexCoords);
384 geometry->setTexCoordArray(1, secTexCoords);
386 geometry->setTexCoordArray(0, priTexCoords);
389 const unsigned invalid = ~unsigned(0);
390 std::vector<unsigned> indexMap(getNumVertices(), invalid);
392 DrawElementsFacade deFacade(vertices->size());
393 for (index_type i = 0; i < triangles.size(); ++i) {
394 triangle_ref triangle = triangles[i];
395 if (indexMap[triangle[0]] == invalid) {
396 indexMap[triangle[0]] = vertices->size();
397 vertices->push_back(toOsg(getVertex(triangle[0]).GetVertex()));
398 normals->push_back(toOsg(getVertex(triangle[0]).GetNormal()));
399 priTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(0)));
401 secTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(1)));
404 deFacade.push_back(indexMap[triangle[0]]);
406 if (indexMap[triangle[1]] == invalid) {
407 indexMap[triangle[1]] = vertices->size();
408 vertices->push_back(toOsg(getVertex(triangle[1]).GetVertex()));
409 normals->push_back(toOsg(getVertex(triangle[1]).GetNormal()));
410 priTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(0)));
412 secTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(1)));
415 deFacade.push_back(indexMap[triangle[1]]);
417 if (indexMap[triangle[2]] == invalid) {
418 indexMap[triangle[2]] = vertices->size();
419 vertices->push_back(toOsg(getVertex(triangle[2]).GetVertex()));
420 normals->push_back(toOsg(getVertex(triangle[2]).GetNormal()));
421 priTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(0)));
423 secTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(1)));
426 deFacade.push_back(indexMap[triangle[2]]);
428 geometry->addPrimitiveSet(deFacade.getDrawElements());
433 osg::Geometry* buildGeometry(bool useVBOs) const
434 { return buildGeometry(getTriangles(), useVBOs); }
436 int getTextureIndex() const
438 if (empty() || getNumTriangles() == 0)
441 triangle_ref triangleRef = getTriangleRef(0);
442 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
444 return floor(v0.x());
447 void hasSecondaryTexCoord( bool sec_tc ) { has_sec_tcs = sec_tc; }
450 // Random seed for the triangle.
453 // does the triangle array have secondary texture coordinates