osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthDrivers/engine_seamless/Geographic.cpp

Go to the documentation of this file.
00001 #include "Geographic"
00002 
00003 #include <algorithm>
00004 #include <cmath>
00005 #include <iterator>
00006 #include <vector>
00007 
00008 #include <osg/ClusterCullingCallback>
00009 #include <osg/CoordinateSystemNode>
00010 #include <osg/Math>
00011 #include <osg/NodeCallback>
00012 #include <osg/NodeVisitor>
00013 #include <osg/Texture2D>
00014 
00015 #include <osgEarth/ImageUtils>
00016 #include <osgEarth/Notify>
00017 #include <osgEarth/VerticalSpatialReference>
00018 #include <osgEarth/TaskService>
00019 
00020 #include "GeoPatch"
00021 #include "MultiArray"
00022 
00023 namespace seamless
00024 {
00025 using namespace std;
00026 using namespace osg;
00027 using namespace osgEarth;
00028 
00029 typedef multi_array_ref<Vec3f, Vec3Array, 2> PatchArray;
00030 
00031 Geographic::Geographic(const Map* map,
00032                        const osgEarth::Drivers::SeamlessOptions& options)
00033     : PatchSet(options, new PatchOptions), _profile(new EulerProfile),
00034       _eModel(new EllipsoidModel)
00035 {
00036     setPrecisionFactor(8);
00037     setMap(map);
00038     {
00039         int maxLevel = 0;
00040         const ElevationLayerVector& elevations = _mapf->elevationLayers();
00041         for (ElevationLayerVector::const_iterator itr = elevations.begin(),
00042                  end = elevations.end();
00043              itr != end;
00044              ++itr)
00045         {
00046             const TerrainLayerOptions& options
00047                 = (*itr)->getTerrainLayerOptions();
00048             if (options.maxLevel().isSet()
00049                 && options.maxLevel().get() > maxLevel)
00050                 maxLevel = options.maxLevel().get();
00051         }
00052         if (maxLevel > 0)
00053             setMaxLevel(maxLevel);
00054 
00055     }
00056     int serviceThreads = computeLoadingThreads(_options.loadingPolicy().get());
00057     _hfService = new TaskService("Height Field Service", serviceThreads);
00058     _imageService = new TaskService("Image Service", serviceThreads);
00059 }
00060 
00061 Geographic::Geographic(const Geographic& rhs, const osg::CopyOp& copyop)
00062     : PatchSet(rhs, copyop),
00063       _profile(static_cast<EulerProfile*>(copyop(rhs._profile.get()))),
00064       _eModel(static_cast<EllipsoidModel*>(copyop(rhs._eModel.get()))),
00065       _hfService(rhs._hfService), _imageService(rhs._imageService)
00066 {
00067 }
00068 
00069 Geographic::~Geographic()
00070 {
00071 }
00072 
00073 Node* Geographic::createPatchSetGraph(const std::string& filename)
00074 {
00075     CoordinateSystemNode* csn = new CoordinateSystemNode;
00076     // Should these values come from the map profile?
00077     csn->setCoordinateSystem("EPSG:4326");
00078     csn->setFormat("WKT");
00079     csn->setEllipsoidModel(_eModel.get());
00080     for (int face = 0; face < 6; ++face)
00081     {
00082         double x = 0.0, y = 0.0;
00083         euler::faceToCube(x, y, face);
00084         PatchOptions* poptions = static_cast<PatchOptions*>(
00085             osg::clone(getPatchOptionsPrototype()));
00086         poptions->setPatchSet(this);
00087         poptions->setTileKey(_profile->createTileKey(x, y, 2));
00088         Node* node = createPatchGroup("foobar.osgearth_engine_seamless_patch",
00089                                       poptions);
00090         csn->addChild(node);
00091     }
00092     return csn;
00093 }
00094 
00095 namespace
00096 {
00097 
00098 GeoHeightField
00099 mergeHeightFields(const GeoExtent& targetExtent, const GeoHeightFieldVector& hfs)
00100 {
00101     if (hfs.size() != 4)
00102     {
00103         OE_FATAL << "mergeHeightFields expected 4 height fields\n";
00104         return GeoHeightField();
00105     }
00106     // List is in tile subkey quadrant order.
00107     // Assume the height fields all have the same dimensions
00108     unsigned targetCols = hfs[0].getHeightField()->getNumColumns() * 2 - 1;
00109     unsigned targetRows = hfs[0].getHeightField()->getNumRows() * 2 - 1;
00110     HeightField* targethf = new HeightField;
00111     targethf->allocate(targetCols, targetRows);
00112     GeoHeightField geo(targethf, targetExtent, 0);
00113     for (int i = 0; i < 4; ++i)
00114     {
00115         const HeightField* src = hfs[i].getHeightField();
00116         unsigned targetColumn
00117             = floor((hfs[i].getExtent().xMin() - targetExtent.xMin())
00118                     / targetExtent.width() * (targetCols - 1) + .5);
00119         unsigned targetRow
00120             = floor((hfs[i].getExtent().yMin() - targetExtent.yMin())
00121                     / targetExtent.height() * (targetRows - 1) + .5);
00122         for (unsigned sj = 0, tj = targetRow;
00123              sj < src->getNumRows() && tj < targetRows;
00124              ++sj, ++tj)
00125         {
00126             for (unsigned si = 0, ti = targetColumn;
00127              si < src->getNumColumns() && ti < targetCols;
00128              ++si, ++ti)
00129                 targethf->setHeight(ti, tj, src->getHeight(si, sj));
00130         }
00131     }
00132     return geo;
00133 }
00134 
00135 GeoImage
00136 mergeImages(const GeoExtent& targetExtent, const GeoImageVector& imgs)
00137 {
00138     Image* targetImage = new Image;
00139     const Image* proto = imgs[0].getImage();
00140     targetImage->setInternalTextureFormat(proto->getInternalTextureFormat());
00141     int numRows = proto->s() * 2;
00142     int numCols = proto->t() * 2;
00143     targetImage->allocateImage(numRows, numCols, proto->r(),
00144                                proto->getPixelFormat(), proto->getDataType(),
00145                                proto->getPacking());
00146     for (GeoImageVector::const_iterator itr = imgs.begin(),
00147              end = imgs.end();
00148          itr != end;
00149          ++itr)
00150     {
00151         const GeoExtent& srcExtent = itr->getExtent();
00152         int dstx
00153             = floor((srcExtent.xMin() - targetExtent.xMin()) / targetExtent.width()
00154                     * numCols + .5);
00155         int dsty
00156             = floor((srcExtent.yMin() - targetExtent.yMin()) / targetExtent.height()
00157                     * numRows + .5);
00158         ImageUtils::copyAsSubImage(itr->getImage(), targetImage,
00159                                    dstx, dsty);
00160     }
00161     return GeoImage(targetImage, targetExtent);
00162 }
00163 }
00164 
00165 // Create vertex arrays from the height field for a patch and install
00166 // them in the patch.
00167 void expandHeights(Geographic* gpatchset, const TileKey& key,
00168                    const GeoHeightField& hf, Vec3Array* verts,
00169                    Vec3Array* normals)
00170 {
00171     int resolution = gpatchset->getResolution();
00172     const GeoExtent& patchExtent = key.getExtent();
00173     double centx, centy;
00174     patchExtent.getCentroid(centx, centy);
00175     Vec3d patchCenter = gpatchset->toModel(centx, centy, 0);
00176     const SpatialReference* srs = key.getProfile()->getSRS();
00177     const SpatialReference* geoSrs = srs->getGeographicSRS();
00178     // Populate cell
00179     ref_ptr<Patch::Data> data = new Patch::Data;
00180     int patchDim = resolution + 1;
00181     double xInc = (patchExtent.xMax() - patchExtent.xMin()) / resolution;
00182     double yInc = (patchExtent.yMax() - patchExtent.yMin()) / resolution;
00183     const EllipsoidModel* eModel = gpatchset->getEllipsoidModel();
00184     const float verticalScale = gpatchset->getVerticalScale();
00185     PatchArray mverts(*verts, patchDim);
00186     for (int j = 0; j < patchDim; ++j)
00187     {
00188         for (int i = 0; i < patchDim; i++)
00189         {
00190             Vec2d cubeCoord(patchExtent.xMin() + i * xInc,
00191                             patchExtent.yMin() + j * yInc);
00192             double lon, lat;
00193             srs->transform(cubeCoord.x(), cubeCoord.y(), geoSrs, lon, lat);
00194             float elevation;
00195 
00196             bool found = hf.getElevation(srs, cubeCoord.x(), cubeCoord.y(),
00197                                          INTERP_BILINEAR, 0, elevation);
00198             // Into ec coordinates
00199             if (!found)
00200             {
00201                 OE_WARN << "Couldn't find height sample for cube coordinates "
00202                         << cubeCoord.x() << ", " << cubeCoord.y()
00203                         << " (lon lat " << lon << ", " << lat << ")\n";
00204                 continue;
00205             }
00206             elevation *= verticalScale;
00207             Vec3d coord;
00208             eModel->convertLatLongHeightToXYZ(
00209                 DegreesToRadians(lat), DegreesToRadians(lon), elevation,
00210                 coord.x(), coord.y(), coord.z());
00211             mverts[j][i] = coord - patchCenter;
00212             if (fabs(mverts[j][i].z()) > 6000000)
00213                 OE_WARN << "found huge coordinate.\n";
00214         }
00215     }
00216     // Normals. Average the normals of the triangles around the sample
00217     // point. We're not following the actual tessallation of the grid.
00218     for (int j = 0; j < patchDim; ++j)
00219     {
00220         for (int i = 0; i < patchDim; i++)
00221         {
00222             const Vec3& pt = (*verts)[j * patchDim + i];
00223             // A cross of points.
00224             Vec3 delta[4];      // Initialized to zero vectors
00225             for (int k = 0; k < 2; ++k)
00226             {
00227                 int gridx = i + 2 * k - 1;
00228                 if (gridx < patchDim && gridx >= 0)
00229                     delta[2 * k] = (*verts)[j * patchDim + gridx] - pt;
00230             }
00231             for (int k = 0; k < 2; ++k)
00232             {
00233                 int gridy = j + 2 * k - 1;
00234                 if (gridy < patchDim && gridy >= 0)
00235                     delta[2 * k + 1] = (*verts)[gridy * patchDim + i] - pt;
00236             }
00237             Vec3 normal;
00238             for (int k = 1; k <= 4; ++k)
00239             {
00240                 int v1 = k - 1, v2 = k % 4;
00241                 // If One or both of the deltas are 0, then the cross
00242                 // product is 0 and won't contribute to the average.
00243                 normal += delta[v1] ^ delta[v2];
00244             }
00245             normal.normalize();
00246             (*normals)[j * patchDim + i] = normal;
00247         }
00248     }
00249 }
00250 
00251 void installHeightField(GeoPatch* patch, const TileKey& key,
00252                         const GeoHeightField& hf)
00253 {
00254     Geographic* gpatchset = patch->getGeographic();
00255     int resolution = gpatchset->getResolution();
00256     // Populate cell
00257     int patchDim = resolution + 1;
00258     Vec3Array* verts = new Vec3Array(patchDim * patchDim);
00259     verts->setDataVariance(Object::DYNAMIC);
00260     Vec3Array* normals = new Vec3Array(patchDim * patchDim);
00261     normals->setDataVariance(Object::DYNAMIC);
00262     Vec2Array* texCoords = new Vec2Array(patchDim * patchDim);
00263     expandHeights(gpatchset, key, hf, verts, normals);
00264     const float resinv = 1.0f / static_cast<float>(resolution);
00265     for (int j = 0; j < patchDim; ++j)
00266     {
00267         for (int i = 0; i < patchDim; i++)
00268         {
00269             (*texCoords)[j * patchDim +i] = Vec2(i * resinv, j * resinv);
00270         }
00271     }
00272     // Construct the patch and its transform.
00273     ref_ptr<Patch::Data> data = new Patch::Data;
00274     data->vertexData.array = verts;
00275     data->vertexData.binding = Geometry::BIND_PER_VERTEX;
00276     data->normalData.array = normals;
00277     data->normalData.binding = Geometry::BIND_PER_VERTEX;
00278     Vec4Array* colors = new Vec4Array(1);
00279     (*colors)[0] = Vec4(1.0, 1.0, 1.0, 1.0);
00280     data->colorData.array = colors;
00281     data->colorData.binding = Geometry::BIND_OVERALL;
00282     data->texCoordList
00283         .push_back(Geometry::ArrayData(texCoords, Geometry::BIND_PER_VERTEX));
00284     patch->setData(data);
00285 }
00286 
00287 // Create a patch and the transform that places it in the
00288 // world. Install a height field if one is given.
00289 MatrixTransform* createPatchAux(Geographic* gpatchset,
00290                                 const TileKey& key,
00291                                 const GeoHeightField& hf)
00292 {
00293     GeoPatch* patch = new GeoPatch(key);
00294     patch->setGeographic(gpatchset);
00295     const GeoExtent& patchExtent = key.getExtent();
00296     double centx, centy;
00297     patchExtent.getCentroid(centx, centy);
00298     Vec3d patchCenter = gpatchset->toModel(centx, centy, 0);
00299     Matrixd patchMat = Matrixd::translate(patchCenter);
00300     installHeightField(patch, key, hf);
00301     MatrixTransform* result = new MatrixTransform;
00302     result->addChild(patch);
00303     result->setMatrix(patchMat);
00304     return result;
00305 }
00306 
00307 
00308 namespace
00309 {
00310 // Get a height field from the map, or an empty one if there is no
00311 // data for this tile.
00312 GeoHeightField getGeoHeightField(MapFrame& mapf, const TileKey& key,
00313                                  int resolution)
00314 {
00315     osg::ref_ptr<HeightField> hf;
00316     mapf.getHeightField(key, true, hf, 0L, INTERP_BILINEAR);
00317     if  (!hf)
00318         hf = key.getProfile()->getVerticalSRS()
00319             ->createReferenceHeightField(key.getExtent(),
00320                                          resolution + 1, resolution + 1);
00321     return GeoHeightField(hf, key.getExtent(),
00322                           key.getProfile()->getVerticalSRS());
00323 }
00324 
00325 // Split up patch keys that cross the Date Line. The only patches
00326 // that do are the the equatorial face with center at (-180, 0),
00327 // and the poles faces.
00328 
00329 inline bool crossesDateLine(const TileKey& key)
00330 {
00331     int face = EulerProfile::getFace(key);
00332     const GeoExtent& keyExtent = key.getExtent();
00333     return ((face == 2 || face == 4 || face == 5)
00334             && keyExtent.xMax() - keyExtent.xMin() > .5);
00335 }
00336 
00337 struct HeightFieldRequest : public TaskRequest
00338 {
00339     HeightFieldRequest(Geographic* gpatchset, const TileKey& key)
00340 
00341         : _gpatchset(gpatchset), _key(key), _mapf(gpatchset->getMapFrame())
00342     {
00343     }
00344     void operator()(ProgressCallback* progress)
00345     {
00346         const Map* map = _gpatchset->getMap();
00347         int resolution = _gpatchset->getResolution();
00348         GeoHeightField hf;
00349         if (crossesDateLine(_key))
00350         {
00351             GeoHeightFieldVector hfs;
00352             for (int child = 0; child < 4; ++child)
00353             {
00354                 TileKey subCubeKey = _key.createChildKey(child);
00355                 hfs.push_back(getGeoHeightField(_mapf, subCubeKey, resolution));
00356             }
00357             hf = mergeHeightFields(_key.getExtent(), hfs);
00358         }
00359         else
00360         {
00361             hf = getGeoHeightField(_mapf, _key, resolution);
00362         }
00363         int patchDim = resolution + 1;
00364         Vec3Array* verts = new Vec3Array(patchDim * patchDim);
00365         _result = verts;
00366         _normalResult = new Vec3Array(patchDim * patchDim);
00367         expandHeights(_gpatchset.get(), _key, hf,
00368                       verts, _normalResult.get());
00369     }
00370     ref_ptr<Geographic> _gpatchset;
00371     TileKey _key;
00372     // vertices are in _result;
00373     ref_ptr<Vec3Array> _normalResult;
00374     MapFrame _mapf;
00375 };
00376 
00377 struct ImageRequest : public TaskRequest
00378 {
00379     ImageRequest(Geographic* gpatchset, const TileKey& key)
00380         : _gpatchset(gpatchset), _key(key), _mapf(gpatchset->getMapFrame())
00381     {
00382     }
00383 
00384     void operator()(ProgressCallback* progress)
00385     {
00386         GeoImage gimage;
00387         const ImageLayerVector& layers = _mapf.imageLayers();
00388         if (crossesDateLine(_key))
00389         {
00390             GeoImageVector gis;
00391             if (!layers.empty())
00392             {
00393                 for (int child = 0; child < 4; ++child)
00394                 {
00395                     TileKey subCubeKey = _key.createChildKey(child);
00396                     gis.push_back(layers[0]->createImage(subCubeKey));
00397                 }
00398             }
00399             if (!gis.empty())
00400                 gimage = mergeImages(_key.getExtent(), gis);
00401         }
00402         else
00403         {
00404             if (!layers.empty())
00405                 gimage = layers[0]->createImage(_key);
00406         }
00407         _result = gimage.getImage();
00408     }
00409     ref_ptr<Geographic> _gpatchset;
00410     const TileKey _key;
00411     MapFrame _mapf;
00412 };
00413 
00414 // Update a patch node once map data is available
00415 class GeoPatchUpdateCallback : public NodeCallback
00416 {
00417 public:
00418     GeoPatchUpdateCallback() {}
00419     GeoPatchUpdateCallback(HeightFieldRequest* hfRequest,
00420                            ImageRequest* imageRequest)
00421         : _hfRequest(hfRequest), _imageRequest(imageRequest)
00422     {
00423     }
00424 
00425     GeoPatchUpdateCallback(const GeoPatchUpdateCallback& nc,
00426                            const CopyOp& copyop)
00427         : NodeCallback(nc, copyop), _hfRequest(nc._hfRequest),
00428           _imageRequest(nc._imageRequest)
00429     {
00430     }
00431 
00432     META_Object(seamless, GeoPatchUpdateCallback);
00433 
00434     virtual void operator()(Node* node, NodeVisitor* nv);
00435     
00436     ref_ptr<HeightFieldRequest> _hfRequest;
00437     ref_ptr<ImageRequest> _imageRequest;
00438 };
00439 }
00440 
00441 Transform* Geographic::createPatch(const std::string& filename,
00442                                    PatchOptions* poptions)
00443 {
00444     const TileKey patchKey = poptions->getTileKey();
00445     // Dummy height field until data is available.
00446     const VerticalSpatialReference* vsrs
00447         = patchKey.getProfile()->getVerticalSRS();
00448     ref_ptr<HeightField> hf = vsrs->createReferenceHeightField(
00449         patchKey.getExtent(), _resolution + 1, _resolution + 1);
00450     GeoHeightField ghf(hf.get(), patchKey.getExtent(), vsrs);
00451     ref_ptr<MatrixTransform> transform = createPatchAux(this, patchKey, ghf);
00452     GeoPatch* patch = dynamic_cast<GeoPatch*>(transform->getChild(0));
00453     ref_ptr<HeightFieldRequest> hfr = new HeightFieldRequest(this, patchKey);
00454     ref_ptr<ImageRequest> ir = new ImageRequest(this, patchKey);
00455     patch->setUpdateCallback(new GeoPatchUpdateCallback(hfr.get(), ir.get()));
00456     _hfService->add(hfr.get());
00457     _imageService->add(ir.get());
00458     return transform.release();
00459 }
00460 
00461 namespace
00462 {
00463 ClusterCullingCallback*
00464 createClusterCullingCallback(const Matrixd& transform, const Patch* patch,
00465                              const EllipsoidModel* et)
00466 {
00467     //This code is a very slightly modified version of the
00468     //DestinationTile::createClusterCullingCallback in
00469     //VirtualPlanetBuilder.
00470     double globe_radius =  et->getRadiusPolar();
00471     Vec3 center_position(transform.getTrans());
00472     Vec3 center_normal(center_position);
00473     center_normal.normalize();
00474 
00475     // populate the vertex/normal/texcoord arrays from the grid.
00476 
00477     float min_dot_product = 1.0f;
00478     float max_cluster_culling_height = 0.0f;
00479     float max_cluster_culling_radius = 0.0f;
00480     const Vec3Array* verts = static_cast<const Vec3Array*>(
00481         patch->getData()->vertexData.array.get());
00482     for (Vec3Array::const_iterator itr = verts->begin(), end = verts->end();
00483          itr != end;
00484          ++itr)
00485     {
00486         Vec3d dv = *itr;
00487         Vec3d v = dv + center_position;
00488         double lat, lon, height;
00489 
00490         et->convertXYZToLatLongHeight(v.x(), v.y(), v.z(),
00491                                       lat, lon, height);
00492 
00493         double d = sqrt(dv.x()*dv.x() + dv.y()*dv.y() + dv.z()*dv.z());
00494         double theta = acos(globe_radius / (globe_radius + fabs(height)));
00495         double phi = 2.0 * asin (d*0.5 / globe_radius); // d/globe_radius;
00496         double beta = theta + phi;
00497         double sb = sin(beta);
00498         double cb = cos(beta);
00499         double cutoff = osg::PI_2 - 0.1;
00500 
00501         //log(osg::INFO,"theta="<<theta<<"\tphi="<<phi<<" beta "<<beta);
00502         if (phi<cutoff && beta<cutoff)
00503         {
00504             float local_dot_product = -sb;
00505             float local_m = globe_radius*( 1.0/ cb - 1.0);
00506             float local_radius = static_cast<float>(globe_radius * sb / cb); // beta*globe_radius;
00507             min_dot_product = osg::minimum(min_dot_product, local_dot_product);
00508             max_cluster_culling_height = osg::maximum(max_cluster_culling_height,local_m);
00509             max_cluster_culling_radius = osg::maximum(max_cluster_culling_radius,local_radius);
00510         }
00511         else
00512         {
00513             //log(osg::INFO,"Turning off cluster culling for wrap around tile.");
00514             return 0;
00515         }
00516     }
00517 
00518     osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback;
00519 
00520     ccc->set(center_position + center_normal*max_cluster_culling_height ,
00521              center_normal,
00522              min_dot_product,
00523              max_cluster_culling_radius);
00524 
00525     return ccc;
00526 }
00527 }
00528 
00529 Node* Geographic::createPatchGroup(const string& filename,
00530                                    PatchOptions* poptions)
00531 {
00532     Node* result = PatchSet::createPatchGroup(filename, poptions);
00533     PatchGroup* pgroup = dynamic_cast<PatchGroup*>(result);
00534     // Make a cluster culling callback
00535     MatrixTransform* transform
00536         = dynamic_cast<MatrixTransform*>(pgroup->getChild(0));
00537     Patch* patch = dynamic_cast<Patch*>(transform->getChild(0));
00538     ClusterCullingCallback* ccc
00539         = createClusterCullingCallback(transform->getMatrix(), patch,
00540                                        _eModel.get());
00541     pgroup->setCullCallback(ccc);
00542     return pgroup;
00543 }
00544 
00545 Vec3d Geographic::toModel(double cubeX, double cubeY, double elevation)
00546 {
00547     double faceX = cubeX, faceY = cubeY;
00548     int face;
00549     euler::cubeToFace(faceX, faceY, face);
00550     double lat_deg, lon_deg;
00551     euler::faceCoordsToLatLon(faceX, faceY, face, lat_deg, lon_deg);
00552     Vec3d result;
00553     _eModel->convertLatLongHeightToXYZ(
00554         DegreesToRadians(lat_deg), DegreesToRadians(lon_deg), elevation,
00555         result.x(), result.y(), result.z());
00556     return result;
00557 }
00558 
00559 Node* Geographic::createChild(const PatchOptions* parentOptions, int childNum)
00560 {
00561     PatchOptions* poptions = static_cast<PatchOptions*>(
00562         osg::clone(parentOptions));
00563     poptions->setPatchLevel(parentOptions->getPatchLevel() + 1);
00564     poptions->setTileKey(parentOptions->getTileKey().createChildKey(childNum));
00565     return createPatchGroup("foobies.osgearth_engine_seamless_patch", poptions);
00566 
00567 }
00568 
00569 // A tile can be thought of lying between edges with integer
00570 // coordinates at its LOD. With x going to the right and y going down,
00571 // a tile between (tile_x, tile_y) and (tile_x + 1, tile_y + 1).
00572 //
00573 // edge order should be counter clockwise
00574 
00575 struct GridEdge
00576 {
00577     unsigned v[2][2];
00578     unsigned lod;
00579 };
00580 
00581 struct KeyIndex
00582 {
00583     KeyIndex() : lod(0), x(0), y(0) {}
00584     KeyIndex(unsigned lod_, unsigned x_, unsigned y_)
00585         : lod(lod_), x(x_), y(y_)
00586     {
00587     }
00588     KeyIndex(const TileKey& key)
00589         : lod(key.getLevelOfDetail()), x(key.getTileX()), y(key.getTileY())
00590     {
00591     }
00592     bool operator==(const KeyIndex& rhs) const
00593     {
00594         return lod == rhs.lod && x == rhs.x && y == rhs.y;
00595     }
00596     unsigned lod;
00597     unsigned x;
00598     unsigned y;
00599 };
00600 
00601 bool containsTile(const KeyIndex& parent, const KeyIndex& child)
00602 {
00603     if (parent.lod > child.lod)
00604         return false;
00605     if (parent.lod == child.lod)
00606         return parent.x == child.x && parent.y == child.y;
00607     int lodDiff = child.lod - parent.lod;
00608     if (child.x >> lodDiff == parent.x && child.y >> lodDiff == parent.y)
00609         return true;
00610     else
00611         return false;
00612 }
00613 
00614 // assume that tile is at a lower or equal LOD than neighbor
00615 bool isNeighborTile(const KeyIndex& tile, const KeyIndex& neighbor)
00616 {
00617     int lodDiff = neighbor.lod - tile.lod;
00618     int lodMult = 1 << lodDiff;
00619     unsigned tx = tile.x << lodDiff;
00620     unsigned ty = tile.y << lodDiff;
00621     if (tx == neighbor.x + 1 || tx + lodMult == neighbor.x)
00622         return neighbor.y >= ty && neighbor.y + 1 <= ty + lodMult;
00623     else if (ty == neighbor.y + 1 || ty + lodMult == neighbor.y)
00624         return neighbor.x >= tx && neighbor.x + 1 <= tx + lodMult;
00625     return false;
00626         
00627 }
00628 
00629 // Do tiles share a corner?
00630 bool adjoinsTile(const KeyIndex& tile, const KeyIndex& neighbor)
00631 {
00632     int lodDiff = neighbor.lod - tile.lod;
00633     int lodMult = 1 << lodDiff;
00634     unsigned tx = tile.x << lodDiff;
00635     unsigned ty = tile.y << lodDiff;
00636     if (tx == neighbor.x + 1 || tx + lodMult == neighbor.x)
00637         return ty == neighbor.y + 1 || ty + lodMult == neighbor.y;
00638     else
00639         return false;
00640 }
00641 
00642 PatchGroup* findFaceRoot(GeoPatch* patch, NodePath& pathList)
00643 {
00644     // Get the patch's key
00645     Group* parent = patch->getParent(0);
00646     PatchGroup* parentPatch = dynamic_cast<PatchGroup*>(parent->getParent(0));
00647     if (!parentPatch)
00648         return 0;
00649     PatchOptions* parentOptions = parentPatch->getOptions();
00650     TileKey patchKey = parentOptions->getTileKey();
00651     int x = patchKey.getTileX() >> (patchKey.getLevelOfDetail() - 2);
00652     int y = patchKey.getTileY() >> (patchKey.getLevelOfDetail() - 2);
00653     
00654     for (NodePath::iterator itr = pathList.begin(), end = pathList.end();
00655          itr != end;
00656          ++itr)
00657     {
00658         PatchGroup* pg = dynamic_cast<PatchGroup*>(*itr);
00659         if (pg)
00660         {
00661             PatchOptions* poptions = pg->getOptions();
00662             if (poptions)
00663             {
00664                 TileKey key = poptions->getTileKey();
00665                 if (key.getLevelOfDetail() == 2 && x == key.getTileX()
00666                     && y == key.getTileY())
00667                 return pg;
00668             }
00669         }
00670     }
00671     return 0;
00672 }
00673 
00674 typedef vector_ref<Vec3f, Vec3Array> EdgeRef;
00675 
00676 EdgeRef makeEdgeRef(GeoPatch* gpatch, int edgeno, int mult)
00677 {
00678     Vec3Array* verts
00679         = static_cast<Vec3Array*>(gpatch->getData()->vertexData.array.get());
00680     int patchDim = gpatch->getPatchSet()->getResolution() + 1;
00681     int shape = (patchDim - 1) / mult + 1;
00682     switch(edgeno)
00683     {
00684     case 0:
00685         return EdgeRef(*verts, mult, shape, 0);
00686     case 1:
00687         return EdgeRef(*verts, patchDim * mult, shape, patchDim - 1);
00688     case 2:
00689         return EdgeRef(*verts, mult, shape, (patchDim - 1) * patchDim);
00690     case 3:
00691         return EdgeRef(*verts, patchDim * mult, shape, 0);
00692     default:
00693         return EdgeRef(*verts, 0, 0, 0); // shouldn't happen
00694     }
00695 }
00696 
00697 struct ShareResult
00698 {
00699     ShareResult()
00700         : numEdges(0)
00701     {
00702         for (int i = 0; i < 2; ++i)
00703             tile1[i] = tile2[i] = -1;
00704     }
00705     int numEdges;
00706     int tile1[2];
00707     int tile2[2];
00708 };
00709 
00710 // tile2 is at a higher LOD than tile1
00711 ShareResult tilesShareEdges(const KeyIndex& tile1, const KeyIndex& tile2)
00712 {
00713     ShareResult result;
00714     int lodDiff = tile2.lod - tile1.lod;
00715     int x = tile1.x << lodDiff;
00716     int xleft = (tile1.x + 1) << lodDiff;
00717     int y = tile1.y << lodDiff;
00718     int ybottom = (tile1.y + 1) << lodDiff;
00719     if (tile2.x >= x && tile2.x + 1 <= xleft
00720         && tile2.y >= y && tile2.y + 1 <= ybottom)
00721     {
00722         // tile1 contains tile2; do they share edges?
00723         if (x == tile2.x)
00724         {
00725             result.tile1[0] = 3;
00726             result.tile2[0] = 3;
00727             result.numEdges = 1;
00728         }
00729         else if (xleft == tile2.x + 1)
00730         {
00731             result.tile1[0] = 1;
00732             result.tile2[0] = 1;
00733             result.numEdges = 1;
00734         }
00735         if (y == tile2.y)
00736         {
00737             result.tile1[result.numEdges] = 2;
00738             result.tile2[result.numEdges] = 2;
00739             result.numEdges++;
00740         }
00741         else if (ybottom == tile2.y + 1)
00742         {
00743             result.tile1[result.numEdges] = 0;
00744             result.tile2[result.numEdges] = 0;
00745             result.numEdges++;
00746         }
00747     }
00748     else
00749     {
00750         // Tiles can share 1 edge.
00751         if (x == tile2.x + 1)
00752         {
00753             result.tile1[0] = 3;
00754             result.tile2[0] = 1;
00755             result.numEdges = 1;
00756         }
00757         else if (xleft == tile2.x)
00758         {
00759             result.tile1[0] = 1;
00760             result.tile2[0] = 3;
00761             result.numEdges = 1;
00762         }
00763         else if (y == tile2.y + 1)
00764         {
00765             result.tile1[0] = 2;
00766             result.tile2[0] = 0;
00767             result.numEdges = 1;
00768         }
00769         else if (ybottom == tile2.y)
00770         {
00771             result.tile1[0] = 0;
00772             result.tile2[0] = 2;
00773             result.numEdges = 1;
00774         }
00775     }
00776     return result;
00777 }
00778 
00779 void transferEdges(
00780     GeoPatch* toPatch, const Matrixd& toMat, const KeyIndex& toIdx,
00781     GeoPatch* fromPatch, const Matrixd& fromMat, const KeyIndex& fromIdx,
00782     const ShareResult& shared);
00783 
00784 void safeCopy(Vec3f& dest, const Vec3f& src, const Matrixd& mat)
00785 {
00786     Vec3f tmp = src * mat;
00787     if ((tmp - dest).length2() > 100000000)
00788         OE_WARN << "whoops!\n";
00789     dest = tmp;
00790 }
00791 
00792 class TileUpdater : public NodeVisitor
00793 {
00794 public:
00795     TileUpdater(GeoPatch* gpatch)
00796         : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN), _gpatch(gpatch)
00797     {
00798         const MatrixTransform* trans
00799             = static_cast<const MatrixTransform*>(_gpatch->getParent(0));
00800         _tileMat = trans->getMatrix();
00801         const PatchGroup* pg
00802             = static_cast<const PatchGroup*>(trans->getParent(0));
00803         const PatchOptions* popt = pg->getOptions();
00804         _tileIndex = popt->getTileKey();
00805     }
00806 
00807     void apply(PagedLOD& node)
00808     {
00809         PatchGroup* pgrp = dynamic_cast<PatchGroup*>(&node);
00810         if (!pgrp)
00811             return;
00812         const PatchOptions* popt = pgrp->getOptions();
00813         if (!popt)
00814             return;
00815         KeyIndex idx = popt->getTileKey();
00816         if (idx == _tileIndex)
00817             return;
00818 
00819         if (containsTile(idx, _tileIndex) || isNeighborTile(idx, _tileIndex))
00820             copyTileEdges(pgrp, popt);
00821         else if (adjoinsTile(idx, _tileIndex))
00822             copyCorner(pgrp, popt);
00823         else
00824             return;
00825         if (node.getNumChildren() > 1)
00826             traverse(*node.getChild(1));
00827     }
00828 protected:
00829     void copyTileEdges(PatchGroup* node, const PatchOptions* gopt)
00830     {
00831         // The tile to update
00832         MatrixTransform* trans
00833             = static_cast<MatrixTransform*>(node->getChild(0));
00834         GeoPatch* tpatch = static_cast<GeoPatch*>(trans->getChild(0));
00835         KeyIndex idx(gopt->getTileKey());
00836         ShareResult shared = tilesShareEdges(idx, _tileIndex);
00837         if (shared.numEdges != 0)
00838         {
00839             transferEdges(tpatch, trans->getMatrix(), idx,
00840                           _gpatch, _tileMat, _tileIndex, shared);
00841             tpatch->dirtyVertexData();
00842         }
00843     }
00844     void copyCorner(PatchGroup* node, const PatchOptions* gopt)
00845     {
00846         // The tile to update
00847         MatrixTransform* trans
00848             = static_cast<MatrixTransform*>(node->getChild(0));
00849         Matrixd toMat = trans->getMatrix();
00850         Matrixd transferMat =  _tileMat * Matrixd::inverse(toMat);
00851         GeoPatch* tpatch = static_cast<GeoPatch*>(trans->getChild(0));
00852         KeyIndex tidx(gopt->getTileKey());
00853         Geographic* gset = _gpatch->getGeographic();
00854         int patchDim = gset->getResolution() + 1;
00855         Vec3Array* verts = static_cast<Vec3Array*>(_gpatch->getData()
00856                                                    ->vertexData.array.get());
00857         PatchArray varray(*verts, patchDim);
00858         Vec3Array* tverts = static_cast<Vec3Array*>(tpatch->getData()
00859                                                     ->vertexData.array.get());
00860         PatchArray tarray(*tverts, patchDim);
00861         int lodDiff = _tileIndex.lod - tidx.lod;
00862         int lodMult = 1 << lodDiff;
00863         unsigned tx = tidx.x << lodDiff;
00864         unsigned ty = tidx.y << lodDiff;
00865         
00866         if (_tileIndex.x < tx)
00867         {
00868             if (_tileIndex.y == ty + lodMult)
00869                 //tarray[0][0] = varray[patchDim - 1][patchDim - 1] * transferMat;
00870                 safeCopy(tarray[0][0], varray[patchDim - 1][patchDim - 1], transferMat);
00871             else
00872                 //tarray[patchDim - 1][0] = varray[0][patchDim - 1] * transferMat;
00873                 safeCopy(tarray[patchDim - 1][0], varray[0][patchDim - 1], transferMat);
00874         }
00875         else
00876         {
00877             if (_tileIndex.y == ty + lodMult)
00878                 // tarray[0][patchDim - 1] = varray[patchDim - 1][0] * transferMat;
00879                 safeCopy(tarray[0][patchDim - 1], varray[patchDim - 1][0], transferMat);
00880             else
00881                 // tarray[patchDim - 1][patchDim - 1] = varray[0][0] * transferMat;
00882                 safeCopy(tarray[patchDim - 1][patchDim - 1], varray[0][0], transferMat);
00883         }
00884         tpatch->dirtyVertexData();
00885     }
00886     GeoPatch* _gpatch;
00887     KeyIndex _tileIndex;
00888     Matrixd _tileMat;
00889 };
00890 
00891 void transferEdges(
00892     GeoPatch* toPatch, const Matrixd& toMat, const KeyIndex& toIdx,
00893     GeoPatch* fromPatch, const Matrixd& fromMat, const KeyIndex& fromIdx,
00894     const ShareResult& shared)
00895 {
00896     int resolution = toPatch->getPatchSet()->getResolution();
00897     int patchDim = resolution + 1;
00898     int lodDiff = fromIdx.lod - toIdx.lod;
00899     int detailMult = 1 << lodDiff;
00900     Matrixd transferMat = fromMat * Matrixd::inverse(toMat);
00901     for (int i = 0; i < shared.numEdges; ++i)
00902     {
00903         EdgeRef toEdge = makeEdgeRef(toPatch, shared.tile1[i], 1);
00904         EdgeRef fromEdge = makeEdgeRef(fromPatch, shared.tile2[i], detailMult);
00905         int toStart;
00906         if (shared.tile1[i] == 0 || shared.tile1[i] == 2)
00907             toStart = (fromIdx.x - (toIdx.x * detailMult)) * resolution / detailMult;
00908         else
00909             toStart = (detailMult - 1 - (fromIdx.y - (toIdx.y * detailMult)))
00910                 * resolution / detailMult;
00911         for (int jt = toStart, jf = 0; jf < fromEdge.shape(); ++jt, ++jf)
00912         {
00913 #if 0
00914             Vec3d vtx = Vec3d(fromEdge[jf]);
00915             vtx = vtx * transferMat;
00916             toEdge[jt] = Vec3f(vtx);
00917 #endif
00918             safeCopy(toEdge[jt], fromEdge[jf], transferMat);
00919         }
00920     }
00921 }
00922 
00923 void GeoPatchUpdateCallback::operator()(Node* node, NodeVisitor* nv)
00924 {
00925     GeoPatch* patch = dynamic_cast<GeoPatch*>(node);
00926     if (!patch)
00927         return;
00928     if (_hfRequest.valid() && _hfRequest->isCompleted())
00929     {
00930         Vec3Array* verts = dynamic_cast<Vec3Array*>(_hfRequest->getResult());
00931         Vec3Array* norms = _hfRequest->_normalResult.get();
00932         if (verts && norms)
00933         {
00934             Vec3Array* patchVerts = static_cast<Vec3Array*>(
00935                 patch->getData()->vertexData.array.get());
00936             Vec3Array* patchNorms = static_cast<Vec3Array*>(
00937                 patch->getData()->normalData.array.get());
00938             copy(verts->begin(), verts->end(), patchVerts->begin());
00939             patchVerts->dirty();
00940             copy(norms->begin(), norms->end(), patchNorms->begin());
00941             patchNorms->dirty();
00942         }
00943         _hfRequest = 0;
00944         PatchGroup* faceRoot = findFaceRoot(patch, nv->getNodePath());
00945         if (faceRoot)
00946         {
00947             TileUpdater tileUpdater(patch);
00948             faceRoot->accept(tileUpdater);
00949         }
00950     }
00951     if (_imageRequest.valid() && _imageRequest->isCompleted())
00952     {
00953         Image* image = dynamic_cast<Image*>(_imageRequest->getResult());
00954         if (image)
00955         {
00956             Texture2D* tex = new Texture2D();
00957             tex->setImage(image);
00958             tex->setWrap(Texture::WRAP_S, Texture::CLAMP_TO_EDGE);
00959             tex->setWrap(Texture::WRAP_T, Texture::CLAMP_TO_EDGE);
00960             tex->setFilter(Texture::MIN_FILTER,
00961                            Texture::LINEAR_MIPMAP_LINEAR);
00962             tex->setFilter(Texture::MAG_FILTER, Texture::LINEAR);
00963             StateSet* ss = patch->getOrCreateStateSet();
00964             ss->setTextureAttributeAndModes(0, tex, StateAttribute::ON);
00965         }
00966         _imageRequest = 0;
00967     }
00968     if (!_hfRequest.valid() && !_imageRequest.valid())
00969         node->setUpdateCallback(0);
00970 }
00971 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines