//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "platform/platform.h" #include "environment/waterPlane.h" #include "core/util/safeDelete.h" #include "scene/sceneRenderState.h" #include "scene/sceneManager.h" #include "lighting/lightInfo.h" #include "core/stream/bitStream.h" #include "math/mathIO.h" #include "math/mathUtils.h" #include "console/consoleTypes.h" #include "gui/3d/guiTSControl.h" #include "gfx/primBuilder.h" #include "gfx/gfxTransformSaver.h" #include "gfx/gfxDebugEvent.h" #include "gfx/gfxOcclusionQuery.h" #include "renderInstance/renderPassManager.h" #include "sim/netConnection.h" #include "scene/reflectionManager.h" #include "ts/tsShapeInstance.h" #include "T3D/gameFunctions.h" #include "postFx/postEffect.h" #include "math/util/matrixSet.h" extern ColorI gCanvasClearColor; #define BLEND_TEX_SIZE 256 #define V_SHADER_PARAM_OFFSET 50 IMPLEMENT_CO_NETOBJECT_V1(WaterPlane); ConsoleDocClass( WaterPlane, "@brief Represents a large body of water stretching to the horizon in all directions.\n\n" "WaterPlane's position is defined only height, the z element of position, " "it is infinite in xy and depth. %WaterPlane is designed to represent the " "ocean on an island scene and viewed from ground level; other uses may not " "be appropriate and a WaterBlock may be used.\n\n" "@see WaterObject for inherited functionality.\n\n" "Limitations:\n\n" "Because %WaterPlane cannot be projected exactly to the far-clip distance, " "other objects nearing this distance can have noticible artifacts as they " "clip through first the %WaterPlane and then the far plane.\n\n" "To avoid this large objects should be positioned such that they will not line up with " "the far-clip from vantage points the player is expected to be. In particular, " "your TerrainBlock should be completely contained by the far-clip distance.\n\n" "Viewing %WaterPlane from a high altitude with a tight far-clip distance " "will accentuate this limitation. %WaterPlane is primarily designed to " "be viewed from ground level.\n\n" "@ingroup Water" ); WaterPlane::WaterPlane() { mGridElementSize = 1.0f; mGridSize = 101; mGridSizeMinusOne = mGridSize - 1; mNetFlags.set(Ghostable | ScopeAlways); mVertCount = 0; mIndxCount = 0; mPrimCount = 0; } WaterPlane::~WaterPlane() { } bool WaterPlane::onAdd() { if ( !Parent::onAdd() ) return false; setGlobalBounds(); resetWorldBox(); addToScene(); mWaterFogData.plane.set( 0, 0, 1, -getPosition().z ); return true; } void WaterPlane::onRemove() { removeFromScene(); Parent::onRemove(); } void WaterPlane::initPersistFields() { addGroup( "WaterPlane" ); addProtectedField( "gridSize", TypeS32, Offset( mGridSize, WaterPlane ), &protectedSetGridSize, &defaultProtectedGetFn, "Spacing between vertices in the WaterBlock mesh" ); addProtectedField( "gridElementSize", TypeF32, Offset( mGridElementSize, WaterPlane ), &protectedSetGridElementSize, &defaultProtectedGetFn, "Duplicate of gridElementSize for backwards compatility"); endGroup( "WaterPlane" ); Parent::initPersistFields(); removeField( "rotation" ); removeField( "scale" ); } U32 WaterPlane::packUpdate(NetConnection* con, U32 mask, BitStream* stream) { U32 retMask = Parent::packUpdate(con, mask, stream); stream->write( mGridSize ); stream->write( mGridElementSize ); if ( stream->writeFlag( mask & UpdateMask ) ) { stream->write( getPosition().z ); } return retMask; } void WaterPlane::unpackUpdate(NetConnection* con, BitStream* stream) { Parent::unpackUpdate(con, stream); U32 inGridSize; stream->read( &inGridSize ); setGridSize( inGridSize ); F32 inGridElementSize; stream->read( &inGridElementSize ); setGridElementSize( inGridElementSize ); if( stream->readFlag() ) // UpdateMask { F32 posZ; stream->read( &posZ ); Point3F newPos = getPosition(); newPos.z = posZ; setPosition( newPos ); } } void WaterPlane::setupVBIB( SceneRenderState *state ) { const Frustum &frustum = state->getCullingFrustum(); // World-Up vector, assigned as normal for all verts. const Point3F worldUp( 0.0f, 0.0f, 1.0f ); // World-unit size of a grid cell. const F32 squareSize = mGridElementSize; // Column/Row count. // So we don't neet to access class-specific member variables // in the code below. const U32 gridSize = mGridSize; // Number of verts in one column / row const U32 gridStride = gridSize + 1; // Grid is filled in this order... // Ex. Grid with gridSize of 2. // // Letters are cells. // Numbers are verts, enumerated in their order within the vert buffer. // // 6 7 8 // (c) (d) // 3 4 5 // (a) (b) // 0 1 2 // // Note... // Camera would be positioned at vert 4 ( in this particular grid not a constant ). // Positive Y points UP the diagram ( verts 0, 3, 6 ). // Positive X points RIGHT across the diagram ( verts 0, 1, 2 ). // Length of a grid row/column divided by two. F32 gridSideHalfLen = squareSize * gridSize * 0.5f; // Position of the first vertex in the grid. // Relative to the camera this is the "Back Left" corner vert. const Point3F cornerPosition( -gridSideHalfLen, -gridSideHalfLen, 0.0f ); // Number of verts in the grid centered on the camera. const U32 gridVertCount = gridStride * gridStride; // Number of verts surrounding the grid, projected by the frustum. const U32 borderVertCount = gridSize * 4; // Number of verts in the front-most row which are raised to the horizon. const U32 horizonVertCount = gridStride; // Total number of verts. Calculation explained above. mVertCount = gridVertCount + borderVertCount + horizonVertCount; // Fill the vertex buffer... mVertBuff.set( GFX, mVertCount, GFXBufferTypeStatic ); GFXWaterVertex *vertPtr = mVertBuff.lock(); // Fill verts in the camera centered grid... // Temorary storage for calculation of vert position. F32 xVal, yVal; for ( U32 i = 0; i < gridStride; i++ ) { yVal = cornerPosition.y + (F32)( i * squareSize ); for ( U32 j = 0; j < gridStride; j++ ) { xVal = cornerPosition.x + (F32)( j * squareSize ); vertPtr->point.set( xVal, yVal, 0.0f ); vertPtr->normal = worldUp; vertPtr->undulateData.set( xVal, yVal ); vertPtr->horizonFactor.set( 0, 0, 0, 0 ); vertPtr++; } } // Fill in 'border' verts, surrounding the grid, projected by the frustum. // Ex. Grid with gridSize of 2. // // Letters in parenthesis are cells. // x's are grid-verts ( we have already filled ). // Numbers are border verts, enumerated in their order within the vert buffer. // // Lines connecting verts explained in the code below. // // Front // // L 0------1 2 R // e x x x | i // f (c) (d) | g // t 7 x x x 3 h // | (a) (b) t // | x x x // 6 5------4 // // Back // // As in previous diagram... // Camera would be positioned at vert 4 ( in this particular grid not a constant ). // Positive Y points UP the diagram ( verts 6, 7, 0 ). // Positive X points RIGHT across the diagram ( verts 0, 1, 2 ). // Iterator i is looping through the 4 'sides' of the grid. // Inner loop ( using iterator j ) will fill in a number of verts // where that count is 'gridSize'. // // // Ex. Given the grid with gridSize of 2 diagramed above, // Outer loop iterates through: Front, Right, Back, Left // Inner loop fills 2 verts per iteration of the outer loop: { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } // Grid-space vectors indexed by 'side'. // Each vector describes the direction we iterate when // filling in verts ( mathematically the tangent ). const Point2F sBorderTangentVec [4] = { Point2F( 1, 0 ), // Front ( 0 ) Point2F( 0, -1 ), // Right ( 1 ) Point2F( -1, 0 ), // Back ( 2 ) Point2F( 0, 1 ) // Left ( 3 ) }; // Normalized positions indexed by 'side' // Defines the 'start' position of each side, eg. the position of the first vert. // See Diagram below. const Point2F sBorderStartPos [4] = { Point2F( -1, 1 ), // Front ( 0 ) Point2F( 1, 1 ), // Right ( 1 ) Point2F( 1, -1 ), // Back ( 2 ) Point2F( -1, -1 ) // Left ( 3 ) }; // Diagram of Start vert position per Side. // // Labeling convention for verts is 'As' where A is the first letter of // that side's descriptive name and lower-case s indicates 'start'. // // // // Front // (-1,1) // Fs------o-----Rs(1,1)R // | | i // | | g // o (0,0) o h // | | t // | | // L(-1,-1)Ls------o-----Bs // e (1,-1) // f Back // t // Calculate the world-space dimension of the border-vert-ring... // Diagram shows overall layout of the WaterPlane with a gridSize of 1, // with all 'quads' enumerated. // center-grid ( 1 ), border ( 2, 3, 4, 5 ), and horizon ( 6 ). // // // x------------x // \ 6 \ <- horizon quad is really 'above' the front border // x --------- x not in front of it // | \ 2 / | // | x---- x | // | | | | // | 5| 1 |3 | // | x --- x | // | / 4 \| // x------------x // WaterPlane renders relative to the camera rotation around z and xy position. // // That is, it rotates around the z-up axis with the camera such that the // camera is always facing towards the front border unless looking straight // down or up. // // Also note that the horizon verts are pulled straight up from the front // border verts. // // Therefore... // // The front border must be as close to the farclip plane as possible // so distant objects clip through the horizon and farplane at the same time. // // The left and right borders must be pulled outward a distance such // that water extends horizontally across the entire viewable area while // looking straight forward +y or straight down -z. // // const F32 farDistScale = 0.99f; // F32 farDist = frustum.getFarDist() * farDistScale; // F32 farWidth = (F32)state->getViewport().extent.x * farDist / state->getWorldToScreenScale().x; Point2F borderExtents( farWidth * 2.0f, farDist * 2.0f ); Point2F borderHalfExtents( farWidth, farDist ); Point2F borderDir; Point2F borderStart; for ( U32 i = 0; i < 4; i++ ) { borderDir = sBorderTangentVec[i]; borderStart = sBorderStartPos[i]; for ( U32 j = 0; j < gridSize; j++ ) { F32 frac = (F32)j / (F32)gridSize; Point2F pos( borderStart * borderHalfExtents ); pos += borderDir * borderExtents * frac; vertPtr->point.set( pos.x, pos.y, 0.0f ); vertPtr->undulateData.set( pos.x, pos.y ); vertPtr->horizonFactor.set( 0, 0, 0, 0 ); vertPtr->normal = worldUp; vertPtr++; } } // Fill in row of horizion verts. // Verts are positioned identical to the front border, but will be // manipulated in z within the shader. // // Z position of 50.0f is unimportant unless you want to disable // shader manipulation and render in wireframe for debugging. for ( U32 i = 0; i < gridStride; i++ ) { F32 frac = (F32)i / (F32)gridSize; Point2F pos( sBorderStartPos[0] * borderHalfExtents ); pos += sBorderTangentVec[0] * borderExtents * frac; vertPtr->point.set( pos.x, pos.y, 50.0f ); vertPtr->undulateData.set( pos.x, pos.y ); vertPtr->horizonFactor.set( 1, 0, 0, 0 ); vertPtr->normal = worldUp; vertPtr++; } mVertBuff.unlock(); // Fill in the PrimitiveBuffer... // 2 triangles per cell/quad const U32 gridTriCount = gridSize * gridSize * 2; // 4 sides, mGridSize quads per side, 2 triangles per quad const U32 borderTriCount = 4 * gridSize * 2; // 1 quad per gridSize, 2 triangles per quad // i.e. an extra row of 'cells' leading the front side of the grid const U32 horizonTriCount = gridSize * 2; mPrimCount = gridTriCount + borderTriCount + horizonTriCount; // 3 indices per triangle. mIndxCount = mPrimCount * 3; mPrimBuff.set( GFX, mIndxCount, mPrimCount, GFXBufferTypeStatic ); U16 *idxPtr; mPrimBuff.lock(&idxPtr); // Temporaries to hold indices for the corner points of a quad. U32 p00, p01, p11, p10; U32 offset = 0; // Given a single cell of the grid diagramed below, // quad indice variables are in this orientation. // // p01 --- p11 // | | // | | // p00 --- p10 // // Positive Y points UP the diagram ( p00, p01 ). // Positive X points RIGHT across the diagram ( p00, p10 ) // // i iterates bottom to top "column-wise" for ( U32 i = 0; i < mGridSize; i++ ) { // j iterates left to right "row-wise" for ( U32 j = 0; j < mGridSize; j++ ) { // where (j,i) is a particular cell. p00 = offset; p10 = offset + 1; p01 = offset + gridStride; p11 = offset + 1 + gridStride; // Top Left Triangle *idxPtr = p00; idxPtr++; *idxPtr = p01; idxPtr++; *idxPtr = p11; idxPtr++; // Bottom Right Triangle *idxPtr = p00; idxPtr++; *idxPtr = p11; idxPtr++; *idxPtr = p10; idxPtr++; offset += 1; } offset += 1; } // Fill border indices... // Given a grid size of 1, // the grid / border verts are in the vertex buffer in this order. // // // 4 5 // 2 --- 3 // | | // | | // 0 --- 1 // 7 6 // // Positive Y points UP the diagram ( p00, p01 ). // Positive X points RIGHT across the diagram ( p00, p10 ) // // Note we duplicate the first border vert ( 4 ) since it is also the last // and this makes our loop easier. const U32 sBorderStartVert [4] = { gridStride * gridSize, // Index to the Top-Left grid vert. gridStride * gridSize + gridSize, // Index to the Top-Right grid vert. gridSize, // Index to the Bottom-Right grid vert. 0, // Index to the Bottom-Left grid vert. }; const S32 sBorderStepSize [4] = { // Step size to the next grid vert along the specified side.... 1, // Top -(S32)gridStride, // Right -1, // Bottom (S32)gridStride, // Left }; const U32 firstBorderVert = gridStride * gridSize + gridStride; const U32 lastBorderVert = firstBorderVert + ( borderVertCount - 1 ); U32 startBorderVert = firstBorderVert; U32 startGridVert; U32 curStepSize; for ( U32 i = 0; i < 4; i++ ) { startGridVert = sBorderStartVert[i]; curStepSize = sBorderStepSize[i]; for ( U32 j = 0; j < gridSize; j++ ) { // Each border cell is 1 quad, 2 triangles. p00 = startGridVert; p10 = startGridVert + curStepSize; p01 = startBorderVert; p11 = startBorderVert + 1; if ( p11 > lastBorderVert ) p11 = firstBorderVert; // Top Left Triangle *idxPtr = p00; idxPtr++; *idxPtr = p01; idxPtr++; *idxPtr = p11; idxPtr++; // Bottom Right Triangle *idxPtr = p00; idxPtr++; *idxPtr = p11; idxPtr++; *idxPtr = p10; idxPtr++; startBorderVert++; startGridVert += curStepSize; } } // Fill in 'horizon' triangles. U32 curHorizonVert = lastBorderVert + 1; U32 curBorderVert = firstBorderVert; for ( U32 i = 0; i < gridSize; i++ ) { p00 = curBorderVert; p10 = curBorderVert + 1; p01 = curHorizonVert; p11 = curHorizonVert + 1; // Top Left Triangle *idxPtr = p00; idxPtr++; *idxPtr = p01; idxPtr++; *idxPtr = p11; idxPtr++; // Bottom Right Triangle *idxPtr = p00; idxPtr++; *idxPtr = p11; idxPtr++; *idxPtr = p10; idxPtr++; curBorderVert++; curHorizonVert++; } mPrimBuff.unlock(); } SceneData WaterPlane::setupSceneGraphInfo( SceneRenderState *state ) { SceneData sgData; sgData.lights[0] = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); // fill in water's transform sgData.objTrans = &getRenderTransform(); // fog sgData.setFogParams( state->getSceneManager()->getFogData() ); // misc sgData.backBuffTex = REFLECTMGR->getRefractTex(); sgData.reflectTex = mPlaneReflector.reflectTex; sgData.wireframe = GFXDevice::getWireframe() || smWireframe; return sgData; } void WaterPlane::setShaderParams( SceneRenderState *state, BaseMatInstance* mat, const WaterMatParams& paramHandles) { // Set variables that will be assigned to shader consts within WaterCommon // before calling Parent::setShaderParams mUndulateMaxDist = mGridElementSize * mGridSizeMinusOne * 0.5f; Parent::setShaderParams( state, mat, paramHandles ); // Now set the rest of the shader consts that are either unique to this // class or that WaterObject leaves to us to handle... MaterialParameters* matParams = mat->getMaterialParameters(); // set vertex shader constants //----------------------------------- matParams->setSafe(paramHandles.mGridElementSizeSC, (F32)mGridElementSize); //matParams->setSafe( paramHandles.mReflectTexSizeSC, mReflectTexSize ); if ( paramHandles.mModelMatSC->isValid() ) matParams->set(paramHandles.mModelMatSC, getRenderTransform(), GFXSCT_Float4x4); // set pixel shader constants //----------------------------------- LinearColorF c( mWaterFogData.color ); matParams->setSafe( paramHandles.mBaseColorSC, c ); // By default we need to show a true reflection is fullReflect is enabled and // we are above water. F32 reflect = mPlaneReflector.isEnabled() && !isUnderwater( state->getCameraPosition() ); // If we were occluded the last frame a query was fetched ( not necessarily last frame ) // and we weren't updated last frame... we don't have a valid texture to show // so use the cubemap / fake reflection color this frame. if ( mPlaneReflector.lastUpdateMs != REFLECTMGR->getLastUpdateMs() && mPlaneReflector.isOccluded() ) reflect = false; //Point4F reflectParams( getRenderPosition().z, mReflectMinDist, mReflectMaxDist, reflect ); Point4F reflectParams( getRenderPosition().z, 0.0f, 1000.0f, !reflect ); // TODO: This is a hack... why is this broken... check after // we merge advanced lighting with trunk! // reflectParams.z = 0.0f; matParams->setSafe( paramHandles.mReflectParamsSC, reflectParams ); VectorF reflectNorm( 0, 0, 1 ); matParams->setSafe(paramHandles.mReflectNormalSC, reflectNorm ); } void WaterPlane::prepRenderImage( SceneRenderState *state ) { PROFILE_SCOPE(WaterPlane_prepRenderImage); if( !state->isDiffusePass() ) return; mBasicLighting = dStricmp( LIGHTMGR->getId(), "BLM" ) == 0; mUnderwater = isUnderwater( state->getCameraPosition() ); mMatrixSet->setSceneView(GFX->getWorldMatrix()); const Frustum &frustum = state->getCameraFrustum(); if ( mPrimBuff.isNull() || mGenerateVB || frustum != mFrustum ) { mFrustum = frustum; setupVBIB( state ); mGenerateVB = false; MatrixF proj( true ); MathUtils::getZBiasProjectionMatrix( 0.0001f, mFrustum, &proj ); mMatrixSet->setSceneProjection(proj); } _getWaterPlane( state->getCameraPosition(), mWaterPlane, mWaterPos ); mWaterFogData.plane = mWaterPlane; mPlaneReflector.refplane = mWaterPlane; updateUnderwaterEffect( state ); ObjectRenderInst *ri = state->getRenderPass()->allocInst(); ri->renderDelegate.bind( this, &WaterObject::renderObject ); ri->type = RenderPassManager::RIT_Water; state->getRenderPass()->addInst( ri ); //mRenderUpdateCount++; } void WaterPlane::innerRender( SceneRenderState *state ) { GFXDEBUGEVENT_SCOPE( WaterPlane_innerRender, ColorI( 255, 0, 0 ) ); const Point3F &camPosition = state->getCameraPosition(); Point3F rvec, fvec, uvec, pos; const MatrixF &objMat = getTransform(); //getRenderTransform(); const MatrixF &camMat = state->getCameraTransform(); MatrixF renderMat( true ); camMat.getColumn( 1, &fvec ); uvec.set( 0, 0, 1 ); rvec = mCross( fvec, uvec ); rvec.normalize(); fvec = mCross( uvec, rvec ); pos = camPosition; pos.z = objMat.getPosition().z; renderMat.setColumn( 0, rvec ); renderMat.setColumn( 1, fvec ); renderMat.setColumn( 2, uvec ); renderMat.setColumn( 3, pos ); setRenderTransform( renderMat ); // Setup SceneData SceneData sgData = setupSceneGraphInfo( state ); // set the material S32 matIdx = getMaterialIndex( camPosition ); if ( !initMaterial( matIdx ) ) return; BaseMatInstance *mat = mMatInstances[matIdx]; WaterMatParams matParams = mMatParamHandles[matIdx]; // render the geometry if ( mat ) { // setup proj/world transform mMatrixSet->restoreSceneViewProjection(); mMatrixSet->setWorld(getRenderTransform()); setShaderParams( state, mat, matParams ); while( mat->setupPass( state, sgData ) ) { mat->setSceneInfo(state, sgData); mat->setTransforms(*mMatrixSet, state); setCustomTextures( matIdx, mat->getCurPass(), matParams ); // set vert/prim buffer GFX->setVertexBuffer( mVertBuff ); GFX->setPrimitiveBuffer( mPrimBuff ); GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, mVertCount, 0, mPrimCount ); } } } bool WaterPlane::isUnderwater( const Point3F &pnt ) const { F32 height = getPosition().z; F32 diff = pnt.z - height; return ( diff < 0.1 ); } F32 WaterPlane::distanceTo( const Point3F& point ) const { if( isUnderwater( point ) ) return 0.f; else return ( point.z - getPosition().z ); } bool WaterPlane::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& ) { if(context == PLC_Navigation) { polyList->setObject( this ); polyList->setTransform( &MatrixF::Identity, Point3F( 1.0f, 1.0f, 1.0f ) ); F32 z = getPosition().z; Point3F p0(box.minExtents.x, box.maxExtents.y, z), p1(box.maxExtents.x, box.maxExtents.y, z), p2(box.maxExtents.x, box.minExtents.y, z), p3(box.minExtents.x, box.minExtents.y, z); // Add vertices to poly list. U32 v0 = polyList->addPoint(p0); polyList->addPoint(p1); polyList->addPoint(p2); polyList->addPoint(p3); // Add plane between first three vertices. polyList->begin(0, 0); polyList->vertex(v0); polyList->vertex(v0+1); polyList->vertex(v0+2); polyList->plane(v0, v0+1, v0+2); polyList->end(); // Add plane between last three vertices. polyList->begin(0, 1); polyList->vertex(v0+2); polyList->vertex(v0+3); polyList->vertex(v0); polyList->plane(v0+2, v0+3, v0); polyList->end(); return true; } return false; } void WaterPlane::inspectPostApply() { Parent::inspectPostApply(); setMaskBits( UpdateMask ); } void WaterPlane::setTransform( const MatrixF &mat ) { // We only accept the z value from the new transform. MatrixF newMat( true ); Point3F newPos = getPosition(); newPos.z = mat.getPosition().z; newMat.setPosition( newPos ); Parent::setTransform( newMat ); // Parent::setTransforms ends up setting our worldBox to something other than // global, so we have to set it back... but we can't actually call setGlobalBounds // again because it does extra work adding and removing us from the container. mGlobalBounds = true; mObjBox.minExtents.set(-1e10, -1e10, -1e10); mObjBox.maxExtents.set( 1e10, 1e10, 1e10); // Keep mWaterPlane up to date. mWaterFogData.plane.set( 0, 0, 1, -getPosition().z ); } void WaterPlane::onStaticModified( const char* slotName, const char*newValue ) { Parent::onStaticModified( slotName, newValue ); if ( dStricmp( slotName, "surfMaterial" ) == 0 ) setMaskBits( MaterialMask ); } bool WaterPlane::castRay(const Point3F& start, const Point3F& end, RayInfo* info ) { // Simply look for the hit on the water plane // and ignore any future issues with waves, etc. const Point3F norm(0,0,1); PlaneF plane( Point3F::Zero, norm ); F32 hit = plane.intersect( start, end ); if ( hit < 0.0f || hit > 1.0f ) return false; info->t = hit; info->object = this; info->point = start + ( ( end - start ) * hit ); info->normal = norm; info->material = mMatInstances[ WaterMat ]; return true; } F32 WaterPlane::getWaterCoverage( const Box3F &testBox ) const { F32 posZ = getPosition().z; F32 coverage = 0.0f; if ( posZ > testBox.minExtents.z ) { if ( posZ < testBox.maxExtents.z ) coverage = (posZ - testBox.minExtents.z) / (testBox.maxExtents.z - testBox.minExtents.z); else coverage = 1.0f; } return coverage; } F32 WaterPlane::getSurfaceHeight( const Point2F &pos ) const { return getPosition().z; } void WaterPlane::onReflectionInfoChanged() { /* if ( isClientObject() && GFX->getPixelShaderVersion() >= 1.4 ) { if ( mFullReflect ) REFLECTMGR->registerObject( this, ReflectDelegate( this, &WaterPlane::updateReflection ), mReflectPriority, mReflectMaxRateMs, mReflectMaxDist ); else { REFLECTMGR->unregisterObject( this ); mReflectTex = NULL; } } */ } void WaterPlane::setGridSize( U32 inSize ) { if ( inSize == mGridSize ) return; // GridSize must be an odd number. //if ( inSize % 2 == 0 ) // inSize++; // GridSize must be at least 1 inSize = getMax( inSize, (U32)1 ); mGridSize = inSize; mGridSizeMinusOne = mGridSize - 1; mGenerateVB = true; setMaskBits( UpdateMask ); } void WaterPlane::setGridElementSize( F32 inSize ) { if ( inSize == mGridElementSize ) return; // GridElementSize must be greater than 0 inSize = getMax( inSize, 0.0001f ); mGridElementSize = inSize; mGenerateVB = true; setMaskBits( UpdateMask ); } bool WaterPlane::protectedSetGridSize( void *obj, const char *index, const char *data ) { WaterPlane *object = static_cast(obj); S32 size = dAtoi( data ); object->setGridSize( size ); // We already set the field. return false; } bool WaterPlane::protectedSetGridElementSize( void *obj, const char *index, const char *data ) { WaterPlane *object = static_cast(obj); F32 size = dAtof( data ); object->setGridElementSize( size ); // We already set the field. return false; } void WaterPlane::_getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ) { outPos = getPosition(); outPlane.set( outPos, Point3F(0,0,1) ); }