/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DWater.cpp /////////////////////////////////////////////////////////////////////////////
// Created: Mark Wilczynski, June 2001
// Desc: Draw reflective water surface. Also handles drawing of waves/ripples
// on the surface.
///////////////////////////////////////////////////////////////////////////////////////////////////
#define SCROLL_UV
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "stdio.h"
#include "W3DDevice/GameClient/W3DWater.h"
#include "W3DDevice/GameClient/heightmap.h"
#include "W3DDevice/GameClient/W3DShroud.h"
#include "W3DDevice/GameClient/W3DWaterTracks.h"
#include "W3DDevice/GameClient/W3DAssetManager.h"
#include "texture.h"
#include "assetmgr.h"
#include "rinfo.h"
#include "camera.h"
#include "scene.h"
#include "dx8wrapper.h"
#include "light.h"
#include "D3dx8math.h"
#include "simplevec.h"
#include "mesh.h"
#include "matinfo.h"
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/PerfTimer.h"
#include "Common/Xfer.h"
#include "Common/GameLOD.h"
#include "GameClient/Water.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/PolygonTrigger.h"
#include "GameLogic/ScriptEngine.h"
#include "W3DDevice/GameClient/W3DShaderManager.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DPoly.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "W3DDevice/GameClient/W3DCustomScene.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#define MIPMAP_BUMP_TEXTURE
// DEFINES ////////////////////////////////////////////////////////////////////////////////////////
#define SKYPLANE_SIZE (384.0f*MAP_XY_FACTOR)
#define SKYPLANE_HEIGHT (30.0f)
#define SKYBODY_TEXTURE "TSMoonLarg.tga"
#define SKYBODY_SIZE 45.0f //extent or radius of sky body
#define SKYBODY_X 150.0f //location of skybody
#define SKYBODY_Y 550.0f //location of skybody
/* in the bay
#define SKYBODY_X 120.0f //location of skybody
#define SKYBODY_Y 75.0f //location of skybody
*/
#define SKYBODY_HEIGHT SKYPLANE_HEIGHT //altitude of sky body (z-buffer disabled, so can equal sky height).
//GeForce3 water system defines
#define PATCH_SIZE 15 //number of vertices on patch edge. Large patches may waste vertices off edge of screen.
#define PATCH_UV_TILES 42 //number of times the bump map texture is tiled across patch (must be integer!).
#define PATCH_SCALE (4.0f * MAP_XY_FACTOR) //horizontal scale factor. Adjust this and size to get desired vertex density.
#define SEA_REFLECTION_SIZE 256 //dimensions of reflection texture
#define SEA_BUMP_SCALE (0.06f) //scales the du/dv offsets stored in bump map (~ amount to perturb)
#define BUMP_SIZE (50.f)
#define REFLECTION_FACTOR 0.1f
#define PATCH_WIDTH (PATCH_SIZE-1) //internal defines
#define PATCH_UV_SCALE ((Real)PATCH_UV_TILES/(Real)PATCH_WIDTH)
//3D Grid Mesh Water defines.
#define WATER_MESH_OPACITY 0.5f
#define WATER_MESH_X_VERTICES 128
#define WATER_MESH_Y_VERTICES 128
#define WATER_MESH_SPACING MAP_XY_FACTOR //same as terrain
#ifdef USE_MESH_NORMALS
#define WATER_MESH_FVF DX8_FVF_XYZNDUV2
typedef VertexFormatXYZNDUV2 MaterMeshVertexFormat;
#else
#define WATER_MESH_FVF DX8_FVF_XYZDUV2
typedef VertexFormatXYZDUV2 MaterMeshVertexFormat;
#endif
// Converts a FLOAT to a DWORD for use in SetRenderState() calls
static inline DWORD F2DW( FLOAT f ) { return *((DWORD*)&f); }
#define DRAW_WATER_WAKES
/// @todo: Fix clipping of objects that intersect the mirror surface
//#define CLIP_GEOMETRY_TO_PLANE // this enables clipping of objects that intersect the mirror surfaces
// Some shader combinations that can be useful in rendering water:
// Modulate stage0 with stage1 texture. Also modulate stage 0 with vertex color.
#define SC_DETAIL_BLEND ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE,\
ShaderClass::SRCBLEND_SRC_ALPHA,ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, \
ShaderClass::TEXTURING_ENABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_ENABLE, ShaderClass::DETAILCOLOR_DETAILBLEND, ShaderClass::DETAILALPHA_DISABLE) )
// Just a z-buffer fill, nothing is written to the color buffer.
#define SC_ZFILL_BLEND ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_DISABLE, ShaderClass::SRCBLEND_ZERO, \
ShaderClass::DSTBLEND_ONE, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::DETAILCOLOR_SCALE, ShaderClass::DETAILALPHA_DISABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_SCALE, ShaderClass::DETAILALPHA_DISABLE) )
// No texturing, just vertex color with vertex alpha
#define SC_ZFILL_BLENDx ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE, \
ShaderClass::SRCBLEND_ZERO, ShaderClass::DSTBLEND_SRC_COLOR, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, \
ShaderClass::TEXTURING_DISABLE, ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
// Modulate blended with vertex alpha modulation
#define SC_ZFILL_MODULATE_TEX ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE,\
ShaderClass::SRCBLEND_ZERO, ShaderClass::DSTBLEND_SRC_COLOR, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, \
ShaderClass::TEXTURING_ENABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
// Alpha blended with vertex alpha modulation
#define SC_ZFILL_ALPHA_TEX ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE,\
ShaderClass::SRCBLEND_SRC_ALPHA, ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_DISABLE, ShaderClass::SECONDARY_GRADIENT_DISABLE, \
ShaderClass::TEXTURING_ENABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
// Alpha blended with vertex alpha modulation
#define SC_OPAQUE_TEXONLY ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE,\
ShaderClass::SRCBLEND_ONE, ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_DISABLE, ShaderClass::SECONDARY_GRADIENT_DISABLE, \
ShaderClass::TEXTURING_ENABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
// Alpha blended with vertex alpha modulation
#define SC_ZFILL_BLEND3 ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE,\
ShaderClass::SRCBLEND_SRC_ALPHA, ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, \
ShaderClass::TEXTURING_ENABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass zFillAlphaShader(SC_ZFILL_BLEND3);
static ShaderClass blendStagesShader(SC_DETAIL_BLEND);
WaterRenderObjClass *TheWaterRenderObj=NULL; ///Release(); (p)=NULL; } }
void doSkyBoxSet(Bool startDraw)
{
if (TheWritableGlobalData)
TheWritableGlobalData->m_drawSkyBox = startDraw;
}
#define DONUT_SIDES 90
#define INNER_RADIUS 200.0f
#define OUTER_RADIUS 250.0f
#define TEXTURE_REPEAT_COUNT 16
#define DONUT_HEIGHT 15.0f
//#define DO_FLAT_DONUT
#define AMP_SCALE (30.0f/120.0f)
#define WAVE_FREQ 0.3f
#define AMP_SCALE2 (10.0f/120.0f)
#define NOISE_FREQ (2.0f*PI/WAVE_FREQ)
#define NOISE_REPEAT_FACTOR ((float)(1.0f/(16.0f)))
static Bool wireframeForDebug = 0;
void WaterRenderObjClass::setupJbaWaterShader(void)
{
DX8Wrapper::Set_Shader(ShaderClass::_PresetAlphaShader);
VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
DX8Wrapper::Set_Material(vmat);
REF_PTR_RELEASE(vmat);
m_riverTexture->Set_Mag_Filter(TextureClass::FILTER_TYPE_BEST);
m_riverTexture->Set_Min_Filter(TextureClass::FILTER_TYPE_BEST);
m_riverTexture->Set_Mip_Mapping(TextureClass::FILTER_TYPE_BEST);
// Setting *setting=&m_settings[m_tod];
DX8Wrapper::Apply_Render_State_Changes(); //force update of view and projection matrices
DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_ALPHAOP, D3DTOP_ADD );
DX8Wrapper::_Get_D3D_Device8()->SetTexture(3,m_riverAlphaEdge->Peek_DX8_Texture());
DX8Wrapper::Set_DX8_Texture_Stage_State(3, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
DX8Wrapper::Set_DX8_Texture_Stage_State(3, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
DX8Wrapper::Set_DX8_Texture_Stage_State(0, D3DTSS_TEXCOORDINDEX, 0);
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_TEXCOORDINDEX, 0);
DX8Wrapper::Set_DX8_Texture_Stage_State(3, D3DTSS_TEXCOORDINDEX, 1);
Bool doSparkles = true;
if (m_riverWaterPixelShader && doSparkles) {
DX8Wrapper::_Get_D3D_Device8()->SetTexture(1,m_waterSparklesTexture->Peek_DX8_Texture());
DX8Wrapper::_Get_D3D_Device8()->SetTexture(2,m_waterNoiseTexture->Peek_DX8_Texture());
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
DX8Wrapper::Set_DX8_Texture_Stage_State(2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
// Two output coordinates are used.
DX8Wrapper::Set_DX8_Texture_Stage_State(2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
DX8Wrapper::Set_DX8_Texture_Stage_State(2, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
DX8Wrapper::Set_DX8_Texture_Stage_State(2, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
D3DXMATRIX inv;
float det;
Matrix4 curView;
DX8Wrapper::_Get_DX8_Transform(D3DTS_VIEW, curView);
D3DXMatrixInverse(&inv, &det, (D3DXMATRIX*)&curView);
D3DXMATRIX scale;
D3DXMatrixScaling(&scale, NOISE_REPEAT_FACTOR, NOISE_REPEAT_FACTOR,1);
D3DXMATRIX destMatrix = inv * scale;
D3DXMatrixTranslation(&scale, m_riverVOrigin, m_riverVOrigin,0);
destMatrix = destMatrix*scale;
DX8Wrapper::_Set_DX8_Transform(D3DTS_TEXTURE2, *(Matrix4*)&destMatrix);
}
m_pDev->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 1, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 2, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 2, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 3, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 3, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
if (m_riverWaterPixelShader){
DX8Wrapper::_Get_D3D_Device8()->SetPixelShaderConstant(0, D3DXVECTOR4(REFLECTION_FACTOR, REFLECTION_FACTOR, REFLECTION_FACTOR, 1.0f), 1);
DX8Wrapper::_Get_D3D_Device8()->SetPixelShader(m_riverWaterPixelShader);
}
}
//-------------------------------------------------------------------------------------------------
/** Destructor. Releases w3d assets. */
//-------------------------------------------------------------------------------------------------
WaterRenderObjClass::~WaterRenderObjClass(void)
{
REF_PTR_RELEASE(m_meshVertexMaterialClass);
REF_PTR_RELEASE(m_vertexMaterialClass);
REF_PTR_RELEASE(m_meshLight);
REF_PTR_RELEASE(m_alphaClippingTexture);
REF_PTR_RELEASE (m_skyBox);
REF_PTR_RELEASE (m_riverTexture);
REF_PTR_RELEASE (m_whiteTexture);
REF_PTR_RELEASE (m_waterNoiseTexture);
REF_PTR_RELEASE (m_riverAlphaEdge);
REF_PTR_RELEASE (m_waterSparklesTexture);
Int i;
for(i=0; iGet_Mip_Level_Count();
pBumpSource->Get_Level_Description(d3dsd);
if (Get_Bytes_Per_Pixel(d3dsd.Format) != 4)
{
// LORENZEN WAS BUGGED BY THIS,
// DEBUG_CRASH(("WaterRenderObjClass::Invalid BumpMap format - Was it compressed?") );
return S_OK;
}
pTex[0]=DX8Wrapper::_Create_DX8_Texture(d3dsd.Width,d3dsd.Height,WW3D_FORMAT_U8V8,TextureClass::MIP_LEVELS_ALL,D3DPOOL_MANAGED,false);
for (Int level=0; level < numLevels; level++)
{
surf=pBumpSource->Get_Surface_Level(level);
surf->Get_Description(d3dsd);
pSrc=(unsigned char *)surf->Lock((int *)&dwSrcPitch);
pTex[0]->LockRect( level, &d3dlr, 0, 0 );
DWORD dwDstPitch = (DWORD)d3dlr.Pitch;
BYTE* pDst = (BYTE*)d3dlr.pBits;
for( DWORD y=0; y1 ) ? 63 : 127;
switch( D3DFMT_V8U8)//m_BumpMapFormat )
{
case D3DFMT_V8U8:
*pDstT++ = (BYTE)iDu;
*pDstT++ = (BYTE)iDv;
break;
case D3DFMT_L6V5U5:
*(WORD*)pDstT = (WORD)( ( (iDu>>3) & 0x1f ) << 0 );
*(WORD*)pDstT |= (WORD)( ( (iDv>>3) & 0x1f ) << 5 );
*(WORD*)pDstT |= (WORD)( ( ( uL>>2) & 0x3f ) << 10 );
pDstT += 2;
break;
case D3DFMT_X8L8V8U8:
*pDstT++ = (BYTE)iDu;
*pDstT++ = (BYTE)iDv;
*pDstT++ = (BYTE)uL;
*pDstT++ = (BYTE)0L;
break;
}
// Move one pixel to the left (src is 32-bpp)
pSrcB0+=4; pSrcB1+=4; pSrcB2+=4;
}
// Move to the next line
pSrc += dwSrcPitch; pDst += dwDstPitch;
}
pTex[0]->UnlockRect(level);
surf->Unlock();
REF_PTR_RELEASE (surf);
}//for each level
#else
surf=pBumpSource->Get_Surface_Level();
surf->Get_Description(d3dsd);
pSrc=(unsigned char *)surf->Lock((int *)&dwSrcPitch);
// Create the bumpmap's surface and texture objects
m_pBumpTexture[i]=DX8Wrapper::_Create_DX8_Texture(d3dsd.Width,d3dsd.Height,WW3D_FORMAT_U8V8,TextureClass::MIP_LEVELS_1,D3DPOOL_MANAGED,false);
// Fill the bits of the new texture surface with bits from
// a private format.
m_pBumpTexture[i]->LockRect( 0, &d3dlr, 0, 0 );
DWORD dwDstPitch = (DWORD)d3dlr.Pitch;
BYTE* pDst = (BYTE*)d3dlr.pBits;
for( DWORD y=0; y1 ) ? 63 : 127;
switch( D3DFMT_V8U8)//m_BumpMapFormat )
{
case D3DFMT_V8U8:
*pDstT++ = (BYTE)iDu;
*pDstT++ = (BYTE)iDv;
break;
case D3DFMT_L6V5U5:
*(WORD*)pDstT = (WORD)( ( (iDu>>3) & 0x1f ) << 0 );
*(WORD*)pDstT |= (WORD)( ( (iDv>>3) & 0x1f ) << 5 );
*(WORD*)pDstT |= (WORD)( ( ( uL>>2) & 0x3f ) << 10 );
pDstT += 2;
break;
case D3DFMT_X8L8V8U8:
*pDstT++ = (BYTE)iDu;
*pDstT++ = (BYTE)iDv;
*pDstT++ = (BYTE)uL;
*pDstT++ = (BYTE)0L;
break;
}
// Move one pixel to the left (src is 32-bpp)
pSrcB0+=4; pSrcB1+=4; pSrcB2+=4;
}
// Move to the next line
pSrc += dwSrcPitch; pDst += dwDstPitch;
}
m_pBumpTexture[i]->UnlockRect(0);
surf->Unlock();
#endif
return S_OK;
}
//-------------------------------------------------------------------------------------------------
/** Create and fill a D3D vertex buffer with water surface vertices */
//-------------------------------------------------------------------------------------------------
HRESULT WaterRenderObjClass::generateVertexBuffer( Int sizeX, Int sizeY, Int vertexSize, Bool doStatic)
{
m_numVertices=sizeX*sizeY;
//Assuming dynamic vertex buffer, allocate maximum multiple of required size to allow rendering from
//different parts of the buffer. 5-15-03: Disabled this since we use DISCARD mode instead to avoid Nvidia Runtime bug. -MW
//m_numVertices=(65536 / (sizeX*sizeY))*sizeX*sizeY;
SEA_PATCH_VERTEX* pVertices;
Setting *setting=&m_settings[m_tod];
HRESULT hr;
//default setting for a dynamic vertex buffer
D3DPOOL pool = D3DPOOL_DEFAULT;
DWORD usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
DWORD fvf = WATER_MESH_FVF;
if (doStatic)
{ //change settings for a static vertex buffer
pool = D3DPOOL_MANAGED;
usage = D3DUSAGE_WRITEONLY;
fvf=0;// DX8 Docs confusing on this. Say no FVF for vertex shaders. Else DX8_FVF_XYZDUV1;
m_numVertices=sizeX*sizeY;
}
if (m_vertexBufferD3D == NULL)
{ // Create vertex buffer
if (FAILED(hr=m_pDev->CreateVertexBuffer
(
m_numVertices*vertexSize,
usage,
fvf,
pool,
&m_vertexBufferD3D
)))
return hr;
}
m_vertexBufferD3DOffset=0;
if (!doStatic)
return S_OK; //only create the buffer, other code will fill it.
// load results into buffer
if (FAILED(hr=m_vertexBufferD3D->Lock
(
0,
m_numVertices*sizeof(SEA_PATCH_VERTEX),
(BYTE**)&pVertices,
0//D3DLOCK_DISCARD
)))
return hr;
Int x,z;
for (z=0; zx=(float)x;
pVertices->y=m_level;
pVertices->z=(float)z;
pVertices->tu=(float)x*PATCH_UV_SCALE;
pVertices->tv=(float)z*PATCH_UV_SCALE;
pVertices->c=setting->transparentWaterDiffuse; //vertex alpha/color
pVertices++;
}
}
if (FAILED(hr=m_vertexBufferD3D->Unlock())) return hr;
return S_OK;
}
//-------------------------------------------------------------------------------------------------
/** Create and fill a D3D index buffer with water surface strip indices */
//-------------------------------------------------------------------------------------------------
HRESULT WaterRenderObjClass::generateIndexBuffer(Int sizeX, Int sizeY)
{
HRESULT hr;
//Will need SizeY-1 strips, each of length SizeX*2 (2 indices per strip segment).
//Will also need 2 extra indices to connect each strip to next one (except last strip)
//Total index buffer size = (SizeY-1)*(SizeX*2+2) - 2 (drop the extra 2 indices from last strip)
m_numIndices=(sizeY-1)*(sizeX*2+2) - 2;
//old way
// Create index buffer
WORD* pIndices;
if (FAILED(hr=m_pDev->CreateIndexBuffer
(
(m_numIndices+2)*sizeof(WORD),
D3DUSAGE_WRITEONLY,
D3DFMT_INDEX16,
D3DPOOL_MANAGED,
&m_indexBufferD3D
)))
return hr;
if (FAILED(hr=m_indexBufferD3D->Lock
(
0,
m_numIndices*sizeof(WORD),
(BYTE**)&pIndices,
0
)))
return hr;
Int i,j,k;
for (i=0,j=0,k=0; i0; x-=step)
{
*pIndices++=(WORD)((z-step)*size+(x-step));
*pIndices++=(WORD)((z-0)*size+(x-step));
}
// insert additional degenerate to start next row
*pIndices++=pIndices[-1];
*pIndices++=pIndices[-1];
}
s_toggle=!s_toggle;
}
*/
if (FAILED(hr=m_indexBufferD3D->Unlock())) return hr;
return S_OK;
}
//-------------------------------------------------------------------------------------------------
/** Releases all w3d assets, to prepare for Reset device call. */
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::ReleaseResources(void)
{
REF_PTR_RELEASE(m_indexBuffer);
REF_PTR_RELEASE(m_pReflectionTexture);
SAFE_RELEASE(m_vertexBufferD3D);
SAFE_RELEASE(m_indexBufferD3D);
if (m_waterTrackSystem)
m_waterTrackSystem->ReleaseResources();
if (m_dwWavePixelShader)
m_pDev->DeletePixelShader(m_dwWavePixelShader);
if (m_dwWaveVertexShader)
m_pDev->DeleteVertexShader(m_dwWaveVertexShader);
if (m_waterPixelShader)
m_pDev->DeletePixelShader(m_waterPixelShader);
if (m_trapezoidWaterPixelShader)
m_pDev->DeletePixelShader(m_trapezoidWaterPixelShader);
if (m_riverWaterPixelShader)
m_pDev->DeletePixelShader(m_riverWaterPixelShader);
m_dwWavePixelShader=0;
m_dwWaveVertexShader=0;
m_waterPixelShader = 0;
m_trapezoidWaterPixelShader=0;
m_riverWaterPixelShader=0;
}
//-------------------------------------------------------------------------------------------------
/** (Re)allocates all W3D assets after a reset.. */
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::ReAcquireResources(void)
{
HRESULT hr;
m_indexBuffer=NEW_REF(DX8IndexBufferClass,(6));
// Fill up the IB
{
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexBuffer);
UnsignedShort *ib=lockIdxBuffer.Get_Index_Array();
//quad of 2 triangles:
// 3-----2
// | /|
// | / |
// |/ |
// 0-----1
ib[0]=3;
ib[1]=0;
ib[2]=2;
ib[3]=2;
ib[4]=0;
ib[5]=1;
}
m_pDev=DX8Wrapper::_Get_D3D_Device8();
//We're using the same grid for either 3D Water Mesh or Pixel/Vertex shader. Just
//allocate the right size depending on usage
if (m_meshData)
{
//Create new grid data
if (FAILED(generateIndexBuffer(m_gridCellsX+1,m_gridCellsY+1)))
return;
if (FAILED(generateVertexBuffer(m_gridCellsX+1,m_gridCellsY+1,sizeof(MaterMeshVertexFormat),false)))
return;
}
else
if (m_waterType == WATER_TYPE_2_PVSHADER)
{ //pixel/vertex shader based water assets.
if (FAILED(hr=generateIndexBuffer(PATCH_SIZE,PATCH_SIZE)))
return;
if (FAILED(hr=generateVertexBuffer(PATCH_SIZE,PATCH_SIZE,sizeof(SEA_PATCH_VERTEX),true)))
return;
//shader decleration
DWORD Declaration[]=
{
(D3DVSD_STREAM(0)),
(D3DVSD_REG(0, D3DVSDT_FLOAT3)), // Position
(D3DVSD_REG(1, D3DVSDT_D3DCOLOR)), // Diffuse
(D3DVSD_REG(2, D3DVSDT_FLOAT2)), // Bump map texture
(D3DVSD_END())
};
hr = W3DShaderManager::LoadAndCreateD3DShader("shaders\\wave.pso", &Declaration[0], 0, false, &m_dwWavePixelShader);
if (FAILED(hr))
return;
hr = W3DShaderManager::LoadAndCreateD3DShader("shaders\\wave.vso", &Declaration[0], 0, true, &m_dwWaveVertexShader);
if (FAILED(hr))
return;
// Create reflection texture
m_pReflectionTexture = DX8Wrapper::Create_Render_Target (SEA_REFLECTION_SIZE, SEA_REFLECTION_SIZE);
}
if (m_waterTrackSystem)
m_waterTrackSystem->ReAcquireResources();
if (W3DShaderManager::getChipset() >= DC_GENERIC_PIXEL_SHADER_1_1)
{
ID3DXBuffer *compiledShader;
char *shader =
"ps.1.1\n \
tex t0 \n\
tex t1 \n\
tex t2 \n\
tex t3\n\
mul r0,v0,t0 ; blend vertex color into t0. \n\
mul r1, t1, t2 ; mul\n\
add r0.rgb, r0, t3\n\
+mul r0.a, r0, t3\n\
add r0.rgb, r0, r1\n";
hr = D3DXAssembleShader( shader, strlen(shader), 0, NULL, &compiledShader, NULL);
if (hr==0) {
hr = DX8Wrapper::_Get_D3D_Device8()->CreatePixelShader((DWORD*)compiledShader->GetBufferPointer(), &m_riverWaterPixelShader);
compiledShader->Release();
}
shader =
"ps.1.1\n \
tex t0 \n\
tex t1 \n\
texbem t2, t1 ; use t1 as env map adjustment on t2.\n\
mul r0,v0,t0 ; blend vertex color into t0. \n\
mul r1.rgb,t2,c0 ; reduce t2 (environment mapped reflection) by constant\n\
add r0.rgb, r0, r1";
hr = D3DXAssembleShader( shader, strlen(shader), 0, NULL, &compiledShader, NULL);
if (hr==0) {
hr = DX8Wrapper::_Get_D3D_Device8()->CreatePixelShader((DWORD*)compiledShader->GetBufferPointer(), &m_waterPixelShader);
compiledShader->Release();
}
shader =
"ps.1.1\n \
tex t0 \n\
tex t1 \n\
tex t2 \n\
tex t3 ; get black shroud \n\
mul r0,v0,t0 ; blend vertex color and alpha into base texture. \n\
mad r0.rgb, t1, t2, r0 ; blend sparkles and noise \n\
mul r0.rgb, r0, t3 ; blend in black shroud \n\
;\n";
hr = D3DXAssembleShader( shader, strlen(shader), 0, NULL, &compiledShader, NULL);
if (hr==0) {
hr = DX8Wrapper::_Get_D3D_Device8()->CreatePixelShader((DWORD*)compiledShader->GetBufferPointer(), &m_trapezoidWaterPixelShader);
compiledShader->Release();
}
}
}
void WaterRenderObjClass::load(void)
{
if (m_waterTrackSystem)
m_waterTrackSystem->loadTracks();
}
//-------------------------------------------------------------------------------------------------
/** Initializes water with dimensions and parent scene.
* During rendering, we will render a water surface of given dimensions
* and reflect the parent scene in its surface. For now, waters are
* forced to be rectangles. */
//-------------------------------------------------------------------------------------------------
Int WaterRenderObjClass::init(Real waterLevel, Real dx, Real dy, SceneClass *parentScene, WaterType type)
{
m_iBumpFrame=0;
m_fBumpScale=SEA_BUMP_SCALE;
m_dx=dx;
m_dy=dy;
m_level=waterLevel;
m_LastUpdateTime=timeGetTime();
m_uScrollPerMs=0.001f;
m_vScrollPerMs=0.001f;
m_uOffset=0;
m_vOffset=0;
m_parentScene=parentScene;
m_waterType = type;
/// Hack for now
//m_waterType = WATER_TYPE_0_TRANSLUCENT;
///@todo: calculate a real normal/distance for arbitrary planes.
m_planeNormal=Vector3(0,0,1); //water plane normal
m_planeDistance=m_level; //water plane distance(always at zero for now)
m_meshLight=NEW_REF(LightClass,(LightClass::DIRECTIONAL));
m_meshLight->Set_Ambient(Vector3(0.1f,0.1f,0.1f));
m_meshLight->Set_Diffuse(Vector3(1.0f,1.0f,1.0f));
m_meshLight->Set_Specular(Vector3(1.0f,1.0f,1.0f));
m_meshLight->Set_Position(Vector3(1000,1000,1000));
//testLight->Set_Spot_Direction(Vector3(TheGlobalData->m_terrainLightX,TheGlobalData->m_terrainLightY,TheGlobalData->m_terrainLightZ));
m_meshLight->Set_Spot_Direction(Vector3(-0.57f,-0.57f,-0.57f));
//Setup material for 3D Mesh water.
m_meshVertexMaterialClass=NEW_REF(VertexMaterialClass,());
m_meshVertexMaterialClass->Set_Shininess(20.0);
m_meshVertexMaterialClass->Set_Ambient(1.0f,1.0f,1.0f);
m_meshVertexMaterialClass->Set_Diffuse(1.0f,1.0f,1.0f);
m_meshVertexMaterialClass->Set_Specular(0.5,0.5,0.5);
m_meshVertexMaterialClass->Set_Opacity(WATER_MESH_OPACITY);
m_meshVertexMaterialClass->Set_Lighting(true);
//
// assign the data from the WaterSettings[] global to the data for this
// render object (we at present only have one water plane)
//
loadSetting( &m_settings[ TIME_OF_DAY_MORNING ], TIME_OF_DAY_MORNING );
loadSetting( &m_settings[ TIME_OF_DAY_AFTERNOON ], TIME_OF_DAY_AFTERNOON );
loadSetting( &m_settings[ TIME_OF_DAY_EVENING ], TIME_OF_DAY_EVENING );
loadSetting( &m_settings[ TIME_OF_DAY_NIGHT ], TIME_OF_DAY_NIGHT );
Set_Sort_Level(2); //force water to be drawn after all other non translucent objects in scene.
Set_Force_Visible(TRUE); //water is always visible since it's a composite object made of multiple planes all over the map.
ReAcquireResources();
if (type == WATER_TYPE_2_PVSHADER || (W3DShaderManager::getChipset() >= DC_GENERIC_PIXEL_SHADER_1_1))
{ //geforce3 specific water requires some extra D3D assets
m_pDev=DX8Wrapper::_Get_D3D_Device8();
//save previous thumbnail mode
WW3D::TextureThumbnailModeEnum tmMode=WW3D::Get_Texture_Thumbnail_Mode ();
WW3D::Set_Texture_Thumbnail_Mode(WW3D::TEXTURE_THUMBNAIL_MODE_OFF);
//load bump map textures off disk
TextureClass *pBumpSource; //temporary textures in a format W3D understands
TextureClass *pBumpSource2; //temporary textures in a format W3D understands
Int i;
i=NUM_BUMP_FRAMES;
while (i--)
{
char bump_name[128];
sprintf(bump_name,"caust%.2d.tga",i);
pBumpSource=WW3DAssetManager::Get_Instance()->Get_Texture(bump_name);
sprintf(bump_name,"caustS%.2d.tga",i);
pBumpSource2=WW3DAssetManager::Get_Instance()->Get_Texture(bump_name);
initBumpMap(m_pBumpTexture+i, pBumpSource);
initBumpMap(m_pBumpTexture2+i, pBumpSource2);
WW3DAssetManager::Get_Instance()->Release_Texture(pBumpSource);
WW3DAssetManager::Get_Instance()->Release_Texture(pBumpSource2);
REF_PTR_RELEASE(pBumpSource);
REF_PTR_RELEASE(pBumpSource2);
}
//restore previous thumpnail mode
WW3D::Set_Texture_Thumbnail_Mode(tmMode);
}
//Setup material for regular water
m_vertexMaterialClass=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
m_shaderClass = zFillAlphaShader;//ShaderClass::_PresetAlphaShader;ShaderClass::_PresetOpaqueShader;//detailOpaqueShader;
m_shaderClass.Set_Cull_Mode(ShaderClass::CULL_MODE_DISABLE); //water should be visible from both sides
//Assets used for all types of water
m_alphaClippingTexture=WW3DAssetManager::Get_Instance()->Get_Texture(SKYBODY_TEXTURE);
#ifdef CLIP_GEOMETRY_TO_PLANE
m_alphaClippingTexture=WW3DAssetManager::Get_Instance()->Get_Texture("alphaclip.tga");
#endif
m_skyBox = ((W3DAssetManager*)W3DAssetManager::Get_Instance())->Create_Render_Obj( "new_skybox", TheGlobalData->m_skyBoxScale, 0);
//Enable clamping on all textures used by the skybox (to reduce corner seams).
if (m_skyBox && m_skyBox->Class_ID() == RenderObjClass::CLASSID_MESH)
{
MeshClass *mesh=(MeshClass*) m_skyBox;
MaterialInfoClass *material = mesh->Get_Material_Info();
for (Int i=0; iTexture_Count(); i++)
{
if (material->Peek_Texture(i))
{
material->Peek_Texture(i)->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
material->Peek_Texture(i)->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
}
}
}
m_riverTexture=WW3DAssetManager::Get_Instance()->Get_Texture("TWWater01.tga");
//For some reason setting a NULL texture does not result in 0xffffffff for pixel shaders so using explicit "white" texture.
m_whiteTexture=MSGNEW("TextureClass") TextureClass(1,1,WW3D_FORMAT_A4R4G4B4,TextureClass::MIP_LEVELS_1);
SurfaceClass *surface=m_whiteTexture->Get_Surface_Level();
surface->DrawPixel(0,0,0xffffffff);
REF_PTR_RELEASE(surface);
m_waterNoiseTexture=WW3DAssetManager::Get_Instance()->Get_Texture("Noise0000.tga");
m_riverAlphaEdge=WW3DAssetManager::Get_Instance()->Get_Texture("TWAlphaEdge.tga");
m_waterSparklesTexture=WW3DAssetManager::Get_Instance()->Get_Texture("WaterSurfaceBubbles.tga");
#ifdef DRAW_WATER_WAKES
m_waterTrackSystem = NEW WaterTracksRenderSystem;
m_waterTrackSystem->init();
#endif
return 0;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void WaterRenderObjClass::reset( void )
{
// for vertex animated water mesh reset the values
if( m_meshData)
{
Int i, j;
WaterMeshData *pData;
Int mx = m_gridCellsX + 1;
Int my = m_gridCellsY + 1;
// go through each mesh point and adjust the height according to the velocity
for( j = 0, pData = m_meshData; j < (my + 2); j++ )
{
for( i = 0; i < (mx + 2); i++ )
{
// areset grid values for this cell
pData->velocity = 0.0f;
pData->height = 0.0f;
pData->preferredHeight = 0.0f;
pData->status = WaterRenderObjClass::AT_REST;
// on to the next one
pData++;
} // end for i
} // end for j
// mesh data is no longer in motion
m_meshInMotion = FALSE;
} // end if, water type 3
if (m_waterTrackSystem)
m_waterTrackSystem->reset();
}
void WaterRenderObjClass::enableWaterGrid(Bool state)
{
m_doWaterGrid = state;
m_drawingRiver = false;
m_disableRiver = false;
if (state && m_meshData == NULL)
{ //water type has changed, must allocate necessary assets for new water.
//contains the current deformed water surface z(height) values. With 1 vertex invisible border
//around surface to speed up normal calculations.
m_meshDataSize = (m_gridCellsX+1+2)*(m_gridCellsY+1+2);
m_meshData=NEW WaterMeshData[ m_meshDataSize ];
memset(m_meshData,0,sizeof(WaterMeshData)*(m_gridCellsX+1+2)*(m_gridCellsY+1+2));
reset();
//Release existing grid data
SAFE_RELEASE(m_vertexBufferD3D);
SAFE_RELEASE(m_indexBufferD3D);
//Create new grid data
if (FAILED(generateIndexBuffer(m_gridCellsX+1,m_gridCellsY+1)))
return;
if (FAILED(generateVertexBuffer(m_gridCellsX+1,m_gridCellsY+1,sizeof(MaterMeshVertexFormat),false)))
return;
}
}
// ------------------------------------------------------------------------------------------------
/** Update phase for water if we need it. This called once per client frame reguardless
* of how fast the logic framerate is running */
// ------------------------------------------------------------------------------------------------
void WaterRenderObjClass::update( void )
{
static UnsignedInt lastLogicFrame = 0;
UnsignedInt currLogicFrame = 0;
if( TheGameLogic )
currLogicFrame = TheGameLogic->getFrame();
m_riverVOrigin += 0.002f;
m_riverXOffset += (Real)(0.0125*33/5000);
m_riverYOffset += (Real)(2*0.0125*33/5000);
if (m_riverXOffset > 1) m_riverXOffset -= 1;
if (m_riverYOffset > 1) m_riverYOffset -= 1;
if (m_riverXOffset < -1) m_riverXOffset += 1;
if (m_riverYOffset < -1) m_riverYOffset += 1;
m_iBumpFrame++;
if (m_iBumpFrame >= NUM_BUMP_FRAMES) {
m_iBumpFrame = 0;
}
// we only process some things if the logic frame has changed
if( lastLogicFrame != currLogicFrame )
{
// for vertex animated water we need to update the vector field
if( m_doWaterGrid && m_meshInMotion == TRUE )
{
const Real PREFERRED_HEIGHT_FUDGE = 1.0f; ///< this is close enough to at rest
const Real AT_REST_VELOCITY_FUDGE = 1.0f; ///< when we're close enought to at rest height and velocity we will stop
const Real WATER_DAMPENING = 0.93f; ///< use with up force of 15.0
Int i, j;
Int mx = m_gridCellsX+1;
Int my = m_gridCellsY+1;
WaterMeshData *pData;
//
// we will mark the mesh as clean now ... if any of the fields are still in motion
// they will continue to mark the mesh as dirty so processing continues next frame
//
m_meshInMotion = FALSE;
// go through each mesh point and adjust the height according to the velocity
for( j = 0, pData = m_meshData; j < (my + 2); j++ )
{
for( i = 0; i < (mx + 2); i++ )
{
// only pay attention to mesh points that are in motion
if( BitTest( pData->status, WaterRenderObjClass::IN_MOTION ) )
{
// DAMPENING to slow the changes down
pData->velocity *= WATER_DAMPENING;
// if the height here is below our preferred height, we want to add upward force to counteract it
if( pData->height < pData->preferredHeight )
pData->velocity -= TheGlobalData->m_gravity * 3.0f;
else
pData->velocity += TheGlobalData->m_gravity * 3.0f;
// adjust the height at this grid location according to the current velocity
pData->height = pData->height + pData->velocity;
//
// if we are close enough to our preferred height and our velocity is small enough
// this will be our resting location
//
if( fabs( pData->height - pData->preferredHeight ) < PREFERRED_HEIGHT_FUDGE &&
fabs( pData->velocity ) < AT_REST_VELOCITY_FUDGE )
{
BitClear( pData->status, WaterRenderObjClass::IN_MOTION );
pData->height = pData->preferredHeight;
pData->velocity = 0.0f;
} // end if
else
{
// there is still motion in the mesh, we need to process next frame
m_meshInMotion = TRUE;
} // end else
} // end if
// on to the next one
pData++;
} // end for i
} // end for j
} // end if
// mark the last logic frame we processed on
lastLogicFrame = currLogicFrame;
} // end if, a logic frame has passed
} // end update
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::replaceSkyboxTexture(const AsciiString& oldTexName, const AsciiString& newTextName)
{
W3DAssetManager* assetManager = ((W3DAssetManager*)W3DAssetManager::Get_Instance());
assetManager->replacePrototypeTexture(m_skyBox, oldTexName.str(), newTextName.str());
//Enable clamping on all textures used by the skybox (to reduce corner seams).
if (m_skyBox && m_skyBox->Class_ID() == RenderObjClass::CLASSID_MESH)
{
MeshClass *mesh=(MeshClass*) m_skyBox;
MaterialInfoClass *material = mesh->Get_Material_Info();
for (Int i=0; iTexture_Count(); i++)
{
if (material->Peek_Texture(i))
{
material->Peek_Texture(i)->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
material->Peek_Texture(i)->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
}
}
}
}
//-------------------------------------------------------------------------------------------------
/** Adjusts various water/sky rendering settings that depend on time of day. */
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::setTimeOfDay(TimeOfDay tod)
{
m_tod=tod;
if (m_waterType == WATER_TYPE_2_PVSHADER)
generateVertexBuffer(PATCH_SIZE,PATCH_SIZE,sizeof(SEA_PATCH_VERTEX),true); //update the water mesh with new lighting/alpha
}
//-------------------------------------------------------------------------------------------------
/**Copies GDF settings dealing with a particular time of day into our own
* structures. Also allocates any required W3D assets (textures). */
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::loadSetting( Setting *setting, TimeOfDay timeOfDay )
{
SurfaceClass::SurfaceDescription surfaceDesc;
// sanity
DEBUG_ASSERTCRASH( setting, ("WaterRenderObjClass::loadSetting, NULL setting\n") );
// textures
setting->skyTexture = WW3DAssetManager::Get_Instance()->Get_Texture( WaterSettings[ timeOfDay ].m_skyTextureFile.str() );
setting->waterTexture = WW3DAssetManager::Get_Instance()->Get_Texture( WaterSettings[ timeOfDay ].m_waterTextureFile.str() );
// texelss per unit
setting->skyTexelsPerUnit = WaterSettings[ timeOfDay ].m_skyTexelsPerUnit;
setting->waterTexture->Get_Level_Description( surfaceDesc, 0 );
setting->skyTexelsPerUnit /= (Real)surfaceDesc.Width;
// water repeat
setting->waterRepeatCount = WaterSettings[ timeOfDay ].m_waterRepeatCount;
// U and V scroll per ms
setting->uScrollPerMs = WaterSettings[ timeOfDay ].m_uScrollPerMs;
setting->vScrollPerMs = WaterSettings[ timeOfDay ].m_vScrollPerMs;
//
// vertex colors
//
// bottom left
setting->vertex00Diffuse = (WaterSettings[ timeOfDay ].m_vertex00Diffuse.red << 16) |
(WaterSettings[ timeOfDay ].m_vertex00Diffuse.green << 8) |
WaterSettings[ timeOfDay ].m_vertex00Diffuse.blue;
// top left
setting->vertex01Diffuse = (WaterSettings[ timeOfDay ].m_vertex01Diffuse.red << 16) |
(WaterSettings[ timeOfDay ].m_vertex01Diffuse.green << 8) |
WaterSettings[ timeOfDay ].m_vertex01Diffuse.blue;
// bottom right
setting->vertex10Diffuse = (WaterSettings[ timeOfDay ].m_vertex10Diffuse.red << 16) |
(WaterSettings[ timeOfDay ].m_vertex10Diffuse.green << 8) |
WaterSettings[ timeOfDay ].m_vertex10Diffuse.blue;
// top right
setting->vertex11Diffuse = (WaterSettings[ timeOfDay ].m_vertex11Diffuse.red << 16) |
(WaterSettings[ timeOfDay ].m_vertex11Diffuse.green << 8) |
WaterSettings[ timeOfDay ].m_vertex11Diffuse.blue;
// diffuse water color
setting->waterDiffuse = (WaterSettings[ timeOfDay ].m_waterDiffuseColor.alpha << 24) |
(WaterSettings[ timeOfDay ].m_waterDiffuseColor.red << 16) |
(WaterSettings[ timeOfDay ].m_waterDiffuseColor.green << 8) |
WaterSettings[ timeOfDay ].m_waterDiffuseColor.blue;
// transparent water color
setting->transparentWaterDiffuse = (WaterSettings[ timeOfDay ].m_transparentWaterDiffuse.alpha << 24) |
(WaterSettings[ timeOfDay ].m_transparentWaterDiffuse.red << 16) |
(WaterSettings[ timeOfDay ].m_transparentWaterDiffuse.green << 8) |
WaterSettings[ timeOfDay ].m_transparentWaterDiffuse.blue;
}
//-------------------------------------------------------------------------------------------------
/** Our water may use effects that require run-time rendered textures. These
* textures need to be updated before we start rendering to the main screen
* render target because D3D doesn't multiple render targets. */
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::updateRenderTargetTextures(CameraClass *cam)
{
if (m_waterType == WATER_TYPE_2_PVSHADER && getClippedWaterPlane(cam, NULL) &&
TheTerrainRenderObject && TheTerrainRenderObject->getMap())
renderMirror(cam); //generate texture containing reflected scene
}
//-------------------------------------------------------------------------------------------------
/** Renders the reflected scene into an offscreen texture. */
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::renderMirror(CameraClass *cam)
{
#ifdef EXTENDED_STATS
if (DX8Wrapper::stats.m_disableWater) {
return;
}
#endif
Matrix3D OldCameraMatrix=cam->Get_Transform();
Matrix4 FullMatrix4(cam->Get_Transform()); //copy 3x4 matrix into a 4x4
Vector3 WaterNormal(0,0,1); //normal of plane used for reflection
Vector4 WaterPlane(WaterNormal.X,WaterNormal.Y,WaterNormal.Z,m_level);
Vector3 rRight,rUp,rN,rPos; //orientation and translation vectors of camera
Matrix4 FullMatrix(FullMatrix4.Transpose()); //swap rows/columns
//reflect camera right vector
Real axis_distance=Vector3::Dot_Product((Vector3&)FullMatrix[0],WaterNormal);
rRight = (Vector3&)FullMatrix[0] - (2.0f*axis_distance*WaterNormal);
//reflect camera up vector
axis_distance=Vector3::Dot_Product((Vector3&)FullMatrix[1],WaterNormal);
rUp = (Vector3&)FullMatrix[1] - (2.0f*axis_distance*WaterNormal);
//reflect camera n vector
axis_distance=Vector3::Dot_Product((Vector3&)FullMatrix[2],WaterNormal);
rN = (Vector3&)FullMatrix[2] - (2.0f*axis_distance*WaterNormal);
//reflect camera position
axis_distance=Vector3::Dot_Product((Vector3&)FullMatrix[3],WaterNormal); //distance cam to origin
axis_distance -= WaterPlane.W; // subtract mirror plane distance to get distance camera to plane
rPos = (Vector3&)FullMatrix[3] - (2.0f*axis_distance*WaterNormal);
//generate a new camera matrix from reflected vectors
Matrix3D reflectedTransform(rRight,rUp,rN,rPos);
DX8Wrapper::Set_Render_Target(m_pReflectionTexture);
// Clear the backbuffer
WW3D::Begin_Render(false,true,Vector3(0.0f,0.0f,0.0f)); //clearing only z-buffer since background always filled with clouds
cam->Set_Transform( reflectedTransform );
//Force reflected image to be drawn into full texture size - not a viewport inside texture.
Vector2 vMin,vMax,vOldMax,vOldMin;
cam->Get_Viewport(vOldMin,vOldMax);
vMax.X=vMax.Y=1.0f;
vMin.X=vMin.Y=0.0f;
cam->Set_Viewport(vMin,vMax);
cam->Apply(); //force an update of all the camera dependent parameters like frustum clip planes
//flip the winding order of polygons to draw the reflected back sides.
ShaderClass::Invert_Backface_Culling(true);
// Render the scene
renderSky();
if (m_tod == TIME_OF_DAY_NIGHT)
renderSkyBody(&reflectedTransform);
WW3D::Render(m_parentScene,cam);
cam->Set_Transform(OldCameraMatrix); //restore original non-reflected matrix
cam->Set_Viewport(vOldMin,vOldMax);
cam->Apply(); //force an update of all the camera dependent parameters like frustum clip planes
ShaderClass::Invert_Backface_Culling(false);
WW3D::End_Render(false);
// Change the rendertarget back to the main backbuffer
DX8Wrapper::Set_Render_Target((IDirect3DSurface8 *)NULL);
}
//-------------------------------------------------------------------------------------------------
/** Renders (draws) the water.
* Algorithm:
* Draw reflected scene.
* Draw reflected sky layer(s) and bodies.
* Clear Zbuffer
* Fill Zbuffer by drawing water surface (allows proper sorting into regular scene).
* Draw non-reflected scene (done in regular app render loop).
*
* This algorithm doesn't apply to translucent water, which is rendered into a
* texture and rendered at end of scene. */
//-------------------------------------------------------------------------------------------------
//DECLARE_PERF_TIMER(Water)
void WaterRenderObjClass::Render(RenderInfoClass & rinfo)
{
//USE_PERF_TIMER(Water)
if (TheTerrainRenderObject && !TheTerrainRenderObject->getMap())
return; //no map has been loaded yet.
if (((RTS3DScene *)rinfo.Camera.Get_User_Data())->getCustomPassMode() == SCENE_PASS_ALPHA_MASK ||
((SceneClass *)rinfo.Camera.Get_User_Data())->Get_Extra_Pass_Polygon_Mode() == SceneClass::EXTRA_PASS_CLEAR_LINE)
return; //water is not drawn in wireframe or custom scene passes
#ifdef EXTENDED_STATS
if (DX8Wrapper::stats.m_disableWater) {
return;
}
#endif
if (ShaderClass::Is_Backface_Culling_Inverted())
return; //the water object will not reflect in itself, so don't do anything if rendering a mirror.
//this water type needs to rendered after the rest of scene, so buffer it up for later
// If static sort lists are enabled and this mesh has a sort level, put it on the list instead
// of rendering it.
unsigned int sort_level = (unsigned int)Get_Sort_Level();
if (WW3D::Are_Static_Sort_Lists_Enabled() && sort_level != SORT_LEVEL_NONE)
{
WW3D::Add_To_Static_Sort_List(this, sort_level);
return;
}
switch(m_waterType)
{
case WATER_TYPE_0_TRANSLUCENT:
case WATER_TYPE_3_GRIDMESH:
//Draw the water surface as a bunch of alpha blended tiles covering areas where water is visible
renderWater();
if (!m_drawingRiver || m_disableRiver) {
renderWaterMesh(); //Draw water surface as 3D deforming mesh if it's enabled on this map.
}
break;
case WATER_TYPE_2_PVSHADER:
//Pixel/Vertex Shader based water which uses an off-screen rendered reflection texture
drawSea(rinfo); //draw water surface
break;
case WATER_TYPE_1_FB_REFLECTION:
{
//Normal frame buffer reflection water type. Non translucent. Legacy code we're not using anymore.
Matrix3D OldCameraMatrix=rinfo.Camera.Get_Transform();
Matrix4 FullMatrix4(rinfo.Camera.Get_Transform()); //copy 3x4 matrix into a 4x4
Vector3 WaterNormal(0,0,1); //normal of plane used for reflection
Vector4 WaterPlane(WaterNormal.X,WaterNormal.Y,WaterNormal.Z,m_level); //assume distance to origin 0
Vector3 rRight,rUp,rN,rPos; //orientation and translation vectors of camera
Matrix4 FullMatrix(FullMatrix4.Transpose()); //swap rows/columns
//reflect camera right vector
Real axis_distance=Vector3::Dot_Product((Vector3&)FullMatrix[0],WaterNormal);
rRight = (Vector3&)FullMatrix[0] - (2.0f*axis_distance*WaterNormal);
//reflect camera up vector
axis_distance=Vector3::Dot_Product((Vector3&)FullMatrix[1],WaterNormal);
rUp = (Vector3&)FullMatrix[1] - (2.0f*axis_distance*WaterNormal);
//reflect camera n vector
axis_distance=Vector3::Dot_Product((Vector3&)FullMatrix[2],WaterNormal);
rN = (Vector3&)FullMatrix[2] - (2.0f*axis_distance*WaterNormal);
//reflect camera position
axis_distance=Vector3::Dot_Product((Vector3&)FullMatrix[3],WaterNormal); //distance cam to origin
axis_distance -= WaterPlane.W; // subtract mirror plane distance to get distance camera to plane
rPos = (Vector3&)FullMatrix[3] - (2.0f*axis_distance*WaterNormal);
//generate a new camera matrix from reflected vectors
Matrix3D reflectedTransform(rRight,rUp,rN,rPos);
//flip the winding order of polygons to draw the reflected back sides.
ShaderClass::Invert_Backface_Culling(true);
#ifdef CLIP_GEOMETRY_TO_PLANE
// Set a clip plane, so that only objects above the water are reflected
WaterPlane.W *= -1.0f; //flip sign of plane distance for D3D use.
// DX8Wrapper::Set_DX8_Clip_Plane( 0, &WaterPlane.X );
// DX8Wrapper::Set_DX8_Render_State(D3DRS_CLIPPLANEENABLE, D3DCLIPPLANE0 ); //turn on first clip plane
// Alternate Clipping Method using alpha testing hack!
/**************************************************************************************/
D3DXMATRIX inv;
D3DXMATRIX clipMatrix;
Real det;
Matrix4 curView;
//get current view matrix
DX8Wrapper::_Get_DX8_Transform(D3DTS_VIEW, curView);
//get inverse of view matrix(= view to world matrix)
D3DXMatrixInverse(&inv, &det, (D3DXMATRIX*)&curView);
//create clipping matrix by inserting our plane equation into the 1st column
D3DXMatrixIdentity(&clipMatrix);
clipMatrix(0,0)=WaterNormal.X;
clipMatrix(1,0)=WaterNormal.Y;
clipMatrix(2,0)=WaterNormal.Z;
clipMatrix(3,0)=WaterPlane.W+0.5f;
inv *=clipMatrix;
// Change texture wrapping mode to 'clamp' for texture stage 1
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
// Use CameraSpace vertices as input to matrix and use texture wrap mode from stage 1
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION|1);
// Two output coordinates are used.
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
// Set texture generation matrix for stage 1
DX8Wrapper::_Set_DX8_Transform(D3DTS_TEXTURE1, *((Matrix4*)&inv));
// Disable bilinear filtering
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_MINFILTER, D3DTEXF_POINT);
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_MAGFILTER, D3DTEXF_POINT);
// Pass stage 0 texture data untouched(by modulating with white)
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE ); //stage 1 texture
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLORARG2, D3DTA_CURRENT ); //previous stage texture
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLOROP, D3DTOP_MODULATE ); //module with white => does nothing
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); //stage 1 texture
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); //previous stage texture
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); //modulate with clipping texture
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAREF,0x00);
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAFUNC,D3DCMP_NOTEQUAL); //pass pixels who's alpha is not zero
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, true); //test pixels if transparent(clipped) before rendering.
// Set clipping texture
m_alphaClippingTexture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
m_alphaClippingTexture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
m_alphaClippingTexture->Set_Min_Filter(TextureClass::FILTER_TYPE_NONE);
m_alphaClippingTexture->Set_Mag_Filter(TextureClass::FILTER_TYPE_NONE);
m_alphaClippingTexture->Set_Mip_Mapping(TextureClass::FILTER_TYPE_NONE);
DX8Wrapper::Set_Texture(0,m_alphaClippingTexture);
//TODO: Will have to make sure that the shader system is not resetting my stage 1 setup
//while rendering the scene
/*************************************************************************************/
#endif
#if 0 // No longer do simple rendering.
if (TheGlobalData->m_useWaterPlane)
{
//@todo : Would it be better to create a new camera or change the transform of the
//existing one?
rinfo.Camera.Set_Transform( reflectedTransform );
rinfo.Camera.Apply(); //force an update of all the camera dependent parameters like frustum clip planes
if(m_useCloudLayer)
{
if (TheGlobalData && TheGlobalData->m_drawEntireTerrain)
m_skyBox->Render(rinfo);
else
{
renderSky();
if (m_tod == TIME_OF_DAY_NIGHT)
renderSkyBody(&reflectedTransform);
}
}
WW3D::Render(m_parentScene,&rinfo.Camera);
rinfo.Camera.Set_Transform(OldCameraMatrix); //restore original non-reflected matrix
rinfo.Camera.Apply(); //force an update of all the camera dependent parameters like frustum clip planes
//clear the z-buffer to remove changes made by objects inside mirror
DX8Wrapper::Clear(false,true,Vector3(0.1f,0.1f,0.1f));
}
#endif
#ifdef CLIP_GEOMETRY_TO_PLANE
//restore default culling mode
// DX8Wrapper::Set_DX8_Render_State(D3DRS_CLIPPLANEENABLE, 0 ); //turn off first clip plane
//disable texture coordinate generation
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, false); //disable alpha testing
#endif
ShaderClass::Invert_Backface_Culling(false); //return culling back to normal
ShaderClass::Invalidate(); //reset shading system so it forces full state set.
renderWater();
} //WATER_TYPE_1
default:
break;
}//switch
if (TheGlobalData && TheGlobalData->m_drawSkyBox)
{ //center skybox around camera
Vector3 pos=rinfo.Camera.Get_Position();
pos.Z = TheGlobalData->m_skyBoxPositionZ;
m_skyBox->Set_Position(pos);
m_skyBox->Render(rinfo);
}
//Clean up after any pixel shaders.
//Force render state apply so that the "NULL" texture gets applied to D3D, thus releasing shroud reference count.
DX8Wrapper::Apply_Render_State_Changes();
DX8Wrapper::Invalidate_Cached_Render_States();
if (m_waterTrackSystem)
m_waterTrackSystem->flush(rinfo);
// renderWaterMesh();
// renderWaterWave();
}
//-------------------------------------------------------------------------------------------------
/** Clips the water plane to the current camera frustum and returns a bounding
* box enclosing the clipped plane. Returns false if water plane is not visible. */
//-------------------------------------------------------------------------------------------------
Bool WaterRenderObjClass::getClippedWaterPlane(CameraClass *cam, AABoxClass *box)
{
const FrustumClass & frustum = cam->Get_Frustum();
ClipPolyClass ClippedPoly0;
ClipPolyClass ClippedPoly1;
///@todo: generate proper sized polygon
ClippedPoly0.Reset();
ClippedPoly0.Add_Vertex(Vector3(0,0,m_level));
ClippedPoly0.Add_Vertex(Vector3(0,m_dy,m_level));
ClippedPoly0.Add_Vertex(Vector3(m_dx,m_dy,m_level));
ClippedPoly0.Add_Vertex(Vector3(m_dx,0,m_level));
//clip against all 6 frustum planes
ClippedPoly0.Clip(frustum.Planes[0],ClippedPoly1);
ClippedPoly1.Clip(frustum.Planes[1],ClippedPoly0);
ClippedPoly0.Clip(frustum.Planes[2],ClippedPoly1);
ClippedPoly1.Clip(frustum.Planes[3],ClippedPoly0);
ClippedPoly0.Clip(frustum.Planes[4],ClippedPoly1);
ClippedPoly1.Clip(frustum.Planes[5],ClippedPoly0);
Int final_vcount = ClippedPoly0.Verts.Count();
//make sure the polygon is visible
if (final_vcount >= 3)
{
//find axis aligned bounding box around visible polygon
if (box)
box->Init(&(ClippedPoly0.Verts[0]),final_vcount);
return TRUE;
}
return FALSE; //water plane is not visible
}
//-------------------------------------------------------------------------------------------------
/** Draws the water surface using a custom D3D vertex/pixel shader and a
* reflection texture. Only tested to work on GeForce3. */
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::drawSea(RenderInfoClass & rinfo)
{
AABoxClass seaBox;
if (!getClippedWaterPlane(&rinfo.Camera,&seaBox))
return; //the sea is not visible
D3DXMATRIX matProj, matView, matWW3D;
//create a transform which will flip the y and z coordinates to fit our system
memset(&matWW3D,0,sizeof(D3DMATRIX));
matWW3D._11=1.0f;
matWW3D._32=1.0f;
matWW3D._23=1.0f;
matWW3D._44=1.0f;
Matrix3D tm(Transform);
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm); //position the water surface
DX8Wrapper::Set_Texture(0,NULL); //we'll be setting our own textures, so reset W3D
DX8Wrapper::Set_Texture(1,NULL); //we'll be setting our own textures, so reset W3D
DX8Wrapper::Apply_Render_State_Changes(); //force update of view and projection matrices
Vector3 camTran;
rinfo.Camera.Get_Transform().Get_Translation(&camTran);
DX8Wrapper::_Get_DX8_Transform(D3DTS_VIEW, *(Matrix4*)&matView);
DX8Wrapper::_Get_DX8_Transform(D3DTS_PROJECTION, *(Matrix4*)&matProj);
//default setup from Kenny's demo
m_pDev->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pDev->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
m_pDev->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE);
m_pDev->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
m_pDev->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
m_pDev->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pDev->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
m_pDev->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE);
m_pDev->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
m_pDev->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 1 );
m_pDev->SetTextureStageState( 2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
m_pDev->SetTextureStageState( 2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU|2);
m_pDev->SetTextureStageState( 3, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
m_pDev->SetTextureStageState( 3, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU|3);
// m_pDev->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
// m_pDev->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
// m_pDev->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_POINT );
// m_pDev->SetTextureStageState( 1, D3DTSS_MINFILTER, D3DTEXF_POINT );
// m_pDev->SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_POINT );
// m_pDev->SetTextureStageState( 1, D3DTSS_MIPFILTER, D3DTEXF_NONE );
//end of default setup
m_pDev->SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
m_pDev->SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
m_pDev->SetRenderState( D3DRS_WRAP0, D3DWRAP_U | D3DWRAP_V);
m_pDev->SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
m_pDev->SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
m_pDev->SetTexture( 0, m_pBumpTexture[m_iBumpFrame]);
#ifdef MIPMAP_BUMP_TEXTURE
m_pDev->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_POINT );
m_pDev->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
#endif
m_pDev->SetTextureStageState( 1, D3DTSS_BUMPENVMAT00, F2DW(m_fBumpScale) );
m_pDev->SetTextureStageState( 1, D3DTSS_BUMPENVMAT01, F2DW(0.0f) );
m_pDev->SetTextureStageState( 1, D3DTSS_BUMPENVMAT10, F2DW(0.0f) );
m_pDev->SetTextureStageState( 1, D3DTSS_BUMPENVMAT11, F2DW(m_fBumpScale) );
m_pDev->SetTextureStageState( 1, D3DTSS_BUMPENVLSCALE, F2DW(1.0f) );
m_pDev->SetTextureStageState( 1, D3DTSS_BUMPENVLOFFSET, F2DW(0.0f) );
m_pDev->SetTextureStageState( 2, D3DTSS_COLOROP, D3DTOP_DISABLE );
m_pDev->SetTextureStageState( 2, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
m_pDev->SetRenderState(D3DRS_ZWRITEENABLE , FALSE);
D3DXMATRIX mat;
memset(&mat,0,sizeof(D3DXMATRIX));
mat._11 = 0.5f; mat._12 = -0.5f; mat._13 = 0.5f; mat._14=0.5f;
mat._21 = 0.5f; mat._22 = 0.5f; mat._23 = 0.0f; mat._24=0.0f;
mat._31 = 0.0f; mat._32 = 0.0f; mat._33 = 0.0f; mat._34=1.0f;
mat._41 = 0.0f; mat._42 = 0.0f; mat._43 = 0.0f; mat._44=1.0f;
m_pDev->SetVertexShaderConstant(CV_TEXPROJ_0, &mat, 4);
// Setup constants
m_pDev->SetVertexShaderConstant(CV_ZERO, D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f), 1);
m_pDev->SetVertexShaderConstant(CV_ONE, D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f), 1);
m_pDev->SetVertexShader(m_dwWaveVertexShader);
m_pDev->SetPixelShader(m_dwWavePixelShader);
// Make reflection brighter to compensate for darker coloring on sea floor
// m_pDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
// m_pDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR );
m_pDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
m_pDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE , TRUE);
m_pDev->SetTexture( 1, m_pReflectionTexture->Peek_DX8_Texture());
// m_pDev->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);//LORENZEN
Int patchX,patchY,startX,startY;
D3DXMATRIX patchMatrix;
memset(&patchMatrix,0,sizeof(D3DXMATRIX));
patchMatrix._11=PATCH_SCALE;
patchMatrix._22=1.0f;
patchMatrix._33=PATCH_SCALE;
patchMatrix._44=1.0f;
m_pDev->SetStreamSource(0,m_vertexBufferD3D,sizeof(WaterRenderObjClass::SEA_PATCH_VERTEX));
m_pDev->SetIndices(m_indexBufferD3D,0);
for (startY=patchY=(seaBox.Center.Y-seaBox.Extent.Y)/(PATCH_WIDTH*PATCH_SCALE); (patchY*PATCH_WIDTH*PATCH_SCALE)<(seaBox.Center.Y+seaBox.Extent.Y); patchY++)
{
for (startX=patchX=(seaBox.Center.X-seaBox.Extent.X)/(PATCH_WIDTH*PATCH_SCALE); (patchX*PATCH_WIDTH*PATCH_SCALE)<(seaBox.Center.X+seaBox.Extent.X); patchX++)
{
D3DXMATRIX matWorldViewProj, matTemp, matTempWorld;
patchMatrix._41=(float)(patchX*PATCH_WIDTH*PATCH_SCALE );
patchMatrix._43=(float)(patchY*PATCH_WIDTH*PATCH_SCALE );
//convert the default D3D coordinate system into ours
D3DXMatrixMultiply(&matTempWorld, &patchMatrix, &matWW3D);
D3DXMatrixMultiply(&matTemp, &matTempWorld, &matView);
D3DXMatrixMultiply(&matWorldViewProj, &matTemp, &matProj);
//matrices must be transposed before loading into vertex shader registers
D3DXMatrixTranspose(&matWorldViewProj, &matWorldViewProj);
m_pDev->SetVertexShaderConstant(CV_WORLDVIEWPROJ_0, &matWorldViewProj, 4); //pass transform matrix into shader
m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP,0,m_numVertices,0,m_numIndices);
}
}
// m_pDev->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE , FALSE);
m_pDev->SetTexture( 0, NULL); //release reference to bump texture
m_pDev->SetTexture( 1, NULL); //release reference to reflection texture
m_pDev->SetTexture( 2, NULL); //release reference to reflection texture
m_pDev->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
m_pDev->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU|0);
m_pDev->SetTextureStageState( 1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
m_pDev->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU|1);
m_pDev->SetRenderState(D3DRS_ZWRITEENABLE , TRUE);
m_pDev->SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
m_pDev->SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
m_pDev->SetRenderState( D3DRS_WRAP0, 0); //turn off texture wrapping
m_pDev->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_DISABLE );
m_pDev->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
m_pDev->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
m_pDev->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
m_pDev->SetTextureStageState( 2, D3DTSS_COLOROP, D3DTOP_DISABLE );
m_pDev->SetTextureStageState( 2, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
//Restore old transforms
DX8Wrapper::_Set_DX8_Transform(D3DTS_VIEW, *(Matrix4*)&matView);
DX8Wrapper::_Set_DX8_Transform(D3DTS_PROJECTION, *(Matrix4*)&matProj);
m_pDev->SetPixelShader(0); //turn off pixel shader
m_pDev->SetVertexShader(DX8_FVF_XYZDUV1); //turn off custom vertex shader
DX8Wrapper::Invalidate_Cached_Render_States();
if (TheTerrainRenderObject->getShroud())
{
//do second pass to apply the shroud on water plane
W3DShaderManager::setTexture(0,TheTerrainRenderObject->getShroud()->getShroudTexture());
W3DShaderManager::setShader(W3DShaderManager::ST_SHROUD_TEXTURE, 0);
m_pDev->SetStreamSource(0,m_vertexBufferD3D,sizeof(WaterRenderObjClass::SEA_PATCH_VERTEX));
m_pDev->SetIndices(m_indexBufferD3D,0);
for (startY=patchY=(seaBox.Center.Y-seaBox.Extent.Y)/(PATCH_WIDTH*PATCH_SCALE); (patchY*PATCH_WIDTH*PATCH_SCALE)<(seaBox.Center.Y+seaBox.Extent.Y); patchY++)
{
for (startX=patchX=(seaBox.Center.X-seaBox.Extent.X)/(PATCH_WIDTH*PATCH_SCALE); (patchX*PATCH_WIDTH*PATCH_SCALE)<(seaBox.Center.X+seaBox.Extent.X); patchX++)
{
D3DXMATRIX matTemp;
patchMatrix._41=(float)(patchX*PATCH_WIDTH*PATCH_SCALE);
patchMatrix._43=(float)(patchY*PATCH_WIDTH*PATCH_SCALE);
D3DXMatrixMultiply(&matTemp, &patchMatrix, &matWW3D);
DX8Wrapper::_Set_DX8_Transform(D3DTS_WORLD, *(Matrix4*)&matTemp);
m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP,0,m_numVertices,0,m_numIndices);
}
}
W3DShaderManager::resetShader(W3DShaderManager::ST_SHROUD_TEXTURE);
}
}
#define FEATHER_LAYER_COUNT (5.0f)
#define FEATHER_THICKNESS (4.0f)
//-------------------------------------------------------------------------------------------------
/** Renders (draws) the water surface.*/
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::renderWater(void)
{
for (PolygonTrigger *pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
if (pTrig->isWaterArea()) {
if (pTrig->getNumPoints()>2) {
if (pTrig->isRiver()) {
drawRiverWater(pTrig);
continue;
}
Int k;
for (k=1; kgetNumPoints()-1; k=k+2) {
ICoord3D pt3 = *pTrig->getPoint(0);
ICoord3D pt2 = *pTrig->getPoint(k);
ICoord3D pt1 = *pTrig->getPoint(k+1);
ICoord3D pt0 = *pTrig->getPoint(k+1);
if (k+2getNumPoints()) {
pt0 = *pTrig->getPoint(k+2);
}
Vector3 points[4];
points[0].Set(pt0.x, pt0.y, pt0.z);
points[1].Set(pt1.x, pt1.y, pt1.z);
points[2].Set(pt2.x, pt2.y, pt2.z);
points[3].Set(pt3.x, pt3.y, pt3.z);
if ( TheGlobalData->m_featherWater )
{
for (int r = 0; r < TheGlobalData->m_featherWater; ++r)
{
drawTrapezoidWater(points);
points[0].Z += (FEATHER_THICKNESS/TheGlobalData->m_featherWater);
}
}
else
drawTrapezoidWater(points);
}
}
}
}
}
//-------------------------------------------------------------------------------------------------
/** Renders (draws) the sky plane. Will apply current time-of-day settings including
* some simple UV scrolling animation. */
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::renderSky(void)
{
Int timeNow,timeDiff;
Real fu,fv;
Setting *setting=&m_settings[m_tod];
timeNow=timeGetTime();
timeDiff=timeNow-m_LastUpdateTime;
m_LastUpdateTime=timeNow;
m_uOffset += timeDiff * setting->uScrollPerMs * setting->skyTexelsPerUnit;
m_vOffset += timeDiff * setting->vScrollPerMs * setting->skyTexelsPerUnit;
//clamp uv coordinate into 0,1 range
m_uOffset = m_uOffset - (Real)((Int) m_uOffset);
m_vOffset = m_vOffset - (Real)((Int) m_vOffset);
fu= m_uOffset + (SKYPLANE_SIZE * 2) * setting->skyTexelsPerUnit;
fv= m_vOffset + (SKYPLANE_SIZE * 2) * setting->skyTexelsPerUnit;
VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
DX8Wrapper::Set_Material(vmat);
REF_PTR_RELEASE(vmat);
ShaderClass m_shader2=ShaderClass::_PresetOpaqueShader;
m_shader2.Set_Cull_Mode(ShaderClass::CULL_MODE_DISABLE);
m_shader2.Set_Depth_Compare(ShaderClass::PASS_ALWAYS); //no need to check against z-buffer, sky always rendered first.
m_shader2.Set_Depth_Mask(ShaderClass::DEPTH_WRITE_DISABLE); //sky is always behind everything so no need to update z-buffer
DX8Wrapper::Set_Shader(m_shader2);
DX8Wrapper::Set_Texture(0,setting->skyTexture);
//draw an infinite sky plane
DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,4);
{
DynamicVBAccessClass::WriteLockClass lock(&vb_access);
VertexFormatXYZNDUV2* verts=lock.Get_Formatted_Vertex_Array();
if(verts)
{
verts[0].x=-SKYPLANE_SIZE;
verts[0].y=SKYPLANE_SIZE;
verts[0].z=SKYPLANE_HEIGHT;
verts[0].u1=m_uOffset;
verts[0].v1=fv;
verts[0].diffuse=setting->vertex01Diffuse;
verts[1].x=SKYPLANE_SIZE;
verts[1].y=SKYPLANE_SIZE;
verts[1].z=SKYPLANE_HEIGHT;
verts[1].u1=fu;
verts[1].v1=fv;
verts[1].diffuse=setting->vertex11Diffuse;
verts[2].x=SKYPLANE_SIZE;
verts[2].y=-SKYPLANE_SIZE;
verts[2].z=SKYPLANE_HEIGHT;
verts[2].u1=fu;
verts[2].v1=m_vOffset;
verts[2].diffuse=setting->vertex10Diffuse;
verts[3].x=-SKYPLANE_SIZE;
verts[3].y=-SKYPLANE_SIZE;
verts[3].z=SKYPLANE_HEIGHT;
verts[3].u1=m_uOffset;
verts[3].v1=m_vOffset;
verts[3].diffuse=setting->vertex00Diffuse;
}
}
DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
DX8Wrapper::Set_Vertex_Buffer(vb_access);
Matrix3D tm(1);
tm.Set_Translation(Vector3(0,0,0));
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
DX8Wrapper::Draw_Triangles( 0,2, 0, 4); //draw a quad, 2 triangles, 4 verts
}
//-------------------------------------------------------------------------------------------------
/** Renders (draws) the sky body. Used for moon and sun. We rotate the image
* so that it always faces the camera. This removes perspective and helps hide that
* it's a flat image. */
//-------------------------------------------------------------------------------------------------
/// @todo: Add code to render properly sorted sun sky body.
void WaterRenderObjClass::renderSkyBody(Matrix3D *mat)
{
Vector3 cPos;
Vector3 pView,pRight,pUp,pPos(SKYBODY_X,SKYBODY_Y,SKYBODY_HEIGHT);
mat->Get_Translation(&cPos);
pView=cPos-pPos; //billboard to camera
pView.Normalize(); //particle view direction
Vector3 WorldUp(0,0,-1); ///@todo: hacked so only works for reflections across xy plane
#ifdef ALLOW_TEMPORARIES
Vector3 rotAxis=Vector3::Cross_Product(WorldUp,pView); //get axis of rotation.
rotAxis.Normalize();
#else
Vector3 rotAxis;
Vector3::Normalized_Cross_Product(WorldUp, pView, &rotAxis);
#endif
Real angle=Vector3::Dot_Product(WorldUp,pView);
angle = acos(angle);
Matrix3D tm(1);
tm.Set(rotAxis,angle);
tm.Adjust_Translation(Vector3(SKYBODY_X,SKYBODY_Y,SKYBODY_HEIGHT));
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
DX8Wrapper::Set_Material(vmat);
REF_PTR_RELEASE(vmat);
ShaderClass m_shader2=ShaderClass::_PresetAlphaShader;
m_shader2.Set_Cull_Mode(ShaderClass::CULL_MODE_DISABLE);
m_shader2.Set_Depth_Compare(ShaderClass::PASS_ALWAYS); //no need to check against z-buffer, sky always rendered first.
m_shader2.Set_Depth_Mask(ShaderClass::DEPTH_WRITE_DISABLE); //sky is always behind everything so no need to update z-buffer
DX8Wrapper::Set_Shader(m_shader2);
// DX8Wrapper::Set_Shader(ShaderClass::/*_PresetAdditiveShader*//*_PresetOpaqueShader*/_PresetAlphaShader);
// DX8Wrapper::Set_Texture(0,setting->skyBodyTexture);
DX8Wrapper::Set_Texture(0,m_alphaClippingTexture);
//draw an infinite sky plane
DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,4);
{
DynamicVBAccessClass::WriteLockClass lock(&vb_access);
VertexFormatXYZNDUV2* verts=lock.Get_Formatted_Vertex_Array();
if(verts)
{
verts[0].x=-SKYBODY_SIZE;
verts[0].y=SKYBODY_SIZE;
verts[0].z=0;
verts[0].u2=0;
verts[0].v2=1;
verts[0].diffuse=0xffffffff;
verts[1].x=SKYBODY_SIZE;
verts[1].y=SKYBODY_SIZE;
verts[1].z=0;
verts[1].u2=1;
verts[1].v2=1;
verts[1].diffuse=0xffffffff;
verts[2].x=SKYBODY_SIZE;
verts[2].y=-SKYBODY_SIZE;
verts[2].z=0;
verts[2].u2=1;
verts[2].v2=0;
verts[2].diffuse=0xffffffff;
verts[3].x=-SKYBODY_SIZE;
verts[3].y=-SKYBODY_SIZE;
verts[3].z=0;
verts[3].u2=0;
verts[3].v2=0;
verts[3].diffuse=0xffffffff;
}
}
DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
DX8Wrapper::Set_Vertex_Buffer(vb_access);
DX8Wrapper::Draw_Triangles( 0,2, 0, 4); //draw a quad, 2 triangles, 4 verts
}
//Defines for procedural water animation.
#define WATER_FREQ (2.0*3.2831/4.0) //2pi (full cycle) cover 4 units
#define WATER_AMP (1.0f)
#define WATER_OFFSET (0.1f)
//-------------------------------------------------------------------------------------------------
/** Renders (draws) the water surface mesh geometry.
* This is a work-in-progress! Do not use this code! */
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::renderWaterMesh(void)
{
if (!m_doWaterGrid)
return; //the water grid is disabled.
//According to Nvidia there's a D3D bug that happens if you don't start with a
//new dynamic VB each frame - so we force a DISCARD by overflowing the counter.
m_vertexBufferD3DOffset = 0xffff;
Setting *setting=&m_settings[m_tod];
WaterMeshData *pData;
Int mx=m_gridCellsX+1;
Int my=m_gridCellsY+1;
Int i,j;
Real cellSizeX=m_gridCellSize;
Real cellSizeY=m_gridCellSize;
// Real uScale2=5.0f*setting->waterRepeatCount/(128.0f)*cellSizeX/10.0f;
// Real vScale2=5.0f*setting->waterRepeatCount/(128.0f)*cellSizeY/10.0f;
//Old waterRepeatCount settings in INI were based on 128x128 water grid of cellsize=10
//Scale values to correct size.
Real uScale=setting->waterRepeatCount/(128.0f)*cellSizeX/10.0f*0.2f;
Real vScale=setting->waterRepeatCount/(128.0f)*cellSizeY/10.0f*0.2f;
Vector3 nx(cellSizeX*2.0f,0,0);
Vector3 ny(0,cellSizeY*2.0f,0);
Vector3 C;
#ifdef DO_WATER_SIMULATION //Debug code used to create a dummy water animation
//
// Mark: If you re-enable this water simulation, you might want to consider moving
// this code to the update() method of the water render object (Colin)
//
static Real PhasePerFrameX=0.1f;
static Real PhasePerFrameY=0.1f;
//update the mesh heights for this frame (update buffer is 2 samples wider/taller due to border)
for (j=0,pData=m_meshData; j<(my+2); j++)
{
for (i=0; i<(mx+2); i++)
{
//*pData = WATER_AMP * sin(WATER_FREQ*(0.7f*i + 0.7f*j) - PhasePerFrame);
pData->height=WATER_OFFSET+WATER_AMP*(sin((float)i*WATER_FREQ*0.4+PhasePerFrameX*0.5)+sin((float)i*WATER_FREQ*0.6+PhasePerFrameX*0.2)+sin((float)j*WATER_FREQ+PhasePerFrameX)+sin((float)j*WATER_FREQ*0.7+PhasePerFrameX*0.3));
// *pData=WATER_OFFSET+WATER_AMP*(sin((float)i*WATER_FREQ*0.4+PhasePerFrameX*0.5)+sin((float)i*WATER_FREQ*0.6+PhasePerFrameX*0.2)+sin((float)j*WATER_FREQ+PhasePerFrameX)+sin((float)j*WATER_FREQ*0.7+PhasePerFrameX*0.3));
pData++;
}
}
PhasePerFrameX -= 0.08f;
PhasePerFrameY -= 0.1f;
#endif
MaterMeshVertexFormat *vb;
if (m_vertexBufferD3DOffset < m_numVertices)
{ //we have room in current VB, append new verts
if(m_vertexBufferD3D->Lock(m_vertexBufferD3DOffset*sizeof(MaterMeshVertexFormat),mx*my*sizeof(MaterMeshVertexFormat),(unsigned char**)&vb,D3DLOCK_NOOVERWRITE) != D3D_OK)
return;
}
else
{ //ran out of room in last VB, request a substitute VB.
if(m_vertexBufferD3D->Lock(0,mx*my*sizeof(MaterMeshVertexFormat),(unsigned char**)&vb,D3DLOCK_DISCARD) != D3D_OK)
return;
m_vertexBufferD3DOffset=0; //reset start of page to first vertex
}
Int diffuse;
diffuse = setting->waterDiffuse&0x00ffffff;
Int alpha = (setting->waterDiffuse & 0xff000000)>>24;
// Reduce alpha for wave mesh
alpha -= 0x20;
diffuse |= alpha<<24;
//I pulled some of these constants out of the loops for speed:
Real uvCosScale=0.02*cos(3*m_riverVOrigin);
Real sinOffset=25*m_riverVOrigin;
Real originScale=m_riverVOrigin/vScale;
Real bumpSizeDiv=cellSizeY/BUMP_SIZE;
Real bumpSizeDiv2=0.3f*cellSizeY/BUMP_SIZE;
//Data has a 1 vertex padding all around it so we don't need to special-case edges. Improves performance
for (j=0,pData=m_meshData+mx+2+1; jheight - (pData-1)->height;
ny.Z=(pData+mx+2)->height - (pData-mx-2)->height;
// nx.Z=*(pData+1)-*(pData-1);
// ny.Z=*(pData+mx+2)-*(pData-mx-2);
Vector3::Cross_Product(nx,ny,&C);
C.Normalize();
vb->nx = C.X;
vb->ny = C.X;
vb->nz = C.X;
#endif
Real x = (float)i*cellSizeX;
vb->x= x;
vb->y= y;
vb->z= pData->height;//WATER_OFFSET+WATER_AMP*(sin((float)i*WATER_FREQ+PhasePerFrame)+cos((float)j*WATER_FREQ+PhasePerFrame));
vb->diffuse = diffuse;
#ifdef SCROLL_UV
// vb->diffuse=0x80ffffff;
vb->u1=(float)i*uScale;
vb->v1=v1Offset;
//old slow version
//vb->v1=m_riverVOrigin+(float)j*vScale + 0.02*cos(3*m_riverVOrigin)*sin(25*m_riverVOrigin+y*PI/(8*MAP_XY_FACTOR));
// vb->u2=m_initialGridU2+(float)i*uScale2;
// vb->v2=m_initialGridV2+(float)j*vScale2;
#else
vb->u1=(float)i*uScale;
vb->v1=(float)j*vScale;
#endif
vb->u2=(float)(i)*cellSizeX/BUMP_SIZE;
vb->v2=v2Offset;
//old slow code
//vb->v2=(float)(j+m_riverVOrigin/vScale )*cellSizeY/BUMP_SIZE+ 0.3f*(float)j*cellSizeY/BUMP_SIZE;
vb++;
pData++;
}
}
m_vertexBufferD3D->Unlock();
Matrix3D tm(Transform);
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm); //position the water surface
DX8Wrapper::Set_Material(m_meshVertexMaterialClass);
ShaderClass::CullModeType oldCullMode=m_shaderClass.Get_Cull_Mode();
ShaderClass::DepthMaskType oldDepthMask=m_shaderClass.Get_Depth_Mask();
m_shaderClass.Set_Depth_Mask(ShaderClass::DEPTH_WRITE_DISABLE); //disable writing to z-buffer to prevent particle clipping.
m_shaderClass.Set_Cull_Mode(ShaderClass::CULL_MODE_ENABLE); //water should be visible from both sides
DX8Wrapper::Set_Shader(m_shaderClass);
#if 1
setupFlatWaterShader();
#else
//DX8Wrapper::Set_Shader(ShaderClass::_PresetOpaqueShader);
DX8Wrapper::Set_Texture(0,setting->waterTexture);
DX8Wrapper::Set_Texture(1,setting->waterTexture);
DX8Wrapper::Set_Light(0,*m_meshLight);
DX8Wrapper::Set_Light(1,NULL);
DX8Wrapper::Set_Light(2,NULL);
DX8Wrapper::Set_Light(3,NULL);
/*
DX8Wrapper::Set_DX8_Render_State(D3DRS_AMBIENT,0); //turn off scene ambient
DX8Wrapper::Set_DX8_Render_State(D3DRS_SPECULARENABLE,TRUE);
DX8Wrapper::Set_DX8_Render_State(D3DRS_LOCALVIEWER,TRUE);
*/
DX8Wrapper::Apply_Render_State_Changes(); //force update of view and projection matrices
#endif
// m_pDev->SetRenderState(D3DRS_ZFUNC,D3DCMP_ALWAYS); //used to display grid under map.
m_pDev->SetIndices(m_indexBufferD3D,m_vertexBufferD3DOffset);
m_pDev->SetStreamSource(0,m_vertexBufferD3D,sizeof(MaterMeshVertexFormat));
m_pDev->SetVertexShader(WATER_MESH_FVF);
if (TheTerrainRenderObject->getShroud() && !m_trapezoidWaterPixelShader)
{ //we have a shroud to apply and can't do it inside the pixel shader.
//so do it in stage1
W3DShaderManager::setTexture(0,TheTerrainRenderObject->getShroud()->getShroudTexture());
W3DShaderManager::setShader(W3DShaderManager::ST_SHROUD_TEXTURE, 1);
//modulate with shroud texture
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE ); //stage 1 texture
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLORARG2, D3DTA_CURRENT ); //previous stage texture
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLOROP, D3DTOP_MODULATE );
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
//Shroud shader uses z-compare of EQUAL which wouldn't work on water because it doesn't
//write to the zbuffer. Change to LESSEQUAL.
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP,0,mx*my,0,m_numIndices-2);
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ZFUNC, D3DCMP_EQUAL);
W3DShaderManager::resetShader(W3DShaderManager::ST_SHROUD_TEXTURE);
}
else
m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP,0,mx*my,0,m_numIndices-2);
Debug_Statistics::Record_DX8_Polys_And_Vertices(m_numIndices-2,mx*my,ShaderClass::_PresetOpaqueShader);
// m_pDev->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
if (m_trapezoidWaterPixelShader) DX8Wrapper::_Get_D3D_Device8()->SetPixelShader(NULL);
m_vertexBufferD3DOffset += mx*my; //advance past vertices already in buffer
DX8Wrapper::Set_Texture(0,NULL);
DX8Wrapper::Set_Texture(1,NULL);
ShaderClass::Invalidate();
m_shaderClass.Set_Cull_Mode(oldCullMode); //water should be visible from both sides
// restore shader to old mask
m_shaderClass.Set_Depth_Mask(oldDepthMask);
//W3DShaderManager::resetShader(W3DShaderManager::ST_SHROUD_TEXTURE);
}
inline void WaterRenderObjClass::setGridVertexHeight(Int x, Int y, Real value)
{
DEBUG_ASSERTCRASH( x < (m_gridCellsX+1) && y < (m_gridCellsY+1), ("Invalid Water Mesh Coordinates\n") );
if (m_meshData)
{
m_meshData[(y+1)*(m_gridCellsX+1+2)+x+1].height = value;
}
}
void WaterRenderObjClass::setGridHeightClamps(Real minz, Real maxz)
{
m_minGridHeight = minz;
m_maxGridHeight = maxz;
}
void WaterRenderObjClass::addVelocity( Real worldX, Real worldY,
Real zVelocity, Real preferredHeight )
{
if( m_doWaterGrid)
{
Real gx,gy;
Real minX,maxX,minY,maxY;
Int x,y;
WaterMeshData *meshPoint;
m_disableRiver = true;
//check if center falls within grid bounds
if (worldToGridSpace(worldX, worldY, gx, gy))
{
//find extents of influence
minX = floorf(gx - m_gridChangeMaxRange);
if (minX < 0 )
minX = 0; //clamp extent to fall within box
maxX = ceilf(gx + m_gridChangeMaxRange);
if (maxX > m_gridCellsX)
maxX = m_gridCellsX; //clamp extent to fall within box
minY = floorf(gy - m_gridChangeMaxRange);
if (minY < 0 )
minY = 0; //clamp extent to fall within box
maxY = ceilf(gy + m_gridChangeMaxRange);
if (maxY > m_gridCellsY)
maxY = m_gridCellsY; //clamp extent to fall within box
for (y=minY; y<=maxY; y++)
{
for (x=minX; x<=maxX; x++)
{
// get the mesh point that we're concerned with
meshPoint = &m_meshData[ (y + 1) * (m_gridCellsX + 1 + 2) + x + 1 ];
// we now have a new preferred height
meshPoint->preferredHeight = preferredHeight;
//
// set the velocity of this point based on the distance from the center of the
// "core" point for this call
//
meshPoint->velocity = meshPoint->velocity + zVelocity;
// this point is now "in motion"
BitSet( meshPoint->status, WaterRenderObjClass::IN_MOTION );
}
}
//
// the mesh data is now dirty, we need to pass through the velocity field
// during an update phase to update the positions
//
m_meshInMotion = TRUE;
}
} // end if, water type is 3
}
void WaterRenderObjClass::changeGridHeight(Real wx, Real wy, Real delta)
{
Real gx,gy;
Real *oldData;
Real newData;
Real distance;
Real minX,maxX,minY,maxY;
Int x,y;
//check if center falls within grid bounds
if (worldToGridSpace(wx, wy, gx, gy))
{ //find extents of influence
minX = floorf(gx - m_gridChangeMaxRange);
if (minX < 0 )
minX = 0; //clamp extent to fall within box
maxX = ceilf(gx + m_gridChangeMaxRange);
if (maxX > m_gridCellsX)
maxX = m_gridCellsX; //clamp extent to fall within box
minY = floorf(gy - m_gridChangeMaxRange);
if (minY < 0 )
minY = 0; //clamp extent to fall within box
maxY = ceilf(gy + m_gridChangeMaxRange);
if (maxY > m_gridCellsY)
maxY = m_gridCellsY; //clamp extent to fall within box
for (y=minY; y<=maxY; y++)
{
for (x=minX; x<=maxX; x++)
{ oldData = &m_meshData[(y+1)*(m_gridCellsX+1+2)+x+1].height;
distance = (gx - (Real)x)*(gx - (Real)x) + (gy - (Real)y)*(gy - (Real)y);
distance = sqrt(distance);
newData = *oldData + 1.0f/(m_gridChangeAtt0+m_gridChangeAtt1*distance+distance*distance*m_gridChangeAtt2)*delta;
//Clamp to min/max values
if (newData < m_minGridHeight)
newData = m_minGridHeight;
if (newData > m_maxGridHeight)
newData = m_maxGridHeight;
*oldData = newData;
}
}
}
}
void WaterRenderObjClass::setGridChangeAttenuationFactors(Real a, Real b, Real c, Real range)
{
m_gridChangeAtt0 = a;
m_gridChangeAtt1 = b;
m_gridChangeAtt2 = c;
m_gridChangeMaxRange = range/m_gridCellSize; //convert range to grid space
}
void WaterRenderObjClass::setGridTransform(Real angle, Real x, Real y, Real z)
{
m_gridDirectionX = Vector2(1.0f,0.0f);
m_gridOrigin.X = x;
m_gridOrigin.Y = y;
Matrix3D xform(1);
xform.Rotate_Z(angle);
m_gridDirectionX.X = xform.Get_X_Vector().X;
m_gridDirectionX.Y = xform.Get_X_Vector().Y;
m_gridDirectionY.X = xform.Get_Y_Vector().X;
m_gridDirectionY.Y = xform.Get_Y_Vector().Y;
xform.Set_Translation(Vector3(x,y,z));
Set_Transform(xform);
}
void WaterRenderObjClass::setGridTransform(const Matrix3D *transform )
{
if( transform )
Set_Transform( *transform );
}
void WaterRenderObjClass::getGridTransform(Matrix3D *transform )
{
if( transform )
*transform = Get_Transform();
}
void WaterRenderObjClass::setGridResolution(Real gridCellsX, Real gridCellsY, Real cellSize)
{
m_gridCellSize=cellSize;
if (m_gridCellsX != gridCellsX || m_gridCellsY != m_gridCellsY)
{ //resolutoin has changed
m_gridCellsX=gridCellsX;
m_gridCellsY=gridCellsY;
if (m_meshData)
{
delete [] m_meshData;//free previously allocated grid and allocate new size
m_meshData = NULL; // must set to NULL so that we properly re-allocate
m_meshDataSize = 0;
Bool enable = m_doWaterGrid;
enableWaterGrid(true); // allocates buffers.
m_doWaterGrid = enable;
}
}
}
void WaterRenderObjClass::getGridResolution( Real *gridCellsX, Real *gridCellsY, Real *cellSize )
{
if( gridCellsX )
*gridCellsX = m_gridCellsX;
if( gridCellsY )
*gridCellsY = m_gridCellsY;
if( cellSize )
*cellSize = m_gridCellSize;
}
static Real wobble(Real baseV, Real offset, Bool wobble)
{
if (!wobble) return 0;
offset = sin(2*PI*baseV - 3*offset);
return offset/22;
}
/**Utility function used to query water heights in a manner that works in both RTS and WB.*/
Real WaterRenderObjClass::getWaterHeight(Real x, Real y)
{
const WaterHandle *waterHandle = NULL;
Real waterZ = 0.0f;
ICoord3D iLoc;
iLoc.x = REAL_TO_INT_FLOOR( x + 0.5f );
iLoc.y = REAL_TO_INT_FLOOR( y + 0.5f );
iLoc.z = 0;
for( PolygonTrigger *pTrig = PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext() )
{
if( !pTrig->isWaterArea() )
continue;
// See if point is in a water area
if( pTrig->pointInTrigger( iLoc ) )
{
if( pTrig->getPoint( 0 )->z >= waterZ )
{
waterZ = pTrig->getPoint( 0 )->z;
waterHandle = pTrig->getWaterHandle();
} // end if
} // end if
} // end for
if (waterHandle)
return waterHandle->m_polygon->getPoint( 0 )->z;
return INVALID_WATER_HEIGHT; //point not underwater
}
//-------------------------------------------------------------------------------------------------
//Draw a many sided river polygon.
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::drawRiverWater(PolygonTrigger *pTrig)
{
DX8Wrapper::Invalidate_Cached_Render_States(); ///@todo: Figure out why rivers don't draw without reset of all states.
Int rectangleCount = pTrig->getNumPoints()/2;
rectangleCount--;
Real bumpFactor = 5;
static Bool doWobble = true;
if (m_disableRiver) return;
m_drawingRiver = true;
//allocate 2 triangles per side with 3 indices per triangle
DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8,(rectangleCount+1)*2*3);
{
DynamicIBAccessClass::WriteLockClass lockib(&ib_access);
UnsignedShort *curIb = lockib.Get_Index_Array();
for (Int i=0; im_terrainAmbient[0].red;
shadeG = TheGlobalData->m_terrainAmbient[0].green;
shadeB = TheGlobalData->m_terrainAmbient[0].blue;
//Add in diffuse lighting from each terrain light
for (Int lightIndex=0; lightIndex < TheGlobalData->m_numGlobalLights; lightIndex++)
{
if (-TheGlobalData->m_terrainLightPos[lightIndex].z > 0)
{ shadeR += -TheGlobalData->m_terrainLightPos[lightIndex].z * TheGlobalData->m_terrainDiffuse[lightIndex].red;
shadeG += -TheGlobalData->m_terrainLightPos[lightIndex].z * TheGlobalData->m_terrainDiffuse[lightIndex].green;
shadeB += -TheGlobalData->m_terrainLightPos[lightIndex].z * TheGlobalData->m_terrainDiffuse[lightIndex].blue;
}
}
//Get water material colors
Real waterShadeR = (m_settings[m_tod].waterDiffuse & 0xff) / 255.0f;
Real waterShadeG = ((m_settings[m_tod].waterDiffuse >> 8) & 0xff) / 255.0f;
Real waterShadeB = ((m_settings[m_tod].waterDiffuse >> 16) & 0xff) / 255.0f;
shadeR=shadeR*waterShadeR*255.0f;
shadeG=shadeG*waterShadeG*255.0f;
shadeB=shadeB*waterShadeB*255.0f;
Int diffuse=REAL_TO_INT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16);
//Keep diffuse from lighting calculations but substitute custom alpha
diffuse |= m_settings[m_tod].waterDiffuse & 0xff000000; //copy alpha/opacity from ini setting
Int innerNdx = pTrig->getRiverStart();
Int outerNdx = innerNdx+1;
Real endLen=0;
Real totalLen=0;
Int i;
for (i=0; igetNumPoints()-1; i++) {
ICoord3D innerPt = *pTrig->getPoint(i);
ICoord3D outerPt = *pTrig->getPoint(i+1);
Real dx = innerPt.x-outerPt.x;
Real dy = innerPt.y-outerPt.y;
Real curLen = sqrt(dx*dx+dy*dy);
totalLen += curLen;
if ( i==innerNdx) {
endLen = curLen;
}
}
bumpFactor = endLen/BUMP_SIZE;
Real lengthOfRiver = (totalLen/2)-endLen;
Real repeatCount = lengthOfRiver / (endLen);
Real vScale=(Real)repeatCount/(Real)rectangleCount;
#define HEIGHT_TO_USE (0.5f)
if (innerNdx >= pTrig->getNumPoints()-1) return;
//allocate 2 vertices per side
DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,(rectangleCount+1)*2);
{
DynamicVBAccessClass::WriteLockClass lock(&vb_access);
VertexFormatXYZNDUV2* vb=lock.Get_Formatted_Vertex_Array();
Real constA=3*m_riverVOrigin;
for (i=0; i<(pTrig->getNumPoints()/2); i++)
{
Real x,y;
ICoord3D innerPt = *pTrig->getPoint(outerNdx);
ICoord3D outerPt = *pTrig->getPoint(innerNdx);
outerNdx++;
innerNdx--;
if (innerNdx<0) {
innerNdx = pTrig->getNumPoints()-1;
}
if (outerNdx >= pTrig->getNumPoints()) {
outerNdx = 0;
}
x=innerPt.x;
y=innerPt.y;
vb->x=x;
vb->y=y;
vb->z=innerPt.z;
vb->diffuse= diffuse;
Real wobbleConst=-m_riverVOrigin+vScale*(Real)i + WWMath::Fast_Sin(2*PI*(vScale*(Real)i) - constA)/22.0f;
//old slower version
//vb->v1=-m_riverVOrigin+vScale*(Real)i + wobble(vScale*i, m_riverVOrigin, doWobble);
vb->v1=wobbleConst;
vb->u1=HEIGHT_TO_USE ;
//old slower version
//vb->v2 = -m_riverVOrigin+vScale*(Real)i + wobble(vScale*i, m_riverVOrigin, doWobble);
vb->v2=wobbleConst;
vb->u2 = 1.0f;
vb->nx = 0;
vb->ny = 0;
vb->nz = 1.0f;
vb++;
x=outerPt.x;
y=outerPt.y;
vb->x=x;
vb->y=y;
vb->z=outerPt.z;
vb->diffuse= diffuse;
//old slower version
//vb->v1=-m_riverVOrigin+vScale*(Real)i + wobble(vScale*i, m_riverVOrigin, doWobble);
vb->v1=wobbleConst;
vb->u1=0;
//old slower version
//vb->v2 = -m_riverVOrigin+vScale*(Real)i + wobble(vScale*i, m_riverVOrigin, doWobble);
vb->v2 =wobbleConst;
vb->u2 = 0;
vb->nx = 0;
vb->ny = 0;
vb->nz = 1.0f;
vb++;
}
}
Matrix3D tm(1);
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm); //position the water surface
DX8Wrapper::Set_Index_Buffer(ib_access,0);
DX8Wrapper::Set_Vertex_Buffer(vb_access);
DX8Wrapper::Set_Texture(0,m_riverTexture); //set to white
setupJbaWaterShader();
if (m_riverWaterPixelShader) DX8Wrapper::_Get_D3D_Device8()->SetPixelShader(m_riverWaterPixelShader);
DWORD cull;
DX8Wrapper::_Get_D3D_Device8()->GetRenderState(D3DRS_CULLMODE, &cull);
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
if (wireframeForDebug) {
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
}
DX8Wrapper::Draw_Triangles( 0,rectangleCount*2, 0, (rectangleCount+1)*2);
if (wireframeForDebug) {
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
}
if (m_riverWaterPixelShader) DX8Wrapper::_Get_D3D_Device8()->SetPixelShader(NULL);
//do second pass to apply the shroud on water plane
if (TheTerrainRenderObject->getShroud())
{
W3DShaderManager::setTexture(0,TheTerrainRenderObject->getShroud()->getShroudTexture());
W3DShaderManager::setShader(W3DShaderManager::ST_SHROUD_TEXTURE, 0);
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
//Shroud shader uses z-compare of EQUAL which wouldn't work on water because it doesn't
//write to the zbuffer. Change to LESSEQUAL.
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
DX8Wrapper::Draw_Triangles( 0,rectangleCount*2, 0, (rectangleCount+1)*2);
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ZFUNC, D3DCMP_EQUAL);
W3DShaderManager::resetShader(W3DShaderManager::ST_SHROUD_TEXTURE);
}
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_CULLMODE, cull);
}
void WaterRenderObjClass::setupFlatWaterShader(void)
{
//Setup shroud to render in same pass as water
if (m_trapezoidWaterPixelShader)
{ if (TheTerrainRenderObject->getShroud())
{
W3DShaderManager::setTexture(0,TheTerrainRenderObject->getShroud()->getShroudTexture());
//Use stage 3 to apply the shroud
W3DShaderManager::setShader(W3DShaderManager::ST_SHROUD_TEXTURE, 3);
m_pDev->SetTextureStageState( 3, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 3, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 3, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
m_pDev->SetTextureStageState( 3, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
//Shroud shader uses z-compare of EQUAL which wouldn't work on water because it doesn't
//write to the zbuffer. Change to LESSEQUAL.
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
}
else
{ //Assume no shroud, so stage 3 will be "NULL" texture but using actual white because
//pixel shader on GF4 generates random colors with SetTexture(3,NULL).
DX8Wrapper::_Get_D3D_Device8()->SetTexture(3,m_whiteTexture->Peek_DX8_Texture());
}
}
DX8Wrapper::Set_Texture(0,m_riverTexture);
DX8Wrapper::Set_Shader(ShaderClass::_PresetAlphaShader);
VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
DX8Wrapper::Set_Material(vmat);
REF_PTR_RELEASE(vmat);
m_riverTexture->Set_Mag_Filter(TextureClass::FILTER_TYPE_BEST);
m_riverTexture->Set_Min_Filter(TextureClass::FILTER_TYPE_BEST);
m_riverTexture->Set_Mip_Mapping(TextureClass::FILTER_TYPE_BEST);
DX8Wrapper::Apply_Render_State_Changes(); //force update of view and projection matrices
DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_ALPHAOP, D3DTOP_ADD );
DX8Wrapper::Set_DX8_Texture_Stage_State(0, D3DTSS_TEXCOORDINDEX, 0);
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_TEXCOORDINDEX, 0);
Bool doSparkles = true;
if (m_trapezoidWaterPixelShader && doSparkles) {
DX8Wrapper::_Get_D3D_Device8()->SetTexture(1,m_waterSparklesTexture->Peek_DX8_Texture());
DX8Wrapper::_Get_D3D_Device8()->SetTexture(2,m_waterNoiseTexture->Peek_DX8_Texture());
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
DX8Wrapper::Set_DX8_Texture_Stage_State(2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
// Two output coordinates are used.
DX8Wrapper::Set_DX8_Texture_Stage_State(2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
DX8Wrapper::Set_DX8_Texture_Stage_State(2, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
DX8Wrapper::Set_DX8_Texture_Stage_State(2, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
D3DXMATRIX inv;
float det;
Matrix4 curView;
DX8Wrapper::_Get_DX8_Transform(D3DTS_VIEW, curView);
D3DXMatrixInverse(&inv, &det, (D3DXMATRIX*)&curView);
D3DXMATRIX scale;
D3DXMatrixScaling(&scale, NOISE_REPEAT_FACTOR, NOISE_REPEAT_FACTOR,1);
D3DXMATRIX destMatrix = inv * scale;
D3DXMatrixTranslation(&scale, m_riverVOrigin, m_riverVOrigin,0);
destMatrix = destMatrix*scale;
DX8Wrapper::_Set_DX8_Transform(D3DTS_TEXTURE2, *(Matrix4*)&destMatrix);
}
m_pDev->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 1, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 2, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pDev->SetTextureStageState( 2, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
if (m_trapezoidWaterPixelShader){
DX8Wrapper::_Get_D3D_Device8()->SetPixelShaderConstant(0, D3DXVECTOR4(REFLECTION_FACTOR, REFLECTION_FACTOR, REFLECTION_FACTOR, 1.0f), 1);
DX8Wrapper::_Get_D3D_Device8()->SetPixelShader(m_trapezoidWaterPixelShader);
}
}
//-------------------------------------------------------------------------------------------------
//Draw a 4 sided flat water area.
//-------------------------------------------------------------------------------------------------
void WaterRenderObjClass::drawTrapezoidWater(Vector3 points[4])
{
Vector3 origin(points[0]);
Vector3 uVec1(points[1]);
Vector3 vVec1(points[3]);
Vector3 uVec2(points[2]);
Vector3 vVec2(points[2]);
uVec2 -= vVec1;
vVec2 -= uVec1;
uVec1 -= origin;
vVec1 -= origin;
Int uCount = (uVec1.Length()+uVec2.Length()) / (8*MAP_XY_FACTOR);
if (uCount<1) uCount = 1;
Int vCount = (vVec1.Length()+vVec2.Length()) / (8*MAP_XY_FACTOR);
if (vCount<1) vCount = 1;
if (uCount>50) uCount = 50;
if (vCount>50) vCount = 50;
static Bool doWobble = true;
Int rectangleCount = uCount*vCount;
uCount++;
vCount++;
Int i, j;
//allocate 2 triangles per side with 3 indices per triangle
DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8,(rectangleCount+1)*2*3);
{
DynamicIBAccessClass::WriteLockClass lockib(&ib_access);
UnsignedShort *curIb = lockib.Get_Index_Array();
for (j=0; jm_terrainAmbient[0].red;
shadeG = TheGlobalData->m_terrainAmbient[0].green;
shadeB = TheGlobalData->m_terrainAmbient[0].blue;
//Add in diffuse lighting from each terrain light
for (Int lightIndex=0; lightIndex < TheGlobalData->m_numGlobalLights; lightIndex++)
{
if (-TheGlobalData->m_terrainLightPos[lightIndex].z > 0)
{ shadeR += -TheGlobalData->m_terrainLightPos[lightIndex].z * TheGlobalData->m_terrainDiffuse[lightIndex].red;
shadeG += -TheGlobalData->m_terrainLightPos[lightIndex].z * TheGlobalData->m_terrainDiffuse[lightIndex].green;
shadeB += -TheGlobalData->m_terrainLightPos[lightIndex].z * TheGlobalData->m_terrainDiffuse[lightIndex].blue;
}
}
//Get water material colors
Real waterShadeR = (m_settings[m_tod].waterDiffuse & 0xff) / 255.0f;
Real waterShadeG = ((m_settings[m_tod].waterDiffuse >> 8) & 0xff) / 255.0f;
Real waterShadeB = ((m_settings[m_tod].waterDiffuse >> 16) & 0xff) / 255.0f;
shadeR=shadeR*waterShadeR*255.0f;
shadeG=shadeG*waterShadeG*255.0f;
shadeB=shadeB*waterShadeB*255.0f;
Int diffuse=REAL_TO_INT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16);
//Keep diffuse from lighting calculations but substitute custom alpha
diffuse |= m_settings[m_tod].waterDiffuse & 0xff000000; //copy alpha/opacity from ini setting
DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,(rectangleCount+1)*2);
//#define WAVY_WATER
//#define FEATHER_LAYER_COUNT (3) //LORENZEN
//#define FEATHER_LAYER_THICKNESS (2.5f)
//#define FEATHER_WATER
//#ifdef WAVY_WATER // the NEW WATER a'la LORENZEN
if ( TheGlobalData->m_featherWater )
{
DynamicVBAccessClass::WriteLockClass lock(&vb_access);
VertexFormatXYZNDUV2* vb=lock.Get_Formatted_Vertex_Array();
Real phase = 0;
Real mapCoeff = PI/(4*MAP_XY_FACTOR);
Real wave = 0;
Real amplitude = 0.5f;
//The first (high order) byte is the Alpha value for this patch
// It needs to be set proportional to the number of feather layers
// this comes from TheGlobalData->m_featherWater, which is a count of layers
Int Alpha = 0;
if ( TheGlobalData->m_featherWater == 5) Alpha = 80;
if ( TheGlobalData->m_featherWater == 4) Alpha = 110;
if ( TheGlobalData->m_featherWater == 3) Alpha = 140;
if ( TheGlobalData->m_featherWater == 2) Alpha = 200;
if ( TheGlobalData->m_featherWater == 1) Alpha = 255;
//Keep diffuse from lighting calculations but substitute custom alpha
Int customDiffuse = (diffuse & 0x00ffffff) | (Alpha<< 24);//(0x80 << 16)|(0x90 << 8)|0xa0;
for (j=0; jx=vertex.X;
vb->y=vertex.Y;
// common to all the waving effects
phase = 25 * m_riverVOrigin + vertex.X * mapCoeff;
wave = (sin(phase) - 1.0f) * amplitude;
vb->z = (vertex.Z + wave);
vb->diffuse = customDiffuse;
vb->u1 = (vertex.X/waterFactor) + 0.02*cos(11*m_riverVOrigin)*wave;
vb->v1 = (vertex.Y/waterFactor) + 0.02*cos(5*m_riverVOrigin)*wave;
vb->u2 = vertex.X/BUMP_SIZE;
vb->v2 = vertex.Y/BUMP_SIZE + 0.3f*vertex.X/BUMP_SIZE;
vb->nx = 0;
vb->ny = 0;
vb->nz = 1.0f;
vb++;
}
}
}
//#else // STILL THE OLD FLAT WATER
else
{
DynamicVBAccessClass::WriteLockClass lock(&vb_access);
VertexFormatXYZNDUV2* vb=lock.Get_Formatted_Vertex_Array();
//Pulling some constants out of the inner loops to improve performance -MW
Real constA=0.02*cos(11*m_riverVOrigin);
Real constB=0.02*cos(5*m_riverVOrigin);
Real constC=25*m_riverVOrigin;
Real ooWaterFactor = 1.0f/waterFactor;
const Real constD=PI/(4*MAP_XY_FACTOR);
Real constE=1.0f/(Real)(vCount-1);
Real constF=1.0f/(Real)(uCount-1);
for (j=0; jx=vertex.X;
vb->y=vertex.Y;
vb->z=vertex.Z;
vb->diffuse= diffuse;
//Old slower version
//vb->u1=(vertex.X/waterFactor) + 0.02*cos(11*m_riverVOrigin)*sin(25*m_riverVOrigin+vertex.X*PI/(4*MAP_XY_FACTOR));
//vb->v1=(vertex.Y/waterFactor) + 0.02*cos(5*m_riverVOrigin)*sin(25*m_riverVOrigin+vertex.Y*PI/(4*MAP_XY_FACTOR));
vb->u1=vertex.X*ooWaterFactor + constA*WWMath::Fast_Sin(constC+vertex.X*constD);
vb->v1=vertex.Y*ooWaterFactor + constB*WWMath::Fast_Sin(constC+vertex.Y*constD);
vb->u2 = vertex.X/BUMP_SIZE;
//Old slower version
//vb->v2 = vertex.Y/BUMP_SIZE + 0.3f*vertex.X/BUMP_SIZE;
vb->v2 = (vertex.Y+0.3f*vertex.X)/BUMP_SIZE;
vb->nx = 0;
vb->ny = 0;
vb->nz = 1.0f;
vb++;
}
}
}
//#endif // OLD VS NEW WATER
Matrix3D tm(1);
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm); //position the water surface
DX8Wrapper::Set_Index_Buffer(ib_access,0);
DX8Wrapper::Set_Vertex_Buffer(vb_access);
setupFlatWaterShader();// lorenzen sez use the alpha shader
//If video card supports it and it's enabled, feather the water edge using destination alpha
if (DX8Wrapper::getBackBufferFormat() == WW3D_FORMAT_A8R8G8B8 && TheGlobalData->m_showSoftWaterEdge && TheWaterTransparency->m_transparentWaterDepth !=0)
{ DX8Wrapper::Set_DX8_Render_State(D3DRS_SRCBLEND, D3DBLEND_DESTALPHA );
DX8Wrapper::Set_DX8_Render_State(D3DRS_DESTBLEND, D3DBLEND_INVDESTALPHA );
}
DWORD cull;
DX8Wrapper::_Get_D3D_Device8()->GetRenderState(D3DRS_CULLMODE, &cull);
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
//#ifdef FEATHER_WATER // the NEW WATER a'la LORENZEN
// int layer = 0;//LORENZEN
// for (layer = 0; layer < FEATHER_LAYER_COUNT; ++layer)//LORENZEN
//#endif // FEATHER_WATER
{
//#ifdef WAVY_WATER // the NEW WATER a'la LORENZEN
//increment the depth of the water's surface for every vert in the buffer
//#ifdef FEATHER_WATER
// VertexFormatXYZNDUV2 *vertBuf = vertexBufferStart;
// while (vertBuf < vertexBufferStart + vCount * uCount)
// {
// vertBuf->z *= FEATHER_LAYER_THICKNESS;
// ++vertBuf;
// }
//#endif // FEATHER_WATER
//#endif //WAVY_WATER
DX8Wrapper::Draw_Triangles( 0,rectangleCount*2, 0, (rectangleCount+1)*2);//lorenzen thinks this is where to itereate the soft shoreline effect
}
if (false) {
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE , false);
DX8Wrapper::Draw_Triangles( 0,rectangleCount*2, 0, (rectangleCount+1)*2);
m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE , true);
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
}
if (m_riverWaterPixelShader) DX8Wrapper::_Get_D3D_Device8()->SetPixelShader(NULL);
//Restore alpha blend to default values since we may have changed them to feather edges.
DX8Wrapper::Set_DX8_Render_State(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
DX8Wrapper::Set_DX8_Render_State(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
if (TheTerrainRenderObject->getShroud())
{
if (m_trapezoidWaterPixelShader)
{ //shroud was applied in stage3 of main pass so just need to restore state here.
W3DShaderManager::resetShader(W3DShaderManager::ST_SHROUD_TEXTURE);
DX8Wrapper::_Get_D3D_Device8()->SetTexture(3,NULL); //free possible reference to shroud texture
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ZFUNC, D3DCMP_EQUAL);
}
else
{ //do second pass to apply the shroud on water plane for cards that can't do it in main pass.
W3DShaderManager::setTexture(0,TheTerrainRenderObject->getShroud()->getShroudTexture());
W3DShaderManager::setShader(W3DShaderManager::ST_SHROUD_TEXTURE, 0);
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
//Shroud shader uses z-compare of EQUAL which wouldn't work on water because it doesn't
//write to the zbuffer. Change to LESSEQUAL.
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
DX8Wrapper::Draw_Triangles( 0,rectangleCount*2, 0, (rectangleCount+1)*2);
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ZFUNC, D3DCMP_EQUAL);
W3DShaderManager::resetShader(W3DShaderManager::ST_SHROUD_TEXTURE);
}
}
DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_CULLMODE, cull);
}
//-------------------------------------------------------------------------------------------------
//debug version where moon rotates with the camera (always upright on screen)
//-------------------------------------------------------------------------------------------------
#if 0
void WaterRenderObjClass::renderSkyBody(Matrix3D *mat)
{
Vector3 vRight,vUp,V0,V1,V2,V3;
mat->Get_X_Vector(&vRight);
mat->Get_Y_Vector(&vUp);
//calculate offsets from quad center to each of the 4 corners
// 0-----1
// | /|
// | / |
// |/ |
// 3-----2
V0=-vRight+vUp;
V2=vRight+vUp;
V2=vRight-vUp;
V3=-vRight-vUp;
VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
DX8Wrapper::Set_Material(vmat);
REF_PTR_RELEASE(vmat);
DX8Wrapper::Set_Shader(ShaderClass::/*_PresetAdditiveShader*//*_PresetOpaqueShader*/_PresetAlphaShader);
// DX8Wrapper::Set_Texture(0,setting->skyBodyTexture);
DX8Wrapper::Set_Texture(0,m_alphaClippingTexture);
//draw an infinite sky plane
DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,4);
{
DynamicVBAccessClass::WriteLockClass lock(&vb_access);
VertexFormatXYZNDUV2* verts=lock.Get_Formatted_Vertex_Array();
if(verts)
{
verts[0].x=SKYBODY_SIZE*V0.X;
verts[0].y=SKYBODY_SIZE*V0.Y;
verts[0].z=SKYBODY_SIZE*V0.Z;
verts[0].u2=0;
verts[0].v2=1;
verts[0].diffuse=0xffffffff;
verts[1].x=SKYBODY_SIZE*V1.X;
verts[1].y=SKYBODY_SIZE*V1.Y;
verts[1].z=SKYBODY_SIZE*V1.Z;
verts[1].u2=1;
verts[1].v2=1;
verts[1].diffuse=0xffffffff;
verts[2].x=SKYBODY_SIZE*V2.X;
verts[2].y=SKYBODY_SIZE*V2.Y;
verts[2].z=SKYBODY_SIZE*V2.Z;
verts[2].u2=1;
verts[2].v2=0;
verts[2].diffuse=0xffffffff;
verts[3].x=SKYBODY_SIZE*V3.X;
verts[3].y=SKYBODY_SIZE*V3.Y;
verts[3].z=SKYBODY_SIZE*V3.Z;
verts[3].u2=0;
verts[3].v2=0;
verts[3].diffuse=0xffffffff;
}
}
DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
DX8Wrapper::Set_Vertex_Buffer(vb_access);
Matrix3D tm(1);
//set postion of skybody in world
// tm.Set_Translation(Vector3(40,0,0));
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
DX8Wrapper::Draw_Triangles( 0,2, 0, 4); //draw a quad, 2 triangles, 4 verts
}
#endif
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void WaterRenderObjClass::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void WaterRenderObjClass::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// grid cells x
Int cellsX = m_gridCellsX;
xfer->xferInt( &cellsX );
if( cellsX != m_gridCellsX )
{
DEBUG_CRASH(( "WaterRenderObjClass::xfer - cells X mismatch\n" ));
throw SC_INVALID_DATA;
} // end if
// grid cells Y
Int cellsY = m_gridCellsY;
xfer->xferInt( &cellsY );
if( cellsY != m_gridCellsY )
{
DEBUG_CRASH(( "WaterRenderObjClass::xfer - cells Y mismatch\n" ));
throw SC_INVALID_DATA;
} // end if
// xfer each of the mesh data points
for( Int i = 0; i < m_meshDataSize; ++i )
{
// height
xfer->xferReal( &m_meshData[ i ].height );
// velocity
xfer->xferReal( &m_meshData[ i ].velocity );
// status
xfer->xferUnsignedByte( &m_meshData[ i ].status );
// preferred height
xfer->xferUnsignedByte( &m_meshData[ i ].preferredHeight );
} // end for, i
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void WaterRenderObjClass::loadPostProcess( void )
{
} // end loadPostProcess