]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/SGTexturedTriangleBin.hxx
Some scene graph optimizations
[simgear.git] / simgear / scene / tgdb / SGTexturedTriangleBin.hxx
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 #include <osg/Array>
26 #include <osg/Geometry>
27 #include <osg/PrimitiveSet>
28
29 #include <simgear/math/sg_random.h>
30 #include <simgear/math/SGMath.hxx>
31 #include "SGTriangleBin.hxx"
32
33 struct SGVertNormTex {
34   SGVertNormTex()
35   { }
36   SGVertNormTex(const SGVec3f& v, const SGVec3f& n, const SGVec2f& t) :
37     vertex(v), normal(n), texCoord(t)
38   { }
39   struct less
40   {
41     inline bool operator() (const SGVertNormTex& l,
42                             const SGVertNormTex& r) const
43     {
44       if (l.vertex < r.vertex) return true;
45       else if (r.vertex < l.vertex) return false;
46       else if (l.normal < r.normal) return true;
47       else if (r.normal < l.normal) return false;
48       else return l.texCoord < r.texCoord;
49     }
50   };
51
52   SGVec3f vertex;
53   SGVec3f normal;
54   SGVec2f texCoord;
55 };
56
57 // Use a DrawElementsUShort if there are few enough vertices,
58 // otherwise fallback to DrawElementsUInt. Hide the differences
59 // between the two from the rest of the code.
60 //
61 // We don't bother with DrawElementsUByte because that is generally
62 // not an advantage on modern hardware.
63 class DrawElementsFacade {
64 public:
65     DrawElementsFacade(unsigned numVerts) :
66         _ushortElements(0), _uintElements(0)
67     {
68         if (numVerts > 65535)
69             _uintElements
70                 = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
71         else
72             _ushortElements
73                 = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES);
74     }
75     
76     void push_back(unsigned val)
77     {
78         if (_uintElements)
79             _uintElements->push_back(val);
80         else
81             _ushortElements->push_back(val);
82     }
83
84     osg::DrawElements* getDrawElements()
85     {
86         if (_uintElements)
87             return _uintElements;
88         return _ushortElements;
89     }
90 protected:
91     osg::DrawElementsUShort* _ushortElements;
92     osg::DrawElementsUInt* _uintElements;
93 };
94
95 class SGTexturedTriangleBin : public SGTriangleBin<SGVertNormTex> {
96 public:
97
98   // Computes and adds random surface points to the points list.
99   // The random points are computed with a density of (coverage points)/1
100   // The points are offsetted away from the triangles in
101   // offset * positive normal direction.
102   void addRandomSurfacePoints(float coverage, float offset,
103                               std::vector<SGVec3f>& points) const
104   {
105     unsigned num = getNumTriangles();
106     for (unsigned i = 0; i < num; ++i) {
107       triangle_ref triangleRef = getTriangleRef(i);
108       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
109       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
110       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
111       SGVec3f normal = cross(v1 - v0, v2 - v0);
112       
113       // Compute the area
114       float area = 0.5f*length(normal);
115       if (area <= SGLimitsf::min())
116         continue;
117       
118       // For partial units of area, use a zombie door method to
119       // create the proper random chance of a light being created
120       // for this triangle
121       float unit = area + sg_random()*coverage;
122       
123       SGVec3f offsetVector = offset*normalize(normal);
124       // generate a light point for each unit of area
125       while ( coverage < unit ) {
126         float a = sg_random();
127         float b = sg_random();
128         if ( a + b > 1 ) {
129           a = 1 - a;
130           b = 1 - b;
131         }
132         float c = 1 - a - b;
133         SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
134         points.push_back(randomPoint);
135         unit -= coverage;
136       }
137     }
138   }
139
140   osg::Geometry* buildGeometry(const TriangleVector& triangles) const
141   {
142     // Do not build anything if there is nothing in here ...
143     if (empty() || triangles.empty())
144       return 0;
145
146     // FIXME: do not include all values here ...
147     osg::Vec3Array* vertices = new osg::Vec3Array;
148     osg::Vec3Array* normals = new osg::Vec3Array;
149     osg::Vec2Array* texCoords = new osg::Vec2Array;
150
151     osg::Vec4Array* colors = new osg::Vec4Array;
152     colors->push_back(osg::Vec4(1, 1, 1, 1));
153
154     osg::Geometry* geometry = new osg::Geometry;
155     geometry->setVertexArray(vertices);
156     geometry->setNormalArray(normals);
157     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
158     geometry->setColorArray(colors);
159     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
160     geometry->setTexCoordArray(0, texCoords);
161
162     const unsigned invalid = ~unsigned(0);
163     std::vector<unsigned> indexMap(getNumVertices(), invalid);
164
165     DrawElementsFacade deFacade(vertices->size());
166     for (index_type i = 0; i < triangles.size(); ++i) {
167       triangle_ref triangle = triangles[i];
168       if (indexMap[triangle[0]] == invalid) {
169         indexMap[triangle[0]] = vertices->size();
170         vertices->push_back(getVertex(triangle[0]).vertex.osg());
171         normals->push_back(getVertex(triangle[0]).normal.osg());
172         texCoords->push_back(getVertex(triangle[0]).texCoord.osg());
173       }
174       deFacade.push_back(indexMap[triangle[0]]);
175
176       if (indexMap[triangle[1]] == invalid) {
177         indexMap[triangle[1]] = vertices->size();
178         vertices->push_back(getVertex(triangle[1]).vertex.osg());
179         normals->push_back(getVertex(triangle[1]).normal.osg());
180         texCoords->push_back(getVertex(triangle[1]).texCoord.osg());
181       }
182       deFacade.push_back(indexMap[triangle[1]]);
183
184       if (indexMap[triangle[2]] == invalid) {
185         indexMap[triangle[2]] = vertices->size();
186         vertices->push_back(getVertex(triangle[2]).vertex.osg());
187         normals->push_back(getVertex(triangle[2]).normal.osg());
188         texCoords->push_back(getVertex(triangle[2]).texCoord.osg());
189       }
190       deFacade.push_back(indexMap[triangle[2]]);
191     }
192     geometry->addPrimitiveSet(deFacade.getDrawElements());
193
194     return geometry;
195   }
196
197   osg::Geometry* buildGeometry() const
198   { return buildGeometry(getTriangles()); }
199 };
200
201 #endif