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 std::vector<SGVec3f>& points)
178 unsigned num = getNumTriangles();
179 for (unsigned i = 0; i < num; ++i) {
180 triangle_ref triangleRef = getTriangleRef(i);
181 SGVec3f v0 = getVertex(triangleRef[0]).vertex;
182 SGVec3f v1 = getVertex(triangleRef[1]).vertex;
183 SGVec3f v2 = getVertex(triangleRef[2]).vertex;
184 SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
185 SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
186 SGVec2f t2 = getVertex(triangleRef[2]).texCoord;
187 SGVec3f normal = cross(v1 - v0, v2 - v0);
190 float area = 0.5f*length(normal);
191 if (area <= SGLimitsf::min())
194 // For partial units of area, use a zombie door method to
195 // create the proper random chance of a point being created
197 float unit = area + mt_rand(&seed)*wood_coverage;
199 // Vegetation density is linear, while we're creating woodland
201 int woodcount = (int) (vegetation_density *
203 unit / wood_coverage);
205 for (int j = 0; j < woodcount; j++) {
206 float a = mt_rand(&seed);
207 float b = mt_rand(&seed);
209 if ( a + b > 1.0f ) {
214 float c = 1.0f - a - b;
216 SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
218 if (object_mask != NULL) {
219 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
221 // Check this random point against the object mask
222 // green (for trees) channel.
223 osg::Image* img = object_mask->getImage();
224 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
225 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
227 if (mt_rand(&seed) < img->getColor(x, y).g()) {
228 // The red channel contains the rotation for this object
229 points.push_back(randomPoint);
232 points.push_back(randomPoint);
238 void addRandomPoints(double coverage,
240 osg::Texture2D* object_mask,
241 std::vector<std::pair<SGVec3f, float> >& points)
243 unsigned num = getNumTriangles();
244 for (unsigned i = 0; i < num; ++i) {
245 triangle_ref triangleRef = getTriangleRef(i);
246 SGVec3f v0 = getVertex(triangleRef[0]).vertex;
247 SGVec3f v1 = getVertex(triangleRef[1]).vertex;
248 SGVec3f v2 = getVertex(triangleRef[2]).vertex;
249 SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
250 SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
251 SGVec2f t2 = getVertex(triangleRef[2]).texCoord;
252 SGVec3f normal = cross(v1 - v0, v2 - v0);
255 float area = 0.5f*length(normal);
256 if (area <= SGLimitsf::min())
259 // for partial units of area, use a zombie door method to
260 // create the proper random chance of an object being created
261 // for this triangle.
262 double num = area / coverage + mt_rand(&seed);
264 // place an object each unit of area
265 while ( num > 1.0 ) {
266 float a = mt_rand(&seed);
267 float b = mt_rand(&seed);
273 SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
275 // Check that the point is sufficiently far from
276 // the edge of the triangle by measuring the distance
277 // from the three lines that make up the triangle.
278 if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) > spacing) &&
279 ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) > spacing) &&
280 ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) > spacing) )
282 if (object_mask != NULL) {
283 SGVec2f texCoord = a*t0 + b*t1 + c*t2;
285 // Check this random point against the object mask
286 // blue (for buildings) channel.
287 osg::Image* img = object_mask->getImage();
288 unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
289 unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
291 if (mt_rand(&seed) < img->getColor(x, y).b()) {
292 // The red channel contains the rotation for this object
293 points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r()));
296 points.push_back(std::make_pair(randomPoint, mt_rand(&seed)));
304 osg::Geometry* buildGeometry(const TriangleVector& triangles) const
306 // Do not build anything if there is nothing in here ...
307 if (empty() || triangles.empty())
310 // FIXME: do not include all values here ...
311 osg::Vec3Array* vertices = new osg::Vec3Array;
312 osg::Vec3Array* normals = new osg::Vec3Array;
313 osg::Vec2Array* texCoords = new osg::Vec2Array;
315 osg::Vec4Array* colors = new osg::Vec4Array;
316 colors->push_back(osg::Vec4(1, 1, 1, 1));
318 osg::Geometry* geometry = new osg::Geometry;
319 geometry->setVertexArray(vertices);
320 geometry->setNormalArray(normals);
321 geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
322 geometry->setColorArray(colors);
323 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
324 geometry->setTexCoordArray(0, texCoords);
326 const unsigned invalid = ~unsigned(0);
327 std::vector<unsigned> indexMap(getNumVertices(), invalid);
329 DrawElementsFacade deFacade(vertices->size());
330 for (index_type i = 0; i < triangles.size(); ++i) {
331 triangle_ref triangle = triangles[i];
332 if (indexMap[triangle[0]] == invalid) {
333 indexMap[triangle[0]] = vertices->size();
334 vertices->push_back(toOsg(getVertex(triangle[0]).vertex));
335 normals->push_back(toOsg(getVertex(triangle[0]).normal));
336 texCoords->push_back(toOsg(getVertex(triangle[0]).texCoord));
338 deFacade.push_back(indexMap[triangle[0]]);
340 if (indexMap[triangle[1]] == invalid) {
341 indexMap[triangle[1]] = vertices->size();
342 vertices->push_back(toOsg(getVertex(triangle[1]).vertex));
343 normals->push_back(toOsg(getVertex(triangle[1]).normal));
344 texCoords->push_back(toOsg(getVertex(triangle[1]).texCoord));
346 deFacade.push_back(indexMap[triangle[1]]);
348 if (indexMap[triangle[2]] == invalid) {
349 indexMap[triangle[2]] = vertices->size();
350 vertices->push_back(toOsg(getVertex(triangle[2]).vertex));
351 normals->push_back(toOsg(getVertex(triangle[2]).normal));
352 texCoords->push_back(toOsg(getVertex(triangle[2]).texCoord));
354 deFacade.push_back(indexMap[triangle[2]]);
356 geometry->addPrimitiveSet(deFacade.getDrawElements());
361 osg::Geometry* buildGeometry() const
362 { return buildGeometry(getTriangles()); }
364 int getTextureIndex() {
365 if (empty() || getNumTriangles() == 0)
368 triangle_ref triangleRef = getTriangleRef(0);
369 SGVec3f v0 = getVertex(triangleRef[0]).vertex;
371 return floor(v0.x());
375 // Random seed for the triangle.