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
25 #define MAX_RANDOM_OBJECTS 100.0
28 #include <osg/Geometry>
29 #include <osg/PrimitiveSet>
30 #include <osg/Texture2D>
31 #include <osg/ref_ptr>
34 #include <simgear/math/sg_random.h>
35 #include <simgear/scene/util/OsgMath.hxx>
36 #include "SGTriangleBin.hxx"
40 struct SGVertNormTex {
47 inline bool tc_is_less ( const SGVertNormTex& l,
48 const SGVertNormTex& r,
51 if ( r.tc_mask & 1<<idx ) {
52 if ( l.tc_mask & 1<<idx ) {
53 if (l.texCoord[idx] < r.texCoord[idx]) {
62 inline bool operator() (const SGVertNormTex& l,
63 const SGVertNormTex& r) const
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;
80 void SetVertex( const SGVec3f& v ) { vertex = v; }
81 const SGVec3f& GetVertex( void ) const { return vertex; }
83 void SetNormal( const SGVec3f& n ) { normal = n; }
84 const SGVec3f& GetNormal( void ) const { return normal; }
86 void SetTexCoord( unsigned idx, const SGVec2f& tc ) {
90 const SGVec2f& GetTexCoord( unsigned idx ) const { return texCoord[idx]; }
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.
104 // We don't bother with DrawElementsUByte because that is generally
105 // not an advantage on modern hardware.
106 class DrawElementsFacade {
108 DrawElementsFacade(void) : count(0)
110 _uintElements = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
111 _ushortElements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES);
114 void push_back(unsigned val)
118 _ushortElements->push_back(val);
120 _uintElements->push_back(val);
123 osg::DrawElements* getDrawElements()
127 return _uintElements;
130 return _ushortElements;
134 osg::ref_ptr<osg::DrawElementsUShort> _ushortElements;
135 osg::ref_ptr<osg::DrawElementsUInt> _uintElements;
139 class SGTexturedTriangleBin : public SGTriangleBin<SGVertNormTex> {
141 SGTexturedTriangleBin()
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.
151 void addRandomSurfacePoints(float coverage, float offset,
152 osg::Texture2D* object_mask,
153 std::vector<SGVec3f>& points)
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);
167 float area = 0.5f*length(normal);
168 if (area <= SGLimitsf::min())
171 // For partial units of area, use a zombie door method to
172 // create the proper random chance of a light being created
174 float unit = area + mt_rand(&seed)*coverage;
176 SGVec3f offsetVector = offset*normalize(normal);
177 // generate a light point for each unit of area
179 while ( coverage < unit ) {
181 float a = mt_rand(&seed);
182 float b = mt_rand(&seed);
189 SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
191 if (object_mask != NULL) {
192 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
194 // Check this random point against the object mask
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();
200 if (mt_rand(&seed) < img->getColor(x, y).r()) {
201 points.push_back(randomPoint);
204 // No object mask, so simply place the object
205 points.push_back(randomPoint);
212 // Computes and adds random surface points to the points list for tree
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 std::vector<SGVec3f>& points,
220 std::vector<SGVec3f>& normals)
222 unsigned num = getNumTriangles();
223 for (unsigned i = 0; i < num; ++i) {
224 triangle_ref triangleRef = getTriangleRef(i);
225 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
226 SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
227 SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
228 SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
229 SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
230 SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
231 SGVec3f normal = cross(v1 - v0, v2 - v0);
233 // Ensure the slope isn't too steep by checking the
234 // cos of the angle between the slope normal and the
235 // vertical (conveniently the z-component of the normalized
236 // normal) and values passed in.
237 float alpha = normalize(normal).z();
238 float slope_density = 1.0;
240 if (alpha < cos_zero_density_angle)
241 continue; // Too steep for any vegetation
243 if (alpha < cos_max_density_angle) {
245 (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
249 float area = 0.5f*length(normal);
250 if (area <= SGLimitsf::min())
253 // Determine the number of trees, taking into account vegetation
254 // density (which is linear) and the slope density factor.
255 // Use a zombie door method to create the proper random chance
256 // of a tree being created for partial values.
257 int woodcount = (int) (vegetation_density * vegetation_density *
259 area / wood_coverage + mt_rand(&seed));
261 for (int j = 0; j < woodcount; j++) {
262 float a = mt_rand(&seed);
263 float b = mt_rand(&seed);
265 if ( a + b > 1.0f ) {
270 float c = 1.0f - a - b;
272 SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
274 if (object_mask != NULL) {
275 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
277 // Check this random point against the object mask
278 // green (for trees) 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();
283 if (mt_rand(&seed) < img->getColor(x, y).g()) {
284 // The red channel contains the rotation for this object
285 points.push_back(randomPoint);
286 normals.push_back(normalize(normal));
289 points.push_back(randomPoint);
290 normals.push_back(normalize(normal));
296 void addRandomPoints(double coverage,
298 osg::Texture2D* object_mask,
299 std::vector<std::pair<SGVec3f, float> >& points)
301 unsigned numtriangles = getNumTriangles();
302 for (unsigned i = 0; i < numtriangles; ++i) {
303 triangle_ref triangleRef = getTriangleRef(i);
304 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
305 SGVec3f v1 = getVertex(triangleRef[1]).GetVertex();
306 SGVec3f v2 = getVertex(triangleRef[2]).GetVertex();
307 SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0);
308 SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0);
309 SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0);
310 SGVec3f normal = cross(v1 - v0, v2 - v0);
313 float area = 0.5f*length(normal);
314 if (area <= SGLimitsf::min())
317 // for partial units of area, use a zombie door method to
318 // create the proper random chance of an object being created
319 // for this triangle.
320 double num = area / coverage + mt_rand(&seed);
322 if (num > MAX_RANDOM_OBJECTS) {
323 SG_LOG(SG_TERRAIN, SG_ALERT,
324 "Per-triangle random object count exceeded limits ("
325 << MAX_RANDOM_OBJECTS << ") " << num);
326 num = MAX_RANDOM_OBJECTS;
329 // place an object each unit of area
330 while ( num > 1.0 ) {
331 float a = mt_rand(&seed);
332 float b = mt_rand(&seed);
338 SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
340 // Check that the point is sufficiently far from
341 // the edge of the triangle by measuring the distance
342 // from the three lines that make up the triangle.
343 if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) > spacing) &&
344 ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) > spacing) &&
345 ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) > spacing) )
347 if (object_mask != NULL) {
348 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
350 // Check this random point against the object mask
351 // blue (for buildings) channel.
352 osg::Image* img = object_mask->getImage();
353 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
354 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
356 if (mt_rand(&seed) < img->getColor(x, y).b()) {
357 // The red channel contains the rotation for this object
358 points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r()));
361 points.push_back(std::make_pair(randomPoint, static_cast<float>(mt_rand(&seed))));
369 osg::Geometry* buildGeometry(const TriangleVector& triangles, bool useVBOs) const
371 // Do not build anything if there is nothing in here ...
372 if (empty() || triangles.empty())
375 // FIXME: do not include all values here ...
376 osg::Vec3Array* vertices = new osg::Vec3Array;
377 osg::Vec3Array* normals = new osg::Vec3Array;
378 osg::Vec2Array* priTexCoords = new osg::Vec2Array;
379 osg::Vec2Array* secTexCoords = new osg::Vec2Array;
381 osg::Vec4Array* colors = new osg::Vec4Array;
382 colors->push_back(osg::Vec4(1, 1, 1, 1));
384 osg::Geometry* geometry = new osg::Geometry;
386 geometry->setUseDisplayList(false);
387 geometry->setUseVertexBufferObjects(true);
390 geometry->setDataVariance(osg::Object::STATIC);
391 geometry->setVertexArray(vertices);
392 geometry->setNormalArray(normals);
393 geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
394 geometry->setColorArray(colors);
395 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
397 geometry->setTexCoordArray(0, priTexCoords);
398 geometry->setTexCoordArray(1, secTexCoords);
400 geometry->setTexCoordArray(0, priTexCoords);
403 const unsigned invalid = ~unsigned(0);
404 std::vector<unsigned> indexMap(getNumVertices(), invalid);
406 DrawElementsFacade deFacade;
407 for (index_type i = 0; i < triangles.size(); ++i) {
408 triangle_ref triangle = triangles[i];
409 if (indexMap[triangle[0]] == invalid) {
410 indexMap[triangle[0]] = vertices->size();
411 vertices->push_back(toOsg(getVertex(triangle[0]).GetVertex()));
412 normals->push_back(toOsg(getVertex(triangle[0]).GetNormal()));
413 priTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(0)));
415 secTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(1)));
418 deFacade.push_back(indexMap[triangle[0]]);
420 if (indexMap[triangle[1]] == invalid) {
421 indexMap[triangle[1]] = vertices->size();
422 vertices->push_back(toOsg(getVertex(triangle[1]).GetVertex()));
423 normals->push_back(toOsg(getVertex(triangle[1]).GetNormal()));
424 priTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(0)));
426 secTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(1)));
429 deFacade.push_back(indexMap[triangle[1]]);
431 if (indexMap[triangle[2]] == invalid) {
432 indexMap[triangle[2]] = vertices->size();
433 vertices->push_back(toOsg(getVertex(triangle[2]).GetVertex()));
434 normals->push_back(toOsg(getVertex(triangle[2]).GetNormal()));
435 priTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(0)));
437 secTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(1)));
440 deFacade.push_back(indexMap[triangle[2]]);
442 geometry->addPrimitiveSet(deFacade.getDrawElements());
447 osg::Geometry* buildGeometry(bool useVBOs) const
448 { return buildGeometry(getTriangles(), useVBOs); }
450 int getTextureIndex() const
452 if (empty() || getNumTriangles() == 0)
455 triangle_ref triangleRef = getTriangleRef(0);
456 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex();
458 return floor(v0.x());
461 void hasSecondaryTexCoord( bool sec_tc ) { has_sec_tcs = sec_tc; }
464 // Random seed for the triangle.
467 // does the triangle array have secondary texture coordinates