osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include "Diamond" 00020 #include "MeshManager" 00021 #include <osgEarth/Cube> 00022 #include <iterator> 00023 00024 #ifdef USE_DEBUG_TEXTURES 00025 00026 static osg::Image* createDebugImage() 00027 { 00028 osg::Image* image = new osg::Image(); 00029 image->allocateImage( 4, 4, 1, GL_RGBA, GL_UNSIGNED_BYTE ); 00030 *(unsigned int*)(image->data( 1, 0 )) = 0xff0000ff; 00031 *(unsigned int*)(image->data( 2, 0 )) = 0xff0000ff; 00032 return image; 00033 } 00034 00035 #endif // USE_DEBUG_TEXTURES 00036 00037 // -------------------------------------------------------------------------- 00038 00039 static int s_numDiamonds = 0; 00040 00041 Diamond::Diamond( MeshManager* mesh, const osgEarth::TileKey& key, Level level, const std::string& name ) : 00042 osg::Referenced(true), 00043 _mesh( mesh ), 00044 _key( key ), 00045 _level( level ), 00046 _name( name ), 00047 _status( ACTIVE ), 00048 _childValence( 4 ), 00049 //_color(1,1,1,1), 00050 _lastCullFrame( 0 ), 00051 _queuedForSplit( false ), 00052 _queuedForMerge( false ), 00053 _queuedForImage( false ), 00054 _drawableDirty( false ), 00055 _bsComputed( false ), 00056 _isSplit( false ), 00057 _hasGeometry( level % 2 == 1 ), 00058 _currentStateSetOwner( 0L ), 00059 _targetStateSetOwner( 0L ), 00060 _targetStateSetRevision( -1 ), 00061 _hasFinalImage( false ) 00062 { 00063 this->setThreadSafeRefUnref(true); 00064 00065 // only ODD-numbered levels have actual geometry. 00066 if ( _hasGeometry ) 00067 { 00068 _stateSet = new RevisionedStateSet(); 00069 _amrDrawable = new AMRDrawable(); 00070 } 00071 00072 s_numDiamonds++; 00073 //OE_NOTICE << s_numDiamonds << " ... " << std::endl; 00074 00075 //if ( _key.valid() ) 00076 // OE_NOTICE << "New Diamond: " << _key->str() << std::endl; 00077 00078 //if ( _key.valid() ) 00079 // OE_NOTICE << _name << ": " << _key->str() << " : " << _key->getGeoExtent().toString() << std::endl; 00080 00081 } 00082 00083 Diamond::~Diamond() 00084 { 00085 s_numDiamonds--; 00086 //OE_NOTICE << s_numDiamonds << " ... " << std::endl; 00087 } 00088 00089 const MeshNode& 00090 Diamond::node() const 00091 { 00092 return _mesh->node( _vi ); 00093 } 00094 00095 void 00096 Diamond::activate() 00097 { 00098 // this can only be called once all ancestors are established 00099 00100 _status = ACTIVE; 00101 computeBound(); 00102 00103 if ( _hasGeometry ) 00104 { 00105 // assign this diamond the stateset of the appropriate quadtree ancestor: 00106 int stateSetLevel = (int)_level; 00107 if ( stateSetLevel < (int)_mesh->_minActiveLevel ) 00108 stateSetLevel = _mesh->_minActiveLevel; 00109 00110 // obtain a pointer to the Diamond that owns the StateSet we wish to use: 00111 _targetStateSetOwner = this; 00112 while( _targetStateSetOwner->_level > stateSetLevel ) 00113 _targetStateSetOwner = _targetStateSetOwner->_a[QUADTREE].get(); 00114 00115 // since that StateSet might not be populated yet, backtrack from there to find the 00116 // first available populated StateSet. This will serve as a temporary "placeholder" 00117 // until our target stateset is ready (i.e. the textures etc are loaded). 00118 _currentStateSetOwner = _targetStateSetOwner; 00119 while( !_currentStateSetOwner->_hasFinalImage && _currentStateSetOwner->_level > _mesh->_minActiveLevel ) 00120 { 00121 _currentStateSetOwner = _currentStateSetOwner->_a[QUADTREE].get(); 00122 } 00123 00124 // assign the stateset to this Diamond's geometry. 00125 _amrDrawable->_stateSet = _currentStateSetOwner->_stateSet.get(); 00126 00127 // synchronize with the target state set. By doing this, we will detect when the target stateset 00128 // does finally get populated, and at that point we can replace the placeholder stateset with 00129 // the final stateset. (This check occurs in Diamond::cull.) 00130 _targetStateSetOwner->_stateSet->sync( _targetStateSetRevision ); 00131 00132 #ifdef USE_TEXTURES 00133 00134 // finally, queue up a request to populate the stateset if necessary. 00135 if ( !_targetStateSetOwner->_hasFinalImage ) 00136 { 00137 _mesh->queueForImage( _targetStateSetOwner, 1.0f ); 00138 } 00139 00140 #endif 00141 } 00142 } 00143 00144 00145 //const osg::BoundingSphere& 00146 //Diamond::getBound() 00147 //{ 00148 // if ( !_bsComputed ) 00149 // { 00150 // computeBound(); 00151 // } 00152 // return _bs; 00153 //} 00154 00155 void 00156 Diamond::setCoord( const osg::Vec3d& coord ) 00157 { 00158 _vi = _mesh->addNode( coord ); 00159 } 00160 00161 void 00162 Diamond::seed( Level maxLevel ) 00163 { 00164 if ( maxLevel > _level ) 00165 { 00166 for( ChildIndex c = 0; c < _childValence; ++c ) 00167 { 00168 getOrCreateChild( c )->seed( maxLevel ); 00169 } 00170 } 00171 } 00172 00173 void 00174 Diamond::split() 00175 { 00176 // mark as split, and mark the primitive set as needing a refesh: 00177 _isSplit = true; 00178 00179 if ( _hasGeometry ) 00180 { 00181 // for geometry diamonds, a split means we must regenerate this diamond 00182 // AND its quadtree ancestor. 00183 this->dirty(); 00184 _a[QUADTREE]->dirty(); 00185 } 00186 else 00187 { 00188 // for intermediate diamonds, splitting means we must rebuild each immediate parent. 00189 _a[PARENT_L]->dirty(); 00190 _a[PARENT_R]->dirty(); 00191 } 00192 00193 _queuedForSplit = false; 00194 00195 // check to see whether any of our neighbors are split. If a neighbor is also 00196 // split, spawn a common child. 00197 for( ChildIndex c = 0; c < _childValence; ++c ) 00198 { 00199 // debugging assertion: 00200 if ( _c[c].valid() ) 00201 { 00202 OE_WARN << "ILLEGAL STATE: diamond just split but has kids!" << std::endl; 00203 } 00204 00205 Diamond* d0 = getNeighbor( c ); 00206 if ( d0 && d0->_isSplit ) 00207 { 00208 getOrCreateChild( c ); 00209 } 00210 } 00211 } 00212 00213 void 00214 Diamond::merge() 00215 { 00216 // have to remove/merge all the children before we can merge. 00217 for( ChildIndex c = 0; c < _childValence; ++c ) 00218 { 00219 if ( _c[c].valid() ) 00220 removeChild( c ); 00221 } 00222 _isSplit = false; 00223 00224 if ( _hasGeometry ) 00225 { 00226 this->dirty(); 00227 _a[QUADTREE]->dirty(); 00228 } 00229 else 00230 { 00231 // for intermediate diamonds, merging means we must rebuild each immediate parent. 00232 _a[PARENT_L]->dirty(); 00233 _a[PARENT_R]->dirty(); 00234 } 00235 00236 _queuedForMerge = false; 00237 } 00238 00239 #define DEVIATION 0 00240 00241 unsigned int 00242 Diamond::cull( osgUtil::CullVisitor* cv ) 00243 { 00244 // NOTE: 00245 // the problem here is that if a diamond gets culled, none of its decendents are 00246 // considered for merging. Perhaps a CULL-OUT means immediate merge-queueing? Or, 00247 // maybe we should check the traversal-number-delta. And if it's more than X, 00248 // queueForMerge. 00249 00250 // if this diamond if maked INACTIVE, we have a problem 00251 if ( _status == INACTIVE ) 00252 { 00253 OE_WARN << "ILLEGAL STATE: " << _name << " is marked INACTIVE but is being tested for cull!" << std::endl; 00254 return 0; 00255 } 00256 00257 // trivial rejection: already traversed this diamond (multiple parenting) 00258 if ( cv->getTraversalNumber() <= _lastCullFrame ) 00259 return 0; 00260 00261 // record the framestamp so we don't cull more than once 00262 _lastCullFrame = cv->getTraversalNumber(); 00263 00264 const osg::Vec3d& eye = cv->getEyePoint(); 00265 float range = (eye - visibleBound().center()).length(); 00266 00267 // NOTE: we never cull intermediate (non-geometry) diamonds. 00268 00269 // if this geometry diamond is outside the extended bounds, it is eligible for merge AND 00270 // the diamond is not visible. 00271 if ( _hasGeometry && cv->isCulled( extendedBound() ) ) 00272 { 00273 // start merging the children if this diamond fails the visibility tests. 00274 if (_level >= _mesh->_minActiveLevel && 00275 _level < _mesh->_maxActiveLevel && 00276 _isSplit && 00277 !_queuedForMerge && 00278 !_queuedForSplit ) 00279 { 00280 _mesh->queueForMerge( this, range ); 00281 //OE_NOTICE << std::fixed << "isCUlled=false, range=" << range << ", radius=" << _bs.radius() << std::endl; 00282 } 00283 return 0; 00284 } 00285 00286 // Back-face culling: 00287 // If the dot product of the eyepoint with each of the four ancestors is negative, then the 00288 // entire diamond is facing away from the camera and can be culled. 00289 if ( _hasGeometry ) 00290 { 00291 int i; 00292 for( i=0; i<4; ++i ) 00293 { 00294 osg::Vec3d eye_vec = eye - _a[i]->node()._vertex; 00295 double len = eye_vec.length(); 00296 if ( len <= visibleBound().radius() ) break; // if we're inside the radius, bail 00297 double dev = ( eye_vec * _a[i]->node()._normal ) / len; 00298 if ( dev >= DEVIATION ) break; 00299 } 00300 if ( i == 4 ) 00301 return 0; 00302 } 00303 00304 // this will determine whether we actually add this diamond to the draw list later. we still 00305 // have to traverse children that are in the extended bounds (even if they're not in the view 00306 // frustum) in order to satifsy split requirements. 00307 bool inVisibleFrustum = 00308 _hasGeometry && !cv->isCulled( visibleBound() ); 00309 00310 // at this point, culling is now complete for this diamond. 00311 00312 #ifdef USE_TEXTURES 00313 00314 // check to see whether the target stateset is "dirty". 00315 if ( _hasGeometry && _targetStateSetOwner->_stateSet->outOfSyncWith( _targetStateSetRevision ) ) 00316 { 00317 // flags the primitive set for regeneration 00318 this->dirty(); 00319 } 00320 00321 #endif 00322 00323 // traverse the diamond's children. 00324 unsigned short numChildren = 0; 00325 for( ChildIndex c = 0; c < _childValence; ++c ) 00326 { 00327 if ( _c[c].valid() ) 00328 { 00329 ++numChildren; 00330 _c[c]->cull( cv ); 00331 } 00332 } 00333 00334 if ( _hasGeometry && _amrDrawable->_triangles.size() == 0 && numChildren < _childValence ) 00335 OE_WARN << "BOOGER" << std::endl; 00336 00337 //if ( _hasGeometry && _amrDrawable->_triangles.size() > 0 ) 00338 if ( inVisibleFrustum ) 00339 { 00340 _mesh->_amrDrawList.push_back( _amrDrawable.get() ); 00341 } 00342 00343 // culling is complete. next we will check to see if we need to split this diamond 00344 // based on LOD range. 00345 // FUTURE: splitting will depend on a couple factors: max split level (resolution), 00346 // available data, frame rate, etc. 00347 if ( _level >= _mesh->_minActiveLevel && _level < _mesh->_maxActiveLevel ) 00348 { 00349 if (!_isSplit && 00350 range < extendedBound().radius() * CULL_RANGE_FACTOR && 00351 !_queuedForSplit && 00352 !_queuedForMerge && 00353 numChildren < _childValence ) 00354 { 00355 _mesh->queueForSplit( this, -range ); 00356 _queuedForMerge = false; 00357 } 00358 00359 else if ( 00360 _isSplit && 00361 range > extendedBound().radius() * CULL_RANGE_FACTOR && 00362 !_queuedForSplit && 00363 !_queuedForMerge && 00364 numChildren > 0 ) 00365 { 00366 _mesh->queueForMerge( this, range ); 00367 _queuedForSplit = false; 00368 } 00369 } 00370 00371 return 0; 00372 //return _numGeometriesAdded; 00373 } 00374 00375 void 00376 Diamond::computeBound() 00377 { 00378 // in vert space. 00379 _visibleBound = osg::BoundingSphere( node()._vertex, 1.0 ); 00380 for(int i=0; i<4; i++) 00381 _visibleBound.expandRadiusBy( _a[i]->node()._vertex ); 00382 00383 _extendedBound = _visibleBound; 00384 _extendedBound.radius() = _extendedBound.radius() * sqrt(2.0) * 2.0; 00385 00386 _bsComputed = true; 00387 } 00388 00389 const osg::BoundingSphere& 00390 Diamond::visibleBound() const 00391 { 00392 if ( !_bsComputed ) 00393 const_cast<Diamond*>(this)->computeBound(); 00394 return _visibleBound; 00395 } 00396 00397 const osg::BoundingSphere& 00398 Diamond::extendedBound() const 00399 { 00400 if ( !_bsComputed ) 00401 const_cast<Diamond*>(this)->computeBound(); 00402 return _extendedBound; 00403 } 00404 00405 void 00406 Diamond::dump() 00407 { 00408 OE_NOTICE 00409 << "Diamond " << _name << "\n" //" (" << _v.x() << "," << _v.y() << "," << _v.z() << ") :\n" 00410 << " level = " << _level << "\n" 00411 << " parent_R = " << _a[PARENT_R]->_name << "\n" 00412 << " parent_L = " << _a[PARENT_L]->_name << "\n" 00413 << " quadtree = " << _a[QUADTREE]->_name << "\n" 00414 << " gdparent = " << _a[GDPARENT]->_name << "\n" 00415 << " children = "; 00416 00417 for( int i=0; i<_childValence; ++i ) 00418 OE_NOTICE << i << ":<" << ( _c[i].valid()? _c[i]->_name : "") << "> "; 00419 00420 OE_NOTICE << std::endl << std::endl; 00421 } 00422 00423 void 00424 Diamond::setChild( ChildIndex index, Diamond* child ) 00425 { 00426 _c[index] = child; 00427 00428 if ( child->_hasGeometry ) 00429 { 00430 // if this is a geometry diamond, invalidate the quadtree ancestor so it can include it. 00431 child->_a[QUADTREE]->dirty(); 00432 child->dirty(); 00433 } 00434 else 00435 { 00436 this->_a[PARENT_L]->dirty(); 00437 this->_a[PARENT_R]->dirty(); 00438 } 00439 } 00440 00441 void 00442 Diamond::dirty() 00443 { 00444 // queue this diamond for a primitive set refresh .. but only if it actually has geometry. 00445 if ( _hasGeometry && _level >= _mesh->_minGeomLevel ) 00446 { 00447 if ( !_drawableDirty ) 00448 { 00449 _drawableDirty = true; 00450 #ifdef USE_DIRTY_QUEUE 00451 _mesh->queueForRefresh( this ); 00452 #else 00453 refreshDrawable(); 00454 #endif 00455 } 00456 } 00457 else 00458 { 00459 //OE_WARN << "ILLEGAL: dirty(): _hasGeometry = " << _hasGeometry << ", _level = " << _level << std::endl; 00460 } 00461 } 00462 00463 // texture coordinate for each orientation. 00464 #define T_QUADTREE 0 00465 #define T_CHILD_0 1 00466 #define T_PARENT_R 2 00467 #define T_CHILD_1 3 00468 #define T_GDPARENT 4 00469 #define T_CHILD_2 5 00470 #define T_PARENT_L 6 00471 #define T_CHILD_3 7 00472 00473 static osg::Vec2f otex[8] = 00474 { 00475 osg::Vec2f(0.0,0.0), // T_QUADTREE 00476 osg::Vec2f(0.5,0.0), // T_CHILD_0 00477 osg::Vec2f(1.0,0.0), // T_PARENT_R 00478 osg::Vec2f(1.0,0.5), // T_CHILD_1 00479 osg::Vec2f(1.0,1.0), // T_GDPARENT 00480 osg::Vec2f(0.5,1.0), // T_CHILD_2 00481 osg::Vec2f(0.0,1.0), // T_PARENT_L 00482 osg::Vec2f(0.0,0.5) // T_CHILD_3 00483 }; 00484 00485 #define OT(I,R) otex[ ( I + R ) % 8 ] 00486 00487 #define ADD_TRI( COORDS, TEX, C1, T1, C2, T2, C3, T3, OFFSET, SPAN ) { \ 00488 COORDS->push_back(C1); \ 00489 COORDS->push_back(C2); \ 00490 COORDS->push_back(C3); \ 00491 TEX->push_back( OFFSET + (T1*SPAN) ); \ 00492 TEX->push_back( OFFSET + (T2*SPAN) ); \ 00493 TEX->push_back( OFFSET + (T3*SPAN) ); 00494 00495 void 00496 Diamond::refreshDrawable() 00497 { 00498 // the primitive set may have already been refreshed (due to double-parenting) 00499 if ( !_drawableDirty ) 00500 return; 00501 00502 // this level does not generate primitive sets 00503 if ( _level < _mesh->_minGeomLevel ) 00504 return; 00505 00506 if ( !_hasGeometry ) 00507 { 00508 OE_WARN << "ILLEGAL: refreshPrimitiveSet called on Diamond at no-geom level " << _level << std::endl; 00509 return; 00510 } 00511 00512 // figure out the subrange transformation. 00513 osg::Vec2f offset( 0.0, 0.0 ); 00514 double span = 1.0; 00515 00516 const TileKey& ssaKey = _currentStateSetOwner->_key; 00517 if ( _level > _currentStateSetOwner->_level ) //_currentStateSetOwner != _targetStateSetOwner ) 00518 { 00519 span = 1.0/(double)(1 << ((_level-_currentStateSetOwner->_level)/2)); 00520 offset.x() = (_key.getExtent().xMin()-ssaKey.getExtent().xMin())/ssaKey.getExtent().width(); 00521 offset.y() = (_key.getExtent().yMin()-ssaKey.getExtent().yMin())/ssaKey.getExtent().height(); 00522 } 00523 00524 int o = _orientation; 00525 00526 // Start by clearing out the old primitive set: 00527 _amrDrawable->_triangles.clear(); 00528 00529 //if ( false ) //!_isSplit ) // took this out to preserve the diamond center point. 00530 //{ 00531 // // if the diamond is not split, simply draw the two triangles. 00532 // _amrDrawable->add( new AMRTriangle( 00533 // _a[GDPARENT]->coord(), _a[GDPARENT]->vert(), offset + OT(T_GDPARENT,o) * span, 00534 // _a[QUADTREE]->coord(), _a[QUADTREE]->vert(), offset + OT(T_QUADTREE,o) * span, 00535 // _a[PARENT_R]->coord(), _a[PARENT_R]->vert(), offset + OT(T_PARENT_R,o) * span ) ); 00536 00537 // _amrDrawable->add( new AMRTriangle( 00538 // _a[GDPARENT]->coord(), _a[GDPARENT]->vert(), offset + OT(T_GDPARENT,o) * span, 00539 // _a[PARENT_L]->coord(), _a[PARENT_L]->vert(), offset + OT(T_PARENT_L,o) * span, 00540 // _a[QUADTREE]->coord(), _a[QUADTREE]->vert(), offset + OT(T_QUADTREE,o) * span ) ); 00541 //} 00542 //else 00543 { 00544 // find this diamond's four quadtree descendants: 00545 Diamond* q0 = _c[0].valid() ? _c[0]->_c[1].get() : 0L; 00546 Diamond* q1 = _c[1].valid() ? _c[1]->_c[3].get() : 0L; 00547 Diamond* q2 = _c[2].valid() ? _c[2]->_c[1].get() : 0L; 00548 Diamond* q3 = _c[3].valid() ? _c[3]->_c[3].get() : 0L; 00549 00550 osg::Vec2f center = osg::Vec2f(.5,.5); 00551 00552 if ( !_c[0].valid() || !_c[0]->_isSplit ) 00553 { 00554 _amrDrawable->add( new AMRTriangle( 00555 node(), offset + center * span, 00556 _a[QUADTREE]->node(), offset + OT(T_QUADTREE,o) * span, 00557 _a[PARENT_R]->node(), offset + OT(T_PARENT_R,o) * span ) ); 00558 } 00559 else 00560 { 00561 if ( !q0 ) 00562 { 00563 _amrDrawable->add( new AMRTriangle( 00564 node(), offset + center * span, 00565 _a[QUADTREE]->node(), offset + OT(T_QUADTREE,o) * span, 00566 _c[0]->node(), offset + OT(T_CHILD_0,o) * span ) ); 00567 } 00568 if ( !q1 ) 00569 { 00570 _amrDrawable->add( new AMRTriangle( 00571 node(), offset + center * span, 00572 _c[0]->node(), offset + OT(T_CHILD_0,o) * span, 00573 _a[PARENT_R]->node(), offset + OT(T_PARENT_R,o) * span ) ); 00574 } 00575 } 00576 00577 if ( !_c[1].valid() || !_c[1]->_isSplit ) 00578 { 00579 _amrDrawable->add( new AMRTriangle( 00580 node(), offset + center * span, 00581 _a[PARENT_R]->node(), offset + OT(T_PARENT_R,o) * span, 00582 _a[GDPARENT]->node(), offset + OT(T_GDPARENT,o) * span ) ); 00583 } 00584 else 00585 { 00586 if ( !q1 ) 00587 { 00588 _amrDrawable->add( new AMRTriangle( 00589 node(), offset + center * span, 00590 _a[PARENT_R]->node(), offset + OT(T_PARENT_R,o) * span, 00591 _c[1]->node(), offset + OT(T_CHILD_1,o) * span ) ); 00592 } 00593 if ( !q2 ) 00594 { 00595 _amrDrawable->add( new AMRTriangle( 00596 node(), offset + center * span, 00597 _c[1]->node(), offset + OT(T_CHILD_1,o) * span, 00598 _a[GDPARENT]->node(), offset + OT(T_GDPARENT,o) * span ) ); 00599 } 00600 } 00601 00602 if ( !_c[2].valid() || !_c[2]->_isSplit ) 00603 { 00604 _amrDrawable->add( new AMRTriangle( 00605 node(), offset + center * span, 00606 _a[GDPARENT]->node(), offset + OT(T_GDPARENT,o) * span, 00607 _a[PARENT_L]->node(), offset + OT(T_PARENT_L,o) * span ) ); 00608 } 00609 else 00610 { 00611 if ( !q2 ) 00612 { 00613 _amrDrawable->add( new AMRTriangle( 00614 node(), offset + center * span, 00615 _a[GDPARENT]->node(), offset + OT(T_GDPARENT,o) * span, 00616 _c[2]->node(), offset + OT(T_CHILD_2,o) * span ) ); 00617 } 00618 if ( !q3 ) 00619 { 00620 _amrDrawable->add( new AMRTriangle( 00621 node(), offset + center * span, 00622 _c[2]->node(), offset + OT(T_CHILD_2,o) * span, 00623 _a[PARENT_L]->node(), offset + OT(T_PARENT_L,o) * span ) ); 00624 } 00625 } 00626 00627 if ( !_c[3].valid() || !_c[3]->_isSplit ) 00628 { 00629 _amrDrawable->add( new AMRTriangle( 00630 node(), offset + center * span, 00631 _a[PARENT_L]->node(), offset + OT(T_PARENT_L,o) * span, 00632 _a[QUADTREE]->node(), offset + OT(T_QUADTREE,o) * span ) ); 00633 } 00634 else 00635 { 00636 if ( !q3 ) 00637 { 00638 _amrDrawable->add( new AMRTriangle( 00639 node(), offset + center * span, 00640 _a[PARENT_L]->node(), offset + OT(T_PARENT_L,o) * span, 00641 _c[3]->node(), offset + OT(T_CHILD_3,o) * span ) ); 00642 } 00643 if ( !q0 ) 00644 { 00645 _amrDrawable->add( new AMRTriangle( 00646 node(), offset + center * span, 00647 _c[3]->node(), offset + OT(T_CHILD_3,o) * span, 00648 _a[QUADTREE]->node(), offset + OT(T_QUADTREE,o) * span ) ); 00649 } 00650 } 00651 } 00652 00653 // dirty the underlying element buffer object 00654 _drawableDirty = false; 00655 } 00656 00657 bool 00658 Diamond::hasChildren() const 00659 { 00660 for( unsigned short c = 0; c < _childValence; ++c ) 00661 { 00662 if ( _c[c].valid() ) 00663 return true; 00664 } 00665 return false; 00666 } 00667 00668 bool 00669 Diamond::hasAllChildren() const 00670 { 00671 for( unsigned short c = 0; c < _childValence; ++c ) 00672 { 00673 if ( !_c[c].valid() ) 00674 return false; 00675 } 00676 return true; 00677 } 00678 00679 ChildIndex 00680 Diamond::getIndexOfChild( Diamond* child ) 00681 { 00682 for( ChildIndex c = 0; c < _childValence; ++c ) 00683 { 00684 if ( _c[c].get() == child ) 00685 return c; 00686 } 00687 return -1; 00688 } 00689 00690 Diamond* 00691 Diamond::getNeighbor( ChildIndex c ) 00692 { 00693 unsigned short parent = c <= 1 ? PARENT_R : PARENT_L; 00694 ChildIndex myIndexInParent = _a[parent]->getIndexOfChild( this ); 00695 ChildIndex i = ( myIndexInParent + (c==0 || c==2? 1 : 3) ) % _a[parent]->_childValence; 00696 return _a[parent]->_c[i].get(); 00697 } 00698 00699 Diamond* 00700 Diamond::getOrCreateNeighbor( ChildIndex c ) 00701 { 00702 //OE_NOTICE << "Looking up neighbor (" << c << ") of " << _name << std::endl; 00703 unsigned short parent = c <= 1 ? PARENT_R : PARENT_L; 00704 ChildIndex myIndexInParent = _a[parent]->getIndexOfChild( this ); 00705 ChildIndex i = ( myIndexInParent + (c==0 || c==2? 1 : 3) ) % _a[parent]->_childValence; 00706 //if ( !_a[parent]->_c[i].valid() ) 00707 // OE_NOTICE << "...doesn't exist, creating:" << std::endl; 00708 return _a[parent]->getOrCreateChild( i ); 00709 } 00710 00711 ChildIndex 00712 Diamond::getIndexOfChildEdgeStartingAt( NodeIndex vi ) 00713 { 00714 if ( vi == _a[QUADTREE]->_vi ) return 0; 00715 else if ( vi == _a[PARENT_R]->_vi ) return 1; 00716 else if ( vi == _a[GDPARENT]->_vi ) return 2; 00717 else return 3; 00718 } 00719 00720 Diamond* 00721 Diamond::getOrCreateChild( ChildIndex c ) 00722 { 00723 if ( _c[c].valid() ) 00724 return _c[c].get(); 00725 00726 // must force a split before creating children. dont' need to call dirty() because 00727 // that gets called later by setChild() 00728 _isSplit = true; 00729 00730 //OE_NOTICE << "Creating child of " << _name << " at " << c << std::endl; 00731 //dump(); 00732 00733 Diamond* d = this; 00734 osg::ref_ptr<Diamond> d0 = getOrCreateNeighbor( c ); 00735 00736 unsigned short whichParent = c <= 1 ? PARENT_R : PARENT_L; 00737 osg::ref_ptr<Diamond> qa = d->_a[whichParent].get(); // the common parent 00738 00739 Diamond* child = new Diamond( _mesh, TileKey(), d->_level+1 ); 00740 //Diamond* child = new Diamond( _mesh, 0L, d->_level+1, " ("+_name+" + "+d0->_name+") " ); 00741 00742 //OE_NOTICE << "...neighbor: " << d0->_name << std::endl; 00743 //OE_NOTICE << "...common parent: " << commonParent->_name << std::endl; 00744 00745 // assign child's quadtree ansector to d and d0's common parent. 00746 child->_a[QUADTREE] = qa.get(); 00747 00748 // assign child's grandparent ansector pointer: 00749 child->_a[GDPARENT] = c == 0 || c == 3 ? d->_a[QUADTREE].get() : d->_a[GDPARENT].get(); 00750 00751 // determine the child index of child in d0: 00752 unsigned short p = c==0 || c==2 ? QUADTREE : GDPARENT; 00753 ChildIndex c_d0 = d0->getIndexOfChildEdgeStartingAt( child->_a[p]->_vi ); 00754 if ( c_d0 < 0 ) 00755 { 00756 OE_WARN << "Diamond: getOrCreateChild: illegal state, cannot find neighbor's child edge...crash immiment\n"; 00757 } 00758 00759 // assign the child's parent pointers 00760 child->_a[PARENT_R] = (c == 0 || c == 2) ? d : d0.get(); 00761 child->_a[PARENT_L] = (c == 0 || c == 2) ? d0.get() : d; 00762 00763 // how that we know the QUADTREE & GDPARENT ancestors, create the diamond vertex. 00764 osg::Vec3d newCoord = _mesh->_manifold->midpoint( 00765 child->_a[QUADTREE]->node()._manifoldCoord, 00766 child->_a[GDPARENT]->node()._manifoldCoord ); 00767 00768 child->setCoord( newCoord ); 00769 00770 // assign the new child in both d and d0: 00771 d->setChild( c, child ); 00772 d0->setChild( c_d0, child ); 00773 00774 if ( child->_hasGeometry ) 00775 { 00776 // if this is a intermediate diamond, make a new key for the parent's quadtree descendant. 00777 // if this is a geometry diamond, the new intermediate child gets no key. 00778 int quadrant = -1; 00779 00780 if ( qa->_orientation == 0 ) 00781 { 00782 quadrant = 00783 qa->q(0) == child ? 2 : 00784 qa->q(1) == child ? 3 : 00785 qa->q(2) == child ? 1 : 0; 00786 } 00787 else if ( qa->_orientation == 2 ) 00788 { 00789 quadrant = 00790 qa->q(0) == child ? 3 : 00791 qa->q(1) == child ? 1 : 00792 qa->q(2) == child ? 0 : 2; 00793 } 00794 else if ( qa->_orientation == 4 ) 00795 { 00796 quadrant = 00797 qa->q(0) == child ? 1 : 00798 qa->q(1) == child ? 0 : 00799 qa->q(2) == child ? 2 : 3; 00800 } 00801 else // if ( qa->_orientation == 6 ) 00802 { 00803 quadrant = 00804 qa->q(0) == child ? 0 : 00805 qa->q(1) == child ? 2 : 00806 qa->q(2) == child ? 3 : 1; 00807 } 00808 00809 child->_key = qa->_key.createChildKey( quadrant ); 00810 00811 child->_orientation = 00812 quadrant == 1 ? 0 : 00813 quadrant == 0 ? 2 : 00814 quadrant == 2 ? 4 : 6; 00815 } 00816 00817 child->activate(); 00818 00819 return child; 00820 } 00821 00822 void 00823 Diamond::removeChild( ChildIndex c ) 00824 { 00825 osg::ref_ptr<Diamond> child = _c[c].get(); 00826 if ( !child.valid() ) return; 00827 00828 // first we must merge the child (i.e. remove all of ITS children recursively). 00829 // TODO: this probably needs to queue up another merge operation with a higher priority. 00830 // that should probably happen in the mesh manager. 00831 child->merge(); 00832 00833 // deactivate the child. it's possible that the child is in a queue somewhere. this 00834 // will prevent it from being processed after it's been removed. 00835 child->_status = INACTIVE; 00836 00837 // remove it from its other parent as well. 00838 osg::ref_ptr<Diamond> d0 = getNeighbor( c ); 00839 if ( d0.valid() ) 00840 { 00841 ChildIndex c_d0 = d0->getIndexOfChild( child.get() ); 00842 if ( c_d0 >= 0 ) 00843 { 00844 d0->_c[c_d0] = 0L; 00845 } 00846 } 00847 00848 // now clear out the child slot and invalidate the primitive set. 00849 this->_c[c] = 0L; 00850 00851 // notify the common ancestry of the change 00852 if ( child->_hasGeometry ) 00853 { 00854 child->_a[QUADTREE]->dirty(); 00855 this->dirty(); 00856 } 00857 else 00858 { 00859 // probably not strictly necessary since a split() will take care of this.. gw 00860 child->_a[PARENT_L]->dirty(); 00861 child->_a[PARENT_R]->dirty(); 00862 } 00863 00864 // zero out the child's ancestor pointers. 00865 for( AncestorIndex i = 0; i < 4; ++i ) 00866 child->_a[i] = 0L; 00867 for( ChildIndex i = 0; i < 4; ++i ) 00868 child->_c[i] = 0L; 00869 00870 // remove the child's vertex from the VBO. 00871 _mesh->removeNode( child->_vi ); 00872 } 00873 00874