mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-12-11 21:21:45 +00:00
9b87a167d7
Most of those which still rely on ZDoom's own definition should be gone, unfortunately the code in files that include Windows headers is a gigantic mess with DWORDs being longs there intead of ints, so this needs to be done with care. DWORD should only remain where the Windows type is actually wanted.
519 lines
11 KiB
C++
519 lines
11 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id:$
|
|
//
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
//
|
|
// This source is available for distribution and/or modification
|
|
// only under the terms of the DOOM Source Code License as
|
|
// published by id Software. All rights reserved.
|
|
//
|
|
// The source is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
// for more details.
|
|
//
|
|
// $Log:$
|
|
//
|
|
// DESCRIPTION:
|
|
// Mission begin melt/wipe screen special effect.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "i_video.h"
|
|
#include "v_video.h"
|
|
#include "m_random.h"
|
|
#include "doomdef.h"
|
|
#include "f_wipe.h"
|
|
#include "c_cvars.h"
|
|
#include "templates.h"
|
|
#include "v_palette.h"
|
|
|
|
EXTERN_CVAR(Bool, r_blendmethod)
|
|
|
|
//
|
|
// SCREEN WIPE PACKAGE
|
|
//
|
|
|
|
static int CurrentWipeType;
|
|
|
|
static short *wipe_scr_start;
|
|
static short *wipe_scr_end;
|
|
static int *y;
|
|
|
|
// [RH] Fire Wipe
|
|
#define FIREWIDTH 64
|
|
#define FIREHEIGHT 64
|
|
static uint8_t *burnarray;
|
|
static int density;
|
|
static int burntime;
|
|
|
|
// [RH] Crossfade
|
|
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;
|
|
short *dest;
|
|
int width = SCREENWIDTH / 2;
|
|
|
|
dest = new short[width*SCREENHEIGHT*2];
|
|
|
|
for(y = 0; y < SCREENHEIGHT; y++)
|
|
for(x = 0; x < width; x++)
|
|
dest[x*SCREENHEIGHT+y] = array[y*width+x];
|
|
|
|
memcpy(array, dest, SCREENWIDTH*SCREENHEIGHT);
|
|
|
|
delete[] dest;
|
|
}
|
|
|
|
bool wipe_initMelt (int ticks)
|
|
{
|
|
int i, r;
|
|
|
|
// copy start screen to main screen
|
|
screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_start);
|
|
|
|
// makes this wipe faster (in theory)
|
|
// to have stuff in column-major format
|
|
wipe_shittyColMajorXform (wipe_scr_start);
|
|
wipe_shittyColMajorXform (wipe_scr_end);
|
|
|
|
// setup initial column positions
|
|
// (y<0 => not ready to scroll yet)
|
|
y = new int[MELT_WIDTH];
|
|
y[0] = -(M_Random() & 15);
|
|
for (i = 1; i < MELT_WIDTH; i++)
|
|
{
|
|
r = (M_Random()%3) - 1;
|
|
y[i] = clamp(y[i-1] + r, -15, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool wipe_doMelt (int ticks)
|
|
{
|
|
int i, j, dy, x;
|
|
const short *s;
|
|
short *d;
|
|
bool done = true;
|
|
|
|
while (ticks--)
|
|
{
|
|
done = true;
|
|
for (i = 0; i < MELT_WIDTH; i++)
|
|
{
|
|
if (y[i] < 0)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
bool wipe_exitMelt (int ticks)
|
|
{
|
|
delete[] y;
|
|
return 0;
|
|
}
|
|
|
|
// Burn -------------------------------------------------------------
|
|
|
|
bool wipe_initBurn (int ticks)
|
|
{
|
|
burnarray = new uint8_t[FIREWIDTH * (FIREHEIGHT+5)];
|
|
memset (burnarray, 0, FIREWIDTH * (FIREHEIGHT+5));
|
|
density = 4;
|
|
burntime = 0;
|
|
return 0;
|
|
}
|
|
|
|
int wipe_CalcBurn (uint8_t *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;
|
|
uint8_t *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)
|
|
{
|
|
uint8_t *pixel = from;
|
|
|
|
// special case: first pixel on line
|
|
uint8_t *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)
|
|
{
|
|
bool done;
|
|
|
|
burntime += ticks;
|
|
ticks *= 2;
|
|
|
|
// Make the fire burn
|
|
done = false;
|
|
while (!done && ticks--)
|
|
{
|
|
density = wipe_CalcBurn(burnarray, FIREWIDTH, FIREHEIGHT, density);
|
|
done = (density < 0);
|
|
}
|
|
|
|
// Draw the screen
|
|
int xstep, ystep, firex, firey;
|
|
int x, y;
|
|
uint8_t *to, *fromold, *fromnew;
|
|
const int SHIFT = 16;
|
|
|
|
xstep = (FIREWIDTH << SHIFT) / SCREENWIDTH;
|
|
ystep = (FIREHEIGHT << SHIFT) / SCREENHEIGHT;
|
|
to = screen->GetBuffer();
|
|
fromold = (uint8_t *)wipe_scr_start;
|
|
fromnew = (uint8_t *)wipe_scr_end;
|
|
|
|
if (!r_blendmethod)
|
|
{
|
|
for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep)
|
|
{
|
|
for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep)
|
|
{
|
|
int fglevel;
|
|
|
|
fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2;
|
|
if (fglevel >= 63)
|
|
{
|
|
to[x] = fromnew[x];
|
|
}
|
|
else if (fglevel == 0)
|
|
{
|
|
to[x] = fromold[x];
|
|
done = false;
|
|
}
|
|
else
|
|
{
|
|
int bglevel = 64-fglevel;
|
|
uint32_t *fg2rgb = Col2RGB8[fglevel];
|
|
uint32_t *bg2rgb = Col2RGB8[bglevel];
|
|
uint32_t fg = fg2rgb[fromnew[x]];
|
|
uint32_t bg = bg2rgb[fromold[x]];
|
|
fg = (fg+bg) | 0x1f07c1f;
|
|
to[x] = RGB32k.All[fg & (fg>>15)];
|
|
done = false;
|
|
}
|
|
}
|
|
fromold += SCREENWIDTH;
|
|
fromnew += SCREENWIDTH;
|
|
to += SCREENPITCH;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep)
|
|
{
|
|
for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep)
|
|
{
|
|
int fglevel;
|
|
|
|
fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2;
|
|
if (fglevel >= 63)
|
|
{
|
|
to[x] = fromnew[x];
|
|
}
|
|
else if (fglevel == 0)
|
|
{
|
|
to[x] = fromold[x];
|
|
done = false;
|
|
}
|
|
else
|
|
{
|
|
int bglevel = 64-fglevel;
|
|
|
|
const PalEntry* pal = GPalette.BaseColors;
|
|
|
|
uint32_t fg = fromnew[x];
|
|
uint32_t bg = fromold[x];
|
|
int r = MIN((pal[fg].r * fglevel + pal[bg].r * bglevel) >> 8, 63);
|
|
int g = MIN((pal[fg].g * fglevel + pal[bg].g * bglevel) >> 8, 63);
|
|
int b = MIN((pal[fg].b * fglevel + pal[bg].b * bglevel) >> 8, 63);
|
|
to[x] = RGB256k.RGB[r][g][b];
|
|
done = false;
|
|
}
|
|
}
|
|
fromold += SCREENWIDTH;
|
|
fromnew += SCREENWIDTH;
|
|
to += SCREENPITCH;
|
|
}
|
|
}
|
|
return done || (burntime > 40);
|
|
}
|
|
|
|
bool wipe_exitBurn (int ticks)
|
|
{
|
|
delete[] burnarray;
|
|
return 0;
|
|
}
|
|
|
|
// Crossfade --------------------------------------------------------
|
|
|
|
bool wipe_initFade (int ticks)
|
|
{
|
|
fade = 0;
|
|
return 0;
|
|
}
|
|
|
|
bool wipe_doFade (int ticks)
|
|
{
|
|
fade += ticks * 2;
|
|
if (fade > 64)
|
|
{
|
|
screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_end);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
int x, y;
|
|
int bglevel = 64 - fade;
|
|
uint32_t *fg2rgb = Col2RGB8[fade];
|
|
uint32_t *bg2rgb = Col2RGB8[bglevel];
|
|
uint8_t *fromnew = (uint8_t *)wipe_scr_end;
|
|
uint8_t *fromold = (uint8_t *)wipe_scr_start;
|
|
uint8_t *to = screen->GetBuffer();
|
|
const PalEntry *pal = GPalette.BaseColors;
|
|
|
|
if (!r_blendmethod)
|
|
{
|
|
for (y = 0; y < SCREENHEIGHT; y++)
|
|
{
|
|
for (x = 0; x < SCREENWIDTH; x++)
|
|
{
|
|
uint32_t fg = fg2rgb[fromnew[x]];
|
|
uint32_t bg = bg2rgb[fromold[x]];
|
|
fg = (fg+bg) | 0x1f07c1f;
|
|
to[x] = RGB32k.All[fg & (fg>>15)];
|
|
}
|
|
fromnew += SCREENWIDTH;
|
|
fromold += SCREENWIDTH;
|
|
to += SCREENPITCH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (y = 0; y < SCREENHEIGHT; y++)
|
|
{
|
|
for (x = 0; x < SCREENWIDTH; x++)
|
|
{
|
|
uint32_t fg = fromnew[x];
|
|
uint32_t bg = fromold[x];
|
|
int r = MIN((pal[fg].r * (64-bglevel) + pal[bg].r * bglevel) >> 8, 63);
|
|
int g = MIN((pal[fg].g * (64-bglevel) + pal[bg].g * bglevel) >> 8, 63);
|
|
int b = MIN((pal[fg].b * (64-bglevel) + pal[bg].b * bglevel) >> 8, 63);
|
|
to[x] = RGB256k.RGB[r][g][b];
|
|
}
|
|
fromnew += SCREENWIDTH;
|
|
fromold += SCREENWIDTH;
|
|
to += SCREENPITCH;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wipe_exitFade (int ticks)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// General Wipe Functions -------------------------------------------
|
|
|
|
static bool (*wipes[])(int) =
|
|
{
|
|
wipe_initMelt, wipe_doMelt, wipe_exitMelt,
|
|
wipe_initBurn, wipe_doBurn, wipe_exitBurn,
|
|
wipe_initFade, wipe_doFade, wipe_exitFade
|
|
};
|
|
|
|
// Returns true if the wipe should be performed.
|
|
bool wipe_StartScreen (int type)
|
|
{
|
|
if (screen->IsBgra())
|
|
return false;
|
|
|
|
CurrentWipeType = clamp(type, 0, wipe_NUMWIPES - 1);
|
|
|
|
if (CurrentWipeType)
|
|
{
|
|
wipe_scr_start = new short[SCREENWIDTH * SCREENHEIGHT / 2];
|
|
screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_start);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void wipe_EndScreen (void)
|
|
{
|
|
if (screen->IsBgra())
|
|
return;
|
|
|
|
if (CurrentWipeType)
|
|
{
|
|
wipe_scr_end = new short[SCREENWIDTH * SCREENHEIGHT / 2];
|
|
screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_end);
|
|
screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_start); // restore start scr.
|
|
|
|
// Initialize the wipe
|
|
(*wipes[(CurrentWipeType-1)*3])(0);
|
|
}
|
|
}
|
|
|
|
// Returns true if the wipe is done.
|
|
bool wipe_ScreenWipe (int ticks)
|
|
{
|
|
bool rc;
|
|
|
|
if (screen->IsBgra())
|
|
return true;
|
|
|
|
if (CurrentWipeType == wipe_None)
|
|
return true;
|
|
|
|
// do a piece of wipe-in
|
|
V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT);
|
|
rc = (*wipes[(CurrentWipeType-1)*3+1])(ticks);
|
|
|
|
return rc;
|
|
}
|
|
|
|
// Final things for the wipe
|
|
void wipe_Cleanup()
|
|
{
|
|
if (screen->IsBgra())
|
|
return;
|
|
|
|
if (wipe_scr_start != NULL)
|
|
{
|
|
delete[] wipe_scr_start;
|
|
wipe_scr_start = NULL;
|
|
}
|
|
if (wipe_scr_end != NULL)
|
|
{
|
|
delete[] wipe_scr_end;
|
|
wipe_scr_end = NULL;
|
|
}
|
|
if (CurrentWipeType > 0)
|
|
{
|
|
(*wipes[(CurrentWipeType-1)*3+2])(0);
|
|
}
|
|
}
|