diff --git a/docs/rh-log.txt b/docs/rh-log.txt index c5420e390..2aa9ad5f0 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,12 @@ +December 31, 2007 +- Removed the screenshot parameter from D_Display(), since it was a relic of + a long-abandoned experiment to write directly to video memory instead of + to a temporary buffer in system meroy. +- Added Direct3D versions of the melt and burn screenwipes. +- Fixed the strip sizes for the melt screenwipe to match what Doom would have + produced on a 320x200 screen, rather than producing more, thinner strips + for wider screens. + December 29, 2007 - Started writing Direct3D-friendly wipe code. It's just a Q&D crossfade for now. diff --git a/src/c_console.cpp b/src/c_console.cpp index c18ce93da..df1b187b5 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -228,7 +228,8 @@ CUSTOM_CVAR (Int, msgmidcolor2, 4, CVAR_ARCHIVE) static void maybedrawnow (bool tick, bool force) { - if (ConsoleDrawing || !gotconback || screen->IsLocked ()) + // FIXME: Does not work right with hw2d + if (ConsoleDrawing || !gotconback || screen->IsLocked () || 1) { return; } diff --git a/src/d_main.cpp b/src/d_main.cpp index 936f31cb4..bd01b7f1f 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -438,7 +438,7 @@ CVAR (Flag, compat_invisibility,compatflags, COMPATF_INVISIBILITY); // //========================================================================== -void D_Display (bool screenshot) +void D_Display () { bool wipe; bool hw2d; @@ -489,7 +489,7 @@ void D_Display (bool screenshot) } setmodeneeded = false; - if (screen->Lock (screenshot)) + if (screen->Lock (false)) { SB_state = screen->GetPageCount (); BorderNeedRefresh = screen->GetPageCount (); @@ -506,7 +506,6 @@ void D_Display (bool screenshot) else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL) { // save the current screen if about to wipe BorderNeedRefresh = screen->GetPageCount (); - wipe = true; if (wipegamestate != GS_FORCEWIPEFADE) { wipe = screen->WipeStartScreen (wipetype); @@ -536,14 +535,10 @@ void D_Display (bool screenshot) { case GS_FULLCONSOLE: screen->SetBlendingRect(0,0,0,0); - if (!screenshot) - { - hw2d = screen->Begin2D(); - } + hw2d = screen->Begin2D(); C_DrawConsole (false); M_Drawer (); - if (!screenshot) - screen->Update (); + screen->Update (); return; case GS_LEVEL: @@ -573,13 +568,10 @@ void D_Display (bool screenshot) { AM_Drawer (); } - if (!screenshot) + if ((hw2d = screen->Begin2D())) { - if ((hw2d = screen->Begin2D())) - { - // Redraw the status bar every frame when using 2D accel - SB_state = screen->GetPageCount(); - } + // Redraw the status bar every frame when using 2D accel + SB_state = screen->GetPageCount(); } if (realviewheight == SCREENHEIGHT && viewactive) { @@ -596,30 +588,21 @@ void D_Display (bool screenshot) case GS_INTERMISSION: screen->SetBlendingRect(0,0,0,0); - if (!screenshot) - { - screen->Begin2D(); - } + hw2d = screen->Begin2D(); WI_Drawer (); CT_Drawer (); break; case GS_FINALE: screen->SetBlendingRect(0,0,0,0); - if (!screenshot) - { - screen->Begin2D(); - } + hw2d = screen->Begin2D(); F_Drawer (); CT_Drawer (); break; case GS_DEMOSCREEN: screen->SetBlendingRect(0,0,0,0); - if (!screenshot) - { - screen->Begin2D(); - } + hw2d = screen->Begin2D(); D_PageDrawer (); CT_Drawer (); break; @@ -655,17 +638,14 @@ void D_Display (bool screenshot) NoWipe = 10; } - if (!wipe || screenshot || NoWipe < 0) + if (!wipe || NoWipe < 0) { NetUpdate (); // send out any new accumulation // normal update C_DrawConsole (hw2d); // draw console M_Drawer (); // menu is drawn even on top of everything FStat::PrintStat (); - if (!screenshot) - { - screen->Update (); // page flip or blit buffer - } + screen->Update (); // page flip or blit buffer } else { @@ -792,7 +772,7 @@ void D_DoomLoop () S_UpdateSounds (players[consoleplayer].camera); // move positional sounds // Update display, next frame, with current state. I_StartTic (); - D_Display (false); + D_Display (); } catch (CRecoverableError &error) { diff --git a/src/d_main.h b/src/d_main.h index 9a595be8b..16e070871 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -38,8 +38,7 @@ void D_DoomMain (void); // Called by IO functions when input is detected. void D_PostEvent (const event_t* ev); -// [RH] Passed true if just drawing for a screenshot -void D_Display (bool screenshot); +void D_Display (); // diff --git a/src/f_wipe.cpp b/src/f_wipe.cpp index 62d6ce01c..e203f4a6f 100644 --- a/src/f_wipe.cpp +++ b/src/f_wipe.cpp @@ -53,6 +53,10 @@ static int fade; // Melt ------------------------------------------------------------- +// Match the strip sizes that oldschool Doom used on a 320x200 screen. +#define MELT_WIDTH 160 +#define MELT_HEIGHT 200 + void wipe_shittyColMajorXform (short *array) { int x, y; @@ -84,14 +88,12 @@ bool wipe_initMelt (int ticks) // setup initial column positions // (y<0 => not ready to scroll yet) - y = new int[SCREENWIDTH*sizeof(int)]; - y[0] = -(M_Random()&0xf); - for (i = 1; i < SCREENWIDTH; i++) + y = new int[MELT_WIDTH]; + y[0] = -(M_Random() & 15); + for (i = 1; i < MELT_WIDTH; i++) { r = (M_Random()%3) - 1; - y[i] = y[i-1] + r; - if (y[i] > 0) y[i] = 0; - else if (y[i] == -16) y[i] = -15; + y[i] = clamp(y[i-1] + r, -15, 0); } return 0; @@ -99,50 +101,51 @@ bool wipe_initMelt (int ticks) bool wipe_doMelt (int ticks) { - int i; - int j; - int dy; - int idx; - - short* s; - short* d; - bool done = true; - - int width = SCREENWIDTH / 2; + int i, j, dy, x; + const short *s; + short *d; + bool done; while (ticks--) { - for (i = 0; i < width; i++) + done = true; + for (i = 0; i < MELT_WIDTH; i++) { if (y[i] < 0) { - y[i]++; done = false; - } - else if (y[i] < SCREENHEIGHT) - { - int pitch = screen->GetPitch() / 2; - dy = (y[i] < 16) ? y[i]+1 : 8; - dy = (dy * SCREENHEIGHT) / 200; - if (y[i]+dy >= SCREENHEIGHT) - dy = SCREENHEIGHT - y[i]; - s = &wipe_scr_end[i*SCREENHEIGHT+y[i]]; - d = &((short *)screen->GetBuffer())[y[i]*pitch+i]; - idx = 0; - for (j=dy;j;j--) - { - d[idx] = *(s++); - idx += pitch; - } - y[i] += dy; - s = &wipe_scr_start[i*SCREENHEIGHT]; - d = &((short *)screen->GetBuffer())[y[i]*pitch+i]; - idx = 0; - for (j=SCREENHEIGHT-y[i];j;j--) - { - d[idx] = *(s++); - idx += pitch; - } + y[i]++; done = false; + } + else if (y[i] < MELT_HEIGHT) + { + dy = (y[i] < 16) ? y[i]+1 : 8; + y[i] = MIN(y[i] + dy, MELT_HEIGHT); + done = false; + } + if (ticks == 0 && y[i] >= 0) + { // Only draw for the final tick. + const int pitch = screen->GetPitch() / 2; + int sy = y[i] * SCREENHEIGHT / MELT_HEIGHT; + + for (x = i * (SCREENWIDTH/2) / MELT_WIDTH; x < (i + 1) * (SCREENWIDTH/2) / MELT_WIDTH; ++x) + { + s = &wipe_scr_end[x*SCREENHEIGHT]; + d = &((short *)screen->GetBuffer())[x]; + + for (j = sy; j != 0; --j) + { + *d = *(s++); + d += pitch; + } + + s = &wipe_scr_start[x*SCREENHEIGHT]; + + for (j = SCREENHEIGHT - sy; j != 0; --j) + { + *d = *(s++); + d += pitch; + } + } } } } @@ -167,136 +170,147 @@ bool wipe_initBurn (int ticks) return 0; } +int wipe_CalcBurn (BYTE *burnarray, int width, int height, int density) +{ + // This is a modified version of the fire that was once used + // on the player setup menu. + static int voop; + + int a, b; + BYTE *from; + + // generator + from = &burnarray[width * height]; + b = voop; + voop += density / 3; + for (a = 0; a < density/8; a++) + { + unsigned int offs = (a+b) & (width - 1); + unsigned int v = M_Random(); + v = MIN(from[offs] + 4 + (v & 15) + (v >> 3) + (M_Random() & 31), 255u); + from[offs] = from[width*2 + ((offs + width*3/2) & (width - 1))] = v; + } + + density = MIN(density + 10, width * 7); + + from = burnarray; + for (b = 0; b <= height; b += 2) + { + BYTE *pixel = from; + + // special case: first pixel on line + BYTE *p = pixel + (width << 1); + unsigned int top = *p + *(p + width - 1) + *(p + 1); + unsigned int bottom = *(pixel + (width << 2)); + unsigned int c1 = (top + bottom) >> 2; + if (c1 > 1) c1--; + *pixel = c1; + *(pixel + width) = (c1 + bottom) >> 1; + pixel++; + + // main line loop + for (a = 1; a < width-1; a++) + { + // sum top pixels + p = pixel + (width << 1); + top = *p + *(p - 1) + *(p + 1); + + // bottom pixel + bottom = *(pixel + (width << 2)); + + // combine pixels + c1 = (top + bottom) >> 2; + if (c1 > 1) c1--; + + // store pixels + *pixel = c1; + *(pixel + width) = (c1 + bottom) >> 1; // interpolate + + // next pixel + pixel++; + } + + // special case: last pixel on line + p = pixel + (width << 1); + top = *p + *(p - 1) + *(p - width + 1); + bottom = *(pixel + (width << 2)); + c1 = (top + bottom) >> 2; + if (c1 > 1) c1--; + *pixel = c1; + *(pixel + width) = (c1 + bottom) >> 1; + + // next line + from += width << 1; + } + + // Check for done-ness. (Every pixel with level 126 or higher counts as done.) + for (a = width * height, from = burnarray; a != 0; --a, ++from) + { + if (*from < 126) + { + return density; + } + } + return -1; +} + bool wipe_doBurn (int ticks) { - static int voop; bool done; - // This is a modified version of the fire from the player - // setup menu. burntime += ticks; ticks *= 2; // Make the fire burn - while (ticks--) + done = false; + while (!done && ticks--) { - int a, b; - BYTE *from; - - // generator - from = burnarray + FIREHEIGHT * FIREWIDTH; - b = voop; - voop += density / 3; - for (a = 0; a < density/8; a++) - { - unsigned int offs = (a+b) % FIREWIDTH; - unsigned int v = M_Random(); - v = from[offs] + 4 + (v & 15) + (v >> 3) + (M_Random() & 31); - if (v > 255) - v = 255; - from[offs] = from[FIREWIDTH*2 + (offs + FIREWIDTH*3/2)%FIREWIDTH] = v; - } - - density += 10; - if (density > FIREWIDTH*7) - density = FIREWIDTH*7; - - from = burnarray; - for (b = 0; b <= FIREHEIGHT; b += 2) - { - BYTE *pixel = from; - - // special case: first pixel on line - BYTE *p = pixel + (FIREWIDTH << 1); - unsigned int top = *p + *(p + FIREWIDTH - 1) + *(p + 1); - unsigned int bottom = *(pixel + (FIREWIDTH << 2)); - unsigned int c1 = (top + bottom) >> 2; - if (c1 > 1) c1--; - *pixel = c1; - *(pixel + FIREWIDTH) = (c1 + bottom) >> 1; - pixel++; - - // main line loop - for (a = 1; a < FIREWIDTH-1; a++) - { - // sum top pixels - p = pixel + (FIREWIDTH << 1); - top = *p + *(p - 1) + *(p + 1); - - // bottom pixel - bottom = *(pixel + (FIREWIDTH << 2)); - - // combine pixels - c1 = (top + bottom) >> 2; - if (c1 > 1) c1--; - - // store pixels - *pixel = c1; - *(pixel + FIREWIDTH) = (c1 + bottom) >> 1; // interpolate - - // next pixel - pixel++; - } - - // special case: last pixel on line - p = pixel + (FIREWIDTH << 1); - top = *p + *(p - 1) + *(p - FIREWIDTH + 1); - bottom = *(pixel + (FIREWIDTH << 2)); - c1 = (top + bottom) >> 2; - if (c1 > 1) c1--; - *pixel = c1; - *(pixel + FIREWIDTH) = (c1 + bottom) >> 1; - - // next line - from += FIREWIDTH << 1; - } + density = wipe_CalcBurn(burnarray, FIREWIDTH, FIREHEIGHT, density); + done = (density < 0); } // Draw the screen + fixed_t xstep, ystep, firex, firey; + int x, y; + BYTE *to, *fromold, *fromnew; + + xstep = (FIREWIDTH * FRACUNIT) / SCREENWIDTH; + ystep = (FIREHEIGHT * FRACUNIT) / SCREENHEIGHT; + to = screen->GetBuffer(); + fromold = (BYTE *)wipe_scr_start; + fromnew = (BYTE *)wipe_scr_end; + + for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep) { - fixed_t xstep, ystep, firex, firey; - int x, y; - BYTE *to, *fromold, *fromnew; - - xstep = (FIREWIDTH * FRACUNIT) / SCREENWIDTH; - ystep = (FIREHEIGHT * FRACUNIT) / SCREENHEIGHT; - to = screen->GetBuffer(); - fromold = (BYTE *)wipe_scr_start; - fromnew = (BYTE *)wipe_scr_end; - done = true; - - for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep) + for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep) { - for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep) - { - int fglevel; + int fglevel; - fglevel = burnarray[(firex>>FRACBITS)+(firey>>FRACBITS)*FIREWIDTH] / 2; - if (fglevel >= 63) - { - to[x] = fromnew[x]; - } - else if (fglevel == 0) - { - to[x] = fromold[x]; - done = false; - } - else - { - int bglevel = 64-fglevel; - DWORD *fg2rgb = Col2RGB8[fglevel]; - DWORD *bg2rgb = Col2RGB8[bglevel]; - DWORD fg = fg2rgb[fromnew[x]]; - DWORD bg = bg2rgb[fromold[x]]; - fg = (fg+bg) | 0x1f07c1f; - to[x] = RGB32k[0][0][fg & (fg>>15)]; - done = false; - } + fglevel = burnarray[(firex>>FRACBITS)+(firey>>FRACBITS)*FIREWIDTH] / 2; + if (fglevel >= 63) + { + to[x] = fromnew[x]; + } + else if (fglevel == 0) + { + to[x] = fromold[x]; + done = false; + } + else + { + int bglevel = 64-fglevel; + DWORD *fg2rgb = Col2RGB8[fglevel]; + DWORD *bg2rgb = Col2RGB8[bglevel]; + DWORD fg = fg2rgb[fromnew[x]]; + DWORD bg = bg2rgb[fromold[x]]; + fg = (fg+bg) | 0x1f07c1f; + to[x] = RGB32k[0][0][fg & (fg>>15)]; + done = false; } - fromold += SCREENWIDTH; - fromnew += SCREENWIDTH; - to += SCREENPITCH; } + fromold += SCREENWIDTH; + fromnew += SCREENWIDTH; + to += SCREENPITCH; } return done || (burntime > 40); @@ -318,7 +332,7 @@ bool wipe_initFade (int ticks) bool wipe_doFade (int ticks) { - fade += ticks; + fade += ticks * 2; if (fade > 64) { screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_end); @@ -348,7 +362,6 @@ bool wipe_doFade (int ticks) to += SCREENPITCH; } } - fade++; return false; } diff --git a/src/f_wipe.h b/src/f_wipe.h index 0e7f70f40..bf5586653 100644 --- a/src/f_wipe.h +++ b/src/f_wipe.h @@ -32,6 +32,10 @@ void wipe_EndScreen (void); bool wipe_ScreenWipe (int ticks); void wipe_Cleanup (); +// The buffer must have an additional 5 rows not included in height +// to use for a seeding area. +int wipe_CalcBurn (BYTE *buffer, int width, int height, int density); + enum { wipe_None, // don't bother diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index ddf3197da..a9c768629 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -77,13 +77,6 @@ IMPLEMENT_CLASS(D3DFB) -struct FBVERTEX -{ - FLOAT x, y, z, rhw; - FLOAT tu, tv; -}; -#define D3DFVF_FBVERTEX (D3DFVF_XYZRHW|D3DFVF_TEX1) - class D3DTex : public FNativeTexture { public: @@ -143,7 +136,6 @@ extern bool VidResizing; EXTERN_CVAR (Bool, fullscreen) EXTERN_CVAR (Float, Gamma) -EXTERN_CVAR (Int, vid_displaybits) EXTERN_CVAR (Bool, vid_vsync) EXTERN_CVAR (Float, transsouls) @@ -183,6 +175,7 @@ D3DFB::D3DFB (int width, int height, bool fullscreen) PlainStencilShader = NULL; DimShader = NULL; GammaFixerShader = NULL; + BurnShader = NULL; FBFormat = D3DFMT_UNKNOWN; PalFormat = D3DFMT_UNKNOWN; VSync = vid_vsync; @@ -196,6 +189,7 @@ D3DFB::D3DFB (int width, int height, bool fullscreen) Textures = NULL; Accel2D = true; GatheringWipeScreen = false; + ScreenWipe = NULL; Gamma = 1.0; FlashConstants[0][3] = FlashConstants[0][2] = FlashConstants[0][1] = FlashConstants[0][0] = 0; @@ -263,8 +257,6 @@ D3DFB::D3DFB (int width, int height, bool fullscreen) D3DFB::~D3DFB () { - KillNativeTexs(); - KillNativePals(); ReleaseResources (); if (D3DDevice != NULL) { @@ -335,6 +327,11 @@ bool D3DFB::CreateResources () Printf ("Windowed mode gamma will not work.\n"); GammaFixerShader = NULL; } + if (FAILED(D3DDevice->CreatePixelShader (BurnShaderDef, &BurnShader))) + { + Printf ("Burn screenwipe will not work in D3D mode.\n"); + BurnShader = NULL; + } CurPixelShader = NULL; memset(Constant, 0, sizeof(Constant)); if (!CreateFBTexture() || @@ -356,34 +353,9 @@ bool D3DFB::CreateResources () void D3DFB::ReleaseResources () { I_SaveWindowedPos (); - if (FBTexture != NULL) - { - FBTexture->Release(); - FBTexture = NULL; - } - if (FinalWipeScreen != NULL) - { - if (FinalWipeScreen != TempRenderTexture) - { - FinalWipeScreen->Release(); - } - FinalWipeScreen = NULL; - } - if (TempRenderTexture != NULL) - { - TempRenderTexture->Release(); - TempRenderTexture = NULL; - } - if (InitialWipeScreen != NULL) - { - InitialWipeScreen->Release(); - InitialWipeScreen = NULL; - } - if (VertexBuffer != NULL) - { - VertexBuffer->Release(); - VertexBuffer = NULL; - } + KillNativeTexs(); + KillNativePals(); + ReleaseDefaultPoolItems(); if (PaletteTexture != NULL) { PaletteTexture->Release(); @@ -424,28 +396,57 @@ void D3DFB::ReleaseResources () GammaFixerShader->Release(); GammaFixerShader = NULL; } + if (BurnShader != NULL) + { + BurnShader->Release(); + BurnShader = NULL; + } + if (ScreenWipe != NULL) + { + delete ScreenWipe; + ScreenWipe = NULL; + } + GatheringWipeScreen = false; } -bool D3DFB::Reset () +// Free resources created with D3DPOOL_DEFAULT. +void D3DFB::ReleaseDefaultPoolItems() { - D3DPRESENT_PARAMETERS d3dpp; - - // Free resources created with D3DPOOL_DEFAULT. if (FBTexture != NULL) { FBTexture->Release(); FBTexture = NULL; } + if (FinalWipeScreen != NULL) + { + if (FinalWipeScreen != TempRenderTexture) + { + FinalWipeScreen->Release(); + } + FinalWipeScreen = NULL; + } if (TempRenderTexture != NULL) { TempRenderTexture->Release(); TempRenderTexture = NULL; } + if (InitialWipeScreen != NULL) + { + InitialWipeScreen->Release(); + InitialWipeScreen = NULL; + } if (VertexBuffer != NULL) { VertexBuffer->Release(); VertexBuffer = NULL; } +} + +bool D3DFB::Reset () +{ + D3DPRESENT_PARAMETERS d3dpp; + + ReleaseDefaultPoolItems(); FillPresentParameters (&d3dpp, !Windowed, VSync); if (!SUCCEEDED(D3DDevice->Reset (&d3dpp))) { @@ -1072,265 +1073,6 @@ void D3DFB::SetBlendingRect(int x1, int y1, int x2, int y2) /* 2D Stuff */ /**************************************************************************/ -//========================================================================== -// -// 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); - } - if (type != wipe_Fade) - { - 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 - { - // GetFrontBufferData works only with this format - desc.Format = D3DFMT_A8R8G8B8; - } - - if (FAILED(D3DDevice->CreateTexture(Width, TrueHeight, 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; - } - WipeTime = 0; - - // 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); - } - WipeTime += 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; - D3DDevice->SetStreamSource(0, VertexBuffer, 0, sizeof(FBVERTEX)); - D3DDevice->SetFVF(D3DFVF_FBVERTEX); - - /* Crossfade only for testing purposes, because it's the simplest. - * More to come later. - */ - - // Put the initial screen back to the buffer, presumably with DMA. - IDirect3DSurface9 *source, *target; - - if (SUCCEEDED(InitialWipeScreen->GetSurfaceLevel(0, &source)) && - SUCCEEDED(D3DDevice->GetRenderTarget(0, &target))) - { - D3DDevice->UpdateSurface(source, NULL, target, NULL); - } - - // Draw the new screen on top of it. - SetTexture(0, FinalWipeScreen); - SetAlphaBlend(TRUE, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA); - SetConstant(0, 0, 0, 0, clamp(WipeTime / 32.f, 0.f, 1.f)); - SetConstant(1, 1, 1, 1, 0); - SetPixelShader(PlainShader); - // FIXME: The FinalWipeScreen gets junk at the top in letterbox modes. - D3DDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2); - - DrawLetterbox(); - - return WipeTime >= 32; -} - -//========================================================================== -// -// D3DFB :: WipeCleanup -// -// Release any resources that were specifically created for the wipe. -// -//========================================================================== - -void D3DFB::WipeCleanup() -{ - if (!test2d) - { - Super::WipeCleanup(); - return; - } - if (InitialWipeScreen != NULL) - { - InitialWipeScreen->Release(); - InitialWipeScreen = NULL; - } - if (FinalWipeScreen != NULL && FinalWipeScreen != TempRenderTexture) - { - FinalWipeScreen->Release(); - } - FinalWipeScreen = NULL; - GatheringWipeScreen = false; -} - //========================================================================== // // D3DTex Constructor diff --git a/src/win32/fb_d3d9_shaders.h b/src/win32/fb_d3d9_shaders.h index 827a026da..843ebb6ea 100644 --- a/src/win32/fb_d3d9_shaders.h +++ b/src/win32/fb_d3d9_shaders.h @@ -339,3 +339,60 @@ const DWORD GammaFixerDef[] = 0x80000000, 0x0200000e, 0x80020000, 0x80550000, 0x0200000e, 0x80040000, 0x80aa0000, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff }; + +// The shader used by the burn effect screen wipe --------------------------- + +#if HLSL_SOURCE_CODE +sampler2D NewScreen : register(s0); +sampler2D Burn : register(s1); + +float4 main (float2 coord[2] : TEXCOORD0) : COLOR +{ + float4 color = tex2D(NewScreen, coord[0]); + float4 alpha = tex2D(Burn, coord[1]); + color.a = alpha.r * 2; + return color; +} +#elif SHADER_ASSEMBLY_CODE +// +// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000 +// +// fxc burn.ps /Tps_1_4 /VnBurnShaderDef /Fhburn.h /LD +// +// +// Parameters: +// +// sampler2D Burn; +// sampler2D NewScreen; +// +// +// Registers: +// +// Name Reg Size +// ------------ ----- ---- +// NewScreen s0 1 +// Burn s1 1 +// + + ps_1_4 + texld r0, t0 + texld r1, t1 + add r0.w, r1.x, r1.x + + mov r0.xyz, r0 + +// approximately 3 instruction slots used (2 texture, 1 arithmetic) +#endif + +const DWORD BurnShaderDef[] = +{ + 0xffff0104, 0x0029fffe, 0x42415443, 0x0000001c, 0x0000006d, 0xffff0104, + 0x00000002, 0x0000001c, 0x00000100, 0x00000066, 0x00000044, 0x00010003, + 0x00000001, 0x0000004c, 0x00000000, 0x0000005c, 0x00000003, 0x00000001, + 0x0000004c, 0x00000000, 0x6e727542, 0xababab00, 0x000c0004, 0x00010001, + 0x00000001, 0x00000000, 0x5377654e, 0x65657263, 0x7370006e, 0x345f315f, + 0x63694d00, 0x6f736f72, 0x28207466, 0x44202952, 0x39584433, 0x61685320, + 0x20726564, 0x706d6f43, 0x72656c69, 0x312e3920, 0x37372e35, 0x30302e39, + 0xab003030, 0x00000042, 0x800f0000, 0xb0e40000, 0x00000042, 0x800f0001, + 0xb0e40001, 0x00000002, 0x80080000, 0x80000001, 0x80000001, 0x40000001, + 0x80070000, 0x80e40000, 0x0000ffff +}; diff --git a/src/win32/fb_d3d9_wipe.cpp b/src/win32/fb_d3d9_wipe.cpp new file mode 100644 index 000000000..61ea41d9b --- /dev/null +++ b/src/win32/fb_d3d9_wipe.cpp @@ -0,0 +1,635 @@ +/* +** 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 +#include +#include + +#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); +} diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index a22291fa4..50508cab0 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -264,6 +264,7 @@ private: void FillPresentParameters (D3DPRESENT_PARAMETERS *pp, bool fullscreen, bool vsync); bool UploadVertices(); bool Reset(); + void ReleaseDefaultPoolItems(); void KillNativePals(); void KillNativeTexs(); void DrawLetterbox(); @@ -309,8 +310,6 @@ private: D3DPal *Palettes; D3DTex *Textures; - int WipeTime; - IDirect3DDevice9 *D3DDevice; IDirect3DVertexBuffer9 *VertexBuffer; IDirect3DTexture9 *FBTexture; @@ -323,12 +322,33 @@ private: IDirect3DPixelShader9 *PlainStencilShader; IDirect3DPixelShader9 *DimShader; IDirect3DPixelShader9 *GammaFixerShader; + IDirect3DPixelShader9 *BurnShader; IDirect3DSurface9 *OldRenderTarget; IDirect3DTexture9 *InitialWipeScreen, *FinalWipeScreen; D3DFB() {} + + class Wiper + { + public: + virtual ~Wiper(); + virtual bool Run(int ticks, D3DFB *fb) = 0; + }; + + class Wiper_Melt; friend class Wiper_Melt; + class Wiper_Burn; friend class Wiper_Burn; + class Wiper_Crossfade; friend class Wiper_Crossfade; + + Wiper *ScreenWipe; }; +struct FBVERTEX +{ + FLOAT x, y, z, rhw; + FLOAT tu, tv; +}; +#define D3DFVF_FBVERTEX (D3DFVF_XYZRHW|D3DFVF_TEX1) + #if 0 #define STARTLOG do { if (!dbg) dbg = fopen ("k:/vid.log", "w"); } while(0) #define STOPLOG do { if (dbg) { fclose (dbg); dbg=NULL; } } while(0) diff --git a/zdoom.vcproj b/zdoom.vcproj index 2b76f0bf0..3a5f20c9f 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1,7 +1,7 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + @@ -2721,14 +2729,6 @@ GeneratePreprocessedFile="0" /> - - - @@ -2751,7 +2751,7 @@ /> - - - @@ -4806,6 +4796,16 @@ Outputs="$(IntDir)/$(InputName).obj" /> + + + @@ -4830,16 +4830,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - @@ -4850,6 +4840,16 @@ Outputs="$(IntDir)/$(InputName).obj" /> + + + @@ -4874,16 +4874,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - @@ -4894,6 +4884,16 @@ Outputs="$(IntDir)\$(InputName).obj" /> + + + @@ -4918,16 +4918,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - @@ -4938,6 +4928,16 @@ Outputs="$(IntDir)\$(InputName).obj" /> + + + @@ -4962,16 +4962,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - @@ -4982,6 +4972,16 @@ Outputs="$(IntDir)\$(InputName).obj" /> + + + @@ -5049,6 +5049,10 @@ RelativePath=".\src\win32\fb_d3d9_shaders.h" > + + @@ -5065,7 +5069,7 @@ /> + + + @@ -5404,14 +5416,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - - - - @@ -5646,6 +5642,14 @@ GeneratePreprocessedFile="0" /> + + + @@ -5667,7 +5671,7 @@ /> + + + @@ -9168,14 +9180,6 @@ AdditionalIncludeDirectories="src\win32;$(NoInherit)" /> - - - @@ -9350,7 +9354,7 @@ />