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 #define MAX_RANDOM_OBJECTS 100.0 26 27 #include <osg/Array> 28 #include <osg/Geometry> 29 #include <osg/PrimitiveSet> 30 #include <osg/Texture2D> 31 #include <osg/ref_ptr> 32 #include <stdio.h> 33 34 #include <simgear/math/sg_random.h> 35 #include <simgear/scene/util/OsgMath.hxx> 36 #include "SGTriangleBin.hxx" 37 38 39 40 struct SGVertNormTex { SGVertNormTexSGVertNormTex41 SGVertNormTex() { 42 tc_mask = 0; 43 } 44 45 struct less 46 { tc_is_lessSGVertNormTex::less47 inline bool tc_is_less ( const SGVertNormTex& l, 48 const SGVertNormTex& r, 49 int idx ) const 50 { 51 if ( r.tc_mask & 1<<idx ) { 52 if ( l.tc_mask & 1<<idx ) { 53 if (l.texCoord[idx] < r.texCoord[idx]) { 54 return true; 55 } 56 } 57 } 58 59 return false; 60 }; 61 operator ()SGVertNormTex::less62 inline bool operator() (const SGVertNormTex& l, 63 const SGVertNormTex& r) const 64 { 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; 76 else return false; 77 } 78 }; 79 SetVertexSGVertNormTex80 void SetVertex( const SGVec3f& v ) { vertex = v; } GetVertexSGVertNormTex81 const SGVec3f& GetVertex( void ) const { return vertex; } 82 SetNormalSGVertNormTex83 void SetNormal( const SGVec3f& n ) { normal = n; } GetNormalSGVertNormTex84 const SGVec3f& GetNormal( void ) const { return normal; } 85 SetTexCoordSGVertNormTex86 void SetTexCoord( unsigned idx, const SGVec2f& tc ) { 87 texCoord[idx] = tc; 88 tc_mask |= 1 << idx; 89 } GetTexCoordSGVertNormTex90 const SGVec2f& GetTexCoord( unsigned idx ) const { return texCoord[idx]; } 91 92 private: 93 SGVec3f vertex; 94 SGVec3f normal; 95 SGVec2f texCoord[4]; 96 97 unsigned tc_mask; 98 }; 99 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. 103 // 104 // We don't bother with DrawElementsUByte because that is generally 105 // not an advantage on modern hardware. 106 class DrawElementsFacade { 107 public: DrawElementsFacade(void)108 DrawElementsFacade(void) : count(0) 109 { 110 _uintElements = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES); 111 _ushortElements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES); 112 } 113 push_back(unsigned val)114 void push_back(unsigned val) 115 { 116 count++; 117 if (count < 65536) { 118 _ushortElements->push_back(val); 119 } 120 _uintElements->push_back(val); 121 } 122 getDrawElements()123 osg::DrawElements* getDrawElements() 124 { 125 if (count > 65535) { 126 _ushortElements = 0; 127 return _uintElements; 128 } else { 129 _uintElements = 0; 130 return _ushortElements; 131 } 132 } 133 protected: 134 osg::ref_ptr<osg::DrawElementsUShort> _ushortElements; 135 osg::ref_ptr<osg::DrawElementsUInt> _uintElements; 136 unsigned count; 137 }; 138 139 class SGTexturedTriangleBin : public SGTriangleBin<SGVertNormTex> { 140 public: SGTexturedTriangleBin()141 SGTexturedTriangleBin() 142 { 143 mt_init(&seed, 123); 144 has_sec_tcs = false; 145 } 146 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. addRandomSurfacePoints(float coverage,float offset,osg::Texture2D * object_mask,std::vector<SGVec3f> & points)151 void addRandomSurfacePoints(float coverage, float offset, 152 osg::Texture2D* object_mask, 153 std::vector<SGVec3f>& points) 154 { 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); 165 166 // Compute the area 167 float area = 0.5f*length(normal); 168 if (area <= SGLimitsf::min()) 169 continue; 170 171 // For partial units of area, use a zombie door method to 172 // create the proper random chance of a light being created 173 // for this triangle 174 float unit = area + mt_rand(&seed)*coverage; 175 176 SGVec3f offsetVector = offset*normalize(normal); 177 // generate a light point for each unit of area 178 179 while ( coverage < unit ) { 180 181 float a = mt_rand(&seed); 182 float b = mt_rand(&seed); 183 184 if ( a + b > 1 ) { 185 a = 1 - a; 186 b = 1 - b; 187 } 188 float c = 1 - a - b; 189 SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2; 190 191 if (object_mask != NULL) { 192 SGVec2f texCoord = a*t0 + b*t1 + c*t2; 193 194 // Check this random point against the object mask 195 // red channel. 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(); 199 200 if (mt_rand(&seed) < img->getColor(x, y).r()) { 201 points.push_back(randomPoint); 202 } 203 } else { 204 // No object mask, so simply place the object 205 points.push_back(randomPoint); 206 } 207 unit -= coverage; 208 } 209 } 210 } 211 212 // Computes and adds random surface points to the points list for tree 213 // coverage. addRandomTreePoints(float wood_coverage,osg::Texture2D * object_mask,float vegetation_density,float cos_max_density_angle,float cos_zero_density_angle,bool is_plantation,std::vector<SGVec3f> & points,std::vector<SGVec3f> & normals)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 bool is_plantation, 220 std::vector<SGVec3f>& points, 221 std::vector<SGVec3f>& normals) 222 { 223 unsigned num = getNumTriangles(); 224 for (unsigned i = 0; i < num; ++i) { 225 triangle_ref triangleRef = getTriangleRef(i); 226 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex(); 227 SGVec3f v1 = getVertex(triangleRef[1]).GetVertex(); 228 SGVec3f v2 = getVertex(triangleRef[2]).GetVertex(); 229 SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0); 230 SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0); 231 SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0); 232 SGVec3f normal = cross(v1 - v0, v2 - v0); 233 234 // Ensure the slope isn't too steep by checking the 235 // cos of the angle between the slope normal and the 236 // vertical (conveniently the z-component of the normalized 237 // normal) and values passed in. 238 float alpha = normalize(normal).z(); 239 float slope_density = 1.0; 240 241 if (alpha < cos_zero_density_angle) 242 continue; // Too steep for any vegetation 243 244 if (alpha < cos_max_density_angle) { 245 slope_density = 246 (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle); 247 } 248 249 // Compute the area 250 float area = 0.5f*length(normal); 251 if (area <= SGLimitsf::min()) 252 continue; 253 if (is_plantation) { // regularly-spaced vegetation 254 255 int separation = (int) ceil(sqrt(wood_coverage)); 256 float max_x = ceil(max(max(v1.x(),v2.x()),v0.x())); 257 float min_x = floor(min(min(v1.x(),v2.x()),v0.x())); 258 float max_y = ceil(max(max(v1.y(),v2.y()),v0.y())); 259 float min_y = floor(min(min(v1.y(),v2.y()),v0.y())); 260 261 // equation of the plane ax+by+cz+d=0, need d 262 263 float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z()); 264 265 // Now loop over a grid, skipping points not in the triangle 266 267 int x_steps = (int) (max_x - min_x)/separation; 268 int y_steps = (int) (max_y - min_y)/separation; 269 SGVec2f v02d = SGVec2f(v0.x(),v0.y()); 270 SGVec2f v12d = SGVec2f(v1.x(),v1.y()); 271 SGVec2f v22d = SGVec2f(v2.x(),v2.y()); 272 273 for (int jx = 0; jx < x_steps; jx++) { 274 float ptx = min_x + jx * separation; 275 276 for (int jy = 0; jy < y_steps; jy++) { 277 float pty = min_y + jy * separation; 278 SGVec2f newpt = SGVec2f(ptx,pty); 279 if (!point_in_triangle(newpt,v02d,v12d,v22d)) 280 continue; 281 282 // z = (-ax-by-d)/c; c is not zero as 283 // that would be alpha of 1.0 284 285 float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z(); 286 SGVec3f randomPoint = SGVec3f(ptx,pty,ptz); 287 288 if (object_mask != NULL) { 289 // Check this point against the object mask 290 // green (for trees) channel. 291 osg::Image* img = object_mask->getImage(); 292 unsigned int x = (int) (img->s() * newpt.x()) % img->s(); 293 unsigned int y = (int) (img->t() * newpt.y()) % img->t(); 294 295 if (mt_rand(&seed) < img->getColor(x, y).g()) { 296 // The red channel contains the rotation for this object 297 points.push_back(randomPoint); 298 normals.push_back(normalize(normal)); 299 } 300 } else { 301 points.push_back(randomPoint); 302 normals.push_back(normalize(normal)); 303 } 304 } 305 } 306 } else { 307 // Determine the number of trees, taking into account vegetation 308 // density (which is linear) and the slope density factor. 309 // Use a zombie door method to create the proper random chance 310 // of a tree being created for partial values. 311 int woodcount = (int) (vegetation_density * vegetation_density * 312 slope_density * 313 area / wood_coverage + mt_rand(&seed)); 314 315 for (int j = 0; j < woodcount; j++) { 316 float a = mt_rand(&seed); 317 float b = mt_rand(&seed); 318 319 if ( a + b > 1.0f ) { 320 a = 1.0f - a; 321 b = 1.0f - b; 322 } 323 324 float c = 1.0f - a - b; 325 326 SGVec3f randomPoint = a*v0 + b*v1 + c*v2; 327 328 if (object_mask != NULL) { 329 SGVec2f texCoord = a*t0 + b*t1 + c*t2; 330 331 // Check this random point against the object mask 332 // green (for trees) channel. 333 osg::Image* img = object_mask->getImage(); 334 unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); 335 unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); 336 337 if (mt_rand(&seed) < img->getColor(x, y).g()) { 338 // The red channel contains the rotation for this object 339 points.push_back(randomPoint); 340 normals.push_back(normalize(normal)); 341 } 342 } else { 343 points.push_back(randomPoint); 344 normals.push_back(normalize(normal)); 345 } 346 } 347 } 348 } 349 } 350 addRandomPoints(double coverage,double spacing,osg::Texture2D * object_mask,std::vector<std::pair<SGVec3f,float>> & points)351 void addRandomPoints(double coverage, 352 double spacing, 353 osg::Texture2D* object_mask, 354 std::vector<std::pair<SGVec3f, float> >& points) 355 { 356 unsigned numtriangles = getNumTriangles(); 357 for (unsigned i = 0; i < numtriangles; ++i) { 358 triangle_ref triangleRef = getTriangleRef(i); 359 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex(); 360 SGVec3f v1 = getVertex(triangleRef[1]).GetVertex(); 361 SGVec3f v2 = getVertex(triangleRef[2]).GetVertex(); 362 SGVec2f t0 = getVertex(triangleRef[0]).GetTexCoord(0); 363 SGVec2f t1 = getVertex(triangleRef[1]).GetTexCoord(0); 364 SGVec2f t2 = getVertex(triangleRef[2]).GetTexCoord(0); 365 SGVec3f normal = cross(v1 - v0, v2 - v0); 366 367 // Compute the area 368 float area = 0.5f*length(normal); 369 if (area <= SGLimitsf::min()) 370 continue; 371 372 // for partial units of area, use a zombie door method to 373 // create the proper random chance of an object being created 374 // for this triangle. 375 double num = area / coverage + mt_rand(&seed); 376 377 if (num > MAX_RANDOM_OBJECTS) { 378 SG_LOG(SG_TERRAIN, SG_ALERT, 379 "Per-triangle random object count exceeded limits (" 380 << MAX_RANDOM_OBJECTS << ") " << num); 381 num = MAX_RANDOM_OBJECTS; 382 } 383 384 // place an object each unit of area 385 while ( num > 1.0 ) { 386 float a = mt_rand(&seed); 387 float b = mt_rand(&seed); 388 if ( a + b > 1 ) { 389 a = 1 - a; 390 b = 1 - b; 391 } 392 float c = 1 - a - b; 393 SGVec3f randomPoint = a*v0 + b*v1 + c*v2; 394 395 // Check that the point is sufficiently far from 396 // the edge of the triangle by measuring the distance 397 // from the three lines that make up the triangle. 398 if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) > spacing) && 399 ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) > spacing) && 400 ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) > spacing) ) 401 { 402 if (object_mask != NULL) { 403 SGVec2f texCoord = a*t0 + b*t1 + c*t2; 404 405 // Check this random point against the object mask 406 // blue (for buildings) channel. 407 osg::Image* img = object_mask->getImage(); 408 unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); 409 unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); 410 411 if (mt_rand(&seed) < img->getColor(x, y).b()) { 412 // The red channel contains the rotation for this object 413 points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r())); 414 } 415 } else { 416 points.push_back(std::make_pair(randomPoint, static_cast<float>(mt_rand(&seed)))); 417 } 418 } 419 num -= 1.0; 420 } 421 } 422 } 423 buildGeometry(const TriangleVector & triangles,bool useVBOs) const424 osg::Geometry* buildGeometry(const TriangleVector& triangles, bool useVBOs) const 425 { 426 // Do not build anything if there is nothing in here ... 427 if (empty() || triangles.empty()) 428 return 0; 429 430 // FIXME: do not include all values here ... 431 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; 432 osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; 433 osg::ref_ptr<osg::Vec2Array> priTexCoords = new osg::Vec2Array; 434 osg::ref_ptr<osg::Vec2Array> secTexCoords = new osg::Vec2Array; 435 436 osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; 437 colors->push_back(osg::Vec4(1, 1, 1, 1)); 438 439 osg::Geometry* geometry = new osg::Geometry; 440 if (useVBOs) { 441 geometry->setUseDisplayList(false); 442 geometry->setUseVertexBufferObjects(true); 443 } 444 445 geometry->setDataVariance(osg::Object::STATIC); 446 geometry->setVertexArray(vertices); 447 geometry->setNormalArray(normals); 448 geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); 449 geometry->setColorArray(colors); 450 geometry->setColorBinding(osg::Geometry::BIND_OVERALL); 451 if ( has_sec_tcs ) { 452 geometry->setTexCoordArray(0, priTexCoords); 453 geometry->setTexCoordArray(1, secTexCoords); 454 } else { 455 geometry->setTexCoordArray(0, priTexCoords); 456 } 457 458 const unsigned invalid = ~unsigned(0); 459 std::vector<unsigned> indexMap(getNumVertices(), invalid); 460 461 DrawElementsFacade deFacade; 462 for (index_type i = 0; i < triangles.size(); ++i) { 463 triangle_ref triangle = triangles[i]; 464 if (indexMap[triangle[0]] == invalid) { 465 indexMap[triangle[0]] = vertices->size(); 466 vertices->push_back(toOsg(getVertex(triangle[0]).GetVertex())); 467 normals->push_back(toOsg(getVertex(triangle[0]).GetNormal())); 468 priTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(0))); 469 if ( has_sec_tcs ) { 470 secTexCoords->push_back(toOsg(getVertex(triangle[0]).GetTexCoord(1))); 471 } 472 } 473 deFacade.push_back(indexMap[triangle[0]]); 474 475 if (indexMap[triangle[1]] == invalid) { 476 indexMap[triangle[1]] = vertices->size(); 477 vertices->push_back(toOsg(getVertex(triangle[1]).GetVertex())); 478 normals->push_back(toOsg(getVertex(triangle[1]).GetNormal())); 479 priTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(0))); 480 if ( has_sec_tcs ) { 481 secTexCoords->push_back(toOsg(getVertex(triangle[1]).GetTexCoord(1))); 482 } 483 } 484 deFacade.push_back(indexMap[triangle[1]]); 485 486 if (indexMap[triangle[2]] == invalid) { 487 indexMap[triangle[2]] = vertices->size(); 488 vertices->push_back(toOsg(getVertex(triangle[2]).GetVertex())); 489 normals->push_back(toOsg(getVertex(triangle[2]).GetNormal())); 490 priTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(0))); 491 if ( has_sec_tcs ) { 492 secTexCoords->push_back(toOsg(getVertex(triangle[2]).GetTexCoord(1))); 493 } 494 } 495 deFacade.push_back(indexMap[triangle[2]]); 496 } 497 geometry->addPrimitiveSet(deFacade.getDrawElements()); 498 499 return geometry; 500 } 501 buildGeometry(bool useVBOs) const502 osg::Geometry* buildGeometry(bool useVBOs) const 503 { return buildGeometry(getTriangles(), useVBOs); } 504 getTextureIndex() const505 int getTextureIndex() const 506 { 507 if (empty() || getNumTriangles() == 0) 508 return 0; 509 510 triangle_ref triangleRef = getTriangleRef(0); 511 SGVec3f v0 = getVertex(triangleRef[0]).GetVertex(); 512 513 return floor(v0.x()); 514 } 515 hasSecondaryTexCoord(bool sec_tc)516 void hasSecondaryTexCoord( bool sec_tc ) { has_sec_tcs = sec_tc; } 517 518 private: 519 // Random seed for the triangle. 520 mt seed; 521 522 // does the triangle array have secondary texture coordinates 523 bool has_sec_tcs; 524 }; 525 526 #endif 527