waterBlock.cpp
Engine/source/environment/waterBlock.cpp
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 ¶mHandles) 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