⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch23p1_waterplane.cpp

📁 游戏开发特殊技巧-special.effects.game.programming
💻 CPP
字号:
#include "Ch23p1_WaterPlane.h"

CWaterPlane::CWaterPlane()
{
  m_pd3dDevice = NULL;
  m_pVBPool = NULL;
  m_pIBPool = NULL;
  m_texInsidePool = NULL;
  m_iNumVerts = 0;

  m_fHeightArray1 = NULL;
  m_fHeightArray2 = NULL;

  m_fDampValue = 1.1f;
  m_fRefractionIndex = 1.33f;
  m_fDepth = 1.0f;
  m_bHardcodeNormals = true;
  m_fEnvBlendFactor = 0.5f;
}

CWaterPlane::~CWaterPlane()
{

}

HRESULT CWaterPlane::CreateVertexGrid(LPDIRECT3DVERTEXBUFFER8 *pVB, LPDIRECT3DINDEXBUFFER8 *pIB,
                         D3DPOOL pool, float fTotalSize, DWORD dwColor, 
                         int iNumVerticesX, int iNumVerticesY, LPDIRECT3DDEVICE8 pDev)
{
  HRESULT hr;

  // create and fill vertex buffer
  {
    if(FAILED(hr = pDev->CreateVertexBuffer(iNumVerticesX*iNumVerticesY*sizeof(VERTEX_XYZ_NORMAL_DIFFUSE_TEX2),
                    0, D3DFVF_XYZ_NORMAL_DIFFUSE_TEX2, pool, pVB)))
      return hr;

    VERTEX_XYZ_NORMAL_DIFFUSE_TEX2 *pVertices;
    float fSizeDiv2 = fTotalSize/2;

    if( FAILED( hr = (*pVB)->Lock( 0, iNumVerticesX*iNumVerticesY*sizeof(VERTEX_XYZ_NORMAL_DIFFUSE_TEX2), (BYTE**)&pVertices, 0 ) ) )
        return hr;

    for (int x=0; x < iNumVerticesX; x++) {
      for (int y=0; y < iNumVerticesY; y++) {
        pVertices[(y*iNumVerticesX)+x].position = D3DXVECTOR3(
          (iNumVerticesX > 1) ? (((float)x/(float)(iNumVerticesX-1))*fTotalSize)-fSizeDiv2 : 0, 
          0.0f,
          (iNumVerticesY > 1) ? (((float)(iNumVerticesY-1-y)/(float)(iNumVerticesY-1))*fTotalSize)-fSizeDiv2 : 0);

        pVertices[(y*iNumVerticesX)+x].color    = dwColor;
        pVertices[(y*iNumVerticesX)+x].tu2       = (float)x/(float)(iNumVerticesX-1);
        pVertices[(y*iNumVerticesX)+x].tv2       = (float)y/(float)(iNumVerticesY-1);
        
      }
    }
  }

  // create index buffer
  {
    if(FAILED(hr = pDev->CreateIndexBuffer(
        iNumVerticesX*iNumVerticesY*2*3*2, // *2 (tris) *3 (indicies per tri) * 2 (bytes per index)
        D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,
        D3DPOOL_MANAGED, pIB)))
      return hr;

    // lock and fill index buffer

    WORD *pIndices;
    if(FAILED(hr = (*pIB)->Lock(0, iNumVerticesX*iNumVerticesY*2*3*2, (unsigned char **)&pIndices, 
      0)))
      return hr;
  
    WORD *pIndex = pIndices;
    for (int x=0; x < iNumVerticesX-1; x++) {
      for (int y=0; y < iNumVerticesY-1; y++) {
        // first triangle
        *(pIndex++) = ((y)*iNumVerticesX)+x;
        *(pIndex++) = ((y)*iNumVerticesX)+x+1;
        *(pIndex++) = ((y+1)*iNumVerticesX)+x+1;

        // second triangle
        *(pIndex++) = ((y)*iNumVerticesX)+x;
        *(pIndex++) = ((y+1)*iNumVerticesX)+x+1;
        *(pIndex++) = ((y+1)*iNumVerticesX)+x;
      }
    }

    if(FAILED(hr = (*pIB)->Unlock())) return hr;
    if(FAILED(hr = (*pVB)->Unlock())) return hr;
  }
  return(S_OK);
}

HRESULT CWaterPlane::RestoreDeviceObjects(LPDIRECT3DDEVICE8 pDev,
  int iNumVerts, const char *strInPoolTextureFilename)
{
  m_iNumVerts = iNumVerts;
  m_pd3dDevice = pDev;

  if (FAILED(D3DXCreateTextureFromFile(m_pd3dDevice, 
             strInPoolTextureFilename, &m_texInsidePool)))
    return E_FAIL;

  CreateVertexGrid(&m_pVBPool, &m_pIBPool, D3DPOOL_MANAGED, 1.0f, 
    D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f), iNumVerts, iNumVerts, m_pd3dDevice);

  // initialize height arrays
  m_fHeightArray1 = new float[iNumVerts*iNumVerts];
  m_fHeightArray2 = new float[iNumVerts*iNumVerts];

  memset(m_fHeightArray1, 0, sizeof(float)*iNumVerts*iNumVerts);
  memset(m_fHeightArray2, 0, sizeof(float)*iNumVerts*iNumVerts);

  m_pActiveHeightArray = m_fHeightArray1;
  m_pScratchHeightArray = m_fHeightArray2;

  m_Timer.Begin();

  return(S_OK);
}

