mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-12-14 14:11:14 +00:00
636 lines
17 KiB
C++
636 lines
17 KiB
C++
|
/*
|
||
|
** fb_d3d9_wipe.cpp
|
||
|
** Implements the different screen wipes using Direct3D calls.
|
||
|
**
|
||
|
**---------------------------------------------------------------------------
|
||
|
** Copyright 1998-2008 Randy Heit
|
||
|
** All rights reserved.
|
||
|
**
|
||
|
** Redistribution and use in source and binary forms, with or without
|
||
|
** modification, are permitted provided that the following conditions
|
||
|
** are met:
|
||
|
**
|
||
|
** 1. Redistributions of source code must retain the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer.
|
||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer in the
|
||
|
** documentation and/or other materials provided with the distribution.
|
||
|
** 3. The name of the author may not be used to endorse or promote products
|
||
|
** derived from this software without specific prior written permission.
|
||
|
**
|
||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
**---------------------------------------------------------------------------
|
||
|
**
|
||
|
*/
|
||
|
|
||
|
// HEADER FILES ------------------------------------------------------------
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#define D3D_DEBUG_INFO
|
||
|
#endif
|
||
|
#define DIRECT3D_VERSION 0x0900
|
||
|
#define WIN32_LEAN_AND_MEAN
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <d3d9.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#define USE_WINDOWS_DWORD
|
||
|
#include "doomtype.h"
|
||
|
#include "f_wipe.h"
|
||
|
#include "win32iface.h"
|
||
|
#include "templates.h"
|
||
|
#include "m_random.h"
|
||
|
|
||
|
// MACROS ------------------------------------------------------------------
|
||
|
|
||
|
// TYPES -------------------------------------------------------------------
|
||
|
|
||
|
class D3DFB::Wiper_Crossfade : public D3DFB::Wiper
|
||
|
{
|
||
|
public:
|
||
|
Wiper_Crossfade();
|
||
|
bool Run(int ticks, D3DFB *fb);
|
||
|
|
||
|
private:
|
||
|
int Clock;
|
||
|
};
|
||
|
|
||
|
class D3DFB::Wiper_Melt : public D3DFB::Wiper
|
||
|
{
|
||
|
public:
|
||
|
Wiper_Melt();
|
||
|
bool Run(int ticks, D3DFB *fb);
|
||
|
|
||
|
private:
|
||
|
// Match the strip sizes that oldschool Doom used.
|
||
|
static const int WIDTH = 160, HEIGHT = 200;
|
||
|
int y[WIDTH];
|
||
|
};
|
||
|
|
||
|
class D3DFB::Wiper_Burn : public D3DFB::Wiper
|
||
|
{
|
||
|
public:
|
||
|
Wiper_Burn(D3DFB *fb);
|
||
|
~Wiper_Burn();
|
||
|
bool Run(int ticks, D3DFB *fb);
|
||
|
|
||
|
private:
|
||
|
static const int WIDTH = 64, HEIGHT = 64;
|
||
|
BYTE BurnArray[WIDTH * (HEIGHT + 5)];
|
||
|
IDirect3DTexture9 *BurnTexture;
|
||
|
int Density;
|
||
|
int BurnTime;
|
||
|
|
||
|
struct BURNVERTEX
|
||
|
{
|
||
|
FLOAT x, y, z, rhw;
|
||
|
FLOAT tu0, tv0;
|
||
|
FLOAT tu1, tv1;
|
||
|
};
|
||
|
#define D3DFVF_BURNVERTEX (D3DFVF_XYZRHW|D3DFVF_TEX2)
|
||
|
};
|
||
|
|
||
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||
|
|
||
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||
|
|
||
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||
|
|
||
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||
|
|
||
|
EXTERN_CVAR(Bool, test2d)
|
||
|
|
||
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||
|
|
||
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||
|
|
||
|
// CODE --------------------------------------------------------------------
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: WipeStartScreen
|
||
|
//
|
||
|
// Called before the current screen has started rendering. This needs to
|
||
|
// save what was drawn the previous frame so that it can be animated into
|
||
|
// what gets drawn this frame.
|
||
|
//
|
||
|
// In fullscreen mode, we use GetFrontBufferData() to grab the data that
|
||
|
// is visible on screen right now.
|
||
|
//
|
||
|
// In windowed mode, we can't do that because we'll get the whole desktop.
|
||
|
// Instead, we can conveniently use the TempRenderTexture, which is normally
|
||
|
// used for gamma-correcting copying the image to the back buffer.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
bool D3DFB::WipeStartScreen(int type)
|
||
|
{
|
||
|
IDirect3DSurface9 *surf, *tsurf;
|
||
|
D3DSURFACE_DESC desc;
|
||
|
|
||
|
if (!test2d)
|
||
|
{
|
||
|
return Super::WipeStartScreen(type);
|
||
|
}
|
||
|
|
||
|
switch (type)
|
||
|
{
|
||
|
case wipe_Melt:
|
||
|
ScreenWipe = new Wiper_Melt;
|
||
|
break;
|
||
|
|
||
|
case wipe_Burn:
|
||
|
ScreenWipe = new Wiper_Burn(this);
|
||
|
break;
|
||
|
|
||
|
case wipe_Fade:
|
||
|
ScreenWipe = new Wiper_Crossfade;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (Windowed)
|
||
|
{
|
||
|
// The InitialWipeScreen must have the same pixel format as
|
||
|
// the TempRenderTexture.
|
||
|
if (FAILED(TempRenderTexture->GetSurfaceLevel(0, &tsurf)) ||
|
||
|
FAILED(tsurf->GetDesc(&desc)))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (FAILED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &tsurf)) ||
|
||
|
FAILED(tsurf->GetDesc(&desc)))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
// GetFrontBufferData works only with this format
|
||
|
desc.Format = D3DFMT_A8R8G8B8;
|
||
|
}
|
||
|
|
||
|
if (FAILED(D3DDevice->CreateTexture(desc.Width, desc.Height, 1, 0,
|
||
|
desc.Format, D3DPOOL_SYSTEMMEM, &InitialWipeScreen, NULL)))
|
||
|
{
|
||
|
InitialWipeScreen = NULL;
|
||
|
return false;
|
||
|
}
|
||
|
if (FAILED(InitialWipeScreen->GetSurfaceLevel(0, &surf)))
|
||
|
{
|
||
|
InitialWipeScreen->Release();
|
||
|
InitialWipeScreen = NULL;
|
||
|
return false;
|
||
|
}
|
||
|
if (!Windowed)
|
||
|
{
|
||
|
if (FAILED(D3DDevice->GetFrontBufferData(0, surf)))
|
||
|
{
|
||
|
InitialWipeScreen->Release();
|
||
|
InitialWipeScreen = NULL;
|
||
|
return false;
|
||
|
}
|
||
|
FinalWipeScreen = TempRenderTexture;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (FAILED(TempRenderTexture->GetSurfaceLevel(0, &tsurf)) ||
|
||
|
FAILED(D3DDevice->GetRenderTargetData(tsurf, surf)))
|
||
|
{
|
||
|
InitialWipeScreen->Release();
|
||
|
InitialWipeScreen = NULL;
|
||
|
return false;
|
||
|
}
|
||
|
// Create another texture to copy the final wipe screen to so
|
||
|
// we can still gamma correct the wipe. Since this is just for
|
||
|
// gamma correction, it's okay to fail (though not desirable.)
|
||
|
if (GammaFixerShader != NULL && Gamma != 1)
|
||
|
{
|
||
|
if (FAILED(tsurf->GetDesc(&desc)) ||
|
||
|
FAILED(D3DDevice->CreateTexture(desc.Width, desc.Height,
|
||
|
1, D3DUSAGE_RENDERTARGET, desc.Format, D3DPOOL_DEFAULT,
|
||
|
&FinalWipeScreen, NULL)))
|
||
|
{
|
||
|
FinalWipeScreen = TempRenderTexture;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FinalWipeScreen = TempRenderTexture;
|
||
|
}
|
||
|
}
|
||
|
// Even fullscreen will render to the TempRenderTexture, so we can have
|
||
|
// a copy of the new screen readily available.
|
||
|
GatheringWipeScreen = true;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: WipeEndScreen
|
||
|
//
|
||
|
// The screen we want to animate to has just been drawn. This function is
|
||
|
// called in place of Update(), so it has not been Presented yet.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void D3DFB::WipeEndScreen()
|
||
|
{
|
||
|
if (!test2d)
|
||
|
{
|
||
|
Super::WipeEndScreen();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Don't do anything if there is no starting point.
|
||
|
if (InitialWipeScreen == NULL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If the whole screen was drawn without 2D accel, get it in to
|
||
|
// video memory now.
|
||
|
if (!In2D)
|
||
|
{
|
||
|
Begin2D();
|
||
|
}
|
||
|
|
||
|
// Don't do anything if there is no ending point.
|
||
|
if (OldRenderTarget == NULL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If these are different, reverse their roles so we don't need to
|
||
|
// waste time copying from TempRenderTexture to FinalWipeScreen.
|
||
|
swap(FinalWipeScreen, TempRenderTexture);
|
||
|
|
||
|
// At this point, InitialWipeScreen holds the screen we are wiping from.
|
||
|
// FinalWipeScreen holds the screen we are wiping to, which may be the
|
||
|
// same texture as TempRenderTexture.
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: WipeDo
|
||
|
//
|
||
|
// Perform the actual wipe animation. The number of tics since the last
|
||
|
// time this function was called is passed in. Returns true when the wipe
|
||
|
// is over. The first time this function has been called, the screen is
|
||
|
// still locked from before and EndScene() still has not been called.
|
||
|
// Successive times need to call BeginScene().
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
bool D3DFB::WipeDo(int ticks)
|
||
|
{
|
||
|
if (!test2d)
|
||
|
{
|
||
|
return Super::WipeDo(ticks);
|
||
|
}
|
||
|
|
||
|
// Sanity checks.
|
||
|
if (InitialWipeScreen == NULL || FinalWipeScreen == NULL)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
if (GatheringWipeScreen)
|
||
|
{ // This is the first time we've been called for this wipe.
|
||
|
GatheringWipeScreen = false;
|
||
|
|
||
|
if (OldRenderTarget == NULL)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
D3DDevice->SetRenderTarget(0, OldRenderTarget);
|
||
|
}
|
||
|
else
|
||
|
{ // This is the second or later time we've been called for this wipe.
|
||
|
D3DDevice->BeginScene();
|
||
|
}
|
||
|
OldRenderTarget = NULL;
|
||
|
if (TempRenderTexture != NULL && TempRenderTexture != FinalWipeScreen &&
|
||
|
((Windowed && GammaFixerShader) || GatheringWipeScreen))
|
||
|
{
|
||
|
IDirect3DSurface9 *targetsurf;
|
||
|
if (FAILED(TempRenderTexture->GetSurfaceLevel(0, &targetsurf)) ||
|
||
|
FAILED(D3DDevice->GetRenderTarget(0, &OldRenderTarget)) ||
|
||
|
FAILED(D3DDevice->SetRenderTarget(0, targetsurf)))
|
||
|
{
|
||
|
// Setting the render target failed.
|
||
|
OldRenderTarget = NULL;
|
||
|
}
|
||
|
}
|
||
|
In2D = 2;
|
||
|
|
||
|
bool done = ScreenWipe->Run(ticks, this);
|
||
|
DrawLetterbox();
|
||
|
return done;
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: WipeCleanup
|
||
|
//
|
||
|
// Release any resources that were specifically created for the wipe.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void D3DFB::WipeCleanup()
|
||
|
{
|
||
|
if (ScreenWipe != NULL)
|
||
|
{
|
||
|
delete ScreenWipe;
|
||
|
ScreenWipe = NULL;
|
||
|
}
|
||
|
if (InitialWipeScreen != NULL)
|
||
|
{
|
||
|
InitialWipeScreen->Release();
|
||
|
InitialWipeScreen = NULL;
|
||
|
}
|
||
|
if (FinalWipeScreen != NULL && FinalWipeScreen != TempRenderTexture)
|
||
|
{
|
||
|
FinalWipeScreen->Release();
|
||
|
}
|
||
|
FinalWipeScreen = NULL;
|
||
|
GatheringWipeScreen = false;
|
||
|
if (!test2d)
|
||
|
{
|
||
|
Super::WipeCleanup();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: Wiper Constructor
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
D3DFB::Wiper::~Wiper()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// WIPE: CROSSFADE ---------------------------------------------------------
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: Wiper_Crossfade Constructor
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
D3DFB::Wiper_Crossfade::Wiper_Crossfade()
|
||
|
: Clock(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: Wiper_Crossfade :: Run
|
||
|
//
|
||
|
// Fades the old screen into the new one over 32 ticks.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
bool D3DFB::Wiper_Crossfade::Run(int ticks, D3DFB *fb)
|
||
|
{
|
||
|
Clock += ticks;
|
||
|
|
||
|
// Put the initial screen back to the buffer, presumably with DMA.
|
||
|
IDirect3DSurface9 *source, *target;
|
||
|
|
||
|
if (SUCCEEDED(fb->InitialWipeScreen->GetSurfaceLevel(0, &source)) &&
|
||
|
SUCCEEDED(fb->D3DDevice->GetRenderTarget(0, &target)))
|
||
|
{
|
||
|
fb->D3DDevice->UpdateSurface(source, NULL, target, NULL);
|
||
|
}
|
||
|
|
||
|
// Draw the new screen on top of it.
|
||
|
fb->D3DDevice->SetStreamSource(0, fb->VertexBuffer, 0, sizeof(FBVERTEX));
|
||
|
fb->D3DDevice->SetFVF(D3DFVF_FBVERTEX);
|
||
|
fb->SetTexture(0, fb->FinalWipeScreen);
|
||
|
fb->SetAlphaBlend(TRUE, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA);
|
||
|
fb->SetConstant(0, 0, 0, 0, clamp(Clock / 32.f, 0.f, 1.f));
|
||
|
fb->SetConstant(1, 1, 1, 1, 0);
|
||
|
fb->SetPixelShader(fb->PlainShader);
|
||
|
// FIXME: The FinalWipeScreen gets junk at the top in letterbox modes.
|
||
|
fb->D3DDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
|
||
|
|
||
|
return Clock >= 32;
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: Wiper_Melt Constructor
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
D3DFB::Wiper_Melt::Wiper_Melt()
|
||
|
{
|
||
|
int i, r;
|
||
|
|
||
|
// setup initial column positions
|
||
|
// (y<0 => not ready to scroll yet)
|
||
|
y[0] = -(M_Random() & 15);
|
||
|
for (i = 1; i < WIDTH; ++i)
|
||
|
{
|
||
|
r = (M_Random()%3) - 1;
|
||
|
y[i] = clamp(y[i-1] + r, -15, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: Wiper_Melt :: Run
|
||
|
//
|
||
|
// Fades the old screen into the new one over 32 ticks.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
bool D3DFB::Wiper_Melt::Run(int ticks, D3DFB *fb)
|
||
|
{
|
||
|
IDirect3DSurface9 *source, *target;
|
||
|
|
||
|
if (FAILED(fb->InitialWipeScreen->GetSurfaceLevel(0, &source)) ||
|
||
|
FAILED(fb->D3DDevice->GetRenderTarget(0, &target)))
|
||
|
{
|
||
|
// A fat lot of good we can do if we can't get these two surfaces.
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Draw the new screen on the bottom.
|
||
|
fb->D3DDevice->SetStreamSource(0, fb->VertexBuffer, 0, sizeof(FBVERTEX));
|
||
|
fb->D3DDevice->SetFVF(D3DFVF_FBVERTEX);
|
||
|
fb->SetTexture(0, fb->FinalWipeScreen);
|
||
|
fb->SetAlphaBlend(FALSE);
|
||
|
fb->SetConstant(0, 0, 0, 0, 0);
|
||
|
fb->SetConstant(1, 1, 1, 1, 1);
|
||
|
fb->SetPixelShader(fb->PlainShader);
|
||
|
fb->D3DDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
|
||
|
|
||
|
int i, dy;
|
||
|
bool done;
|
||
|
|
||
|
// Copy the old screen in vertical strips on top of the new one.
|
||
|
while (ticks--)
|
||
|
{
|
||
|
done = true;
|
||
|
for (i = 0; i < WIDTH; i++)
|
||
|
{
|
||
|
if (y[i] < 0)
|
||
|
{
|
||
|
y[i]++;
|
||
|
done = false;
|
||
|
}
|
||
|
else if (y[i] < HEIGHT)
|
||
|
{
|
||
|
dy = (y[i] < 16) ? y[i]+1 : 8;
|
||
|
y[i] = MIN(y[i] + dy, HEIGHT);
|
||
|
done = false;
|
||
|
}
|
||
|
if (ticks == 0)
|
||
|
{ // Only draw for the final tick.
|
||
|
RECT rect;
|
||
|
POINT dpt;
|
||
|
|
||
|
dpt.x = i * fb->Width / WIDTH;
|
||
|
dpt.y = MAX(0, y[i] * fb->Height / HEIGHT);
|
||
|
rect.left = dpt.x;
|
||
|
rect.top = 0;
|
||
|
rect.right = (i + 1) * fb->Width / WIDTH;
|
||
|
rect.bottom = fb->Height - dpt.y;
|
||
|
if (rect.bottom > rect.top)
|
||
|
{
|
||
|
fb->D3DDevice->UpdateSurface(source, &rect, target, &dpt);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return done;
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: Wiper_Burn Constructor
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
D3DFB::Wiper_Burn::Wiper_Burn(D3DFB *fb)
|
||
|
{
|
||
|
Density = 4;
|
||
|
BurnTime = 0;
|
||
|
memset(BurnArray, 0, sizeof(BurnArray));
|
||
|
if (fb->BurnShader == NULL || FAILED(fb->D3DDevice->CreateTexture(WIDTH, HEIGHT, 1,
|
||
|
D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &BurnTexture, NULL)))
|
||
|
{
|
||
|
BurnTexture = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: Wiper_Burn Destructor
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
D3DFB::Wiper_Burn::~Wiper_Burn()
|
||
|
{
|
||
|
if (BurnTexture != NULL)
|
||
|
{
|
||
|
BurnTexture->Release();
|
||
|
BurnTexture = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// D3DFB :: Wiper_Burn :: Run
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
bool D3DFB::Wiper_Burn::Run(int ticks, D3DFB *fb)
|
||
|
{
|
||
|
bool done;
|
||
|
|
||
|
BurnTime += ticks;
|
||
|
ticks *= 2;
|
||
|
|
||
|
// Make the fire burn
|
||
|
done = false;
|
||
|
while (!done && ticks--)
|
||
|
{
|
||
|
Density = wipe_CalcBurn(BurnArray, WIDTH, HEIGHT, Density);
|
||
|
done = (Density < 0);
|
||
|
}
|
||
|
|
||
|
// Update the burn texture with the new burn data
|
||
|
D3DLOCKED_RECT lrect;
|
||
|
if (SUCCEEDED(BurnTexture->LockRect(0, &lrect, NULL, D3DLOCK_DISCARD)))
|
||
|
{
|
||
|
const BYTE *src = BurnArray;
|
||
|
BYTE *dest = (BYTE *)lrect.pBits;
|
||
|
for (int y = HEIGHT; y != 0; --y)
|
||
|
{
|
||
|
for (int x = WIDTH; x != 0; --x)
|
||
|
{
|
||
|
*dest++ = *src++;
|
||
|
}
|
||
|
dest += lrect.Pitch - WIDTH;
|
||
|
}
|
||
|
BurnTexture->UnlockRect(0);
|
||
|
}
|
||
|
|
||
|
// Put the initial screen back to the buffer.
|
||
|
IDirect3DSurface9 *source, *target;
|
||
|
|
||
|
if (SUCCEEDED(fb->InitialWipeScreen->GetSurfaceLevel(0, &source)) &&
|
||
|
SUCCEEDED(fb->D3DDevice->GetRenderTarget(0, &target)))
|
||
|
{
|
||
|
fb->D3DDevice->UpdateSurface(source, NULL, target, NULL);
|
||
|
}
|
||
|
|
||
|
// Burn the new screen on top of it.
|
||
|
float top = fb->LBOffset - 0.5f;
|
||
|
float right = float(fb->Width) - 0.5f;
|
||
|
float bot = float(fb->Height) + top;
|
||
|
float texright = float(fb->Width) / float(fb->FBWidth);
|
||
|
float texbot = float(fb->Height) / float(fb->FBHeight);
|
||
|
|
||
|
BURNVERTEX verts[4] =
|
||
|
{
|
||
|
{ -0.5f, top, 0.5f, 1.f, 0.f, 0.f, 0, 0 },
|
||
|
{ right, top, 0.5f, 1.f, texright, 0.f, 1, 0 },
|
||
|
{ right, bot, 0.5f, 1.f, texright, texbot, 1, 1 },
|
||
|
{ -0.5f, bot, 0.5f, 1.f, 0.f, texbot, 0, 1 }
|
||
|
};
|
||
|
|
||
|
fb->D3DDevice->SetFVF(D3DFVF_BURNVERTEX);
|
||
|
fb->SetTexture(0, fb->FinalWipeScreen);
|
||
|
fb->SetTexture(1, BurnTexture);
|
||
|
fb->SetAlphaBlend(TRUE, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA);
|
||
|
fb->SetPixelShader(fb->BurnShader);
|
||
|
fb->D3DDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
||
|
fb->D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
|
||
|
fb->D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
|
||
|
fb->D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(BURNVERTEX));
|
||
|
fb->D3DDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
|
||
|
fb->D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
|
||
|
fb->D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
|
||
|
|
||
|
// The fire may not always stabilize, so the wipe is forced to end
|
||
|
// after an arbitrary maximum time.
|
||
|
return done || (BurnTime > 40);
|
||
|
}
|