gzdoom/src/r_poly_playersprite.cpp

299 lines
8.5 KiB
C++

/*
** Handling drawing a player sprite
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "r_poly_playersprite.h"
#include "r_poly.h"
#include "r_things.h" // for pspritexscale
EXTERN_CVAR(Bool, r_drawplayersprites)
EXTERN_CVAR(Bool, r_deathcamera)
EXTERN_CVAR(Bool, st_scale)
void RenderPolyPlayerSprites::Render()
{
// In theory, everything in this function could be moved to RenderRemainingSprites.
// Just in case there's some hack elsewhere that relies on this happening as part
// of the main rendering we do it exactly as the old software renderer did.
ScreenSprites.clear();
if (!r_drawplayersprites ||
!camera ||
!camera->player ||
(players[consoleplayer].cheats & CF_CHASECAM) ||
(r_deathcamera && camera->health <= 0))
return;
float bobx, boby;
P_BobWeapon(camera->player, &bobx, &boby, r_TicFracF);
// Interpolate the main weapon layer once so as to be able to add it to other layers.
double wx, wy;
DPSprite *weapon = camera->player->FindPSprite(PSP_WEAPON);
if (weapon)
{
if (weapon->firstTic)
{
wx = weapon->x;
wy = weapon->y;
}
else
{
wx = weapon->oldx + (weapon->x - weapon->oldx) * r_TicFracF;
wy = weapon->oldy + (weapon->y - weapon->oldy) * r_TicFracF;
}
}
else
{
wx = 0;
wy = 0;
}
for (DPSprite *sprite = camera->player->psprites; sprite != nullptr; sprite = sprite->GetNext())
{
// [RH] Don't draw the targeter's crosshair if the player already has a crosshair set.
// It's possible this psprite's caller is now null but the layer itself hasn't been destroyed
// because it didn't tick yet (if we typed 'take all' while in the console for example).
// In this case let's simply not draw it to avoid crashing.
if ((sprite->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && sprite->GetCaller() != nullptr)
{
RenderSprite(sprite, camera, bobx, boby, wx, wy, r_TicFracF);
}
}
}
void RenderPolyPlayerSprites::RenderRemainingSprites()
{
for (auto &sprite : ScreenSprites)
sprite.Render();
}
void RenderPolyPlayerSprites::RenderSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac)
{
// decide which patch to use
if ((unsigned)sprite->GetSprite() >= (unsigned)sprites.Size())
{
DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite number %i\n", sprite->GetSprite());
return;
}
spritedef_t *def = &sprites[sprite->GetSprite()];
if (sprite->GetFrame() >= def->numframes)
{
DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite frame %i : %i\n", sprite->GetSprite(), sprite->GetFrame());
return;
}
spriteframe_t *frame = &SpriteFrames[def->spriteframes + sprite->GetFrame()];
FTextureID picnum = frame->Texture[0];
bool flip = (frame->Flip & 1) != 0;
FTexture *tex = TexMan(picnum);
if (tex->UseType == FTexture::TEX_Null)
return;
// Can't interpolate the first tic.
if (sprite->firstTic)
{
sprite->firstTic = false;
sprite->oldx = sprite->x;
sprite->oldy = sprite->y;
}
double sx = sprite->oldx + (sprite->x - sprite->oldx) * ticfrac;
double sy = sprite->oldy + (sprite->y - sprite->oldy) * ticfrac;
if (sprite->Flags & PSPF_ADDBOB)
{
sx += bobx;
sy += boby;
}
if (sprite->Flags & PSPF_ADDWEAPON && sprite->GetID() != PSP_WEAPON)
{
sx += wx;
sy += wy;
}
// calculate edges of the shape
double tx = sx - BaseXCenter;
tx -= tex->GetScaledLeftOffset();
int x1 = xs_RoundToInt(CenterX + tx * pspritexscale);
// off the right side
if (x1 > viewwidth)
return;
tx += tex->GetScaledWidth();
int x2 = xs_RoundToInt(CenterX + tx * pspritexscale);
// off the left side
if (x2 <= 0)
return;
double texturemid = (BaseYCenter - sy) * tex->Scale.Y + tex->TopOffset;
// Adjust PSprite for fullscreen views
if (camera->player && (RenderTarget != screen || viewheight == RenderTarget->GetHeight() || (RenderTarget->GetWidth() > (BaseXCenter * 2) && !st_scale)))
{
AWeapon *weapon = dyn_cast<AWeapon>(sprite->GetCaller());
if (weapon != nullptr && weapon->YAdjust != 0)
{
if (RenderTarget != screen || viewheight == RenderTarget->GetHeight())
{
texturemid -= weapon->YAdjust;
}
else
{
texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust;
}
}
}
// Move the weapon down for 1280x1024.
if (sprite->GetID() < PSP_TARGETCENTER)
{
texturemid -= AspectPspriteOffset(WidescreenRatio);
}
int clipped_x1 = MAX(x1, 0);
int clipped_x2 = MIN(x2, viewwidth);
double xscale = pspritexscale / tex->Scale.X;
double yscale = pspriteyscale / tex->Scale.Y;
uint32_t translation = 0; // [RH] Use default colors
double xiscale, startfrac;
if (flip)
{
xiscale = -pspritexiscale * tex->Scale.X;
startfrac = 1;
}
else
{
xiscale = pspritexiscale * tex->Scale.X;
startfrac = 0;
}
if (clipped_x1 > x1)
startfrac += xiscale * (clipped_x1 - x1);
bool noaccel = false;
FDynamicColormap *basecolormap = viewsector->ColorMap;
FDynamicColormap *colormap_to_use = basecolormap;
visstyle_t visstyle;
visstyle.ColormapNum = 0;
visstyle.BaseColormap = basecolormap;
visstyle.Alpha = 0;
visstyle.RenderStyle = STYLE_Normal;
bool foggy = false;
int actualextralight = foggy ? 0 : extralight << 4;
int spriteshade = LIGHT2SHADE(owner->Sector->lightlevel + actualextralight);
double minz = double((2048 * 4) / double(1 << 20));
visstyle.ColormapNum = GETPALOOKUP(r_SpriteVisibility / minz, spriteshade);
if (sprite->GetID() < PSP_TARGETCENTER)
{
// Lots of complicated style and noaccel stuff
}
// Check for hardware-assisted 2D. If it's available, and this sprite is not
// fuzzy, don't draw it until after the switch to 2D mode.
if (!noaccel && RenderTarget == screen && (DFrameBuffer *)screen->Accel2D)
{
FRenderStyle style = visstyle.RenderStyle;
style.CheckFuzz();
if (style.BlendOp != STYLEOP_Fuzz)
{
PolyScreenSprite screenSprite;
screenSprite.Pic = tex;
screenSprite.X1 = viewwindowx + x1;
screenSprite.Y1 = viewwindowy + viewheight / 2 - texturemid * yscale - 0.5;
screenSprite.Width = tex->GetWidth() * xscale;
screenSprite.Height = tex->GetHeight() * yscale;
screenSprite.Translation = TranslationToTable(translation);
screenSprite.Flip = xiscale < 0;
screenSprite.visstyle = visstyle;
screenSprite.Colormap = colormap_to_use;
ScreenSprites.push_back(screenSprite);
return;
}
}
//R_DrawVisSprite(vis);
}
void PolyScreenSprite::Render()
{
FSpecialColormap *special = nullptr;
FColormapStyle colormapstyle;
PalEntry overlay = 0;
bool usecolormapstyle = false;
if (visstyle.BaseColormap >= &SpecialColormaps[0] &&
visstyle.BaseColormap < &SpecialColormaps[SpecialColormaps.Size()])
{
special = static_cast<FSpecialColormap*>(visstyle.BaseColormap);
}
else if (Colormap->Color == PalEntry(255, 255, 255) &&
Colormap->Desaturate == 0)
{
overlay = Colormap->Fade;
overlay.a = BYTE(visstyle.ColormapNum * 255 / NUMCOLORMAPS);
}
else
{
usecolormapstyle = true;
colormapstyle.Color = Colormap->Color;
colormapstyle.Fade = Colormap->Fade;
colormapstyle.Desaturate = Colormap->Desaturate;
colormapstyle.FadeLevel = visstyle.ColormapNum / float(NUMCOLORMAPS);
}
screen->DrawTexture(Pic,
X1,
Y1,
DTA_DestWidthF, Width,
DTA_DestHeightF, Height,
DTA_Translation, Translation,
DTA_FlipX, Flip,
DTA_TopOffset, 0,
DTA_LeftOffset, 0,
DTA_ClipLeft, viewwindowx,
DTA_ClipTop, viewwindowy,
DTA_ClipRight, viewwindowx + viewwidth,
DTA_ClipBottom, viewwindowy + viewheight,
DTA_AlphaF, visstyle.Alpha,
DTA_RenderStyle, visstyle.RenderStyle,
DTA_FillColor, FillColor,
DTA_SpecialColormap, special,
DTA_ColorOverlay, overlay.d,
DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : nullptr,
TAG_DONE);
}