X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Ftgdb%2FSGOceanTile.cxx;h=9ff60f3e0c8729b84371968b5bef7ef0af3655fe;hb=c3b1802e956db11c2b06d354d90bca3ca9622a7b;hp=48b55562d1a2e0a81b4ccf01b3e79dff58136a31;hpb=d1dedc751174d989b2db89be6e8a0dc1a8bd084f;p=simgear.git diff --git a/simgear/scene/tgdb/SGOceanTile.cxx b/simgear/scene/tgdb/SGOceanTile.cxx index 48b55562..9ff60f3e 100644 --- a/simgear/scene/tgdb/SGOceanTile.cxx +++ b/simgear/scene/tgdb/SGOceanTile.cxx @@ -1,6 +1,6 @@ /* -*-c++-*- * - * Copyright (C) 2006-2007 Mathias Froehlich + * Copyright (C) 2006-2007 Mathias Froehlich, Tim Moore * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -25,6 +25,7 @@ #include "SGOceanTile.hxx" +#include #include #include @@ -36,102 +37,285 @@ #include #include #include +#include +#include #include #include -// Generate an ocean tile -osg::Node* SGOceanTile(const SGBucket& b, SGMaterialLib *matlib) +#include + +using namespace simgear; +// Ocean tile with curvature and apron to hide cracks. The cracks are +// mostly with adjoining coastal tiles that assume a flat ocean +// between corners of a tile; they also hide the micro cracks between +// adjoining ocean tiles. This is probably over-engineered, but it +// serves as a testbed for some things that will come later. + +// Helper class for building and accessing the mesh. The layout of the +// points in the mesh is a little wacky. First is the bottom row of +// the points for the apron. Next is the left apron point, the points +// in the mesh, and the right apron point, for each of the rows of the +// mesh; the points for the top apron come last. This order should +// help with things like vertex caching in the OpenGL driver, though +// it may be superfluous for such a small mesh. +namespace { - osg::StateSet *stateSet = 0; +const int lonPoints = 5; +const int latPoints = 5; - double tex_width = 1000.0; - - // find Ocean material in the properties list - SGMaterial *mat = matlib->find( "Ocean" ); - if ( mat != NULL ) { - // set the texture width and height values for this - // material - tex_width = mat->get_xsize(); +class OceanMesh { +public: + OceanMesh(): + geoPoints(latPoints * lonPoints + 2 * (lonPoints + latPoints)), + geod_nodes(latPoints * lonPoints), + vl(new osg::Vec3Array(geoPoints)), + nl(new osg::Vec3Array(geoPoints)), + tl(new osg::Vec2Array(geoPoints)), + vlArray(*vl, lonPoints + 2, lonPoints, 1), + nlArray(*nl, lonPoints + 2, lonPoints, 1), + tlArray(*tl, lonPoints + 2, lonPoints, 1) + { + } + const int geoPoints; + SGGeod geod[latPoints][lonPoints]; + SGVec3f normals[latPoints][lonPoints]; + SGVec3d rel[latPoints][lonPoints]; + + point_list geod_nodes; + + osg::Vec3Array* vl; + osg::Vec3Array* nl; + osg::Vec2Array* tl; + VectorArrayAdapter vlArray; + VectorArrayAdapter nlArray; + VectorArrayAdapter tlArray; + + void calcMesh(const SGVec3d& cartCenter, const SGQuatd& orient, + double clon, double clat, + double height, double width, double tex_width); + void calcApronPt(int latIdx, int lonIdx, int latInner, int lonInner, + int destIdx, double tex_width); + void calcApronPts(double tex_width); - // set ssgState - stateSet = mat->get_state(); - } else { - SG_LOG( SG_TERRAIN, SG_ALERT, "Ack! unknown use material name = Ocean"); - } - - // Calculate center point - SGVec3d cartCenter = SGVec3d::fromGeod(b.get_center()); - Point3D center = Point3D(cartCenter[0], cartCenter[1], cartCenter[2]); - - double clon = b.get_center_lon(); - double clat = b.get_center_lat(); - double height = b.get_height(); - double width = b.get_width(); - - // Caculate corner vertices - SGGeod geod[4]; - geod[0] = SGGeod::fromDeg( clon - 0.5*width, clat - 0.5*height ); - geod[1] = SGGeod::fromDeg( clon + 0.5*width, clat - 0.5*height ); - geod[2] = SGGeod::fromDeg( clon + 0.5*width, clat + 0.5*height ); - geod[3] = SGGeod::fromDeg( clon - 0.5*width, clat + 0.5*height ); - - int i; - SGVec3f normals[4]; - SGVec3d rel[4]; - for ( i = 0; i < 4; ++i ) { - SGVec3d cart = SGVec3d::fromGeod(geod[i]); - rel[i] = cart - center.toSGVec3d(); - normals[i] = toVec3f(normalize(cart)); - } - - // Calculate texture coordinates - point_list geod_nodes; - geod_nodes.clear(); - geod_nodes.reserve(4); - int_list rectangle; - rectangle.clear(); - rectangle.reserve(4); - for ( i = 0; i < 4; ++i ) { - geod_nodes.push_back( Point3D::fromSGGeod(geod[i]) ); - rectangle.push_back( i ); - } - point_list texs = sgCalcTexCoords( b, geod_nodes, rectangle, - 1000.0 / tex_width ); +}; +} + +void OceanMesh::calcMesh(const SGVec3d& cartCenter, const SGQuatd& orient, + double clon, double clat, + double height, double width, double tex_width) +{ + // Calculate vertices. By splitting the tile up into 4 quads on a + // side we avoid curvature-of-the-earth problems; the error should + // be less than .5 meters. + double longInc = width * .25; + double latInc = height * .25; + double startLat = clat - height * .5; + double startLon = clon - width * .5; + for (int j = 0; j < latPoints; j++) { + double lat = startLat + j * latInc; + for (int i = 0; i < lonPoints; i++) { + geod[j][i] = SGGeod::fromDeg(startLon + i * longInc, lat); + SGVec3d cart = SGVec3d::fromGeod(geod[j][i]); + rel[j][i] = orient.transform(cart - cartCenter); + normals[j][i] = toVec3f(orient.transform(normalize(cart))); + } + } + + // Calculate texture coordinates + point_list geod_nodes(latPoints * lonPoints); + VectorArrayAdapter geodNodesArray(geod_nodes, lonPoints); + int_list rectangle(latPoints * lonPoints); + VectorArrayAdapter rectArray(rectangle, lonPoints); + for (int j = 0; j < latPoints; j++) { + for (int i = 0; i < lonPoints; i++) { + geodNodesArray(j, i) = Point3D(geod[j][i].getLongitudeDeg(), + geod[j][i].getLatitudeDeg(), + geod[j][i].getElevationM()); + rectArray(j, i) = j * 5 + i; + } + } + point_list texs = sgCalcTexCoords( clat, geod_nodes, rectangle, + 1000.0 / tex_width ); + VectorArrayAdapter texsArray(texs, lonPoints); - // Allocate ssg structure - osg::Vec3Array *vl = new osg::Vec3Array; - osg::Vec3Array *nl = new osg::Vec3Array; - osg::Vec2Array *tl = new osg::Vec2Array; + 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) +{ + Effect *effect = 0; + + double tex_width = 1000.0; - for ( i = 0; i < 4; ++i ) { - vl->push_back(rel[i].osg()); - nl->push_back(normals[i].osg()); - tl->push_back(texs[i].toSGVec2f().osg()); - } + // find Ocean material in the properties list + SGMaterial *mat = matlib->find( "Ocean" ); + if ( mat != NULL ) { + // set the texture width and height values for this + // material + tex_width = mat->get_xsize(); + + // set OSG State + effect = mat->get_effect(); + } else { + SG_LOG( SG_TERRAIN, SG_ALERT, "Ack! unknown use material name = Ocean"); + } + OceanMesh grid; + // Calculate center point + SGVec3d cartCenter = SGVec3d::fromGeod(b.get_center()); + SGGeod geodPos = SGGeod::fromCart(cartCenter); + SGQuatd hlOr = SGQuatd::fromLonLat(geodPos); + + double clon = b.get_center_lon(); + double clat = b.get_center_lat(); + double height = b.get_height(); + double width = b.get_width(); + + grid.calcMesh(cartCenter, hlOr, clon, clat, height, width, tex_width); + grid.calcApronPts(tex_width); - osg::Vec4Array* cl = new osg::Vec4Array; - cl->push_back(osg::Vec4(1, 1, 1, 1)); + osg::Vec4Array* cl = new osg::Vec4Array; + cl->push_back(osg::Vec4(1, 1, 1, 1)); - osg::Geometry* geometry = new osg::Geometry; - geometry->setVertexArray(vl); - geometry->setNormalArray(nl); - geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); - geometry->setColorArray(cl); - geometry->setColorBinding(osg::Geometry::BIND_OVERALL); - geometry->setTexCoordArray(0, tl); - osg::DrawArrays* drawArrays; - drawArrays = new osg::DrawArrays(GL_TRIANGLE_FAN, 0, vl->size()); - geometry->addPrimitiveSet(drawArrays); - - osg::Geode* geode = new osg::Geode; - geode->setName("Ocean tile"); - geode->addDrawable(geometry); - geode->setStateSet(stateSet); - - osg::MatrixTransform* transform = new osg::MatrixTransform; - transform->setName("Ocean"); - transform->setMatrix(osg::Matrix::translate(cartCenter.osg())); - transform->addChild(geode); + osg::Geometry* geometry = new osg::Geometry; + geometry->setVertexArray(grid.vl); + geometry->setNormalArray(grid.nl); + geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); + geometry->setColorArray(cl); + geometry->setColorBinding(osg::Geometry::BIND_OVERALL); + geometry->setTexCoordArray(0, grid.tl); + + // Allocate the indices for triangles in the mesh and the apron + osg::DrawElementsUShort* drawElements + = new osg::DrawElementsUShort(GL_TRIANGLES, + 6 * ((latPoints - 1) * (lonPoints + 1) + + 2 * (latPoints - 1))); + fillDrawElementsWithApron(latPoints, lonPoints, drawElements->begin()); + geometry->addPrimitiveSet(drawElements); + + EffectGeode* geode = new EffectGeode; + geode->setName("Ocean tile"); + geode->setEffect(effect); + geode->addDrawable(geometry); + + osg::MatrixTransform* transform = new osg::MatrixTransform; + transform->setName("Ocean"); + transform->setMatrix(osg::Matrix::rotate(hlOr.osg())* + osg::Matrix::translate(cartCenter.osg())); + transform->addChild(geode); - return transform; + return transform; }