00001 /* 00002 ----------------------------------------------------------------------------- 00003 This source file is part of OGRE 00004 (Object-oriented Graphics Rendering Engine) 00005 For the latest info, see http://www.ogre3d.org/ 00006 00007 Copyright © 2000-2004 The OGRE Team 00008 Also see acknowledgements in Readme.html 00009 00010 This program is free software; you can redistribute it and/or modify it under 00011 the terms of the GNU Lesser General Public License as published by the Free Software 00012 Foundation; either version 2 of the License, or (at your option) any later 00013 version. 00014 00015 This program is distributed in the hope that it will be useful, but WITHOUT 00016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00017 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 00018 00019 You should have received a copy of the GNU Lesser General Public License along with 00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple 00021 Place - Suite 330, Boston, MA 02111-1307, USA, or go to 00022 http://www.gnu.org/copyleft/lesser.txt. 00023 ----------------------------------------------------------------------------- 00024 */ 00025 /*************************************************************************** 00026 terrainrenderable.cpp - description 00027 ------------------- 00028 begin : Sat Oct 5 2002 00029 copyright : (C) 2002 by Jon Anderson 00030 email : janders@users.sf.net 00031 00032 Enhancements 2003 - 2004 (C) The OGRE Team 00033 00034 ***************************************************************************/ 00035 00036 #include "OgreTerrainRenderable.h" 00037 #include "OgreSceneNode.h" 00038 #include "OgreRenderQueue.h" 00039 #include "OgreRenderOperation.h" 00040 #include "OgreCamera.h" 00041 #include "OgreRoot.h" 00042 #include "OgreTerrainSceneManager.h" 00043 #include "OgreLogManager.h" 00044 #include "OgreStringConverter.h" 00045 #include "OgreViewport.h" 00046 #include "OgreException.h" 00047 00048 namespace Ogre 00049 { 00050 //----------------------------------------------------------------------- 00051 #define MAIN_BINDING 0 00052 #define DELTA_BINDING 1 00053 //----------------------------------------------------------------------- 00054 //----------------------------------------------------------------------- 00055 TerrainBufferCache TerrainRenderable::msIndexCache; 00056 String TerrainRenderable::mType = "TerrainMipMap"; 00057 LevelArray TerrainRenderable::mLevelIndex; 00058 bool TerrainRenderable::mLevelInit = false; 00059 const TerrainOptions* TerrainRenderable::msOptions = 0; 00060 //----------------------------------------------------------------------- 00061 //----------------------------------------------------------------------- 00062 00063 //----------------------------------------------------------------------- 00064 TerrainRenderable::TerrainRenderable(const String& name) 00065 : mTerrain(0), mName(name), mDeltaBuffers(0), mPositionBuffer(0) 00066 { 00067 mForcedRenderLevel = -1; 00068 mLastNextLevel = -1; 00069 // Default query flags to top bit so users can exclude it if they wish 00070 mQueryFlags = 0x80000000; 00071 00072 mMinLevelDistSqr = 0; 00073 00074 mInit = false; 00075 00076 for ( int i = 0; i < 4; i++ ) 00077 { 00078 mNeighbors[ i ] = 0; 00079 } 00080 00081 _initLevelIndexes(); 00082 00083 } 00084 //----------------------------------------------------------------------- 00085 TerrainRenderable::~TerrainRenderable() 00086 { 00087 00088 deleteGeometry(); 00089 _destroyLevelIndexes(); 00090 } 00091 //----------------------------------------------------------------------- 00092 void TerrainRenderable::deleteGeometry() 00093 { 00094 if(mTerrain) 00095 delete mTerrain; 00096 00097 if (mPositionBuffer) 00098 delete [] mPositionBuffer; 00099 00100 if (mDeltaBuffers) 00101 delete [] mDeltaBuffers; 00102 00103 if ( mMinLevelDistSqr != 0 ) 00104 delete [] mMinLevelDistSqr; 00105 } 00106 //----------------------------------------------------------------------- 00107 void TerrainRenderable::initialise(int startx, int startz, 00108 Real* pageHeightData) 00109 { 00110 if (!msOptions) 00111 msOptions = &(TerrainSceneManager::getOptions()); 00112 00113 if ( msOptions->maxGeoMipMapLevel != 0 ) 00114 { 00115 int i = ( int ) 1 << ( msOptions->maxGeoMipMapLevel - 1 ) ; 00116 00117 if ( ( i + 1 ) > msOptions->tileSize ) 00118 { 00119 printf( "Invalid maximum mipmap specifed, must be n, such that 2^(n-1)+1 < tileSize \n" ); 00120 return ; 00121 } 00122 } 00123 00124 deleteGeometry(); 00125 00126 //calculate min and max heights; 00127 Real min = 256000, max = 0; 00128 00129 mTerrain = new VertexData; 00130 mTerrain->vertexStart = 0; 00131 mTerrain->vertexCount = msOptions->tileSize * msOptions->tileSize; 00132 00133 VertexDeclaration* decl = mTerrain->vertexDeclaration; 00134 VertexBufferBinding* bind = mTerrain->vertexBufferBinding; 00135 00136 // positions 00137 size_t offset = 0; 00138 decl->addElement(MAIN_BINDING, offset, VET_FLOAT3, VES_POSITION); 00139 offset += VertexElement::getTypeSize(VET_FLOAT3); 00140 if (TerrainSceneManager::getOptions().lit) 00141 { 00142 decl->addElement(MAIN_BINDING, offset, VET_FLOAT3, VES_NORMAL); 00143 offset += VertexElement::getTypeSize(VET_FLOAT3); 00144 } 00145 // texture coord sets 00146 decl->addElement(MAIN_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); 00147 offset += VertexElement::getTypeSize(VET_FLOAT2); 00148 decl->addElement(MAIN_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 1); 00149 offset += VertexElement::getTypeSize(VET_FLOAT2); 00150 if (TerrainSceneManager::getOptions().coloured) 00151 { 00152 decl->addElement(MAIN_BINDING, offset, VET_COLOUR, VES_DIFFUSE); 00153 offset += VertexElement::getTypeSize(VET_COLOUR); 00154 } 00155 00156 // Create shared vertex buffer 00157 mMainBuffer = 00158 HardwareBufferManager::getSingleton().createVertexBuffer( 00159 decl->getVertexSize(MAIN_BINDING), 00160 mTerrain->vertexCount, 00161 HardwareBuffer::HBU_STATIC_WRITE_ONLY); 00162 // Create system memory copy with just positions in it, for use in simple reads 00163 mPositionBuffer = new Real[mTerrain->vertexCount * 3]; 00164 00165 bind->setBinding(MAIN_BINDING, mMainBuffer); 00166 00167 if (msOptions->lodMorph) 00168 { 00169 // Create additional element for delta 00170 decl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS); 00171 // NB binding is not set here, it is set when deriving the LOD 00172 } 00173 00174 00175 mInit = true; 00176 00177 mRenderLevel = 1; 00178 00179 mMinLevelDistSqr = new Real[ msOptions->maxGeoMipMapLevel ]; 00180 00181 int endx = startx + msOptions->tileSize; 00182 00183 int endz = startz + msOptions->tileSize; 00184 00185 Vector3 left, down, here; 00186 00187 const VertexElement* poselem = decl->findElementBySemantic(VES_POSITION); 00188 const VertexElement* texelem0 = decl->findElementBySemantic(VES_TEXTURE_COORDINATES, 0); 00189 const VertexElement* texelem1 = decl->findElementBySemantic(VES_TEXTURE_COORDINATES, 1); 00190 Real* pSysPos = mPositionBuffer; 00191 00192 unsigned char* pBase = static_cast<unsigned char*>(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD)); 00193 00194 for ( int j = startz; j < endz; j++ ) 00195 { 00196 for ( int i = startx; i < endx; i++ ) 00197 { 00198 Real *pPos, *pTex0, *pTex1; 00199 poselem->baseVertexPointerToElement(pBase, &pPos); 00200 texelem0->baseVertexPointerToElement(pBase, &pTex0); 00201 texelem1->baseVertexPointerToElement(pBase, &pTex1); 00202 00203 Real height = pageHeightData[j * msOptions->pageSize + i]; 00204 height = height * msOptions->scale.y; // scale height 00205 00206 *pSysPos++ = *pPos++ = ( Real ) i * msOptions->scale.x; //x 00207 *pSysPos++ = *pPos++ = height; // y 00208 *pSysPos++ = *pPos++ = ( Real ) j * msOptions->scale.z; //z 00209 00210 *pTex0++ = ( Real ) i / ( Real ) msOptions->pageSize; 00211 *pTex0++ = ( Real ) ( Real ) j / ( Real ) msOptions->pageSize; 00212 00213 *pTex1++ = ( ( Real ) i / ( Real ) msOptions->tileSize ) * msOptions->detailTile; 00214 *pTex1++ = ( ( Real ) ( Real ) j / ( Real ) msOptions->tileSize ) * msOptions->detailTile; 00215 00216 if ( height < min ) 00217 min = ( Real ) height; 00218 00219 if ( height > max ) 00220 max = ( Real ) height; 00221 00222 pBase += mMainBuffer->getVertexSize(); 00223 } 00224 } 00225 00226 mMainBuffer->unlock(); 00227 00228 mBounds.setExtents( 00229 ( Real ) startx * msOptions->scale.x, 00230 min, 00231 ( Real ) startz * msOptions->scale.z, 00232 ( Real ) ( endx - 1 ) * msOptions->scale.x, 00233 max, 00234 ( Real ) ( endz - 1 ) * msOptions->scale.z ); 00235 00236 00237 mCenter = Vector3( ( startx * msOptions->scale.x + (endx - 1) * msOptions->scale.x ) / 2, 00238 ( min + max ) / 2, 00239 ( startz * msOptions->scale.z + (endz - 1) * msOptions->scale.z ) / 2 ); 00240 00241 // Create delta buffer list if required to morph 00242 if (msOptions->lodMorph) 00243 { 00244 // Create delta buffer for all except the lowest mip 00245 mDeltaBuffers = new HardwareVertexBufferSharedPtr[msOptions->maxGeoMipMapLevel - 1]; 00246 } 00247 00248 Real C = _calculateCFactor(); 00249 00250 _calculateMinLevelDist2( C ); 00251 if (msOptions->lit) 00252 _calculateNormals(); 00253 00254 } 00255 //----------------------------------------------------------------------- 00256 void TerrainRenderable::_getNormalAt( float x, float z, Vector3 * result ) 00257 { 00258 00259 assert(msOptions->lit && "No normals present"); 00260 00261 Vector3 here, left, down; 00262 here.x = x; 00263 here.y = getHeightAt( x, z ); 00264 here.z = z; 00265 00266 left.x = x - 1; 00267 left.y = getHeightAt( x - 1, z ); 00268 left.z = z; 00269 00270 down.x = x; 00271 down.y = getHeightAt( x, z + 1 ); 00272 down.z = z + 1; 00273 00274 left = left - here; 00275 00276 down = down - here; 00277 00278 left.normalise(); 00279 down.normalise(); 00280 00281 *result = left.crossProduct( down ); 00282 result -> normalise(); 00283 00284 // result->x = - result->x; 00285 // result->y = - result->y; 00286 // result->z = - result->z; 00287 } 00288 //----------------------------------------------------------------------- 00289 void TerrainRenderable::_calculateNormals() 00290 { 00291 Vector3 norm; 00292 00293 assert (msOptions->lit && "No normals present"); 00294 00295 HardwareVertexBufferSharedPtr vbuf = 00296 mTerrain->vertexBufferBinding->getBuffer(MAIN_BINDING); 00297 const VertexElement* elem = mTerrain->vertexDeclaration->findElementBySemantic(VES_NORMAL); 00298 unsigned char* pBase = static_cast<unsigned char*>( vbuf->lock(HardwareBuffer::HBL_DISCARD) ); 00299 Real* pNorm; 00300 00301 for ( size_t j = 0; j < msOptions->tileSize; j++ ) 00302 { 00303 for ( size_t i = 0; i < msOptions->tileSize; i++ ) 00304 { 00305 00306 _getNormalAt( _vertex( i, j, 0 ), _vertex( i, j, 2 ), &norm ); 00307 00308 // printf( "Normal = %5f,%5f,%5f\n", norm.x, norm.y, norm.z ); 00309 elem->baseVertexPointerToElement(pBase, &pNorm); 00310 *pNorm++ = norm.x; 00311 *pNorm++ = norm.y; 00312 *pNorm++ = norm.z; 00313 pBase += vbuf->getVertexSize(); 00314 } 00315 00316 } 00317 vbuf->unlock(); 00318 } 00319 //----------------------------------------------------------------------- 00320 void TerrainRenderable::_notifyCurrentCamera( Camera* cam ) 00321 { 00322 00323 if ( mForcedRenderLevel >= 0 ) 00324 { 00325 mRenderLevel = mForcedRenderLevel; 00326 return ; 00327 } 00328 00329 00330 Vector3 cpos = cam -> getDerivedPosition(); 00331 Vector3 diff = mCenter - cpos; 00332 00333 Real L = diff.squaredLength(); 00334 00335 mRenderLevel = -1; 00336 00337 for ( int i = 0; i < msOptions->maxGeoMipMapLevel; i++ ) 00338 { 00339 if ( mMinLevelDistSqr[ i ] > L ) 00340 { 00341 mRenderLevel = i - 1; 00342 break; 00343 } 00344 } 00345 00346 if ( mRenderLevel < 0 ) 00347 mRenderLevel = msOptions->maxGeoMipMapLevel - 1; 00348 00349 if (msOptions->lodMorph) 00350 { 00351 // Get the next LOD level down 00352 int nextLevel = mNextLevelDown[mRenderLevel]; 00353 if (nextLevel == 0) 00354 { 00355 // No next level, so never morph 00356 mLODMorphFactor = 0; 00357 } 00358 else 00359 { 00360 // Set the morph such that the morph happens in the last 0.25 of 00361 // the distance range 00362 Real range = mMinLevelDistSqr[nextLevel] - mMinLevelDistSqr[mRenderLevel]; 00363 if (range) 00364 { 00365 Real percent = (L - mMinLevelDistSqr[mRenderLevel]) / range; 00366 // scale result so that msLODMorphStart == 0, 1 == 1, clamp to 0 below that 00367 Real rescale = 1.0f / (1.0f - msOptions->lodMorphStart); 00368 mLODMorphFactor = std::max((percent - msOptions->lodMorphStart) * rescale, 0.0f); 00369 } 00370 else 00371 { 00372 // Identical ranges 00373 mLODMorphFactor = 0.0f; 00374 } 00375 00376 assert(mLODMorphFactor >= 0 && mLODMorphFactor <= 1); 00377 } 00378 00379 // Bind the correct delta buffer if it has changed 00380 // nextLevel - 1 since the first entry is for LOD 1 (since LOD 0 never needs it) 00381 if (mLastNextLevel != nextLevel) 00382 { 00383 if (nextLevel > 0) 00384 { 00385 mTerrain->vertexBufferBinding->setBinding(DELTA_BINDING, 00386 mDeltaBuffers[nextLevel - 1]); 00387 } 00388 else 00389 { 00390 // bind dummy (incase bindings checked) 00391 mTerrain->vertexBufferBinding->setBinding(DELTA_BINDING, 00392 mDeltaBuffers[0]); 00393 } 00394 } 00395 mLastNextLevel = nextLevel; 00396 00397 } 00398 00399 } 00400 //----------------------------------------------------------------------- 00401 void TerrainRenderable::_updateRenderQueue( RenderQueue* queue ) 00402 { 00403 queue->addRenderable( this ); 00404 } 00405 //----------------------------------------------------------------------- 00406 void TerrainRenderable::getRenderOperation( RenderOperation& op ) 00407 { 00408 //setup indexes for vertices and uvs... 00409 00410 assert( mInit && "Uninitialized" ); 00411 00412 op.useIndexes = true; 00413 op.operationType = msOptions->useTriStrips ? 00414 RenderOperation::OT_TRIANGLE_STRIP : RenderOperation::OT_TRIANGLE_LIST; 00415 op.vertexData = mTerrain; 00416 op.indexData = getIndexData(); 00417 00418 00419 } 00420 //----------------------------------------------------------------------- 00421 void TerrainRenderable::getWorldTransforms( Matrix4* xform ) const 00422 { 00423 *xform = mParentNode->_getFullTransform(); 00424 } 00425 //----------------------------------------------------------------------- 00426 const Quaternion& TerrainRenderable::getWorldOrientation(void) const 00427 { 00428 return mParentNode->_getDerivedOrientation(); 00429 } 00430 //----------------------------------------------------------------------- 00431 const Vector3& TerrainRenderable::getWorldPosition(void) const 00432 { 00433 return mParentNode->_getDerivedPosition(); 00434 } 00435 //----------------------------------------------------------------------- 00436 bool TerrainRenderable::_checkSize( int n ) 00437 { 00438 for ( int i = 0; i < 10; i++ ) 00439 { 00440 if ( ( ( 1 << i ) + 1 ) == n ) 00441 return true; 00442 } 00443 00444 return false; 00445 } 00446 //----------------------------------------------------------------------- 00447 void TerrainRenderable::_calculateMinLevelDist2( Real C ) 00448 { 00449 //level 0 has no delta. 00450 mMinLevelDistSqr[ 0 ] = 0; 00451 00452 int i, j; 00453 00454 for ( int level = 1; level < msOptions->maxGeoMipMapLevel; level++ ) 00455 { 00456 mMinLevelDistSqr[ level ] = 0; 00457 00458 int step = 1 << level; 00459 // The step of the next higher LOD 00460 int higherstep = step >> 1; 00461 00462 Real* pDeltas = 0; 00463 if (msOptions->lodMorph) 00464 { 00465 // Create a set of delta values (store at index - 1 since 0 has none) 00466 mDeltaBuffers[level - 1] = createDeltaBuffer(); 00467 // Lock, but don't discard (we want the pre-initialised zeros) 00468 pDeltas = static_cast<Real*>( 00469 mDeltaBuffers[level - 1]->lock(HardwareBuffer::HBL_NORMAL)); 00470 } 00471 00472 for ( j = 0; j < msOptions->tileSize - step; j += step ) 00473 { 00474 for ( i = 0; i < msOptions->tileSize - step; i += step ) 00475 { 00476 /* Form planes relating to the lower detail tris to be produced 00477 For tri lists and even tri strip rows, they are this shape: 00478 x---x 00479 | / | 00480 x---x 00481 For odd tri strip rows, they are this shape: 00482 x---x 00483 | \ | 00484 x---x 00485 */ 00486 00487 Vector3 v1(_vertex( i, j, 0 ), _vertex( i, j, 1 ), _vertex( i, j, 2 )); 00488 Vector3 v2(_vertex( i + step, j, 0 ), _vertex( i + step, j, 1 ), _vertex( i + step, j, 2 )); 00489 Vector3 v3(_vertex( i, j + step, 0 ), _vertex( i, j + step, 1 ), _vertex( i, j + step, 2 )); 00490 Vector3 v4(_vertex( i + step, j + step, 0 ), _vertex( i + step, j + step, 1 ), _vertex( i + step, j + step, 2 )); 00491 00492 Plane t1, t2; 00493 bool backwardTri = false; 00494 if (!msOptions->useTriStrips || j % 2 == 0) 00495 { 00496 t1.redefine(v1, v3, v2); 00497 t2.redefine(v2, v3, v4); 00498 } 00499 else 00500 { 00501 t1.redefine(v1, v3, v4); 00502 t2.redefine(v1, v4, v2); 00503 backwardTri = true; 00504 } 00505 00506 // include the bottommost row of vertices if this is the last row 00507 int zubound = (j == (msOptions->tileSize - step)? step : step - 1); 00508 for ( int z = 0; z <= zubound; z++ ) 00509 { 00510 // include the rightmost col of vertices if this is the last col 00511 int xubound = (i == (msOptions->tileSize - step)? step : step - 1); 00512 for ( int x = 0; x <= xubound; x++ ) 00513 { 00514 int fulldetailx = i + x; 00515 int fulldetailz = j + z; 00516 if ( fulldetailx % step == 0 && 00517 fulldetailz % step == 0 ) 00518 { 00519 // Skip, this one is a vertex at this level 00520 continue; 00521 } 00522 00523 Real zpct = (Real)z / (Real)step; 00524 Real xpct = (Real)x / (Real)step; 00525 00526 //interpolated height 00527 Vector3 actualPos( 00528 _vertex( fulldetailx, fulldetailz, 0 ), 00529 _vertex( fulldetailx, fulldetailz, 1 ), 00530 _vertex( fulldetailx, fulldetailz, 2 )); 00531 Real interp_h; 00532 // Determine which tri we're on 00533 if ((xpct + zpct <= 1.0f && !backwardTri) || 00534 (xpct + (1-zpct) <= 1.0f && backwardTri)) 00535 { 00536 // Solve for x/z 00537 interp_h = 00538 (-(t1.normal.x * actualPos.x) 00539 - t1.normal.z * actualPos.z 00540 - t1.d) / t1.normal.y; 00541 } 00542 else 00543 { 00544 // Second tri 00545 interp_h = 00546 (-(t2.normal.x * actualPos.x) 00547 - t2.normal.z * actualPos.z 00548 - t2.d) / t2.normal.y; 00549 } 00550 00551 Real actual_h = _vertex( fulldetailx, fulldetailz, 1 ); 00552 Real delta = fabs( interp_h - actual_h ); 00553 00554 Real D2 = delta * delta * C * C; 00555 00556 if ( mMinLevelDistSqr[ level ] < D2 ) 00557 mMinLevelDistSqr[ level ] = D2; 00558 00559 // Should be save height difference? 00560 // Don't morph along edges 00561 if (msOptions->lodMorph && 00562 fulldetailx != 0 && fulldetailx != (msOptions->tileSize - 1) && 00563 fulldetailz != 0 && fulldetailz != (msOptions->tileSize - 1) ) 00564 { 00565 // Save height difference 00566 pDeltas[fulldetailx + (fulldetailz * msOptions->tileSize)] = 00567 interp_h - actual_h; 00568 } 00569 00570 } 00571 00572 } 00573 } 00574 } 00575 00576 // Unlock morph deltas if required 00577 if (msOptions->lodMorph) 00578 { 00579 mDeltaBuffers[level - 1]->unlock(); 00580 } 00581 } 00582 00583 00584 00585 // Post validate the whole set 00586 for ( i = 1; i < msOptions->maxGeoMipMapLevel; i++ ) 00587 { 00588 00589 // Make sure no LOD transition within the tile 00590 // This is especially a problem when using large tiles with flat areas 00591 /* Hmm, this can look bad on some areas, disable for now 00592 Vector3 delta(_vertex(0,0,0), mCenter.y, _vertex(0,0,2)); 00593 delta = delta - mCenter; 00594 Real minDist = delta.squaredLength(); 00595 mMinLevelDistSqr[ i ] = std::max(mMinLevelDistSqr[ i ], minDist); 00596 */ 00597 00598 //make sure the levels are increasing... 00599 if ( mMinLevelDistSqr[ i ] < mMinLevelDistSqr[ i - 1 ] ) 00600 { 00601 mMinLevelDistSqr[ i ] = mMinLevelDistSqr[ i - 1 ]; 00602 } 00603 } 00604 00605 // Now reverse traverse the list setting the 'next level down' 00606 Real lastDist = -1; 00607 int lastIndex = 0; 00608 for (i = msOptions->maxGeoMipMapLevel - 1; i >= 0; --i) 00609 { 00610 if (i == msOptions->maxGeoMipMapLevel - 1) 00611 { 00612 // Last one is always 0 00613 lastIndex = i; 00614 lastDist = mMinLevelDistSqr[i]; 00615 mNextLevelDown[i] = 0; 00616 } 00617 else 00618 { 00619 mNextLevelDown[i] = lastIndex; 00620 if (mMinLevelDistSqr[i] != lastDist) 00621 { 00622 lastIndex = i; 00623 lastDist = mMinLevelDistSqr[i]; 00624 } 00625 } 00626 00627 } 00628 00629 00630 } 00631 //----------------------------------------------------------------------- 00632 void TerrainRenderable::_initLevelIndexes() 00633 { 00634 if ( mLevelInit ) 00635 return ; 00636 00637 00638 if ( mLevelIndex.size() == 0 ) 00639 { 00640 for ( int i = 0; i < 16; i++ ) 00641 { 00642 00643 mLevelIndex.push_back( new IndexMap() ); 00644 00645 } 00646 00647 } 00648 00649 mLevelInit = true; 00650 } 00651 //----------------------------------------------------------------------- 00652 void TerrainRenderable::_destroyLevelIndexes() 00653 { 00654 if ( mLevelInit ) 00655 { 00656 for ( int i = 0; i < 16; i++ ) 00657 { 00658 delete mLevelIndex[i]; 00659 } 00660 mLevelIndex.clear(); 00661 mLevelInit = false; 00662 } 00663 } 00664 //----------------------------------------------------------------------- 00665 void TerrainRenderable::_adjustRenderLevel( int i ) 00666 { 00667 00668 mRenderLevel = i; 00669 } 00670 //----------------------------------------------------------------------- 00671 Real TerrainRenderable::_calculateCFactor() 00672 { 00673 Real A, T; 00674 00675 const TerrainOptions& opts = TerrainSceneManager::getOptions(); 00676 if (!opts.primaryCamera) 00677 { 00678 Except(Exception::ERR_ITEM_NOT_FOUND, 00679 "You have not created a camera yet!", 00680 "TerrainRenderable::_calculateCFactor"); 00681 } 00682 00683 //A = 1 / Math::Tan(Math::AngleUnitsToRadians(opts.primaryCamera->getFOVy())); 00684 // Turn off detail compression at higher FOVs 00685 A = 1.0f; 00686 00687 int vertRes = opts.primaryCamera->getViewport()->getActualHeight(); 00688 00689 T = 2 * ( Real ) opts.maxPixelError / ( Real ) vertRes; 00690 00691 return A / T; 00692 } 00693 //----------------------------------------------------------------------- 00694 float TerrainRenderable::getHeightAt( float x, float z ) 00695 { 00696 Vector3 start; 00697 Vector3 end; 00698 00699 start.x = _vertex( 0, 0, 0 ); 00700 start.y = _vertex( 0, 0, 1 ); 00701 start.z = _vertex( 0, 0, 2 ); 00702 00703 end.x = _vertex( msOptions->tileSize - 1, msOptions->tileSize - 1, 0 ); 00704 end.y = _vertex( msOptions->tileSize - 1, msOptions->tileSize - 1, 1 ); 00705 end.z = _vertex( msOptions->tileSize - 1, msOptions->tileSize - 1, 2 ); 00706 00707 /* Safety catch, if the point asked for is outside 00708 * of this tile, it will ask the appropriate tile 00709 */ 00710 00711 if ( x < start.x ) 00712 { 00713 if ( mNeighbors[ WEST ] != 0 ) 00714 return mNeighbors[ WEST ] ->getHeightAt( x, z ); 00715 else 00716 x = start.x; 00717 } 00718 00719 if ( x > end.x ) 00720 { 00721 if ( mNeighbors[ EAST ] != 0 ) 00722 return mNeighbors[ EAST ] ->getHeightAt( x, z ); 00723 else 00724 x = end.x; 00725 } 00726 00727 if ( z < start.z ) 00728 { 00729 if ( mNeighbors[ NORTH ] != 0 ) 00730 return mNeighbors[ NORTH ] ->getHeightAt( x, z ); 00731 else 00732 z = start.z; 00733 } 00734 00735 if ( z > end.z ) 00736 { 00737 if ( mNeighbors[ SOUTH ] != 0 ) 00738 return mNeighbors[ SOUTH ] ->getHeightAt( x, z ); 00739 else 00740 z = end.z; 00741 } 00742 00743 00744 00745 float x_pct = ( x - start.x ) / ( end.x - start.x ); 00746 float z_pct = ( z - start.z ) / ( end.z - start.z ); 00747 00748 float x_pt = x_pct * ( float ) ( msOptions->tileSize - 1 ); 00749 float z_pt = z_pct * ( float ) ( msOptions->tileSize - 1 ); 00750 00751 int x_index = ( int ) x_pt; 00752 int z_index = ( int ) z_pt; 00753 00754 // If we got to the far right / bottom edge, move one back 00755 if (x_index == msOptions->tileSize - 1) 00756 { 00757 --x_index; 00758 x_pct = 1.0f; 00759 } 00760 else 00761 { 00762 // get remainder 00763 x_pct = x_pt - x_index; 00764 } 00765 if (z_index == msOptions->tileSize - 1) 00766 { 00767 --z_index; 00768 z_pct = 1.0f; 00769 } 00770 else 00771 { 00772 z_pct = z_pt - z_index; 00773 } 00774 00775 //bilinear interpolate to find the height. 00776 00777 float t1 = _vertex( x_index, z_index, 1 ); 00778 float t2 = _vertex( x_index + 1, z_index, 1 ); 00779 float b1 = _vertex( x_index, z_index + 1, 1 ); 00780 float b2 = _vertex( x_index + 1, z_index + 1, 1 ); 00781 00782 float midpoint = (b1 + t2) / 2.0; 00783 00784 if (x_pct + z_pct <= 1) { 00785 b2 = midpoint + (midpoint - t1); 00786 } else { 00787 t1 = midpoint + (midpoint - b2); 00788 } 00789 00790 float t = ( t1 * ( 1 - x_pct ) ) + ( t2 * ( x_pct ) ); 00791 float b = ( b1 * ( 1 - x_pct ) ) + ( b2 * ( x_pct ) ); 00792 00793 float h = ( t * ( 1 - z_pct ) ) + ( b * ( z_pct ) ); 00794 00795 return h; 00796 } 00797 //----------------------------------------------------------------------- 00798 bool TerrainRenderable::intersectSegment( const Vector3 & start, const Vector3 & end, Vector3 * result ) 00799 { 00800 Vector3 dir = end - start; 00801 Vector3 ray = start; 00802 00803 //special case... 00804 if ( dir.x == 0 && dir.z == 0 ) 00805 { 00806 if ( ray.y <= getHeightAt( ray.x, ray.z ) ) 00807 { 00808 if ( result != 0 ) 00809 * result = start; 00810 00811 return true; 00812 } 00813 } 00814 00815 dir.normalise(); 00816 00817 //dir.x *= mScale.x; 00818 //dir.y *= mScale.y; 00819 //dir.z *= mScale.z; 00820 00821 const Vector3 * corners = getBoundingBox().getAllCorners(); 00822 00823 //start with the next one... 00824 ray += dir; 00825 00826 00827 while ( ! ( ( ray.x < corners[ 0 ].x ) || 00828 ( ray.x > corners[ 4 ].x ) || 00829 ( ray.z < corners[ 0 ].z ) || 00830 ( ray.z > corners[ 4 ].z ) ) ) 00831 { 00832 00833 00834 float h = getHeightAt( ray.x, ray.z ); 00835 00836 if ( ray.y <= h ) 00837 { 00838 if ( result != 0 ) 00839 * result = ray; 00840 00841 return true; 00842 } 00843 00844 else 00845 { 00846 ray += dir; 00847 } 00848 00849 } 00850 00851 if ( ray.x < corners[ 0 ].x && mNeighbors[ WEST ] != 0 ) 00852 return mNeighbors[ WEST ] ->intersectSegment( ray, end, result ); 00853 else if ( ray.z < corners[ 0 ].z && mNeighbors[ NORTH ] != 0 ) 00854 return mNeighbors[ NORTH ] ->intersectSegment( ray, end, result ); 00855 else if ( ray.x > corners[ 4 ].x && mNeighbors[ EAST ] != 0 ) 00856 return mNeighbors[ EAST ] ->intersectSegment( ray, end, result ); 00857 else if ( ray.z > corners[ 4 ].z && mNeighbors[ SOUTH ] != 0 ) 00858 return mNeighbors[ SOUTH ] ->intersectSegment( ray, end, result ); 00859 else 00860 { 00861 if ( result != 0 ) 00862 * result = Vector3( -1, -1, -1 ); 00863 00864 return false; 00865 } 00866 } 00867 //----------------------------------------------------------------------- 00868 void TerrainRenderable::_generateVertexLighting( const Vector3 &sun, ColourValue ambient ) 00869 { 00870 00871 Vector3 pt; 00872 Vector3 normal; 00873 Vector3 light; 00874 00875 HardwareVertexBufferSharedPtr vbuf = 00876 mTerrain->vertexBufferBinding->getBuffer(MAIN_BINDING); 00877 const VertexElement* elem = mTerrain->vertexDeclaration->findElementBySemantic(VES_DIFFUSE); 00878 //for each point in the terrain, see if it's in the line of sight for the sun. 00879 for ( size_t i = 0; i < msOptions->tileSize; i++ ) 00880 { 00881 for ( size_t j = 0; j < msOptions->tileSize; j++ ) 00882 { 00883 // printf( "Checking %f,%f,%f ", pt.x, pt.y, pt.z ); 00884 pt.x = _vertex( i, j, 0 ); 00885 pt.y = _vertex( i, j, 1 ); 00886 pt.z = _vertex( i, j, 2 ); 00887 00888 light = sun - pt; 00889 00890 light.normalise(); 00891 00892 if ( ! intersectSegment( pt, sun, 0 ) ) 00893 { 00894 // 00895 _getNormalAt( _vertex( i, j, 0 ), _vertex( i, j, 2 ), &normal ); 00896 00897 float l = light.dotProduct( normal ); 00898 00899 ColourValue v; 00900 v.r = ambient.r + l; 00901 v.g = ambient.g + l; 00902 v.b = ambient.b + l; 00903 00904 if ( v.r > 1 ) v.r = 1; 00905 00906 if ( v.g > 1 ) v.g = 1; 00907 00908 if ( v.b > 1 ) v.b = 1; 00909 00910 if ( v.r < 0 ) v.r = 0; 00911 00912 if ( v.g < 0 ) v.g = 0; 00913 00914 if ( v.b < 0 ) v.b = 0; 00915 00916 RGBA colour; 00917 Root::getSingleton().convertColourValue( v, &colour ); 00918 vbuf->writeData( 00919 (_index( i, j ) * vbuf->getVertexSize()) + elem->getOffset(), 00920 sizeof(RGBA), &colour); 00921 } 00922 00923 else 00924 { 00925 RGBA colour; 00926 Root::getSingleton().convertColourValue( ambient, &colour ); 00927 00928 vbuf->writeData( 00929 (_index( i, j ) * vbuf->getVertexSize()) + elem->getOffset(), 00930 sizeof(RGBA), &colour); 00931 } 00932 00933 } 00934 00935 } 00936 00937 printf( "." ); 00938 } 00939 //----------------------------------------------------------------------- 00940 Real TerrainRenderable::getSquaredViewDepth(const Camera* cam) const 00941 { 00942 Vector3 diff = mCenter - cam->getDerivedPosition(); 00943 // Use squared length to avoid square root 00944 return diff.squaredLength(); 00945 } 00946 00947 //----------------------------------------------------------------------- 00948 const LightList& TerrainRenderable::getLights(void) const 00949 { 00950 return getParentSceneNode()->findLights(this->getBoundingRadius()); 00951 } 00952 //----------------------------------------------------------------------- 00953 IndexData* TerrainRenderable::getIndexData(void) 00954 { 00955 unsigned int stitchFlags = 0; 00956 00957 if ( mNeighbors[ EAST ] != 0 && mNeighbors[ EAST ] -> mRenderLevel > mRenderLevel ) 00958 { 00959 stitchFlags |= STITCH_EAST; 00960 stitchFlags |= 00961 (mNeighbors[ EAST ] -> mRenderLevel - mRenderLevel) << STITCH_EAST_SHIFT; 00962 } 00963 00964 if ( mNeighbors[ WEST ] != 0 && mNeighbors[ WEST ] -> mRenderLevel > mRenderLevel ) 00965 { 00966 stitchFlags |= STITCH_WEST; 00967 stitchFlags |= 00968 (mNeighbors[ WEST ] -> mRenderLevel - mRenderLevel) << STITCH_WEST_SHIFT; 00969 } 00970 00971 if ( mNeighbors[ NORTH ] != 0 && mNeighbors[ NORTH ] -> mRenderLevel > mRenderLevel ) 00972 { 00973 stitchFlags |= STITCH_NORTH; 00974 stitchFlags |= 00975 (mNeighbors[ NORTH ] -> mRenderLevel - mRenderLevel) << STITCH_NORTH_SHIFT; 00976 } 00977 00978 if ( mNeighbors[ SOUTH ] != 0 && mNeighbors[ SOUTH ] -> mRenderLevel > mRenderLevel ) 00979 { 00980 stitchFlags |= STITCH_SOUTH; 00981 stitchFlags |= 00982 (mNeighbors[ SOUTH ] -> mRenderLevel - mRenderLevel) << STITCH_SOUTH_SHIFT; 00983 } 00984 00985 // Check preexisting 00986 IndexMap::iterator ii = mLevelIndex[ mRenderLevel ]->find( stitchFlags ); 00987 IndexData* indexData; 00988 if ( ii == mLevelIndex[ mRenderLevel ]->end()) 00989 { 00990 // Create 00991 if (msOptions->useTriStrips) 00992 { 00993 indexData = generateTriStripIndexes(stitchFlags); 00994 } 00995 else 00996 { 00997 indexData = generateTriListIndexes(stitchFlags); 00998 } 00999 mLevelIndex[ mRenderLevel ]->insert( 01000 IndexMap::value_type(stitchFlags, indexData)); 01001 } 01002 else 01003 { 01004 indexData = ii->second; 01005 } 01006 01007 01008 return indexData; 01009 01010 01011 } 01012 //----------------------------------------------------------------------- 01013 IndexData* TerrainRenderable::generateTriStripIndexes(unsigned int stitchFlags) 01014 { 01015 // The step used for the current level 01016 int step = 1 << mRenderLevel; 01017 // The step used for the lower level 01018 int lowstep = 1 << (mRenderLevel + 1); 01019 01020 int numIndexes = 0; 01021 01022 // Calculate the number of indexes required 01023 // This is the number of 'cells' at this detail level x 2 01024 // plus 3 degenerates to turn corners 01025 int numTrisAcross = (((msOptions->tileSize-1) / step) * 2) + 3; 01026 // Num indexes is number of tris + 2 01027 int new_length = numTrisAcross * ((msOptions->tileSize-1) / step) + 2; 01028 //this is the maximum for a level. It wastes a little, but shouldn't be a problem. 01029 01030 IndexData* indexData = new IndexData; 01031 indexData->indexBuffer = 01032 HardwareBufferManager::getSingleton().createIndexBuffer( 01033 HardwareIndexBuffer::IT_16BIT, 01034 new_length, HardwareBuffer::HBU_STATIC_WRITE_ONLY);//, false); 01035 01036 msIndexCache.mCache.push_back( indexData ); 01037 01038 unsigned short* pIdx = static_cast<unsigned short*>( 01039 indexData->indexBuffer->lock(0, 01040 indexData->indexBuffer->getSizeInBytes(), 01041 HardwareBuffer::HBL_DISCARD)); 01042 01043 // Stripified mesh 01044 for ( int j = 0; j < msOptions->tileSize - 1; j += step ) 01045 { 01046 int i; 01047 // Forward strip 01048 // We just do the |/ here, final | done after 01049 for ( i = 0; i < msOptions->tileSize - 1; i += step ) 01050 { 01051 int x[4], y[4]; 01052 x[0] = x[1] = i; 01053 x[2] = x[3] = i + step; 01054 y[0] = y[2] = j; 01055 y[1] = y[3] = j + step; 01056 01057 if (j == 0 && (stitchFlags & STITCH_NORTH)) 01058 { 01059 // North reduction means rounding x[0] and x[2] 01060 if (x[0] % lowstep != 0) 01061 { 01062 // Since we know we only drop down one level of LOD, 01063 // removing 1 step of higher LOD should return to lower 01064 x[0] -= step; 01065 } 01066 if (x[2] % lowstep != 0) 01067 { 01068 x[2] -= step; 01069 } 01070 } 01071 01072 // Never get a south tiling on a forward strip (always finish on 01073 // a backward strip) 01074 01075 if (i == 0 && (stitchFlags & STITCH_WEST)) 01076 { 01077 // West reduction means rounding y[0] / y[1] 01078 if (y[0] % lowstep != 0) 01079 { 01080 y[0] -= step; 01081 } 01082 if (y[1] % lowstep != 0) 01083 { 01084 y[1] -= step; 01085 } 01086 } 01087 if (i == (msOptions->tileSize - 1 - step) && (stitchFlags & STITCH_EAST)) 01088 { 01089 // East tiling means rounding y[2] & y[3] 01090 if (y[2] % lowstep != 0) 01091 { 01092 y[2] -= step; 01093 } 01094 if (y[3] % lowstep != 0) 01095 { 01096 y[3] -= step; 01097 } 01098 } 01099 01100 //triangles 01101 if (i == 0) 01102 { 01103 // Starter 01104 *pIdx++ = _index( x[0], y[0] ); numIndexes++; 01105 } 01106 *pIdx++ = _index( x[1], y[1] ); numIndexes++; 01107 *pIdx++ = _index( x[2], y[2] ); numIndexes++; 01108 01109 if (i == msOptions->tileSize - 1 - step) 01110 { 01111 // Emit extra index to finish row 01112 *pIdx++ = _index( x[3], y[3] ); numIndexes++; 01113 if (j < msOptions->tileSize - 1 - step) 01114 { 01115 // Emit this index twice more (this is to turn around without 01116 // artefacts) 01117 // ** Hmm, looks like we can drop this and it's unnoticeable 01118 //*pIdx++ = _index( x[3], y[3] ); numIndexes++; 01119 //*pIdx++ = _index( x[3], y[3] ); numIndexes++; 01120 } 01121 } 01122 01123 } 01124 // Increment row 01125 j += step; 01126 // Backward strip 01127 for ( i = msOptions->tileSize - 1; i > 0 ; i -= step ) 01128 { 01129 int x[4], y[4]; 01130 x[0] = x[1] = i; 01131 x[2] = x[3] = i - step; 01132 y[0] = y[2] = j; 01133 y[1] = y[3] = j + step; 01134 01135 // Never get a north tiling on a backward strip (always 01136 // start on a forward strip) 01137 if (j == (msOptions->tileSize - 1 - step) && (stitchFlags & STITCH_SOUTH)) 01138 { 01139 // South reduction means rounding x[1] / x[3] 01140 if (x[1] % lowstep != 0) 01141 { 01142 x[1] -= step; 01143 } 01144 if (x[3] % lowstep != 0) 01145 { 01146 x[3] -= step; 01147 } 01148 } 01149 01150 if (i == step && (stitchFlags & STITCH_WEST)) 01151 { 01152 // West tiling on backward strip is rounding of y[2] / y[3] 01153 if (y[2] % lowstep != 0) 01154 { 01155 y[2] -= step; 01156 } 01157 if (y[3] % lowstep != 0) 01158 { 01159 y[3] -= step; 01160 } 01161 } 01162 if (i == msOptions->tileSize - 1 && (stitchFlags & STITCH_EAST)) 01163 { 01164 // East tiling means rounding y[0] and y[1] on backward strip 01165 if (y[0] % lowstep != 0) 01166 { 01167 y[0] -= step; 01168 } 01169 if (y[1] % lowstep != 0) 01170 { 01171 y[1] -= step; 01172 } 01173 } 01174 01175 //triangles 01176 if (i == msOptions->tileSize) 01177 { 01178 // Starter 01179 *pIdx++ = _index( x[0], y[0] ); numIndexes++; 01180 } 01181 *pIdx++ = _index( x[1], y[1] ); numIndexes++; 01182 *pIdx++ = _index( x[2], y[2] ); numIndexes++; 01183 01184 if (i == step) 01185 { 01186 // Emit extra index to finish row 01187 *pIdx++ = _index( x[3], y[3] ); numIndexes++; 01188 if (j < msOptions->tileSize - 1 - step) 01189 { 01190 // Emit this index once more (this is to turn around) 01191 *pIdx++ = _index( x[3], y[3] ); numIndexes++; 01192 } 01193 } 01194 } 01195 } 01196 01197 01198 indexData->indexBuffer->unlock(); 01199 indexData->indexCount = numIndexes; 01200 indexData->indexStart = 0; 01201 01202 return indexData; 01203 01204 } 01205 //----------------------------------------------------------------------- 01206 IndexData* TerrainRenderable::generateTriListIndexes(unsigned int stitchFlags) 01207 { 01208 01209 int numIndexes = 0; 01210 int step = 1 << mRenderLevel; 01211 01212 IndexData* indexData = 0; 01213 01214 int north = stitchFlags & STITCH_NORTH ? step : 0; 01215 int south = stitchFlags & STITCH_SOUTH ? step : 0; 01216 int east = stitchFlags & STITCH_EAST ? step : 0; 01217 int west = stitchFlags & STITCH_WEST ? step : 0; 01218 01219 int new_length = ( msOptions->tileSize / step ) * ( msOptions->tileSize / step ) * 2 * 2 * 2 ; 01220 //this is the maximum for a level. It wastes a little, but shouldn't be a problem. 01221 01222 indexData = new IndexData; 01223 indexData->indexBuffer = 01224 HardwareBufferManager::getSingleton().createIndexBuffer( 01225 HardwareIndexBuffer::IT_16BIT, 01226 new_length, HardwareBuffer::HBU_STATIC_WRITE_ONLY);//, false); 01227 01228 msIndexCache.mCache.push_back( indexData ); 01229 01230 unsigned short* pIdx = static_cast<unsigned short*>( 01231 indexData->indexBuffer->lock(0, 01232 indexData->indexBuffer->getSizeInBytes(), 01233 HardwareBuffer::HBL_DISCARD)); 01234 01235 // Do the core vertices, minus stitches 01236 for ( int j = north; j < msOptions->tileSize - 1 - south; j += step ) 01237 { 01238 for ( int i = west; i < msOptions->tileSize - 1 - east; i += step ) 01239 { 01240 //triangles 01241 *pIdx++ = _index( i, j ); numIndexes++; 01242 *pIdx++ = _index( i, j + step ); numIndexes++; 01243 *pIdx++ = _index( i + step, j ); numIndexes++; 01244 01245 *pIdx++ = _index( i, j + step ); numIndexes++; 01246 *pIdx++ = _index( i + step, j + step ); numIndexes++; 01247 *pIdx++ = _index( i + step, j ); numIndexes++; 01248 } 01249 } 01250 01251 // North stitching 01252 if ( north > 0 ) 01253 { 01254 numIndexes += stitchEdge(NORTH, mRenderLevel, mNeighbors[NORTH]->mRenderLevel, 01255 west > 0, east > 0, &pIdx); 01256 } 01257 // East stitching 01258 if ( east > 0 ) 01259 { 01260 numIndexes += stitchEdge(EAST, mRenderLevel, mNeighbors[EAST]->mRenderLevel, 01261 north > 0, south > 0, &pIdx); 01262 } 01263 // South stitching 01264 if ( south > 0 ) 01265 { 01266 numIndexes += stitchEdge(SOUTH, mRenderLevel, mNeighbors[SOUTH]->mRenderLevel, 01267 east > 0, west > 0, &pIdx); 01268 } 01269 // West stitching 01270 if ( west > 0 ) 01271 { 01272 numIndexes += stitchEdge(WEST, mRenderLevel, mNeighbors[WEST]->mRenderLevel, 01273 south > 0, north > 0, &pIdx); 01274 } 01275 01276 01277 indexData->indexBuffer->unlock(); 01278 indexData->indexCount = numIndexes; 01279 indexData->indexStart = 0; 01280 01281 return indexData; 01282 } 01283 //----------------------------------------------------------------------- 01284 HardwareVertexBufferSharedPtr TerrainRenderable::createDeltaBuffer(void) 01285 { 01286 // Delta buffer is a 1D float buffer of height offsets 01287 HardwareVertexBufferSharedPtr buf = 01288 HardwareBufferManager::getSingleton().createVertexBuffer( 01289 VertexElement::getTypeSize(VET_FLOAT1), 01290 msOptions->tileSize * msOptions->tileSize, 01291 HardwareBuffer::HBU_STATIC_WRITE_ONLY); 01292 // Fill the buffer with zeros, we will only fill in delta 01293 void* pVoid = buf->lock(HardwareBuffer::HBL_DISCARD); 01294 memset(pVoid, 0, msOptions->tileSize * msOptions->tileSize * sizeof(Real)); 01295 buf->unlock(); 01296 01297 return buf; 01298 01299 } 01300 //----------------------------------------------------------------------- 01301 void TerrainRenderable::_updateCustomGpuParameter( 01302 const GpuProgramParameters::AutoConstantEntry& constantEntry, 01303 GpuProgramParameters* params) const 01304 { 01305 if (constantEntry.data == MORPH_CUSTOM_PARAM_ID) 01306 { 01307 // Update morph LOD factor 01308 params->setConstant(constantEntry.index, mLODMorphFactor); 01309 } 01310 else 01311 { 01312 Renderable::_updateCustomGpuParameter(constantEntry, params); 01313 } 01314 01315 } 01316 //----------------------------------------------------------------------- 01317 int TerrainRenderable::stitchEdge(Neighbor neighbor, int hiLOD, int loLOD, 01318 bool omitFirstTri, bool omitLastTri, unsigned short** ppIdx) 01319 { 01320 assert(loLOD > hiLOD); 01321 /* 01322 Now do the stitching; we can stitch from any level to any level. 01323 The stitch pattern is like this for each pair of vertices in the lower LOD 01324 (excuse the poor ascii art): 01325 01326 lower LOD 01327 *-----------* 01328 |\ \ 3 / /| 01329 |1\2 \ / 4/5| 01330 *--*--*--*--* 01331 higher LOD 01332 01333 The algorithm is, for each pair of lower LOD vertices: 01334 1. Iterate over the higher LOD vertices, generating tris connected to the 01335 first lower LOD vertex, up to and including 1/2 the span of the lower LOD 01336 over the higher LOD (tris 1-2). Skip the first tri if it is on the edge 01337 of the tile and that edge is to be stitched itself. 01338 2. Generate a single tri for the middle using the 2 lower LOD vertices and 01339 the middle vertex of the higher LOD (tri 3). 01340 3. Iterate over the higher LOD vertices from 1/2 the span of the lower LOD 01341 to the end, generating tris connected to the second lower LOD vertex 01342 (tris 4-5). Skip the last tri if it is on the edge of a tile and that 01343 edge is to be stitched itself. 01344 01345 The same algorithm works for all edges of the patch; stitching is done 01346 clockwise so that the origin and steps used change, but the general 01347 approach does not. 01348 */ 01349 01350 // Get pointer to be updated 01351 unsigned short* pIdx = *ppIdx; 01352 01353 // Work out the steps ie how to increment indexes 01354 // Step from one vertex to another in the high detail version 01355 int step = 1 << hiLOD; 01356 // Step from one vertex to another in the low detail version 01357 int superstep = 1 << loLOD; 01358 // Step half way between low detail steps 01359 int halfsuperstep = superstep >> 1; 01360 01361 // Work out the starting points and sign of increments 01362 // We always work the strip clockwise 01363 int startx, starty, endx, rowstep; 01364 bool horizontal; 01365 switch(neighbor) 01366 { 01367 case NORTH: 01368 startx = starty = 0; 01369 endx = msOptions->tileSize - 1; 01370 rowstep = step; 01371 horizontal = true; 01372 break; 01373 case SOUTH: 01374 // invert x AND y direction, helps to keep same winding 01375 startx = starty = msOptions->tileSize - 1; 01376 endx = 0; 01377 rowstep = -step; 01378 step = -step; 01379 superstep = -superstep; 01380 halfsuperstep = -halfsuperstep; 01381 horizontal = true; 01382 break; 01383 case EAST: 01384 startx = 0; 01385 endx = msOptions->tileSize - 1; 01386 starty = msOptions->tileSize - 1; 01387 rowstep = -step; 01388 horizontal = false; 01389 break; 01390 case WEST: 01391 startx = msOptions->tileSize - 1; 01392 endx = 0; 01393 starty = 0; 01394 rowstep = step; 01395 step = -step; 01396 superstep = -superstep; 01397 halfsuperstep = -halfsuperstep; 01398 horizontal = false; 01399 break; 01400 }; 01401 01402 int numIndexes = 0; 01403 01404 for ( int j = startx; j != endx; j += superstep ) 01405 { 01406 int k; 01407 for (k = 0; k != halfsuperstep; k += step) 01408 { 01409 int jk = j + k; 01410 //skip the first bit of the corner? 01411 if ( j != startx || k != 0 || !omitFirstTri ) 01412 { 01413 if (horizontal) 01414 { 01415 *pIdx++ = _index( j , starty ); numIndexes++; 01416 *pIdx++ = _index( jk, starty + rowstep ); numIndexes++; 01417 *pIdx++ = _index( jk + step, starty + rowstep ); numIndexes++; 01418 } 01419 else 01420 { 01421 *pIdx++ = _index( starty, j ); numIndexes++; 01422 *pIdx++ = _index( starty + rowstep, jk ); numIndexes++; 01423 *pIdx++ = _index( starty + rowstep, jk + step); numIndexes++; 01424 } 01425 } 01426 } 01427 01428 // Middle tri 01429 if (horizontal) 01430 { 01431 *pIdx++ = _index( j, starty ); numIndexes++; 01432 *pIdx++ = _index( j + halfsuperstep, starty + rowstep); numIndexes++; 01433 *pIdx++ = _index( j + superstep, starty ); numIndexes++; 01434 } 01435 else 01436 { 01437 *pIdx++ = _index( starty, j ); numIndexes++; 01438 *pIdx++ = _index( starty + rowstep, j + halfsuperstep ); numIndexes++; 01439 *pIdx++ = _index( starty, j + superstep ); numIndexes++; 01440 } 01441 01442 for (k = halfsuperstep; k != superstep; k += step) 01443 { 01444 int jk = j + k; 01445 if ( j != endx - superstep || k != superstep - step || !omitLastTri ) 01446 { 01447 if (horizontal) 01448 { 01449 *pIdx++ = _index( j + superstep, starty ); numIndexes++; 01450 *pIdx++ = _index( jk, starty + rowstep ); numIndexes++; 01451 *pIdx++ = _index( jk + step, starty + rowstep ); numIndexes++; 01452 } 01453 else 01454 { 01455 *pIdx++ = _index( starty, j + superstep ); numIndexes++; 01456 *pIdx++ = _index( starty + rowstep, jk ); numIndexes++; 01457 *pIdx++ = _index( starty + rowstep, jk + step ); numIndexes++; 01458 } 01459 } 01460 } 01461 } 01462 01463 *ppIdx = pIdx; 01464 01465 return numIndexes; 01466 01467 } 01468 01469 01470 } //namespace
Copyright © 2002-2003 by The OGRE Team
Last modified Sun Nov 28 19:48:48 2004