?? ch12p1_simplewater.cpp
字號:
/*
#############################################################################
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];
// dampen it!
value = (int)((float)value / 1.05f);
// store it in array
newwater[(y*iWaterWidth)+x] = value;
}
}
/* unremark this section of code to create a eastbound current
for (y=0; y < iWaterHeight; y++) {
for (int x=iWaterWidth-1; x >= 0; x--) {
int xminus1 = x ? x-1 : iWaterWidth-1; // wrap around
newwater[(y*iWaterWidth)+x] = newwater[(y*iWaterWidth)+xminus1];
}
}
for (y=0; y < iWaterHeight; y++) {
for (int x=iWaterWidth-1; x >= 1; x--) {
int xminus1 = x ? x-1 : iWaterWidth-1; // wrap around
oldwater[(y*iWaterWidth)+x] = oldwater[(y*iWaterWidth)+xminus1];
}
}
*/
}
void CreateWaterDroplet(int iX, int iY, int iSize, int iSplashStrength,
int *waterbuf, int iWaterWidth, int iWaterHeight)
{
for (int x=iX-iSize; x <= iX+iSize; x++) {
for (int y=iY-iSize; y <= iY+iSize; y++) {
// make sure we're in bounds
if (x < 0 || x >= iWaterWidth || y < 0 || y >= iWaterHeight) continue;
// see if the point at (x,y) is within the circle of radius size
int square_x = (x-iX)*(x-iX);
int square_y = (y-iY)*(y-iY);
int square_size = iSize*iSize;
if (square_x+square_y <= square_size) {
// it's within the size circle! apply it to the water buffer.
waterbuf[(y*iWaterWidth)+x] += (int)((float)iSplashStrength)*sqrt(square_x+square_y);
}
}
}
}
HRESULT PutWaterOntoTexture(int *waterbuf, int iWaterWidth, int iWaterHeight,
char *lutDisplacement,
LPDIRECT3DDEVICE8 pd3dDevice,
LPDIRECT3DTEXTURE8 pSrcTex,
LPDIRECT3DTEXTURE8 pDestTex)
{
HRESULT hr;
// lock texture
D3DLOCKED_RECT rect_src, rect_dest;
::ZeroMemory(&rect_src, sizeof(rect_src));
::ZeroMemory(&rect_dest, sizeof(rect_dest));
if (FAILED(hr = pSrcTex->LockRect(0, &rect_src, NULL, 0))) return(hr);
if (FAILED(hr = pDestTex->LockRect(0, &rect_dest, NULL, 0))) return(hr);
// our texture surface is now locked, and we can use the pitch to traverse it.
DWORD *pSrc = (DWORD *)(rect_src.pBits);
DWORD *pDest= (DWORD *)(rect_dest.pBits);
int dest_index=0;
int src_pitch = rect_src.Pitch/4; // in DWORDS
// this could be optimized a LOT. It's this way so you can learn the technique.
for (int y=0; y < iWaterHeight; y++) {
for (int x=0; x < iWaterWidth; x++) {
int xdiff = (x == iWaterWidth-1) ? 0 : waterbuf[(y*iWaterWidth)+x+1] - waterbuf[(y*iWaterWidth)+x];
int ydiff = (y == iWaterHeight-1) ? 0 : waterbuf[((y+1)*iWaterWidth)+x] - waterbuf[(y*iWaterWidth)+x];
int xdisp = lutDisplacement[(xdiff+256) % 512];
int ydisp = lutDisplacement[(ydiff+256) % 512];
if (xdiff < 0) {
if (ydiff < 0) {
if (y-ydisp < 0 || y-ydisp >= TEXTURESIZE || x-xdisp < 0 || x-xdisp >= TEXTURESIZE)
pDest[dest_index++] = pSrc[0];
else
pDest[dest_index++] = pSrc[((y-ydisp)*src_pitch)+x-xdisp];
}
else {
if (y+ydisp < 0 || y+ydisp >= TEXTURESIZE || x-xdisp < 0 || x-xdisp >= TEXTURESIZE)
pDest[dest_index++] = pSrc[0];
else
pDest[dest_index++] = pSrc[((y+ydisp)*src_pitch)+x-xdisp];
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -