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