/* ** 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 #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(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(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); }