SRB2/src/hardware/hw_draw.c

1515 lines
40 KiB
C
Raw Normal View History

// SONIC ROBO BLAST 2
2014-03-15 16:59:03 +00:00
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
2021-05-07 15:45:56 +00:00
// Copyright (C) 1999-2021 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
2014-03-15 16:59:03 +00:00
//-----------------------------------------------------------------------------
2020-01-23 23:12:15 +00:00
/// \file hw_draw.c
2014-03-15 16:59:03 +00:00
/// \brief miscellaneous drawing (mainly 2d)
#ifdef __GNUC__
#include <unistd.h>
#endif
#include "../doomdef.h"
#ifdef HWRENDER
#include "hw_main.h"
2014-03-15 16:59:03 +00:00
#include "hw_glob.h"
#include "hw_drv.h"
#include "../m_misc.h" //FIL_WriteFile()
#include "../r_draw.h" //viewborderlump
#include "../r_main.h"
#include "../w_wad.h"
#include "../z_zone.h"
#include "../v_video.h"
#include "../st_stuff.h"
#include "../p_local.h" // stplyr
#include "../g_game.h" // players
2014-03-15 16:59:03 +00:00
#include <fcntl.h>
#include "../i_video.h" // for rendermode != render_glide
#ifndef O_BINARY
#define O_BINARY 0
#endif
#if defined(_MSC_VER)
#pragma pack(1)
#endif
typedef struct
{
UINT8 id_field_length ; // 1
UINT8 color_map_type ; // 2
UINT8 image_type ; // 3
UINT8 dummy[5] ; // 4, 8
INT16 x_origin ; // 9, 10
INT16 y_origin ; //11, 12
INT16 width ; //13, 14
INT16 height ; //15, 16
UINT8 image_pix_size ; //17
UINT8 image_descriptor; //18
} ATTRPACK TGAHeader; // sizeof is 18
#if defined(_MSC_VER)
#pragma pack()
#endif
2014-03-21 18:42:55 +00:00
static UINT8 softwaretranstogl[11] = { 0, 25, 51, 76,102,127,153,178,204,229,255};
static UINT8 softwaretranstogl_hi[11] = { 0, 51,102,153,204,255,255,255,255,255,255};
static UINT8 softwaretranstogl_lo[11] = { 0, 12, 24, 36, 48, 60, 71, 83, 95,111,127};
2014-03-15 16:59:03 +00:00
//
// -----------------+
// HWR_DrawPatch : Draw a 'tile' graphic
// Notes : x,y : positions relative to the original Doom resolution
// : textes(console+score) + menus + status bar
// -----------------+
2020-08-08 08:16:47 +00:00
void HWR_DrawPatch(patch_t *gpatch, INT32 x, INT32 y, INT32 option)
2014-03-15 16:59:03 +00:00
{
FOutVector v[4];
FBITFIELD flags;
2020-08-08 08:16:47 +00:00
GLPatch_t *hwrPatch;
2014-03-15 16:59:03 +00:00
// 3--2
// | /|
// |/ |
// 0--1
float sdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f;
float sdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f;
float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f;
float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f;
// make patch ready in hardware cache
HWR_GetPatch(gpatch);
2020-08-08 08:16:47 +00:00
hwrPatch = ((GLPatch_t *)gpatch->hardware);
2014-03-15 16:59:03 +00:00
switch (option & V_SCALEPATCHMASK)
{
case V_NOSCALEPATCH:
pdupx = pdupy = 2.0f;
break;
case V_SMALLSCALEPATCH:
pdupx = 2.0f * FIXED_TO_FLOAT(vid.fsmalldupx);
pdupy = 2.0f * FIXED_TO_FLOAT(vid.fsmalldupy);
break;
case V_MEDSCALEPATCH:
pdupx = 2.0f * FIXED_TO_FLOAT(vid.fmeddupx);
pdupy = 2.0f * FIXED_TO_FLOAT(vid.fmeddupy);
break;
}
if (option & V_NOSCALESTART)
sdupx = sdupy = 2.0f;
2020-08-08 08:16:47 +00:00
v[0].x = v[3].x = (x*sdupx-(gpatch->leftoffset)*pdupx)/vid.width - 1;
v[2].x = v[1].x = (x*sdupx+(gpatch->width-gpatch->leftoffset)*pdupx)/vid.width - 1;
v[0].y = v[1].y = 1-(y*sdupy-(gpatch->topoffset)*pdupy)/vid.height;
v[2].y = v[3].y = 1-(y*sdupy+(gpatch->height-gpatch->topoffset)*pdupy)/vid.height;
2014-03-15 16:59:03 +00:00
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].s = v[3].s = 0.0f;
2020-08-08 08:16:47 +00:00
v[2].s = v[1].s = hwrPatch->max_s;
v[0].t = v[1].t = 0.0f;
2020-08-08 08:16:47 +00:00
v[2].t = v[3].t = hwrPatch->max_t;
2014-03-15 16:59:03 +00:00
flags = PF_Translucent|PF_NoDepthTest;
2014-03-15 16:59:03 +00:00
if (option & V_WRAPX)
flags |= PF_ForceWrapX;
if (option & V_WRAPY)
flags |= PF_ForceWrapY;
// clip it since it is used for bunny scroll in doom I
2014-03-21 18:42:55 +00:00
HWD.pfnDrawPolygon(NULL, v, 4, flags);
2014-03-15 16:59:03 +00:00
}
2020-08-08 08:16:47 +00:00
void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap)
2014-03-15 16:59:03 +00:00
{
FOutVector v[4];
FBITFIELD flags;
float cx = FIXED_TO_FLOAT(x);
float cy = FIXED_TO_FLOAT(y);
2014-03-21 18:42:55 +00:00
UINT8 alphalevel = ((option & V_ALPHAMASK) >> V_ALPHASHIFT);
2020-08-08 08:16:47 +00:00
GLPatch_t *hwrPatch;
2014-03-15 16:59:03 +00:00
// 3--2
// | /|
// |/ |
// 0--1
float dupx, dupy, fscalew, fscaleh, fwidth, fheight;
UINT8 perplayershuffle = 0;
2014-03-21 18:42:55 +00:00
if (alphalevel >= 10 && alphalevel < 13)
2014-03-21 18:42:55 +00:00
return;
2014-03-15 16:59:03 +00:00
// make patch ready in hardware cache
2014-03-21 18:42:55 +00:00
if (!colormap)
HWR_GetPatch(gpatch);
else
HWR_GetMappedPatch(gpatch, colormap);
2014-03-15 16:59:03 +00:00
2020-08-08 08:16:47 +00:00
hwrPatch = ((GLPatch_t *)gpatch->hardware);
dupx = (float)vid.dupx;
dupy = (float)vid.dupy;
2014-03-15 16:59:03 +00:00
switch (option & V_SCALEPATCHMASK)
{
case V_NOSCALEPATCH:
dupx = dupy = 1.0f;
2014-03-15 16:59:03 +00:00
break;
case V_SMALLSCALEPATCH:
dupx = (float)vid.smalldupx;
dupy = (float)vid.smalldupy;
2014-03-15 16:59:03 +00:00
break;
case V_MEDSCALEPATCH:
dupx = (float)vid.meddupx;
dupy = (float)vid.meddupy;
2014-03-15 16:59:03 +00:00
break;
}
dupx = dupy = (dupx < dupy ? dupx : dupy);
fscalew = fscaleh = FIXED_TO_FLOAT(pscale);
if (vscale != pscale)
fscaleh = FIXED_TO_FLOAT(vscale);
// See my comments in v_video.c's V_DrawFixedPatch
// -- Monster Iestyn 29/10/18
{
float offsetx = 0.0f, offsety = 0.0f;
// left offset
if (option & V_FLIP)
2020-08-08 08:16:47 +00:00
offsetx = (float)(gpatch->width - gpatch->leftoffset) * fscalew;
else
2020-08-08 08:16:47 +00:00
offsetx = (float)(gpatch->leftoffset) * fscalew;
// top offset
// TODO: make some kind of vertical version of V_FLIP, maybe by deprecating V_OFFSET in future?!?
2020-08-08 08:16:47 +00:00
offsety = (float)(gpatch->topoffset) * fscaleh;
if ((option & (V_NOSCALESTART|V_OFFSET)) == (V_NOSCALESTART|V_OFFSET)) // Multiply by dupx/dupy for crosshairs
{
offsetx *= dupx;
offsety *= dupy;
}
cx -= offsetx;
cy -= offsety;
}
2014-03-15 16:59:03 +00:00
if (splitscreen && (option & V_PERPLAYER))
{
float adjusty = ((option & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f;
fscaleh /= 2;
cy /= 2;
#ifdef QUADS
if (splitscreen > 1) // 3 or 4 players
{
float adjustx = ((option & V_NOSCALESTART) ? vid.width : BASEVIDWIDTH)/2.0f;
fscalew /= 2;
cx /= 2;
if (stplyr == &players[displayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
option &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
else if (stplyr == &players[secondarydisplayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
cx += adjustx;
option &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
}
else if (stplyr == &players[thirddisplayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
cy += adjusty;
option &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
}
else if (stplyr == &players[fourthdisplayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
cx += adjustx;
cy += adjusty;
option &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
}
}
else
#endif
// 2 players
{
if (stplyr == &players[displayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle = 1;
option &= ~V_SNAPTOBOTTOM;
}
else //if (stplyr == &players[secondarydisplayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle = 2;
cy += adjusty;
option &= ~V_SNAPTOTOP;
}
}
}
2014-04-19 17:41:29 +00:00
if (!(option & V_NOSCALESTART))
{
cx = cx * dupx;
cy = cy * dupy;
if (!(option & V_SCALEPATCHMASK))
{
// if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT)
// cx and cy are possibly *slightly* off from float maths
// This is done before here compared to software because we directly alter cx and cy to centre
if (cx >= -0.1f && cx <= 0.1f && gpatch->width == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && gpatch->height == BASEVIDHEIGHT)
{
2020-08-08 08:16:47 +00:00
const column_t *column = (const column_t *)((const UINT8 *)(gpatch->columns) + (gpatch->columnofs[0]));
if (!column->topdelta)
{
const UINT8 *source = (const UINT8 *)(column) + 3;
HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0]));
}
}
// centre screen
2018-12-15 00:33:40 +00:00
if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
{
if (option & V_SNAPTORIGHT)
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx));
else if (!(option & V_SNAPTOLEFT))
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/2;
if (perplayershuffle & 4)
cx -= ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/4;
else if (perplayershuffle & 8)
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/4;
}
2018-12-15 00:33:40 +00:00
if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f)
{
if (option & V_SNAPTOBOTTOM)
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy));
else if (!(option & V_SNAPTOTOP))
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy))/2;
if (perplayershuffle & 1)
cy -= ((float)vid.height - ((float)BASEVIDHEIGHT * dupy))/4;
else if (perplayershuffle & 2)
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy))/4;
}
}
}
2014-04-19 17:41:29 +00:00
if (pscale != FRACUNIT || (splitscreen && option & V_PERPLAYER))
{
2020-08-08 08:16:47 +00:00
fwidth = (float)(gpatch->width) * fscalew * dupx;
fheight = (float)(gpatch->height) * fscaleh * dupy;
}
else
{
2020-08-08 08:16:47 +00:00
fwidth = (float)(gpatch->width) * dupx;
fheight = (float)(gpatch->height) * dupy;
}
2014-03-15 16:59:03 +00:00
// positions of the cx, cy, are between 0 and vid.width/vid.height now, we need them to be between -1 and 1
cx = -1 + (cx / (vid.width/2));
cy = 1 - (cy / (vid.height/2));
// fwidth and fheight are similar
fwidth /= vid.width / 2;
fheight /= vid.height / 2;
// set the polygon vertices to the right positions
v[0].x = v[3].x = cx;
v[2].x = v[1].x = cx + fwidth;
v[0].y = v[1].y = cy;
v[2].y = v[3].y = cy - fheight;
2014-04-14 05:14:58 +00:00
2014-03-15 16:59:03 +00:00
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
if (option & V_FLIP)
{
2020-08-08 08:16:47 +00:00
v[0].s = v[3].s = hwrPatch->max_s;
v[2].s = v[1].s = 0.0f;
}
else
{
v[0].s = v[3].s = 0.0f;
2020-08-08 08:16:47 +00:00
v[2].s = v[1].s = hwrPatch->max_s;
}
2014-03-15 16:59:03 +00:00
v[0].t = v[1].t = 0.0f;
2020-08-08 08:16:47 +00:00
v[2].t = v[3].t = hwrPatch->max_t;
2014-04-14 05:14:58 +00:00
flags = PF_Translucent|PF_NoDepthTest;
2014-03-15 16:59:03 +00:00
if (option & V_WRAPX)
flags |= PF_ForceWrapX;
if (option & V_WRAPY)
flags |= PF_ForceWrapY;
// clip it since it is used for bunny scroll in doom I
2014-03-21 18:42:55 +00:00
if (alphalevel)
2014-03-15 16:59:03 +00:00
{
FSurfaceInfo Surf;
Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
else if (alphalevel == 14) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
flags |= PF_Modulated;
HWD.pfnDrawPolygon(&Surf, v, 4, flags);
}
else
HWD.pfnDrawPolygon(NULL, v, 4, flags);
}
2020-08-08 08:16:47 +00:00
void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h)
{
FOutVector v[4];
FBITFIELD flags;
float cx = FIXED_TO_FLOAT(x);
float cy = FIXED_TO_FLOAT(y);
2014-03-21 18:42:55 +00:00
UINT8 alphalevel = ((option & V_ALPHAMASK) >> V_ALPHASHIFT);
2020-08-08 08:16:47 +00:00
GLPatch_t *hwrPatch;
// 3--2
// | /|
// |/ |
// 0--1
float dupx, dupy, fscale, fwidth, fheight;
2014-03-21 18:42:55 +00:00
if (alphalevel >= 10 && alphalevel < 13)
2014-03-21 18:42:55 +00:00
return;
// make patch ready in hardware cache
HWR_GetPatch(gpatch);
2020-08-08 08:16:47 +00:00
hwrPatch = ((GLPatch_t *)gpatch->hardware);
dupx = (float)vid.dupx;
dupy = (float)vid.dupy;
switch (option & V_SCALEPATCHMASK)
{
case V_NOSCALEPATCH:
dupx = dupy = 1.0f;
break;
case V_SMALLSCALEPATCH:
dupx = (float)vid.smalldupx;
dupy = (float)vid.smalldupy;
break;
case V_MEDSCALEPATCH:
dupx = (float)vid.meddupx;
dupy = (float)vid.meddupy;
break;
}
dupx = dupy = (dupx < dupy ? dupx : dupy);
fscale = FIXED_TO_FLOAT(pscale);
// fuck it, no GL support for croppedpatch v_perplayer right now. it's not like it's accessible to Lua or anything, and we only use it for menus...
cy -= (float)(gpatch->topoffset) * fscale;
cx -= (float)(gpatch->leftoffset) * fscale;
if (!(option & V_NOSCALESTART))
{
cx = cx * dupx;
cy = cy * dupy;
if (!(option & V_SCALEPATCHMASK))
{
2021-02-21 21:16:38 +00:00
// if it's meant to cover the whole screen, black out the rest
// no the patch is cropped do not do this ever
// centre screen
2018-12-15 00:33:40 +00:00
if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
{
if (option & V_SNAPTORIGHT)
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx));
else if (!(option & V_SNAPTOLEFT))
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/2;
}
2018-12-15 00:33:40 +00:00
if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f)
{
if (option & V_SNAPTOBOTTOM)
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy));
else if (!(option & V_SNAPTOTOP))
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy))/2;
}
}
}
fwidth = w;
fheight = h;
2021-02-21 21:16:38 +00:00
if (sx + w > gpatch->width)
fwidth = gpatch->width - sx;
2021-02-21 21:16:38 +00:00
if (sy + h > gpatch->height)
fheight = gpatch->height - sy;
if (pscale != FRACUNIT)
{
fwidth *= fscale * dupx;
fheight *= fscale * dupy;
}
else
{
fwidth *= dupx;
fheight *= dupy;
}
// positions of the cx, cy, are between 0 and vid.width/vid.height now, we need them to be between -1 and 1
cx = -1 + (cx / (vid.width/2));
cy = 1 - (cy / (vid.height/2));
// fwidth and fheight are similar
fwidth /= vid.width / 2;
fheight /= vid.height / 2;
// set the polygon vertices to the right positions
v[0].x = v[3].x = cx;
v[2].x = v[1].x = cx + fwidth;
v[0].y = v[1].y = cy;
v[2].y = v[3].y = cy - fheight;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
2020-08-08 08:16:47 +00:00
v[0].s = v[3].s = ((sx)/(float)(gpatch->width))*hwrPatch->max_s;
if (sx + w > gpatch->width)
2021-02-21 21:16:38 +00:00
v[2].s = v[1].s = hwrPatch->max_s;
else
2020-08-08 08:16:47 +00:00
v[2].s = v[1].s = ((sx+w)/(float)(gpatch->width))*hwrPatch->max_s;
2020-08-08 08:16:47 +00:00
v[0].t = v[1].t = ((sy)/(float)(gpatch->height))*hwrPatch->max_t;
if (sy + h > gpatch->height)
2021-02-21 21:16:38 +00:00
v[2].t = v[3].t = hwrPatch->max_t;
else
2020-08-08 08:16:47 +00:00
v[2].t = v[3].t = ((sy+h)/(float)(gpatch->height))*hwrPatch->max_t;
flags = PF_Translucent|PF_NoDepthTest;
if (option & V_WRAPX)
flags |= PF_ForceWrapX;
if (option & V_WRAPY)
flags |= PF_ForceWrapY;
// clip it since it is used for bunny scroll in doom I
2014-03-21 18:42:55 +00:00
if (alphalevel)
{
2014-03-15 16:59:03 +00:00
FSurfaceInfo Surf;
Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
else if (alphalevel == 14) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
2014-03-15 16:59:03 +00:00
flags |= PF_Modulated;
HWD.pfnDrawPolygon(&Surf, v, 4, flags);
}
else
HWD.pfnDrawPolygon(NULL, v, 4, flags);
}
void HWR_DrawPic(INT32 x, INT32 y, lumpnum_t lumpnum)
{
FOutVector v[4];
2020-08-08 08:16:47 +00:00
const patch_t *patch;
2014-03-15 16:59:03 +00:00
// make pic ready in hardware cache
patch = HWR_GetPic(lumpnum);
// 3--2
// | /|
// |/ |
// 0--1
v[0].x = v[3].x = 2.0f * (float)x/vid.width - 1;
v[2].x = v[1].x = 2.0f * (float)(x + patch->width*FIXED_TO_FLOAT(vid.fdupx))/vid.width - 1;
v[0].y = v[1].y = 1.0f - 2.0f * (float)y/vid.height;
v[2].y = v[3].y = 1.0f - 2.0f * (float)(y + patch->height*FIXED_TO_FLOAT(vid.fdupy))/vid.height;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
2020-08-08 08:16:47 +00:00
v[0].s = v[3].s = 0;
v[2].s = v[1].s = ((GLPatch_t *)patch->hardware)->max_s;
v[0].t = v[1].t = 0;
v[2].t = v[3].t = ((GLPatch_t *)patch->hardware)->max_t;
2014-03-15 16:59:03 +00:00
//Hurdler: Boris, the same comment as above... but maybe for pics
// it not a problem since they don't have any transparent pixel
// if I'm right !?
// But then, the question is: why not 0 instead of PF_Masked ?
// or maybe PF_Environment ??? (like what I said above)
// BP: PF_Environment don't change anything ! and 0 is undifined
2020-10-27 03:03:41 +00:00
HWD.pfnDrawPolygon(NULL, v, 4, PF_Translucent | PF_NoDepthTest);
2014-03-15 16:59:03 +00:00
}
// ==========================================================================
// V_VIDEO.C STUFF
// ==========================================================================
// --------------------------------------------------------------------------
// Fills a box of pixels using a flat texture as a pattern
// --------------------------------------------------------------------------
void HWR_DrawFlatFill (INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatlumpnum)
{
FOutVector v[4];
double dflatsize;
INT32 flatflag;
const size_t len = W_LumpLength(flatlumpnum);
switch (len)
{
case 4194304: // 2048x2048 lump
dflatsize = 2048.0f;
flatflag = 2047;
break;
case 1048576: // 1024x1024 lump
dflatsize = 1024.0f;
flatflag = 1023;
break;
case 262144:// 512x512 lump
dflatsize = 512.0f;
flatflag = 511;
break;
case 65536: // 256x256 lump
dflatsize = 256.0f;
flatflag = 255;
break;
case 16384: // 128x128 lump
dflatsize = 128.0f;
flatflag = 127;
break;
case 1024: // 32x32 lump
dflatsize = 32.0f;
flatflag = 31;
break;
default: // 64x64 lump
dflatsize = 64.0f;
flatflag = 63;
break;
}
// 3--2
// | /|
// |/ |
// 0--1
v[0].x = v[3].x = (x - 160.0f)/160.0f;
v[2].x = v[1].x = ((x+w) - 160.0f)/160.0f;
v[0].y = v[1].y = -(y - 100.0f)/100.0f;
v[2].y = v[3].y = -((y+h) - 100.0f)/100.0f;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
// flat is 64x64 lod and texture offsets are [0.0, 1.0]
v[0].s = v[3].s = (float)((x & flatflag)/dflatsize);
v[2].s = v[1].s = (float)(v[0].s + w/dflatsize);
v[0].t = v[1].t = (float)((y & flatflag)/dflatsize);
v[2].t = v[3].t = (float)(v[0].t + h/dflatsize);
2014-03-15 16:59:03 +00:00
HWR_GetRawFlat(flatlumpnum);
2014-03-15 16:59:03 +00:00
//Hurdler: Boris, the same comment as above... but maybe for pics
// it not a problem since they don't have any transparent pixel
// if I'm right !?
// BTW, I see we put 0 for PFs, and If I'm right, that
// means we take the previous PFs as default
// how can we be sure they are ok?
HWD.pfnDrawPolygon(NULL, v, 4, PF_NoDepthTest); //PF_Translucent);
}
// --------------------------------------------------------------------------
// Fade down the screen so that the menu drawn on top of it looks brighter
// --------------------------------------------------------------------------
// 3--2
// | /|
// |/ |
// 0--1
void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength)
2014-03-15 16:59:03 +00:00
{
FOutVector v[4];
FSurfaceInfo Surf;
v[0].x = v[3].x = -1.0f;
v[2].x = v[1].x = 1.0f;
v[0].y = v[1].y = -1.0f;
2014-03-15 16:59:03 +00:00
v[2].y = v[3].y = 1.0f;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 1.0f;
v[2].t = v[3].t = 0.0f;
2014-03-15 16:59:03 +00:00
if (color & 0xFF00) // Do COLORMAP fade.
{
Surf.PolyColor.rgba = UINT2RGBA(0x01010160);
Surf.PolyColor.s.alpha = (strength*8);
}
else // Do TRANSMAP** fade.
{
Surf.PolyColor.rgba = V_GetColor(color).rgba;
Surf.PolyColor.s.alpha = softwaretranstogl[strength];
}
2014-03-15 16:59:03 +00:00
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
// -----------------+
// HWR_DrawFadeFill : draw flat coloured rectangle, with transparency
// -----------------+
void HWR_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT16 actualcolor, UINT8 strength)
{
FOutVector v[4];
FSurfaceInfo Surf;
float fx, fy, fw, fh;
UINT8 perplayershuffle = 0;
// 3--2
// | /|
// |/ |
// 0--1
if (splitscreen && (color & V_PERPLAYER))
{
fixed_t adjusty = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f;
h >>= 1;
y >>= 1;
#ifdef QUADS
if (splitscreen > 1) // 3 or 4 players
{
fixed_t adjustx = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f;
w >>= 1;
x >>= 1;
if (stplyr == &players[displayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
color &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
else if (stplyr == &players[secondarydisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
x += adjustx;
color &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
}
else if (stplyr == &players[thirddisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
y += adjusty;
color &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
}
else //if (stplyr == &players[fourthdisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
x += adjustx;
y += adjusty;
color &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
}
}
else
#endif
// 2 players
{
if (stplyr == &players[displayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
color &= ~V_SNAPTOBOTTOM;
}
else //if (stplyr == &players[secondarydisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
y += adjusty;
color &= ~V_SNAPTOTOP;
}
}
}
fx = (float)x;
fy = (float)y;
fw = (float)w;
fh = (float)h;
if (!(color & V_NOSCALESTART))
{
float dupx = (float)vid.dupx, dupy = (float)vid.dupy;
fx *= dupx;
fy *= dupy;
fw *= dupx;
fh *= dupy;
if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
{
if (color & V_SNAPTORIGHT)
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx));
else if (!(color & V_SNAPTOLEFT))
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 2;
if (perplayershuffle & 4)
fx -= ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4;
else if (perplayershuffle & 8)
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4;
}
if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f)
{
// same thing here
if (color & V_SNAPTOBOTTOM)
fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy));
else if (!(color & V_SNAPTOTOP))
fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 2;
if (perplayershuffle & 1)
fy -= ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4;
else if (perplayershuffle & 2)
fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4;
}
}
if (fx >= vid.width || fy >= vid.height)
return;
if (fx < 0)
{
fw += fx;
fx = 0;
}
if (fy < 0)
{
fh += fy;
fy = 0;
}
if (fw <= 0 || fh <= 0)
return;
if (fx + fw > vid.width)
fw = (float)vid.width - fx;
if (fy + fh > vid.height)
fh = (float)vid.height - fy;
fx = -1 + fx / (vid.width / 2);
fy = 1 - fy / (vid.height / 2);
fw = fw / (vid.width / 2);
fh = fh / (vid.height / 2);
v[0].x = v[3].x = fx;
v[2].x = v[1].x = fx + fw;
v[0].y = v[1].y = fy;
v[2].y = v[3].y = fy - fh;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = 1.0f;
if (actualcolor & 0xFF00) // Do COLORMAP fade.
{
Surf.PolyColor.rgba = UINT2RGBA(0x01010160);
Surf.PolyColor.s.alpha = (strength*8);
}
else // Do TRANSMAP** fade.
{
Surf.PolyColor.rgba = V_GetColor(actualcolor).rgba;
Surf.PolyColor.s.alpha = softwaretranstogl[strength];
}
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
2014-03-15 16:59:03 +00:00
// Draw the console background with translucency support
void HWR_DrawConsoleBack(UINT32 color, INT32 height)
{
FOutVector v[4];
FSurfaceInfo Surf;
// setup some neat-o translucency effect
if (!height) //cool hack 0 height is full height
height = vid.height;
v[0].x = v[3].x = -1.0f;
v[2].x = v[1].x = 1.0f;
v[0].y = v[1].y = 1.0f-((height<<1)/(float)vid.height);
v[2].y = v[3].y = 1.0f;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 1.0f;
v[2].t = v[3].t = 0.0f;
2014-03-15 16:59:03 +00:00
Surf.PolyColor.rgba = UINT2RGBA(color);
Surf.PolyColor.s.alpha = 0x80;
2014-03-15 16:59:03 +00:00
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
2018-11-03 21:14:17 +00:00
// Very similar to HWR_DrawConsoleBack, except we draw from the middle(-ish) of the screen to the bottom.
void HWR_DrawTutorialBack(UINT32 color, INT32 boxheight)
{
FOutVector v[4];
FSurfaceInfo Surf;
Introducing Marathon Run. (I was going to call it Marathon Mode, but NiGHTS Mode being right next to it on the menu looked terrible.) Basically a dedicated Record Attack-like experience for speedrunning the game as a continuous chunk rather than ILs. Has several quality of life features. Benefits include: * An unambiguous real-time bar across the bottom of the screen, always displaying the current time, ticking up until you reach the ending. * Disable the console (pausing is still allowed, but the timer will still increment). * Automatically skip intermissions as if you're holding down the spin button. * Show centiseconds on HUD automatically, like record attack. * "Live Event Backups" - a category of run fit for major events like GDQ, where recovery from crashes or chokes makes for better entertainment. Essentially a modified SP savefile, down to using the same basic functions, but has its own filename and tweaked internal layout. * "spmarathon_start" MainCfg block parameter and "marathonnext" mapheader parameter, allowing for a customised flow (makes this fit for purpose for an eventual SUGOI port). * Disabling inter-level custom cutscenes by default with a menu option to toggle this (won't show up if the mod doesn't *have* any custom cutscenes), although either way ending cutscenes (vanilla or custom) remain intact since is time is called before them. * Won't show up if you have a mod that consists of only one level (determined by spmarathon_start's nextlevel; this won't trip if you manually set its marathonnext). * Unconditional gratitude on the evaluation screen, instead of a negging "Try again..." if you didn't get all the emeralds (which you may not have been aiming for). * Gorgeous new menu (no new assets required, unless you wanna give it a header later). Changes which were required for the above but affect other areas of the game include: * "useBlackRock" MainCFG block parameter, which can be used to disable the presence of the Black Rock or Egg Rock in both the Evaluation screen and the Marathon Run menu (for total conversions with different stories). * Disabling Continues in NiGHTS mode, to match the most common singleplayer experience post 2.2.4's release (is reverted if useContinues is set to true). * Hiding the exitmove "powerup" outside of multiplayer. (Okay, this isn't really related, I just saw this bug in action a lot while doing test runs and got annoyed enough to fix it here.) * The ability to use V_DrawPromptBack (in hardcode only at the moment, but) to draw in terms of pixels rather than rows of text, by providing negative instead of positive inputs). * A refactoring of redundant game saves smattered across the ending, credits, and evaluation - in addition to saving the game slightly earlier. * Minor m_menu.c touchups and refactorings here and there. Built using feedback from the official server's #speedruns channel, among other places.
2020-05-14 22:10:00 +00:00
INT32 height;
if (boxheight < 0)
height = -boxheight;
else
height = (boxheight * 4) + (boxheight/2)*5; // 4 lines of space plus gaps between and some leeway
2018-11-03 21:14:17 +00:00
// setup some neat-o translucency effect
v[0].x = v[3].x = -1.0f;
v[2].x = v[1].x = 1.0f;
v[0].y = v[1].y = -1.0f;
v[2].y = v[3].y = -1.0f+((height<<1)/(float)vid.height);
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 1.0f;
v[2].t = v[3].t = 0.0f;
2018-11-03 21:14:17 +00:00
Surf.PolyColor.rgba = UINT2RGBA(color);
Surf.PolyColor.s.alpha = (color == 0 ? 0xC0 : 0x80); // make black darker, like software
2018-11-03 21:14:17 +00:00
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
2014-03-15 16:59:03 +00:00
// ==========================================================================
// R_DRAW.C STUFF
// ==========================================================================
// ------------------
// HWR_DrawViewBorder
// Fill the space around the view window with a Doom flat texture, draw the
// beveled edges.
// 'clearlines' is useful to clear the heads up messages, when the view
// window is reduced, it doesn't refresh all the view borders.
// ------------------
void HWR_DrawViewBorder(INT32 clearlines)
{
INT32 x, y;
INT32 top, side;
INT32 baseviewwidth, baseviewheight;
INT32 basewindowx, basewindowy;
2020-08-08 08:16:47 +00:00
patch_t *patch;
2014-03-15 16:59:03 +00:00
2020-07-06 04:15:08 +00:00
// if (gl_viewwidth == vid.width)
2014-03-15 16:59:03 +00:00
// return;
if (!clearlines)
clearlines = BASEVIDHEIGHT; // refresh all
// calc view size based on original game resolution
2020-07-06 04:15:08 +00:00
baseviewwidth = FixedInt(FixedDiv(FLOAT_TO_FIXED(gl_viewwidth), vid.fdupx)); //(cv_viewsize.value * BASEVIDWIDTH/10)&~7;
baseviewheight = FixedInt(FixedDiv(FLOAT_TO_FIXED(gl_viewheight), vid.fdupy));
top = FixedInt(FixedDiv(FLOAT_TO_FIXED(gl_baseviewwindowy), vid.fdupy));
side = FixedInt(FixedDiv(FLOAT_TO_FIXED(gl_viewwindowx), vid.fdupx));
2014-03-15 16:59:03 +00:00
// top
HWR_DrawFlatFill(0, 0,
BASEVIDWIDTH, (top < clearlines ? top : clearlines),
st_borderpatchnum);
// left
if (top < clearlines)
HWR_DrawFlatFill(0, top, side,
(clearlines-top < baseviewheight ? clearlines-top : baseviewheight),
st_borderpatchnum);
// right
if (top < clearlines)
HWR_DrawFlatFill(side + baseviewwidth, top, side,
(clearlines-top < baseviewheight ? clearlines-top : baseviewheight),
st_borderpatchnum);
// bottom
if (top + baseviewheight < clearlines)
HWR_DrawFlatFill(0, top + baseviewheight,
BASEVIDWIDTH, BASEVIDHEIGHT, st_borderpatchnum);
//
// draw the view borders
//
basewindowx = (BASEVIDWIDTH - baseviewwidth)>>1;
if (baseviewwidth == BASEVIDWIDTH)
basewindowy = 0;
else
basewindowy = top;
// top edge
if (clearlines > basewindowy - 8)
{
2019-09-08 21:27:35 +00:00
patch = W_CachePatchNum(viewborderlump[BRDR_T], PU_PATCH);
2014-03-15 16:59:03 +00:00
for (x = 0; x < baseviewwidth; x += 8)
HWR_DrawPatch(patch, basewindowx + x, basewindowy - 8,
0);
}
// bottom edge
if (clearlines > basewindowy + baseviewheight)
{
2019-09-08 21:27:35 +00:00
patch = W_CachePatchNum(viewborderlump[BRDR_B], PU_PATCH);
2014-03-15 16:59:03 +00:00
for (x = 0; x < baseviewwidth; x += 8)
HWR_DrawPatch(patch, basewindowx + x,
basewindowy + baseviewheight, 0);
}
// left edge
if (clearlines > basewindowy)
{
2019-09-08 21:27:35 +00:00
patch = W_CachePatchNum(viewborderlump[BRDR_L], PU_PATCH);
2014-03-15 16:59:03 +00:00
for (y = 0; y < baseviewheight && basewindowy + y < clearlines;
y += 8)
{
HWR_DrawPatch(patch, basewindowx - 8, basewindowy + y,
0);
}
}
// right edge
if (clearlines > basewindowy)
{
2019-09-08 21:27:35 +00:00
patch = W_CachePatchNum(viewborderlump[BRDR_R], PU_PATCH);
2014-03-15 16:59:03 +00:00
for (y = 0; y < baseviewheight && basewindowy+y < clearlines;
y += 8)
{
HWR_DrawPatch(patch, basewindowx + baseviewwidth,
basewindowy + y, 0);
}
}
// Draw beveled corners.
if (clearlines > basewindowy - 8)
HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_TL],
2019-09-08 21:27:35 +00:00
PU_PATCH),
2014-03-15 16:59:03 +00:00
basewindowx - 8, basewindowy - 8, 0);
if (clearlines > basewindowy - 8)
HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_TR],
2019-09-08 21:27:35 +00:00
PU_PATCH),
2014-03-15 16:59:03 +00:00
basewindowx + baseviewwidth, basewindowy - 8, 0);
if (clearlines > basewindowy+baseviewheight)
HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_BL],
2019-09-08 21:27:35 +00:00
PU_PATCH),
2014-03-15 16:59:03 +00:00
basewindowx - 8, basewindowy + baseviewheight, 0);
if (clearlines > basewindowy + baseviewheight)
HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_BR],
2019-09-08 21:27:35 +00:00
PU_PATCH),
2014-03-15 16:59:03 +00:00
basewindowx + baseviewwidth,
basewindowy + baseviewheight, 0);
}
// ==========================================================================
// AM_MAP.C DRAWING STUFF
// ==========================================================================
// -----------------+
// HWR_drawAMline : draw a line of the automap (the clipping is already done in automap code)
// Arg : color is a RGB 888 value
// -----------------+
void HWR_drawAMline(const fline_t *fl, INT32 color)
{
F2DCoord v1, v2;
RGBA_t color_rgba;
color_rgba = V_GetColor(color);
v1.x = ((float)fl->a.x-(vid.width/2.0f))*(2.0f/vid.width);
v1.y = ((float)fl->a.y-(vid.height/2.0f))*(2.0f/vid.height);
v2.x = ((float)fl->b.x-(vid.width/2.0f))*(2.0f/vid.width);
v2.y = ((float)fl->b.y-(vid.height/2.0f))*(2.0f/vid.height);
HWD.pfnDraw2DLine(&v1, &v2, color_rgba);
}
2018-07-31 09:10:02 +00:00
// -------------------+
// HWR_DrawConsoleFill : draw flat coloured transparent rectangle because that's cool, and hw sucks less than sw for that.
// -------------------+
2019-08-04 09:27:09 +00:00
void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT32 actualcolor)
2018-07-31 09:10:02 +00:00
{
FOutVector v[4];
FSurfaceInfo Surf;
float fx, fy, fw, fh;
UINT8 perplayershuffle = 0;
2018-07-31 09:10:02 +00:00
// 3--2
// | /|
// |/ |
// 0--1
if (splitscreen && (color & V_PERPLAYER))
{
fixed_t adjusty = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f;
h >>= 1;
y >>= 1;
#ifdef QUADS
if (splitscreen > 1) // 3 or 4 players
{
fixed_t adjustx = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f;
w >>= 1;
x >>= 1;
if (stplyr == &players[displayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
color &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
else if (stplyr == &players[secondarydisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
x += adjustx;
color &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
}
else if (stplyr == &players[thirddisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
y += adjusty;
color &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
}
else //if (stplyr == &players[fourthdisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
x += adjustx;
y += adjusty;
color &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
}
}
else
#endif
// 2 players
{
if (stplyr == &players[displayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
color &= ~V_SNAPTOBOTTOM;
}
else //if (stplyr == &players[secondarydisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
y += adjusty;
color &= ~V_SNAPTOTOP;
}
}
}
2018-07-31 09:10:02 +00:00
fx = (float)x;
fy = (float)y;
fw = (float)w;
fh = (float)h;
if (!(color & V_NOSCALESTART))
2018-07-31 09:10:02 +00:00
{
float dupx = (float)vid.dupx, dupy = (float)vid.dupy;
fx *= dupx;
fy *= dupy;
fw *= dupx;
fh *= dupy;
if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
2018-07-31 09:10:02 +00:00
{
if (color & V_SNAPTORIGHT)
2018-07-31 09:10:02 +00:00
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx));
else if (!(color & V_SNAPTOLEFT))
2018-07-31 09:10:02 +00:00
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 2;
if (perplayershuffle & 4)
fx -= ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4;
else if (perplayershuffle & 8)
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4;
2018-07-31 09:10:02 +00:00
}
if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f)
2018-07-31 09:10:02 +00:00
{
// same thing here
if (color & V_SNAPTOBOTTOM)
2018-07-31 09:10:02 +00:00
fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy));
else if (!(color & V_SNAPTOTOP))
2018-07-31 09:10:02 +00:00
fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 2;
if (perplayershuffle & 1)
fy -= ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4;
else if (perplayershuffle & 2)
fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4;
2018-07-31 09:10:02 +00:00
}
}
if (fx >= vid.width || fy >= vid.height)
return;
if (fx < 0)
{
fw += fx;
fx = 0;
}
if (fy < 0)
{
fh += fy;
fy = 0;
}
if (fw <= 0 || fh <= 0)
return;
if (fx + fw > vid.width)
fw = (float)vid.width - fx;
if (fy + fh > vid.height)
fh = (float)vid.height - fy;
fx = -1 + fx / (vid.width / 2);
fy = 1 - fy / (vid.height / 2);
fw = fw / (vid.width / 2);
fh = fh / (vid.height / 2);
v[0].x = v[3].x = fx;
v[2].x = v[1].x = fx + fw;
v[0].y = v[1].y = fy;
v[2].y = v[3].y = fy - fh;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = 1.0f;
Surf.PolyColor.rgba = UINT2RGBA(actualcolor);
Surf.PolyColor.s.alpha = 0x80;
2018-07-31 09:10:02 +00:00
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
2014-03-15 16:59:03 +00:00
// -----------------+
// HWR_DrawFill : draw flat coloured rectangle, with no texture
// -----------------+
void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
{
FOutVector v[4];
FSurfaceInfo Surf;
2018-03-16 19:46:45 +00:00
float fx, fy, fw, fh;
2014-03-15 16:59:03 +00:00
UINT8 perplayershuffle = 0;
2014-03-15 16:59:03 +00:00
// 3--2
// | /|
// |/ |
// 0--1
if (splitscreen && (color & V_PERPLAYER))
{
fixed_t adjusty = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f;
h >>= 1;
y >>= 1;
#ifdef QUADS
if (splitscreen > 1) // 3 or 4 players
{
fixed_t adjustx = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f;
w >>= 1;
x >>= 1;
if (stplyr == &players[displayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
color &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
else if (stplyr == &players[secondarydisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
x += adjustx;
color &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
}
else if (stplyr == &players[thirddisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
y += adjusty;
color &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
}
else //if (stplyr == &players[fourthdisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
x += adjustx;
y += adjusty;
color &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
}
}
else
#endif
// 2 players
{
if (stplyr == &players[displayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
color &= ~V_SNAPTOBOTTOM;
}
else //if (stplyr == &players[secondarydisplayplayer])
{
if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
y += adjusty;
color &= ~V_SNAPTOTOP;
}
}
}
2018-03-16 19:46:45 +00:00
fx = (float)x;
fy = (float)y;
fw = (float)w;
fh = (float)h;
2018-03-16 19:46:45 +00:00
if (!(color & V_NOSCALESTART))
{
float dupx = (float)vid.dupx, dupy = (float)vid.dupy;
if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
{
RGBA_t rgbaColour = V_GetColor(color);
FRGBAFloat clearColour;
clearColour.red = (float)rgbaColour.s.red / 255;
clearColour.green = (float)rgbaColour.s.green / 255;
clearColour.blue = (float)rgbaColour.s.blue / 255;
clearColour.alpha = 1;
HWD.pfnClearBuffer(true, false, &clearColour);
return;
}
fx *= dupx;
fy *= dupy;
fw *= dupx;
fh *= dupy;
2018-12-15 00:33:40 +00:00
if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
2018-03-16 19:46:45 +00:00
{
if (color & V_SNAPTORIGHT)
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx));
else if (!(color & V_SNAPTOLEFT))
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 2;
if (perplayershuffle & 4)
fx -= ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4;
else if (perplayershuffle & 8)
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4;
2018-03-16 19:46:45 +00:00
}
2018-12-15 00:33:40 +00:00
if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f)
2018-03-16 19:46:45 +00:00
{
// same thing here
if (color & V_SNAPTOBOTTOM)
fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy));
else if (!(color & V_SNAPTOTOP))
fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 2;
if (perplayershuffle & 1)
fy -= ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4;
else if (perplayershuffle & 2)
fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4;
}
}
2018-03-16 19:46:45 +00:00
if (fx >= vid.width || fy >= vid.height)
return;
if (fx < 0)
{
fw += fx;
fx = 0;
}
if (fy < 0)
{
fh += fy;
fy = 0;
}
2018-03-16 19:46:45 +00:00
if (fw <= 0 || fh <= 0)
return;
if (fx + fw > vid.width)
fw = (float)vid.width - fx;
if (fy + fh > vid.height)
fh = (float)vid.height - fy;
fx = -1 + fx / (vid.width / 2);
fy = 1 - fy / (vid.height / 2);
fw = fw / (vid.width / 2);
fh = fh / (vid.height / 2);
v[0].x = v[3].x = fx;
v[2].x = v[1].x = fx + fw;
v[0].y = v[1].y = fy;
v[2].y = v[3].y = fy - fh;
2014-03-15 16:59:03 +00:00
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = 1.0f;
2014-03-15 16:59:03 +00:00
Surf.PolyColor = V_GetColor(color);
2014-03-15 16:59:03 +00:00
HWD.pfnDrawPolygon(&Surf, v, 4,
PF_Modulated|PF_NoTexture|PF_NoDepthTest);
}
#ifdef HAVE_PNG
#ifndef _MSC_VER
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#endif
#ifndef _LFS64_LARGEFILE
#define _LFS64_LARGEFILE
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 0
#endif
#include "png.h"
#ifdef PNG_WRITE_SUPPORTED
#define USE_PNG // PNG is only used if write is supported (see ../m_misc.c)
#endif
#endif
#ifndef USE_PNG
// --------------------------------------------------------------------------
// save screenshots with TGA format
// --------------------------------------------------------------------------
static inline boolean saveTGA(const char *file_name, void *buffer,
INT32 width, INT32 height)
{
INT32 fd;
TGAHeader tga_hdr;
INT32 i;
UINT8 *buf8 = buffer;
fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
if (fd < 0)
return false;
memset(&tga_hdr, 0, sizeof (tga_hdr));
tga_hdr.width = SHORT(width);
tga_hdr.height = SHORT(height);
tga_hdr.image_pix_size = 24;
tga_hdr.image_type = 2;
tga_hdr.image_descriptor = 32;
if ( -1 == write(fd, &tga_hdr, sizeof (TGAHeader)))
{
close(fd);
return false;
}
2014-03-15 16:59:03 +00:00
// format to 888 BGR
for (i = 0; i < width * height * 3; i+=3)
{
const UINT8 temp = buf8[i];
buf8[i] = buf8[i+2];
buf8[i+2] = temp;
}
if ( -1 == write(fd, buffer, width * height * 3))
{
close(fd);
return false;
}
2014-03-15 16:59:03 +00:00
close(fd);
return true;
}
#endif
// --------------------------------------------------------------------------
// screen shot
// --------------------------------------------------------------------------
UINT8 *HWR_GetScreenshot(void)
{
UINT8 *buf = malloc(vid.width * vid.height * 3 * sizeof (*buf));
if (!buf)
return NULL;
// returns 24bit 888 RGB
HWD.pfnReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf);
return buf;
}
2019-01-02 04:01:57 +00:00
boolean HWR_Screenshot(const char *pathname)
2014-03-15 16:59:03 +00:00
{
boolean ret;
UINT8 *buf = malloc(vid.width * vid.height * 3 * sizeof (*buf));
if (!buf)
{
2019-01-02 04:01:57 +00:00
CONS_Debug(DBG_RENDER, "HWR_Screenshot: Failed to allocate memory\n");
2014-03-15 16:59:03 +00:00
return false;
}
2014-03-15 16:59:03 +00:00
// returns 24bit 888 RGB
HWD.pfnReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf);
#ifdef USE_PNG
2019-01-02 04:01:57 +00:00
ret = M_SavePNG(pathname, buf, vid.width, vid.height, NULL);
2014-03-15 16:59:03 +00:00
#else
ret = saveTGA(pathname, buf, vid.width, vid.height);
2014-03-15 16:59:03 +00:00
#endif
free(buf);
return ret;
}
#endif //HWRENDER