2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// 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"
2025-03-09 16:53:23 +00:00
# include "console/typeValidators.h"
2012-09-19 15:15:01 +00:00
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 ( )
{
2023-01-27 07:13:15 +00:00
docsURL ;
2012-09-19 15:15:01 +00:00
addGroup ( " WaterPlane " ) ;
2025-03-12 03:09:21 +00:00
addProtectedFieldV ( " gridSize " , TypeRangedS32 , Offset ( mGridSize , WaterPlane ) , & protectedSetGridSize , & defaultProtectedGetFn , & CommonValidators : : NaturalNumber ,
2012-09-19 15:15:01 +00:00
" Spacing between vertices in the WaterBlock mesh " ) ;
2025-08-02 20:04:43 +00:00
addProtectedFieldV ( " gridElementSize " , TypeRangedF32 , Offset ( mGridElementSize , WaterPlane ) , & protectedSetGridElementSize , & defaultProtectedGetFn , & CommonValidators : : PositiveFloat , " Duplicate of gridElementSize for backwards compatility " ) ;
2012-09-19 15:15:01 +00:00
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
{
2013-08-04 21:58:59 +00:00
F32 posZ ;
2012-09-19 15:15:01 +00:00
stream - > read ( & posZ ) ;
Point3F newPos = getPosition ( ) ;
newPos . z = posZ ;
setPosition ( newPos ) ;
}
}
void WaterPlane : : setupVBIB ( SceneRenderState * state )
{
2013-11-07 20:07:16 +00:00
const Frustum & frustum = state - > getCullingFrustum ( ) ;
2012-09-19 15:15:01 +00:00
// 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
2016-09-28 01:09:48 +00:00
( S32 ) gridStride , // Left
2012-09-19 15:15:01 +00:00
} ;
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
//-----------------------------------
2017-06-23 16:36:20 +00:00
LinearColorF c ( mWaterFogData . color ) ;
2012-09-19 15:15:01 +00:00
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 ;
2025-08-02 20:04:43 +00:00
GFXTransformSaver saver ;
2012-09-19 15:15:01 +00:00
mBasicLighting = dStricmp ( LIGHTMGR - > getId ( ) , " BLM " ) = = 0 ;
mUnderwater = isUnderwater ( state - > getCameraPosition ( ) ) ;
mMatrixSet - > setSceneView ( GFX - > getWorldMatrix ( ) ) ;
2013-11-07 20:07:16 +00:00
const Frustum & frustum = state - > getCameraFrustum ( ) ;
2012-09-19 15:15:01 +00:00
if ( mPrimBuff . isNull ( ) | |
mGenerateVB | |
frustum ! = mFrustum )
{
mFrustum = frustum ;
setupVBIB ( state ) ;
mGenerateVB = false ;
MatrixF proj ( true ) ;
2023-04-15 02:13:28 +00:00
MathUtils : : getZBiasProjectionMatrix ( 0.0f , mFrustum , & proj ) ;
2012-09-19 15:15:01 +00:00
mMatrixSet - > setSceneProjection ( proj ) ;
}
_getWaterPlane ( state - > getCameraPosition ( ) , mWaterPlane , mWaterPos ) ;
mWaterFogData . plane = mWaterPlane ;
mPlaneReflector . refplane = mWaterPlane ;
updateUnderwaterEffect ( state ) ;
ObjectRenderInst * ri = state - > getRenderPass ( ) - > allocInst < ObjectRenderInst > ( ) ;
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 ) ;
}
2014-11-28 08:42:10 +00:00
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 ;
}
2012-09-19 15:15:01 +00:00
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 < WaterPlane * > ( 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 < WaterPlane * > ( 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 ) ) ;
2023-01-27 07:13:15 +00:00
}