terrCell.cpp

Engine/source/terrain/terrCell.cpp

More...

Public Functions

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