1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 // Mission begin melt/wipe screen special effect.
21 //
22 //-----------------------------------------------------------------------------
23
24 #include "i_video.h"
25 #include "v_video.h"
26 #include "m_random.h"
27 #include "doomdef.h"
28 #include "f_wipe.h"
29 #include "c_cvars.h"
30 #include "templates.h"
31
32 //
33 // SCREEN WIPE PACKAGE
34 //
35
36 static int CurrentWipeType;
37
38 static short *wipe_scr_start;
39 static short *wipe_scr_end;
40 static int *y;
41
42 // [RH] Fire Wipe
43 #define FIREWIDTH 64
44 #define FIREHEIGHT 64
45 static BYTE *burnarray;
46 static int density;
47 static int burntime;
48
49 // [RH] Crossfade
50 static int fade;
51
52
53 // Melt -------------------------------------------------------------
54
55 // Match the strip sizes that oldschool Doom used on a 320x200 screen.
56 #define MELT_WIDTH 160
57 #define MELT_HEIGHT 200
58
wipe_shittyColMajorXform(short * array)59 void wipe_shittyColMajorXform (short *array)
60 {
61 int x, y;
62 short *dest;
63 int width = SCREENWIDTH / 2;
64
65 dest = new short[width*SCREENHEIGHT*2];
66
67 for(y = 0; y < SCREENHEIGHT; y++)
68 for(x = 0; x < width; x++)
69 dest[x*SCREENHEIGHT+y] = array[y*width+x];
70
71 memcpy(array, dest, SCREENWIDTH*SCREENHEIGHT);
72
73 delete[] dest;
74 }
75
wipe_initMelt(int ticks)76 bool wipe_initMelt (int ticks)
77 {
78 int i, r;
79
80 // copy start screen to main screen
81 screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start);
82
83 // makes this wipe faster (in theory)
84 // to have stuff in column-major format
85 wipe_shittyColMajorXform (wipe_scr_start);
86 wipe_shittyColMajorXform (wipe_scr_end);
87
88 // setup initial column positions
89 // (y<0 => not ready to scroll yet)
90 y = new int[MELT_WIDTH];
91 y[0] = -(M_Random() & 15);
92 for (i = 1; i < MELT_WIDTH; i++)
93 {
94 r = (M_Random()%3) - 1;
95 y[i] = clamp(y[i-1] + r, -15, 0);
96 }
97
98 return 0;
99 }
100
wipe_doMelt(int ticks)101 bool wipe_doMelt (int ticks)
102 {
103 int i, j, dy, x;
104 const short *s;
105 short *d;
106 bool done = true;
107
108 while (ticks--)
109 {
110 done = true;
111 for (i = 0; i < MELT_WIDTH; i++)
112 {
113 if (y[i] < 0)
114 {
115 y[i]++;
116 done = false;
117 }
118 else if (y[i] < MELT_HEIGHT)
119 {
120 dy = (y[i] < 16) ? y[i]+1 : 8;
121 y[i] = MIN(y[i] + dy, MELT_HEIGHT);
122 done = false;
123 }
124 if (ticks == 0 && y[i] >= 0)
125 { // Only draw for the final tick.
126 const int pitch = screen->GetPitch() / 2;
127 int sy = y[i] * SCREENHEIGHT / MELT_HEIGHT;
128
129 for (x = i * (SCREENWIDTH/2) / MELT_WIDTH; x < (i + 1) * (SCREENWIDTH/2) / MELT_WIDTH; ++x)
130 {
131 s = &wipe_scr_end[x*SCREENHEIGHT];
132 d = &((short *)screen->GetBuffer())[x];
133
134 for (j = sy; j != 0; --j)
135 {
136 *d = *(s++);
137 d += pitch;
138 }
139
140 s = &wipe_scr_start[x*SCREENHEIGHT];
141
142 for (j = SCREENHEIGHT - sy; j != 0; --j)
143 {
144 *d = *(s++);
145 d += pitch;
146 }
147 }
148 }
149 }
150 }
151
152 return done;
153 }
154
wipe_exitMelt(int ticks)155 bool wipe_exitMelt (int ticks)
156 {
157 delete[] y;
158 return 0;
159 }
160
161 // Burn -------------------------------------------------------------
162
wipe_initBurn(int ticks)163 bool wipe_initBurn (int ticks)
164 {
165 burnarray = new BYTE[FIREWIDTH * (FIREHEIGHT+5)];
166 memset (burnarray, 0, FIREWIDTH * (FIREHEIGHT+5));
167 density = 4;
168 burntime = 0;
169 return 0;
170 }
171
wipe_CalcBurn(BYTE * burnarray,int width,int height,int density)172 int wipe_CalcBurn (BYTE *burnarray, int width, int height, int density)
173 {
174 // This is a modified version of the fire that was once used
175 // on the player setup menu.
176 static int voop;
177
178 int a, b;
179 BYTE *from;
180
181 // generator
182 from = &burnarray[width * height];
183 b = voop;
184 voop += density / 3;
185 for (a = 0; a < density/8; a++)
186 {
187 unsigned int offs = (a+b) & (width - 1);
188 unsigned int v = M_Random();
189 v = MIN(from[offs] + 4 + (v & 15) + (v >> 3) + (M_Random() & 31), 255u);
190 from[offs] = from[width*2 + ((offs + width*3/2) & (width - 1))] = v;
191 }
192
193 density = MIN(density + 10, width * 7);
194
195 from = burnarray;
196 for (b = 0; b <= height; b += 2)
197 {
198 BYTE *pixel = from;
199
200 // special case: first pixel on line
201 BYTE *p = pixel + (width << 1);
202 unsigned int top = *p + *(p + width - 1) + *(p + 1);
203 unsigned int bottom = *(pixel + (width << 2));
204 unsigned int c1 = (top + bottom) >> 2;
205 if (c1 > 1) c1--;
206 *pixel = c1;
207 *(pixel + width) = (c1 + bottom) >> 1;
208 pixel++;
209
210 // main line loop
211 for (a = 1; a < width-1; a++)
212 {
213 // sum top pixels
214 p = pixel + (width << 1);
215 top = *p + *(p - 1) + *(p + 1);
216
217 // bottom pixel
218 bottom = *(pixel + (width << 2));
219
220 // combine pixels
221 c1 = (top + bottom) >> 2;
222 if (c1 > 1) c1--;
223
224 // store pixels
225 *pixel = c1;
226 *(pixel + width) = (c1 + bottom) >> 1; // interpolate
227
228 // next pixel
229 pixel++;
230 }
231
232 // special case: last pixel on line
233 p = pixel + (width << 1);
234 top = *p + *(p - 1) + *(p - width + 1);
235 bottom = *(pixel + (width << 2));
236 c1 = (top + bottom) >> 2;
237 if (c1 > 1) c1--;
238 *pixel = c1;
239 *(pixel + width) = (c1 + bottom) >> 1;
240
241 // next line
242 from += width << 1;
243 }
244
245 // Check for done-ness. (Every pixel with level 126 or higher counts as done.)
246 for (a = width * height, from = burnarray; a != 0; --a, ++from)
247 {
248 if (*from < 126)
249 {
250 return density;
251 }
252 }
253 return -1;
254 }
255
wipe_doBurn(int ticks)256 bool wipe_doBurn (int ticks)
257 {
258 bool done;
259
260 burntime += ticks;
261 ticks *= 2;
262
263 // Make the fire burn
264 done = false;
265 while (!done && ticks--)
266 {
267 density = wipe_CalcBurn(burnarray, FIREWIDTH, FIREHEIGHT, density);
268 done = (density < 0);
269 }
270
271 // Draw the screen
272 fixed_t xstep, ystep, firex, firey;
273 int x, y;
274 BYTE *to, *fromold, *fromnew;
275
276 xstep = (FIREWIDTH * FRACUNIT) / SCREENWIDTH;
277 ystep = (FIREHEIGHT * FRACUNIT) / SCREENHEIGHT;
278 to = screen->GetBuffer();
279 fromold = (BYTE *)wipe_scr_start;
280 fromnew = (BYTE *)wipe_scr_end;
281
282 for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep)
283 {
284 for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep)
285 {
286 int fglevel;
287
288 fglevel = burnarray[(firex>>FRACBITS)+(firey>>FRACBITS)*FIREWIDTH] / 2;
289 if (fglevel >= 63)
290 {
291 to[x] = fromnew[x];
292 }
293 else if (fglevel == 0)
294 {
295 to[x] = fromold[x];
296 done = false;
297 }
298 else
299 {
300 int bglevel = 64-fglevel;
301 DWORD *fg2rgb = Col2RGB8[fglevel];
302 DWORD *bg2rgb = Col2RGB8[bglevel];
303 DWORD fg = fg2rgb[fromnew[x]];
304 DWORD bg = bg2rgb[fromold[x]];
305 fg = (fg+bg) | 0x1f07c1f;
306 to[x] = RGB32k.All[fg & (fg>>15)];
307 done = false;
308 }
309 }
310 fromold += SCREENWIDTH;
311 fromnew += SCREENWIDTH;
312 to += SCREENPITCH;
313 }
314
315 return done || (burntime > 40);
316 }
317
wipe_exitBurn(int ticks)318 bool wipe_exitBurn (int ticks)
319 {
320 delete[] burnarray;
321 return 0;
322 }
323
324 // Crossfade --------------------------------------------------------
325
wipe_initFade(int ticks)326 bool wipe_initFade (int ticks)
327 {
328 fade = 0;
329 return 0;
330 }
331
wipe_doFade(int ticks)332 bool wipe_doFade (int ticks)
333 {
334 fade += ticks * 2;
335 if (fade > 64)
336 {
337 screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_end);
338 return true;
339 }
340 else
341 {
342 int x, y;
343 fixed_t bglevel = 64 - fade;
344 DWORD *fg2rgb = Col2RGB8[fade];
345 DWORD *bg2rgb = Col2RGB8[bglevel];
346 BYTE *fromnew = (BYTE *)wipe_scr_end;
347 BYTE *fromold = (BYTE *)wipe_scr_start;
348 BYTE *to = screen->GetBuffer();
349
350 for (y = 0; y < SCREENHEIGHT; y++)
351 {
352 for (x = 0; x < SCREENWIDTH; x++)
353 {
354 DWORD fg = fg2rgb[fromnew[x]];
355 DWORD bg = bg2rgb[fromold[x]];
356 fg = (fg+bg) | 0x1f07c1f;
357 to[x] = RGB32k.All[fg & (fg>>15)];
358 }
359 fromnew += SCREENWIDTH;
360 fromold += SCREENWIDTH;
361 to += SCREENPITCH;
362 }
363 }
364 return false;
365 }
366
wipe_exitFade(int ticks)367 bool wipe_exitFade (int ticks)
368 {
369 return 0;
370 }
371
372 // General Wipe Functions -------------------------------------------
373
374 static bool (*wipes[])(int) =
375 {
376 wipe_initMelt, wipe_doMelt, wipe_exitMelt,
377 wipe_initBurn, wipe_doBurn, wipe_exitBurn,
378 wipe_initFade, wipe_doFade, wipe_exitFade
379 };
380
381 // Returns true if the wipe should be performed.
wipe_StartScreen(int type)382 bool wipe_StartScreen (int type)
383 {
384 CurrentWipeType = clamp(type, 0, wipe_NUMWIPES - 1);
385
386 if (CurrentWipeType)
387 {
388 wipe_scr_start = new short[SCREENWIDTH * SCREENHEIGHT / 2];
389 screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start);
390 return true;
391 }
392 return false;
393 }
394
wipe_EndScreen(void)395 void wipe_EndScreen (void)
396 {
397 if (CurrentWipeType)
398 {
399 wipe_scr_end = new short[SCREENWIDTH * SCREENHEIGHT / 2];
400 screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_end);
401 screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start); // restore start scr.
402 // Initialize the wipe
403 (*wipes[(CurrentWipeType-1)*3])(0);
404 }
405 }
406
407 // Returns true if the wipe is done.
wipe_ScreenWipe(int ticks)408 bool wipe_ScreenWipe (int ticks)
409 {
410 bool rc;
411
412 if (CurrentWipeType == wipe_None)
413 return true;
414
415 // do a piece of wipe-in
416 V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT);
417 rc = (*wipes[(CurrentWipeType-1)*3+1])(ticks);
418
419 return rc;
420 }
421
422 // Final things for the wipe
wipe_Cleanup()423 void wipe_Cleanup()
424 {
425 if (wipe_scr_start != NULL)
426 {
427 delete[] wipe_scr_start;
428 wipe_scr_start = NULL;
429 }
430 if (wipe_scr_end != NULL)
431 {
432 delete[] wipe_scr_end;
433 wipe_scr_end = NULL;
434 }
435 if (CurrentWipeType > 0)
436 {
437 (*wipes[(CurrentWipeType-1)*3+2])(0);
438 }
439 }
440