waterBlock.cpp

Engine/source/environment/waterBlock.cpp

More...

Public Functions

ConsoleDocClass(WaterBlock , "@brief A block shaped water volume defined by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D scale and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">orientation.\n\n</a>" "@see <a href="/coding/class/classwaterobject/">WaterObject</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> inherited <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">functionality.\n\n</a>" "@ingroup Water" )

Detailed Description

Public Functions

ConsoleDocClass(WaterBlock , "@brief A block shaped water volume defined by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D scale and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">orientation.\n\n</a>" "@see <a href="/coding/class/classwaterobject/">WaterObject</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> inherited <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">functionality.\n\n</a>" "@ingroup Water" )

IMPLEMENT_CO_NETOBJECT_V1(WaterBlock )

  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#include "platform/platform.h"
 25#include "environment/waterBlock.h"
 26
 27#include "core/util/safeDelete.h"
 28#include "scene/sceneRenderState.h"
 29#include "scene/sceneManager.h"
 30#include "lighting/lightInfo.h"
 31#include "core/stream/bitStream.h"
 32#include "math/mathIO.h"
 33#include "console/consoleTypes.h"
 34#include "gui/3d/guiTSControl.h"
 35#include "gfx/primBuilder.h"
 36#include "gfx/gfxTransformSaver.h"
 37#include "gfx/gfxDebugEvent.h"
 38#include "gfx/gfxOcclusionQuery.h"
 39#include "renderInstance/renderPassManager.h"
 40#include "sim/netConnection.h"   
 41#include "scene/reflectionManager.h"
 42#include "ts/tsShapeInstance.h"
 43#include "postFx/postEffect.h"
 44#include "math/util/matrixSet.h"
 45
 46IMPLEMENT_CO_NETOBJECT_V1(WaterBlock);
 47
 48ConsoleDocClass( WaterBlock,
 49   "@brief A block shaped water volume defined by a 3D scale and orientation.\n\n"      
 50
 51   "@see WaterObject for inherited functionality.\n\n"
 52   
 53   "@ingroup Water"
 54);
 55
 56WaterBlock::WaterBlock()
 57{
 58   mGridElementSize = 5.0f;
 59   mObjScale.set( 100.0f, 100.0f, 10.0f );
 60   mWidth = 2;
 61   mHeight = 2;
 62   mNetFlags.set(Ghostable | ScopeAlways);
 63
 64   mObjBox.minExtents.set( -0.5f, -0.5f, -0.5f );
 65   mObjBox.maxExtents.set(  0.5f,  0.5f,  0.5f );
 66
 67   mElapsedTime = 0.0f;   
 68   mGenerateVB = true;
 69}
 70
 71WaterBlock::~WaterBlock()
 72{
 73}
 74
 75bool WaterBlock::onAdd()
 76{
 77   if ( !Parent::onAdd() )
 78      return false;
 79   
 80   resetWorldBox();
 81   addToScene();
 82
 83   return true;
 84}
 85
 86void WaterBlock::onRemove()
 87{
 88   clearVertBuffers();
 89
 90   removeFromScene();
 91
 92   Parent::onRemove();
 93}
 94
 95//-----------------------------------------------------------------------------
 96// packUpdate
 97//-----------------------------------------------------------------------------
 98U32 WaterBlock::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
 99{
100   U32 retMask = Parent::packUpdate(con, mask, stream);
101
102   stream->write( mGridElementSize );
103
104   if ( stream->writeFlag( mask & UpdateMask ) )
105   {
106      // This is set to allow the user to modify the size of the water dynamically
107      // in the editor
108      mathWrite( *stream, mObjScale );
109      stream->writeAffineTransform( mObjToWorld );      
110   }
111
112   return retMask;
113}
114
115//-----------------------------------------------------------------------------
116// unpackUpdate
117//-----------------------------------------------------------------------------
118void WaterBlock::unpackUpdate(NetConnection* con, BitStream* stream)
119{
120   Parent::unpackUpdate(con, stream);
121
122   F32 gridSize = mGridElementSize;
123   stream->read( &mGridElementSize );
124   if ( gridSize != mGridElementSize )
125      mGenerateVB = true;
126
127   if( stream->readFlag() ) // UpdateMask
128   {
129      Point3F scale;
130      mathRead( *stream, &scale );
131            
132      setScale( scale );
133
134      MatrixF objToWorld;
135      stream->readAffineTransform( &objToWorld );      
136
137      setTransform( objToWorld );
138   } 
139}
140
141//-----------------------------------------------------------------------------
142// Setup vertex and index buffers
143//-----------------------------------------------------------------------------
144void WaterBlock::setupVBIB()
145{
146   clearVertBuffers();
147
148   const U32 maxIndexedVerts = 65536; // max number of indexed verts with U16 size indices
149
150   if( mObjScale.x < mGridElementSize ||
151       mObjScale.y < mGridElementSize )
152   {
153      F32 oldGridSize = mGridElementSize;
154      mGridElementSize = getMin(mObjScale.x, mObjScale.y);
155      logWarning("gridElementSize %g is larger than scale (%g, %g), clamping gridElementSize to %g",
156         oldGridSize, mObjScale.x, mObjScale.y, mGridElementSize);
157   }
158
159   Point3F div = getScale() / mGridElementSize;
160   // Add one to width and height for the edge.
161   mWidth   = (U32)mCeil(div.x) + 1;
162   mHeight  = (U32)mCeil(div.y) + 1;
163   
164   if( mWidth > maxIndexedVerts / 2 )
165      mWidth = maxIndexedVerts / 2;
166
167   // figure out how many blocks are needed and their size
168   U32 maxBlockRows = maxIndexedVerts / mWidth;
169   U32 rowOffset = 0;
170   
171   while( (rowOffset+1) < mHeight )
172   {
173      U32 numRows = mHeight - rowOffset;
174      if( numRows == 1 ) numRows++;
175      if( numRows > maxBlockRows )
176      {
177         numRows = maxBlockRows;
178      }
179
180      setupVertexBlock( mWidth, numRows, rowOffset );
181      setupPrimitiveBlock( mWidth, numRows );
182
183      rowOffset += numRows - 1;
184   }
185
186}
187
188//-----------------------------------------------------------------------------
189// Set up a block of vertices - the width is always the width of the entire
190// waterBlock, so this is a block of full rows.
191//-----------------------------------------------------------------------------
192void WaterBlock::setupVertexBlock( U32 width, U32 height, U32 rowOffset )
193{
194   RayInfo rInfo;
195   VectorF sunVector(-0.61f, 0.354f, 0.707f);
196
197   if ( LIGHTMGR )
198   {
199      LightInfo* linfo = LIGHTMGR->getSpecialLight( LightManager::slSunLightType );
200      if ( linfo )
201         sunVector = linfo->getDirection();
202   }
203
204   sunVector.normalize();
205
206   U32 numVerts = width * height;
207
208   GFXWaterVertex *verts = new GFXWaterVertex[ numVerts ];
209
210   U32 index = 0;
211   for( U32 i=0; i<height; i++ )
212   {
213      for( U32 j=0; j<width; j++, index++ )
214      {
215         F32 vertX = getMin((-mObjScale.x / 2.0f) + mGridElementSize * j, mObjScale.x / 2.0f);
216         F32 vertY = getMin((-mObjScale.y / 2.0f) + mGridElementSize * (i + rowOffset), mObjScale.y / 2.0f);
217         GFXWaterVertex *vert = &verts[index];
218         vert->point.x = vertX;
219         vert->point.y = vertY;
220         vert->point.z = 0.0;
221         vert->normal.set(0,0,1);
222         vert->undulateData.set( vertX, vertY );
223         vert->horizonFactor.set( 0, 0, 0, 0 );
224
225         // Calculate the water depth
226
227         /*
228         vert->depthData.set( 0.0f, 0.0f );
229
230         Point3F start, end;
231         Point3F worldPoint = vert->point + pos;
232
233         start.x = end.x = worldPoint.x;
234         start.y = end.y = worldPoint.y;
235         start.z = -2000; // Really high, might be over kill
236         end.z = 2000; // really low, might be overkill
237
238         // Cast a ray to see how deep the water is. We are
239         // currently just testing for terrain and atlas
240         // objects, but potentially any object that responds
241         // to a ray cast could detected.
242         if(gClientContainer.castRay(start, end, 
243            //StaticObjectType | 
244            //InteriorObjectType | 
245            //ShapeBaseObjectType | 
246            //StaticShapeObjectType | 
247            //ItemObjectType |
248            //StaticTSObjectType |
249            TerrainObjectType
250            , &rInfo))
251         {
252            F32 depth = -(rInfo.point.z - pos.z);
253
254            if(depth <= 0.0f)
255            {
256               depth = 1.0f;
257            }
258            else
259            {
260               depth = depth / mVisibilityDepth;
261               if(depth > 1.0f)
262               {
263                  depth = 1.0f;
264               }
265
266               depth = 1.0f - depth;
267            }
268
269            vert->depthData.x = depth;
270         }
271         else
272         {
273            vert->depthData.x = 0.0f;
274         }
275
276         // Cast a ray to do some AO-style shadowing.
277         F32 &shadow = vert->depthData.y;
278
279         if(gClientContainer.castRay(worldPoint, worldPoint + sunVector * 9000.f, 
280            //StaticObjectType | 
281            //InteriorObjectType | 
282            //ShapeBaseObjectType | 
283            //StaticShapeObjectType | 
284            //ItemObjectType |
285            //StaticTSObjectType |
286            TerrainObjectType
287            , &rInfo))
288         {
289            shadow = 0.f;
290         }
291         else
292         {
293            shadow = 1.f;
294         }
295         */
296      }
297   }
298
299   // copy to vertex buffer
300   GFXVertexBufferHandle <GFXWaterVertex> * vertBuff = new GFXVertexBufferHandle <GFXWaterVertex>;
301
302   vertBuff->set( GFX, numVerts, GFXBufferTypeStatic );
303   GFXWaterVertex *vbVerts = vertBuff->lock();
304   dMemcpy( vbVerts, verts, sizeof(GFXWaterVertex) * numVerts );
305   vertBuff->unlock();
306   mVertBuffList.push_back( vertBuff );
307   
308
309   delete [] verts;
310
311}
312
313//-----------------------------------------------------------------------------
314// Set up a block of indices to match the block of vertices. The width is 
315// always the width of the entire waterBlock, so this is a block of full rows.
316//-----------------------------------------------------------------------------
317void WaterBlock::setupPrimitiveBlock( U32 width, U32 height )
318{
319   AssertFatal( height > 1, "WaterBlock::setupPrimitiveBlock() - invalid height" );
320   
321   // setup vertex / primitive buffers
322   U32 numIndices = (width-1) * (height-1) * 6;
323   U16 *indices = new U16[ numIndices ];
324   U32 numVerts = width * height;
325
326   // This uses indexed triangle lists instead of strips, but it shouldn't be
327   // significantly slower if the indices cache well.
328   
329   // Rough diagram of the index order
330   //   0----2----+ ...
331   //   |  / |    |
332   //   |/   |    |
333   //   1----3----+ ...
334   //   |    |    |
335   //   |    |    |
336   //   +----+----+ ...
337
338   U32 index = 0;
339   for( U32 i=0; i<(height-1); i++ )
340   {
341      for( U32 j=0; j<(width-1); j++, index+=6 )
342      {
343         // Process one quad at a time.  Note it will re-use the same indices from
344         // previous quad, thus optimizing vert cache.  Cache will run out at
345         // end of each row with this implementation however.
346         indices[index+0] = (i) * mWidth + j;         // 0
347         indices[index+1] = (i+1) * mWidth + j;       // 1
348         indices[index+2] =  i * mWidth + j+1;        // 2
349         indices[index+3] = (i+1) * mWidth + j;       // 1
350         indices[index+4] = (i+1) * mWidth + j+1;     // 3
351         indices[index+5] =  i * mWidth + j+1;        // 2
352      }
353
354   }
355
356   GFXPrimitiveBufferHandle *indexBuff = new GFXPrimitiveBufferHandle;
357   
358   GFXPrimitive pInfo;
359   pInfo.type = GFXTriangleList;
360   pInfo.numPrimitives = numIndices / 3;
361   pInfo.startIndex = 0;
362   pInfo.minIndex = 0;
363   pInfo.numVertices = numVerts;
364
365   U16 *ibIndices;
366   GFXPrimitive *piInput;
367   indexBuff->set( GFX, numIndices, 1, GFXBufferTypeStatic );
368   indexBuff->lock( &ibIndices, &piInput );
369   dMemcpy( ibIndices, indices, numIndices * sizeof(U16) );
370   dMemcpy( piInput, &pInfo, sizeof(GFXPrimitive) );
371   indexBuff->unlock();
372   mPrimBuffList.push_back( indexBuff );
373
374
375   delete [] indices;
376}
377
378//------------------------------------------------------------------------------
379// Setup scenegraph data structure for materials
380//------------------------------------------------------------------------------
381SceneData WaterBlock::setupSceneGraphInfo( SceneRenderState *state )
382{
383   SceneData sgData;
384
385   sgData.lights[0] = LIGHTMGR->getSpecialLight( LightManager::slSunLightType );
386
387   // fill in water's transform
388   sgData.objTrans = &getRenderTransform();
389
390   // fog
391   sgData.setFogParams( state->getSceneManager()->getFogData() );
392
393   // misc
394   sgData.backBuffTex = REFLECTMGR->getRefractTex();
395   sgData.reflectTex = mPlaneReflector.reflectTex;
396   sgData.wireframe = GFXDevice::getWireframe() || smWireframe;
397
398   return sgData;
399}
400
401//-----------------------------------------------------------------------------
402// set shader parameters
403//-----------------------------------------------------------------------------
404void WaterBlock::setShaderParams( SceneRenderState *state, BaseMatInstance *mat, const WaterMatParams &paramHandles)
405{
406   // Set variables that will be assigned to shader consts within WaterCommon
407   // before calling Parent::setShaderParams
408
409   mUndulateMaxDist = F32_MAX;
410
411   Parent::setShaderParams( state, mat, paramHandles );   
412
413   // Now set the rest of the shader consts that are either unique to this
414   // class or that WaterObject leaves to us to handle...
415
416   MaterialParameters* matParams = mat->getMaterialParameters();
417   
418   // set vertex shader constants
419   //-----------------------------------   
420   
421   MatrixF modelMat( getRenderTransform() );
422   if ( paramHandles.mModelMatSC->isValid() )
423      matParams->set(paramHandles.mModelMatSC, modelMat, GFXSCT_Float4x4);
424   matParams->setSafe(paramHandles.mGridElementSizeSC, (F32)mGridElementSize);
425
426   // set pixel shader constants
427   //-----------------------------------
428
429   LinearColorF c( mWaterFogData.color );
430   matParams->setSafe( paramHandles.mBaseColorSC, c );
431      
432   // By default we need to show a true reflection is fullReflect is enabled and
433   // we are above water.
434   F32 reflect = mPlaneReflector.isEnabled() && !isUnderwater( state->getCameraPosition() );
435   
436   // If we were occluded the last frame a query was fetched ( not necessarily last frame )
437   // and we weren't updated last frame... we don't have a valid texture to show
438   // so use the cubemap / fake reflection color this frame.
439   if ( mPlaneReflector.lastUpdateMs != REFLECTMGR->getLastUpdateMs() && mPlaneReflector.isOccluded() )
440      reflect = false;
441
442   Point4F reflectParams( mWaterPos.z, 0.0f, 1000.0f, !reflect );   
443   matParams->setSafe( paramHandles.mReflectParamsSC, reflectParams );
444
445   VectorF reflectNorm = mReflectNormalUp ? VectorF(0,0,1) : static_cast<VectorF>(mPlaneReflector.refplane);
446   matParams->setSafe(paramHandles.mReflectNormalSC, reflectNorm ); 
447}
448
449void WaterBlock::innerRender( SceneRenderState *state )
450{      
451   GFXDEBUGEVENT_SCOPE( WaterBlock_innerRender, ColorI( 255, 0, 0 ) );
452
453   if ( mGenerateVB )
454   {
455      setupVBIB();
456      mGenerateVB = false;
457   }
458
459   // Setup SceneData
460   SceneData sgData = setupSceneGraphInfo( state );
461   const Point3F &camPosition = state->getCameraPosition();
462
463   // set the material
464
465   S32 matIdx = getMaterialIndex( camPosition );
466
467   if ( !initMaterial( matIdx ) )
468      return;
469
470   BaseMatInstance *mat = mMatInstances[matIdx];
471   WaterMatParams matParams = mMatParamHandles[matIdx];
472
473   // render the geometry
474   if ( mat )
475   {
476      // setup proj/world transform
477      mMatrixSet->setWorld(getRenderTransform());
478      mMatrixSet->restoreSceneViewProjection();
479
480      setShaderParams( state, mat, matParams );
481
482      while ( mat->setupPass( state, sgData ) )
483      {      
484         mat->setSceneInfo(state, sgData);
485         mat->setTransforms(*mMatrixSet, state);
486         setCustomTextures( matIdx, mat->getCurPass(), matParams );
487
488         for ( U32 i = 0; i < mVertBuffList.size(); i++ )
489         {
490            GFX->setVertexBuffer( *mVertBuffList[i] );
491            GFXPrimitiveBuffer *primBuff = *mPrimBuffList[i];
492            GFX->setPrimitiveBuffer( primBuff );
493            GFX->drawPrimitives();
494         }
495      }
496   }   
497}
498
499bool WaterBlock::setGridSizeProperty( void *obj, const char *index, const char *data )
500{
501   WaterBlock* object = static_cast<WaterBlock*>(obj);
502   F32 gridSize = dAtof(data);
503   
504   Point3F scale = object->getScale();
505   
506   if(gridSize < 0.001f)
507   {
508      object->logWarning("gridSize cannot be <= 0, clamping to scale");
509      gridSize = getMin(scale.x, scale.y);
510   }
511   
512   if(gridSize > scale.x || gridSize > scale.y)
513   {
514      object->logWarning("gridSize cannot be > scale.  Your scale is (%g, %g) and your gridsize is %g", 
515                 scale.x, scale.y, gridSize);
516      gridSize = getMin(scale.x, scale.y);
517   }
518   
519   object->mGridElementSize = gridSize;
520               
521   // This is a hack so the console system doesn't go in and set our variable
522   // again, after we've already set it (possibly with a different value...)
523   return false;
524}
525
526//-----------------------------------------------------------------------------
527// initPersistFields
528//-----------------------------------------------------------------------------
529void WaterBlock::initPersistFields()
530{
531   addGroup( "WaterBlock" );
532      addProtectedField( "gridElementSize", TypeF32,  Offset( mGridElementSize, WaterBlock ), 
533         &setGridSizeProperty, &defaultProtectedGetFn, "Spacing between vertices in the WaterBlock mesh" );
534      addProtectedField( "gridSize", TypeF32,  Offset( mGridElementSize, WaterBlock ), 
535         &setGridSizeProperty, &defaultProtectedGetFn, "Duplicate of gridElementSize for backwards compatility" );
536   endGroup( "WaterBlock" );
537
538   Parent::initPersistFields();
539}     
540
541bool WaterBlock::isUnderwater( const Point3F &pnt ) const
542{
543   // Transform point into object space so we can test if it is within
544   // the WaterBlock's object box, include rotation/scale.
545
546   Point3F objPnt = pnt;
547   mWorldToObj.mulP( pnt, &objPnt );
548
549   objPnt.z -= 0.1f;
550         
551   Box3F testBox = mObjBox;
552   testBox.scale( mObjScale );     
553
554   // We already tested if below the surface plane,
555   // so clamping the z height of the box is not really necessary.
556   testBox.maxExtents.z = testBox.getCenter().z;      
557
558   if ( testBox.isContained( objPnt ) )
559      return true;
560
561   return false;
562}
563
564void WaterBlock::clearVertBuffers()
565{
566   for( U32 i=0; i<mVertBuffList.size(); i++ )
567      delete mVertBuffList[i];
568   mVertBuffList.clear();
569
570   for( U32 i=0; i<mPrimBuffList.size(); i++ )
571      delete mPrimBuffList[i];
572   mPrimBuffList.clear();
573}
574
575void WaterBlock::inspectPostApply()
576{
577   Parent::inspectPostApply();
578
579   VectorF scale = getScale();
580   if( scale.x < mGridElementSize )
581      scale.x = mGridElementSize;
582   if( scale.y < mGridElementSize )
583      scale.y = mGridElementSize;
584   
585   if( scale != getScale() )
586      setScale( scale );
587
588   setMaskBits( UpdateMask );
589}
590
591void WaterBlock::setTransform( const MatrixF &mat )
592{
593   // If our transform changes we need to recalculate the 
594   // per vertex depth/shadow info.  Would be nice if this could
595   // be done independently of generating the whole VBIB...   
596   Parent::setTransform( mat );
597
598   // We don't need to regen our vb anymore since we aren't calculating
599   // per vert depth/shadow on the cpu anymore.
600   //if ( oldMat != mObjToWorld )   
601   //   mGenerateVB = true;   
602
603   // Keep mWaterPlane up to date.
604   mWaterFogData.plane.set( 0, 0, 1, -getPosition().z ); 
605}
606
607void WaterBlock::setScale( const Point3F &scale )
608{
609   Point3F oldScale = mObjScale;
610   
611   Parent::setScale( scale );
612   
613   if ( oldScale != mObjScale )
614      mGenerateVB = true;
615}
616
617void WaterBlock::onStaticModified( const char* slotName, const char*newValue )
618{
619   Parent::onStaticModified( slotName, newValue );
620
621   if ( dStricmp( slotName, "surfMaterial" ) == 0 )
622      setMaskBits( MaterialMask );
623   if ( dStricmp( slotName, "gridElementSize" ) == 0 )
624   {
625      mGenerateVB = true;
626      setMaskBits( UpdateMask );
627   }
628}
629
630bool WaterBlock::castRay( const Point3F &start, const Point3F &end, RayInfo *info )
631{
632   // Simply look for the hit on the water plane
633   // and ignore any future issues with waves, etc.
634   const Point3F norm(0,0,1);
635   PlaneF plane( Point3F::Zero, norm );
636
637   F32 hit = plane.intersect( start, end );
638   if ( hit < 0.0f || hit > 1.0f )
639      return false;
640   
641   info->t = hit;
642   info->object = this;
643   info->point = start + ( ( end - start ) * hit );
644   info->normal = norm;
645   info->material = mMatInstances[WaterMat];
646
647   return mObjBox.isContained(info->point);
648}
649
650bool WaterBlock::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& )
651{
652   if(context == PLC_Navigation && box.isOverlapped(mWorldBox))
653   {
654      polyList->setObject( this );
655      MatrixF mat(true);
656      Point3F pos = getPosition();
657      pos.x = pos.y = 0;
658      mat.setPosition(pos);
659      polyList->setTransform( &mat, Point3F(1, 1, 1) );
660
661      Box3F ov = box.getOverlap(mWorldBox);
662      Point3F
663         p0(ov.minExtents.x, ov.maxExtents.y, 0),
664         p1(ov.maxExtents.x, ov.maxExtents.y, 0),
665         p2(ov.maxExtents.x, ov.minExtents.y, 0),
666         p3(ov.minExtents.x, ov.minExtents.y, 0);
667
668      // Add vertices to poly list.
669      U32 v0 = polyList->addPoint(p0);
670      polyList->addPoint(p1);
671      polyList->addPoint(p2);
672      polyList->addPoint(p3);
673
674      // Add plane between first three vertices.
675      polyList->begin(0, 0);
676      polyList->vertex(v0);
677      polyList->vertex(v0+1);
678      polyList->vertex(v0+2);
679      polyList->plane(v0, v0+1, v0+2);
680      polyList->end();
681
682      // Add plane between last three vertices.
683      polyList->begin(0, 1);
684      polyList->vertex(v0+2);
685      polyList->vertex(v0+3);
686      polyList->vertex(v0);
687      polyList->plane(v0+2, v0+3, v0);
688      polyList->end();
689
690      return true;
691   }
692
693   return false;
694}
695
696F32 WaterBlock::getWaterCoverage( const Box3F &testBox ) const
697{
698   Box3F wbox = getWorldBox();
699   wbox.maxExtents.z = wbox.getCenter().z;
700   
701   F32 coverage = 0.0f;
702
703   if ( wbox.isOverlapped(testBox) ) 
704   {
705      if (wbox.maxExtents.z < testBox.maxExtents.z)
706         coverage = (wbox.maxExtents.z - testBox.minExtents.z) / (testBox.maxExtents.z - testBox.minExtents.z);
707      else
708         coverage = 1.0f;
709   }
710
711   return coverage;
712}
713
714F32 WaterBlock::getSurfaceHeight( const Point2F &pos ) const
715{
716   if ( !mWorldBox.isContained( pos ) )
717      return -1.0f;
718
719   return getPosition().z;   
720}
721
722void WaterBlock::_getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos )
723{
724   outPos = getPosition();
725   if ( mReflectNormalUp )
726      outPlane.set( outPos, Point3F(0,0,1) );
727   else
728   {
729      Point3F normal;
730      getRenderTransform().getColumn( 2, &normal );
731      outPlane.set( outPos, normal );
732   }
733}
734
735F32 WaterBlock::distanceTo( const Point3F& point ) const
736{
737   Box3F waterBox = getWorldBox();
738   waterBox.maxExtents.z = getPosition().z;
739   
740   return waterBox.getDistanceToPoint( point );
741}
742