blankart/src/hardware/hw_main.c

6719 lines
207 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// BLANKART
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file hw_main.c
/// \brief hardware renderer, using the standard HardWareRender driver DLL for SRB2
#include <math.h>
#include "../doomstat.h"
#include "../qs22j.h"
#ifdef HWRENDER
#include "hw_clip.h"
#include "hw_glob.h"
#include "hw_light.h"
#include "hw_drv.h"
#include "hw_batching.h"
#include "../i_video.h" // for rendermode == render_glide
#include "../v_video.h"
#include "../p_local.h"
#include "../p_setup.h"
#include "../r_fps.h"
#include "../r_local.h"
#include "../r_patch.h"
#include "../r_picformats.h"
#include "../r_bsp.h"
#include "../d_clisrv.h"
#include "../w_wad.h"
#include "../z_zone.h"
#include "../r_splats.h"
#include "../g_game.h"
#include "../st_stuff.h"
#include "../i_system.h"
#include "../m_cheat.h"
#include "../f_finale.h"
#include "../r_things.h" // R_GetShadowZ
#include "../d_main.h"
#include "../p_slopes.h"
#include "hw_md2.h"
// SRB2Kart
#include "../k_kart.h"
#include "../r_fps.h"
#include "../r_plane.h" // R_FlatDimensionsFromLumpSize
#include "../k_items.h"
#define R_FAKEFLOORS
#define HWPRECIP
// ==========================================================================
// the hardware driver object
// ==========================================================================
struct hwdriver_s hwdriver;
// ==========================================================================
// PROTOS
// ==========================================================================
static void HWR_AddSprites(sector_t *sec);
static void HWR_ProjectSprite(mobj_t *thing);
#ifdef HWPRECIP
static void HWR_AddPrecipitationSprites(void);
static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing);
#endif
static void HWR_ProjectBoundingBox(mobj_t *thing);
static void HWR_RollTransform(FTransform *tr, angle_t roll);
void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap);
void HWR_AddTransparentPolyobjectFloor(levelflat_t *levelflat, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight,
INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap);
// ==========================================================================
// VIEW GLOBALS
// ==========================================================================
// Fineangles in the SCREENWIDTH wide window.
#define FIELDOFVIEW ANGLE_90
#define ABS(x) ((x) < 0 ? -(x) : (x))
static angle_t gl_clipangle;
// The viewangletox[viewangle + FINEANGLES/4] lookup
// maps the visible view angles to screen X coordinates,
// flattening the arc to a flat projection plane.
// There will be many angles mapped to the same X.
static INT32 gl_viewangletox[FINEANGLES/2];
// The xtoviewangleangle[] table maps a screen pixel
// to the lowest viewangle that maps back to x ranges
// from clipangle to -clipangle.
static angle_t gl_xtoviewangle[MAXVIDWIDTH+1];
// ==========================================================================
// GLOBALS
// ==========================================================================
// uncomment to remove the plane rendering
#define DOPLANES
//#define DOWALLS
// test change fov when looking up/down but bsp projection messup :(
//#define NOCRAPPYMLOOK
// base values set at SetViewSize
static float gl_basecentery;
static float gl_basecenterx;
float gl_baseviewwindowy, gl_basewindowcentery;
float gl_baseviewwindowx, gl_basewindowcenterx;
float gl_viewwidth, gl_viewheight; // viewport clipping boundaries (screen coords)
float gl_viewwindowx;
static float gl_centerx, gl_centery;
static float gl_viewwindowy; // top left corner of view window
static float gl_windowcenterx; // center of view window, for projection
static float gl_windowcentery;
static float gl_pspritexscale, gl_pspriteyscale;
static seg_t *gl_curline;
static side_t *gl_sidedef;
static line_t *gl_linedef;
static sector_t *gl_frontsector;
static sector_t *gl_backsector;
// Render stats
precise_t ps_hw_skyboxtime = 0;
precise_t ps_hw_nodesorttime = 0;
precise_t ps_hw_nodedrawtime = 0;
precise_t ps_hw_spritesorttime = 0;
precise_t ps_hw_spritedrawtime = 0;
// Render stats for batching
int ps_hw_numpolys = 0;
int ps_hw_numverts = 0;
int ps_hw_numcalls = 0;
int ps_hw_numshaders = 0;
int ps_hw_numtextures = 0;
int ps_hw_numpolyflags = 0;
int ps_hw_numcolors = 0;
precise_t ps_hw_batchsorttime = 0;
precise_t ps_hw_batchdrawtime = 0;
boolean gl_init = false;
boolean gl_maploaded = false;
boolean gl_sessioncommandsadded = false;
// false if shaders have not been initialized yet, or if shaders are not available
boolean gl_shadersavailable = false;
// Whether the internal state is set to palette rendering or not.
static boolean gl_palette_rendering_state = false;
// --------------------------------------------------------------------------
// STUFF FOR THE PROJECTION CODE
// --------------------------------------------------------------------------
FTransform atransform;
// duplicates of the main code, set after R_SetupFrame() passed them into sharedstruct,
// copied here for local use
static fixed_t dup_viewx, dup_viewy, dup_viewz;
static angle_t dup_viewangle;
static float gl_viewx, gl_viewy, gl_viewz;
float gl_viewsin, gl_viewcos;
// Maybe not necessary with the new T&L code (needs to be checked!)
static float gl_viewludsin, gl_viewludcos; // look up down kik test
static float gl_fovlud;
static angle_t gl_aimingangle;
static void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox);
// ==========================================================================
// Lighting
// ==========================================================================
// Returns true if shaders can be used.
boolean HWR_UseShader(void)
{
return (cv_glshaders.value && gl_shadersavailable);
}
boolean HWR_OverrideObjectLightLevel(mobj_t *thing, INT32 *lightlevel)
{
if (R_ThingIsFullBright(thing))
*lightlevel = 255;
else if (R_ThingIsFullDark(thing))
*lightlevel = 0;
else if (thing->renderflags & RF_ABSOLUTELIGHTLEVEL)
*lightlevel = R_ThingLightLevel(thing);
else
return false;
return true;
}
void HWR_ObjectLightLevelPost(gl_vissprite_t *spr, const sector_t *sector, INT32 *lightlevel, boolean model)
{
const boolean semibright = R_ThingIsSemiBright(spr->mobj);
const boolean papersprite = R_ThingIsPaperSprite(spr->mobj);
*lightlevel += R_ThingLightLevel(spr->mobj);
if (maplighting.directional == true && P_SectorUsesDirectionalLighting(sector))
{
if (model == false) // this is implemented by shader
{
fixed_t extralight = R_GetSpriteDirectionalLighting(
papersprite
? spr->angle + (spr->flip ? -ANGLE_90 : ANGLE_90)
: R_PointToAngle(spr->mobj->x, spr->mobj->y) // fixme
);
// Less change in contrast in dark sectors
extralight = FixedMul(extralight, min(max(0, *lightlevel), 255) * FRACUNIT / 255);
if (papersprite)
{
// Papersprite contrast should match walls
*lightlevel += FixedFloor(extralight + (FRACUNIT / 2)) / FRACUNIT;
}
else
{
// simple OGL approximation
fixed_t tr = R_PointToDist(spr->mobj->x, spr->mobj->y);
fixed_t xscale = FixedDiv((vid.width / 2) << FRACBITS, tr);
// Less change in contrast at further distances, to counteract DOOM diminished light
fixed_t n = FixedDiv(FixedMul(xscale, LIGHTRESOLUTIONFIX), ((MAXLIGHTSCALE-1) << LIGHTSCALESHIFT));
extralight = FixedMul(extralight, min(n, FRACUNIT));
// Contrast is stronger for normal sprites, stronger than wall lighting is at the same distance
*lightlevel += FixedFloor((extralight * 2) + (FRACUNIT / 2)) / FRACUNIT;
}
}
// Semibright objects will be made slightly brighter to compensate contrast
if (semibright)
{
*lightlevel += 16;
}
}
if (semibright)
{
*lightlevel = 192 + (*lightlevel >> 1);
}
}
void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *colormap, const boolean directional)
{
RGBA_t poly_color, tint_color, fade_color;
poly_color.rgba = 0xFFFFFFFF;
tint_color.rgba = (colormap != NULL) ? (UINT32)colormap->rgba : GL_DEFAULTMIX;
fade_color.rgba = (colormap != NULL) ? (UINT32)colormap->fadergba : GL_DEFAULTFOG;
// Crappy backup coloring if you can't do shaders
if (!HWR_UseShader())
{
// be careful, this may get negative for high lightlevel values.
float tint_alpha, fade_alpha;
float red, green, blue;
red = (float)poly_color.s.red;
green = (float)poly_color.s.green;
blue = (float)poly_color.s.blue;
// 48 is just an arbritrary value that looked relatively okay.
tint_alpha = (float)(sqrt(tint_color.s.alpha) * 48) / 255.0f;
// 8 is roughly the brightness of the "close" color in Software, and 16 the brightness of the "far" color.
// 8 is too bright for dark levels, and 16 is too dark for bright levels.
// 12 is the compromise value. It doesn't look especially good anywhere, but it's the most balanced.
// (Also, as far as I can tell, fade_color's alpha is actually not used in Software, so we only use light level.)
fade_alpha = (float)(sqrt(255-light_level) * 12) / 255.0f;
// Clamp the alpha values
tint_alpha = min(max(tint_alpha, 0.0f), 1.0f);
fade_alpha = min(max(fade_alpha, 0.0f), 1.0f);
red = (tint_color.s.red * tint_alpha) + (red * (1.0f - tint_alpha));
green = (tint_color.s.green * tint_alpha) + (green * (1.0f - tint_alpha));
blue = (tint_color.s.blue * tint_alpha) + (blue * (1.0f - tint_alpha));
red = (fade_color.s.red * fade_alpha) + (red * (1.0f - fade_alpha));
green = (fade_color.s.green * fade_alpha) + (green * (1.0f - fade_alpha));
blue = (fade_color.s.blue * fade_alpha) + (blue * (1.0f - fade_alpha));
poly_color.s.red = (UINT8)red;
poly_color.s.green = (UINT8)green;
poly_color.s.blue = (UINT8)blue;
}
// Shift the lightlevel for Palette rendering mode to replicate software´s limited 32 lightlevels
if (HWR_ShouldUsePaletteRendering())
light_level = (light_level >> LIGHTSEGSHIFT) << LIGHTSEGSHIFT;
// Clamp the light level, since it can sometimes go out of the 0-255 range from animations
light_level = min(max(light_level, cv_secbright.value), 255);
V_CubeApply(&tint_color.s.red, &tint_color.s.green, &tint_color.s.blue);
V_CubeApply(&fade_color.s.red, &fade_color.s.green, &fade_color.s.blue);
Surface->PolyColor.rgba = poly_color.rgba;
Surface->TintColor.rgba = tint_color.rgba;
Surface->FadeColor.rgba = fade_color.rgba;
Surface->LightInfo.light_level = light_level;
Surface->LightInfo.fade_start = (colormap != NULL) ? colormap->fadestart : 0;
Surface->LightInfo.fade_end = (colormap != NULL) ? colormap->fadeend : 31;
Surface->LightInfo.newfade = (colormap != NULL) ? !!(colormap->flags & CMF_NEWFADE) : false;
Surface->LightInfo.directional = (maplighting.directional == true && directional == true);
if (HWR_ShouldUsePaletteRendering())
Surface->LightTableId = HWR_GetLightTableID(colormap);
else
Surface->LightTableId = 0;
}
UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap) // Let's see if this can work
{
RGBA_t realcolor, surfcolor;
INT32 alpha;
realcolor.rgba = (colormap != NULL) ? colormap->rgba : GL_DEFAULTMIX;
if (cv_glshaders.value && gl_shadersavailable)
{
surfcolor.s.alpha = (255 - light);
}
else
{
light = light - (255 - light);
// Don't go out of bounds
if (light < 0)
light = 0;
else if (light > 255)
light = 255;
alpha = (realcolor.s.alpha*255)/25;
// at 255 brightness, alpha is between 0 and 127, at 0 brightness alpha will always be 255
surfcolor.s.alpha = (alpha*light) / (2*256) + 255-light;
}
return surfcolor.s.alpha;
}
static FUINT HWR_CalcWallLight(FUINT lightnum, seg_t *seg)
{
INT16 finallight = lightnum;
if (seg != NULL && P_ApplyLightOffsetFine(lightnum, seg->frontsector))
{
finallight += seg->hwLightOffset;
if (finallight > 255) finallight = 255;
if (finallight < 0) finallight = 0;
}
return (FUINT)finallight;
}
static FUINT HWR_CalcSlopeLight(FUINT lightnum, pslope_t *slope, const sector_t *sector, const boolean fof)
{
INT16 finallight = lightnum;
if (slope != NULL && sector != NULL && P_ApplyLightOffsetFine(lightnum, sector))
{
finallight += (fof ? -slope->hwLightOffset : slope->hwLightOffset);
if (finallight > 255) finallight = 255;
if (finallight < 0) finallight = 0;
}
return (FUINT)finallight;
}
// ==========================================================================
// FLOOR/CEILING GENERATION FROM SUBSECTORS
// ==========================================================================
#ifdef DOPLANES
// -----------------+
// HWR_RenderPlane : Render a floor or ceiling convex polygon
// -----------------+
static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap)
{
polyvertex_t * pv;
float height; //constant y for all points on the convex flat polygon
FOutVector *v3d;
INT32 nrPlaneVerts; //verts original define of convex flat polygon
INT32 i;
float flatxref,flatyref;
float fflatwidth = 64.0f, fflatheight = 64.0f;
float scrollx = 0.0f, scrolly = 0.0f, anglef = 0.0f;
angle_t angle = 0;
FSurfaceInfo Surf;
float tempxsow, tempytow;
pslope_t *slope = NULL;
static FOutVector *planeVerts = NULL;
static UINT16 numAllocedPlaneVerts = 0;
INT32 shader = SHADER_NONE;
// no convex poly were generated for this subsector
if (!xsub->planepoly)
return;
// Get the slope pointer to simplify future code
if (FOFsector)
{
if (FOFsector->f_slope && !isceiling)
slope = FOFsector->f_slope;
else if (FOFsector->c_slope && isceiling)
slope = FOFsector->c_slope;
}
else
{
if (gl_frontsector->f_slope && !isceiling)
slope = gl_frontsector->f_slope;
else if (gl_frontsector->c_slope && isceiling)
slope = gl_frontsector->c_slope;
}
// Set fixedheight to the slope's height from our viewpoint, if we have a slope
if (slope)
fixedheight = P_GetSlopeZAt(slope, viewx, viewy);
height = FIXED_TO_FLOAT(fixedheight);
pv = xsub->planepoly->pts;
nrPlaneVerts = xsub->planepoly->numpts;
if (nrPlaneVerts < 3) //not even a triangle ?
return;
// Allocate plane-vertex buffer if we need to
if (!planeVerts || nrPlaneVerts > numAllocedPlaneVerts)
{
numAllocedPlaneVerts = (UINT16)nrPlaneVerts;
Z_Free(planeVerts);
Z_Malloc(numAllocedPlaneVerts * sizeof (FOutVector), PU_LEVEL, &planeVerts);
}
// set texture for polygon
if (levelflat != NULL)
{
texture_t *texture = textures[R_GetTextureNumForFlat(levelflat)];
fflatwidth = texture->width;
fflatheight = texture->height;
}
else // set no texture
HWR_SetCurrentTexture(NULL);
// reference point for flat texture coord for each vertex around the polygon
flatxref = 0.0;
flatyref = 0.0;
// transform
if (FOFsector != NULL)
{
if (!isceiling) // it's a floor
{
scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth;
scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight;
angle = FOFsector->floorpic_angle;
}
else // it's a ceiling
{
scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth;
scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight;
angle = FOFsector->ceilingpic_angle;
}
}
else if (gl_frontsector)
{
if (!isceiling) // it's a floor
{
scrollx = FIXED_TO_FLOAT(gl_frontsector->floor_xoffs)/fflatwidth;
scrolly = FIXED_TO_FLOAT(gl_frontsector->floor_yoffs)/fflatheight;
angle = gl_frontsector->floorpic_angle;
}
else // it's a ceiling
{
scrollx = FIXED_TO_FLOAT(gl_frontsector->ceiling_xoffs)/fflatwidth;
scrolly = FIXED_TO_FLOAT(gl_frontsector->ceiling_yoffs)/fflatheight;
angle = gl_frontsector->ceilingpic_angle;
}
}
anglef = ANG2RAD(InvAngle(angle));
#define SETUP3DVERT(vert, vx, vy) {\
/* Hurdler: add scrolling texture on floor/ceiling */ \
vert->s = (float)(((vx) / fflatwidth) - flatxref + scrollx);\
vert->t = (float)(flatyref - ((vy) / fflatheight) + scrolly);\
\
/* Need to rotate before translate */\
if (angle) /* Only needs to be done if there's an altered angle */\
{\
tempxsow = vert->s;\
tempytow = vert->t;\
vert->s = (tempxsow * cos(anglef)) - (tempytow * sin(anglef));\
vert->t = (tempxsow * sin(anglef)) + (tempytow * cos(anglef));\
}\
\
vert->x = (vx);\
vert->y = height;\
vert->z = (vy);\
\
if (slope)\
{\
fixedheight = P_GetSlopeZAt(slope, FLOAT_TO_FIXED((vx)), FLOAT_TO_FIXED((vy)));\
vert->y = FIXED_TO_FLOAT(fixedheight);\
}\
}
for (i = 0, v3d = planeVerts; i < nrPlaneVerts; i++,v3d++,pv++)
SETUP3DVERT(v3d, pv->x, pv->y);
lightlevel = HWR_CalcSlopeLight(lightlevel, slope, gl_frontsector, (FOFsector != NULL));
HWR_Lighting(&Surf, lightlevel, planecolormap, P_SectorUsesDirectionalLighting(gl_frontsector));
if (PolyFlags & PF_EnvironmentTrans)
{
Surf.PolyColor.s.alpha = (UINT8)alpha;
PolyFlags |= PF_Modulated;
}
else
PolyFlags |= PF_Masked|PF_Modulated;
if (HWR_UseShader())
{
if (PolyFlags & PF_Fog)
shader = SHADER_FOG;
else if ((PolyFlags & PF_Ripple) && levelflat != NULL && levelflat->type == LEVELFLAT_TEXTURE && R_GetTextureBrightmap(levelflat->texture_id) && HWR_ShouldUsePaletteRendering())
shader = SHADER_BRIGHTMAP_WATER;
else if (PolyFlags & PF_Ripple)
shader = SHADER_WATER;
else if (levelflat != NULL && levelflat->type == LEVELFLAT_TEXTURE && R_GetTextureBrightmap(levelflat->texture_id) && HWR_ShouldUsePaletteRendering())
shader = SHADER_BRIGHTMAP_FLOOR;
else
shader = SHADER_FLOOR;
PolyFlags |= PF_ColorMapped;
}
HWR_ProcessPolygon(&Surf, planeVerts, nrPlaneVerts, PolyFlags, shader, false);
if (subsector)
{
// Horizon lines
FOutVector horizonpts[6];
float dist, vx, vy;
float x1, y1, xd, yd;
UINT8 numplanes, j;
vertex_t v; // For determining the closest distance from the line to the camera, to split render planes for minimum distortion;
const float renderdist = 27000.0f; // How far out to properly render the plane
const float farrenderdist = 32768.0f; // From here, raise plane to horizon level to fill in the line with some texture distortion
seg_t *line = &segs[subsector->firstline];
for (i = 0; i < subsector->numlines; i++, line++)
{
if (!line->glseg && line->linedef->special == HORIZONSPECIAL && R_PointOnSegSide(dup_viewx, dup_viewy, line) == 0)
{
P_ClosestPointOnLine(viewx, viewy, line->linedef, &v);
dist = FIXED_TO_FLOAT(R_PointToDist(v.x, v.y));
if (line->pv1)
{
x1 = ((polyvertex_t *)line->pv1)->x;
y1 = ((polyvertex_t *)line->pv1)->y;
}
else
{
x1 = FIXED_TO_FLOAT(line->v1->x);
y1 = FIXED_TO_FLOAT(line->v1->x);
}
if (line->pv2)
{
xd = ((polyvertex_t *)line->pv2)->x - x1;
yd = ((polyvertex_t *)line->pv2)->y - y1;
}
else
{
xd = FIXED_TO_FLOAT(line->v2->x) - x1;
yd = FIXED_TO_FLOAT(line->v2->y) - y1;
}
// Based on the seg length and the distance from the line, split horizon into multiple poly sets to reduce distortion
dist = sqrtf((xd*xd) + (yd*yd)) / dist / 16.0f;
if (dist > 100.0f)
numplanes = 100;
else
numplanes = (UINT8)dist + 1;
for (j = 0; j < numplanes; j++)
{
// Left side
vx = x1 + xd * j / numplanes;
vy = y1 + yd * j / numplanes;
SETUP3DVERT((&horizonpts[1]), vx, vy);
dist = sqrtf(powf(vx - gl_viewx, 2) + powf(vy - gl_viewy, 2));
vx = (vx - gl_viewx) * renderdist / dist + gl_viewx;
vy = (vy - gl_viewy) * renderdist / dist + gl_viewy;
SETUP3DVERT((&horizonpts[0]), vx, vy);
// Right side
vx = x1 + xd * (j+1) / numplanes;
vy = y1 + yd * (j+1) / numplanes;
SETUP3DVERT((&horizonpts[2]), vx, vy);
dist = sqrtf(powf(vx - gl_viewx, 2) + powf(vy - gl_viewy, 2));
vx = (vx - gl_viewx) * renderdist / dist + gl_viewx;
vy = (vy - gl_viewy) * renderdist / dist + gl_viewy;
SETUP3DVERT((&horizonpts[3]), vx, vy);
// Horizon fills
vx = (horizonpts[0].x - gl_viewx) * farrenderdist / renderdist + gl_viewx;
vy = (horizonpts[0].z - gl_viewy) * farrenderdist / renderdist + gl_viewy;
SETUP3DVERT((&horizonpts[5]), vx, vy);
horizonpts[5].y = gl_viewz;
vx = (horizonpts[3].x - gl_viewx) * farrenderdist / renderdist + gl_viewx;
vy = (horizonpts[3].z - gl_viewy) * farrenderdist / renderdist + gl_viewy;
SETUP3DVERT((&horizonpts[4]), vx, vy);
horizonpts[4].y = gl_viewz;
// Draw
HWR_ProcessPolygon(&Surf, horizonpts, 6, PolyFlags, shader, true);
}
}
}
}
#ifdef ALAM_LIGHTING
// add here code for dynamic lighting on planes
HWR_PlaneLighting(planeVerts, nrPlaneVerts);
#endif
}
#endif //doplanes
FBITFIELD HWR_GetBlendModeFlag(INT32 ast)
{
switch (ast)
{
//case AST_COPY: -- intentionally defaults to translucent
case AST_OVERLAY:
return PF_Masked;
case AST_ADD:
return PF_Additive;
case AST_SUBTRACT:
return PF_ReverseSubtract;
case AST_REVERSESUBTRACT:
return PF_Subtractive;
case AST_MODULATE:
return PF_Multiplicative;
default:
return PF_Translucent;
}
return 0;
}
UINT8 HWR_GetTranstableAlpha(INT32 transtablenum)
{
transtablenum = max(min(transtablenum, tr_trans90), 0);
switch (transtablenum)
{
case 0 : return 0xff;
case tr_trans10 : return 0xe6;
case tr_trans20 : return 0xcc;
case tr_trans30 : return 0xb3;
case tr_trans40 : return 0x99;
case tr_trans50 : return 0x80;
case tr_trans60 : return 0x66;
case tr_trans70 : return 0x4c;
case tr_trans80 : return 0x33;
case tr_trans90 : return 0x19;
}
return 0xff;
}
FBITFIELD HWR_SurfaceBlend(INT32 style, INT32 transtablenum, FSurfaceInfo *pSurf)
{
pSurf->PolyColor.s.alpha = 0xff;
if (style == AST_MODULATE)
return PF_Multiplicative;
if (!transtablenum && !style)
return PF_Masked;
pSurf->PolyColor.s.alpha = HWR_GetTranstableAlpha(transtablenum);
return HWR_GetBlendModeFlag(style);
}
FBITFIELD HWR_TranstableToAlpha(INT32 transtablenum, FSurfaceInfo *pSurf)
{
if (!transtablenum)
{
pSurf->PolyColor.s.alpha = 0x00;
return PF_Masked;
}
pSurf->PolyColor.s.alpha = HWR_GetTranstableAlpha(transtablenum);
return PF_Translucent;
}
static void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, INT32 texnum, boolean noencore, FBITFIELD blend, boolean fogwall, INT32 lightlevel, extracolormap_t *wallcolormap);
// ==========================================================================
// Wall generation from subsector segs
// ==========================================================================
/*
wallVerts order is :
3--2
| /|
|/ |
0--1
*/
//
// HWR_ProjectWall
//
static void HWR_ProjectWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blendmode, INT32 lightlevel, extracolormap_t *wallcolormap, INT32 texnum)
{
INT32 shader = SHADER_NONE;
HWR_Lighting(pSurf, lightlevel, wallcolormap, P_SectorUsesDirectionalLighting(gl_frontsector));
if (HWR_UseShader())
{
if (R_GetTextureBrightmap(texnum) && HWR_ShouldUsePaletteRendering())
shader = SHADER_BRIGHTMAP_WALL;
else
shader = SHADER_WALL;
blendmode |= PF_ColorMapped;
}
HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode|PF_Modulated|PF_Occlude, shader, false);
}
// ==========================================================================
// BSP, CULL, ETC..
// ==========================================================================
//
// HWR_SplitWall
// SoM: split up and light walls according to the lightlist.
// This may also include leaving out parts of the wall that can't be seen
//
static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, boolean noencore, FSurfaceInfo* Surf, INT32 cutflag, ffloor_t *pfloor, FBITFIELD polyflags)
{
float realtop, realbot, top, bot;
float pegt, pegb, pegmul;
float height = 0.0f, bheight = 0.0f;
float endrealtop, endrealbot, endtop, endbot;
float endpegt, endpegb, endpegmul;
float endheight = 0.0f, endbheight = 0.0f;
float diff;
fixed_t v1x = FloatToFixed(wallVerts[0].x);
fixed_t v1y = FloatToFixed(wallVerts[0].z);
fixed_t v2x = FloatToFixed(wallVerts[1].x);
fixed_t v2y = FloatToFixed(wallVerts[1].z);
const UINT8 alpha = Surf->PolyColor.s.alpha;
FUINT lightnum = sector->lightlevel;
extracolormap_t *colormap = NULL;
realtop = top = wallVerts[3].y;
realbot = bot = wallVerts[0].y;
diff = top - bot;
pegt = wallVerts[3].t;
pegb = wallVerts[0].t;
// Lactozilla: If both heights of a side lay on the same position, then this wall is a triangle.
// To avoid division by zero, which would result in a NaN, we check if the vertical difference
// between the two vertices is not zero.
if (fpclassify(diff) == FP_ZERO)
pegmul = 0.0;
else
pegmul = (pegb - pegt) / diff;
endrealtop = endtop = wallVerts[2].y;
endrealbot = endbot = wallVerts[1].y;
diff = endtop - endbot;
endpegt = wallVerts[2].t;
endpegb = wallVerts[1].t;
if (fpclassify(diff) == FP_ZERO)
endpegmul = 0.0;
else
endpegmul = (endpegb - endpegt) / diff;
for (INT32 i = 0; i < sector->numlights; i++)
{
if (endtop < endrealbot && top < realbot)
return;
lightlist_t *list = sector->lightlist;
if (!(list[i].flags & FOF_NOSHADE))
{
if (pfloor && (pfloor->fofflags & FOF_FOG))
{
lightnum = pfloor->master->frontsector->lightlevel;
colormap = pfloor->master->frontsector->extra_colormap;
}
else
{
lightnum = *list[i].lightlevel;
colormap = *list[i].extra_colormap;
}
}
boolean solid = false;
if ((sector->lightlist[i].flags & FOF_CUTSOLIDS) && !(cutflag & FOF_EXTRA))
solid = true;
else if ((sector->lightlist[i].flags & FOF_CUTEXTRA) && (cutflag & FOF_EXTRA))
{
if (sector->lightlist[i].flags & FOF_EXTRA)
{
if ((sector->lightlist[i].flags & (FOF_FOG|FOF_SWIMMABLE)) == (cutflag & (FOF_FOG|FOF_SWIMMABLE))) // Only merge with your own types
solid = true;
}
else
solid = true;
}
else
solid = false;
height = FixedToFloat(P_GetLightZAt(&list[i], v1x, v1y));
endheight = FixedToFloat(P_GetLightZAt(&list[i], v2x, v2y));
if (solid)
{
bheight = FixedToFloat(P_GetFFloorBottomZAt(list[i].caster, v1x, v1y));
endbheight = FixedToFloat(P_GetFFloorBottomZAt(list[i].caster, v2x, v2y));
}
if (endheight >= endtop && height >= top)
{
if (solid && top > bheight)
top = bheight;
if (solid && endtop > endbheight)
endtop = endbheight;
}
if (i + 1 < sector->numlights)
{
bheight = FixedToFloat(P_GetLightZAt(&list[i+1], v1x, v1y));
endbheight = FixedToFloat(P_GetLightZAt(&list[i+1], v2x, v2y));
}
else
{
bheight = realbot;
endbheight = endrealbot;
}
if (endbheight > endtop)
endbot = endtop;
if (bheight >= top)
continue;
// Found a break
// The heights are clamped to ensure the polygon doesn't cross itself.
bot = bheight;
if (bot < realbot)
bot = realbot;
endbot = min(max(endbheight, endrealbot), endtop);
Surf->PolyColor.s.alpha = alpha;
wallVerts[3].t = pegt + ((realtop - top) * pegmul);
wallVerts[2].t = endpegt + ((endrealtop - endtop) * endpegmul);
wallVerts[0].t = pegt + ((realtop - bot) * pegmul);
wallVerts[1].t = endpegt + ((endrealtop - endbot) * endpegmul);
// set top/bottom coords
wallVerts[3].y = top;
wallVerts[2].y = endtop;
wallVerts[0].y = bot;
wallVerts[1].y = endbot;
if (polyflags & PF_Fog)
HWR_AddTransparentWall(wallVerts, Surf, texnum, noencore, polyflags, true, HWR_CalcWallLight(lightnum, gl_curline), colormap);
else if (polyflags & PF_EnvironmentTrans)
HWR_AddTransparentWall(wallVerts, Surf, texnum, noencore, polyflags, false, HWR_CalcWallLight(lightnum, gl_curline), colormap);
else
HWR_ProjectWall(wallVerts, Surf, polyflags, HWR_CalcWallLight(lightnum, gl_curline), colormap, texnum);
top = bot;
endtop = endbot;
}
bot = realbot;
endbot = endrealbot;
if (endtop <= endrealbot && top <= realbot)
return;
Surf->PolyColor.s.alpha = alpha;
wallVerts[3].t = pegt + ((realtop - top) * pegmul);
wallVerts[2].t = endpegt + ((endrealtop - endtop) * endpegmul);
wallVerts[0].t = pegt + ((realtop - bot) * pegmul);
wallVerts[1].t = endpegt + ((endrealtop - endbot) * endpegmul);
// set top/bottom coords
wallVerts[3].y = top;
wallVerts[2].y = endtop;
wallVerts[0].y = bot;
wallVerts[1].y = endbot;
if (polyflags & PF_Fog)
HWR_AddTransparentWall(wallVerts, Surf, texnum, noencore, polyflags, true, HWR_CalcWallLight(lightnum, gl_curline), colormap);
else if (polyflags & PF_EnvironmentTrans)
HWR_AddTransparentWall(wallVerts, Surf, texnum, noencore, polyflags, false, HWR_CalcWallLight(lightnum, gl_curline), colormap);
else
HWR_ProjectWall(wallVerts, Surf, polyflags, HWR_CalcWallLight(lightnum, gl_curline), colormap, texnum);
}
// HWR_DrawSkyWall
// Draw walls into the depth buffer so that anything behind is culled properly
static void HWR_DrawSkyWall(FOutVector *wallVerts, FSurfaceInfo *Surf)
{
HWR_SetCurrentTexture(NULL);
// no texture
wallVerts[3].t = wallVerts[2].t = 0;
wallVerts[0].t = wallVerts[1].t = 0;
wallVerts[0].s = wallVerts[3].s = 0;
wallVerts[2].s = wallVerts[1].s = 0;
// this no longer sets top/bottom coords, this should be done before caling the function
HWR_ProjectWall(wallVerts, Surf, PF_Invisible|PF_NoTexture, 255, NULL, 0);
// PF_Invisible so it's not drawn into the colour buffer
// PF_NoTexture for no texture
// PF_Occlude is set in HWR_ProjectWall to draw into the depth buffer
}
// Returns true if the midtexture is visible, and false if... it isn't...
static boolean HWR_BlendMidtextureSurface(FSurfaceInfo *pSurf)
{
FUINT blendmode = PF_Masked;
pSurf->PolyColor.s.alpha = 0xFF;
if (LIKELY(!gl_curline->polyseg))
{
if (gl_linedef->blendmode && gl_linedef->blendmode != AST_FOG)
{
if (gl_linedef->alpha >= 0 && gl_linedef->alpha < FRACUNIT)
blendmode = HWR_SurfaceBlend(gl_linedef->blendmode, R_GetLinedefTransTable(gl_linedef->alpha), pSurf);
else
blendmode = HWR_GetBlendModeFlag(gl_linedef->blendmode);
}
else if (gl_linedef->alpha >= 0 && gl_linedef->alpha < FRACUNIT)
blendmode = HWR_TranstableToAlpha(R_GetLinedefTransTable(gl_linedef->alpha), pSurf);
}
else if (gl_curline->polyseg->translucency > 0)
{
// Polyobject translucency is done differently
if (gl_curline->polyseg->translucency >= NUMTRANSMAPS) // wall not drawn
return false;
else
blendmode = HWR_TranstableToAlpha(gl_curline->polyseg->translucency, pSurf);
}
if (blendmode != PF_Masked && pSurf->PolyColor.s.alpha == 0x00)
return false;
pSurf->PolyFlags = blendmode;
return true;
}
//
// HWR_ProcessSeg
// A portion or all of a wall segment will be drawn, from startfrac to endfrac,
// where 0 is the start of the segment, 1 the end of the segment
// Anything between means the wall segment has been clipped with solidsegs,
// reducing wall overdraw to a minimum
//
static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
{
FOutVector wallVerts[4];
v2d_t vs, ve; // start, end vertices of 2d line (view from above)
fixed_t worldtop, worldbottom;
fixed_t worldhigh = 0, worldlow = 0;
fixed_t worldtopslope, worldbottomslope;
fixed_t worldhighslope = 0, worldlowslope = 0;
fixed_t v1x, v1y, v2x, v2y;
fixed_t h, l; // 3D sides and 2s middle textures
fixed_t hS, lS;
boolean tripwire;
boolean noencore;
gl_sidedef = gl_curline->sidedef;
gl_linedef = gl_curline->linedef;
if (LIKELY(gl_curline->pv1))
{
vs.x = ((polyvertex_t *)gl_curline->pv1)->x;
vs.y = ((polyvertex_t *)gl_curline->pv1)->y;
}
else
{
vs.x = FIXED_TO_FLOAT(gl_curline->v1->x);
vs.y = FIXED_TO_FLOAT(gl_curline->v1->y);
}
if (LIKELY(gl_curline->pv2))
{
ve.x = ((polyvertex_t *)gl_curline->pv2)->x;
ve.y = ((polyvertex_t *)gl_curline->pv2)->y;
}
else
{
ve.x = FIXED_TO_FLOAT(gl_curline->v2->x);
ve.y = FIXED_TO_FLOAT(gl_curline->v2->y);
}
v1x = FLOAT_TO_FIXED(vs.x);
v1y = FLOAT_TO_FIXED(vs.y);
v2x = FLOAT_TO_FIXED(ve.x);
v2y = FLOAT_TO_FIXED(ve.y);
#define SLOPEPARAMS(slope, end1, end2, normalheight) \
end1 = P_GetZAt(slope, v1x, v1y, normalheight); \
end2 = P_GetZAt(slope, v2x, v2y, normalheight);
SLOPEPARAMS(gl_frontsector->c_slope, worldtop, worldtopslope, gl_frontsector->ceilingheight)
SLOPEPARAMS(gl_frontsector->f_slope, worldbottom, worldbottomslope, gl_frontsector->floorheight)
// remember vertices ordering
// 3--2
// | /|
// |/ |
// 0--1
// make a wall polygon (with 2 triangles), using the floor/ceiling heights,
// and the 2d map coords of start/end vertices
wallVerts[0].x = wallVerts[3].x = vs.x;
wallVerts[0].z = wallVerts[3].z = vs.y;
wallVerts[2].x = wallVerts[1].x = ve.x;
wallVerts[2].z = wallVerts[1].z = ve.y;
// x offset the texture
fixed_t texturehpeg = gl_sidedef->textureoffset + gl_curline->offset;
float cliplow = (float)texturehpeg;
float cliphigh = (float)(texturehpeg + (gl_curline->flength*FRACUNIT));
FUINT lightnum = 255;
extracolormap_t *colormap = gl_frontsector->extra_colormap;
FSurfaceInfo Surf;
Surf.PolyColor.s.alpha = 255;
tripwire = P_IsLineTripWire(gl_linedef);
noencore = (gl_linedef->flags & ML_TFERLINE);
if (tripwire == false)
{
lightnum = HWR_CalcWallLight(gl_frontsector->lightlevel, gl_curline);
}
INT32 gl_midtexture = R_GetTextureNum(gl_sidedef->midtexture);
GLMapTexture_t *grTex = NULL;
// two sided line
if (gl_backsector)
{
INT32 gl_toptexture = 0, gl_bottomtexture = 0;
fixed_t texturevpeg;
boolean bothceilingssky = false; // turned on if both back and front ceilings are sky
boolean bothfloorssky = false; // likewise, but for floors
SLOPEPARAMS(gl_backsector->c_slope, worldhigh, worldhighslope, gl_backsector->ceilingheight)
SLOPEPARAMS(gl_backsector->f_slope, worldlow, worldlowslope, gl_backsector->floorheight)
if (mapnamespace == MNS_SRB2KART)
{
if (!gl_curline->polyseg) // Don't do it for polyobjects
{
// Sky Ceilings
wallVerts[3].y = wallVerts[2].y = FIXED_TO_FLOAT(INT32_MAX);
if (gl_frontsector->ceilingpic == skyflatnum)
{
if (gl_backsector->ceilingpic == skyflatnum)
{
// Both front and back sectors are sky, needs skywall from the frontsector's ceiling, but only if the
// backsector is lower
if ((worldhigh <= worldtop && worldhighslope <= worldtopslope)// Assuming ESLOPE is always on with my changes
&& (worldhigh != worldtop || worldhighslope != worldtopslope))
// Removing the second line above will render more rarely visible skywalls. Example: Cave garden ceiling in Dark race
{
wallVerts[0].y = FIXED_TO_FLOAT(worldhigh);
wallVerts[1].y = FIXED_TO_FLOAT(worldhighslope);
HWR_DrawSkyWall(wallVerts, &Surf);
}
}
else
{
// Only the frontsector is sky, just draw a skywall from the front ceiling
wallVerts[0].y = FIXED_TO_FLOAT(worldtop);
wallVerts[1].y = FIXED_TO_FLOAT(worldtopslope);
HWR_DrawSkyWall(wallVerts, &Surf);
}
}
else if (gl_backsector->ceilingpic == skyflatnum)
{
// Only the backsector is sky, just draw a skywall from the front ceiling
wallVerts[0].y = FIXED_TO_FLOAT(worldtop);
wallVerts[1].y = FIXED_TO_FLOAT(worldtopslope);
HWR_DrawSkyWall(wallVerts, &Surf);
}
// Sky Floors
wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(INT32_MIN);
if (gl_frontsector->floorpic == skyflatnum)
{
if (gl_backsector->floorpic == skyflatnum)
{
// Both front and back sectors are sky, needs skywall from the backsector's floor, but only if the
// it's higher, also needs to check for bottomtexture as the floors don't usually move down
// when both sides are sky floors
if ((worldlow >= worldbottom && worldlowslope >= worldbottomslope)
&& (worldlow != worldbottom || worldlowslope != worldbottomslope)
// Removing the second line above will render more rarely visible skywalls. Example: Cave garden ceiling in Dark race
&& !(gl_sidedef->bottomtexture))
{
wallVerts[3].y = FIXED_TO_FLOAT(worldlow);
wallVerts[2].y = FIXED_TO_FLOAT(worldlowslope);
HWR_DrawSkyWall(wallVerts, &Surf);
}
}
else
{
// Only the backsector has sky, just draw a skywall from the back floor
wallVerts[3].y = FIXED_TO_FLOAT(worldbottom);
wallVerts[2].y = FIXED_TO_FLOAT(worldbottomslope);
HWR_DrawSkyWall(wallVerts, &Surf);
}
}
else if ((gl_backsector->floorpic == skyflatnum) && !(gl_sidedef->bottomtexture))
{
// Only the backsector has sky, just draw a skywall from the back floor if there's no bottomtexture
wallVerts[3].y = FIXED_TO_FLOAT(worldlow);
wallVerts[2].y = FIXED_TO_FLOAT(worldlowslope);
HWR_DrawSkyWall(wallVerts, &Surf);
}
}
}
// hack to allow height changes in outdoor areas
// This is what gets rid of the upper textures if there should be sky
if (gl_frontsector->ceilingpic == skyflatnum
&& gl_backsector->ceilingpic == skyflatnum)
{
if (mapnamespace != MNS_SRB2KART)
bothceilingssky = true;
else
{
worldtop = worldhigh;
worldtopslope = worldhighslope;
}
}
if (mapnamespace != MNS_SRB2KART)
{
// likewise, but for floors and upper textures
if (gl_frontsector->floorpic == skyflatnum
&& gl_backsector->floorpic == skyflatnum)
{
bothfloorssky = true;
}
}
// this is always false in binary anyways
if (!bothceilingssky)
gl_toptexture = R_GetTextureNum(gl_sidedef->toptexture);
if (!bothfloorssky)
gl_bottomtexture = R_GetTextureNum(gl_sidedef->bottomtexture);
// check TOP TEXTURE
if ((worldhighslope < worldtopslope || worldhigh < worldtop) && gl_toptexture)
{
// PEGGING
if (gl_linedef->flags & ML_DONTPEGTOP)
texturevpeg = 0;
else if (gl_linedef->flags & ML_SKEWTD)
texturevpeg = worldhigh + textureheight[gl_toptexture] - worldtop;
else
texturevpeg = gl_backsector->ceilingheight + textureheight[gl_toptexture] - gl_frontsector->ceilingheight;
texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_top;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
texturevpeg %= textureheight[gl_toptexture];
grTex = HWR_GetTexture(gl_toptexture, noencore);
wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_backsector->ceilingheight) * grTex->scaleY;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_top) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_top) * grTex->scaleX;
// Adjust t value for sloped walls
if (!(gl_linedef->flags & ML_SKEWTD))
{
// Unskewed
wallVerts[3].t -= (worldtop - gl_frontsector->ceilingheight) * grTex->scaleY;
wallVerts[2].t -= (worldtopslope - gl_frontsector->ceilingheight) * grTex->scaleY;
wallVerts[0].t -= (worldhigh - gl_backsector->ceilingheight) * grTex->scaleY;
wallVerts[1].t -= (worldhighslope - gl_backsector->ceilingheight) * grTex->scaleY;
}
else if (gl_linedef->flags & ML_DONTPEGTOP)
{
// Skewed by top
wallVerts[0].t = (texturevpeg + worldtop - worldhigh) * grTex->scaleY;
wallVerts[1].t = (texturevpeg + worldtopslope - worldhighslope) * grTex->scaleY;
}
else
{
// Skewed by bottom
wallVerts[0].t = wallVerts[1].t = (texturevpeg + worldtop - worldhigh) * grTex->scaleY;
wallVerts[3].t = wallVerts[0].t - (worldtop - worldhigh) * grTex->scaleY;
wallVerts[2].t = wallVerts[1].t - (worldtopslope - worldhighslope) * grTex->scaleY;
}
// set top/bottom coords
wallVerts[3].y = FIXED_TO_FLOAT(worldtop);
wallVerts[0].y = FIXED_TO_FLOAT(worldhigh);
wallVerts[2].y = FIXED_TO_FLOAT(worldtopslope);
wallVerts[1].y = FIXED_TO_FLOAT(worldhighslope);
FBITFIELD polyflags = PF_Masked;
if (grTex->mipmap.flags & TF_TRANSPARENT)
polyflags = PF_Environment;
if (gl_frontsector->numlights)
HWR_SplitWall(gl_frontsector, wallVerts, gl_toptexture, noencore, &Surf, FOF_CUTLEVEL, NULL, polyflags);
else if (grTex->mipmap.flags & TF_TRANSPARENT)
HWR_AddTransparentWall(wallVerts, &Surf, gl_toptexture, noencore, polyflags, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, polyflags, lightnum, colormap, gl_toptexture);
}
// check BOTTOM TEXTURE
if ((worldlowslope > worldbottomslope || worldlow > worldbottom) && gl_bottomtexture)
{
// PEGGING
if (!(gl_linedef->flags & ML_DONTPEGBOTTOM))
texturevpeg = 0;
else if (gl_linedef->flags & ML_SKEWTD)
texturevpeg = worldbottom - worldlow;
else
texturevpeg = gl_frontsector->floorheight - gl_backsector->floorheight;
texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_bot;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
texturevpeg %= textureheight[gl_bottomtexture];
grTex = HWR_GetTexture(gl_bottomtexture, noencore);
wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_backsector->floorheight - gl_frontsector->floorheight) * grTex->scaleY;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_bot) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_bot) * grTex->scaleX;
// Adjust t value for sloped walls
if (!(gl_linedef->flags & ML_SKEWTD))
{
// Unskewed
wallVerts[0].t -= (worldbottom - gl_frontsector->floorheight) * grTex->scaleY;
wallVerts[1].t -= (worldbottomslope - gl_frontsector->floorheight) * grTex->scaleY;
wallVerts[3].t -= (worldlow - gl_backsector->floorheight) * grTex->scaleY;
wallVerts[2].t -= (worldlowslope - gl_backsector->floorheight) * grTex->scaleY;
}
else if (gl_linedef->flags & ML_DONTPEGBOTTOM)
{
// Skewed by bottom
wallVerts[0].t = wallVerts[1].t = (texturevpeg + worldlow - worldbottom) * grTex->scaleY;
wallVerts[2].t = wallVerts[1].t - (worldlowslope - worldbottomslope) * grTex->scaleY;
}
else
{
// Skewed by top
wallVerts[0].t = (texturevpeg + worldlow - worldbottom) * grTex->scaleY;
wallVerts[1].t = (texturevpeg + worldlowslope - worldbottomslope) * grTex->scaleY;
}
// set top/bottom coords
wallVerts[3].y = FIXED_TO_FLOAT(worldlow);
wallVerts[0].y = FIXED_TO_FLOAT(worldbottom);
wallVerts[2].y = FIXED_TO_FLOAT(worldlowslope);
wallVerts[1].y = FIXED_TO_FLOAT(worldbottomslope);
FBITFIELD polyflags = PF_Masked;
if (grTex->mipmap.flags & TF_TRANSPARENT)
polyflags = PF_Environment;
if (gl_frontsector->numlights)
HWR_SplitWall(gl_frontsector, wallVerts, gl_bottomtexture, noencore, &Surf, FOF_CUTLEVEL, NULL, polyflags);
else if (grTex->mipmap.flags & TF_TRANSPARENT)
HWR_AddTransparentWall(wallVerts, &Surf, gl_bottomtexture, noencore, polyflags, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, polyflags, lightnum, colormap, gl_bottomtexture);
}
// Render midtexture if there's one. Determine if it's visible first, though
if (gl_midtexture && HWR_BlendMidtextureSurface(&Surf))
{
sector_t *front, *back;
INT32 repeats;
if (gl_linedef->frontsector->heightsec != -1)
front = &sectors[gl_linedef->frontsector->heightsec];
else
front = gl_linedef->frontsector;
if (gl_linedef->backsector->heightsec != -1)
back = &sectors[gl_linedef->backsector->heightsec];
else
back = gl_linedef->backsector;
if (gl_sidedef->repeatcnt)
repeats = 1 + gl_sidedef->repeatcnt;
else if (gl_linedef->flags & ML_WRAPMIDTEX)
{
fixed_t high, low;
if (front->ceilingheight > back->ceilingheight)
high = back->ceilingheight;
else
high = front->ceilingheight;
if (front->floorheight > back->floorheight)
low = front->floorheight;
else
low = back->floorheight;
repeats = (high - low) / textureheight[gl_midtexture];
if ((high - low) % textureheight[gl_midtexture])
repeats++; // tile an extra time to fill the gap -- Monster Iestyn
}
else
repeats = 1;
// SoM: a little note: popentop and popenbottom
// record the limits the texture can be displayed in.
// polytop and polybottom, are the ideal (i.e. unclipped)
// heights of the polygon, and h & l, are the final (clipped)
// poly coords.
fixed_t popentop, popenbottom, polytop, polybottom, lowcut, highcut;
fixed_t popentopslope, popenbottomslope, polytopslope, polybottomslope, lowcutslope, highcutslope;
// NOTE: With polyobjects, whenever you need to check the properties of the polyobject sector it belongs to,
// you must use the linedef's backsector to be correct
// From CB
if (gl_curline->polyseg)
{
popentop = popentopslope = back->ceilingheight;
popenbottom = popenbottomslope = back->floorheight;
}
else
{
popentop = min(worldtop, worldhigh);
popenbottom = max(worldbottom, worldlow);
popentopslope = min(worldtopslope, worldhighslope);
popenbottomslope = max(worldbottomslope, worldlowslope);
}
// Find the wall's coordinates
fixed_t midtexheight = textureheight[gl_midtexture] * repeats;
// Texture is not skewed
if (gl_linedef->flags & ML_NOSKEW)
{
if (gl_linedef->flags & ML_MIDPEG) // Peg it to the floor
{
polybottom = max(front->floorheight, back->floorheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
polytop = polybottom + midtexheight;
}
else // Peg it to the ceiling
{
polytop = min(front->ceilingheight, back->ceilingheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
polybottom = polytop - midtexheight;
}
// The right side's coordinates are the the same as the left side
polytopslope = polytop;
polybottomslope = polybottom;
}
else if (gl_linedef->flags & ML_MIDPEG) // Skew the texture, but peg it to the floor
{
polybottom = popenbottom + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
polytop = polybottom + midtexheight;
polybottomslope = popenbottomslope + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
polytopslope = polybottomslope + midtexheight;
}
else // Skew it according to the ceiling's slope
{
polytop = popentop + gl_sidedef->rowoffset;
polybottom = polytop - midtexheight;
polytopslope = popentopslope + gl_sidedef->rowoffset;
polybottomslope = polytopslope - midtexheight;
}
// CB
// NOTE: With polyobjects, whenever you need to check the properties of the polyobject sector it belongs to,
// you must use the linedef's backsector to be correct
if (gl_curline->polyseg)
{
lowcut = polybottom;
highcut = polytop;
lowcutslope = polybottomslope;
highcutslope = polytopslope;
}
else
{
// The cut-off values of a linedef can always be constant, since every line has an absoulute front and or back sector
lowcut = popenbottom;
highcut = popentop;
lowcutslope = popenbottomslope;
highcutslope = popentopslope;
}
h = min(highcut, polytop);
l = max(polybottom, lowcut);
hS = min(highcutslope, polytopslope);
lS = max(polybottomslope, lowcutslope);
// PEGGING
fixed_t texturevpegslope;
if (gl_linedef->flags & ML_MIDPEG)
{
texturevpeg = midtexheight - h + polybottom;
texturevpegslope = midtexheight - hS + polybottomslope;
}
else
{
texturevpeg = polytop - h;
texturevpegslope = polytopslope - hS;
}
grTex = HWR_GetTexture(gl_midtexture, noencore);
// Left side
wallVerts[3].t = texturevpeg * grTex->scaleY;
wallVerts[0].t = (h - l + texturevpeg) * grTex->scaleY;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
// Right side
wallVerts[2].t = texturevpegslope * grTex->scaleY;
wallVerts[1].t = (hS - lS + texturevpegslope) * grTex->scaleY;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
// set top/bottom coords
// Take the texture peg into account, rather than changing the offsets past
// where the polygon might not be.
wallVerts[3].y = FIXED_TO_FLOAT(h);
wallVerts[0].y = FIXED_TO_FLOAT(l);
wallVerts[2].y = FIXED_TO_FLOAT(hS);
wallVerts[1].y = FIXED_TO_FLOAT(lS);
// TODO: Actually use the surface's flags so that I don't have to do this
FUINT blendmode = Surf.PolyFlags;
if (mapnamespace != MNS_SRB2KART)
{
// Correct to account for slopes
fixed_t midtextureslant;
if (gl_linedef->flags & ML_NOSKEW)
midtextureslant = 0;
else if (gl_linedef->flags & ML_MIDPEG)
midtextureslant = worldlow < worldbottom
? worldbottomslope-worldbottom
: worldlowslope-worldlow;
else
midtextureslant = worldtop < worldhigh
? worldtopslope-worldtop
: worldhighslope-worldhigh;
polytop += midtextureslant;
polybottom += midtextureslant;
highcut += worldtop < worldhigh
? worldtopslope-worldtop
: worldhighslope-worldhigh;
lowcut += worldlow < worldbottom
? worldbottomslope-worldbottom
: worldlowslope-worldlow;
// Texture stuff
h = min(highcut, polytop);
l = max(polybottom, lowcut);
{
// PEGGING
if (gl_linedef->flags & ML_MIDPEG)
texturevpeg = textureheight[gl_sidedef->midtexture]*repeats - h + polybottom;
else
texturevpeg = polytop - h;
wallVerts[2].t = texturevpeg * grTex->scaleY;
wallVerts[1].t = (h - l + texturevpeg) * grTex->scaleY;
}
wallVerts[2].y = FIXED_TO_FLOAT(h);
wallVerts[1].y = FIXED_TO_FLOAT(l);
}
// Render midtextures on two-sided lines with a z-buffer offset.
// This will cause the midtexture appear on top, if a FOF overlaps with it.
blendmode |= PF_Decal;
if (tripwire == false && gl_frontsector->numlights)
{
if (!(blendmode & PF_Masked))
HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, noencore, &Surf, FOF_TRANSLUCENT, NULL, blendmode); // vanilla just uses PF_Masked here - if we run into any issues, maybe change to that
else
{
HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, noencore, &Surf, FOF_CUTLEVEL, NULL, blendmode); // vanilla just uses PF_Masked here - if we run into any issues, maybe change to that
}
}
else if (!(blendmode & PF_Masked))
HWR_AddTransparentWall(wallVerts, &Surf, gl_midtexture, noencore, blendmode, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, blendmode, lightnum, colormap, gl_midtexture);
}
// Sky culling
// No longer so much a mess as before!
// It now is a mess again, thx kart!
// see above!
if (mapnamespace != MNS_SRB2KART)
{
if (!gl_curline->polyseg) // Don't do it for polyobjects
{
if (gl_frontsector->ceilingpic == skyflatnum
&& gl_backsector->ceilingpic != skyflatnum) // don't cull if back sector is also sky
{
wallVerts[2].y = wallVerts[3].y = FIXED_TO_FLOAT(INT32_MAX); // draw to top of map space
wallVerts[0].y = FIXED_TO_FLOAT(worldtop);
wallVerts[1].y = FIXED_TO_FLOAT(worldtopslope);
HWR_DrawSkyWall(wallVerts, &Surf);
}
if (gl_frontsector->floorpic == skyflatnum
&& gl_backsector->floorpic != skyflatnum) // same thing here
{
wallVerts[3].y = FIXED_TO_FLOAT(worldbottom);
wallVerts[2].y = FIXED_TO_FLOAT(worldbottomslope);
wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(INT32_MIN); // draw to bottom of map space
HWR_DrawSkyWall(wallVerts, &Surf);
}
}
}
}
else
{
// Single sided line... Deal only with the middletexture (if one exists)
if (gl_midtexture && gl_linedef->special != HORIZONSPECIAL) // (Ignore horizon line for OGL)
{
fixed_t texturevpeg;
// PEGGING
if ((gl_linedef->flags & (ML_DONTPEGBOTTOM|ML_NOSKEW)) == (ML_DONTPEGBOTTOM|ML_NOSKEW))
texturevpeg = gl_frontsector->floorheight + textureheight[gl_sidedef->midtexture] - gl_frontsector->ceilingheight + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
else if (gl_linedef->flags & ML_DONTPEGBOTTOM)
texturevpeg = worldbottom + textureheight[gl_sidedef->midtexture] - worldtop + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
else
// top of texture at top
texturevpeg = gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
grTex = HWR_GetTexture(gl_midtexture, noencore);
wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_frontsector->floorheight) * grTex->scaleY;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
// Texture correction for slopes
if (gl_linedef->flags & ML_NOSKEW) {
wallVerts[3].t += (gl_frontsector->ceilingheight - worldtop) * grTex->scaleY;
wallVerts[2].t += (gl_frontsector->ceilingheight - worldtopslope) * grTex->scaleY;
wallVerts[0].t += (gl_frontsector->floorheight - worldbottom) * grTex->scaleY;
wallVerts[1].t += (gl_frontsector->floorheight - worldbottomslope) * grTex->scaleY;
} else if (gl_linedef->flags & ML_DONTPEGBOTTOM) {
wallVerts[3].t = wallVerts[0].t + (worldbottom-worldtop) * grTex->scaleY;
wallVerts[2].t = wallVerts[1].t + (worldbottomslope-worldtopslope) * grTex->scaleY;
} else {
wallVerts[0].t = wallVerts[3].t - (worldbottom-worldtop) * grTex->scaleY;
wallVerts[1].t = wallVerts[2].t - (worldbottomslope-worldtopslope) * grTex->scaleY;
}
//Set textures properly on single sided walls that are sloped
wallVerts[3].y = FIXED_TO_FLOAT(worldtop);
wallVerts[0].y = FIXED_TO_FLOAT(worldbottom);
wallVerts[2].y = FIXED_TO_FLOAT(worldtopslope);
wallVerts[1].y = FIXED_TO_FLOAT(worldbottomslope);
// I don't think that solid walls can use translucent linedef types...
if (gl_frontsector->numlights)
HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, noencore, &Surf, FOF_CUTLEVEL, NULL, 0);
else
{
FBITFIELD blendmode = PF_Masked;
if (grTex->mipmap.flags & TF_TRANSPARENT)
blendmode = PF_Environment;
// I don't think that solid walls can use translucent linedef types...
if (gl_frontsector->numlights)
HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, noencore, &Surf, FOF_CUTLEVEL, NULL, blendmode);
else
{
if (grTex->mipmap.flags & TF_TRANSPARENT)
HWR_AddTransparentWall(wallVerts, &Surf, gl_midtexture, noencore, blendmode, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, blendmode, lightnum, colormap, gl_midtexture);
}
}
}
else if (mapnamespace == MNS_SRB2KART)
{
//Set textures properly on single sided walls that are sloped
wallVerts[3].y = FIXED_TO_FLOAT(worldtop);
wallVerts[0].y = FIXED_TO_FLOAT(worldbottom);
wallVerts[2].y = FIXED_TO_FLOAT(worldtopslope);
wallVerts[1].y = FIXED_TO_FLOAT(worldbottomslope);
// When there's no midtexture, draw a skywall to prevent rendering behind it
HWR_DrawSkyWall(wallVerts, &Surf);
}
if (!gl_curline->polyseg)
{
if (gl_frontsector->ceilingpic == skyflatnum) // It's a single-sided line with sky for its sector
{
wallVerts[2].y = wallVerts[3].y = FIXED_TO_FLOAT(INT32_MAX); // draw to top of map space
wallVerts[0].y = FIXED_TO_FLOAT(worldtop);
wallVerts[1].y = FIXED_TO_FLOAT(worldtopslope);
HWR_DrawSkyWall(wallVerts, &Surf);
}
if (gl_frontsector->floorpic == skyflatnum)
{
wallVerts[3].y = FIXED_TO_FLOAT(worldbottom);
wallVerts[2].y = FIXED_TO_FLOAT(worldbottomslope);
wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(INT32_MIN); // draw to bottom of map space
HWR_DrawSkyWall(wallVerts, &Surf);
}
}
}
//Hurdler: 3d-floors test
if (gl_backsector && !Tag_Compare(&gl_frontsector->tags, &gl_backsector->tags) && (gl_backsector->ffloors || gl_frontsector->ffloors))
{
ffloor_t * rover;
fixed_t highcut = 0, lowcut = 0;
fixed_t lowcutslope, highcutslope;
// Used for height comparisons and etc across FOFs and slopes
fixed_t high1, highslope1, low1, lowslope1;
INT32 texnum;
line_t * newline = NULL; // Multi-Property FOF
lowcut = max(worldbottom, worldlow);
highcut = min(worldtop, worldhigh);
lowcutslope = max(worldbottomslope, worldlowslope);
highcutslope = min(worldtopslope, worldhighslope);
if (gl_backsector->ffloors)
{
for (rover = gl_backsector->ffloors; rover; rover = rover->next)
{
boolean bothsides = false;
// Skip if it exists on both sectors.
ffloor_t * r2;
for (r2 = gl_frontsector->ffloors; r2; r2 = r2->next)
if (rover->master == r2->master)
{
bothsides = true;
break;
}
if (bothsides) continue;
if (!(rover->fofflags & FOF_EXISTS) || !(rover->fofflags & FOF_RENDERSIDES))
continue;
if (!(rover->fofflags & FOF_ALLSIDES) && rover->fofflags & FOF_INVERTSIDES)
continue;
SLOPEPARAMS(*rover->t_slope, high1, highslope1, *rover->topheight)
SLOPEPARAMS(*rover->b_slope, low1, lowslope1, *rover->bottomheight)
if ((high1 < lowcut || highslope1 < lowcutslope) || (low1 > highcut || lowslope1 > highcutslope))
continue;
texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = min(gl_curline->linedef-gl_backsector->lines[0], (long int)rover->master->frontsector->linecount);
newline = rover->master->frontsector->lines[0] + linenum;
texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
}
h = P_GetFFloorTopZAt (rover, v1x, v1y);
hS = P_GetFFloorTopZAt (rover, v2x, v2y);
l = P_GetFFloorBottomZAt(rover, v1x, v1y);
lS = P_GetFFloorBottomZAt(rover, v2x, v2y);
// Adjust the heights so the FOF does not overlap with top and bottom textures.
if (h >= highcut && hS >= highcutslope)
{
h = highcut;
hS = highcutslope;
}
if (l <= lowcut && lS <= lowcutslope)
{
l = lowcut;
lS = lowcutslope;
}
//Hurdler: HW code starts here
//FIXME: check if peging is correct
// set top/bottom coords
wallVerts[3].y = FIXED_TO_FLOAT(h);
wallVerts[2].y = FIXED_TO_FLOAT(hS);
wallVerts[0].y = FIXED_TO_FLOAT(l);
wallVerts[1].y = FIXED_TO_FLOAT(lS);
if (rover->fofflags & FOF_FOG)
{
wallVerts[3].t = wallVerts[2].t = 0;
wallVerts[0].t = wallVerts[1].t = 0;
wallVerts[0].s = wallVerts[3].s = 0;
wallVerts[2].s = wallVerts[1].s = 0;
}
else
{
fixed_t texturevpeg;
boolean attachtobottom = false;
boolean slopeskew = false; // skew FOF walls with slopes?
// Wow, how was this missing from OpenGL for so long?
// ...Oh well, anyway, Lower Unpegged now changes pegging of FOFs like in software
// -- Monster Iestyn 26/06/18
if (newline)
{
texturevpeg = sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid;
attachtobottom = !!(newline->flags & ML_DONTPEGBOTTOM);
slopeskew = !!(newline->flags & ML_SKEWTD);
}
else
{
texturevpeg = sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid;
attachtobottom = !!(gl_linedef->flags & ML_DONTPEGBOTTOM);
slopeskew = !!(rover->master->flags & ML_SKEWTD);
}
grTex = HWR_GetTexture(texnum, noencore);
if (!slopeskew) // no skewing
{
if (attachtobottom)
texturevpeg -= *rover->topheight - *rover->bottomheight;
wallVerts[3].t = (*rover->topheight - h + texturevpeg) * grTex->scaleY;
wallVerts[2].t = (*rover->topheight - hS + texturevpeg) * grTex->scaleY;
wallVerts[0].t = (*rover->topheight - l + texturevpeg) * grTex->scaleY;
wallVerts[1].t = (*rover->topheight - lS + texturevpeg) * grTex->scaleY;
}
else
{
if (!attachtobottom) // skew by top
{
wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
wallVerts[0].t = (h - l + texturevpeg) * grTex->scaleY;
wallVerts[1].t = (hS - lS + texturevpeg) * grTex->scaleY;
}
else // skew by bottom
{
wallVerts[0].t = wallVerts[1].t = texturevpeg * grTex->scaleY;
wallVerts[3].t = wallVerts[0].t - (h - l) * grTex->scaleY;
wallVerts[2].t = wallVerts[1].t - (hS - lS) * grTex->scaleY;
}
}
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
}
if (rover->fofflags & FOF_FOG)
{
FBITFIELD blendmode;
blendmode = PF_Fog|PF_NoTexture;
lightnum = HWR_CalcWallLight(rover->master->frontsector->lightlevel, gl_curline);
colormap = rover->master->frontsector->extra_colormap;
Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap);
if (gl_frontsector->numlights)
HWR_SplitWall(gl_frontsector, wallVerts, 0, false, &Surf, rover->fofflags, rover, blendmode);
else
HWR_AddTransparentWall(wallVerts, &Surf, 0, false ,blendmode, true, lightnum, colormap);
}
else
{
FBITFIELD blendmode = PF_Masked;
if (rover->fofflags & FOF_TRANSLUCENT)
{
if (rover->alpha < 256 || rover->blend)
{
blendmode = HWR_GetBlendModeFlag(rover->blend);
Surf.PolyColor.s.alpha = (UINT8)(max(0, min(rover->alpha, 255)));
}
}
if (gl_frontsector->numlights)
HWR_SplitWall(gl_frontsector, wallVerts, texnum, noencore, &Surf, rover->fofflags, rover, blendmode);
else
{
if (blendmode != PF_Masked)
HWR_AddTransparentWall(wallVerts, &Surf, texnum, noencore, blendmode, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap, texnum);
}
}
}
}
if (gl_frontsector->ffloors) // Putting this seperate should allow 2 FOF sectors to be connected without too many errors? I think?
{
for (rover = gl_frontsector->ffloors; rover; rover = rover->next)
{
boolean bothsides = false;
// Skip if it exists on both sectors.
ffloor_t * r2;
for (r2 = gl_backsector->ffloors; r2; r2 = r2->next)
if (rover->master == r2->master)
{
bothsides = true;
break;
}
if (bothsides) continue;
if (!(rover->fofflags & FOF_EXISTS) || !(rover->fofflags & FOF_RENDERSIDES))
continue;
if (!(rover->fofflags & FOF_ALLSIDES || rover->fofflags & FOF_INVERTSIDES))
continue;
SLOPEPARAMS(*rover->t_slope, high1, highslope1, *rover->topheight)
SLOPEPARAMS(*rover->b_slope, low1, lowslope1, *rover->bottomheight)
if ((high1 < lowcut || highslope1 < lowcutslope) || (low1 > highcut || lowslope1 > highcutslope))
continue;
texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = min(gl_curline->linedef-gl_backsector->lines[0], (long int)rover->master->frontsector->linecount);
newline = rover->master->frontsector->lines[0] + linenum;
texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
}
h = P_GetFFloorTopZAt (rover, v1x, v1y);
hS = P_GetFFloorTopZAt (rover, v2x, v2y);
l = P_GetFFloorBottomZAt(rover, v1x, v1y);
lS = P_GetFFloorBottomZAt(rover, v2x, v2y);
// Adjust the heights so the FOF does not overlap with top and bottom textures.
if (h >= highcut && hS >= highcutslope)
{
h = highcut;
hS = highcutslope;
}
if (l <= lowcut && lS <= lowcutslope)
{
l = lowcut;
lS = lowcutslope;
}
//Hurdler: HW code starts here
//FIXME: check if peging is correct
// set top/bottom coords
wallVerts[3].y = FIXED_TO_FLOAT(h);
wallVerts[2].y = FIXED_TO_FLOAT(hS);
wallVerts[0].y = FIXED_TO_FLOAT(l);
wallVerts[1].y = FIXED_TO_FLOAT(lS);
if (rover->fofflags & FOF_FOG)
{
wallVerts[3].t = wallVerts[2].t = 0;
wallVerts[0].t = wallVerts[1].t = 0;
wallVerts[0].s = wallVerts[3].s = 0;
wallVerts[2].s = wallVerts[1].s = 0;
}
else
{
grTex = HWR_GetTexture(texnum, noencore);
if (newline)
{
wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid) * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset) + sides[newline->sidenum[0]].offsety_mid) * grTex->scaleY;
}
else
{
wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid) * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid)) * grTex->scaleY;
}
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
}
if (rover->fofflags & FOF_FOG)
{
FBITFIELD blendmode;
blendmode = PF_Fog|PF_NoTexture;
lightnum = HWR_CalcWallLight(rover->master->frontsector->lightlevel, gl_curline);
colormap = rover->master->frontsector->extra_colormap;
Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap);
if (gl_backsector->numlights)
HWR_SplitWall(gl_backsector, wallVerts, 0, false, &Surf, rover->fofflags, rover, blendmode);
else
HWR_AddTransparentWall(wallVerts, &Surf, 0, false, blendmode, true, lightnum, colormap);
}
else
{
FBITFIELD blendmode = PF_Masked;
if (rover->fofflags & FOF_TRANSLUCENT)
{
if (rover->alpha < 256 || rover->blend)
{
blendmode = HWR_GetBlendModeFlag(rover->blend);
Surf.PolyColor.s.alpha = (UINT8)(max(0, min(rover->alpha, 255)));
}
}
if (gl_backsector->numlights)
HWR_SplitWall(gl_backsector, wallVerts, texnum, noencore, &Surf, rover->fofflags, rover, blendmode);
else
{
if (blendmode != PF_Masked)
HWR_AddTransparentWall(wallVerts, &Surf, texnum, noencore, blendmode, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap, texnum);
}
}
}
}
}
#undef SLOPEPARAMS
//Hurdler: end of 3d-floors test
}
// From PrBoom:
//
// e6y: Check whether the player can look beyond this line
//
boolean checkforemptylines = true;
// Don't modify anything here, just check
// Kalaron: Modified for sloped linedefs
static boolean CheckClip(seg_t * seg, sector_t * afrontsector, sector_t * abacksector)
{
fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends
fixed_t backf1, backf2, backc1, backc2; // back floor ceiling ends
boolean bothceilingssky = false, bothfloorssky = false;
if (mapnamespace != MNS_SRB2KART)
{
if (abacksector->ceilingpic == skyflatnum && afrontsector->ceilingpic == skyflatnum)
bothceilingssky = true;
if (abacksector->floorpic == skyflatnum && afrontsector->floorpic == skyflatnum)
bothfloorssky = true;
}
// GZDoom method of sloped line clipping
if (afrontsector->f_slope || afrontsector->c_slope || abacksector->f_slope || abacksector->c_slope)
{
fixed_t v1x, v1y, v2x, v2y; // the seg's vertexes as fixed_t
if (LIKELY(gl_curline->pv1))
{
v1x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->x);
v1y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->y);
}
else
{
v1x = gl_curline->v1->x;
v1y = gl_curline->v1->y;
}
if (LIKELY(gl_curline->pv2))
{
v2x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->x);
v2y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->y);
}
else
{
v2x = gl_curline->v2->x;
v2y = gl_curline->v2->y;
}
#define SLOPEPARAMS(slope, end1, end2, normalheight) \
end1 = P_GetZAt(slope, v1x, v1y, normalheight); \
end2 = P_GetZAt(slope, v2x, v2y, normalheight);
SLOPEPARAMS(afrontsector->f_slope, frontf1, frontf2, afrontsector-> floorheight)
SLOPEPARAMS(afrontsector->c_slope, frontc1, frontc2, afrontsector->ceilingheight)
SLOPEPARAMS( abacksector->f_slope, backf1, backf2, abacksector-> floorheight)
SLOPEPARAMS( abacksector->c_slope, backc1, backc2, abacksector->ceilingheight)
#undef SLOPEPARAMS
}
else
{
frontf1 = frontf2 = afrontsector-> floorheight;
frontc1 = frontc2 = afrontsector->ceilingheight;
backf1 = backf2 = abacksector-> floorheight;
backc1 = backc2 = abacksector->ceilingheight;
}
// properly render skies (consider door "open" if both ceilings are sky)
// same for floors
// lug: but only on kart maps :chaosleep:
// kart does some funky crap on its binary maps and thus using this code breaks many maps visually and generally makes performance really bad
if (mapnamespace != MNS_SRB2KART)
{
if (!bothceilingssky && !bothfloorssky)
{
// now check for closed sectors!
if ((backc1 <= frontf1 && backc2 <= frontf2)
|| (backf1 >= frontc1 && backf2 >= frontc2))
{
checkforemptylines = false;
return true;
}
if (backc1 <= backf1 && backc2 <= backf2)
{
// preserve a kind of transparent door/lift special effect:
if (((backc1 >= frontc1 && backc2 >= frontc2) || seg->sidedef->toptexture)
&& ((backf1 <= frontf1 && backf2 <= frontf2) || seg->sidedef->bottomtexture))
{
checkforemptylines = false;
return true;
}
}
}
if (!bothceilingssky)
{
if (backc1 != frontc1 || backc2 != frontc2)
{
checkforemptylines = false;
return false;
}
}
if (!bothfloorssky)
{
if (backf1 != frontf1 || backf2 != frontf2)
{
checkforemptylines = false;
return false;
}
}
}
else
{
// not in vanilla kart, but this makes noclipcam alot more bearable when its stuck in walls
// otherwise it may cull half your view which is not really desirable
if (afrontsector == viewsector || abacksector == viewsector)
{
fixed_t viewf1, viewf2, viewc1, viewc2;
if (afrontsector == viewsector)
{
viewf1 = frontf1;
viewf2 = frontf2;
viewc1 = frontc1;
viewc2 = frontc2;
}
else
{
viewf1 = backf1;
viewf2 = backf2;
viewc1 = backc1;
viewc2 = backc2;
}
// check if camera is outside the bounds of the floor and the ceiling (noclipping)
// either above the ceiling or below the floor
if ((viewz > viewc1 && viewz > viewc2) || (viewz < viewf1 && viewz < viewf2))
return false;
}
// now check for closed sectors!
// here we're talking about a CEILING lower than a floor. ...yeah we don't even need to bother.
if (backc1 <= frontf1 && backc2 <= frontf2)
{
checkforemptylines = false;
return true;
}
// here we're talking about floors higher than ceilings, don't even bother either.
if (backf1 >= frontc1 && backf2 >= frontc2)
{
checkforemptylines = false;
return true;
}
// Lat: Ok, here's what we need to do, we want to draw thok barriers. Let's define what a thok barrier is;
// -Must have ceilheight <= floorheight
// -ceilpic must be skyflatnum
// -an adjacant sector needs to have a ceilingheight or a floor height different than the one we have, otherwise, it's just a huge ass wall, we shouldn't render past it.
// -said adjacant sector cannot also be a thok barrier, because that's also dumb and we could render far more than we need to as a result :V
if (backc1 <= backf1 && backc2 <= backf2)
{
checkforemptylines = false;
// before we do anything, if both sectors are thok barriers, GET ME OUT OF HERE!
if (frontc1 <= backc1 && frontc2 <= backc2)
return true; // STOP RENDERING.
// draw floors at the top of thok barriers:
if (backc1 < frontc1 || backc2 < frontc2)
return false;
if (backf1 > frontf1 || backf2 > frontf2)
return false;
return true;
}
// Window.
// We know it's a window when the above isn't true and the back and front sectors don't match
if (backc1 != frontc1 || backc2 != frontc2 || backf1 != frontf1 || backf2 != frontf2)
{
checkforemptylines = false;
return false;
}
}
return false;
}
// -----------------+
// HWR_AddLine : Clips the given segment and adds any visible pieces to the line list.
// Notes : gl_cursectorlight is set to the current subsector -> sector -> light value
// : (it may be mixed with the wall's own flat colour in the future ...)
// -----------------+
static void HWR_AddLine(seg_t * line)
{
angle_t angle1, angle2;
// SoM: Backsector needs to be run through R_FakeFlat
static sector_t tempsec;
fixed_t v1x, v1y, v2x, v2y; // the seg's vertexes as fixed_t
if (line->polyseg && !(line->polyseg->flags & POF_RENDERSIDES))
return;
gl_curline = line;
if (LIKELY(gl_curline->pv1))
{
v1x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->x);
v1y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->y);
}
else
{
v1x = gl_curline->v1->x;
v1y = gl_curline->v1->y;
}
if (LIKELY(gl_curline->pv2))
{
v2x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->x);
v2y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->y);
}
else
{
v2x = gl_curline->v2->x;
v2y = gl_curline->v2->y;
}
// OPTIMIZE: quickly reject orthogonal back sides.
angle1 = R_PointToAngle64(v1x, v1y);
angle2 = R_PointToAngle64(v2x, v2y);
// PrBoom: Back side, i.e. backface culling - read: endAngle >= startAngle!
if (angle2 - angle1 < ANGLE_180)
return;
// PrBoom: use REAL clipping math YAYYYYYYY!!!
if (!gld_clipper_SafeCheckRange(angle2, angle1))
{
return;
}
checkforemptylines = true;
gl_backsector = line->backsector;
if (!line->backsector)
{
gld_clipper_SafeAddClipRange(angle2, angle1);
}
else
{
boolean bothceilingssky = false, bothfloorssky = false;
gl_backsector = R_FakeFlat(gl_backsector, &tempsec, NULL, NULL, true);
if (mapnamespace != MNS_SRB2KART) // same here, thisll most likely break kart maps
{
if (gl_backsector->ceilingpic == skyflatnum && gl_frontsector->ceilingpic == skyflatnum)
bothceilingssky = true;
if (gl_backsector->floorpic == skyflatnum && gl_frontsector->floorpic == skyflatnum)
bothfloorssky = true;
if (bothceilingssky && bothfloorssky) // everything's sky? let's save us a bit of time then
{
if (!line->polyseg &&
!line->sidedef->midtexture
&& ((!gl_frontsector->ffloors && !gl_backsector->ffloors)
|| Tag_Compare(&gl_frontsector->tags, &gl_backsector->tags)))
return; // line is empty, don't even bother
// treat like wide open window instead
HWR_ProcessSeg(); // Doesn't need arguments because they're defined globally :D
return;
}
}
if (CheckClip(line, gl_frontsector, gl_backsector))
{
gld_clipper_SafeAddClipRange(angle2, angle1);
checkforemptylines = false;
}
// Reject empty lines used for triggers and special events.
// Identical floor and ceiling on both sides,
// identical light levels on both sides,
// and no middle texture.
if (checkforemptylines && R_IsEmptyLine(line, gl_frontsector, gl_backsector))
return;
}
HWR_ProcessSeg(); // Doesn't need arguments because they're defined globally :D
return;
}
// HWR_CheckBBox
// Checks BSP node/subtree bounding box.
// Returns true
// if some part of the bbox might be visible.
//
// modified to use local variables
static boolean HWR_CheckBBox(const fixed_t *bspcoord)
{
INT32 boxpos;
fixed_t px1, py1, px2, py2;
angle_t angle1, angle2;
// Find the corners of the box
// that define the edges from current viewpoint.
if (dup_viewx <= bspcoord[BOXLEFT])
boxpos = 0;
else if (dup_viewx < bspcoord[BOXRIGHT])
boxpos = 1;
else
boxpos = 2;
if (dup_viewy >= bspcoord[BOXTOP])
boxpos |= 0;
else if (dup_viewy > bspcoord[BOXBOTTOM])
boxpos |= 1<<2;
else
boxpos |= 2<<2;
if (boxpos == 5)
return true;
px1 = bspcoord[checkcoord[boxpos][0]];
py1 = bspcoord[checkcoord[boxpos][1]];
px2 = bspcoord[checkcoord[boxpos][2]];
py2 = bspcoord[checkcoord[boxpos][3]];
angle1 = R_PointToAngle64(px1, py1);
angle2 = R_PointToAngle64(px2, py2);
return gld_clipper_SafeCheckRange(angle2, angle1);
}
//
// HWR_AddPolyObjectSegs
//
// haleyjd 02/19/06
// Adds all segs in all polyobjects in the given subsector.
// Modified for hardware rendering.
//
static inline void HWR_AddPolyObjectSegs(void)
{
size_t i, j;
seg_t gl_fakeline;
polyvertex_t pv1;
polyvertex_t pv2;
// Sort through all the polyobjects
for (i = 0; i < numpolys; ++i)
{
// Render the polyobject's lines
for (j = 0; j < po_ptrs[i]->segCount; ++j)
{
// Copy the info of a polyobject's seg, then convert it to OpenGL floating point
memcpy(&gl_fakeline, po_ptrs[i]->segs[j], sizeof(seg_t));
// Now convert the line to float and add it to be rendered
pv1.x = FIXED_TO_FLOAT(gl_fakeline.v1->x);
pv1.y = FIXED_TO_FLOAT(gl_fakeline.v1->y);
pv2.x = FIXED_TO_FLOAT(gl_fakeline.v2->x);
pv2.y = FIXED_TO_FLOAT(gl_fakeline.v2->y);
gl_fakeline.pv1 = &pv1;
gl_fakeline.pv2 = &pv2;
HWR_AddLine(&gl_fakeline);
}
}
}
static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fixed_t fixedheight,
FBITFIELD blendmode, UINT8 lightlevel, levelflat_t *levelflat, sector_t *FOFsector,
UINT8 alpha, extracolormap_t *planecolormap)
{
FSurfaceInfo Surf;
FOutVector *v3d;
INT32 shader = SHADER_NONE;
size_t nrPlaneVerts = polysector->numVertices;
INT32 i;
float height = FIXED_TO_FLOAT(fixedheight); // constant y for all points on the convex flat polygon
float flatxref, flatyref;
float fflatwidth = 64.0f, fflatheight = 64.0f;
float scrollx = 0.0f, scrolly = 0.0f;
angle_t angle = 0;
fixed_t tempxs, tempyt;
static FOutVector *planeVerts = NULL;
static UINT16 numAllocedPlaneVerts = 0;
if (nrPlaneVerts < 3) // Not even a triangle?
return;
else if (nrPlaneVerts > (size_t)UINT16_MAX) // FIXME: exceeds plVerts size
{
CONS_Debug(DBG_RENDER, "polygon size of %s exceeds max value of %d vertices\n", sizeu1(nrPlaneVerts), UINT16_MAX);
return;
}
// Allocate plane-vertex buffer if we need to
if (!planeVerts || nrPlaneVerts > numAllocedPlaneVerts)
{
numAllocedPlaneVerts = (UINT16)nrPlaneVerts;
Z_Free(planeVerts);
Z_Malloc(numAllocedPlaneVerts * sizeof (FOutVector), PU_LEVEL, &planeVerts);
}
// set texture for polygon
if (levelflat != NULL)
{
texture_t *texture = textures[R_GetTextureNumForFlat(levelflat)];
fflatwidth = texture->width;
fflatheight = texture->height;
}
else // set no texture
HWR_SetCurrentTexture(NULL);
// reference point for flat texture coord for each vertex around the polygon
flatxref = 0.0;
flatyref = 0.0;
// transform
v3d = planeVerts;
if (FOFsector != NULL)
{
if (!isceiling) // it's a floor
{
scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth;
scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight;
angle = FOFsector->floorpic_angle;
}
else // it's a ceiling
{
scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth;
scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight;
angle = FOFsector->ceilingpic_angle;
}
}
else if (gl_frontsector)
{
if (!isceiling) // it's a floor
{
scrollx = FIXED_TO_FLOAT(gl_frontsector->floor_xoffs)/fflatwidth;
scrolly = FIXED_TO_FLOAT(gl_frontsector->floor_yoffs)/fflatheight;
angle = gl_frontsector->floorpic_angle;
}
else // it's a ceiling
{
scrollx = FIXED_TO_FLOAT(gl_frontsector->ceiling_xoffs)/fflatwidth;
scrolly = FIXED_TO_FLOAT(gl_frontsector->ceiling_yoffs)/fflatheight;
angle = gl_frontsector->ceilingpic_angle;
}
}
for (i = 0; i < (INT32)nrPlaneVerts; i++,v3d++)
{
// Go from the polysector's original vertex locations
// Means the flat is offset based on the original vertex locations
v3d->s = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) - flatxref + scrollx);
v3d->t = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly);
// Need to rotate before translate
if (angle) // Only needs to be done if there's an altered angle
{
tempxs = FLOAT_TO_FIXED(v3d->s);
tempyt = FLOAT_TO_FIXED(v3d->t);
angle = ANG2RAD(InvAngle(angle));
v3d->s = (FIXED_TO_FLOAT(FixedMul(tempxs, FINECOSINE(angle)) - FixedMul(tempyt, FINESINE(angle))));
v3d->t = (FIXED_TO_FLOAT(FixedMul(tempxs, FINESINE(angle)) + FixedMul(tempyt, FINECOSINE(angle))));
}
v3d->x = FIXED_TO_FLOAT(polysector->vertices[i]->x);
v3d->y = height;
v3d->z = FIXED_TO_FLOAT(polysector->vertices[i]->y);
}
HWR_Lighting(&Surf, lightlevel, planecolormap, P_SectorUsesDirectionalLighting((FOFsector != NULL) ? FOFsector : gl_frontsector));
if (blendmode & PF_Translucent)
{
Surf.PolyColor.s.alpha = (UINT8)alpha;
blendmode |= PF_Modulated|PF_Occlude;
}
else
blendmode |= PF_Masked|PF_Modulated;
if (HWR_UseShader())
{
shader = SHADER_FLOOR;
blendmode |= PF_ColorMapped;
}
HWR_ProcessPolygon(&Surf, planeVerts, nrPlaneVerts, blendmode, shader, false);
}
static void HWR_AddPolyObjectPlanes(void)
{
size_t i;
sector_t *polyobjsector;
INT32 light = 0;
// Polyobject Planes need their own function for drawing because they don't have extrasubsectors by themselves
// It should be okay because polyobjects should always be convex anyway
for (i = 0; i < numpolys; i++)
{
polyobjsector = po_ptrs[i]->lines[0]->backsector; // the in-level polyobject sector
if (!(po_ptrs[i]->flags & POF_RENDERPLANES)) // Only render planes when you should
continue;
if (po_ptrs[i]->translucency >= NUMTRANSMAPS)
continue;
if (polyobjsector->floorheight <= gl_frontsector->ceilingheight
&& polyobjsector->floorheight >= gl_frontsector->floorheight
&& (viewz < polyobjsector->floorheight))
{
light = R_GetPlaneLight(gl_frontsector, polyobjsector->floorheight, true);
if (po_ptrs[i]->translucency > 0)
{
FSurfaceInfo Surf;
FBITFIELD blendmode;
memset(&Surf, 0x00, sizeof(Surf));
blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf);
HWR_AddTransparentPolyobjectFloor(&levelflats[polyobjsector->floorpic], po_ptrs[i], false, polyobjsector->floorheight,
(light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), Surf.PolyColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gl_frontsector->extra_colormap : *gl_frontsector->lightlist[light].extra_colormap));
}
else
{
HWR_GetLevelFlat(&levelflats[polyobjsector->floorpic], R_NoEncore(polyobjsector, &levelflats[polyobjsector->floorpic], false));
HWR_RenderPolyObjectPlane(po_ptrs[i], false, polyobjsector->floorheight, PF_Occlude,
(light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), &levelflats[polyobjsector->floorpic],
polyobjsector, 255, (light == -1 ? gl_frontsector->extra_colormap : *gl_frontsector->lightlist[light].extra_colormap));
}
}
if (polyobjsector->ceilingheight >= gl_frontsector->floorheight
&& polyobjsector->ceilingheight <= gl_frontsector->ceilingheight
&& (viewz > polyobjsector->ceilingheight))
{
light = R_GetPlaneLight(gl_frontsector, polyobjsector->ceilingheight, true);
if (po_ptrs[i]->translucency > 0)
{
FSurfaceInfo Surf;
FBITFIELD blendmode;
memset(&Surf, 0x00, sizeof(Surf));
blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf);
HWR_AddTransparentPolyobjectFloor(&levelflats[polyobjsector->ceilingpic], po_ptrs[i], true, polyobjsector->ceilingheight,
(light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), Surf.PolyColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gl_frontsector->extra_colormap : *gl_frontsector->lightlist[light].extra_colormap));
}
else
{
HWR_GetLevelFlat(&levelflats[polyobjsector->ceilingpic], R_NoEncore(polyobjsector, &levelflats[polyobjsector->ceilingpic], true));
HWR_RenderPolyObjectPlane(po_ptrs[i], true, polyobjsector->ceilingheight, PF_Occlude,
(light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), &levelflats[polyobjsector->ceilingpic],
polyobjsector, 255, (light == -1 ? gl_frontsector->extra_colormap : *gl_frontsector->lightlist[light].extra_colormap));
}
}
}
}
static FBITFIELD HWR_RippleBlend(sector_t *sector, ffloor_t *rover, boolean ceiling)
{
return R_IsRipplePlane(sector, rover, ceiling) ? PF_Ripple : 0;
}
// -----------------+
// HWR_Subsector : Determine floor/ceiling planes.
// : Add sprites of things in sector.
// : Draw one or more line segments.
// Notes : Sets gl_cursectorlight to the light of the parent sector, to modulate wall textures
// -----------------+
static void HWR_Subsector(size_t num)
{
INT16 count;
seg_t *line;
subsector_t *sub;
static sector_t tempsec; //SoM: 4/7/2000
INT32 floorlightlevel;
INT32 ceilinglightlevel;
INT32 locFloorHeight, locCeilingHeight;
INT32 cullFloorHeight, cullCeilingHeight;
INT32 light = 0;
extracolormap_t *floorcolormap;
extracolormap_t *ceilingcolormap;
ffloor_t *rover;
#ifdef PARANOIA //no risk while developing, enough debugging nights!
if (num >= addsubsector)
I_Error("HWR_Subsector: ss %s with numss = %s, addss = %s\n",
sizeu1(num), sizeu2(numsubsectors), sizeu3(addsubsector));
/*if (num >= numsubsectors)
I_Error("HWR_Subsector: ss %i with numss = %i",
num,
numsubsectors);*/
#endif
if (num < numsubsectors)
{
// subsector
sub = &subsectors[num];
// sector
gl_frontsector = sub->sector;
// how many linedefs
count = sub->numlines;
// first line seg
line = &segs[sub->firstline];
}
else
{
// there are no segs but only planes
sub = &subsectors[0];
gl_frontsector = sub->sector;
count = 0;
line = NULL;
}
//SoM: 4/7/2000: Test to make Boom water work in Hardware mode.
gl_frontsector = R_FakeFlat(gl_frontsector, &tempsec, &floorlightlevel,
&ceilinglightlevel, false);
//FIXME: Use floorlightlevel and ceilinglightlevel insted of lightlevel.
floorcolormap = ceilingcolormap = gl_frontsector->extra_colormap;
// ------------------------------------------------------------------------
// sector lighting, DISABLED because it's done in HWR_StoreWallRange
// ------------------------------------------------------------------------
/// \todo store a RGBA instead of just intensity, allow coloured sector lighting
//light = (FUBYTE)(sub->sector->lightlevel & 0xFF) / 255.0f;
//gl_cursectorlight.red = light;
//gl_cursectorlight.green = light;
//gl_cursectorlight.blue = light;
//gl_cursectorlight.alpha = light;
// ----- end special tricks -----
cullFloorHeight = P_GetSectorFloorZAt (gl_frontsector, viewx, viewy);
cullCeilingHeight = P_GetSectorCeilingZAt(gl_frontsector, viewx, viewy);
locFloorHeight = P_GetSectorFloorZAt (gl_frontsector, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y);
locCeilingHeight = P_GetSectorCeilingZAt(gl_frontsector, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y);
if (gl_frontsector->ffloors)
{
boolean anyMoved = gl_frontsector->moved;
if (anyMoved == false)
{
for (rover = gl_frontsector->ffloors; rover; rover = rover->next)
{
sector_t *controlSec = &sectors[rover->secnum];
if (controlSec->moved == true)
{
anyMoved = true;
break;
}
}
}
if (anyMoved == true)
{
gl_frontsector->numlights = sub->sector->numlights = 0;
R_Prep3DFloors(gl_frontsector, locCeilingHeight);
sub->sector->lightlist = gl_frontsector->lightlist;
sub->sector->numlights = gl_frontsector->numlights;
sub->sector->moved = gl_frontsector->moved = false;
}
light = R_GetPlaneLight(gl_frontsector, locFloorHeight, false);
if (gl_frontsector->floorlightsec == -1 && !gl_frontsector->floorlightabsolute)
floorlightlevel = max(0, min(255, *gl_frontsector->lightlist[light].lightlevel + gl_frontsector->floorlightlevel));
floorcolormap = *gl_frontsector->lightlist[light].extra_colormap;
light = R_GetPlaneLight(gl_frontsector, locCeilingHeight, false);
if (gl_frontsector->ceilinglightsec == -1 && !gl_frontsector->ceilinglightabsolute)
ceilinglightlevel = max(0, min(255, *gl_frontsector->lightlist[light].lightlevel + gl_frontsector->ceilinglightlevel));
ceilingcolormap = *gl_frontsector->lightlist[light].extra_colormap;
}
sub->sector->extra_colormap = gl_frontsector->extra_colormap;
// render floor ?
#ifdef DOPLANES
// yeah, easy backface cull! :)
if (cullFloorHeight < dup_viewz)
{
if (gl_frontsector->floorpic != skyflatnum)
{
if (sub->validcount != validcount)
{
HWR_GetLevelFlat(&levelflats[gl_frontsector->floorpic], R_NoEncore(gl_frontsector, &levelflats[gl_frontsector->floorpic], false));
HWR_RenderPlane(sub, &extrasubsectors[num], false,
// Hack to make things continue to work around slopes.
locFloorHeight == cullFloorHeight ? locFloorHeight : gl_frontsector->floorheight,
// We now return you to your regularly scheduled rendering.
PF_Occlude | HWR_RippleBlend(gl_frontsector, NULL, false),
floorlightlevel, &levelflats[gl_frontsector->floorpic], NULL, 255, floorcolormap);
}
}
}
if (cullCeilingHeight > dup_viewz)
{
if (gl_frontsector->ceilingpic != skyflatnum)
{
if (sub->validcount != validcount)
{
HWR_GetLevelFlat(&levelflats[gl_frontsector->ceilingpic], R_NoEncore(gl_frontsector, &levelflats[gl_frontsector->ceilingpic], true));
HWR_RenderPlane(sub, &extrasubsectors[num], true,
// Hack to make things continue to work around slopes.
locCeilingHeight == cullCeilingHeight ? locCeilingHeight : gl_frontsector->ceilingheight,
// We now return you to your regularly scheduled rendering.
PF_Occlude | HWR_RippleBlend(gl_frontsector, NULL, true),
ceilinglightlevel, &levelflats[gl_frontsector->ceilingpic], NULL, 255, ceilingcolormap);
}
}
}
#ifdef R_FAKEFLOORS
if (gl_frontsector->ffloors)
{
/// \todo fix light, xoffs, yoffs, extracolormap ?
for (rover = gl_frontsector->ffloors;
rover; rover = rover->next)
{
fixed_t cullHeight, centerHeight;
// bottom plane
cullHeight = P_GetFFloorBottomZAt(rover, viewx, viewy);
centerHeight = P_GetFFloorBottomZAt(rover, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y);
if (!(rover->fofflags & FOF_EXISTS) || !(rover->fofflags & FOF_RENDERPLANES))
continue;
if (sub->validcount == validcount)
continue;
if (centerHeight <= locCeilingHeight &&
centerHeight >= locFloorHeight &&
((dup_viewz < cullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) ||
(dup_viewz > cullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES))))
{
if (rover->fofflags & FOF_FOG)
{
UINT8 alpha;
light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap);
HWR_AddTransparentFloor(0,
&extrasubsectors[num],
false,
*rover->bottomheight,
*gl_frontsector->lightlist[light].lightlevel,
alpha, rover->master->frontsector, PF_Fog|PF_NoTexture,
true, rover->master->frontsector->extra_colormap);
}
else if (rover->fofflags & FOF_TRANSLUCENT
&& (rover->alpha < 256 || rover->blend)) // SoM: Flags are more efficient
{
FBITFIELD blendmode = HWR_GetBlendModeFlag(rover->blend) | HWR_RippleBlend(gl_frontsector, rover, false);
light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
HWR_AddTransparentFloor(&levelflats[*rover->bottompic],
&extrasubsectors[num],
false,
*rover->bottomheight,
*gl_frontsector->lightlist[light].lightlevel,
max(0, min(rover->alpha, 255)), rover->master->frontsector, blendmode,
false, *gl_frontsector->lightlist[light].extra_colormap);
}
else
{
HWR_GetLevelFlat(&levelflats[*rover->bottompic], R_NoEncore(gl_frontsector, &levelflats[*rover->bottompic], false));
light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
HWR_RenderPlane(sub, &extrasubsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->bottompic],
rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap);
}
}
// top plane
cullHeight = P_GetFFloorTopZAt(rover, viewx, viewy);
centerHeight = P_GetFFloorTopZAt(rover, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y);
if (centerHeight >= locFloorHeight &&
centerHeight <= locCeilingHeight &&
((dup_viewz > cullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) ||
(dup_viewz < cullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES))))
{
if (rover->fofflags & FOF_FOG)
{
UINT8 alpha;
light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap);
HWR_AddTransparentFloor(0,
&extrasubsectors[num],
true,
*rover->topheight,
*gl_frontsector->lightlist[light].lightlevel,
alpha, rover->master->frontsector, PF_Fog|PF_NoTexture,
true, rover->master->frontsector->extra_colormap);
}
else if (rover->fofflags & FOF_TRANSLUCENT
&& (rover->alpha < 256 || rover->blend)) // SoM: Flags are more efficient
{
FBITFIELD blendmode = HWR_GetBlendModeFlag(rover->blend) | HWR_RippleBlend(gl_frontsector, rover, false);
light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
HWR_AddTransparentFloor(&levelflats[*rover->toppic],
&extrasubsectors[num],
true,
*rover->topheight,
*gl_frontsector->lightlist[light].lightlevel,
max(0, min(rover->alpha, 255)), rover->master->frontsector, blendmode,
false, *gl_frontsector->lightlist[light].extra_colormap);
}
else
{
HWR_GetLevelFlat(&levelflats[*rover->toppic], R_NoEncore(gl_frontsector, &levelflats[*rover->toppic], true));
light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
HWR_RenderPlane(sub, &extrasubsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, true) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->toppic],
rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap);
}
}
}
}
#endif
#endif //doplanes
// Draw all the polyobjects in this subsector
if (sub->polyList)
{
polyobj_t *po = sub->polyList;
numpolys = 0;
// Count all the polyobjects, reset the list, and recount them
while (po)
{
++numpolys;
po = (polyobj_t *)(po->link.next);
}
// for render stats
ps_numpolyobjects += numpolys;
// Sort polyobjects
R_SortPolyObjects(sub);
// Draw polyobject lines.
HWR_AddPolyObjectSegs();
if (sub->validcount != validcount) // This validcount situation seems to let us know that the floors have already been drawn.
{
// Draw polyobject planes
HWR_AddPolyObjectPlanes();
}
}
// Hurder ici se passe les choses INT32<33>essantes!
// on vient de tracer le sol et le plafond
// on trace <20>pr<70>ent d'abord les sprites et ensuite les murs
// hurdler: faux: on ajoute seulement les sprites, le murs sont trac<61> d'abord
if (line)
{
if (sub->sector->validcount != validcount)
{
// draw sprites first, coz they are clipped to the solidsegs of
// subsectors more 'in front'
HWR_AddSprites(gl_frontsector);
}
//Hurdler: at this point validcount must be the same, but is not because
// gl_frontsector doesn't point anymore to sub->sector due to
// the call gl_frontsector = R_FakeFlat(...)
// if it's not done, the sprite is drawn more than once,
// what looks really bad with translucency or dynamic light,
// without talking about the overdraw of course.
sub->sector->validcount = validcount;/// \todo fix that in a better way
while (count--)
{
if (LIKELY(!line->glseg && !line->polyseg)) // ignore segs that belong to polyobjects
HWR_AddLine(line);
line++;
}
}
sub->validcount = validcount;
}
//
// Renders all subsectors below a given node,
// traversing subtree recursively.
// Just call with BSP root.
static void HWR_RenderBSPNode(INT32 bspnum)
{
const node_t *bsp;
INT32 side;
ps_numbspcalls++;
while (!(bspnum & NF_SUBSECTOR)) // Found a subsector?
{
bsp = &nodes[bspnum];
// Decide which side the view point is on.
side = R_PointOnSideFast(viewx, viewy, bsp);
// Recursively divide front space.
HWR_RenderBSPNode(bsp->children[side]);
// Possibly divide back space
if (!(HWR_CheckBBox(bsp->bbox[side^1])))
return;
bspnum = bsp->children[side^1];
}
// e6y: support for extended nodes
HWR_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);
}
// ==========================================================================
// FROM R_MAIN.C
// ==========================================================================
//BP : exactely the same as R_InitTextureMapping
void HWR_InitTextureMapping(void)
{
angle_t i;
INT32 x;
INT32 t;
fixed_t focallength;
fixed_t grcenterx;
fixed_t grcenterxfrac;
INT32 grviewwidth;
#define clipanglefov (FIELDOFVIEW>>ANGLETOFINESHIFT)
grviewwidth = vid.width;
grcenterx = grviewwidth/2;
grcenterxfrac = grcenterx<<FRACBITS;
// Use tangent table to generate viewangletox:
// viewangletox will give the next greatest x
// after the view angle.
//
// Calc focallength
// so FIELDOFVIEW angles covers SCREENWIDTH.
focallength = FixedDiv(grcenterxfrac,
FINETANGENT(FINEANGLES/4+clipanglefov/2));
for (i = 0; i < FINEANGLES/2; i++)
{
if (FINETANGENT(i) > FRACUNIT*2)
t = -1;
else if (FINETANGENT(i) < -FRACUNIT*2)
t = grviewwidth+1;
else
{
t = FixedMul(FINETANGENT(i), focallength);
t = (grcenterxfrac - t+FRACUNIT-1)>>FRACBITS;
if (t < -1)
t = -1;
else if (t > grviewwidth+1)
t = grviewwidth+1;
}
gl_viewangletox[i] = t;
}
// Scan viewangletox[] to generate xtoviewangle[]:
// xtoviewangle will give the smallest view angle
// that maps to x.
for (x = 0; x <= grviewwidth; x++)
{
i = 0;
while (gl_viewangletox[i]>x)
i++;
gl_xtoviewangle[x] = (i<<ANGLETOFINESHIFT) - ANGLE_90;
}
// Take out the fencepost cases from viewangletox.
for (i = 0; i < FINEANGLES/2; i++)
{
if (gl_viewangletox[i] == -1)
gl_viewangletox[i] = 0;
else if (gl_viewangletox[i] == grviewwidth+1)
gl_viewangletox[i] = grviewwidth;
}
gl_clipangle = gl_xtoviewangle[0];
}
// ==========================================================================
// gl_things.c
// ==========================================================================
// sprites are drawn after all wall and planes are rendered, so that
// sprite translucency effects apply on the rendered view (instead of the background sky!!)
static UINT32 gl_visspritecount;
static gl_vissprite_t *gl_visspritechunks[MAXVISSPRITES >> VISSPRITECHUNKBITS] = {NULL};
// --------------------------------------------------------------------------
// HWR_ClearSprites
// Called at frame start.
// --------------------------------------------------------------------------
static void HWR_ClearSprites(void)
{
gl_visspritecount = 0;
}
// --------------------------------------------------------------------------
// HWR_NewVisSprite
// --------------------------------------------------------------------------
static gl_vissprite_t gl_overflowsprite;
static gl_vissprite_t *HWR_GetVisSprite(UINT32 num)
{
UINT32 chunk = num >> VISSPRITECHUNKBITS;
// Allocate chunk if necessary
if (!gl_visspritechunks[chunk])
Z_Malloc(sizeof(gl_vissprite_t) * VISSPRITESPERCHUNK, PU_LEVEL, &gl_visspritechunks[chunk]);
return gl_visspritechunks[chunk] + (num & VISSPRITEINDEXMASK);
}
static gl_vissprite_t *HWR_NewVisSprite(void)
{
if (gl_visspritecount == MAXVISSPRITES)
return &gl_overflowsprite;
return HWR_GetVisSprite(gl_visspritecount++);
}
// A hack solution for transparent surfaces appearing on top of linkdraw sprites.
// Keep a list of linkdraw sprites and draw their shapes to the z-buffer after all other
// sprite drawing is done. (effectively the z-buffer drawing of linkdraw sprites is delayed)
// NOTE: This will no longer be necessary once full translucent sorting is implemented, where
// translucent sprites and surfaces are sorted together.
typedef struct
{
FOutVector verts[4];
gl_vissprite_t *spr;
} zbuffersprite_t;
// this list is used to store data about linkdraw sprites
zbuffersprite_t linkdrawlist[MAXVISSPRITES];
UINT32 linkdrawcount = 0;
// add the necessary data to the list for delayed z-buffer drawing
static void HWR_LinkDrawHackAdd(FOutVector *verts, gl_vissprite_t *spr)
{
if (linkdrawcount < MAXVISSPRITES)
{
memcpy(linkdrawlist[linkdrawcount].verts, verts, sizeof(FOutVector) * 4);
linkdrawlist[linkdrawcount].spr = spr;
linkdrawcount++;
}
}
// process and clear the list of sprites for delayed z-buffer drawing
static void HWR_LinkDrawHackFinish(void)
{
UINT32 i;
FSurfaceInfo surf;
surf.PolyColor.rgba = 0xFFFFFFFF;
surf.TintColor.rgba = 0xFFFFFFFF;
surf.FadeColor.rgba = 0xFFFFFFFF;
surf.LightInfo.light_level = 0;
surf.LightInfo.fade_start = 0;
surf.LightInfo.fade_end = 31;
surf.LightInfo.newfade = false;
for (i = 0; i < linkdrawcount; i++)
{
// draw sprite shape, only to z-buffer
HWR_GetPatch(linkdrawlist[i].spr->gpatch);
HWR_ProcessPolygon(&surf, linkdrawlist[i].verts, 4, PF_Translucent|PF_Occlude|PF_Invisible, 0, false);
}
// reset list
linkdrawcount = 0;
}
//
// HWR_DoCulling
// Hardware version of R_DoCulling
// (see r_main.c)
static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float vz, float bottomh, float toph)
{
float cullplane;
if (!cullheight)
return false;
cullplane = FIXED_TO_FLOAT(cullheight->frontsector->floorheight);
if (cullheight->args[1]) // Group culling
{
if (!viewcullheight)
return false;
// Make sure this is part of the same group
if (viewcullheight->frontsector == cullheight->frontsector)
{
// OK, we can cull
if (vz > cullplane && toph < cullplane) // Cull if below plane
return true;
if (bottomh > cullplane && vz <= cullplane) // Cull if above plane
return true;
}
}
else // Quick culling
{
if (vz > cullplane && toph < cullplane) // Cull if below plane
return true;
if (bottomh > cullplane && vz <= cullplane) // Cull if above plane
return true;
}
return false;
}
static patch_t *shadowpatch = NULL;
static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
{
patch_t *gpatch = NULL;
FOutVector shadowVerts[4];
FSurfaceInfo sSurf;
float fscale; float fx; float fy; float offset;
float ph;
extracolormap_t *colormap = NULL;
FBITFIELD blendmode = PF_ReverseSubtract;
INT32 shader = SHADER_NONE;
UINT8 i;
SINT8 flip = P_MobjFlip(thing);
INT32 light;
fixed_t scalemul;
fixed_t groundz;
fixed_t slopez;
pslope_t *groundslope;
// uncapped/interpolation
interpmobjstate_t interp = {0};
R_InterpolateMobjState(thing, R_GetTimeFrac(RTF_LEVEL), &interp);
// sprite offset
interp.x += thing->sprxoff;
interp.y += thing->spryoff;
interp.z += thing->sprzoff;
groundz = R_GetShadowZ(thing, &groundslope);
if (!shadowpatch || !shadowpatch->hardware)
{
CONS_Debug(DBG_RENDER, "remaking shadowsprite\n");
shadowpatch = (patch_t *)W_CachePatchName("DSHADOW", PU_SPRITE);
}
gpatch = shadowpatch;
if (!gpatch || !gpatch->hardware || !((GLPatch_t *)gpatch->hardware)->mipmap->format)
return;
HWR_GetPatch(gpatch);
scalemul = FixedMul(scale, (thing->radius * 2) / gpatch->height);
ph = (float)gpatch->height;
fscale = FIXED_TO_FLOAT(scalemul);
fx = FIXED_TO_FLOAT(interp.x);
fy = FIXED_TO_FLOAT(interp.y);
if (fscale > 0.0)
{
offset = (ph / 2) * fscale;
}
else
{
return;
}
// 3--2
// | /|
// |/ |
// 0--1
shadowVerts[2].x = shadowVerts[3].x = fx + offset;
shadowVerts[1].x = shadowVerts[0].x = fx - offset;
shadowVerts[1].z = shadowVerts[2].z = fy - offset;
shadowVerts[0].z = shadowVerts[3].z = fy + offset;
for (i = 0; i < 4; i++)
{
float oldx = shadowVerts[i].x;
float oldy = shadowVerts[i].z;
shadowVerts[i].x = fx + ((oldx - fx) * gl_viewcos) - ((oldy - fy) * gl_viewsin);
shadowVerts[i].z = fy + ((oldx - fx) * gl_viewsin) + ((oldy - fy) * gl_viewcos);
}
if (groundslope)
{
for (i = 0; i < 4; i++)
{
slopez = P_GetSlopeZAt(groundslope, FLOAT_TO_FIXED(shadowVerts[i].x), FLOAT_TO_FIXED(shadowVerts[i].z));
shadowVerts[i].y = FIXED_TO_FLOAT(slopez) + flip * 0.05f;
}
}
else
{
for (i = 0; i < 4; i++)
shadowVerts[i].y = FIXED_TO_FLOAT(groundz) + flip * 0.05f;
}
shadowVerts[0].s = shadowVerts[3].s = 0;
shadowVerts[2].s = shadowVerts[1].s = ((GLPatch_t *)gpatch->hardware)->max_s;
shadowVerts[3].t = shadowVerts[2].t = 0;
shadowVerts[0].t = shadowVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t;
if (!(thing->renderflags & RF_NOCOLORMAPS))
{
if (thing->subsector->sector->numlights)
{
// Always use the light at the top instead of whatever I was doing before
light = R_GetPlaneLight(thing->subsector->sector, groundz, false);
if (*thing->subsector->sector->lightlist[light].extra_colormap)
colormap = *thing->subsector->sector->lightlist[light].extra_colormap;
}
else if (thing->subsector->sector->extra_colormap)
colormap = thing->subsector->sector->extra_colormap;
}
HWR_Lighting(&sSurf, 255, colormap, P_SectorUsesDirectionalLighting(thing->subsector->sector));
sSurf.PolyColor.s.alpha = 255;
if (thing->whiteshadow == true)
{
blendmode = PF_Additive;
}
if (HWR_UseShader())
{
shader = SHADER_SPRITE;
blendmode |= PF_ColorMapped;
}
HWR_ProcessPolygon(&sSurf, shadowVerts, 4, blendmode|PF_Modulated, shader, false);
}
// This is expecting a pointer to an array containing 4 wallVerts for a sprite
static void HWR_RotateSpritePolyToAim(gl_vissprite_t *spr, FOutVector *wallVerts, const boolean precip)
{
if (cv_glspritebillboarding.value
&& spr && spr->mobj && !R_ThingIsPaperSprite(spr->mobj)
&& wallVerts)
{
// uncapped/interpolation
interpmobjstate_t interp = {0};
// do interpolation
if (spr->precip)
{
R_InterpolatePrecipMobjState((precipmobj_t *)spr->mobj, R_GetTimeFrac(RTF_LEVEL), &interp);
}
else
{
R_InterpolateMobjState(spr->mobj, R_GetTimeFrac(RTF_LEVEL), &interp);
}
float basey = FIXED_TO_FLOAT(interp.z);
float lowy = wallVerts[0].y;
if (!precip && P_MobjFlip(spr->mobj) == -1) // precip doesn't have eflags so they can't flip
{
basey = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height);
}
// Rotate sprites to fully billboard with the camera
// X, Y, AND Z need to be manipulated for the polys to rotate around the
// origin, because of how the origin setting works I believe that should
// be mobj->z or mobj->z + mobj->height
wallVerts[2].y = wallVerts[3].y = (spr->gzt - basey) * gl_viewludsin + basey;
wallVerts[0].y = wallVerts[1].y = (lowy - basey) * gl_viewludsin + basey;
// translate back to be around 0 before translating back
wallVerts[3].x += ((spr->gzt - basey) * gl_viewludcos) * gl_viewcos;
wallVerts[2].x += ((spr->gzt - basey) * gl_viewludcos) * gl_viewcos;
wallVerts[0].x += ((lowy - basey) * gl_viewludcos) * gl_viewcos;
wallVerts[1].x += ((lowy - basey) * gl_viewludcos) * gl_viewcos;
wallVerts[3].z += ((spr->gzt - basey) * gl_viewludcos) * gl_viewsin;
wallVerts[2].z += ((spr->gzt - basey) * gl_viewludcos) * gl_viewsin;
wallVerts[0].z += ((lowy - basey) * gl_viewludcos) * gl_viewsin;
wallVerts[1].z += ((lowy - basey) * gl_viewludcos) * gl_viewsin;
}
}
static void HWR_SplitSprite(gl_vissprite_t *spr)
{
FOutVector wallVerts[4];
FOutVector baseWallVerts[4]; // This is what the verts should end up as
patch_t *gpatch;
FSurfaceInfo Surf;
extracolormap_t *colormap = NULL;
INT32 lightlevel;
boolean lightset = true;
FBITFIELD blend = 0;
FBITFIELD occlusion;
INT32 shader = SHADER_NONE;
boolean use_linkdraw_hack = false;
UINT8 alpha;
INT32 i;
float realtop, realbot, top, bot;
float ttop, tbot, tmult;
float bheight;
float realheight, heightmult;
const sector_t *sector = spr->mobj->subsector->sector;
const lightlist_t *list = sector->lightlist;
float endrealtop, endrealbot, endtop, endbot;
float endbheight;
float endrealheight;
fixed_t temp;
fixed_t v1x, v1y, v2x, v2y;
gpatch = spr->gpatch;
// cache the patch in the graphics card memory
//12/12/99: Hurdler: same comment as above (for md2)
//Hurdler: 25/04/2000: now support colormap in hardware mode
HWR_GetMappedPatch(gpatch, spr->colormap);
baseWallVerts[0].x = baseWallVerts[3].x = spr->x1;
baseWallVerts[2].x = baseWallVerts[1].x = spr->x2;
baseWallVerts[0].z = baseWallVerts[3].z = spr->z1;
baseWallVerts[1].z = baseWallVerts[2].z = spr->z2;
baseWallVerts[2].y = baseWallVerts[3].y = spr->gzt;
baseWallVerts[0].y = baseWallVerts[1].y = spr->gz;
v1x = FLOAT_TO_FIXED(spr->x1);
v1y = FLOAT_TO_FIXED(spr->z1);
v2x = FLOAT_TO_FIXED(spr->x2);
v2y = FLOAT_TO_FIXED(spr->z2);
if (spr->flip)
{
baseWallVerts[0].s = baseWallVerts[3].s = ((GLPatch_t *)gpatch->hardware)->max_s;
baseWallVerts[2].s = baseWallVerts[1].s = 0;
}
else
{
baseWallVerts[0].s = baseWallVerts[3].s = 0;
baseWallVerts[2].s = baseWallVerts[1].s = ((GLPatch_t *)gpatch->hardware)->max_s;
}
// flip the texture coords (look familiar?)
if (spr->vflip)
{
baseWallVerts[3].t = baseWallVerts[2].t = ((GLPatch_t *)gpatch->hardware)->max_t;
baseWallVerts[0].t = baseWallVerts[1].t = 0;
}
else
{
baseWallVerts[3].t = baseWallVerts[2].t = 0;
baseWallVerts[0].t = baseWallVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t;
}
// push it toward the camera to mitigate floor-clipping sprites
if (/*!R_ThingIsPaperSprite(spr->mobj) && */!(spr->mobj->terrain && spr->mobj->terrain->floorClip)) // but not for papersprites or for floorclip
{
// Let dispoffset work first since this adjust each vertex
HWR_RotateSpritePolyToAim(spr, baseWallVerts, false);
// push it toward the camera to mitigate floor-clipping sprites
float sprdist = sqrtf((spr->x1 - gl_viewx)*(spr->x1 - gl_viewx) + (spr->z1 - gl_viewy)*(spr->z1 - gl_viewy) + (spr->gzt - gl_viewz)*(spr->gzt - gl_viewz));
float distfact = ((2.0f*spr->dispoffset) + 20.0f) / sprdist;
for (i = 0; i < 4; i++)
{
baseWallVerts[i].x += (gl_viewx - baseWallVerts[i].x)*distfact;
baseWallVerts[i].z += (gl_viewy - baseWallVerts[i].z)*distfact;
baseWallVerts[i].y += (gl_viewz - baseWallVerts[i].y)*distfact;
}
}
realtop = top = baseWallVerts[3].y;
realbot = bot = baseWallVerts[0].y;
ttop = baseWallVerts[3].t;
tbot = baseWallVerts[0].t;
tmult = (tbot - ttop) / (top - bot);
endrealtop = endtop = baseWallVerts[2].y;
endrealbot = baseWallVerts[1].y;
// copy the contents of baseWallVerts into the drawn wallVerts array
// baseWallVerts is used to know the final shape to easily get the vertex
// co-ordinates
memcpy(wallVerts, baseWallVerts, sizeof(baseWallVerts));
// if sprite has linkdraw, then dont write to z-buffer (by not using PF_Occlude)
// this will result in sprites drawn afterwards to be drawn on top like intended when using linkdraw.
if ((spr->mobj->flags2 & MF2_LINKDRAW) && spr->mobj->tracer)
occlusion = 0;
else
occlusion = PF_Occlude;
// Determine the blendmode and translucency value
{
UINT32 blendmode, trans;
if (spr->mobj->renderflags & RF_BLENDMASK)
blendmode = (spr->mobj->renderflags & RF_BLENDMASK) >> RF_BLENDSHIFT;
else
blendmode = (spr->mobj->frame & FF_BLENDMASK) >> FF_BLENDSHIFT;
if (blendmode)
blendmode++; // realign to constants
if (spr->mobj->renderflags & RF_TRANSMASK)
trans = (spr->mobj->renderflags & RF_TRANSMASK) >> RF_TRANSSHIFT;
else
trans = (spr->mobj->frame & FF_TRANSMASK) >> FF_TRANSSHIFT;
if (trans >= NUMTRANSMAPS)
return; // cap
blend = HWR_SurfaceBlend(blendmode, trans, &Surf);
// if sprite has PF_ALWAYSONTOP, draw on top of everything.
if (cv_debugrender_spriteclip.value || spr->mobj->renderflags & RF_ALWAYSONTOP)
blend |= PF_NoDepthTest;
if (!trans && !blendmode)
{
// BP: i agree that is little better in environement but it don't
// work properly under glide nor with fogcolor to ffffff :(
// Hurdler: PF_Environement would be cool, but we need to fix
// the issue with the fog before
blend |= occlusion;
if (!occlusion) use_linkdraw_hack = true;
}
}
if (HWR_UseShader())
{
shader = SHADER_SPRITE;
blend |= PF_ColorMapped;
}
alpha = Surf.PolyColor.s.alpha;
// Start with the lightlevel and colormap from the top of the sprite
lightlevel = *list[sector->numlights - 1].lightlevel;
if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS))
colormap = *list[sector->numlights - 1].extra_colormap;
i = 0;
temp = FLOAT_TO_FIXED(realtop);
lightset = HWR_OverrideObjectLightLevel(spr->mobj, &lightlevel);
for (i = 1; i < sector->numlights; i++)
{
fixed_t h = P_GetLightZAt(&sector->lightlist[i], spr->mobj->x, spr->mobj->y);
if (h <= temp)
{
if (!lightset)
lightlevel = *list[i-1].lightlevel > 255 ? 255 : *list[i-1].lightlevel;
if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS))
colormap = *list[i-1].extra_colormap;
break;
}
}
if (!lightset)
HWR_ObjectLightLevelPost(spr, sector, &lightlevel, false);
for (i = 0; i < sector->numlights; i++)
{
if (endtop < endrealbot && top < realbot)
return;
// even if we aren't changing colormap or lightlevel, we still need to continue drawing down the sprite
if (!(list[i].flags & FOF_NOSHADE) && (list[i].flags & FOF_CUTSPRITES))
{
if (!lightset)
{
lightlevel = *list[i].lightlevel > 255 ? 255 : *list[i].lightlevel;
HWR_ObjectLightLevelPost(spr, sector, &lightlevel, false);
}
if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS))
colormap = *list[i].extra_colormap;
}
if (i + 1 < sector->numlights)
{
temp = P_GetLightZAt(&list[i+1], v1x, v1y);
bheight = FIXED_TO_FLOAT(temp);
temp = P_GetLightZAt(&list[i+1], v2x, v2y);
endbheight = FIXED_TO_FLOAT(temp);
}
else
{
bheight = realbot;
endbheight = endrealbot;
}
if (endbheight >= endtop && bheight >= top)
continue;
bot = bheight;
if (bot < realbot)
bot = realbot;
endbot = endbheight;
if (endbot < endrealbot)
endbot = endrealbot;
wallVerts[3].t = ttop + ((realtop - top) * tmult);
wallVerts[2].t = ttop + ((endrealtop - endtop) * tmult);
wallVerts[0].t = ttop + ((realtop - bot) * tmult);
wallVerts[1].t = ttop + ((endrealtop - endbot) * tmult);
wallVerts[3].y = top;
wallVerts[2].y = endtop;
wallVerts[0].y = bot;
wallVerts[1].y = endbot;
// The x and y only need to be adjusted in the case that it's not a papersprite
if (cv_glspritebillboarding.value
&& spr->mobj && !R_ThingIsPaperSprite(spr->mobj))
{
// Get the x and z of the vertices so billboarding draws correctly
realheight = realbot - realtop;
endrealheight = endrealbot - endrealtop;
heightmult = (realtop - top) / realheight;
wallVerts[3].x = baseWallVerts[3].x + (baseWallVerts[3].x - baseWallVerts[0].x) * heightmult;
wallVerts[3].z = baseWallVerts[3].z + (baseWallVerts[3].z - baseWallVerts[0].z) * heightmult;
heightmult = (endrealtop - endtop) / endrealheight;
wallVerts[2].x = baseWallVerts[2].x + (baseWallVerts[2].x - baseWallVerts[1].x) * heightmult;
wallVerts[2].z = baseWallVerts[2].z + (baseWallVerts[2].z - baseWallVerts[1].z) * heightmult;
heightmult = (realtop - bot) / realheight;
wallVerts[0].x = baseWallVerts[3].x + (baseWallVerts[3].x - baseWallVerts[0].x) * heightmult;
wallVerts[0].z = baseWallVerts[3].z + (baseWallVerts[3].z - baseWallVerts[0].z) * heightmult;
heightmult = (endrealtop - endbot) / endrealheight;
wallVerts[1].x = baseWallVerts[2].x + (baseWallVerts[2].x - baseWallVerts[1].x) * heightmult;
wallVerts[1].z = baseWallVerts[2].z + (baseWallVerts[2].z - baseWallVerts[1].z) * heightmult;
}
HWR_Lighting(&Surf, lightlevel, colormap, P_SectorUsesDirectionalLighting(sector) && !R_ThingIsFullBright(spr->mobj));
Surf.PolyColor.s.alpha = alpha;
HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false);
if (use_linkdraw_hack)
HWR_LinkDrawHackAdd(wallVerts, spr);
top = bot;
endtop = endbot;
}
bot = realbot;
endbot = endrealbot;
if (endtop <= endrealbot && top <= realbot)
return;
// If we're ever down here, somehow the above loop hasn't draw all the light levels of sprite
wallVerts[3].t = ttop + ((realtop - top) * tmult);
wallVerts[2].t = ttop + ((endrealtop - endtop) * tmult);
wallVerts[0].t = ttop + ((realtop - bot) * tmult);
wallVerts[1].t = ttop + ((endrealtop - endbot) * tmult);
wallVerts[3].y = top;
wallVerts[2].y = endtop;
wallVerts[0].y = bot;
wallVerts[1].y = endbot;
HWR_Lighting(&Surf, lightlevel, colormap, P_SectorUsesDirectionalLighting(sector));
Surf.PolyColor.s.alpha = alpha;
HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false);
if (use_linkdraw_hack)
HWR_LinkDrawHackAdd(wallVerts, spr);
}
static void HWR_DrawBoundingBox(gl_vissprite_t *vis)
{
FOutVector v[24];
FSurfaceInfo Surf = {0};
//
// create a cube (side view)
//
// 5--4 3
// |
// |
// 0--1 2
//
// repeat this 4 times (overhead)
//
//
// 17 20 21 11
// 16 15 14 10
// 27 22 *--* 07 12
// | |
// 26 23 *--* 06 13
// 24 00 01 02
// 25 05 04 03
//
v[000].x = v[005].x = v[015].x = v[016].x = v[017].x = v[020].x =
v[022].x = v[023].x = v[024].x = v[025].x = v[026].x = v[027].x = vis->x1; // west
v[001].x = v[002].x = v[003].x = v[004].x = v[006].x = v[007].x =
v[010].x = v[011].x = v[012].x = v[013].x = v[014].x = v[021].x = vis->x2; // east
v[000].z = v[001].z = v[002].z = v[003].z = v[004].z = v[005].z =
v[006].z = v[013].z = v[023].z = v[024].z = v[025].z = v[026].z = vis->z1; // south
v[007].z = v[010].z = v[011].z = v[012].z = v[014].z = v[015].z =
v[016].z = v[017].z = v[020].z = v[021].z = v[022].z = v[027].z = vis->z2; // north
v[000].y = v[001].y = v[002].y = v[006].y = v[007].y = v[010].y =
v[014].y = v[015].y = v[016].y = v[022].y = v[023].y = v[024].y = vis->gz; // bottom
v[003].y = v[004].y = v[005].y = v[011].y = v[012].y = v[013].y =
v[017].y = v[020].y = v[021].y = v[025].y = v[026].y = v[027].y = vis->gzt; // top
Surf.PolyColor = V_GetColor(R_GetBoundingBoxColor(vis->mobj));
HWR_ProcessPolygon(&Surf, v, 24, PF_Modulated|PF_NoTexture|PF_WireFrame, SHADER_NONE, false);
}
// -----------------+
// HWR_DrawSprite : Draw flat sprites
// : (monsters, bonuses, weapons, lights, ...)
// Returns :
// -----------------+
static void HWR_DrawSprite(gl_vissprite_t *spr)
{
FOutVector wallVerts[4];
patch_t *gpatch;
FSurfaceInfo Surf;
const boolean splat = R_ThingIsFloorSprite(spr->mobj);
if (!spr->mobj)
return;
if (!spr->mobj->subsector)
return;
if (spr->mobj->subsector->sector->numlights
&& (spr->mobj->renderflags & RF_ABSOLUTELIGHTLEVEL) == 0
&& !splat)
{
HWR_SplitSprite(spr);
return;
}
// cache sprite graphics
//12/12/99: Hurdler:
// OK, I don't change anything for MD2 support because I want to be
// sure to do it the right way. So actually, we keep normal sprite
// in memory and we add the md2 model if it exists for that sprite
gpatch = spr->gpatch;
#ifdef ALAM_LIGHTING
if (!(spr->mobj->flags2 & MF2_DEBRIS) && (spr->mobj->sprite != SPR_PLAY))
HWR_DL_AddLight(spr, gpatch);
#endif
// create the sprite billboard
//
// 3--2
// | /|
// |/ |
// 0--1
if (splat)
{
F2DCoord verts[4];
F2DCoord rotated[4];
angle_t angle;
float ca, sa;
float w, h;
float xscale, yscale;
float xoffset, yoffset;
float leftoffset, topoffset;
float scale = spr->scale;
float zoffset = (P_MobjFlip(spr->mobj) * 0.05f);
pslope_t *splatslope = NULL;
INT32 i;
renderflags_t renderflags = spr->renderflags;
if (renderflags & RF_SHADOWEFFECTS)
scale *= spr->shadowscale;
if (spr->rotateflags & SRF_3D || renderflags & RF_NOSPLATBILLBOARD)
angle = spr->angle;
else
angle = viewangle;
if (!spr->rotated)
angle += spr->mobj->rollangle;
angle = -angle;
angle += ANGLE_90;
topoffset = spr->spriteyoffset;
leftoffset = spr->spritexoffset;
if (spr->flip)
leftoffset = ((float)gpatch->width - leftoffset);
xscale = spr->scale * spr->spritexscale;
yscale = spr->scale * spr->spriteyscale;
xoffset = leftoffset * xscale;
yoffset = topoffset * yscale;
w = (float)gpatch->width * xscale;
h = (float)gpatch->height * yscale;
// Set positions
// 3--2
// | |
// 0--1
verts[3].x = -xoffset;
verts[3].y = yoffset;
verts[2].x = w - xoffset;
verts[2].y = yoffset;
verts[1].x = w - xoffset;
verts[1].y = -h + yoffset;
verts[0].x = -xoffset;
verts[0].y = -h + yoffset;
ca = FIXED_TO_FLOAT(FINECOSINE((-angle)>>ANGLETOFINESHIFT));
sa = FIXED_TO_FLOAT(FINESINE((-angle)>>ANGLETOFINESHIFT));
// Rotate
for (i = 0; i < 4; i++)
{
rotated[i].x = (verts[i].x * ca) - (verts[i].y * sa);
rotated[i].y = (verts[i].x * sa) + (verts[i].y * ca);
}
// Translate
for (i = 0; i < 4; i++)
{
wallVerts[i].x = rotated[i].x + spr->x1;
wallVerts[i].z = rotated[i].y + spr->z1;
}
if (renderflags & (RF_SLOPESPLAT | RF_OBJECTSLOPESPLAT))
{
pslope_t *standingslope = spr->mobj->standingslope; // The slope that the object is standing on.
// The slope that was defined for the sprite.
if (renderflags & RF_SLOPESPLAT)
splatslope = spr->mobj->floorspriteslope;
if (standingslope && (renderflags & RF_OBJECTSLOPESPLAT))
splatslope = standingslope;
}
// Set vertical position
if (splatslope)
{
for (i = 0; i < 4; i++)
{
fixed_t slopez = P_GetSlopeZAt(splatslope, FLOAT_TO_FIXED(wallVerts[i].x), FLOAT_TO_FIXED(wallVerts[i].z));
wallVerts[i].y = FIXED_TO_FLOAT(slopez) + zoffset;
}
}
else
{
for (i = 0; i < 4; i++)
wallVerts[i].y = FIXED_TO_FLOAT(spr->gz) + zoffset;
}
}
else
{
// these were already scaled in HWR_ProjectSprite
wallVerts[0].x = wallVerts[3].x = spr->x1;
wallVerts[2].x = wallVerts[1].x = spr->x2;
wallVerts[2].y = wallVerts[3].y = spr->gzt;
wallVerts[0].y = wallVerts[1].y = spr->gz;
// make a wall polygon (with 2 triangles), using the floor/ceiling heights,
// and the 2d map coords of start/end vertices
wallVerts[0].z = wallVerts[3].z = spr->z1;
wallVerts[1].z = wallVerts[2].z = spr->z2;
}
// cache the patch in the graphics card memory
//12/12/99: Hurdler: same comment as above (for md2)
//Hurdler: 25/04/2000: now support colormap in hardware mode
HWR_GetMappedPatch(gpatch, spr->colormap);
if (spr->flip)
{
wallVerts[0].s = wallVerts[3].s = ((GLPatch_t *)gpatch->hardware)->max_s;
wallVerts[2].s = wallVerts[1].s = 0;
}else{
wallVerts[0].s = wallVerts[3].s = 0;
wallVerts[2].s = wallVerts[1].s = ((GLPatch_t *)gpatch->hardware)->max_s;
}
// flip the texture coords (look familiar?)
if (spr->vflip)
{
wallVerts[3].t = wallVerts[2].t = ((GLPatch_t *)gpatch->hardware)->max_t;
wallVerts[0].t = wallVerts[1].t = 0;
}else{
wallVerts[3].t = wallVerts[2].t = 0;
wallVerts[0].t = wallVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t;
}
float sprdist = 0.0f, distfact = 0.0f;
size_t i;
if (!splat /*&& !R_ThingIsPaperSprite(spr->mobj)*/ && !(spr->mobj->terrain && spr->mobj->terrain->floorClip))
{
// Let dispoffset work first since this adjust each vertex
HWR_RotateSpritePolyToAim(spr, wallVerts, false);
// push it toward the camera to mitigate floor-clipping sprites
sprdist = sqrtf((spr->x1 - gl_viewx)*(spr->x1 - gl_viewx) + (spr->z1 - gl_viewy)*(spr->z1 -gl_viewy)+ (spr->gzt - gl_viewz)*(spr->gzt - gl_viewz));
distfact = ((2.0f* spr->dispoffset) + 20.0f) / sprdist;
for (i = 0; i < 4; i++)
{
wallVerts[i].x += (gl_viewx - wallVerts[i].x)*distfact;
wallVerts[i].z += (gl_viewy - wallVerts[i].z)*distfact;
wallVerts[i].y += (gl_viewz - wallVerts[i].y)*distfact;
}
}
else if (R_ThingIsFloorSprite(spr->mobj))
{
sprdist = sqrtf((spr->x1 - gl_viewx)*(spr->x1 - gl_viewx) + (spr->z1 - gl_viewy)*(spr->z1 - gl_viewy));
distfact = (2.0f + 20.0f) / sprdist;
// pull splats out of the floor
for (i = 0; i < 4; i++)
{
wallVerts[i].x += (gl_viewx - wallVerts[i].x)*distfact;
wallVerts[i].z += (gl_viewy - wallVerts[i].z)*distfact;
wallVerts[i].y += (gl_viewz - wallVerts[i].y)*distfact;
}
}
// This needs to be AFTER the shadows so that the regular sprites aren't drawn completely black.
// sprite lighting by modulating the RGB components
/// \todo coloured
// colormap test
{
sector_t *sector = spr->mobj->subsector->sector;
INT32 lightlevel = 0;
boolean lightset = HWR_OverrideObjectLightLevel(spr->mobj, &lightlevel);
extracolormap_t *colormap = NULL;
if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS))
colormap = sector->extra_colormap;
if (splat && sector->numlights)
{
INT32 light = R_GetPlaneLight(sector, spr->mobj->z, false);
if (!lightset)
lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel;
if (!R_ThingIsFullBright(spr->mobj) && *sector->lightlist[light].extra_colormap && !(spr->mobj->renderflags & RF_NOCOLORMAPS))
colormap = *sector->lightlist[light].extra_colormap;
}
else if (!lightset)
lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel;
if (!lightset)
HWR_ObjectLightLevelPost(spr, sector, &lightlevel, false);
HWR_Lighting(&Surf, lightlevel, colormap, P_SectorUsesDirectionalLighting(sector) && !R_ThingIsFullBright(spr->mobj));
}
{
INT32 shader = SHADER_NONE;
FBITFIELD blend = 0;
FBITFIELD occlusion;
boolean use_linkdraw_hack = false;
// if sprite has linkdraw, then dont write to z-buffer (by not using PF_Occlude)
// this will result in sprites drawn afterwards to be drawn on top like intended when using linkdraw.
if ((spr->mobj->flags2 & MF2_LINKDRAW) && spr->mobj->tracer)
occlusion = 0;
else
occlusion = PF_Occlude;
// Determine the blendmode and translucency value
{
UINT32 blendmode, trans;
if (spr->mobj->renderflags & RF_BLENDMASK)
blendmode = (spr->mobj->renderflags & RF_BLENDMASK) >> RF_BLENDSHIFT;
else
blendmode = (spr->mobj->frame & FF_BLENDMASK) >> FF_BLENDSHIFT;
if (blendmode)
blendmode++; // realign to constants
if (spr->mobj->renderflags & RF_TRANSMASK)
trans = (spr->mobj->renderflags & RF_TRANSMASK) >> RF_TRANSSHIFT;
else
trans = (spr->mobj->frame & FF_TRANSMASK) >> FF_TRANSSHIFT;
if (trans >= NUMTRANSMAPS)
return; // cap
blend = HWR_SurfaceBlend(blendmode, trans, &Surf);
// if sprite has PF_ALWAYSONTOP, draw on top of everything.
if (cv_debugrender_spriteclip.value || spr->mobj->renderflags & RF_ALWAYSONTOP)
blend |= PF_NoDepthTest;
if (!trans && !blendmode)
{
// BP: i agree that is little better in environement but it don't
// work properly under glide nor with fogcolor to ffffff :(
// Hurdler: PF_Environement would be cool, but we need to fix
// the issue with the fog before
blend |= occlusion;
if (!occlusion) use_linkdraw_hack = true;
}
}
if (spr->renderflags & RF_SHADOWEFFECTS)
{
INT32 alpha = Surf.PolyColor.s.alpha;
alpha -= ((INT32)(spr->shadowheight / 4.0f)) + 75;
if (alpha < 1)
return;
Surf.PolyColor.s.alpha = (UINT8)(alpha);
blend = PF_Translucent|occlusion;
if (!occlusion) use_linkdraw_hack = true;
}
if (HWR_UseShader())
{
shader = SHADER_SPRITE;
blend |= PF_ColorMapped;
}
HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false);
if (use_linkdraw_hack)
HWR_LinkDrawHackAdd(wallVerts, spr);
}
}
#ifdef HWPRECIP
// Sprite drawer for precipitation
static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr)
{
INT32 shader = SHADER_NONE;
FBITFIELD blend = 0;
FOutVector wallVerts[4];
patch_t *gpatch;
FSurfaceInfo Surf;
if (!spr->mobj)
return;
if (!spr->mobj->subsector)
return;
// cache sprite graphics
gpatch = spr->gpatch;
// create the sprite billboard
//
// 3--2
// | /|
// |/ |
// 0--1
wallVerts[0].x = wallVerts[3].x = spr->x1;
wallVerts[2].x = wallVerts[1].x = spr->x2;
wallVerts[2].y = wallVerts[3].y = spr->gzt;
wallVerts[0].y = wallVerts[1].y = spr->gz;
// make a wall polygon (with 2 triangles), using the floor/ceiling heights,
// and the 2d map coords of start/end vertices
wallVerts[0].z = wallVerts[3].z = spr->z1;
wallVerts[1].z = wallVerts[2].z = spr->z2;
// Let dispoffset work first since this adjust each vertex
HWR_RotateSpritePolyToAim(spr, wallVerts, true);
wallVerts[0].s = wallVerts[3].s = 0;
wallVerts[2].s = wallVerts[1].s = ((GLPatch_t *)gpatch->hardware)->max_s;
wallVerts[3].t = wallVerts[2].t = 0;
wallVerts[0].t = wallVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t;
// cache the patch in the graphics card memory
//12/12/99: Hurdler: same comment as above (for md2)
//Hurdler: 25/04/2000: now support colormap in hardware mode
HWR_GetMappedPatch(gpatch, spr->colormap);
// colormap test
{
sector_t *sector = spr->mobj->subsector->sector;
UINT8 lightlevel = 255;
extracolormap_t *colormap = sector->extra_colormap;
if (sector->numlights)
{
// Always use the light at the top instead of whatever I was doing before
INT32 light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false);
if (!R_ThingIsFullBright(spr->mobj))
lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel;
if (!(spr->mobj->renderflags & RF_NOCOLORMAPS))
{
if (*sector->lightlist[light].extra_colormap)
colormap = *sector->lightlist[light].extra_colormap;
}
}
else
{
if (!R_ThingIsFullBright(spr->mobj))
lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel;
if (!(spr->mobj->renderflags & RF_NOCOLORMAPS))
{
if (sector->extra_colormap)
colormap = sector->extra_colormap;
}
}
//lightlevel = 128 + (lightlevel>>1);
HWR_Lighting(&Surf, lightlevel, colormap, P_SectorUsesDirectionalLighting(sector));
}
// Determine the blendmode and translucency value
{
UINT32 blendmode, trans;
if (spr->mobj->renderflags & RF_BLENDMASK)
blendmode = (spr->mobj->renderflags & RF_BLENDMASK) >> RF_BLENDSHIFT;
else
blendmode = (spr->mobj->frame & FF_BLENDMASK) >> FF_BLENDSHIFT;
if (blendmode)
blendmode++; // realign to constants
if (spr->mobj->renderflags & RF_TRANSMASK)
trans = (spr->mobj->renderflags & RF_TRANSMASK) >> RF_TRANSSHIFT;
else
trans = (spr->mobj->frame & FF_TRANSMASK) >> FF_TRANSSHIFT;
if (trans >= NUMTRANSMAPS)
return; // cap
blend = HWR_SurfaceBlend(blendmode, trans, &Surf);
if (!trans && !blendmode)
{
// BP: i agree that is little better in environement but it don't
// work properly under glide nor with fogcolor to ffffff :(
// Hurdler: PF_Environement would be cool, but we need to fix
// the issue with the fog before
blend |= PF_Occlude;
}
}
if (HWR_UseShader())
{
shader = SHADER_SPRITE;
blend |= PF_ColorMapped;
}
HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false);
}
#endif
// --------------------------------------------------------------------------
// Sort vissprites by distance
// --------------------------------------------------------------------------
gl_vissprite_t* gl_vsprorder[MAXVISSPRITES];
// Note: For more correct transparency the transparent sprites would need to be
// sorted and drawn together with transparent surfaces.
static int CompareVisSprites(const void *p1, const void *p2)
{
gl_vissprite_t* spr1 = *(gl_vissprite_t*const*)p1;
gl_vissprite_t* spr2 = *(gl_vissprite_t*const*)p2;
int idiff;
float fdiff;
float tz1, tz2;
// Make transparent sprites last. Comment from the previous sort implementation:
// Sryder: Oh boy, while it's nice having ALL the sprites sorted properly, it fails when we bring MD2's into the
// mix and they want to be translucent. So let's place all the translucent sprites and MD2's AFTER
// everything else, but still ordered of course, the depth buffer can handle the opaque ones plenty fine.
// We just need to move all translucent ones to the end in order
// TODO: Fully sort all sprites and MD2s with walls and floors, this part will be unnecessary after that
int transparency1;
int transparency2;
int renderflags1;
int renderflags2;
int frame1;
int frame2;
int linkdraw1;
int linkdraw2;
// bbox doesn't need to be sorted
if (spr1->bbox || spr2->bbox)
return 0;
// check for precip first, because then sprX->mobj is actually a precipmobj_t and does not have flags2 or tracer
linkdraw1 = !spr1->precip && (spr1->mobj->flags2 & MF2_LINKDRAW) && spr1->mobj->tracer;
linkdraw2 = !spr2->precip && (spr2->mobj->flags2 & MF2_LINKDRAW) && spr2->mobj->tracer;
// ^ is the XOR operation
// if comparing a linkdraw and non-linkdraw sprite or 2 linkdraw sprites with different tracers, then use
// the tracer's properties instead of the main sprite's.
if ((linkdraw1 && linkdraw2 && spr1->mobj->tracer != spr2->mobj->tracer) || (linkdraw1 ^ linkdraw2))
{
if (linkdraw1)
{
tz1 = spr1->tracertz;
renderflags1 = spr1->mobj->tracer->renderflags;
frame1 = spr1->mobj->tracer->frame;
}
else
{
tz1 = spr1->tz;
renderflags1 = spr1->mobj->renderflags;
frame1 = spr1->mobj->frame;
}
if (linkdraw2)
{
tz2 = spr2->tracertz;
renderflags2 = spr2->mobj->tracer->renderflags;
frame2 = spr2->mobj->tracer->frame;
}
else
{
tz2 = spr2->tz;
renderflags2 = spr2->mobj->renderflags;
frame2 = spr2->mobj->frame;
}
}
else
{
tz1 = spr1->tz;
renderflags1 = (spr1->precip ? 0 : spr1->mobj->renderflags);
frame1 = spr1->mobj->frame;
tz2 = spr2->tz;
renderflags2 = (spr2->precip ? 0 : spr2->mobj->renderflags);
frame2 = spr2->mobj->frame;
}
// first compare transparency flags, then compare tz, then compare dispoffset
transparency1 = (renderflags1 & RF_TRANSMASK) ?
((renderflags1 & RF_TRANSMASK)>>RF_TRANSSHIFT) :
((frame1 & FF_TRANSMASK)>>FF_TRANSSHIFT);
transparency2 = (renderflags2 & RF_TRANSMASK) ?
((renderflags2 & RF_TRANSMASK)>>RF_TRANSSHIFT) :
((frame2 & FF_TRANSMASK)>>FF_TRANSSHIFT);
idiff = transparency1 - transparency2;
if (idiff != 0) return idiff;
fdiff = tz2 - tz1; // this order seems correct when checking with apitrace. Back to front.
if (fabsf(fdiff) < 1.0E-36f)
return spr1->dispoffset - spr2->dispoffset; // smallest dispoffset first if sprites are at (almost) same location.
else if (fdiff > 0)
return 1;
else
return -1;
}
static void HWR_SortVisSprites(void)
{
UINT32 i;
for (i = 0; i < gl_visspritecount; i++)
{
gl_vsprorder[i] = HWR_GetVisSprite(i);
}
qs22j(gl_vsprorder, gl_visspritecount, sizeof(gl_vissprite_t*), CompareVisSprites);
}
// A drawnode is something that points to a 3D floor, 3D side, or masked
// middle texture. This is used for sorting with sprites.
typedef struct
{
FOutVector wallVerts[4];
FSurfaceInfo Surf;
INT32 texnum;
FBITFIELD blend;
INT32 drawcount;
boolean fogwall;
boolean noencore;
INT32 lightlevel;
extracolormap_t *wallcolormap; // Doing the lighting in HWR_RenderWall now for correct fog after sorting
} wallinfo_t;
static wallinfo_t *wallinfo = NULL;
static size_t numwalls = 0; // a list of transparent walls to be drawn
void HWR_RenderWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend, boolean fogwall, INT32 lightlevel, extracolormap_t *wallcolormap, INT32 texnum);
#define MAX_TRANSPARENTWALL 256
typedef struct
{
extrasubsector_t *xsub;
boolean isceiling;
fixed_t fixedheight;
INT32 lightlevel;
levelflat_t *levelflat;
INT32 alpha;
sector_t *FOFSector;
FBITFIELD blend;
boolean fogplane;
extracolormap_t *planecolormap;
INT32 drawcount;
} planeinfo_t;
static size_t numplanes = 0; // a list of transparent floors to be drawn
static planeinfo_t *planeinfo = NULL;
typedef struct
{
polyobj_t *polysector;
boolean isceiling;
fixed_t fixedheight;
INT32 lightlevel;
levelflat_t *levelflat;
INT32 alpha;
sector_t *FOFSector;
FBITFIELD blend;
extracolormap_t *planecolormap;
INT32 drawcount;
} polyplaneinfo_t;
static size_t numpolyplanes = 0; // a list of transparent poyobject floors to be drawn
static polyplaneinfo_t *polyplaneinfo = NULL;
//Hurdler: 3D water sutffs
typedef struct gl_drawnode_s
{
planeinfo_t *plane;
polyplaneinfo_t *polyplane;
wallinfo_t *wall;
gl_vissprite_t *sprite;
// struct gl_drawnode_s *next;
// struct gl_drawnode_s *prev;
} gl_drawnode_t;
static INT32 drawcount = 0;
#define MAX_TRANSPARENTFLOOR 512
// This will likely turn into a copy of HWR_Add3DWater and replace it.
void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap)
{
static size_t allocedplanes = 0;
// Force realloc if buffer has been freed
if (!planeinfo)
allocedplanes = 0;
if (allocedplanes < numplanes + 1)
{
allocedplanes += MAX_TRANSPARENTFLOOR;
Z_Realloc(planeinfo, allocedplanes * sizeof (*planeinfo), PU_LEVEL, &planeinfo);
}
planeinfo[numplanes].isceiling = isceiling;
planeinfo[numplanes].fixedheight = fixedheight;
planeinfo[numplanes].lightlevel = ((planecolormap && (planecolormap->flags & CMF_FOG)) || mapnamespace == MNS_SRB2KART) ? lightlevel : 255;
planeinfo[numplanes].levelflat = levelflat;
planeinfo[numplanes].xsub = xsub;
planeinfo[numplanes].alpha = alpha;
planeinfo[numplanes].FOFSector = FOFSector;
planeinfo[numplanes].blend = blend;
planeinfo[numplanes].fogplane = fogplane;
planeinfo[numplanes].planecolormap = planecolormap;
planeinfo[numplanes].drawcount = drawcount++;
numplanes++;
}
// Adding this for now until I can create extrasubsector info for polyobjects
// When that happens it'll just be done through HWR_AddTransparentFloor and HWR_RenderPlane
void HWR_AddTransparentPolyobjectFloor(levelflat_t *levelflat, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap)
{
static size_t allocedpolyplanes = 0;
// Force realloc if buffer has been freed
if (!polyplaneinfo)
allocedpolyplanes = 0;
if (allocedpolyplanes < numpolyplanes + 1)
{
allocedpolyplanes += MAX_TRANSPARENTFLOOR;
Z_Realloc(polyplaneinfo, allocedpolyplanes * sizeof (*polyplaneinfo), PU_LEVEL, &polyplaneinfo);
}
polyplaneinfo[numpolyplanes].isceiling = isceiling;
polyplaneinfo[numpolyplanes].fixedheight = fixedheight;
polyplaneinfo[numpolyplanes].lightlevel = ((planecolormap && (planecolormap->flags & CMF_FOG)) || mapnamespace == MNS_SRB2KART) ? lightlevel : 255;
polyplaneinfo[numpolyplanes].levelflat = levelflat;
polyplaneinfo[numpolyplanes].polysector = polysector;
polyplaneinfo[numpolyplanes].alpha = alpha;
polyplaneinfo[numpolyplanes].FOFSector = FOFSector;
polyplaneinfo[numpolyplanes].blend = blend;
polyplaneinfo[numpolyplanes].planecolormap = planecolormap;
polyplaneinfo[numpolyplanes].drawcount = drawcount++;
numpolyplanes++;
}
// putting sortindex and sortnode here so the comparator function can see them
gl_drawnode_t *sortnode;
size_t *sortindex;
static int CompareDrawNodes(const void *p1, const void *p2)
{
size_t n1 = *(const size_t*)p1;
size_t n2 = *(const size_t*)p2;
INT32 v1 = 0;
INT32 v2 = 0;
INT32 diff;
if (sortnode[n1].plane)
v1 = sortnode[n1].plane->drawcount;
else if (sortnode[n1].polyplane)
v1 = sortnode[n1].polyplane->drawcount;
else if (sortnode[n1].wall)
v1 = sortnode[n1].wall->drawcount;
else I_Error("CompareDrawNodes: n1 unknown");
if (sortnode[n2].plane)
v2 = sortnode[n2].plane->drawcount;
else if (sortnode[n2].polyplane)
v2 = sortnode[n2].polyplane->drawcount;
else if (sortnode[n2].wall)
v2 = sortnode[n2].wall->drawcount;
else I_Error("CompareDrawNodes: n2 unknown");
diff = v2 - v1;
if (diff == 0) I_Error("CompareDrawNodes: diff is zero");
return diff;
}
static int CompareDrawNodePlanes(const void *p1, const void *p2)
{
size_t n1 = *(const size_t*)p1;
size_t n2 = *(const size_t*)p2;
if (!sortnode[n1].plane) I_Error("CompareDrawNodePlanes: Uh.. This isn't a plane! (n1)");
if (!sortnode[n2].plane) I_Error("CompareDrawNodePlanes: Uh.. This isn't a plane! (n2)");
return ABS(sortnode[n2].plane->fixedheight - viewz) - ABS(sortnode[n1].plane->fixedheight - viewz);
}
//
// HWR_CreateDrawNodes
// Creates and sorts a list of drawnodes for the scene being rendered.
static void HWR_CreateDrawNodes(void)
{
UINT32 i = 0, p = 0;
size_t run_start = 0;
// Dump EVERYTHING into a huge drawnode list. Then we'll sort it!
// Could this be optimized into _AddTransparentWall/_AddTransparentPlane?
// Hell yes! But sort algorithm must be modified to use a linked list.
sortnode = Z_Calloc((sizeof(planeinfo_t)*numplanes)
+ (sizeof(polyplaneinfo_t)*numpolyplanes)
+ (sizeof(wallinfo_t)*numwalls)
,PU_STATIC, NULL);
// todo:
// However, in reality we shouldn't be re-copying and shifting all this information
// that is already lying around. This should all be in some sort of linked list or lists.
sortindex = Z_Calloc(sizeof(size_t) * (numplanes + numpolyplanes + numwalls), PU_STATIC, NULL);
ps_hw_nodesorttime = I_GetPreciseTime();
for (i = 0; i < numplanes; i++, p++)
{
sortnode[p].plane = &planeinfo[i];
sortindex[p] = p;
}
for (i = 0; i < numpolyplanes; i++, p++)
{
sortnode[p].polyplane = &polyplaneinfo[i];
sortindex[p] = p;
}
for (i = 0; i < numwalls; i++, p++)
{
sortnode[p].wall = &wallinfo[i];
sortindex[p] = p;
}
ps_numdrawnodes = p;
// p is the number of stuff to sort
// sort the list based on the value of the 'drawcount' member of the drawnodes.
qs22j(sortindex, p, sizeof(size_t), CompareDrawNodes);
// an additional pass is needed to correct the order of consecutive planes in the list.
// for each consecutive run of planes in the list, sort that run based on plane height and view height.
while (run_start < p-1)// p-1 because a 1 plane run at the end of the list does not count
{
// locate run start
if (sortnode[sortindex[run_start]].plane)
{
// found it, now look for run end
size_t run_end;// (inclusive)
for (i = run_start+1; i < p; i++)// size_t and UINT32 being used mixed here... shouldnt break anything though..
{
if (!sortnode[sortindex[i]].plane) break;
}
run_end = i-1;
if (run_end > run_start)// if there are multiple consecutive planes, not just one
{
// consecutive run of planes found, now sort it
qs22j(sortindex + run_start, run_end - run_start + 1, sizeof(size_t), CompareDrawNodePlanes);
}
run_start = run_end + 1;// continue looking for runs coming right after this one
}
else
{
// this wasnt the run start, try next one
run_start++;
}
}
ps_hw_nodesorttime = I_GetPreciseTime() - ps_hw_nodesorttime;
ps_hw_nodedrawtime = I_GetPreciseTime();
// Okay! Let's draw it all! Woo!
HWD.pfnSetTransform(&atransform);
for (i = 0; i < p; i++)
{
if (sortnode[sortindex[i]].plane)
{
// We aren't traversing the BSP tree, so make gl_frontsector null to avoid crashes.
gl_frontsector = NULL;
if (!(sortnode[sortindex[i]].plane->blend & PF_NoTexture))
HWR_GetLevelFlat(sortnode[sortindex[i]].plane->levelflat, R_NoEncore(sortnode[sortindex[i]].plane->FOFSector, sortnode[sortindex[i]].plane->levelflat, sortnode[sortindex[i]].plane->isceiling));
HWR_RenderPlane(NULL, sortnode[sortindex[i]].plane->xsub, sortnode[sortindex[i]].plane->isceiling, sortnode[sortindex[i]].plane->fixedheight, sortnode[sortindex[i]].plane->blend, sortnode[sortindex[i]].plane->lightlevel,
sortnode[sortindex[i]].plane->levelflat, sortnode[sortindex[i]].plane->FOFSector, sortnode[sortindex[i]].plane->alpha, sortnode[sortindex[i]].plane->planecolormap);
}
else if (sortnode[sortindex[i]].polyplane)
{
// We aren't traversing the BSP tree, so make gl_frontsector null to avoid crashes.
gl_frontsector = NULL;
if (!(sortnode[sortindex[i]].polyplane->blend & PF_NoTexture))
HWR_GetLevelFlat(sortnode[sortindex[i]].polyplane->levelflat, R_NoEncore(sortnode[sortindex[i]].polyplane->FOFSector, sortnode[sortindex[i]].polyplane->levelflat, sortnode[sortindex[i]].polyplane->isceiling));
HWR_RenderPolyObjectPlane(sortnode[sortindex[i]].polyplane->polysector, sortnode[sortindex[i]].polyplane->isceiling, sortnode[sortindex[i]].polyplane->fixedheight, sortnode[sortindex[i]].polyplane->blend, sortnode[sortindex[i]].polyplane->lightlevel,
sortnode[sortindex[i]].polyplane->levelflat, sortnode[sortindex[i]].polyplane->FOFSector, sortnode[sortindex[i]].polyplane->alpha, sortnode[sortindex[i]].polyplane->planecolormap);
}
else if (sortnode[sortindex[i]].wall)
{
if (!(sortnode[sortindex[i]].wall->blend & PF_NoTexture))
HWR_GetTexture(sortnode[sortindex[i]].wall->texnum, sortnode[sortindex[i]].wall->noencore);
HWR_RenderWall(sortnode[sortindex[i]].wall->wallVerts, &sortnode[sortindex[i]].wall->Surf, sortnode[sortindex[i]].wall->blend, sortnode[sortindex[i]].wall->fogwall,
sortnode[sortindex[i]].wall->lightlevel, sortnode[sortindex[i]].wall->wallcolormap, sortnode[sortindex[i]].wall->texnum);
}
}
ps_hw_nodedrawtime = I_GetPreciseTime() - ps_hw_nodedrawtime;
numwalls = 0;
numplanes = 0;
numpolyplanes = 0;
// No mem leaks, please.
Z_Free(sortnode);
Z_Free(sortindex);
}
// --------------------------------------------------------------------------
// Draw all vissprites
// --------------------------------------------------------------------------
// added the stransform so they can be switched as drawing happenes so MD2s and sprites are sorted correctly with each other
static void HWR_DrawSprites(void)
{
UINT32 i;
boolean skipshadow = false; // skip shadow if it was drawn already for a linkdraw sprite encountered earlier in the list
#ifdef BAD_MODEL_OPTIONS
HWD.pfnSetSpecialState(HWD_SET_MODEL_LIGHTING, cv_glmodellighting.value);
#else
HWD.pfnSetSpecialState(HWD_SET_MODEL_LIGHTING, 1);
#endif
for (i = 0; i < gl_visspritecount; i++)
{
gl_vissprite_t *spr = gl_vsprorder[i];
if (spr->bbox)
HWR_DrawBoundingBox(spr);
else
#ifdef HWPRECIP
if (spr->precip)
HWR_DrawPrecipitationSprite(spr);
else
#endif
{
if (spr->mobj && spr->mobj->shadowscale && cv_shadow.value && !skipshadow)
{
HWR_DrawDropShadow(spr->mobj, spr->mobj->shadowscale);
}
if ((spr->mobj->flags2 & MF2_LINKDRAW) && spr->mobj->tracer)
{
// If this linkdraw sprite is behind a sprite that has a shadow,
// then that shadow has to be drawn first, otherwise the shadow ends up on top of
// the linkdraw sprite because the linkdraw sprite does not modify the z-buffer.
// The !skipshadow check is there in case there are multiple linkdraw sprites connected
// to the same tracer, so the tracer's shadow only gets drawn once.
if (cv_shadow.value && !skipshadow && spr->dispoffset < 0 && spr->mobj->tracer->shadowscale)
{
HWR_DrawDropShadow(spr->mobj->tracer, spr->mobj->tracer->shadowscale);
skipshadow = true;
// The next sprite in this loop should be either another linkdraw sprite or the tracer.
// When the tracer is inevitably encountered, skipshadow will cause it's shadow
// to get skipped and skipshadow will get set to false by the 'else' clause below.
}
}
else
{
skipshadow = false;
}
if (spr->mobj && spr->mobj->skin && spr->mobj->sprite == SPR_PLAY)
{
if (LIKELY(!cv_glmodels.value || md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound || md2_playermodels[(skin_t*)spr->mobj->skin-skins].scale < 0.0f))
HWR_DrawSprite(spr);
else
{
if (!HWR_DrawModel(spr))
HWR_DrawSprite(spr);
}
}
else
{
if (LIKELY(!cv_glmodels.value || md2_models[spr->mobj->sprite].notfound || md2_models[spr->mobj->sprite].scale < 0.0f))
HWR_DrawSprite(spr);
else
{
if (!HWR_DrawModel(spr))
HWR_DrawSprite(spr);
}
}
}
}
HWD.pfnSetSpecialState(HWD_SET_MODEL_LIGHTING, 0);
// At the end of sprite drawing, draw shapes of linkdraw sprites to z-buffer, so they
// don't get drawn over by transparent surfaces.
HWR_LinkDrawHackFinish();
// Work around a r_opengl.c bug with PF_Invisible by making this SetBlend call
// where PF_Invisible is off and PF_Masked is on.
// (Other states probably don't matter. Here I left them same as in LinkDrawHackFinish)
// Without this workaround the rest of the draw calls in this frame (including UI, screen texture)
// can get drawn using an incorrect glBlendFunc, resulting in a occasional black screen.
HWD.pfnSetBlend(PF_Translucent|PF_Occlude|PF_Masked);
}
// --------------------------------------------------------------------------
// HWR_AddSprites
// During BSP traversal, this adds sprites by sector.
// --------------------------------------------------------------------------
static UINT8 sectorlight;
static void HWR_AddSprites(sector_t *sec)
{
mobj_t *thing;
fixed_t limit_dist;
// BSP is traversed by subsector.
// A sector might have been split into several
// subsectors during BSP building.
// Thus we check whether its already added.
if (sec->validcount == validcount)
return;
// Well, now it will be done.
sec->validcount = validcount;
// sprite lighting
sectorlight = sec->lightlevel & 0xff;
// Handle all things in sector.
// If a limit exists, handle things a tiny bit different.
limit_dist = (fixed_t)(cv_drawdist.value) * mapobjectscale;
for (thing = sec->thinglist; thing; thing = thing->snext)
{
if (R_ThingWithinDist(thing, limit_dist))
{
if (R_ThingVisible(thing))
{
HWR_ProjectSprite(thing);
}
HWR_ProjectBoundingBox(thing);
}
}
}
#ifdef HWPRECIP
// --------------------------------------------------------------------------
// HWR_AddPrecipitationSprites
// This renders through the blockmap instead of BSP to avoid
// iterating a huge amount of precipitation sprites in sectors
// that are beyond drawdist.
// --------------------------------------------------------------------------
static void HWR_AddPrecipitationSprites(void)
{
const fixed_t drawdist = cv_drawdist_precip.value * mapobjectscale;
INT32 xl, xh, yl, yh, bx, by;
precipmobj_t *th;
// no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off
if (drawdist == 0)
{
return;
}
// No weather to draw so save some time by skipping blockmap iterations
if (curWeather == PRECIP_NONE || curWeather == PRECIP_STORM_NORAIN)
{
return;
}
R_GetRenderBlockMapDimensions(drawdist, &xl, &xh, &yl, &yh);
for (bx = xl; bx <= xh; bx++)
{
for (by = yl; by <= yh; by++)
{
for (th = precipblocklinks[(by * bmapwidth) + bx]; th; th = th->bnext)
{
if (R_PrecipThingVisible(th))
{
HWR_ProjectPrecipitationSprite(th);
}
}
}
}
}
#endif
// --------------------------------------------------------------------------
// HWR_ProjectSprite
// Generates a vissprite for a thing if it might be visible.
// --------------------------------------------------------------------------
// BP why not use xtoviexangle/viewangletox like in bsp ?....
static void HWR_ProjectSprite(mobj_t *thing)
{
gl_vissprite_t *vis;
float tr_x, tr_y;
float tz;
float tracertz = 0.0f;
float x1, x2;
float rightsin, rightcos;
float this_scale, this_xscale, this_yscale;
fixed_t highresscale;
float spritexscale, spriteyscale;
float shadowheight = 1.0f, shadowscale = 1.0f;
float gz, gzt;
spritedef_t *sprdef;
spriteframe_t *sprframe;
#ifdef ROTSPRITE
spriteinfo_t *sprinfo;
#endif
md2_t *md2;
size_t lumpoff;
unsigned rot;
UINT16 flip;
boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(thing));
boolean mirrored = thing->mirrored;
boolean hflip = (!R_ThingHorizontallyFlipped(thing) != !mirrored);
INT32 dispoffset;
angle_t ang;
INT32 heightsec, phs;
const boolean splat = R_ThingIsFloorSprite(thing);
const boolean papersprite = (R_ThingIsPaperSprite(thing) && !splat);
float z1, z2;
fixed_t spr_width, spr_height;
fixed_t spr_offset, spr_topoffset;
#ifdef ROTSPRITE
patch_t *rotsprite = NULL;
INT32 rollangle = 0;
angle_t spriterotangle = 0;
vector2_t visoffs, rotoffset;
#endif
// uncapped/interpolation
interpmobjstate_t interp = {0};
mobj_t *interptarg;
if (!thing)
return;
interptarg = thing;
if (R_IsOverlayingInvinciblePlayer(thing))
{
// Kill overlay misalignment
interptarg = thing->target;
}
R_InterpolateMobjState(interptarg, R_GetTimeFrac(RTF_LEVEL), &interp);
dispoffset = thing->dispoffset;
// sprite offset
interp.x += thing->sprxoff;
interp.y += thing->spryoff;
interp.z += thing->sprzoff;
if (interp.spritexscale < 1 || interp.spriteyscale < 1)
return;
this_scale = FIXED_TO_FLOAT(interp.scale);
spritexscale = FIXED_TO_FLOAT(interp.spritexscale);
spriteyscale = FIXED_TO_FLOAT(interp.spriteyscale);
// transform the origin point
tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx;
tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy;
// rotation around vertical axis
tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin);
// thing is behind view plane?
if (tz < ZCLIP_PLANE && !(papersprite || splat))
{
if (cv_glmodels.value) //Yellow: Only MD2's dont disappear
{
if (thing->skin && thing->sprite == SPR_PLAY)
md2 = &md2_playermodels[( (skin_t *)thing->skin - skins )];
else
md2 = &md2_models[thing->sprite];
if (md2->notfound || md2->scale < 0.0f)
return;
}
else
return;
}
// The above can stay as it works for cutting sprites that are too close
tr_x = FIXED_TO_FLOAT(interp.x);
tr_y = FIXED_TO_FLOAT(interp.y);
// decide which patch to use for sprite relative to player
#ifdef RANGECHECK
if ((unsigned)thing->sprite >= numsprites)
I_Error("HWR_ProjectSprite: invalid sprite number %i ", thing->sprite);
#endif
rot = thing->frame&FF_FRAMEMASK;
//Fab : 02-08-98: 'skin' override spritedef currently used for skin
if (thing->skin && thing->sprite == SPR_PLAY)
{
sprdef = &((skin_t *)thing->skin)->sprites[thing->sprite2];
#ifdef ROTSPRITE
sprinfo = &((skin_t *)thing->skin)->sprinfo[thing->sprite2];
#endif
}
else if (thing->sprite == SPR_ITEM)
{
sprdef = &kartitems[thing->threshold > 0 && thing->threshold < numkartitems ? thing->threshold : 0].spritedef;
#ifdef ROTSPRITE
sprinfo = &spriteinfo[SPR_UNKN];
#endif
}
else
{
sprdef = &sprites[thing->sprite];
#ifdef ROTSPRITE
sprinfo = &spriteinfo[thing->sprite];
#endif
}
if (rot >= sprdef->numframes)
{
CONS_Alert(CONS_ERROR, M_GetText("HWR_ProjectSprite: invalid sprite frame %s/%s for %s\n"),
sizeu1(rot), sizeu2(sprdef->numframes), sprnames[thing->sprite]);
thing->sprite = states[S_UNKNOWN].sprite;
thing->frame = states[S_UNKNOWN].frame;
sprdef = &sprites[thing->sprite];
#ifdef ROTSPRITE
sprinfo = &spriteinfo[thing->sprite];
#endif
rot = thing->frame&FF_FRAMEMASK;
thing->state->sprite = thing->sprite;
thing->state->frame = thing->frame;
}
sprframe = &sprdef->spriteframes[rot];
#ifdef PARANOIA
if (!sprframe)
I_Error("sprframes NULL for sprite %d\n", thing->sprite);
#endif
ang = R_PointToAngle (interp.x, interp.y) - interp.angle;
if (mirrored)
ang = InvAngle(ang);
if (sprframe->rotate == SRF_SINGLE)
{
// use single rotation for all views
rot = 0; //Fab: for vis->patch below
lumpoff = sprframe->lumpid[0]; //Fab: see note above
flip = sprframe->flip; // Will only be 0x00 or 0xFF
if (papersprite && ang < ANGLE_180)
flip ^= 0xFFFF;
}
else
{
// choose a different rotation based on player view
if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right
rot = 6; // F7 slot
else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left
rot = 2; // F3 slot
else if (sprframe->rotate & SRF_3DGE) // 16-angle mode
{
rot = (ang+ANGLE_180+ANGLE_11hh)>>28;
rot = ((rot & 1)<<3)|(rot>>1);
}
else // Normal behaviour
rot = (ang+ANGLE_202h)>>29;
//Fab: lumpid is the index for spritewidth,spriteoffset... tables
lumpoff = sprframe->lumpid[rot];
flip = sprframe->flip & (1<<rot);
if (papersprite && ang < ANGLE_180)
flip ^= (1<<rot);
}
if (thing->skin && ((skin_t *)thing->skin)->highresscale != FRACUNIT)
{
float hi_res = ((skin_t *)thing->skin)->highresscale;
this_scale *= FIXED_TO_FLOAT(hi_res);
highresscale = hi_res;
}
else
{
highresscale = FRACUNIT;
}
spr_width = spritecachedinfo[lumpoff].width;
spr_height = spritecachedinfo[lumpoff].height;
spr_offset = spritecachedinfo[lumpoff].offset;
spr_topoffset = spritecachedinfo[lumpoff].topoffset;
#ifdef ROTSPRITE
spriterotangle = R_SpriteRotationAngle(thing, NULL, &interp);
if (spriterotangle != 0
&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
{
rollangle = R_GetRollAngle(papersprite == vflip
? spriterotangle : InvAngle(spriterotangle));
rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
if (rotsprite != NULL)
{
spr_width = rotsprite->width << FRACBITS;
spr_height = rotsprite->height << FRACBITS;
spr_offset = rotsprite->leftoffset << FRACBITS;
spr_topoffset = rotsprite->topoffset << FRACBITS;
spr_topoffset += FEETADJUST;
// flip -> rotate, not rotate -> flip
flip = 0;
}
}
// initialize and rotate pitch/roll vectors
visoffs.x = 0;
visoffs.y = 0;
rotoffset.x = 0;
rotoffset.y = 0;
const fixed_t visoffymul = (vflip ? -FRACUNIT : FRACUNIT);
if (R_ThingIsUsingBakedOffsets(interptarg))
{
R_RotateSpriteOffsetsByPitchRoll(interptarg,
vflip,
hflip,
&interp,
&visoffs,
&rotoffset);
rotoffset.x *= FRACUNIT;
}
#endif
if (thing->renderflags & RF_ABSOLUTEOFFSETS)
{
spr_offset = FixedDiv(interp.spritexoffset,highresscale);
#ifdef ROTSPRITE
spr_topoffset = (FixedDiv(interp.spriteyoffset,highresscale) + FixedDiv((visoffs.y * visoffymul), mapobjectscale) + (rotoffset.y * visoffymul));
#else
spr_topoffset = FixedDiv(interp.spriteyoffset,highresscale);
#endif
}
else
{
SINT8 flipoffset = 1;
if ((thing->renderflags & RF_FLIPOFFSETS) && flip)
flipoffset = -1;
spr_offset += FixedDiv(interp.spritexoffset,highresscale) * flipoffset;
#ifdef ROTSPRITE
spr_topoffset += (FixedDiv(interp.spriteyoffset,highresscale) + FixedDiv((visoffs.y * visoffymul),
mapobjectscale) + (rotoffset.y * visoffymul)) * flipoffset;
#else
spr_topoffset += FixedDiv(interp.spriteyoffset,highresscale) * flipoffset;
#endif
}
if (papersprite)
{
rightsin = FIXED_TO_FLOAT(FINESINE(interp.angle >> ANGLETOFINESHIFT));
rightcos = FIXED_TO_FLOAT(FINECOSINE(interp.angle >> ANGLETOFINESHIFT));
}
else
{
rightsin = FIXED_TO_FLOAT(FINESINE((viewangle + ANGLE_90)>>ANGLETOFINESHIFT));
rightcos = FIXED_TO_FLOAT(FINECOSINE((viewangle + ANGLE_90)>>ANGLETOFINESHIFT));
}
flip = !flip != !hflip;
if (thing->renderflags & RF_SHADOWEFFECTS)
{
mobj_t *caster = thing->target;
if (caster && !P_MobjWasRemoved(caster))
{
interpmobjstate_t casterinterp = {0};
fixed_t groundz;
fixed_t floordiff;
R_InterpolateMobjState(caster, R_GetTimeFrac(RTF_LEVEL), &casterinterp);
groundz = R_GetShadowZ(thing, NULL);
floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? caster->height : 0) + casterinterp.z - groundz);
shadowheight = FIXED_TO_FLOAT(floordiff);
shadowscale = FIXED_TO_FLOAT(FixedMul(FRACUNIT - floordiff/640, casterinterp.scale));
if (splat)
spritexscale *= shadowscale;
spriteyscale *= shadowscale;
}
}
this_xscale = spritexscale * this_scale;
this_yscale = spriteyscale * this_scale;
if (splat)
{
z1 = z2 = tr_y;
x1 = x2 = tr_x;
gz = gzt = interp.z;
}
else
{
#ifdef ROTSPRITE
if (visoffs.x)
{
visoffs.x = (FixedDiv((visoffs.x * FRACUNIT), mapobjectscale));
}
#endif
if (flip)
{
#ifdef ROTSPRITE
spr_offset -= visoffs.x;
spr_offset -= rotoffset.x;
#endif
x1 = (FIXED_TO_FLOAT((spr_width - spr_offset)) * this_xscale);
x2 = (FIXED_TO_FLOAT(spr_offset) * this_xscale);
}
else
{
#ifdef ROTSPRITE
spr_offset += visoffs.x;
spr_offset += rotoffset.x;
#endif
x1 = (FIXED_TO_FLOAT(spr_offset) * this_xscale);
x2 = (FIXED_TO_FLOAT(spr_width - spr_offset) * this_xscale);
}
// test if too close
/*
if (papersprite)
{
z1 = tz - x1 * angle_scalez;
z2 = tz + x2 * angle_scalez;
if (max(z1, z2) < ZCLIP_PLANE)
return;
}
*/
z1 = tr_y + x1 * rightsin;
z2 = tr_y - x2 * rightsin;
x1 = tr_x + x1 * rightcos;
x2 = tr_x - x2 * rightcos;
if (thing->terrain && thing->terrain->floorClip)
spr_topoffset -= thing->terrain->floorClip;
if (vflip)
{
gz = FIXED_TO_FLOAT(interp.z + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
gzt = gz + (FIXED_TO_FLOAT(spr_height) * this_yscale);
}
else
{
gzt = FIXED_TO_FLOAT(interp.z) + (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
gz = gzt - (FIXED_TO_FLOAT(spr_height) * this_yscale);
}
}
if (thing->subsector->sector->cullheight)
{
if (HWR_DoCulling(thing->subsector->sector->cullheight, viewsector->cullheight, gl_viewz, gz, gzt))
return;
}
heightsec = thing->subsector->sector->heightsec;
if (viewplayer && viewplayer->mo && viewplayer->mo->subsector)
phs = viewplayer->mo->subsector->sector->heightsec;
else
phs = -1;
if (heightsec != -1 && phs != -1) // only clip things which are in special sectors
{
fixed_t secheight;
const fixed_t fgzt = FloatToFixed(gzt);
secheight = P_GetSectorFloorZAt(&sectors[heightsec], viewx, viewy);
if (viewz < P_GetSectorFloorZAt(&sectors[phs], interp.x, interp.y) ?
interp.z >= secheight :
fgzt < secheight)
return;
secheight = P_GetSectorCeilingZAt(&sectors[heightsec], viewx, viewy);
if (viewz > P_GetSectorCeilingZAt(&sectors[phs], interp.x, interp.y) ?
fgzt < secheight && viewz >= secheight :
interp.z >= secheight)
return;
}
if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer)
{
interpmobjstate_t tracer_interp = {0};
if (! R_ThingVisible(thing->tracer))
return;
R_InterpolateMobjState(thing->tracer, R_GetTimeFrac(RTF_LEVEL), &tracer_interp);
// calculate tz for tracer, same way it is calculated for this sprite
// transform the origin point
tr_x = FIXED_TO_FLOAT(tracer_interp.x) - gl_viewx;
tr_y = FIXED_TO_FLOAT(tracer_interp.y) - gl_viewy;
// rotation around vertical axis
tracertz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin);
// Software does not render the linkdraw sprite if the tracer is behind the view plane,
// so do the same check here.
// NOTE: This check has the same flaw as the view plane check at the beginning of HWR_ProjectSprite:
// the view aiming angle is not taken into account, leading to sprites disappearing too early when they
// can still be seen when looking down/up at steep angles.
if (tracertz < ZCLIP_PLANE)
return;
// if the sprite is behind the tracer, invert dispoffset, putting the sprite behind the tracer
if (tz > tracertz)
dispoffset *= -1;
}
// store information in a vissprite
vis = HWR_NewVisSprite();
vis->x1 = x1;
vis->x2 = x2;
vis->z1 = z1;
vis->z2 = z2;
vis->tz = tz; // Keep tz for the simple sprite sorting that happens
vis->tracertz = tracertz;
vis->renderflags = thing->renderflags;
vis->rotateflags = sprframe->rotate;
vis->shadowheight = shadowheight;
vis->shadowscale = shadowscale;
vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15: HARDWARE SUPPORT AT LAST
vis->flip = flip;
vis->scale = this_scale;
vis->spritexscale = spritexscale;
vis->spriteyscale = spriteyscale;
vis->spritexoffset = FIXED_TO_FLOAT(spr_offset);
vis->spriteyoffset = FIXED_TO_FLOAT(spr_topoffset);
vis->rotated = false;
#ifdef ROTSPRITE
if (rotsprite)
{
vis->gpatch = (patch_t *)rotsprite;
vis->rotated = true;
}
else
#endif
vis->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
vis->mobj = thing;
//Hurdler: 25/04/2000: now support colormap in hardware mode
/*
else if ((vis->mobj->flags & (MF_ENEMY|MF_BOSS)) && (vis->mobj->flags2 & MF2_FRET) && !(vis->mobj->flags & MF_GRENADEBOUNCE) && (leveltime & 1)) // Bosses "flash"
{
if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized)
vis->colormap = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
else if (vis->mobj->type == MT_METALSONIC_BATTLE)
vis->colormap = R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE);
else
vis->colormap = R_GetTranslationColormap(TC_BOSS, 0, GTC_CACHE);
}
*/
if (thing->color)
{
// New colormap stuff for skins Tails 06-07-2002
if (thing->colorized)
{
vis->colormap = R_GetTranslationColormap(
R_IsOverlayingInvinciblePlayer(thing) ? TC_BLINK : TC_RAINBOW,
thing->color,
GTC_CACHE);
}
else if (thing->skin && thing->sprite == SPR_PLAY) // This thing is a player!
{
size_t skinnum = (skin_t*)thing->skin-skins;
vis->colormap = R_GetTranslationColormap((INT32)skinnum, thing->color, GTC_CACHE);
}
else
{
vis->colormap = R_GetTranslationColormap(TC_DEFAULT, vis->mobj->color ? vis->mobj->color : SKINCOLOR_GREEN, GTC_CACHE);
}
}
else
{
vis->colormap = colormaps;
if (encoremap && !(thing->flags & MF_DONTENCOREMAP))
vis->colormap += COLORMAP_REMAPOFFSET;
}
// set top/bottom coords
vis->gzt = gzt;
vis->gz = gz;
//CONS_Debug(DBG_RENDER, "------------------\nH: sprite : %d\nH: frame : %x\nH: type : %d\nH: sname : %s\n\n",
// thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]);
vis->vflip = vflip;
vis->precip = false;
vis->bbox = false;
vis->angle = interp.angle;
}
#ifdef HWPRECIP
// Precipitation projector for hardware mode
static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
{
gl_vissprite_t *vis;
float tr_x, tr_y;
float tz;
float x1, x2;
float z1, z2;
float rightsin, rightcos;
float this_scale;
spritedef_t *sprdef;
spriteframe_t *sprframe;
size_t lumpoff;
unsigned rot = 0;
UINT8 flip;
if (!thing)
return;
// uncapped/interpolation
interpmobjstate_t interp = {0};
// okay... this is a hack, but weather isn't networked, so it should be ok
if (!P_PrecipThinker(thing))
{
return;
}
// do interpolation
R_InterpolatePrecipMobjState(thing, R_GetTimeFrac(RTF_LEVEL), &interp);
this_scale = FIXED_TO_FLOAT(interp.scale);
// transform the origin point
tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx;
tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy;
// rotation around vertical axis
tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin);
// thing is behind view plane?
if (tz < ZCLIP_PLANE)
return;
tr_x = FIXED_TO_FLOAT(interp.x);
tr_y = FIXED_TO_FLOAT(interp.y);
// decide which patch to use for sprite relative to player
if ((unsigned)thing->sprite >= numsprites)
{
CONS_Debug(DBG_RENDER, "R_ProjectPrecipitationSprite: invalid sprite number %d\n",
thing->sprite);
return;
}
sprdef = &sprites[thing->sprite];
if ((UINT8)(thing->frame&FF_FRAMEMASK) >= sprdef->numframes)
{
CONS_Debug(DBG_RENDER, "R_ProjectPrecipitationSprite: invalid sprite frame %d : %d for %s\n",
thing->sprite, thing->frame, sprnames[thing->sprite]);
return;
}
sprframe = &sprdef->spriteframes[ thing->frame & FF_FRAMEMASK];
if (!sprframe)
#ifdef PARANOIA
I_Error("R_ProjectPrecipitationSprite: sprframes NULL for sprite %d\n", thing->sprite);
#else
return;
#endif
// use single rotation for all views
lumpoff = sprframe->lumpid[0];
flip = sprframe->flip; // Will only be 0x00 or 0xFF
rightsin = FIXED_TO_FLOAT(FINESINE((viewangle + ANGLE_90)>>ANGLETOFINESHIFT));
rightcos = FIXED_TO_FLOAT(FINECOSINE((viewangle + ANGLE_90)>>ANGLETOFINESHIFT));
if (flip)
{
x1 = FIXED_TO_FLOAT(spritecachedinfo[lumpoff].width - spritecachedinfo[lumpoff].offset);
x2 = FIXED_TO_FLOAT(spritecachedinfo[lumpoff].offset);
}
else
{
x1 = FIXED_TO_FLOAT(spritecachedinfo[lumpoff].offset);
x2 = FIXED_TO_FLOAT(spritecachedinfo[lumpoff].width - spritecachedinfo[lumpoff].offset);
}
x1 *= this_scale;
x2 *= this_scale;
z1 = tr_y + x1 * rightsin;
z2 = tr_y - x2 * rightsin;
x1 = tr_x + x1 * rightcos;
x2 = tr_x - x2 * rightcos;
//
// store information in a vissprite
//
vis = HWR_NewVisSprite();
vis->x1 = x1;
vis->x2 = x2;
vis->z1 = z1;
vis->z2 = z2;
vis->tz = tz;
vis->dispoffset = 0; // Monster Iestyn: 23/11/15: HARDWARE SUPPORT AT LAST
vis->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
vis->flip = flip;
vis->mobj = (mobj_t *)thing;
vis->colormap = NULL;
if (encoremap && !(thing->flags & MF_DONTENCOREMAP))
vis->colormap += COLORMAP_REMAPOFFSET;
// set top/bottom coords
vis->gzt = FIXED_TO_FLOAT(interp.z) + (FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale);
vis->gz = vis->gzt - (FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale);
vis->precip = true;
vis->bbox = false;
}
#endif
static void HWR_ProjectBoundingBox(mobj_t *thing)
{
gl_vissprite_t *vis;
float tr_x, tr_y;
float tz;
float rad;
// uncapped/interpolation
interpmobjstate_t interp = {0};
if (!thing)
return;
if (!R_ThingBoundingBoxVisible(thing))
return;
R_InterpolateMobjState(thing, R_GetTimeFrac(RTF_LEVEL), &interp);
// transform the origin point
tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx;
tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy;
// rotation around vertical axis
tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin);
// thing is behind view plane?
if (tz < ZCLIP_PLANE)
return;
tr_x += gl_viewx;
tr_y += gl_viewy;
rad = FIXED_TO_FLOAT(thing->radius);
vis = HWR_NewVisSprite();
vis->x1 = tr_x - rad;
vis->x2 = tr_x + rad;
vis->z1 = tr_y - rad;
vis->z2 = tr_y + rad;
vis->gz = FIXED_TO_FLOAT(interp.z);
vis->gzt = vis->gz + FIXED_TO_FLOAT(thing->height);
vis->mobj = thing;
vis->precip = false;
vis->bbox = true;
}
// ==========================================================================
// Sky dome rendering, ported from PrBoom+
// ==========================================================================
static gl_sky_t gl_sky;
static void HWR_SkyDomeVertex(gl_sky_t *sky, gl_skyvertex_t *vbo, int r, int c, signed char yflip, float delta, boolean foglayer)
{
const float radians = (float)(M_PIl / 180.0f);
const float scale = 10000.0f;
const float maxSideAngle = 60.0f;
float topAngle = (c / (float)sky->columns * 360.0f);
float sideAngle = (maxSideAngle * (sky->rows - r) / sky->rows);
float height = (float)(sin(sideAngle * radians));
float realRadius = (float)(scale * cos(sideAngle * radians));
float x = (float)(realRadius * cos(topAngle * radians));
float y = (!yflip) ? scale * height : -scale * height;
float z = (float)(realRadius * sin(topAngle * radians));
float timesRepeat = (4 * (256.0f / sky->width));
if (fpclassify(timesRepeat) == FP_ZERO)
timesRepeat = 1.0f;
if (!foglayer)
{
vbo->r = 255;
vbo->g = 255;
vbo->b = 255;
vbo->a = (r == 0 ? 0 : 255);
// And the texture coordinates.
vbo->u = (-timesRepeat * c / (float)sky->columns);
if (!yflip) // Flipped Y is for the lower hemisphere.
vbo->v = (r / (float)sky->rows) + 0.5f;
else
vbo->v = 1.0f + ((sky->rows - r) / (float)sky->rows) + 0.5f;
}
if (r != 4)
y += 300.0f;
// And finally the vertex.
vbo->x = x;
vbo->y = y + delta;
vbo->z = z;
}
// Clears the sky dome.
void HWR_ClearSkyDome(void)
{
gl_sky_t *sky = &gl_sky;
if (sky->loops)
free(sky->loops);
if (sky->data)
free(sky->data);
sky->loops = NULL;
sky->data = NULL;
sky->vbo = 0;
sky->rows = sky->columns = 0;
sky->loopcount = 0;
sky->detail = 0;
sky->texture = -1;
sky->width = sky->height = 0;
sky->rebuild = true;
}
void HWR_BuildSkyDome(void)
{
int c, r;
signed char yflip;
int row_count = 4;
int col_count = 4;
float delta;
gl_sky_t *sky = &gl_sky;
gl_skyvertex_t *vertex_p;
texture_t *texture = textures[texturetranslation[skytexture]];
sky->detail = 16;
col_count *= sky->detail;
if ((sky->columns != col_count) || (sky->rows != row_count))
HWR_ClearSkyDome();
sky->columns = col_count;
sky->rows = row_count;
sky->vertex_count = 2 * sky->rows * (sky->columns * 2 + 2) + sky->columns * 2;
if (!sky->loops)
sky->loops = malloc((sky->rows * 2 + 2) * sizeof(sky->loops[0]));
// create vertex array
if (!sky->data)
sky->data = malloc(sky->vertex_count * sizeof(sky->data[0]));
sky->texture = texturetranslation[skytexture];
sky->width = texture->width;
sky->height = texture->height;
vertex_p = &sky->data[0];
sky->loopcount = 0;
for (yflip = 0; yflip < 2; yflip++)
{
sky->loops[sky->loopcount].mode = HWD_SKYLOOP_FAN;
sky->loops[sky->loopcount].vertexindex = vertex_p - &sky->data[0];
sky->loops[sky->loopcount].vertexcount = col_count;
sky->loops[sky->loopcount].use_texture = false;
sky->loopcount++;
for (c = 0; c < col_count; c++)
{
HWR_SkyDomeVertex(sky, vertex_p, 1, c, yflip, 0.0f, true);
vertex_p->r = 255;
vertex_p->g = 255;
vertex_p->b = 255;
vertex_p->a = 255;
vertex_p++;
}
delta = (yflip ? 5.0f : -5.0f) / 128.0f;
for (r = 0; r < row_count; r++)
{
sky->loops[sky->loopcount].mode = HWD_SKYLOOP_STRIP;
sky->loops[sky->loopcount].vertexindex = vertex_p - &sky->data[0];
sky->loops[sky->loopcount].vertexcount = 2 * col_count + 2;
sky->loops[sky->loopcount].use_texture = true;
sky->loopcount++;
for (c = 0; c <= col_count; c++)
{
HWR_SkyDomeVertex(sky, vertex_p++, r + (yflip ? 1 : 0), (c ? c : 0), yflip, delta, false);
HWR_SkyDomeVertex(sky, vertex_p++, r + (yflip ? 0 : 1), (c ? c : 0), yflip, delta, false);
}
}
}
}
static void HWR_DrawSkyBackground(player_t *player)
{
UINT8 viewnum = R_GetViewNumber();
camera_t *thiscam = &camera[viewnum];
HWD.pfnSetBlend(PF_Translucent|PF_NoDepthTest|PF_Modulated);
if (cv_glskydome.value)
{
FTransform dometransform;
const float fpov = FIXED_TO_FLOAT(cv_fov[viewssnum].value+player->fovadd);
memset(&dometransform, 0x00, sizeof(FTransform));
//04/01/2000: Hurdler: added for T&L
// It should replace all other gl_viewxxx when finished
HWR_SetTransformAiming(&dometransform, player, false);
dometransform.angley = (float)((viewangle-ANGLE_270)>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
dometransform.flip = false;
if ((thiscam->postimgflags & POSTIMG_FLIP) && !(thiscam->postimgflags & POSTIMG_MIRROR))
dometransform.flip = true;
dometransform.mirror = false;
if ((thiscam->postimgflags & POSTIMG_MIRROR) && !(thiscam->postimgflags & POSTIMG_FLIP))
dometransform.mirror = true;
dometransform.mirrorflip = false;
if ((thiscam->postimgflags & POSTIMG_FLIP) && (thiscam->postimgflags & POSTIMG_MIRROR))
dometransform.mirrorflip = true;
dometransform.scalex = 1;
dometransform.scaley = (float)vid.width/vid.height;
dometransform.scalez = 1;
dometransform.fovxangle = fpov; // Tails
dometransform.fovyangle = fpov; // Tails
HWR_RollTransform(&dometransform, viewroll);
dometransform.splitscreen = r_splitscreen;
HWR_GetTexture(texturetranslation[skytexture], false);
if (gl_sky.texture != texturetranslation[skytexture])
{
HWR_ClearSkyDome();
HWR_BuildSkyDome();
}
if (HWR_UseShader())
HWD.pfnSetShader(HWR_GetShaderFromTarget(SHADER_SKY));
HWD.pfnSetTransform(&dometransform);
HWD.pfnRenderSkyDome(&gl_sky);
}
else
{
FOutVector v[4];
angle_t angle;
float dimensionmultiply;
float aspectratio;
float angleturn;
HWR_GetTexture(texturetranslation[skytexture], false);
aspectratio = (float)vid.width/(float)vid.height;
//Hurdler: the sky is the only texture who need 4.0f instead of 1.0
// because it's called just after clearing the screen
// and thus, the near clipping plane is set to 3.99
// Sryder: Just use the near clipping plane value then
// 3--2
// | /|
// |/ |
// 0--1
v[0].x = v[3].x = -ZCLIP_PLANE-1;
v[1].x = v[2].x = ZCLIP_PLANE+1;
v[0].y = v[1].y = -ZCLIP_PLANE-1;
v[2].y = v[3].y = ZCLIP_PLANE+1;
v[0].z = v[1].z = v[2].z = v[3].z = ZCLIP_PLANE+1;
// X
// NOTE: This doesn't work right with texture widths greater than 1024
// software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly
// The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture
angle = (dup_viewangle + gl_xtoviewangle[0]);
dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f);
v[0].s = v[3].s = (-1.0f * angle) / (((float)ANGLE_90-1.0f)*dimensionmultiply); // left
v[2].s = v[1].s = v[0].s + (1.0f/dimensionmultiply); // right (or left + 1.0f)
// use +angle and -1.0f above instead if you wanted old backwards behavior
// Y
angle = aimingangle;
dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio));
if (r_splitscreen == 1)
{
dimensionmultiply *= 2;
angle *= 2;
}
// Middle of the sky should always be at angle 0
// need to keep correct aspect ratio with X
if (atransform.flip)
{
// During vertical flip the sky should be flipped and it's y movement should also be flipped obviously
v[3].t = v[2].t = -(0.5f-(0.5f/dimensionmultiply)); // top
v[0].t = v[1].t = v[3].t - (1.0f/dimensionmultiply); // bottom (or top - 1.0f)
}
else
{
v[0].t = v[1].t = -(0.5f-(0.5f/dimensionmultiply)); // bottom
v[3].t = v[2].t = v[0].t - (1.0f/dimensionmultiply); // top (or bottom - 1.0f)
}
angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply;
if (angle > ANGLE_180) // Do this because we don't want the sky to suddenly teleport when crossing over 0 to 360 and vice versa
{
angle = InvAngle(angle);
v[3].t = v[2].t += ((float) angle / angleturn);
v[0].t = v[1].t += ((float) angle / angleturn);
}
else
{
v[3].t = v[2].t -= ((float) angle / angleturn);
v[0].t = v[1].t -= ((float) angle / angleturn);
}
HWD.pfnUnSetShader();
HWD.pfnDrawPolygon(NULL, v, 4, 0);
}
}
// -----------------+
// HWR_ClearView : clear the viewwindow, with maximum z value
// -----------------+
static inline void HWR_ClearView(void)
{
// 3--2
// | /|
// |/ |
// 0--1
/// \bug faB - enable depth mask, disable color mask
HWD.pfnGClipRect((INT32)gl_viewwindowx,
(INT32)gl_viewwindowy,
(INT32)(gl_viewwindowx + gl_viewwidth),
(INT32)(gl_viewwindowy + gl_viewheight),
ZCLIP_PLANE);
HWD.pfnClearBuffer(false, true, 0);
//disable clip window - set to full size
// rem by Hurdler
// HWD.pfnGClipRect(0, 0, vid.width, vid.height);
}
// -----------------+
// HWR_SetViewSize : set projection and scaling values
// -----------------+
void HWR_SetViewSize(void)
{
// setup view size
gl_viewwidth = (float)vid.width;
gl_viewheight = (float)vid.height;
if (r_splitscreen > 0)
{
gl_viewheight /= 2;
if (r_splitscreen > 1)
{
gl_viewwidth /= 2;
}
}
gl_basecenterx = gl_viewwidth / 2;
gl_basecentery = gl_viewheight / 2;
gl_baseviewwindowx = 0;
gl_basewindowcenterx = gl_viewwidth / 2;
gl_baseviewwindowy = 0;
gl_basewindowcentery = gl_viewheight / 2;
gl_pspritexscale = gl_viewwidth / BASEVIDWIDTH;
gl_pspriteyscale = ((vid.height*gl_pspritexscale*BASEVIDWIDTH)/BASEVIDHEIGHT)/vid.width;
HWD.pfnFlushScreenTextures();
}
// -------------------+
// HWR_ShiftViewPort : offset viewport according to current split
// -------------------+
static void HWR_ShiftViewPort(void)
{
gl_centerx = gl_basecenterx;
gl_viewwindowx = gl_baseviewwindowx;
gl_windowcenterx = gl_basewindowcenterx;
gl_centery = gl_basecentery;
gl_viewwindowy = gl_baseviewwindowy;
gl_windowcentery = gl_basewindowcentery;
if (viewssnum > ( r_splitscreen > 1 ))
{
gl_viewwindowy += gl_viewheight;
gl_windowcentery += gl_viewheight;
}
if (r_splitscreen > 1 && viewssnum & 1)
{
gl_viewwindowx += gl_viewwidth;
gl_windowcenterx += gl_viewwidth;
}
}
// Set view aiming, for the sky dome, the skybox,
// and the normal view, all with a single function.
static void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox)
{
// 1 = always on
// 2 = chasecam only
if (cv_glshearing.value == 1 || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox)))
{
fixed_t fixedaiming = AIMINGTODY(aimingangle);
trans->viewaiming = FIXED_TO_FLOAT(fixedaiming);
trans->shearing = true;
gl_aimingangle = 0;
}
else
{
trans->shearing = false;
gl_aimingangle = aimingangle;
}
trans->anglex = (float)(gl_aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
}
//
// Sets the shader state.
//
static void HWR_SetShaderState(void)
{
HWD.pfnSetSpecialState(HWD_SET_SHADERS, (INT32)HWR_UseShader());
}
static void HWR_ClearClipper(void)
{
angle_t a1 = gld_FrustumAngle(gl_aimingangle);
gld_clipper_Clear();
gld_clipper_SafeAddClipRange(viewangle + a1, viewangle - a1);
#ifdef HAVE_SPHEREFRUSTRUM
gld_FrustrumSetup();
#endif
}
// ==========================================================================
// Same as rendering the player view, but from the skybox object
// ==========================================================================
void HWR_RenderSkyboxView(player_t *player)
{
UINT8 viewnum = R_GetViewNumber();
camera_t *thiscam = &camera[viewnum];
const float fpov = FIXED_TO_FLOAT(cv_fov[viewssnum].value+player->fovadd);
if (!HWR_ShouldUsePaletteRendering())
{
// do we really need to save player (is it not the same)?
player_t *saved_player = stplyr;
stplyr = player;
ST_doPaletteStuff();
stplyr = saved_player;
#ifdef ALAM_LIGHTING
HWR_SetLights(viewssnum);
#endif
}
// note: sets viewangle, viewx, viewy, viewz
R_SkyboxFrame(viewssnum);
// copy view cam position for local use
dup_viewx = viewx;
dup_viewy = viewy;
dup_viewz = viewz;
dup_viewangle = viewangle;
// set window position
HWR_ShiftViewPort();
// check for new console commands.
NetUpdate();
gl_viewx = FIXED_TO_FLOAT(dup_viewx);
gl_viewy = FIXED_TO_FLOAT(dup_viewy);
gl_viewz = FIXED_TO_FLOAT(dup_viewz);
gl_viewsin = FIXED_TO_FLOAT(viewsin);
gl_viewcos = FIXED_TO_FLOAT(viewcos);
//04/01/2000: Hurdler: added for T&L
// It should replace all other gl_viewxxx when finished
memset(&atransform, 0x00, sizeof(FTransform));
HWR_SetTransformAiming(&atransform, player, true);
atransform.angley = (float)(viewangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
gl_viewludsin = FIXED_TO_FLOAT(FINECOSINE(gl_aimingangle>>ANGLETOFINESHIFT));
gl_viewludcos = FIXED_TO_FLOAT(-FINESINE(gl_aimingangle>>ANGLETOFINESHIFT));
atransform.flip = false;
if ((thiscam->postimgflags & POSTIMG_FLIP) && !(thiscam->postimgflags & POSTIMG_MIRROR))
atransform.flip = true;
atransform.mirror = false;
if ((thiscam->postimgflags & POSTIMG_MIRROR) && !(thiscam->postimgflags & POSTIMG_FLIP))
atransform.mirror = true;
atransform.mirrorflip = false;
if ((thiscam->postimgflags & POSTIMG_FLIP) && (thiscam->postimgflags & POSTIMG_MIRROR))
atransform.mirrorflip = true;
atransform.x = gl_viewx; // FIXED_TO_FLOAT(viewx)
atransform.y = gl_viewy; // FIXED_TO_FLOAT(viewy)
atransform.z = gl_viewz; // FIXED_TO_FLOAT(viewz)
atransform.scalex = 1;
atransform.scaley = (float)vid.width/vid.height;
atransform.scalez = 1;
atransform.fovxangle = fpov; // Tails
atransform.fovyangle = fpov; // Tails
HWR_RollTransform(&atransform, viewroll);
atransform.splitscreen = r_splitscreen;
gl_fovlud = (float)(1.0l/tan((double)(fpov*M_PIl/360l)));
//------------------------------------------------------------------------
HWR_ClearView();
HWR_DrawSkyBackground(player);
HWR_ClearSprites();
drawcount = 0;
//04/01/2000: Hurdler: added for T&L
// Actually it only works on Walls and Planes
HWD.pfnSetTransform(&atransform);
HWR_ClearClipper();
// Reset the shader state.
HWR_SetShaderState();
validcount++;
if (LIKELY(cv_glbatching.value))
HWR_StartBatching();
#ifdef HWPRECIP
HWR_AddPrecipitationSprites();
#endif
HWR_RenderBSPNode((INT32)numnodes-1);
if (LIKELY(cv_glbatching.value))
HWR_RenderBatches();
// Check for new console commands.
NetUpdate();
#ifdef ALAM_LIGHTING
//14/11/99: Hurdler: moved here because it doesn't work with
// subsector, see other comments;
HWR_ResetLights();
#endif
// Draw MD2 and sprites
HWR_SortVisSprites();
HWR_DrawSprites();
#ifdef NEWCORONAS
//Hurdler: they must be drawn before translucent planes, what about gl fog?
HWR_DrawCoronas();
#endif
if (numplanes || numpolyplanes || numwalls) //Hurdler: render 3D water and transparent walls after everything
{
HWR_CreateDrawNodes();
}
HWD.pfnSetTransform(NULL);
HWD.pfnUnSetShader();
// Check for new console commands.
NetUpdate();
// added by Hurdler for correct splitscreen
// moved here by hurdler so it works with the new near clipping plane
HWD.pfnGClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
}
// ==========================================================================
//
// ==========================================================================
static void HWR_RollTransform(FTransform *tr, angle_t roll)
{
if (roll != 0)
{
tr->rollangle = roll / (float)ANG1;
tr->roll = true;
tr->rollx = 1.0f;
tr->rollz = 0.0f;
}
}
void HWR_RenderPlayerView(void)
{
player_t * player = &players[displayplayers[viewssnum]];
UINT8 viewnum = R_GetViewNumber();
camera_t *thiscam = &camera[viewnum];
const float fpov = FIXED_TO_FLOAT(cv_fov[viewssnum].value+player->fovadd);
const boolean skybox = (skyboxmo[0] && cv_skybox.value); // True if there's a skybox object and skyboxes are on
FRGBAFloat ClearColor;
ClearColor.red = 0.0f;
ClearColor.green = 0.0f;
ClearColor.blue = 0.0f;
ClearColor.alpha = 1.0f;
if (cv_glshaders.value)
HWD.pfnSetShaderInfo(HWD_SHADERINFO_LEVELTIME, (INT32)leveltime); // The water surface shader needs the leveltime.
if (viewssnum == 0) // Only do it if it's the first screen being rendered
HWD.pfnClearBuffer(true, false, &ClearColor); // Clear the Color Buffer, stops HOMs. Also seems to fix the skybox issue on Intel GPUs.
ps_hw_skyboxtime = I_GetPreciseTime();
if (skybox) // If there's a skybox and we should be drawing the sky, draw the skybox
HWR_RenderSkyboxView(player); // This is drawn before everything else so it is placed behind
ps_hw_skyboxtime = I_GetPreciseTime() - ps_hw_skyboxtime;
{
// do we really need to save player (is it not the same)?
player_t *saved_player = stplyr;
stplyr = player;
ST_doPaletteStuff();
stplyr = saved_player;
#ifdef ALAM_LIGHTING
HWR_SetLights(viewssnum);
#endif
}
// note: sets viewangle, viewx, viewy, viewz
R_SetupFrame(viewssnum, skybox);
framecount++; // timedemo
// copy view cam position for local use
dup_viewx = viewx;
dup_viewy = viewy;
dup_viewz = viewz;
dup_viewangle = viewangle;
// set window position
HWR_ShiftViewPort();
if (r_splitscreen == 2 && player == &players[displayplayers[2]]) // No black void please.
{
// V_DrawPatchFill, but for the fourth screen only
patch_t *gpatch = W_CachePatchName("SRB2BACK", PU_CACHE);
INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
INT32 x, y, pw = SHORT(gpatch->width) * dupz, ph = SHORT(gpatch->height) * dupz;
for (x = vid.width>>1; x < vid.width; x += pw)
{
for (y = vid.height>>1; y < vid.height; y += ph)
HWR_DrawStretchyFixedPatch(gpatch, (x)<<FRACBITS, (y)<<FRACBITS, FRACUNIT, FRACUNIT, V_NOSCALESTART, NULL);
}
}
// check for new console commands.
NetUpdate();
gl_viewx = FIXED_TO_FLOAT(dup_viewx);
gl_viewy = FIXED_TO_FLOAT(dup_viewy);
gl_viewz = FIXED_TO_FLOAT(dup_viewz);
gl_viewsin = FIXED_TO_FLOAT(viewsin);
gl_viewcos = FIXED_TO_FLOAT(viewcos);
//04/01/2000: Hurdler: added for T&L
// It should replace all other gl_viewxxx when finished
memset(&atransform, 0x00, sizeof(FTransform));
HWR_SetTransformAiming(&atransform, player, false);
atransform.angley = (float)(viewangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
gl_viewludsin = FIXED_TO_FLOAT(FINECOSINE(gl_aimingangle>>ANGLETOFINESHIFT));
gl_viewludcos = FIXED_TO_FLOAT(-FINESINE(gl_aimingangle>>ANGLETOFINESHIFT));
atransform.flip = false;
if ((thiscam->postimgflags & POSTIMG_FLIP) && !(thiscam->postimgflags & POSTIMG_MIRROR))
atransform.flip = true;
atransform.mirror = false;
if ((thiscam->postimgflags & POSTIMG_MIRROR) && !(thiscam->postimgflags & POSTIMG_FLIP))
atransform.mirror = true;
atransform.mirrorflip = false;
if ((thiscam->postimgflags & POSTIMG_FLIP) && (thiscam->postimgflags & POSTIMG_MIRROR))
atransform.mirrorflip = true;
atransform.x = gl_viewx; // FIXED_TO_FLOAT(viewx)
atransform.y = gl_viewy; // FIXED_TO_FLOAT(viewy)
atransform.z = gl_viewz; // FIXED_TO_FLOAT(viewz)
atransform.scalex = 1;
atransform.scaley = (float)vid.width/vid.height;
atransform.scalez = 1;
atransform.fovxangle = fpov; // Tails
atransform.fovyangle = fpov; // Tails
HWR_RollTransform(&atransform, viewroll);
atransform.splitscreen = r_splitscreen;
gl_fovlud = (float)(1.0l/tan((double)(fpov*M_PIl/360l)));
//------------------------------------------------------------------------
HWR_ClearView(); // Clears the depth buffer and resets the view I believe
if (!skybox) // Don't draw the regular sky if there's a skybox
HWR_DrawSkyBackground(player);
HWR_ClearSprites();
drawcount = 0;
//04/01/2000: Hurdler: added for T&L
// Actually it only works on Walls and Planes
HWD.pfnSetTransform(&atransform);
HWR_ClearClipper();
// Reset the shader state.
HWR_SetShaderState();
ps_numbspcalls = 0;
ps_numpolyobjects = 0;
ps_bsptime = I_GetPreciseTime();
validcount++;
if (cv_glbatching.value)
HWR_StartBatching();
#ifdef HWPRECIP
HWR_AddPrecipitationSprites();
#endif
HWR_RenderBSPNode((INT32)numnodes-1);
ps_bsptime = I_GetPreciseTime() - ps_bsptime;
if (cv_glbatching.value)
HWR_RenderBatches();
// Check for new console commands.
NetUpdate();
#ifdef ALAM_LIGHTING
//14/11/99: Hurdler: moved here because it doesn't work with
// subsector, see other comments;
HWR_ResetLights();
#endif
// Draw MD2 and sprites
ps_numsprites = gl_visspritecount;
ps_hw_spritesorttime = I_GetPreciseTime();
HWR_SortVisSprites();
ps_hw_spritesorttime = I_GetPreciseTime() - ps_hw_spritesorttime;
ps_hw_spritedrawtime = I_GetPreciseTime();
HWR_DrawSprites();
ps_hw_spritedrawtime = I_GetPreciseTime() - ps_hw_spritedrawtime;
#ifdef NEWCORONAS
//Hurdler: they must be drawn before translucent planes, what about gl fog?
HWR_DrawCoronas();
#endif
ps_numdrawnodes = 0;
ps_hw_nodesorttime = 0;
ps_hw_nodedrawtime = 0;
if (numplanes || numpolyplanes || numwalls) //Hurdler: render 3D water and transparent walls after everything
{
HWR_CreateDrawNodes();
}
HWD.pfnSetTransform(NULL);
HWD.pfnUnSetShader();
HWR_DoPostProcessor(player);
// Check for new console commands.
NetUpdate();
// added by Hurdler for correct splitscreen
// moved here by hurdler so it works with the new near clipping plane
HWD.pfnGClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
}
// Returns whether palette rendering is "actually enabled."
// Can't have palette rendering if shaders are disabled.
boolean HWR_ShouldUsePaletteRendering(void)
{
return (cv_glpaletterendering.value && HWR_UseShader());
}
// enable or disable palette rendering state depending on settings and availability
// called when relevant settings change
// shader recompilation is done in the cvar callback
static void HWR_TogglePaletteRendering(void)
{
// which state should we go to?
if (HWR_ShouldUsePaletteRendering())
{
// are we not in that state already?
if (!gl_palette_rendering_state)
{
gl_palette_rendering_state = true;
// The textures will still be converted to RGBA by r_opengl.
// This however makes hw_cache use paletted blending for composite textures!
// (patchformat is not touched)
textureformat = GL_TEXFMT_P_8;
HWR_SetMapPalette();
HWR_SetPalette(pLocalPalette);
// If the r_opengl "texture palette" stays the same during this switch, these textures
// will not be cleared out. However they are still out of date since the
// composite texture blending method has changed. Therefore they need to be cleared.
HWR_LoadMapTextures(numtextures);
}
}
else
{
// are we not in that state already?
if (gl_palette_rendering_state)
{
gl_palette_rendering_state = false;
textureformat = GL_TEXFMT_RGBA;
HWR_SetPalette(pLocalPalette);
// If the r_opengl "texture palette" stays the same during this switch, these textures
// will not be cleared out. However they are still out of date since the
// composite texture blending method has changed. Therefore they need to be cleared.
HWR_LoadMapTextures(numtextures);
}
}
}
void HWR_LoadLevel(void)
{
#ifdef ALAM_LIGHTING
// BP: reset light between levels (we draw preview frame lights on current frame)
HWR_ResetLights();
#endif
HWR_CreatePlanePolygons((INT32)numnodes - 1);
// Build the sky dome
HWR_ClearSkyDome();
HWR_BuildSkyDome();
if (HWR_ShouldUsePaletteRendering())
HWR_SetMapPalette();
gl_maploaded = true;
}
// ==========================================================================
// 3D ENGINE COMMANDS
// ==========================================================================
CV_PossibleValue_t glshaders_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Ignore custom shaders"}, {0, NULL}};
#ifdef BAD_MODEL_OPTIONS
static CV_PossibleValue_t glmodelinterpolation_cons_t[] = {{0, "Off"}, {1, "Sometimes"}, {2, "Always"}, {0, NULL}};
#endif
static CV_PossibleValue_t glshearing_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Third-person"}, {0, NULL}};
static void CV_glfiltermode_OnChange(void);
static void CV_glanisotropic_OnChange(void);
static CV_PossibleValue_t glfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSAMPLED, "Nearest"},
{HWD_SET_TEXTUREFILTER_BILINEAR, "Bilinear"}, {HWD_SET_TEXTUREFILTER_TRILINEAR, "Trilinear"},
{HWD_SET_TEXTUREFILTER_MIXED1, "Linear_Nearest"},
{HWD_SET_TEXTUREFILTER_MIXED2, "Nearest_Linear"},
{HWD_SET_TEXTUREFILTER_MIXED3, "Nearest_Mipmap"},
{0, NULL}};
CV_PossibleValue_t glanisotropicmode_cons_t[] = {{1, "MIN"}, {16, "MAX"}, {0, NULL}};
static CV_PossibleValue_t glsecbright_cons_t[] = {{0, "MIN"}, {255, "MAX"}, {0, NULL}};
consvar_t cv_glshaders = CVAR_INIT ("gr_shaders", "On", CV_SAVE, glshaders_cons_t, NULL);
consvar_t cv_glallowshaders = CVAR_INIT ("gr_allowclientshaders", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_fovchange = CVAR_INIT ("gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL);
consvar_t cv_glsecbright = CVAR_INIT("gr_secbright", "0", CV_SAVE, glsecbright_cons_t, NULL);
#ifdef ALAM_LIGHTING
consvar_t cv_gldynamiclighting = CVAR_INIT ("gr_dynamiclighting", "On", CV_SAVE, CV_OnOff, NULL);
consvar_t cv_glstaticlighting = CVAR_INIT ("gr_staticlighting", "On", CV_SAVE, CV_OnOff, NULL);
consvar_t cv_glcoronas = CVAR_INIT ("gr_coronas", "On", CV_SAVE, CV_OnOff, NULL);
consvar_t cv_glcoronasize = CVAR_INIT ("gr_coronasize", "1", CV_SAVE|CV_FLOAT, 0, NULL);
#endif
consvar_t cv_glmodels = CVAR_INIT ("gr_models", "Off", CV_SAVE, CV_OnOff, NULL);
#ifdef BAD_MODEL_OPTIONS
consvar_t cv_glmodelinterpolation = CVAR_INIT ("gr_modelinterpolation", "Sometimes", CV_SAVE, glmodelinterpolation_cons_t, NULL);
consvar_t cv_glmodellighting = CVAR_INIT ("gr_modellighting", "Off", CV_SAVE, CV_OnOff, NULL);
#endif
consvar_t cv_glshearing = CVAR_INIT ("gr_shearing", "Off", CV_SAVE, glshearing_cons_t, NULL);
consvar_t cv_glspritebillboarding = CVAR_INIT ("gr_spritebillboarding", "On", CV_SAVE, CV_OnOff, NULL);
consvar_t cv_glskydome = CVAR_INIT ("gr_skydome", "On", CV_SAVE, CV_OnOff, NULL);
consvar_t cv_glfiltermode = CVAR_INIT ("gr_filtermode", "Nearest", CV_SAVE|CV_CALL, glfiltermode_cons_t, CV_glfiltermode_OnChange);
consvar_t cv_glanisotropicmode = CVAR_INIT ("gr_anisotropicmode", "1", CV_SAVE|CV_CALL, glanisotropicmode_cons_t, CV_glanisotropic_OnChange);
consvar_t cv_glsolvetjoin = CVAR_INIT ("gr_solvetjoin", "On", 0, CV_OnOff, NULL);
consvar_t cv_glbatching = CVAR_INIT ("gr_batching", "On", 0, CV_OnOff, NULL);
CV_PossibleValue_t glpalettedepth_cons_t[] = {{16, "16 bits"}, {24, "24 bits"}, {0, NULL}};
void CV_glpaletterendering_OnChange(void);
void CV_glpalettedepth_OnChange(void);
consvar_t cv_glpaletterendering = CVAR_INIT ("gr_paletteshader", "Off", CV_CALL|CV_SAVE, CV_OnOff, CV_glpaletterendering_OnChange);
consvar_t cv_glpalettedepth = CVAR_INIT ("gr_palettedepth", "16 bits", CV_SAVE|CV_CALL, glpalettedepth_cons_t, CV_glpalettedepth_OnChange);
#define ONLY_IF_GL_LOADED if (vid.glstate != VID_GL_LIBRARY_LOADED) return;
static void CV_glfiltermode_OnChange(void)
{
ONLY_IF_GL_LOADED
HWD.pfnSetSpecialState(HWD_SET_TEXTUREFILTERMODE, cv_glfiltermode.value);
}
static void CV_glanisotropic_OnChange(void)
{
ONLY_IF_GL_LOADED
HWD.pfnSetSpecialState(HWD_SET_TEXTUREANISOTROPICMODE, cv_glanisotropicmode.value);
}
void CV_glmodellighting_OnChange(void);
void CV_glmodellighting_OnChange(void)
{
ONLY_IF_GL_LOADED
// if shaders have been compiled, then they now need to be recompiled.
if (gl_shadersavailable)
HWR_CompileShaders();
}
void CV_glpaletterendering_OnChange(void)
{
ONLY_IF_GL_LOADED
if (gl_shadersavailable)
{
HWR_CompileShaders();
HWR_TogglePaletteRendering();
}
}
void CV_glpalettedepth_OnChange(void);
void CV_glpalettedepth_OnChange(void)
{
ONLY_IF_GL_LOADED
// refresh the screen palette
if (HWR_ShouldUsePaletteRendering())
HWR_SetPalette(pLocalPalette);
}
void CV_glshaders_OnChange(void);
void CV_glshaders_OnChange(void)
{
ONLY_IF_GL_LOADED
HWR_SetShaderState();
if (cv_glpaletterendering.value)
{
// can't do palette rendering without shaders, so update the state if needed
HWR_TogglePaletteRendering();
}
}
//added by Hurdler: console varibale that are saved
void HWR_AddCommands(void)
{
CV_RegisterVar(&cv_fovchange);
#ifdef ALAM_LIGHTING
CV_RegisterVar(&cv_glstaticlighting);
CV_RegisterVar(&cv_gldynamiclighting);
CV_RegisterVar(&cv_glcoronasize);
CV_RegisterVar(&cv_glcoronas);
#endif
#ifdef BAD_MODEL_OPTIONS
CV_RegisterVar(&cv_glmodellighting);
CV_RegisterVar(&cv_glmodelinterpolation);
#endif
CV_RegisterVar(&cv_glmodels);
CV_RegisterVar(&cv_glskydome);
CV_RegisterVar(&cv_glspritebillboarding);
CV_RegisterVar(&cv_glshearing);
CV_RegisterVar(&cv_glshaders);
CV_RegisterVar(&cv_glsecbright);
CV_RegisterVar(&cv_glallowshaders);
CV_RegisterVar(&cv_glfiltermode);
CV_RegisterVar(&cv_glsolvetjoin);
CV_RegisterVar(&cv_glbatching);
CV_RegisterVar(&cv_glanisotropicmode);
CV_RegisterVar(&cv_glpaletterendering);
CV_RegisterVar(&cv_glpalettedepth);
}
void HWR_AddSessionCommands(void)
{
if (gl_sessioncommandsadded)
return;
gl_sessioncommandsadded = true;
}
// --------------------------------------------------------------------------
// Setup the hardware renderer
// --------------------------------------------------------------------------
void HWR_Startup(void)
{
if (!gl_init)
{
CONS_Printf("HWR_Startup()...\n");
textureformat = patchformat = GL_TEXFMT_RGBA;
HWR_InitPolyPool();
HWR_AddSessionCommands();
HWR_InitMapTextures();
HWR_InitModels();
#ifdef ALAM_LIGHTING
HWR_InitLight();
#endif
gl_shadersavailable = HWR_InitShaders();
HWR_SetShaderState();
HWR_LoadAllCustomShaders();
HWR_TogglePaletteRendering();
}
gl_init = true;
}
// --------------------------------------------------------------------------
// Called after switching to the hardware renderer
// --------------------------------------------------------------------------
void HWR_Switch(void)
{
// Add session commands
if (!gl_sessioncommandsadded)
HWR_AddSessionCommands();
// Set special states from CVARs
HWD.pfnSetSpecialState(HWD_SET_TEXTUREFILTERMODE, cv_glfiltermode.value);
HWD.pfnSetSpecialState(HWD_SET_TEXTUREANISOTROPICMODE, cv_glanisotropicmode.value);
// Load textures
if (!gl_maptexturesloaded)
HWR_LoadMapTextures(numtextures);
// Create plane polygons
if (!gl_maploaded && levelloaded)
{
HWR_ClearAllTextures();
HWR_LoadLevel();
}
}
// --------------------------------------------------------------------------
// Free resources allocated by the hardware renderer
// --------------------------------------------------------------------------
void HWR_Shutdown(void)
{
CONS_Printf("HWR_Shutdown()\n");
HWR_FreeExtraSubsectors();
HWR_FreePolyPool();
HWR_FreeMapTextures();
HWD.pfnFlushScreenTextures();
}
void transform(float *cx, float *cy, float *cz)
{
float tr_x,tr_y;
// translation
tr_x = *cx - gl_viewx;
tr_y = *cz - gl_viewy;
// *cy = *cy;
// rotation around vertical y axis
*cx = (tr_x * gl_viewsin) - (tr_y * gl_viewcos);
tr_x = (tr_x * gl_viewcos) + (tr_y * gl_viewsin);
//look up/down ----TOTAL SUCKS!!!--- do the 2 in one!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
tr_y = *cy - gl_viewz;
*cy = (tr_x * gl_viewludcos) + (tr_y * gl_viewludsin);
*cz = (tr_x * gl_viewludsin) - (tr_y * gl_viewludcos);
//scale y before frustum so that frustum can be scaled to screen height
*cy *= ORIGINAL_ASPECT * gl_fovlud;
*cx *= gl_fovlud;
}
static void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, INT32 texnum, boolean noencore, FBITFIELD blend, boolean fogwall, INT32 lightlevel, extracolormap_t *wallcolormap)
{
static size_t allocedwalls = 0;
// Force realloc if buffer has been freed
if (!wallinfo)
allocedwalls = 0;
if (allocedwalls < numwalls + 1)
{
allocedwalls += MAX_TRANSPARENTWALL;
Z_Realloc(wallinfo, allocedwalls * sizeof (*wallinfo), PU_LEVEL, &wallinfo);
}
memcpy(wallinfo[numwalls].wallVerts, wallVerts, sizeof (wallinfo[numwalls].wallVerts));
memcpy(&wallinfo[numwalls].Surf, pSurf, sizeof (FSurfaceInfo));
wallinfo[numwalls].texnum = texnum;
wallinfo[numwalls].blend = blend;
wallinfo[numwalls].noencore = noencore;
wallinfo[numwalls].drawcount = drawcount++;
wallinfo[numwalls].fogwall = fogwall;
wallinfo[numwalls].lightlevel = lightlevel;
wallinfo[numwalls].wallcolormap = wallcolormap;
numwalls++;
}
void HWR_RenderWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend, boolean fogwall, INT32 lightlevel, extracolormap_t *wallcolormap, INT32 texnum)
{
FBITFIELD blendmode = blend;
UINT8 alpha = pSurf->PolyColor.s.alpha; // retain the alpha
INT32 shader = SHADER_NONE;
// Lighting is done here instead so that fog isn't drawn incorrectly on transparent walls after sorting
HWR_Lighting(pSurf, lightlevel, wallcolormap, P_SectorUsesDirectionalLighting(gl_frontsector));
pSurf->PolyColor.s.alpha = alpha; // put the alpha back after lighting
if (blend & PF_Environment)
blendmode |= PF_Occlude; // PF_Occlude must be used for solid objects
if (HWR_UseShader())
{
if (fogwall)
shader = SHADER_FOG;
else if (R_GetTextureBrightmap(texnum) && HWR_ShouldUsePaletteRendering())
shader = SHADER_BRIGHTMAP_WALL;
else
shader = SHADER_WALL;
blendmode |= PF_ColorMapped;
}
if (fogwall)
blendmode |= PF_Fog;
blendmode |= PF_Modulated; // No PF_Occlude means overlapping (incorrect) transparency
HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode, shader, false);
}
INT32 HWR_GetTextureUsed(void)
{
return HWD.pfnGetTextureUsed();
}
void HWR_DoPostProcessor(player_t *player)
{
HWD.pfnUnSetShader();
// Armageddon Blast Flash!
// Could this even be considered postprocessor?
if (player->flashcount && !HWR_ShouldUsePaletteRendering())
{
FOutVector v[4];
FSurfaceInfo Surf;
v[0].x = v[2].y = v[3].x = v[3].y = -4.0f;
v[0].y = v[1].x = v[1].y = v[2].x = 4.0f;
v[0].z = v[1].z = v[2].z = v[3].z = 4.0f; // 4.0 because of the same reason as with the sky, just after the screen is cleared so near clipping plane is 3.99
// This won't change if the flash palettes are changed unfortunately, but it works for its purpose
if (player->flashpal == PAL_NUKE)
{
Surf.PolyColor.s.red = 0xff;
Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0x7F; // The nuke palette is kind of pink-ish
}
else
Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
Surf.PolyColor.s.alpha = 0xc0; // match software mode
V_CubeApply(&Surf.PolyColor.s.red, &Surf.PolyColor.s.green, &Surf.PolyColor.s.blue);
HWD.pfnDrawPolygon(&Surf, v, 4, PF_Modulated|PF_Additive|PF_NoTexture|PF_NoDepthTest);
}
// Capture the screen for intermission and screen waving
if (gamestate != GS_INTERMISSION)
HWD.pfnMakeScreenTexture(HWD_SCREENTEXTURE_GENERIC1);
if (r_splitscreen) // Not supported in splitscreen - someone want to add support?
return;
//UINT8 viewnum = R_GetViewNumber(); // see above
//camera_t *thiscam = &camera[viewnum];
camera_t *thiscam = &camera[0];
// Drunken vision! WooOOooo~
if (thiscam->postimgflags & POSTIMG_WATER || thiscam->postimgflags & POSTIMG_HEAT)
{
// 10 by 10 grid. 2 coordinates (xy)
float v[SCREENVERTS][SCREENVERTS][2];
float disStart = (leveltime-1) + FIXED_TO_FLOAT(R_GetTimeFrac(RTF_LEVEL));
UINT8 x, y;
INT32 WAVELENGTH;
INT32 AMPLITUDE;
INT32 FREQUENCY;
// Modifies the wave.
if (thiscam->postimgflags & POSTIMG_WATER)
{
WAVELENGTH = 5;
AMPLITUDE = 40;
FREQUENCY = 8;
}
else
{
WAVELENGTH = 10;
AMPLITUDE = 60;
FREQUENCY = 4;
}
for (x = 0; x < SCREENVERTS; x++)
{
for (y = 0; y < SCREENVERTS; y++)
{
// Change X position based on its Y position.
v[x][y][0] = (x/((float)(SCREENVERTS-1.0f)/9.0f))-4.5f + (float)sin((disStart+(y*WAVELENGTH))/FREQUENCY)/AMPLITUDE;
v[x][y][1] = (y/((float)(SCREENVERTS-1.0f)/9.0f))-4.5f;
}
}
HWD.pfnPostImgRedraw(v);
// Capture the screen again for screen waving on the intermission
if (gamestate != GS_INTERMISSION)
HWD.pfnMakeScreenTexture(HWD_SCREENTEXTURE_GENERIC1);
}
// Flipping of the screen isn't done here anymore
}
void HWR_StartScreenWipe(void)
{
//CONS_Debug(DBG_RENDER, "In HWR_StartScreenWipe()\n");
HWD.pfnMakeScreenTexture(HWD_SCREENTEXTURE_WIPE_START);
}
void HWR_EndScreenWipe(void)
{
//CONS_Debug(DBG_RENDER, "In HWR_EndScreenWipe()\n");
HWD.pfnMakeScreenTexture(HWD_SCREENTEXTURE_WIPE_END);
}
void HWR_DrawIntermissionBG(void)
{
HWD.pfnDrawScreenTexture(HWD_SCREENTEXTURE_GENERIC1, NULL, 0);
}
//
// hwr mode wipes
//
static lumpnum_t wipelumpnum;
// puts wipe lumpname in wipename[9]
static boolean HWR_WipeCheck(UINT8 wipenum, UINT8 scrnnum)
{
static char lumpname[9] = "FADEmmss";
size_t lsize;
// not a valid wipe number
if (wipenum > 99 || scrnnum > 99)
return false; // shouldn't end up here really, the loop should've stopped running beforehand
// puts the numbers into the wipename
lumpname[4] = '0'+(wipenum/10);
lumpname[5] = '0'+(wipenum%10);
lumpname[6] = '0'+(scrnnum/10);
lumpname[7] = '0'+(scrnnum%10);
wipelumpnum = W_CheckNumForName(lumpname);
// again, shouldn't be here really
if (wipelumpnum == LUMPERROR)
return false;
lsize = W_LumpLength(wipelumpnum);
if (!(lsize == 256000 || lsize == 64000 || lsize == 16000 || lsize == 4000))
{
CONS_Alert(CONS_WARNING, "Fade mask lump %s of incorrect size, ignored\n", lumpname);
return false; // again, shouldn't get here if it is a bad size
}
return true;
}
void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum)
{
if (!HWR_WipeCheck(wipenum, scrnnum))
return;
HWR_GetFadeMask(wipelumpnum);
HWD.pfnDoScreenWipe(HWD_SCREENTEXTURE_WIPE_START, HWD_SCREENTEXTURE_WIPE_END);
}
void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum)
{
// It does the same thing
HWR_DoWipe(wipenum, scrnnum);
}
void HWR_RenderVhsEffect(INT16 upbary, INT16 downbary, UINT8 updistort, UINT8 downdistort, UINT8 barsize)
{
HWD.pfnRenderVhsEffect(upbary, downbary, updistort, downdistort, barsize);
}
void HWR_MakeScreenFinalTexture(void)
{
int tex = HWR_ShouldUsePaletteRendering() ? HWD_SCREENTEXTURE_GENERIC3 : HWD_SCREENTEXTURE_GENERIC2;
HWD.pfnMakeScreenTexture(tex);
}
void HWR_DrawScreenFinalTexture(int width, int height)
{
int tex = HWR_ShouldUsePaletteRendering() ? HWD_SCREENTEXTURE_GENERIC3 : HWD_SCREENTEXTURE_GENERIC2;
HWD.pfnDrawScreenFinalTexture(tex, width, height);
}
#endif // HWRENDER