/*
#############################################################################
Ch12p1_SimpleWater.cpp: a program that demonstrates the water algorithm,
without any annoying bells and/or whistles.
#############################################################################
*/
// include files ////////////////////////////////////////////////////////////
#define STRICT
#include <stdio.h>
#include <math.h>
#include <D3DX8.h>
#include "D3DApp.h"
#include "D3DFile.h"
#include "D3DFont.h"
#include "D3DUtil.h"
#include "DXUtil.h"
#include "D3DHelperFuncs.h"
#include "Ch12p1_resource.h"
#include "CommonFuncs.h"
// A structure for our custom vertex type.
struct CUSTOMVERTEX
{
D3DXVECTOR3 position; // The position
D3DCOLOR color; // The color
FLOAT tu, tv; // The texture coordinates
};
const int TEXTURESIZE = 256; // size of the fire texture
// Our custom FVF, which describes our custom vertex structure
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
//-----------------------------------------------------------------------------
// Name: class CMyD3DApplication
// Desc: Application class. The base class (CD3DApplication) provides the
// generic functionality needed in all Direct3D samples. CMyD3DApplication
// adds functionality specific to this sample program.
//-----------------------------------------------------------------------------
class CMyD3DApplication : public CD3DApplication
{
// Font for drawing text
CD3DFont* m_pFont;
CD3DFont* m_pFontSmall;
// Scene
LPDIRECT3DVERTEXBUFFER8 m_pVB;
DWORD m_dwNumVertices;
// Texture
LPDIRECT3DTEXTURE8 m_pImageTex; // this texture will go "underwater"...
LPDIRECT3DTEXTURE8 m_pWaterTex; // ... and will appear on this texture.
// Texture Palette
char m_strTextureSurfFormat[256];
int m_iWaterField[TEXTURESIZE*TEXTURESIZE]; // first water array
int m_iWaterField2[TEXTURESIZE*TEXTURESIZE]; // second water array
int *m_pWaterActive; // we use these two pointers to flip
int *m_pWaterScratch; // the active water array back and forth.
char m_lutDisplacement[512]; // displacement lookup table (to optimize calculations)
protected:
HRESULT OneTimeSceneInit();
HRESULT InitDeviceObjects();
HRESULT RestoreDeviceObjects();
HRESULT InvalidateDeviceObjects();
HRESULT DeleteDeviceObjects();
HRESULT FinalCleanup();
HRESULT Render();
HRESULT FrameMove();
HRESULT ConfirmDevice( D3DCAPS8* pCaps, DWORD dwBehavior, D3DFORMAT Format );
LRESULT MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
public:
CMyD3DApplication();
};
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: Entry point to the program. Initializes everything, and goes into a
// message-processing loop. Idle time is used to render the scene.
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
CMyD3DApplication d3dApp;
if( FAILED( d3dApp.Create( hInst ) ) )
return 0;
return d3dApp.Run();
}
//-----------------------------------------------------------------------------
// Name: CMyD3DApplication()
// Desc: Application constructor. Sets attributes for the app.
//-----------------------------------------------------------------------------
CMyD3DApplication::CMyD3DApplication()
{
m_strWindowTitle = _T("Ch12p1_SimpleWater");
m_bUseDepthBuffer = TRUE;
m_pFont = new CD3DFont( _T("Arial"), 12, D3DFONT_BOLD );
m_pFontSmall = new CD3DFont( _T("Arial"), 9, D3DFONT_BOLD );
m_pVB = NULL;
m_dwNumVertices = 6;
m_pImageTex = NULL;
m_pWaterTex = NULL;
}
/****************************************************************************
MakeDisplacementLookupTable: populates our m_cDisplacement map with valid
values based on a refraction index. The refraction index of water is 2.0.
****************************************************************************/
void MakeDisplacementLookupTable(char *pDisplacement, int iArraySize,
float fRefractionIndex,
float fDepth)
{
for (int i=-iArraySize/2; i < (iArraySize/2)-1; i++) {
float heightdiff = i*fDepth;
// the angle is the arctan of the height difference
float angle = (float)atan(heightdiff);
// now, calculate the angle of the refracted beam.
float beamangle = (float)asin(sin(angle) / fRefractionIndex);
// finally, calculate the displacement, based on the refracted beam
// and the height difference.
pDisplacement[i+(iArraySize/2)] = (int)(tan(beamangle) * heightdiff);
}
}
/****************************************************************************
ProcessWater: this function processes our water. It takes two input buffers,
the water dimensions, and the cooling amount. It calculates the new water
values from waterfield1 and puts them into waterfield2.
****************************************************************************/
void ProcessWater(int *oldwater, int *newwater,
int iWaterWidth, int iWaterHeight, float fDampValue)
{
// loop through all the water values...
for (int y=0; y < iWaterHeight; y++) {
for (int x=0; x < iWaterWidth; x++) {
// add up the values of all the neighboring water values...
int value;
int xminus1 = x-1; if (xminus1 < 0) xminus1 = 0;
int xminus2 = x-2; if (xminus2 < 0) xminus2 = 0;
int yminus1 = y-1; if (yminus1 < 0) yminus1 = 0;
int yminus2 = y-2; if (yminus2 < 0) yminus2 = 0;
int xplus1 = x+1; if (xplus1 >= iWaterWidth) xplus1 = iWaterWidth-1;
int xplus2 = x+2; if (xplus2 >= iWaterWidth) xplus2 = iWaterWidth-1;
int yplus1 = y+1; if (yplus1 >= iWaterHeight) yplus1 = iWaterHeight-1;
int yplus2 = y+2; if (yplus2 >= iWaterHeight) yplus2 = iWaterHeight-1;
//////////////////////////
//
// Blending methods: uncomment one of these two methods.
//
//////////////////////////
// Method 1: Slower but yields slightly better looking water
{
/*
value = (float)oldwater[((y) *iWaterWidth)+xminus1];
value += (float)oldwater[((y) *iWaterWidth)+xminus2];
value += (float)oldwater[((y) *iWaterWidth)+xplus1];
value += (float)oldwater[((y) *iWaterWidth)+xplus2];
value += (float)oldwater[((yminus1)*iWaterWidth)+x];
value += (float)oldwater[((yminus2)*iWaterWidth)+x];
value += (float)oldwater[((yplus1) *iWaterWidth)+x];
value += (float)oldwater[((yplus2) *iWaterWidth)+x];
value += (float)oldwater[((yminus1)*iWaterWidth)+xminus1];
value += (float)oldwater[((yminus1)*iWaterWidth)+xplus1];
value += (float)oldwater[((yplus1) *iWaterWidth)+xminus1];
value += (float)oldwater[((yplus1) *iWaterWidth)+xplus1];
// average them
value /= 6;
*/
}
// Method 2: This method is faster but doesn't look as good (IMHO)
{
value = oldwater[((y) *iWaterWidth)+xminus1];
value += oldwater[((y) *iWaterWidth)+xplus1];
value += oldwater[((yminus1)*iWaterWidth)+x];
value += oldwater[((yplus1) *iWaterWidth)+x];
// average them (/4) then multiply by two
// so they don't die off as quickly.
value /= 2;
}
////////////////////////
//
// regardless of the blending method we choose, we still must
// do this stuff.
//
////////////////////////
// subtract the previous water value
value -= newwater[(y*iWaterWidth)+x];
// dam