📄 ch11p2_texturefire.cpp
字号:
// Ch11p2_TextureFire.cpp: implementation of the CTextureFire class.
//
//////////////////////////////////////////////////////////////////////
#include "CommonFuncs.h"
#include "Ch11p2_TextureFire.h"
// Our hard-coded color palette for our fire.
// Nuthin' but a string of bytes.
unsigned char g_FireRed[256] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,4,4,4,4,4,4,4,8,8,8,8,8,12,12,12,12,16,16,16,16,20,20,20,24,24,
24,28,28,32,32,32,36,36,40,40,44,44,48,48,52,52,56,56,60,60,64,68,68,72,72,
76,80,80,84,88,88,92,92,96,100,100,104,108,108,112,116,120,120,124,128,128,
132,136,136,140,144,144,148,152,152,156,160,160,164,164,172,172,172,176,176,
180,184,184,188,188,192,192,196,196,200,200,204,204,208,208,208,212,212,216,
216,220,220,220,220,224,224,224,228,228,228,228,232,232,232,232,236,236,236,
236,236,240,240,240,240,240,240,240,244,244,244,244,244,244,244,244,244,244,
248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,
248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,
248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,
248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,
248,248,248,248,248};
unsigned char g_FireGreen[256]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,8,8,8,8,
8,8,8,8,8,12,12,12,12,12,12,16,16,16,16,16,16,20,20,20,20,20,24,24,24,24,28,
28,28,28,32,32,32,32,36,36,36,36,40,40,40,44,44,44,48,48,48,52,52,52,56,56,
56,60,60,60,64,64,64,68,68,72,72,72,76,76,80,80,80,84,84,84,88,88,92,92,92,
96,96,100,100,104,104,104,108,108,112,112,116,116,116,120,120,124,124,128,
128,128,132,132,136,136,140,140,140,144,144,148,148,148,152,152,156,156,160,
160,163,164,164,168,168,168,172,172,172,176,176,180,180,180,184,184,184,188,
188,188,192,192,192,196,196,200,200,200,200,200,204,204,204,208,208,208,208,
212,212,212,212,216,216,216,216,220,220,220,220,220,224,224,224,224,224,228,
228,228,228,228,228,232,232,232,232,232,232,236,236,236,236,236,236,236,236,
240,240,240,240,240,240};
unsigned char g_FireBlue[256]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,8,8,8,2,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,16,16,16,16,16,16,16,16,16,16,16,16,16,16,20,20,20,20,
20,20,20,20,20,20,20,20,24,24,24,24,24,24,24,24,24,24,24,24,28,28,28,28,28,
28,28,28,28,28,32,32,32,32,32,32,32,32,32,32,36,36,36,36,36,36,36,36,36,40,
40,40,40,40,40,40,40,44,44,44,44,44};
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
void CTextureFire::InitDeviceObjects(LPDIRECT3DDEVICE8 pDevice, int iTextureSize,
int iCoolAmount)
{
if (!pDevice) { throw("CTextureFire::InitDeviceObjects: invalid pDevice."); }
if (iTextureSize < 2 || iTextureSize > 512 || !IsPowerOf2(iTextureSize)) {
throw("CTextureFire::InitDeviceObjects: invalid texture size.");
}
m_iCoolAmount = iCoolAmount;
m_pd3dDevice = pDevice;
m_iTextureSize = iTextureSize;
// allocate fire buffers
m_FireBuffer1 = new unsigned char[iTextureSize*iTextureSize];
m_FireBuffer2 = new unsigned char[iTextureSize*iTextureSize];
// set the first fire buffer active, 2nd one as scratch surface
m_pActiveBuffer = m_FireBuffer1;
m_pScratchBuffer = m_FireBuffer2;
// clear the buffers
memset(m_FireBuffer1, 0, iTextureSize*iTextureSize);
memset(m_FireBuffer2, 0, iTextureSize*iTextureSize);
// set up a default palette
for (int q=0; q < 256; q++) {
m_Palette[q].peBlue = g_FireBlue[q];
m_Palette[q].peGreen = g_FireGreen[q];
m_Palette[q].peRed = g_FireRed[q];
m_Palette[q].peFlags = 0;
}
}
void CTextureFire::RestoreDeviceObjects(void)
{
HRESULT hr;
// create fire texture
if (FAILED(hr = D3DXCreateTexture(m_pd3dDevice, m_iTextureSize, m_iTextureSize, 1, 0,
D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &m_pTexture))) {
throw("CTextureFire::RestoreDeviceObjects: can't create texture!");
}
}
void CTextureFire::InvalidateDeviceObjects(void)
{
SAFE_RELEASE( m_pTexture );
}
void CTextureFire::DeleteDeviceObjects(void)
{
// delete fire buffers
delete[] m_FireBuffer1; m_FireBuffer1 = NULL;
delete[] m_FireBuffer2; m_FireBuffer2 = NULL;
}
void CTextureFire::SetupTextureStages(void)
{
// set the fire stage active...
m_pd3dDevice->SetTexture( 0, m_pTexture );
// set up our texture stages for a simple texture copy...
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
// turning on linear filtering for our fire texture really helps out with
// the image quality.
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
}
/****************************************************************************
Process: this function processes our fire. It takes two input buffers,
the fire dimensions, and the cooling amount. It calculates the new fire
values from firefield1 and puts them into firefield2.
****************************************************************************/
void CTextureFire::Process(void)
{
// loop through all the fire values...
for (int y=0; y < m_iTextureSize; y++) {
for (int x=0; x < m_iTextureSize-0; x++) {
// these will store, temporarily, the fire values immediately to the
// left, right, top, and bottom of this fire value.
unsigned char firevalue_left, firevalue_right, firevalue_bottom, firevalue_top;
int finalfirevalue;
// x+-1 and y+-1 aren't always that simple; we must account for wrapping
// around the horizontal edge (not vertical, however).
// so we calculate x/y +- 1 and store them temporarily.
int xplus1, xminus1, yplus1, yminus1;
xplus1 = x+1; if (xplus1 >= m_iTextureSize) xplus1=0;
xminus1= x-1; if (xminus1 < 0) xminus1 = m_iTextureSize-1;
yplus1 = y+1; if (yplus1 >= m_iTextureSize) yplus1=m_iTextureSize-1;
yminus1= y-1; if (yminus1 < 0) yminus1 = 0;
// now we can get the fire values of the neighboring pixels
firevalue_right = m_pActiveBuffer[(y*m_iTextureSize)+xplus1];
firevalue_left = m_pActiveBuffer[(y*m_iTextureSize)+xminus1];
firevalue_bottom= m_pActiveBuffer[((yplus1)*m_iTextureSize)+x];
firevalue_top = m_pActiveBuffer[((yminus1)*m_iTextureSize)+x];
// now, the most important part- calculate the new fire value..
finalfirevalue = (firevalue_left+firevalue_right+firevalue_top+firevalue_bottom)/4;
// subtract a certain amount to simulate the fire "cooling."
// this is where you'd apply your cooling map.
finalfirevalue -= m_iCoolAmount;
// make sure that the subtraction of the coolamount didn't take us
// below zero.
if (finalfirevalue < 0) finalfirevalue = 0;
// store the fire value on the scratch array, up one line from where
// it originally was. This simulates the flames rising.
m_pScratchBuffer[((yminus1)*m_iTextureSize)+x] = finalfirevalue;
}
}
// add fuel to the fire. This particular fueling method is one of my
// favorite; it creates little jet streams of flame.
// we work in blocks of 2x1...
for (int x=0; x < m_iTextureSize; x+=2) {
// we add fuel only to the last row.
int y=m_iTextureSize-1;
// determine whether this particular spot gets fuel added or taken
// away from it, by adding a number between (-31..31)
int fuel = m_pActiveBuffer[(y*m_iTextureSize)+x] + (rand() % 64) - 32;
// we must be between 0-255.
if (fuel > 255) fuel = 255;
if (fuel < 0) fuel = 0;
// apply the new fuel value to two adjacent pixels. This helps reduce
// the "dithering" effect that the fire is prone to.
m_pScratchBuffer[(y*m_iTextureSize)+x] = (unsigned char)fuel;
m_pScratchBuffer[(y*m_iTextureSize)+x+1] = (unsigned char)fuel;
}
}
void CTextureFire::FrameMove(void)
{
Process();
CopyToTexture();
// flip-flop the buffers.
unsigned char *temp = m_pActiveBuffer;
m_pActiveBuffer = m_pScratchBuffer;
m_pScratchBuffer = temp;
}
bool CTextureFire::CopyToTexture(void)
{
HRESULT hr;
// lock texture
D3DLOCKED_RECT lockedrect;
::ZeroMemory(&lockedrect, sizeof(lockedrect));
if (FAILED(hr = m_pTexture->LockRect(0, &lockedrect, NULL, 0))) return(false);
// our texture surface is now locked, and we can use the pitch to traverse it.
unsigned char *pSurfBits = static_cast<unsigned char *>(lockedrect.pBits);
int index=0;
for (int y=0; y < m_iTextureSize; y++) {
for (int x=0; x < m_iTextureSize; x++) {
// the fire value at this position determines the color of this texel
pSurfBits[index++] = m_Palette[m_pActiveBuffer[(y*m_iTextureSize)+x]].peBlue; // blue
pSurfBits[index++] = m_Palette[m_pActiveBuffer[(y*m_iTextureSize)+x]].peGreen; // green
pSurfBits[index++] = m_Palette[m_pActiveBuffer[(y*m_iTextureSize)+x]].peRed; // red
pSurfBits[index++] = m_Palette[m_pActiveBuffer[(y*m_iTextureSize)+x]].peFlags; // alpha
}
// next line
index += lockedrect.Pitch - (m_iTextureSize*4);
}
// unlock texture surface
if (FAILED(hr = m_pTexture->UnlockRect(0))) return(false);
return(true);
}
bool CTextureFire::LoadPaletteFromBMP(const char *strBMPfile)
{
LPDIRECT3DTEXTURE8 pTexture;
if (FAILED(D3DXCreateTextureFromFileExA(m_pd3dDevice,
strBMPfile,
0,
0,
1,
0,
D3DFMT_UNKNOWN,
D3DPOOL_MANAGED,
0,
0,
0,
NULL,
m_Palette,
&pTexture))) { return(false); }
SAFE_RELEASE(pTexture);
return(true);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -