+ for (int j = 0; j < latPoints; j++) {
+ for (int i = 0; i < lonPoints; ++i) {
+ vlArray(j, i) = rel[j][i].osg();
+ nlArray(j, i) = normals[j][i].osg();
+ tlArray(j, i) = texsArray(j, i).toSGVec2f().osg();
+ }
+ }
+
+}
+
+// Apron points. For each point on the edge we'll go 150
+// metres "down" and 40 metres "out" to create a nice overlap. The
+// texture should be applied according to this dimension. The
+// normals of the apron polygons will be the same as the those of
+// the points on the edge to better disguise the apron.
+void OceanMesh::calcApronPt(int latIdx, int lonIdx, int latInner, int lonInner,
+ int destIdx, double tex_width)
+{
+ static const float downDist = 150.0f;
+ static const float outDist = 40.0f;
+ // Get vector along edge, in the right direction to make a cross
+ // product with the normal vector that will point out from the
+ // mesh.
+ osg::Vec3f edgePt = vlArray(latIdx, lonIdx);
+ osg::Vec3f edgeVec;
+ if (lonIdx == lonInner) { // bottom or top edge
+ if (lonIdx > 0)
+ edgeVec = vlArray(latIdx, lonIdx - 1) - edgePt;
+ else
+ edgeVec = edgePt - vlArray(latIdx, lonIdx + 1);
+ if (latIdx > latInner)
+ edgeVec = -edgeVec; // Top edge
+ } else { // right or left edge
+ if (latIdx > 0)
+ edgeVec = edgePt - vlArray(latIdx - 1, lonIdx);
+ else
+ edgeVec = vlArray(latIdx + 1, lonIdx) - edgePt;
+ if (lonIdx > lonInner) // right edge
+ edgeVec = -edgeVec;
+ }
+ edgeVec.normalize();
+ osg::Vec3f outVec = nlArray(latIdx, lonIdx) ^ edgeVec;
+ (*vl)[destIdx]
+ = edgePt - nlArray(latIdx, lonIdx) * downDist + outVec * outDist;
+ (*nl)[destIdx] = nlArray(latIdx, lonIdx);
+ static const float apronDist
+ = sqrtf(downDist * downDist + outDist * outDist);
+ float texDelta = apronDist / tex_width;
+ if (lonIdx == lonInner) {
+ if (latIdx > latInner)
+ (*tl)[destIdx]
+ = tlArray(latIdx, lonIdx) + osg::Vec2f(0.0f, texDelta);
+ else
+ (*tl)[destIdx]
+ = tlArray(latIdx, lonIdx) - osg::Vec2f(0.0f, texDelta);
+ } else {
+ if (lonIdx > lonInner)
+ (*tl)[destIdx]
+ = tlArray(latIdx, lonIdx) + osg::Vec2f(texDelta, 0.0f);
+ else
+ (*tl)[destIdx]
+ = tlArray(latIdx, lonIdx) - osg::Vec2f(texDelta, 0.0f);
+ }
+}
+
+void OceanMesh::calcApronPts(double tex_width)
+{
+ for (int i = 0; i < lonPoints; i++)
+ calcApronPt(0, i, 1, i, i, tex_width);
+ int topApronOffset = latPoints + (2 + lonPoints) * latPoints;
+ for (int i = 0; i < lonPoints; i++)
+ calcApronPt(latPoints - 1, i, latPoints - 2, i,
+ i + topApronOffset, tex_width);
+ for (int i = 0; i < latPoints; i++) {
+ calcApronPt(i, 0, i, 1, lonPoints + i * (lonPoints + 2), tex_width);
+ calcApronPt(i, lonPoints - 1, i, lonPoints - 2,
+ lonPoints + i * (lonPoints + 2) + 1 + lonPoints, tex_width);
+ }
+}
+
+namespace
+{
+// Enter the vertices of triangles that fill one row of the
+// mesh. The vertices are entered in counter-clockwise order.
+void fillDrawElementsRow(int width, short row0Start, short row1Start,
+ osg::DrawElementsUShort::vector_type::iterator&
+ elements)
+{
+ short row0Idx = row0Start;
+ short row1Idx = row1Start;
+ for (int i = 0; i < width - 1; i++, row0Idx++, row1Idx++) {
+ *elements++ = row0Idx;
+ *elements++ = row0Idx + 1;
+ *elements++ = row1Idx;
+ *elements++ = row1Idx;
+ *elements++ = row0Idx + 1;
+ *elements++ = row1Idx + 1;
+ }
+}
+
+void fillDrawElementsWithApron(short height, short width,
+ osg::DrawElementsUShort::vector_type::iterator
+ elements)
+{
+ // First apron row
+ fillDrawElementsRow(width, 0, width + 1, elements);
+ for (short i = 0; i < height - 1; i++)
+ fillDrawElementsRow(width + 2, width + i * (width + 2),
+ width + (i + 1) * (width + 2),
+ elements);
+ // Last apron row
+ short topApronBottom = width + (height - 1) * (width + 2) + 1;
+ fillDrawElementsRow(width, topApronBottom, topApronBottom + width + 1,
+ elements);
+}
+}
+
+osg::Node* SGOceanTile(const SGBucket& b, SGMaterialLib *matlib)
+{
+ osg::StateSet *stateSet = 0;
+
+ double tex_width = 1000.0;