void CWaterPlane::InvalidateDeviceObjects()
{
  SAFE_RELEASE(m_texInsidePool);
  SAFE_RELEASE(m_pVBPool);
  SAFE_RELEASE(m_pIBPool);

  SAFE_DELETE_ARRAY(m_fHeightArray1);
  SAFE_DELETE_ARRAY(m_fHeightArray2);
}

/****************************************************************************

 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 CWaterPlane::ProcessWater()
{
  // loop through all the water values...
  for (int y=0; y < m_iNumVerts; y++) {
    for (int x=0; x < m_iNumVerts; x++) {

      // add up the values of all the neighboring water values...
      float 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 >= m_iNumVerts) xplus1 = (m_iNumVerts)-1;
      int xplus2 = x+2; if (xplus2 >= m_iNumVerts) xplus2 = (m_iNumVerts)-1;
      int yplus1 = y+1; if (yplus1 >= m_iNumVerts) yplus1 = (m_iNumVerts)-1;
      int yplus2 = y+2; if (yplus2 >= m_iNumVerts) yplus2 = (m_iNumVerts)-1;

      // Method 1: Slower but yields slightly better looking water
      {
        
        value  = m_pActiveHeightArray[((y)      *m_iNumVerts)+xminus1];
        value += m_pActiveHeightArray[((y)      *m_iNumVerts)+xminus2];
        value += m_pActiveHeightArray[((y)      *m_iNumVerts)+xplus1];
        value += m_pActiveHeightArray[((y)      *m_iNumVerts)+xplus2];
        value += m_pActiveHeightArray[((yminus1)*m_iNumVerts)+x];
        value += m_pActiveHeightArray[((yminus2)*m_iNumVerts)+x];
        value += m_pActiveHeightArray[((yplus1) *m_iNumVerts)+x];
        value += m_pActiveHeightArray[((yplus2) *m_iNumVerts)+x];
        value += m_pActiveHeightArray[((yminus1)*m_iNumVerts)+xminus1];
        value += m_pActiveHeightArray[((yminus1)*m_iNumVerts)+xplus1];
        value += m_pActiveHeightArray[((yplus1) *m_iNumVerts)+xminus1];
        value += m_pActiveHeightArray[((yplus1) *m_iNumVerts)+xplus1];
      
        // average them
        value /= 6.0f;
        
      }

      // subtract the previous water value
      value -= m_pScratchHeightArray[(y*m_iNumVerts)+x];

      // dampen it!
      value /= m_fDampValue;

      if (value > 10.0f) value = 10.0f;
      if (value < -10.0f) value = -10.0f;

      // store it in array
      m_pScratchHeightArray[(y*m_iNumVerts)+x] = value;
    }
  }
}

float CWaterPlane::CalcDisplacement(float fHeightdiff)
{
  // the angle is the arctan of the height difference
  float angle = (float)atan(fHeightdiff);

  // now, calculate the angle of the refracted beam.
  float beamangle = (float)asin(sin(angle) / m_fRefractionIndex);

  // finally, calculate the displacement, based on the refracted beam
  // and the height difference.
  return(tan(beamangle) * (fHeightdiff+m_fDepth));
}

void CWaterPlane::ApplyHeightArrayToVB()
{
  HRESULT hr;

  VERTEX_XYZ_NORMAL_DIFFUSE_TEX2 *pVertices;
  float fSizeDiv2 = 0.5f;

  if( FAILED( hr = m_pVBPool->Lock( 0, 
      m_iNumVerts*m_iNumVerts*sizeof(VERTEX_XYZ_NORMAL_DIFFUSE_TEX2), 
      (BYTE**)&pVertices, 0 ) ) )
    return;

  for (int x=0; x < m_iNumVerts; x++) {
    for (int y=0; y < m_iNumVerts; y++) {
      float fValue = m_pActiveHeightArray[(y*m_iNumVerts)+x];
      if (fValue > 2.0f) fValue = 2.0f;
      
      
      pVertices[(y*m_iNumVerts)+x].position = D3DXVECTOR3(
        (m_iNumVerts > 1) ? (((float)x/(float)(m_iNumVerts-1)))-fSizeDiv2 : 0, 
        fValue,
        (m_iNumVerts > 1) ? (((float)(m_iNumVerts-1-y)/(float)(m_iNumVerts-1)))-fSizeDiv2 : 0);
      
      // calculate texture coordinates
      {
        float xdiff = (x == m_iNumVerts-1) ? 0 : 
          m_pActiveHeightArray[(y*m_iNumVerts)+x+1] - 
          m_pActiveHeightArray[(y*m_iNumVerts)+x];
        
        float ydiff = (y == m_iNumVerts-1) ? 0 : 
          m_pActiveHeightArray[((y+1)*m_iNumVerts)+x] - 
          m_pActiveHeightArray[(y*m_iNumVerts)+x];
      
        float xdisp = CalcDisplacement(xdiff);
        float ydisp = CalcDisplacement(ydiff);

        float tu, tv;

        if (xdiff < 0) {
          if (ydiff < 0) {
            tu = (float)x/(float)(m_iNumVerts-1)-xdisp;
            tv = (float)y/(float)(m_iNumVerts-1)-ydisp;
          }
          else {
            tu = (float)x/(float)(m_iNumVerts-1)-xdisp;
            tv = (float)y/(float)(m_iNumVerts-1)+ydisp;
          }
        }
        else {
          if (ydiff < 0) {
            tu = (float)x/(float)(m_iNumVerts-1)+xdisp;
            tv = (float)y/(float)(m_iNumVerts-1)-ydisp;
          }
          else {
            tu = (float)x/(float)(m_iNumVerts-1)+xdisp;
            tv = (float)y/(float)(m_iNumVerts-1)+ydisp;
          }
        }

        if (tu < 0.0f) tu = 0.0f; if (tu > 1.0f) tu = 1.0f;
        if (tv < 0.0f) tv = 0.0f; if (tv > 1.0f) tv = 1.0f;

        pVertices[(y*m_iNumVerts)+x].tu2 = tu;
        pVertices[(y*m_iNumVerts)+x].tv2 = tv;
        pVertices[(y*m_iNumVerts)+x].color = 
          D3DXCOLOR(1.0f, 1.0f, 1.0f, m_fEnvBlendFactor);
      }
    }
  }

  
  // since our model has changed, we must now recompute the normals.
  for (x=0; x < m_iNumVerts; x++) {
    for (int y=0; y < m_iNumVerts; y++) {
      if (m_bHardcodeNormals) {
        pVertices[(y*m_iNumVerts)+x].normal = D3DXVECTOR3(0,1,0);
      }
      else {
        // calc normal
        int xplus1 = (x == m_iNumVerts-1) ? m_iNumVerts-2 : x+1;
        int yplus1 = (y == m_iNumVerts-1) ? m_iNumVerts-2 : y+1;

        D3DXVECTOR3 v1 = pVertices[(y*m_iNumVerts)+x].position;
        D3DXVECTOR3 v2 = pVertices[(y*m_iNumVerts)+xplus1].position;
        D3DXVECTOR3 v3 = pVertices[(yplus1*m_iNumVerts)+x].position;

        D3DXVECTOR3 vNorm1 = v2 - v1;
        D3DXVECTOR3 vNorm2 = v3 - v1;
        D3DXVECTOR3 vNorm;
      
        D3DXVec3Normalize(&vNorm1, &vNorm1);
        D3DXVec3Normalize(&vNorm2, &vNorm2);
        D3DXVec3Cross(&vNorm, &vNorm2, &vNorm1);

        D3DXVec3Normalize(&vNorm, &vNorm);

        pVertices[(y*m_iNumVerts)+x].normal = vNorm;
      }
    }
  }
  
  
  m_pVBPool->Unlock();
}

void CWaterPlane::CreateWaterDroplet(int iX, int iY, int iSize, 
                                     int iSplashStrength)
{
  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 >= m_iNumVerts || y < 0 || y >= m_iNumVerts) 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.
        m_pActiveHeightArray[(y*m_iNumVerts)+(x)] += 
          (float)iSplashStrength*sqrt(square_x+square_y);
      }
    }
  }
}

void CWaterPlane::Update(float fElapsedTime)
{
  if (m_Timer.GetTime() > 0.04) {
  
    ProcessWater();
    ApplyHeightArrayToVB();
  

    // flip-flop the water buffers.
    float *temp = m_pActiveHeightArray;
    m_pActiveHeightArray = m_pScratchHeightArray;
    m_pScratchHeightArray = temp;
    m_Timer.Begin();
  }
}

void CWaterPlane::Render()
{
  m_pd3dDevice->SetTexture( 1, m_texInsidePool );
  m_pd3dDevice->SetStreamSource( 0, m_pVBPool, sizeof(VERTEX_XYZ_NORMAL_DIFFUSE_TEX2) );
  m_pd3dDevice->SetVertexShader(D3DFVF_XYZ_NORMAL_DIFFUSE_TEX2);
  m_pd3dDevice->SetIndices( m_pIBPool, 0L );
  m_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 
    m_iNumVerts*m_iNumVerts, 0, m_iNumVerts*m_iNumVerts*2);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -