terrCell.cpp
Engine/source/terrain/terrCell.cpp
Public Functions
GFXImplementVertexFormat(TerrVertex )
Detailed Description
Public Functions
GFXImplementVertexFormat(TerrVertex )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames 26// Copyright (C) 2015 Faust Logic, Inc. 27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 28 29#include "platform/platform.h" 30#include "terrain/terrCell.h" 31 32#include "math/util/frustum.h" 33#include "terrain/terrData.h" 34#include "terrain/terrCellMaterial.h" 35#include "scene/sceneRenderState.h" 36#include "lighting/lightManager.h" 37#include "gfx/gfxDrawUtil.h" 38 39 40GFXImplementVertexFormat( TerrVertex ) 41{ 42 addElement( "POSITION", GFXDeclType_Float3 ); 43 addElement( "NORMAL", GFXDeclType_Float3 ); 44 addElement( "TangentZ", GFXDeclType_Float, 0 ); 45 addElement( "Empty", GFXDeclType_Float, 1 ); 46}; 47 48const U32 TerrCell::smMinCellSize = 64; 49const U32 TerrCell::smVBStride = TerrCell::smMinCellSize + 1; // 129 50const U32 TerrCell::smVBSize = ( TerrCell::smVBStride * TerrCell::smVBStride ) + 51 ( TerrCell::smVBStride * 4 ); // 17,157 52const U32 TerrCell::smPBSize = ( TerrCell::smMinCellSize * TerrCell::smMinCellSize * 6 ) + 53 ( TerrCell::smMinCellSize * 4 * 6 ); // 101,376 54const U32 TerrCell::smTriCount = TerrCell::smPBSize / 3; // 33,792 55 56 57TerrCell::TerrCell() 58 : mTriCount( 0 ), 59 mHasEmpty( false ), 60 mMaterial( NULL ), 61 mMaterials( 0 ), 62 mIsInteriorOnly( false ), 63 mSize(smMinCellSize), 64 mLevel(0), 65 mTerrain(NULL), 66 mRadius(0.5f) 67{ 68 dMemset( mChildren, 0, sizeof( mChildren ) ); 69 zode_vertexBuffer = 0; 70} 71 72TerrCell::~TerrCell() 73{ 74 SAFE_DELETE( mMaterial ); 75 76 for ( U32 i=0; i < 4; i++ ) 77 SAFE_DELETE( mChildren[i] ); 78 deleteZodiacVertexBuffer(); 79} 80 81void TerrCell::createPrimBuffer( GFXPrimitiveBufferHandle *primBuffer ) 82{ 83 PROFILE_SCOPE( TerrCell_AllocPrimBuffer ); 84 85 primBuffer->set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" ); 86 87 // We don't use the primitive for normal clipmap 88 // rendering, but it is used for the shadow pass. 89 GFXPrimitive *prim = primBuffer->getPointer()->mPrimitiveArray; 90 prim->type = GFXTriangleList; 91 prim->numPrimitives = smTriCount; 92 prim->numVertices = smVBSize; 93 94 // 95 // The vertex pattern for the terrain is as 96 // follows... 97 // 98 // 0----1----2.....n 99 // |\ | /| 100 // | \ | / | 101 // | \ | / | 102 // | \|/ | 103 // n----n----n 104 // | /|\ | 105 // | / | \ | 106 // | / | \ | 107 // |/ | \| 108 // n----n----n 109 // 110 111 // Lock and fill it up! 112 U16 *idxBuff; 113 primBuffer->lock( &idxBuff ); 114 U32 counter = 0; 115 U32 maxIndex = 0; 116 117 for ( U32 y = 0; y < smMinCellSize; y++ ) 118 { 119 const U32 yTess = y % 2; 120 121 for ( U32 x = 0; x < smMinCellSize; x++ ) 122 { 123 U32 index = ( y * smVBStride ) + x; 124 125 const U32 xTess = x % 2; 126 127 if ( ( xTess == 0 && yTess == 0 ) || 128 ( xTess != 0 && yTess != 0 ) ) 129 { 130 idxBuff[0] = index + 0; 131 idxBuff[1] = index + smVBStride; 132 idxBuff[2] = index + smVBStride + 1; 133 134 idxBuff[3] = index + 0; 135 idxBuff[4] = index + smVBStride + 1; 136 idxBuff[5] = index + 1; 137 } 138 else 139 { 140 idxBuff[0] = index + 1; 141 idxBuff[1] = index; 142 idxBuff[2] = index + smVBStride; 143 144 idxBuff[3] = index + 1; 145 idxBuff[4] = index + smVBStride; 146 idxBuff[5] = index + smVBStride + 1; 147 } 148 149 idxBuff += 6; 150 maxIndex = index + 1 + smVBStride; 151 counter += 6; 152 } 153 } 154 155 // Now add indices for the 'skirts'. 156 // These could probably be reduced to a loop. 157 158 // Temporaries that hold triangle indices. 159 // Top/Bottom - 0,1 160 U32 t0, t1, b0, b1; 161 162 // Top edge skirt... 163 164 // Index to the first vert of the top row. 165 U32 startIndex = 0; 166 // Index to the first vert of the skirt under the top row. 167 U32 skirtStartIdx = smVBStride * smVBStride; 168 // Step to go one vert to the right. 169 U32 step = 1; 170 171 for ( U32 i = 0; i < smMinCellSize; i++ ) 172 { 173 t0 = startIndex + i * step; 174 t1 = t0 + step; 175 b0 = skirtStartIdx + i; 176 b1 = skirtStartIdx + i + 1; 177 178 idxBuff[0] = b0; 179 idxBuff[1] = t0; 180 idxBuff[2] = t1; 181 182 idxBuff[3] = b1; 183 idxBuff[4] = b0; 184 idxBuff[5] = t1; 185 186 idxBuff += 6; 187 maxIndex = b1; 188 counter += 6; 189 } 190 191 // Bottom edge skirt... 192 193 // Index to the first vert of the bottom row. 194 startIndex = smVBStride * smVBStride - smVBStride; 195 // Index to the first vert of the skirt under the bottom row. 196 skirtStartIdx = startIndex + smVBStride * 2; 197 // Step to go one vert to the right. 198 step = 1; 199 200 for ( U32 i = 0; i < smMinCellSize; i++ ) 201 { 202 t0 = startIndex + ( i * step ); 203 t1 = t0 + step; 204 b0 = skirtStartIdx + i; 205 b1 = skirtStartIdx + i + 1; 206 207 idxBuff[0] = t1; 208 idxBuff[1] = t0; 209 idxBuff[2] = b0; 210 211 idxBuff[3] = t1; 212 idxBuff[4] = b0; 213 idxBuff[5] = b1; 214 215 idxBuff += 6; 216 maxIndex = b1; 217 counter += 6; 218 } 219 220 // Left edge skirt... 221 222 // Index to the first vert of the left column. 223 startIndex = 0; 224 // Index to the first vert of the skirt under the left column. 225 skirtStartIdx = smVBStride * smVBStride + smVBStride * 2; 226 // Step to go one vert down. 227 step = smVBStride; 228 229 for ( U32 i = 0; i < smMinCellSize; i++ ) 230 { 231 t0 = startIndex + ( i * step ); 232 t1 = t0 + step; 233 b0 = skirtStartIdx + i; 234 b1 = skirtStartIdx + i + 1; 235 236 idxBuff[0] = t1; 237 idxBuff[1] = t0; 238 idxBuff[2] = b0; 239 240 idxBuff[3] = t1; 241 idxBuff[4] = b0; 242 idxBuff[5] = b1; 243 244 idxBuff += 6; 245 maxIndex = b1; 246 counter += 6; 247 } 248 249 // Right edge skirt... 250 251 // Index to the first vert of the right column. 252 startIndex = smVBStride - 1; 253 // Index to the first vert of the skirt under the right column. 254 skirtStartIdx = smVBStride * smVBStride + smVBStride * 3; 255 // Step to go one vert down. 256 step = smVBStride; 257 258 for ( U32 i = 0; i < smMinCellSize; i++ ) 259 { 260 t0 = startIndex + ( i * step ); 261 t1 = t0 + step; 262 b0 = skirtStartIdx + i; 263 b1 = skirtStartIdx + i + 1; 264 265 idxBuff[0] = b0; 266 idxBuff[1] = t0; 267 idxBuff[2] = t1; 268 269 idxBuff[3] = b1; 270 idxBuff[4] = b0; 271 idxBuff[5] = t1; 272 273 idxBuff += 6; 274 maxIndex = b1; 275 counter += 6; 276 } 277 278 primBuffer->unlock(); 279} 280 281TerrCell* TerrCell::init( TerrainBlock *terrain ) 282{ 283 // Just create the root cell and call the inner init. 284 TerrCell *root = new TerrCell; 285 root->_init( terrain, 286 Point2I( 0, 0 ), 287 terrain->getBlockSize(), 288 0 ); 289 290 // Set initial states of OBBs. 291 root->updateOBBs(); 292 293 return root; 294} 295 296 297void TerrCell::_init( TerrainBlock *terrain, 298 const Point2I &point, 299 U32 size, 300 U32 level ) 301{ 302 PROFILE_SCOPE( TerrCell_Init ); 303 304 mTerrain = terrain; 305 mPoint = point; 306 mSize = size; 307 mLevel = level; 308 309 // Generate a VB (and maybe a PB) for this cell, unless we are the Root cell. 310 if ( level > 0 ) 311 { 312 _updateVertexBuffer(); 313 _updatePrimitiveBuffer(); 314 } 315 316 if ( mSize <= smMinCellSize ) 317 { 318 // Update our bounds and materials... the 319 // parent will use it to update itself. 320 _updateBounds(); 321 _updateMaterials(); 322 return; 323 } 324 325 // Create our children and update our 326 // bounds and materials from them. 327 328 const U32 childSize = mSize / 2; 329 const U32 childLevel = mLevel + 1; 330 331 mChildren[0] = new TerrCell; 332 mChildren[0]->_init( mTerrain, 333 Point2I( mPoint.x, mPoint.y ), 334 childSize, 335 childLevel ); 336 mBounds = mChildren[0]->getBounds(); 337 mMaterials = mChildren[0]->getMaterials(); 338 339 mChildren[1] = new TerrCell; 340 mChildren[1]->_init( mTerrain, 341 Point2I( mPoint.x + childSize, mPoint.y ), 342 childSize, 343 childLevel ); 344 mBounds.intersect( mChildren[1]->getBounds() ); 345 mMaterials |= mChildren[1]->getMaterials(); 346 347 mChildren[2] = new TerrCell; 348 mChildren[2]->_init( mTerrain, 349 Point2I( mPoint.x, mPoint.y + childSize ), 350 childSize, 351 childLevel ); 352 mBounds.intersect( mChildren[2]->getBounds() ); 353 mMaterials |= mChildren[2]->getMaterials(); 354 355 mChildren[3] = new TerrCell; 356 mChildren[3]->_init( mTerrain, 357 Point2I( mPoint.x + childSize, mPoint.y + childSize ), 358 childSize, 359 childLevel ); 360 mBounds.intersect( mChildren[3]->getBounds() ); 361 mMaterials |= mChildren[3]->getMaterials(); 362 363 mRadius = mBounds.len() * 0.5f; 364 365 _updateOBB(); 366} 367 368void TerrCell::updateGrid( const RectI &gridRect, bool opacityOnly ) 369{ 370 PROFILE_SCOPE( TerrCell_UpdateGrid ); 371 372 // If we have a VB... then update it. 373 if ( mVertexBuffer.isValid() && !opacityOnly ) 374 _updateVertexBuffer(); 375 376 // Update our PB, if any 377 _updatePrimitiveBuffer(); 378 379 // If we don't have children... then we're 380 // a leaf at the bottom of the cell quadtree 381 // and we should just update our bounds. 382 if ( !mChildren[0] ) 383 { 384 if ( !opacityOnly ) 385 _updateBounds(); 386 387 _updateMaterials(); 388 return; 389 } 390 391 // Otherwise, we must call updateGrid on our children 392 // and then update our bounds/materials AFTER to contain them. 393 394 mMaterials = 0; 395 396 for ( U32 i = 0; i < 4; i++ ) 397 { 398 TerrCell *cell = mChildren[i]; 399 400 // The overlap test doesn't hit shared edges 401 // so grow it a bit when we create it. 402 const RectI cellRect( cell->mPoint.x - 1, 403 cell->mPoint.y - 1, 404 cell->mSize + 2, 405 cell->mSize + 2 ); 406 407 // We do an overlap and containment test as it 408 // properly handles zero sized rects. 409 if ( cellRect.contains( gridRect ) || 410 cellRect.overlaps( gridRect ) ) 411 cell->updateGrid( gridRect, opacityOnly ); 412 413 // Update the bounds from our children. 414 if ( !opacityOnly ) 415 { 416 if ( i == 0 ) 417 mBounds = mChildren[i]->getBounds(); 418 else 419 mBounds.intersect( mChildren[i]->getBounds() ); 420 421 mRadius = mBounds.len() * 0.5f; 422 } 423 424 // Update the material flags. 425 mMaterials |= mChildren[i]->getMaterials(); 426 } 427 428 if ( mMaterial ) 429 mMaterial->init( mTerrain, mMaterials ); 430} 431 432void TerrCell::_updateVertexBuffer() 433{ 434 PROFILE_SCOPE( TerrCell_UpdateVertexBuffer ); 435 436 // Start off with no empty squares 437 mHasEmpty = false; 438 mEmptyVertexList.clear(); 439 440 mVertexBuffer.set( GFX, smVBSize, GFXBufferTypeStatic ); 441 442 const F32 squareSize = mTerrain->getSquareSize(); 443 const U32 blockSize = mTerrain->getBlockSize(); 444 const U32 stepSize = mSize / smMinCellSize; 445 446 U32 vbcounter = 0; 447 448 TerrVertex *vert = mVertexBuffer.lock(); 449 450 Point2I gridPt; 451 Point2F point; 452 F32 height; 453 Point3F normal; 454 455 const TerrainFile *file = mTerrain->getFile(); 456 457 for ( U32 y = 0; y < smVBStride; y++ ) 458 { 459 for ( U32 x = 0; x < smVBStride; x++ ) 460 { 461 // We clamp here to keep the geometry from reading across 462 // one side of the height map to the other causing walls 463 // around the edges of the terrain. 464 gridPt.x = mClamp( mPoint.x + x * stepSize, 0, blockSize - 1 ); 465 gridPt.y = mClamp( mPoint.y + y * stepSize, 0, blockSize - 1 ); 466 467 // Setup this point. 468 point.x = (F32)gridPt.x * squareSize; 469 point.y = (F32)gridPt.y * squareSize; 470 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); 471 vert->point.x = point.x; 472 vert->point.y = point.y; 473 vert->point.z = height; 474 475 // Get the normal. 476 mTerrain->getSmoothNormal( point, &normal, true, false ); 477 vert->normal = normal; 478 479 // Get the tangent z. 480 vert->tangentZ = fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ) - height; 481 482 // Test the empty state for this vert. 483 if ( file->isEmptyAt( gridPt.x, gridPt.y ) ) 484 { 485 mHasEmpty = true; 486 mEmptyVertexList.push_back( vbcounter ); 487 } 488 489 vbcounter++; 490 ++vert; 491 } 492 } 493 494 // Add verts for 'skirts' around/beneath the edge verts of this cell. 495 // This could probably be reduced to a loop... 496 497 const F32 skirtDepth = mSize / smMinCellSize * mTerrain->getSquareSize(); 498 499 // Top edge skirt 500 for ( U32 i = 0; i < smVBStride; i++ ) 501 { 502 gridPt.x = mClamp( mPoint.x + i * stepSize, 0, blockSize - 1 ); 503 gridPt.y = mClamp( mPoint.y, 0, blockSize - 1 ); 504 505 point.x = (F32)gridPt.x * squareSize; 506 point.y = (F32)gridPt.y * squareSize; 507 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); 508 vert->point.x = point.x; 509 vert->point.y = point.y; 510 vert->point.z = height - skirtDepth; 511 512 // Get the normal. 513 mTerrain->getNormal( point, &normal, true, false ); 514 vert->normal = normal; 515 516 // Get the tangent. 517 vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ); 518 519 vbcounter++; 520 ++vert; 521 } 522 523 // Bottom edge skirt 524 for ( U32 i = 0; i < smVBStride; i++ ) 525 { 526 gridPt.x = mClamp( mPoint.x + i * stepSize, 0, blockSize - 1 ); 527 gridPt.y = mClamp( mPoint.y + smMinCellSize * stepSize, 0, blockSize - 1 ); 528 529 point.x = (F32)gridPt.x * squareSize; 530 point.y = (F32)gridPt.y * squareSize; 531 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); 532 vert->point.x = point.x; 533 vert->point.y = point.y; 534 vert->point.z = height - skirtDepth; 535 536 // Get the normal. 537 mTerrain->getNormal( point, &normal, true, false ); 538 vert->normal = normal; 539 540 // Get the tangent. 541 vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ); 542 543 vbcounter++; 544 ++vert; 545 } 546 547 // Left edge skirt 548 for ( U32 i = 0; i < smVBStride; i++ ) 549 { 550 gridPt.x = mClamp( mPoint.x, 0, blockSize - 1 ); 551 gridPt.y = mClamp( mPoint.y + i * stepSize, 0, blockSize - 1 ); 552 553 point.x = (F32)gridPt.x * squareSize; 554 point.y = (F32)gridPt.y * squareSize; 555 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); 556 vert->point.x = point.x; 557 vert->point.y = point.y; 558 vert->point.z = height - skirtDepth; 559 560 // Get the normal. 561 mTerrain->getNormal( point, &normal, true, false ); 562 vert->normal = normal; 563 564 // Get the tangent. 565 vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ); 566 567 vbcounter++; 568 ++vert; 569 } 570 571 // Right edge skirt 572 for ( U32 i = 0; i < smVBStride; i++ ) 573 { 574 gridPt.x = mClamp( mPoint.x + smMinCellSize * stepSize, 0, blockSize - 1 ); 575 gridPt.y = mClamp( mPoint.y + i * stepSize, 0, blockSize - 1 ); 576 577 point.x = (F32)gridPt.x * squareSize; 578 point.y = (F32)gridPt.y * squareSize; 579 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); 580 vert->point.x = point.x; 581 vert->point.y = point.y; 582 vert->point.z = height - skirtDepth; 583 584 // Get the normal. 585 mTerrain->getNormal( point, &normal, true, false ); 586 vert->normal = normal; 587 588 // Get the tangent. 589 vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ); 590 591 vbcounter++; 592 ++vert; 593 } 594 595 AssertFatal( vbcounter == smVBSize, "bad" ); 596 mVertexBuffer.unlock(); 597 deleteZodiacVertexBuffer(); 598} 599 600void TerrCell::_updatePrimitiveBuffer() 601{ 602 PROFILE_SCOPE( TerrCell_UpdatePrimitiveBuffer ); 603 604 if ( !mHasEmpty ) 605 { 606 if ( mPrimBuffer.isValid() ) 607 { 608 // There are no more empty squares for this cell, so 609 // get rid of the primitive buffer to use the standard one. 610 mPrimBuffer = NULL; 611 } 612 613 return; 614 } 615 616 // Build our custom primitive buffer. We're setting it to the maximum allowed 617 // size, but should be just shy of it depending on the number of empty squares 618 // in this cell. We could calculate it, but note that it would be different 619 // from mEmptyVertexList.size() as that can include vertices on the edges that 620 // are really considered part of another cell's squares. So we take a slightly 621 // larger buffer over running through the calculation. 622 mPrimBuffer.set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" ); 623 624 GFXPrimitive *prim = mPrimBuffer.getPointer()->mPrimitiveArray; 625 prim->type = GFXTriangleList; 626 prim->numVertices = smVBSize; 627 628 mTriCount = 0; 629 630 // Lock and fill it up! 631 U16 *idxBuff; 632 mPrimBuffer.lock( &idxBuff ); 633 U32 counter = 0; 634 U32 maxIndex = 0; 635 636 for ( U32 y = 0; y < smMinCellSize; y++ ) 637 { 638 const U32 yTess = y % 2; 639 640 for ( U32 x = 0; x < smMinCellSize; x++ ) 641 { 642 U32 index = ( y * smVBStride ) + x; 643 644 // Should this square be skipped? 645 if ( _isVertIndexEmpty(index) ) 646 continue; 647 648 const U32 xTess = x % 2; 649 650 if ( ( xTess == 0 && yTess == 0 ) || 651 ( xTess != 0 && yTess != 0 ) ) 652 { 653 idxBuff[0] = index + 0; 654 idxBuff[1] = index + smVBStride; 655 idxBuff[2] = index + smVBStride + 1; 656 657 idxBuff[3] = index + 0; 658 idxBuff[4] = index + smVBStride + 1; 659 idxBuff[5] = index + 1; 660 } 661 else 662 { 663 idxBuff[0] = index + 1; 664 idxBuff[1] = index; 665 idxBuff[2] = index + smVBStride; 666 667 idxBuff[3] = index + 1; 668 idxBuff[4] = index + smVBStride; 669 idxBuff[5] = index + smVBStride + 1; 670 } 671 672 idxBuff += 6; 673 maxIndex = index + 1 + smVBStride; 674 counter += 6; 675 676 mTriCount += 2; 677 } 678 } 679 680 // Now add indices for the 'skirts'. 681 // These could probably be reduced to a loop. 682 683 // Temporaries that hold triangle indices. 684 // Top/Bottom - 0,1 685 U32 t0, t1, b0, b1; 686 687 // Top edge skirt... 688 689 // Index to the first vert of the top row. 690 U32 startIndex = 0; 691 // Index to the first vert of the skirt under the top row. 692 U32 skirtStartIdx = smVBStride * smVBStride; 693 // Step to go one vert to the right. 694 U32 step = 1; 695 696 for ( U32 i = 0; i < smMinCellSize; i++ ) 697 { 698 t0 = startIndex + i * step; 699 700 // Should this square be skipped? 701 if ( _isVertIndexEmpty(t0) ) 702 continue; 703 704 t1 = t0 + step; 705 b0 = skirtStartIdx + i; 706 b1 = skirtStartIdx + i + 1; 707 708 idxBuff[0] = b0; 709 idxBuff[1] = t0; 710 idxBuff[2] = t1; 711 712 idxBuff[3] = b1; 713 idxBuff[4] = b0; 714 idxBuff[5] = t1; 715 716 idxBuff += 6; 717 maxIndex = b1; 718 counter += 6; 719 720 mTriCount += 2; 721 } 722 723 // Bottom edge skirt... 724 725 // Index to the first vert of the bottom row. 726 startIndex = smVBStride * smVBStride - smVBStride; 727 // Index to the first vert of the skirt under the bottom row. 728 skirtStartIdx = startIndex + smVBStride * 2; 729 // Step to go one vert to the right. 730 step = 1; 731 732 for ( U32 i = 0; i < smMinCellSize; i++ ) 733 { 734 t0 = startIndex + ( i * step ); 735 736 // Should this square be skipped? We actually need to test 737 // the vertex one row down as it defines the empty state 738 // for this square. 739 if ( _isVertIndexEmpty( t0 - smVBStride ) ) 740 continue; 741 742 t1 = t0 + step; 743 b0 = skirtStartIdx + i; 744 b1 = skirtStartIdx + i + 1; 745 746 idxBuff[0] = t1; 747 idxBuff[1] = t0; 748 idxBuff[2] = b0; 749 750 idxBuff[3] = t1; 751 idxBuff[4] = b0; 752 idxBuff[5] = b1; 753 754 idxBuff += 6; 755 maxIndex = b1; 756 counter += 6; 757 758 mTriCount += 2; 759 } 760 761 // Left edge skirt... 762 763 // Index to the first vert of the left column. 764 startIndex = 0; 765 // Index to the first vert of the skirt under the left column. 766 skirtStartIdx = smVBStride * smVBStride + smVBStride * 2; 767 // Step to go one vert down. 768 step = smVBStride; 769 770 for ( U32 i = 0; i < smMinCellSize; i++ ) 771 { 772 t0 = startIndex + ( i * step ); 773 774 // Should this square be skipped? 775 if ( _isVertIndexEmpty(t0) ) 776 continue; 777 778 t1 = t0 + step; 779 b0 = skirtStartIdx + i; 780 b1 = skirtStartIdx + i + 1; 781 782 idxBuff[0] = t1; 783 idxBuff[1] = t0; 784 idxBuff[2] = b0; 785 786 idxBuff[3] = t1; 787 idxBuff[4] = b0; 788 idxBuff[5] = b1; 789 790 idxBuff += 6; 791 maxIndex = b1; 792 counter += 6; 793 794 mTriCount += 2; 795 } 796 797 // Right edge skirt... 798 799 // Index to the first vert of the right column. 800 startIndex = smVBStride - 1; 801 // Index to the first vert of the skirt under the right column. 802 skirtStartIdx = smVBStride * smVBStride + smVBStride * 3; 803 // Step to go one vert down. 804 step = smVBStride; 805 806 for ( U32 i = 0; i < smMinCellSize; i++ ) 807 { 808 t0 = startIndex + ( i * step ); 809 810 // Should this square be skipped? We actually need to test 811 // the vertex one column to the left as it defines the empty 812 // state for this square. 813 if ( _isVertIndexEmpty( t0 - 1 ) ) 814 continue; 815 816 t1 = t0 + step; 817 b0 = skirtStartIdx + i; 818 b1 = skirtStartIdx + i + 1; 819 820 idxBuff[0] = b0; 821 idxBuff[1] = t0; 822 idxBuff[2] = t1; 823 824 idxBuff[3] = b1; 825 idxBuff[4] = b0; 826 idxBuff[5] = t1; 827 828 idxBuff += 6; 829 maxIndex = b1; 830 counter += 6; 831 832 mTriCount += 2; 833 } 834 835 mPrimBuffer.unlock(); 836 prim->numPrimitives = mTriCount; 837} 838 839void TerrCell::_updateMaterials() 840{ 841 PROFILE_SCOPE( TerrCell_UpdateMaterials ); 842 843 // This should really only be called for cells of smMinCellSize, 844 // in which case stepSize is always one. 845 U32 stepSize = mSize / smMinCellSize; 846 mMaterials = 0; 847 U8 index; 848 U32 x, y; 849 850 const TerrainFile *file = mTerrain->getFile(); 851 852 // Step thru the samples in the map then. 853 for ( y = 0; y < smVBStride; y++ ) 854 { 855 for ( x = 0; x < smVBStride; x++ ) 856 { 857 index = file->getLayerIndex( mPoint.x + x * stepSize, 858 mPoint.y + y * stepSize ); 859 860 // Skip empty layers and anything that doesn't fit 861 // the 64bit material flags. 862 if ( index == U8_MAX || index > 63 ) 863 continue; 864 865 mMaterials |= (U64)((U64)1<<index); 866 } 867 } 868 869 if ( mMaterial ) 870 mMaterial->init( mTerrain, mMaterials ); 871} 872 873void TerrCell::_updateBounds() 874{ 875 PROFILE_SCOPE( TerrCell_UpdateBounds ); 876 877 const F32 squareSize = mTerrain->getSquareSize(); 878 879 // This should really only be called for cells of smMinCellSize, 880 // in which case stepSize is always one. 881 const U32 stepSize = mSize / smMinCellSize; 882 883 // Prepare to expand the bounds. 884 mBounds.minExtents.set( F32_MAX, F32_MAX, F32_MAX ); 885 mBounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX ); 886 887 Point3F vert; 888 Point2F texCoord; 889 890 const TerrainFile *file = mTerrain->getFile(); 891 892 for ( U32 y = 0; y < smVBStride; y++ ) 893 { 894 for ( U32 x = 0; x < smVBStride; x++ ) 895 { 896 // Setup this point. 897 vert.x = (F32)( mPoint.x + x * stepSize ) * squareSize; 898 vert.y = (F32)( mPoint.y + y * stepSize ) * squareSize; 899 vert.z = fixedToFloat( file->getHeight( mPoint.x + x, 900 mPoint.y + y ) ); 901 902 // HACK: Call it twice to deal with the inverted 903 // inital bounds state... shouldn't be a perf issue. 904 mBounds.extend( vert ); 905 mBounds.extend( vert ); 906 } 907 } 908 909 mRadius = mBounds.len() * 0.5; 910 911 _updateOBB(); 912} 913 914void TerrCell::_updateOBB() 915{ 916 mOBB.set( mTerrain->getTransform(), mBounds ); 917} 918 919void TerrCell::updateOBBs() 920{ 921 _updateOBB(); 922 923 // Update children. 924 925 if( mChildren[ 0 ] ) 926 for( U32 i = 0; i < 4; ++ i ) 927 mChildren[ i ]->updateOBBs(); 928} 929 930void TerrCell::updateZoning( const SceneZoneSpaceManager *zoneManager ) 931{ 932 PROFILE_SCOPE( TerrCell_UpdateZoning ); 933 934 mZoneOverlap.setSize( zoneManager->getNumZones() ); 935 mZoneOverlap.clear(); 936 mIsInteriorOnly = true; 937 938 if ( mChildren[0] == NULL ) 939 { 940 Box3F worldBounds( mBounds ); 941 mTerrain->getTransform().mul( worldBounds ); 942 943 Vector<U32> zones; 944 zoneManager->findZones( worldBounds, zones ); 945 946 for ( U32 i=0; i < zones.size(); i++ ) 947 { 948 // Set overlap bit for zone except it's the outdoor zone. 949 if( zones[ i ] != SceneZoneSpaceManager::RootZoneId ) 950 mZoneOverlap.set( zones[i] ); 951 else 952 mIsInteriorOnly = false; 953 } 954 955 return; 956 } 957 958 for ( U32 i = 0; i < 4; i++ ) 959 { 960 TerrCell *cell = mChildren[i]; 961 cell->updateZoning( zoneManager ); 962 mZoneOverlap.combineOR( cell->getZoneOverlap() ); 963 mIsInteriorOnly &= cell->mIsInteriorOnly; 964 } 965} 966 967void TerrCell::cullCells( const SceneRenderState *state, 968 const Point3F &objLodPos, 969 Vector<TerrCell*> *outCells ) 970{ 971 // If we have a VB and no children then just add 972 // ourselves to the results and return. 973 if ( mVertexBuffer.isValid() && !mChildren[0] ) 974 { 975 outCells->push_back( this ); 976 return; 977 } 978 979 const F32 screenError = mTerrain->getScreenError(); 980 const BitVector &zoneState = state->getCullingState().getZoneVisibilityFlags(); 981 982 for ( U32 i = 0; i < 4; i++ ) 983 { 984 TerrCell *cell = mChildren[i]; 985 986 // Test cell visibility for interior zones. 987 988 const bool visibleInside = !cell->getZoneOverlap().empty() ? zoneState.testAny( cell->getZoneOverlap() ) : false; 989 990 // Test cell visibility for outdoor zone, but only 991 // if we need to. 992 993 bool visibleOutside = false; 994 if( !mIsInteriorOnly && !visibleInside ) 995 { 996 U32 outdoorZone = SceneZoneSpaceManager::RootZoneId; 997 visibleOutside = !state->getCullingState().isCulled( cell->mOBB, &outdoorZone, 1 ); 998 } 999 1000 // Skip cell if neither visible indoors nor outdoors. 1001 1002 if( !visibleInside && !visibleOutside ) 1003 continue; 1004 1005 // Lod based on screen error... 1006 // If far enough, just add this child cells vb ( skipping its children ). 1007 F32 dist = cell->getDistanceTo( objLodPos ); 1008 F32 errorMeters = ( cell->mSize / smMinCellSize ) * mTerrain->getSquareSize(); 1009 U32 errorPixels = mCeil( state->projectRadius( dist, errorMeters ) ); 1010 1011 if ( errorPixels < screenError ) 1012 { 1013 if ( cell->mVertexBuffer.isValid() ) 1014 outCells->push_back( cell ); 1015 } 1016 else 1017 cell->cullCells( state, objLodPos, outCells ); 1018 } 1019} 1020 1021void TerrCell::getRenderPrimitive( GFXPrimitive *prim, 1022 GFXVertexBufferHandleBase *vertBuff, 1023 GFXPrimitiveBufferHandle *primBuff ) const 1024{ 1025 *vertBuff = mVertexBuffer; 1026 1027 // Only supply a primitive buffer if we're using our own 1028 // due to empty squares. 1029 bool useStaticPrimBuffer = true; 1030 if ( mPrimBuffer.isValid() ) 1031 { 1032 useStaticPrimBuffer = false; 1033 *primBuff = mPrimBuffer; 1034 } 1035 1036 prim->type = GFXTriangleList; 1037 prim->startVertex = 0; 1038 prim->minIndex = 0; 1039 prim->startIndex = 0; 1040 prim->numVertices = smVBSize; 1041 1042 if ( useStaticPrimBuffer ) 1043 { 1044 // Use the standard primitive buffer count 1045 prim->numPrimitives = smTriCount; 1046 } 1047 else 1048 { 1049 // Use our triangle count that matches out primitive buffer 1050 prim->numPrimitives = mTriCount; 1051 } 1052} 1053 1054void TerrCell::renderBounds() const 1055{ 1056 LinearColorF color; 1057 color.interpolate( ColorI::RED, ColorI::GREEN, (F32)mLevel / 3.0f ); 1058 1059 GFXStateBlockDesc desc; 1060 desc.setZReadWrite( true, false ); 1061 desc.fillMode = GFXFillWireframe; 1062 1063 GFX->getDrawUtil()->drawCube( desc, mBounds, color.toColorI()); 1064} 1065 1066void TerrCell::preloadMaterials() 1067{ 1068 PROFILE_SCOPE( TerrCell_PreloadMaterials ); 1069 1070 // If we have a VB then we need a material. 1071 if ( mVertexBuffer.isValid() ) 1072 { 1073 TerrainCellMaterial *material = getMaterial(); 1074 material->getReflectMat(); 1075 1076 if ( GFX->getPixelShaderVersion() > 2.0f && 1077 String::compare( LIGHTMGR->getId(), "BLM" ) != 0) 1078 material->getDeferredMat(); 1079 } 1080 1081 for ( U32 i = 0; i < 4; i++ ) 1082 if ( mChildren[i] ) 1083 mChildren[i]->preloadMaterials(); 1084} 1085 1086TerrainCellMaterial* TerrCell::getMaterial() 1087{ 1088 if ( !mMaterial ) 1089 { 1090 mMaterial = new TerrainCellMaterial; 1091 mMaterial->init( mTerrain, mMaterials ); 1092 } 1093 1094 return mMaterial; 1095} 1096 1097void TerrCell::deleteMaterials() 1098{ 1099 SAFE_DELETE( mMaterial ); 1100 1101 for ( U32 i = 0; i < 4; i++ ) 1102 if ( mChildren[i] ) 1103 mChildren[i]->deleteMaterials(); 1104} 1105 1106const Point3F* TerrCell::getZodiacVertexBuffer() 1107{ 1108 if (!zode_vertexBuffer) 1109 createZodiacVertexBuffer(); 1110 return zode_vertexBuffer; 1111} 1112 1113void TerrCell::createZodiacPrimBuffer(U16** zode_primBuffer) 1114{ 1115 if (*zode_primBuffer != 0) 1116 delete [] *zode_primBuffer; 1117 1118 *zode_primBuffer = new U16[TerrCell::smMinCellSize*TerrCell::smMinCellSize*6]; 1119 1120 // Lock and fill it up! 1121 U16* idxBuff = *zode_primBuffer; 1122 U32 counter = 0; 1123 U32 maxIndex = 0; 1124 1125 for ( U32 y = 0; y < smMinCellSize; y++ ) 1126 { 1127 const U32 yTess = y % 2; 1128 1129 for ( U32 x = 0; x < smMinCellSize; x++ ) 1130 { 1131 U32 index = ( y * smVBStride ) + x; 1132 1133 const U32 xTess = x % 2; 1134 1135 if ( ( xTess == 0 && yTess == 0 ) || 1136 ( xTess != 0 && yTess != 0 ) ) 1137 { 1138 idxBuff[0] = index + 0; 1139 idxBuff[1] = index + smVBStride; 1140 idxBuff[2] = index + smVBStride + 1; 1141 1142 idxBuff[3] = index + 0; 1143 idxBuff[4] = index + smVBStride + 1; 1144 idxBuff[5] = index + 1; 1145 } 1146 else 1147 { 1148 idxBuff[0] = index + 1; 1149 idxBuff[1] = index; 1150 idxBuff[2] = index + smVBStride; 1151 1152 idxBuff[3] = index + 1; 1153 idxBuff[4] = index + smVBStride; 1154 idxBuff[5] = index + smVBStride + 1; 1155 } 1156 1157 idxBuff += 6; 1158 maxIndex = index + 1 + smVBStride; 1159 counter += 6; 1160 } 1161 } 1162} 1163 1164void TerrCell::createZodiacVertexBuffer() 1165{ 1166 const F32 squareSize = mTerrain->getSquareSize(); 1167 const U32 blockSize = mTerrain->getBlockSize(); 1168 const U32 stepSize = mSize / smMinCellSize; 1169 1170 if (zode_vertexBuffer) 1171 delete [] zode_vertexBuffer; 1172 1173 zode_vertexBuffer = new Point3F[smVBStride*smVBStride]; 1174 1175 Point3F* vert = zode_vertexBuffer; 1176 1177 Point2I gridPt; 1178 Point2F point; 1179 F32 height; 1180 1181 const TerrainFile *file = mTerrain->getFile(); 1182 1183 for ( U32 y = 0; y < smVBStride; y++ ) 1184 { 1185 for ( U32 x = 0; x < smVBStride; x++ ) 1186 { 1187 // We clamp here to keep the geometry from reading across 1188 // one side of the height map to the other causing walls 1189 // around the edges of the terrain. 1190 gridPt.x = mClamp( mPoint.x + x * stepSize, 0, blockSize - 1 ); 1191 gridPt.y = mClamp( mPoint.y + y * stepSize, 0, blockSize - 1 ); 1192 1193 // Setup this point. 1194 point.x = (F32)gridPt.x * squareSize; 1195 point.y = (F32)gridPt.y * squareSize; 1196 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); 1197 1198 vert->x = point.x; 1199 vert->y = point.y; 1200 vert->z = height; 1201 1202 ++vert; 1203 } 1204 } 1205} 1206 1207void TerrCell::deleteZodiacVertexBuffer() 1208{ 1209 if (zode_vertexBuffer) 1210 { 1211 delete [] zode_vertexBuffer; 1212 zode_vertexBuffer = 0; 1213 } 1214} 1215 1216