osgEarth 2.1.1
|
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 }