mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-12-25 20:20:52 +00:00
626 lines
15 KiB
C
626 lines
15 KiB
C
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// 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 r_splats.c
|
|
/// \brief floor and wall splats
|
|
|
|
#include "r_draw.h"
|
|
#include "r_main.h"
|
|
#include "r_plane.h"
|
|
#include "r_splats.h"
|
|
#include "w_wad.h"
|
|
#include "z_zone.h"
|
|
#include "d_netcmd.h"
|
|
|
|
#ifdef WALLSPLATS
|
|
static wallsplat_t wallsplats[MAXLEVELSPLATS]; // WALL splats
|
|
static INT32 freewallsplat;
|
|
#endif
|
|
|
|
#ifdef USEASM
|
|
/// \brief for floorsplats \note accessed by asm code
|
|
struct rastery_s *prastertab;
|
|
#endif
|
|
|
|
#ifdef FLOORSPLATS
|
|
static floorsplat_t floorsplats[1]; // FLOOR splats
|
|
static INT32 freefloorsplat;
|
|
|
|
struct rastery_s
|
|
{
|
|
fixed_t minx, maxx; // for each raster line starting at line 0
|
|
fixed_t tx1, ty1;
|
|
fixed_t tx2, ty2; // start/end points in texture at this line
|
|
};
|
|
static struct rastery_s rastertab[MAXVIDHEIGHT];
|
|
|
|
static void prepare_rastertab(void);
|
|
#endif
|
|
|
|
// --------------------------------------------------------------------------
|
|
// setup splat cache
|
|
// --------------------------------------------------------------------------
|
|
void R_ClearLevelSplats(void)
|
|
{
|
|
#ifdef WALLSPLATS
|
|
freewallsplat = 0;
|
|
memset(wallsplats, 0, sizeof (wallsplats));
|
|
#endif
|
|
#ifdef FLOORSPLATS
|
|
freefloorsplat = 0;
|
|
memset(floorsplats, 0, sizeof (floorsplats));
|
|
|
|
// setup to draw floorsplats
|
|
prastertab = rastertab;
|
|
prepare_rastertab();
|
|
#endif
|
|
}
|
|
|
|
// ==========================================================================
|
|
// WALL SPLATS
|
|
// ==========================================================================
|
|
#ifdef WALLSPLATS
|
|
// --------------------------------------------------------------------------
|
|
// Return a pointer to a splat free for use, or NULL if no more splats are
|
|
// available
|
|
// --------------------------------------------------------------------------
|
|
static wallsplat_t *R_AllocWallSplat(void)
|
|
{
|
|
wallsplat_t *splat;
|
|
wallsplat_t *p_splat;
|
|
line_t *li;
|
|
|
|
// clear the splat from the line if it was in use
|
|
splat = &wallsplats[freewallsplat];
|
|
li = splat->line;
|
|
if (li)
|
|
{
|
|
// remove splat from line splats list
|
|
if (li->splats == splat)
|
|
li->splats = splat->next; // remove from head
|
|
else
|
|
{
|
|
I_Assert(li->splats != NULL);
|
|
for (p_splat = li->splats; p_splat->next; p_splat = p_splat->next)
|
|
if (p_splat->next == splat)
|
|
{
|
|
p_splat->next = splat->next;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
memset(splat, 0, sizeof (wallsplat_t));
|
|
|
|
// for next allocation
|
|
freewallsplat++;
|
|
if (freewallsplat >= 20)
|
|
freewallsplat = 0;
|
|
|
|
return splat;
|
|
}
|
|
|
|
// Add a new splat to the linedef:
|
|
// top: top z coord
|
|
// wallfrac: frac along the linedef vector (0 to FRACUNIT)
|
|
// splatpatchname: name of patch to draw
|
|
void R_AddWallSplat(line_t *wallline, INT16 sectorside, const char *patchname, fixed_t top,
|
|
fixed_t wallfrac, INT32 flags)
|
|
{
|
|
fixed_t fracsplat, linelength;
|
|
wallsplat_t *splat = NULL;
|
|
wallsplat_t *p_splat;
|
|
patch_t *patch;
|
|
sector_t *backsector = NULL;
|
|
|
|
if (W_CheckNumForName(patchname) != LUMPERROR)
|
|
splat = R_AllocWallSplat();
|
|
if (!splat)
|
|
return;
|
|
|
|
// set the splat
|
|
splat->patch = W_GetNumForName(patchname);
|
|
sectorside ^= 1;
|
|
if (wallline->sidenum[sectorside] != 0xffff)
|
|
{
|
|
backsector = sides[wallline->sidenum[sectorside]].sector;
|
|
|
|
if (top < backsector->floorheight)
|
|
{
|
|
splat->yoffset = &backsector->floorheight;
|
|
top -= backsector->floorheight;
|
|
}
|
|
else if (top > backsector->ceilingheight)
|
|
{
|
|
splat->yoffset = &backsector->ceilingheight;
|
|
top -= backsector->ceilingheight;
|
|
}
|
|
}
|
|
|
|
splat->top = top;
|
|
splat->flags = flags;
|
|
|
|
// bad.. but will be needed for drawing anyway..
|
|
patch = W_CachePatchNum(splat->patch, PU_PATCH);
|
|
|
|
// offset needed by draw code for texture mapping
|
|
linelength = P_SegLength((seg_t *)wallline);
|
|
splat->offset = FixedMul(wallfrac, linelength) - (SHORT(patch->width)<<(FRACBITS-1));
|
|
fracsplat = FixedDiv(((SHORT(patch->width)<<FRACBITS)>>1), linelength);
|
|
|
|
wallfrac -= fracsplat;
|
|
if (wallfrac > linelength)
|
|
return;
|
|
splat->v1.x = wallline->v1->x + FixedMul(wallline->dx, wallfrac);
|
|
splat->v1.y = wallline->v1->y + FixedMul(wallline->dy, wallfrac);
|
|
wallfrac += fracsplat + fracsplat;
|
|
if (wallfrac < 0)
|
|
return;
|
|
splat->v2.x = wallline->v1->x + FixedMul(wallline->dx, wallfrac);
|
|
splat->v2.y = wallline->v1->y + FixedMul(wallline->dy, wallfrac);
|
|
|
|
if (wallline->frontsector && wallline->frontsector == backsector)
|
|
return;
|
|
|
|
// insert splat in the linedef splat list
|
|
// BP: why not insert in head is much more simple?
|
|
// BP: because for remove it is more simple!
|
|
splat->line = wallline;
|
|
splat->next = NULL;
|
|
if (wallline->splats)
|
|
{
|
|
p_splat = wallline->splats;
|
|
while (p_splat->next)
|
|
p_splat = p_splat->next;
|
|
p_splat->next = splat;
|
|
}
|
|
else
|
|
wallline->splats = splat;
|
|
}
|
|
#endif // WALLSPLATS
|
|
|
|
// ==========================================================================
|
|
// FLOOR SPLATS
|
|
// ==========================================================================
|
|
#ifdef FLOORSPLATS
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Return a pointer to a splat free for use, or NULL if no more splats are
|
|
// available
|
|
// --------------------------------------------------------------------------
|
|
static floorsplat_t *R_AllocFloorSplat(void)
|
|
{
|
|
floorsplat_t *splat;
|
|
floorsplat_t *p_splat;
|
|
subsector_t *sub;
|
|
|
|
// find splat to use
|
|
freefloorsplat++;
|
|
if (freefloorsplat >= 1)
|
|
freefloorsplat = 0;
|
|
|
|
// clear the splat from the line if it was in use
|
|
splat = &floorsplats[freefloorsplat];
|
|
sub = splat->subsector;
|
|
if (sub)
|
|
{
|
|
// remove splat from subsector splats list
|
|
if (sub->splats == splat)
|
|
sub->splats = splat->next; // remove from head
|
|
else
|
|
{
|
|
p_splat = sub->splats;
|
|
while (p_splat->next)
|
|
{
|
|
if (p_splat->next == splat)
|
|
p_splat->next = splat->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
memset(splat, 0, sizeof (floorsplat_t));
|
|
return splat;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Add a floor splat to the subsector
|
|
// --------------------------------------------------------------------------
|
|
void R_AddFloorSplat(subsector_t *subsec, mobj_t *mobj, const char *picname, fixed_t x, fixed_t y, fixed_t z,
|
|
INT32 flags)
|
|
{
|
|
floorsplat_t *splat = NULL;
|
|
floorsplat_t *p_splat;
|
|
INT32 size;
|
|
|
|
if (W_CheckNumForName(picname) != LUMPERROR)
|
|
splat = R_AllocFloorSplat();
|
|
if (!splat)
|
|
return;
|
|
|
|
// set the splat
|
|
splat->pic = W_GetNumForName(picname);
|
|
splat->flags = flags;
|
|
splat->mobj = mobj;
|
|
|
|
splat->z = z;
|
|
|
|
size = W_LumpLength(splat->pic);
|
|
|
|
switch (size)
|
|
{
|
|
case 4194304: // 2048x2048 lump
|
|
splat->size = 1024;
|
|
break;
|
|
case 1048576: // 1024x1024 lump
|
|
splat->size = 512;
|
|
break;
|
|
case 262144:// 512x512 lump
|
|
splat->size = 256;
|
|
break;
|
|
case 65536: // 256x256 lump
|
|
splat->size = 128;
|
|
break;
|
|
case 16384: // 128x128 lump
|
|
splat->size = 64;
|
|
break;
|
|
case 1024: // 32x32 lump
|
|
splat->size = 16;
|
|
break;
|
|
default: // 64x64 lump
|
|
splat->size = 32;
|
|
break;
|
|
}
|
|
|
|
// 3--2
|
|
// | |
|
|
// 0--1
|
|
//
|
|
splat->verts[0].x = splat->verts[3].x = x - (splat->size<<FRACBITS);
|
|
splat->verts[2].x = splat->verts[1].x = x + ((splat->size-1)<<FRACBITS);
|
|
splat->verts[3].y = splat->verts[2].y = y + ((splat->size-1)<<FRACBITS);
|
|
splat->verts[0].y = splat->verts[1].y = y - (splat->size<<FRACBITS);
|
|
|
|
// insert splat in the subsector splat list
|
|
splat->subsector = subsec;
|
|
splat->next = NULL;
|
|
if (subsec->splats)
|
|
{
|
|
p_splat = subsec->splats;
|
|
while (p_splat->next)
|
|
p_splat = p_splat->next;
|
|
p_splat->next = splat;
|
|
}
|
|
else
|
|
subsec->splats = splat;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Before each frame being rendered, clear the visible floorsplats list
|
|
// --------------------------------------------------------------------------
|
|
static floorsplat_t *visfloorsplats;
|
|
|
|
void R_ClearVisibleFloorSplats(void)
|
|
{
|
|
visfloorsplats = NULL;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Add a floorsplat to the visible floorsplats list, for the current frame
|
|
// --------------------------------------------------------------------------
|
|
void R_AddVisibleFloorSplats(subsector_t *subsec)
|
|
{
|
|
floorsplat_t *pSplat;
|
|
I_Assert(subsec->splats != NULL);
|
|
|
|
pSplat = subsec->splats;
|
|
// the splat is not visible from below
|
|
// FIXME: depending on some flag in pSplat->flags, some splats may be visible from 2 sides
|
|
// (above/below)
|
|
if (pSplat->z < viewz)
|
|
{
|
|
pSplat->nextvis = visfloorsplats;
|
|
visfloorsplats = pSplat;
|
|
}
|
|
|
|
while (pSplat->next)
|
|
{
|
|
pSplat = pSplat->next;
|
|
if (pSplat->z < viewz)
|
|
{
|
|
pSplat->nextvis = visfloorsplats;
|
|
visfloorsplats = pSplat;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef USEASM
|
|
// tv1, tv2 = x/y qui varie dans la texture, tc = x/y qui est constant.
|
|
void ASMCALL rasterize_segment_tex(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32 tv1, INT32 tv2,
|
|
INT32 tc, INT32 dir);
|
|
#endif
|
|
|
|
// current test with floor tile
|
|
//#define FLOORSPLATSOLIDCOLOR
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Rasterize the four edges of a floor splat polygon,
|
|
// fill the polygon with linear interpolation, call span drawer for each
|
|
// scan line
|
|
// --------------------------------------------------------------------------
|
|
static void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, UINT8 *pTex)
|
|
{
|
|
// rasterizing
|
|
INT32 miny = vid.height + 1, maxy = 0, y, x1, ry1, x2, y2;
|
|
fixed_t offsetx, offsety;
|
|
|
|
#ifdef FLOORSPLATSOLIDCOLOR
|
|
UINT8 *pDest;
|
|
INT32 tdx, tdy, ty, tx, x;
|
|
#else
|
|
lighttable_t **planezlight;
|
|
fixed_t planeheight;
|
|
angle_t angle, planecos, planesin;
|
|
fixed_t distance, span;
|
|
size_t indexr;
|
|
INT32 light;
|
|
#endif
|
|
(void)pTex;
|
|
|
|
offsetx = pSplat->verts[0].x & ((pSplat->size << FRACBITS)-1);
|
|
offsety = pSplat->verts[0].y & ((pSplat->size << FRACBITS)-1);
|
|
|
|
// do segment a -> top of texture
|
|
x1 = verts[3].x;
|
|
ry1 = verts[3].y;
|
|
x2 = verts[2].x;
|
|
y2 = verts[2].y;
|
|
if (ry1 < 0)
|
|
ry1 = 0;
|
|
if (ry1 >= vid.height)
|
|
ry1 = vid.height - 1;
|
|
if (y2 < 0)
|
|
y2 = 0;
|
|
if (y2 >= vid.height)
|
|
y2 = vid.height - 1;
|
|
rasterize_segment_tex(x1, ry1, x2, y2, 0, pSplat->size - 1, 0, 0);
|
|
if (ry1 < miny)
|
|
miny = ry1;
|
|
if (ry1 > maxy)
|
|
maxy = ry1;
|
|
|
|
// do segment b -> right side of texture
|
|
x1 = x2;
|
|
ry1 = y2;
|
|
x2 = verts[1].x;
|
|
y2 = verts[1].y;
|
|
if (ry1 < 0)
|
|
ry1 = 0;
|
|
if (ry1 >= vid.height)
|
|
ry1 = vid.height - 1;
|
|
if (y2 < 0)
|
|
y2 = 0;
|
|
if (y2 >= vid.height)
|
|
y2 = vid.height - 1;
|
|
rasterize_segment_tex(x1, ry1, x2, y2, 0, pSplat->size - 1, pSplat->size - 1, 1);
|
|
if (ry1 < miny)
|
|
miny = ry1;
|
|
if (ry1 > maxy)
|
|
maxy = ry1;
|
|
|
|
// do segment c -> bottom of texture
|
|
x1 = x2;
|
|
ry1 = y2;
|
|
x2 = verts[0].x;
|
|
y2 = verts[0].y;
|
|
if (ry1 < 0)
|
|
ry1 = 0;
|
|
if (ry1 >= vid.height)
|
|
ry1 = vid.height - 1;
|
|
if (y2 < 0)
|
|
y2 = 0;
|
|
if (y2 >= vid.height)
|
|
y2 = vid.height - 1;
|
|
rasterize_segment_tex(x1, ry1, x2, y2, pSplat->size - 1, 0, pSplat->size - 1, 0);
|
|
if (ry1 < miny)
|
|
miny = ry1;
|
|
if (ry1 > maxy)
|
|
maxy = ry1;
|
|
|
|
// do segment d -> left side of texture
|
|
x1 = x2;
|
|
ry1 = y2;
|
|
x2 = verts[3].x;
|
|
y2 = verts[3].y;
|
|
if (ry1 < 0)
|
|
ry1 = 0;
|
|
if (ry1 >= vid.height)
|
|
ry1 = vid.height - 1;
|
|
if (y2 < 0)
|
|
y2 = 0;
|
|
if (y2 >= vid.height)
|
|
y2 = vid.height - 1;
|
|
rasterize_segment_tex(x1, ry1, x2, y2, pSplat->size - 1, 0, 0, 1);
|
|
if (ry1 < miny)
|
|
miny = ry1;
|
|
if (ry1 > maxy)
|
|
maxy = ry1;
|
|
|
|
#ifndef FLOORSPLATSOLIDCOLOR
|
|
// prepare values for all the splat
|
|
ds_source = W_CacheLumpNum(pSplat->pic, PU_CACHE);
|
|
planeheight = abs(pSplat->z - viewz);
|
|
light = (pSplat->subsector->sector->lightlevel >> LIGHTSEGSHIFT);
|
|
if (light >= LIGHTLEVELS)
|
|
light = LIGHTLEVELS - 1;
|
|
if (light < 0)
|
|
light = 0;
|
|
planezlight = zlight[light];
|
|
|
|
for (y = miny; y <= maxy; y++)
|
|
{
|
|
x1 = rastertab[y].minx>>FRACBITS;
|
|
x2 = rastertab[y].maxx>>FRACBITS;
|
|
|
|
if (x1 < 0)
|
|
x1 = 0;
|
|
if (x2 >= vid.width)
|
|
x2 = vid.width - 1;
|
|
|
|
angle = (currentplane->viewangle + currentplane->plangle)>>ANGLETOFINESHIFT;
|
|
planecos = FINECOSINE(angle);
|
|
planesin = FINESINE(angle);
|
|
|
|
if (planeheight != cachedheight[y])
|
|
{
|
|
cachedheight[y] = planeheight;
|
|
distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]);
|
|
ds_xstep = cachedxstep[y] = FixedMul(distance,basexscale);
|
|
ds_ystep = cachedystep[y] = FixedMul(distance,baseyscale);
|
|
|
|
if ((span = abs(centery-y)))
|
|
{
|
|
ds_xstep = cachedxstep[y] = FixedMul(planesin, planeheight) / span;
|
|
ds_ystep = cachedystep[y] = FixedMul(planecos, planeheight) / span;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
distance = cacheddistance[y];
|
|
ds_xstep = cachedxstep[y];
|
|
ds_ystep = cachedystep[y];
|
|
}
|
|
|
|
ds_xfrac = xoffs + FixedMul(planecos, distance) + (x1 - centerx) * ds_xstep;
|
|
ds_yfrac = yoffs - FixedMul(planesin, distance) + (x1 - centerx) * ds_ystep;
|
|
ds_xfrac -= offsetx;
|
|
ds_yfrac += offsety;
|
|
|
|
indexr = distance >> LIGHTZSHIFT;
|
|
if (indexr >= MAXLIGHTZ)
|
|
indexr = MAXLIGHTZ - 1;
|
|
ds_colormap = planezlight[indexr];
|
|
|
|
ds_y = y;
|
|
if (x2 >= x1) // sanity check
|
|
{
|
|
ds_x1 = x1;
|
|
ds_x2 = x2;
|
|
ds_transmap = transtables + ((tr_trans50-1)<<FF_TRANSSHIFT);
|
|
(spanfuncs[SPANDRAWFUNC_SPLAT])();
|
|
}
|
|
|
|
// reset for next calls to edge rasterizer
|
|
rastertab[y].minx = INT32_MAX;
|
|
rastertab[y].maxx = INT32_MIN;
|
|
}
|
|
|
|
#else
|
|
for (y = miny; y <= maxy; y++)
|
|
{
|
|
x1 = rastertab[y].minx>>FRACBITS;
|
|
x2 = rastertab[y].maxx>>FRACBITS;
|
|
if (x1 < 0)
|
|
x1 = 0;
|
|
if (x2 >= vid.width)
|
|
x2 = vid.width - 1;
|
|
|
|
// pDest = ylookup[y] + columnofs[x1];
|
|
pDest = &topleft[y*vid.width + x1];
|
|
|
|
x = x2 - x1 + 1;
|
|
|
|
// starting point of the texture
|
|
tx = rastertab[y].tx1;
|
|
ty = rastertab[y].ty1;
|
|
|
|
// HORRIBLE BUG!!!
|
|
if (x > 0)
|
|
{
|
|
tdx = (rastertab[y].tx2 - tx) / x;
|
|
tdy = (rastertab[y].ty2 - ty) / x;
|
|
|
|
while (x-- > 0)
|
|
{
|
|
*(pDest++) = (UINT8)(y&1);
|
|
tx += tdx;
|
|
ty += tdy;
|
|
}
|
|
}
|
|
|
|
// reinitialise the minimum and maximum for the next approach
|
|
rastertab[y].minx = INT32_MAX;
|
|
rastertab[y].maxx = INT32_MIN;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// R_DrawVisibleFloorSplats
|
|
// draw the flat floor/ceiling splats
|
|
// --------------------------------------------------------------------------
|
|
void R_DrawVisibleFloorSplats(void)
|
|
{
|
|
floorsplat_t *pSplat;
|
|
INT32 iCount = 0, i;
|
|
fixed_t tr_x, tr_y, rot_x, rot_y, rot_z, xscale, yscale;
|
|
vertex_t *v3d;
|
|
vertex_t v2d[4];
|
|
|
|
pSplat = visfloorsplats;
|
|
while (pSplat)
|
|
{
|
|
iCount++;
|
|
|
|
// Draw a floor splat
|
|
// 3--2
|
|
// | |
|
|
// 0--1
|
|
|
|
rot_z = pSplat->z - viewz;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
v3d = &pSplat->verts[i];
|
|
|
|
// transform the origin point
|
|
tr_x = v3d->x - viewx;
|
|
tr_y = v3d->y - viewy;
|
|
|
|
// rotation around vertical y axis
|
|
rot_x = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos);
|
|
rot_y = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
|
|
|
|
if (rot_y < 4*FRACUNIT)
|
|
goto skipit;
|
|
|
|
// note: y from view above of map, is distance far away
|
|
xscale = FixedDiv(projection, rot_y);
|
|
yscale = -FixedDiv(projectiony, rot_y);
|
|
|
|
// projection
|
|
v2d[i].x = (centerxfrac + FixedMul (rot_x, xscale))>>FRACBITS;
|
|
v2d[i].y = (centeryfrac + FixedMul (rot_z, yscale))>>FRACBITS;
|
|
}
|
|
|
|
R_RenderFloorSplat(pSplat, v2d, NULL);
|
|
skipit:
|
|
pSplat = pSplat->nextvis;
|
|
}
|
|
}
|
|
|
|
static void prepare_rastertab(void)
|
|
{
|
|
INT32 iLine;
|
|
for (iLine = 0; iLine < vid.height; iLine++)
|
|
{
|
|
rastertab[iLine].minx = INT32_MAX;
|
|
rastertab[iLine].maxx = INT32_MIN;
|
|
}
|
|
}
|
|
|
|
#endif // FLOORSPLATS
|