]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/SGOceanTile.cxx
Remove using std:: from the metar header, remove HTTP support, add very basic unit...
[simgear.git] / simgear / scene / tgdb / SGOceanTile.cxx
1 /* -*-c++-*-
2  *
3  * Copyright (C) 2006-2007 Mathias Froehlich, Tim Moore
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 #ifdef HAVE_CONFIG_H
23 #  include <simgear_config.h>
24 #endif
25
26 #include "SGOceanTile.hxx"
27
28 #include <math.h>
29 #include <simgear/compiler.h>
30
31 #include <osg/Geode>
32 #include <osg/Geometry>
33 #include <osg/MatrixTransform>
34 #include <osg/StateSet>
35
36 #include <simgear/bucket/newbucket.hxx>
37 #include <simgear/math/sg_geodesy.hxx>
38 #include <simgear/math/sg_types.hxx>
39 #include <simgear/misc/texcoord.hxx>
40 #include <simgear/scene/material/Effect.hxx>
41 #include <simgear/scene/material/EffectGeode.hxx>
42 #include <simgear/scene/material/mat.hxx>
43 #include <simgear/scene/material/matlib.hxx>
44
45 #include <simgear/scene/util/VectorArrayAdapter.hxx>
46
47 using namespace simgear;
48 // Ocean tile with curvature and apron to hide cracks. The cracks are
49 // mostly with adjoining coastal tiles that assume a flat ocean
50 // between corners of a tile; they also hide the micro cracks between
51 // adjoining ocean tiles. This is probably over-engineered, but it
52 // serves as a testbed for some things that will come later.
53
54 // Helper class for building and accessing the mesh. The layout of the
55 // points in the mesh is a little wacky. First is the bottom row of
56 // the points for the apron. Next is the left apron point, the points
57 // in the mesh, and the right apron point, for each of the rows of the
58 // mesh; the points for the top apron come last. This order should
59 // help with things like vertex caching in the OpenGL driver, though
60 // it may be superfluous for such a small mesh.
61 namespace
62 {
63 const int lonPoints = 5;
64 const int latPoints = 5;
65
66 class OceanMesh {
67 public:
68     OceanMesh():
69         geoPoints(latPoints * lonPoints + 2 * (lonPoints + latPoints)),
70         geod_nodes(latPoints * lonPoints),
71         vl(new osg::Vec3Array(geoPoints)),
72         nl(new osg::Vec3Array(geoPoints)),
73         tl(new osg::Vec2Array(geoPoints)),
74         vlArray(*vl, lonPoints + 2, lonPoints, 1),
75         nlArray(*nl, lonPoints + 2, lonPoints, 1),
76         tlArray(*tl, lonPoints + 2, lonPoints, 1)
77     {
78     }
79     const int geoPoints;
80     SGGeod geod[latPoints][lonPoints];
81     SGVec3f normals[latPoints][lonPoints];
82     SGVec3d rel[latPoints][lonPoints];
83
84     std::vector<SGGeod> geod_nodes;
85
86     osg::Vec3Array* vl;
87     osg::Vec3Array* nl;
88     osg::Vec2Array* tl;
89     VectorArrayAdapter<osg::Vec3Array> vlArray;
90     VectorArrayAdapter<osg::Vec3Array> nlArray;
91     VectorArrayAdapter<osg::Vec2Array> tlArray;
92
93     void calcMesh(const SGVec3d& cartCenter, const SGQuatd& orient,
94                   double clon, double clat,
95                   double height, double width, double tex_width);
96     void calcApronPt(int latIdx, int lonIdx, int latInner, int lonInner,
97                      int destIdx, double tex_width);
98     void calcApronPts(double tex_width);
99     
100 };
101 }
102
103 void OceanMesh::calcMesh(const SGVec3d& cartCenter, const SGQuatd& orient,
104                          double clon, double clat,
105                          double height, double width, double tex_width)
106 {
107     // Calculate vertices. By splitting the tile up into 4 quads on a
108     // side we avoid curvature-of-the-earth problems; the error should
109     // be less than .5 meters.
110     double longInc = width * .25;
111     double latInc = height * .25;
112     double startLat = clat - height * .5;
113     double startLon = clon - width * .5;
114     for (int j = 0; j < latPoints; j++) {
115         double lat = startLat + j * latInc;
116         for (int i = 0; i < lonPoints; i++) {
117             geod[j][i] = SGGeod::fromDeg(startLon + i * longInc, lat);
118             SGVec3d cart = SGVec3d::fromGeod(geod[j][i]);
119             rel[j][i] = orient.transform(cart - cartCenter);
120             normals[j][i] = toVec3f(orient.transform(normalize(cart)));
121         }
122     }
123     
124     // Calculate texture coordinates
125     typedef std::vector<SGGeod> GeodVector;
126     
127     GeodVector geod_nodes(latPoints * lonPoints);
128     VectorArrayAdapter<GeodVector> geodNodesArray(geod_nodes, lonPoints);
129     int_list rectangle(latPoints * lonPoints);
130     VectorArrayAdapter<int_list> rectArray(rectangle, lonPoints);
131     for (int j = 0; j < latPoints; j++) {
132         for (int i = 0; i < lonPoints; i++) {
133             geodNodesArray(j, i) = geod[j][i];
134             rectArray(j, i) = j * 5 + i;
135         }
136     }
137     
138     typedef std::vector<SGVec2f> Vec2Array;
139     Vec2Array texs = sgCalcTexCoords( clat, geod_nodes, rectangle, 
140                                        1000.0 / tex_width );
141                                 
142                                        
143     VectorArrayAdapter<Vec2Array> texsArray(texs, lonPoints);
144   
145     for (int j = 0; j < latPoints; j++) {
146         for (int i = 0; i < lonPoints; ++i) {
147             vlArray(j, i) = toOsg(rel[j][i]);
148             nlArray(j, i) = toOsg(normals[j][i]);
149             tlArray(j, i) = toOsg(texsArray(j, i));
150         }
151     }
152
153 }
154
155 // Apron points. For each point on the edge we'll go 150
156 // metres "down" and 40 metres "out" to create a nice overlap. The
157 // texture should be applied according to this dimension. The
158 // normals of the apron polygons will be the same as the those of
159 // the points on the edge to better disguise the apron.
160 void OceanMesh::calcApronPt(int latIdx, int lonIdx, int latInner, int lonInner,
161                             int destIdx, double tex_width)
162 {
163     static const float downDist = 150.0f;
164     static const float outDist = 40.0f;
165     // Get vector along edge, in the right direction to make a cross
166     // product with the normal vector that will point out from the
167     // mesh.
168     osg::Vec3f edgePt = vlArray(latIdx, lonIdx);
169     osg::Vec3f edgeVec;
170     if (lonIdx == lonInner) {   // bottom or top edge
171         if (lonIdx > 0)
172             edgeVec = vlArray(latIdx, lonIdx - 1) - edgePt;
173         else
174             edgeVec = edgePt - vlArray(latIdx, lonIdx + 1);
175         if (latIdx > latInner)
176             edgeVec = -edgeVec;  // Top edge
177     } else {                     // right or left edge
178         if (latIdx > 0)
179             edgeVec = edgePt - vlArray(latIdx - 1, lonIdx);
180         else
181             edgeVec = vlArray(latIdx + 1, lonIdx) - edgePt;
182         if (lonIdx > lonInner)  // right edge
183             edgeVec = -edgeVec;
184     }
185     edgeVec.normalize();
186     osg::Vec3f outVec = nlArray(latIdx, lonIdx) ^ edgeVec;
187     (*vl)[destIdx]
188         = edgePt - nlArray(latIdx, lonIdx) * downDist + outVec * outDist;
189     (*nl)[destIdx] = nlArray(latIdx, lonIdx);
190     static const float apronDist
191         = sqrtf(downDist * downDist  + outDist * outDist);
192     float texDelta = apronDist / tex_width;
193     if (lonIdx == lonInner) {
194         if (latIdx > latInner)
195             (*tl)[destIdx]
196                 = tlArray(latIdx, lonIdx) + osg::Vec2f(0.0f, texDelta);
197         else
198             (*tl)[destIdx]
199                 = tlArray(latIdx, lonIdx) - osg::Vec2f(0.0f, texDelta);
200     } else {
201         if (lonIdx > lonInner)
202             (*tl)[destIdx]
203                 = tlArray(latIdx, lonIdx) + osg::Vec2f(texDelta, 0.0f);
204         else
205             (*tl)[destIdx]
206                 = tlArray(latIdx, lonIdx) - osg::Vec2f(texDelta, 0.0f);
207     }
208 }
209
210 void OceanMesh::calcApronPts(double tex_width)
211 {
212     for (int i = 0; i < lonPoints; i++)
213         calcApronPt(0, i, 1, i, i, tex_width);
214     int topApronOffset = latPoints + (2 + lonPoints) * latPoints;
215     for (int i = 0; i < lonPoints; i++)
216         calcApronPt(latPoints - 1, i, latPoints - 2, i,
217                     i + topApronOffset, tex_width);
218     for (int i = 0; i < latPoints; i++) {
219         calcApronPt(i, 0, i, 1, lonPoints + i * (lonPoints + 2), tex_width);
220         calcApronPt(i, lonPoints - 1, i, lonPoints - 2,
221                     lonPoints + i * (lonPoints + 2) + 1 + lonPoints, tex_width);
222     }
223 }
224
225 namespace
226 {
227 // Enter the vertices of triangles that fill one row of the
228 // mesh. The vertices are entered in counter-clockwise order.
229 void fillDrawElementsRow(int width, short row0Start, short row1Start,
230                          osg::DrawElementsUShort::vector_type::iterator&
231                          elements)
232 {
233     short row0Idx = row0Start;
234     short row1Idx = row1Start;
235     for (int i = 0; i < width - 1; i++, row0Idx++, row1Idx++) {
236         *elements++ = row0Idx;
237         *elements++ = row0Idx + 1;
238         *elements++ = row1Idx;
239         *elements++ = row1Idx;
240         *elements++ = row0Idx + 1;
241         *elements++ = row1Idx + 1;
242     }
243 }
244
245 void fillDrawElementsWithApron(short height, short width,
246                                osg::DrawElementsUShort::vector_type::iterator
247                                elements)
248 {
249     // First apron row
250     fillDrawElementsRow(width, 0, width + 1, elements);
251     for (short i = 0; i < height - 1; i++)
252         fillDrawElementsRow(width + 2, width + i * (width + 2),
253                             width + (i + 1) * (width + 2),
254                             elements);
255     // Last apron row
256     short topApronBottom = width + (height - 1) * (width + 2) + 1;
257     fillDrawElementsRow(width, topApronBottom, topApronBottom + width + 1,
258                         elements);
259 }
260 }
261
262 osg::Node* SGOceanTile(const SGBucket& b, SGMaterialLib *matlib)
263 {
264     Effect *effect = 0;
265
266     double tex_width = 1000.0;
267   
268     // find Ocean material in the properties list
269     SGMaterial *mat = matlib->find( "Ocean" );
270     if ( mat != NULL ) {
271         // set the texture width and height values for this
272         // material
273         tex_width = mat->get_xsize();
274     
275         // set OSG State
276         effect = mat->get_effect();
277     } else {
278         SG_LOG( SG_TERRAIN, SG_ALERT, "Ack! unknown use material name = Ocean");
279     }
280     OceanMesh grid;
281     // Calculate center point
282     SGVec3d cartCenter = SGVec3d::fromGeod(b.get_center());
283     SGGeod geodPos = SGGeod::fromCart(cartCenter);
284     SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
285
286     double clon = b.get_center_lon();
287     double clat = b.get_center_lat();
288     double height = b.get_height();
289     double width = b.get_width();
290
291     grid.calcMesh(cartCenter, hlOr, clon, clat, height, width, tex_width);
292     grid.calcApronPts(tex_width);
293   
294     osg::Vec4Array* cl = new osg::Vec4Array;
295     cl->push_back(osg::Vec4(1, 1, 1, 1));
296   
297     osg::Geometry* geometry = new osg::Geometry;
298     geometry->setVertexArray(grid.vl);
299     geometry->setNormalArray(grid.nl);
300     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
301     geometry->setColorArray(cl);
302     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
303     geometry->setTexCoordArray(0, grid.tl);
304
305     // Allocate the indices for triangles in the mesh and the apron
306     osg::DrawElementsUShort* drawElements
307         = new osg::DrawElementsUShort(GL_TRIANGLES,
308                                       6 * ((latPoints - 1) * (lonPoints + 1)
309                                            + 2 * (latPoints - 1)));
310     fillDrawElementsWithApron(latPoints, lonPoints, drawElements->begin());
311     geometry->addPrimitiveSet(drawElements);
312
313     EffectGeode* geode = new EffectGeode;
314     geode->setName("Ocean tile");
315     geode->setEffect(effect);
316     geode->addDrawable(geometry);
317     geode->runGenerators(geometry);
318
319     osg::MatrixTransform* transform = new osg::MatrixTransform;
320     transform->setName("Ocean");
321     transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
322                          osg::Matrix::translate(toOsg(cartCenter)));
323     transform->addChild(geode);
324   
325     return transform;
326 }