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 {
40 SGVertNormTex(const SGVec3f& v, const SGVec3f& n, const SGVec2f& t) :
41 vertex(v), normal(n), texCoord(t)
45 inline bool operator() (const SGVertNormTex& l,
46 const SGVertNormTex& r) const
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;
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.
65 // We don't bother with DrawElementsUByte because that is generally
66 // not an advantage on modern hardware.
67 class DrawElementsFacade {
69 DrawElementsFacade(unsigned numVerts) :
70 _ushortElements(0), _uintElements(0)
74 = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
77 = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES);
80 void push_back(unsigned val)
83 _uintElements->push_back(val);
85 _ushortElements->push_back(val);
88 osg::DrawElements* getDrawElements()
92 return _ushortElements;
95 osg::DrawElementsUShort* _ushortElements;
96 osg::DrawElementsUInt* _uintElements;
99 class SGTexturedTriangleBin : public SGTriangleBin<SGVertNormTex> {
101 SGTexturedTriangleBin()
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)
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);
126 float area = 0.5f*length(normal);
127 if (area <= SGLimitsf::min())
130 // For partial units of area, use a zombie door method to
131 // create the proper random chance of a light being created
133 float unit = area + mt_rand(&seed)*coverage;
135 SGVec3f offsetVector = offset*normalize(normal);
136 // generate a light point for each unit of area
138 while ( coverage < unit ) {
140 float a = mt_rand(&seed);
141 float b = mt_rand(&seed);
148 SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
150 if (object_mask != NULL) {
151 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
153 // Check this random point against the object mask
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();
159 if (mt_rand(&seed) < img->getColor(x, y).r()) {
160 points.push_back(randomPoint);
163 // No object mask, so simply place the object
164 points.push_back(randomPoint);
171 // Computes and adds random surface points to the points list for tree
173 void addRandomTreePoints(float wood_coverage,
174 osg::Texture2D* object_mask,
175 float vegetation_density,
176 float cos_max_density_angle,
177 float cos_zero_density_angle,
178 std::vector<SGVec3f>& points)
180 unsigned num = getNumTriangles();
181 for (unsigned i = 0; i < num; ++i) {
182 triangle_ref triangleRef = getTriangleRef(i);
183 SGVec3f v0 = getVertex(triangleRef[0]).vertex;
184 SGVec3f v1 = getVertex(triangleRef[1]).vertex;
185 SGVec3f v2 = getVertex(triangleRef[2]).vertex;
186 SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
187 SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
188 SGVec2f t2 = getVertex(triangleRef[2]).texCoord;
189 SGVec3f normal = cross(v1 - v0, v2 - v0);
191 // Ensure the slope isn't too steep by checking the
192 // cos of the angle between the slope normal and the
193 // vertical (conveniently the z-component of the normalized
194 // normal) and values passed in.
195 float alpha = normalize(normal).z();
196 float slope_density = 1.0;
198 if (alpha < cos_zero_density_angle)
199 continue; // Too steep for any vegetation
201 if (alpha < cos_max_density_angle) {
203 (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
207 float area = 0.5f*length(normal);
208 if (area <= SGLimitsf::min())
211 // Determine the number of trees, taking into account vegetation
212 // density (which is linear) and the slope density factor.
213 // Use a zombie door method to create the proper random chance
214 // of a tree being created for partial values.
215 int woodcount = (int) (vegetation_density * vegetation_density *
217 area / wood_coverage + mt_rand(&seed));
219 for (int j = 0; j < woodcount; j++) {
220 float a = mt_rand(&seed);
221 float b = mt_rand(&seed);
223 if ( a + b > 1.0f ) {
228 float c = 1.0f - a - b;
230 SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
232 if (object_mask != NULL) {
233 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
235 // Check this random point against the object mask
236 // green (for trees) channel.
237 osg::Image* img = object_mask->getImage();
238 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
239 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
241 if (mt_rand(&seed) < img->getColor(x, y).g()) {
242 // The red channel contains the rotation for this object
243 points.push_back(randomPoint);
246 points.push_back(randomPoint);
252 void addRandomPoints(double coverage,
254 osg::Texture2D* object_mask,
255 std::vector<std::pair<SGVec3f, float> >& points)
257 unsigned num = getNumTriangles();
258 for (unsigned i = 0; i < num; ++i) {
259 triangle_ref triangleRef = getTriangleRef(i);
260 SGVec3f v0 = getVertex(triangleRef[0]).vertex;
261 SGVec3f v1 = getVertex(triangleRef[1]).vertex;
262 SGVec3f v2 = getVertex(triangleRef[2]).vertex;
263 SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
264 SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
265 SGVec2f t2 = getVertex(triangleRef[2]).texCoord;
266 SGVec3f normal = cross(v1 - v0, v2 - v0);
269 float area = 0.5f*length(normal);
270 if (area <= SGLimitsf::min())
273 // for partial units of area, use a zombie door method to
274 // create the proper random chance of an object being created
275 // for this triangle.
276 double num = area / coverage + mt_rand(&seed);
278 // place an object each unit of area
279 while ( num > 1.0 ) {
280 float a = mt_rand(&seed);
281 float b = mt_rand(&seed);
287 SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
289 // Check that the point is sufficiently far from
290 // the edge of the triangle by measuring the distance
291 // from the three lines that make up the triangle.
292 if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) > spacing) &&
293 ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) > spacing) &&
294 ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) > spacing) )
296 if (object_mask != NULL) {
297 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
299 // Check this random point against the object mask
300 // blue (for buildings) channel.
301 osg::Image* img = object_mask->getImage();
302 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
303 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
305 if (mt_rand(&seed) < img->getColor(x, y).b()) {
306 // The red channel contains the rotation for this object
307 points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r()));
310 points.push_back(std::make_pair(randomPoint, static_cast<float>(mt_rand(&seed))));
318 osg::Geometry* buildGeometry(const TriangleVector& triangles, bool useVBOs) const
320 // Do not build anything if there is nothing in here ...
321 if (empty() || triangles.empty())
324 // FIXME: do not include all values here ...
325 osg::Vec3Array* vertices = new osg::Vec3Array;
326 osg::Vec3Array* normals = new osg::Vec3Array;
327 osg::Vec2Array* texCoords = new osg::Vec2Array;
329 osg::Vec4Array* colors = new osg::Vec4Array;
330 colors->push_back(osg::Vec4(1, 1, 1, 1));
332 osg::Geometry* geometry = new osg::Geometry;
334 geometry->setUseDisplayList(false);
335 geometry->setUseVertexBufferObjects(true);
338 geometry->setDataVariance(osg::Object::STATIC);
339 geometry->setVertexArray(vertices);
340 geometry->setNormalArray(normals);
341 geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
342 geometry->setColorArray(colors);
343 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
344 geometry->setTexCoordArray(0, texCoords);
346 const unsigned invalid = ~unsigned(0);
347 std::vector<unsigned> indexMap(getNumVertices(), invalid);
349 DrawElementsFacade deFacade(vertices->size());
350 for (index_type i = 0; i < triangles.size(); ++i) {
351 triangle_ref triangle = triangles[i];
352 if (indexMap[triangle[0]] == invalid) {
353 indexMap[triangle[0]] = vertices->size();
354 vertices->push_back(toOsg(getVertex(triangle[0]).vertex));
355 normals->push_back(toOsg(getVertex(triangle[0]).normal));
356 texCoords->push_back(toOsg(getVertex(triangle[0]).texCoord));
358 deFacade.push_back(indexMap[triangle[0]]);
360 if (indexMap[triangle[1]] == invalid) {
361 indexMap[triangle[1]] = vertices->size();
362 vertices->push_back(toOsg(getVertex(triangle[1]).vertex));
363 normals->push_back(toOsg(getVertex(triangle[1]).normal));
364 texCoords->push_back(toOsg(getVertex(triangle[1]).texCoord));
366 deFacade.push_back(indexMap[triangle[1]]);
368 if (indexMap[triangle[2]] == invalid) {
369 indexMap[triangle[2]] = vertices->size();
370 vertices->push_back(toOsg(getVertex(triangle[2]).vertex));
371 normals->push_back(toOsg(getVertex(triangle[2]).normal));
372 texCoords->push_back(toOsg(getVertex(triangle[2]).texCoord));
374 deFacade.push_back(indexMap[triangle[2]]);
376 geometry->addPrimitiveSet(deFacade.getDrawElements());
381 osg::Geometry* buildGeometry(bool useVBOs) const
382 { return buildGeometry(getTriangles(), useVBOs); }
384 int getTextureIndex() const
386 if (empty() || getNumTriangles() == 0)
389 triangle_ref triangleRef = getTriangleRef(0);
390 SGVec3f v0 = getVertex(triangleRef[0]).vertex;
392 return floor(v0.x());
396 // Random seed for the triangle.