Merge branch 'portals2' into 'master'

Portal-based skybox rendering replacement for the software renderer

See merge request STJr/SRB2Internal!226
This commit is contained in:
Nev3r 2019-06-16 04:08:00 -04:00
commit 740c561b0e
16 changed files with 633 additions and 340 deletions

View file

@ -123,6 +123,7 @@ set(SRB2_CORE_RENDER_SOURCES
r_sky.c
r_splats.c
r_things.c
r_portal.c
r_bsp.h
r_data.h
@ -136,6 +137,7 @@ set(SRB2_CORE_RENDER_SOURCES
r_splats.h
r_state.h
r_things.h
r_portal.h
)
set(SRB2_CORE_GAME_SOURCES

View file

@ -455,6 +455,7 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/r_sky.o \
$(OBJDIR)/r_splats.o \
$(OBJDIR)/r_things.o \
$(OBJDIR)/r_portal.o \
$(OBJDIR)/screen.o \
$(OBJDIR)/v_video.o \
$(OBJDIR)/s_sound.o \

View file

@ -6194,7 +6194,7 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
}
// note: sets viewangle, viewx, viewy, viewz
R_SetupFrame(player, false); // This can stay false because it is only used to set viewsky in r_main.c, which isn't used here
R_SetupFrame(player);
// copy view cam position for local use
dup_viewx = viewx;

View file

@ -3147,7 +3147,6 @@ boolean P_SetupLevel(boolean skipprecip)
savedata.lives = 0;
}
skyVisible = skyVisible1 = skyVisible2 = true; // assume the skybox is visible on level load.
if (loadprecip) // uglier hack
{ // to make a newly loaded level start on the second frame.
INT32 buf = gametic % BACKUPTICS;

View file

@ -15,6 +15,7 @@
#include "g_game.h"
#include "r_local.h"
#include "r_state.h"
#include "r_portal.h" // Add seg portals
#include "r_splats.h"
#include "p_local.h" // camera
@ -26,11 +27,11 @@ side_t *sidedef;
line_t *linedef;
sector_t *frontsector;
sector_t *backsector;
boolean portalline; // is curline a portal seg?
// very ugly realloc() of drawsegs at run-time, I upped it to 512
// instead of 256.. and someone managed to send me a level with
// 896 drawsegs! So too bad here's a limit removal a-la-Boom
drawseg_t *curdrawsegs = NULL; /**< This is used to handle multiple lists for masked drawsegs. */
drawseg_t *drawsegs = NULL;
drawseg_t *ds_p = NULL;
@ -459,7 +460,7 @@ static void R_AddLine(seg_t *line)
line2 = P_FindSpecialLineFromTag(40, line->linedef->tag, line2);
if (line2 >= 0) // found it!
{
R_AddPortal(line->linedef-lines, line2, x1, x2); // Remember the lines for later rendering
Portal_Add2Lines(line->linedef-lines, line2, x1, x2); // Remember the lines for later rendering
//return; // Don't fill in that space now!
goto clipsolid;
}
@ -1377,13 +1378,5 @@ void R_RenderBSPNode(INT32 bspnum)
bspnum = bsp->children[side^1];
}
// PORTAL CULLING
if (portalcullsector) {
sector_t *sect = subsectors[bspnum & ~NF_SUBSECTOR].sector;
if (sect != portalcullsector)
return;
portalcullsector = NULL;
}
R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);
}

View file

@ -29,6 +29,7 @@ extern boolean portalline; // is curline a portal seg?
extern INT32 checkcoord[12][4];
extern drawseg_t *curdrawsegs;
extern drawseg_t *drawsegs;
extern drawseg_t *ds_p;
extern INT32 doorclosed;
@ -38,7 +39,6 @@ void R_ClearClipSegs(void);
void R_PortalClearClipSegs(INT32 start, INT32 end);
void R_ClearDrawSegs(void);
void R_RenderBSPNode(INT32 bspnum);
void R_AddPortal(INT32 line1, INT32 line2, INT32 x1, INT32 x2);
#ifdef POLYOBJECTS
void R_SortPolyObjects(subsector_t *sub);

View file

@ -30,6 +30,7 @@
#include "p_spec.h" // skyboxmo
#include "z_zone.h"
#include "m_random.h" // quake camera shake
#include "r_portal.h"
#ifdef HWRENDER
#include "hardware/hw_main.h"
@ -65,37 +66,9 @@ size_t loopcount;
fixed_t viewx, viewy, viewz;
angle_t viewangle, aimingangle;
fixed_t viewcos, viewsin;
boolean viewsky, skyVisible;
boolean skyVisible1, skyVisible2; // saved values of skyVisible for P1 and P2, for splitscreen
sector_t *viewsector;
player_t *viewplayer;
// PORTALS!
// You can thank and/or curse JTE for these.
UINT8 portalrender;
sector_t *portalcullsector;
typedef struct portal_pair
{
INT32 line1;
INT32 line2;
UINT8 pass;
struct portal_pair *next;
fixed_t viewx;
fixed_t viewy;
fixed_t viewz;
angle_t viewangle;
INT32 start;
INT32 end;
INT16 *ceilingclip;
INT16 *floorclip;
fixed_t *frontscale;
} portal_pair;
portal_pair *portal_base, *portal_cap;
line_t *portalclipline;
INT32 portalclipstart, portalclipend;
//
// precalculated math tables
//
@ -764,7 +737,7 @@ static void R_SetupFreelook(void)
#undef AIMINGTODY
void R_SetupFrame(player_t *player, boolean skybox)
void R_SetupFrame(player_t *player)
{
camera_t *thiscam;
boolean chasecam = false;
@ -794,7 +767,6 @@ void R_SetupFrame(player_t *player, boolean skybox)
else if (!chasecam)
thiscam->chase = false;
viewsky = !skybox;
if (player->awayviewtics)
{
// cut-away view stuff
@ -883,7 +855,6 @@ void R_SkyboxFrame(player_t *player)
thiscam = &camera;
// cut-away view stuff
viewsky = true;
viewmobj = skyboxmo[0];
#ifdef PARANOIA
if (!viewmobj)
@ -1010,17 +981,8 @@ void R_SkyboxFrame(player_t *player)
R_SetupFreelook();
}
#define ANGLED_PORTALS
static void R_PortalFrame(line_t *start, line_t *dest, portal_pair *portal)
static void R_PortalFrame(portal_t *portal)
{
vertex_t dest_c, start_c;
#ifdef ANGLED_PORTALS
// delta angle
angle_t dangle = R_PointToAngle2(0,0,dest->dx,dest->dy) - R_PointToAngle2(start->dx,start->dy,0,0);
#endif
//R_SetupFrame(player, false);
viewx = portal->viewx;
viewy = portal->viewy;
viewz = portal->viewz;
@ -1029,96 +991,37 @@ static void R_PortalFrame(line_t *start, line_t *dest, portal_pair *portal)
viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT);
viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
portalcullsector = dest->frontsector;
viewsector = dest->frontsector;
portalclipline = dest;
portalclipstart = portal->start;
portalclipend = portal->end;
// Offset the portal view by the linedef centers
// looking glass center
start_c.x = (start->v1->x + start->v2->x) / 2;
start_c.y = (start->v1->y + start->v2->y) / 2;
// other side center
dest_c.x = (dest->v1->x + dest->v2->x) / 2;
dest_c.y = (dest->v1->y + dest->v2->y) / 2;
// Heights!
viewz += dest->frontsector->floorheight - start->frontsector->floorheight;
// calculate the difference in position and rotation!
#ifdef ANGLED_PORTALS
if (dangle == 0)
#endif
{ // the entrance goes straight opposite the exit, so we just need to mess with the offset.
viewx += dest_c.x - start_c.x;
viewy += dest_c.y - start_c.y;
return;
}
#ifdef ANGLED_PORTALS
viewangle += dangle;
viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT);
viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
//CONS_Printf("dangle == %u\n", AngleFixed(dangle)>>FRACBITS);
// ????
if (portal->clipline != -1)
{
fixed_t disttopoint;
angle_t angtopoint;
disttopoint = R_PointToDist2(start_c.x, start_c.y, viewx, viewy);
angtopoint = R_PointToAngle2(start_c.x, start_c.y, viewx, viewy);
angtopoint += dangle;
viewx = dest_c.x+FixedMul(FINECOSINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
viewy = dest_c.y+FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
}
#endif
}
void R_AddPortal(INT32 line1, INT32 line2, INT32 x1, INT32 x2)
{
portal_pair *portal = Z_Malloc(sizeof(portal_pair), PU_LEVEL, NULL);
INT16 *ceilingclipsave = Z_Malloc(sizeof(INT16)*(x2-x1), PU_LEVEL, NULL);
INT16 *floorclipsave = Z_Malloc(sizeof(INT16)*(x2-x1), PU_LEVEL, NULL);
fixed_t *frontscalesave = Z_Malloc(sizeof(fixed_t)*(x2-x1), PU_LEVEL, NULL);
portal->line1 = line1;
portal->line2 = line2;
portal->pass = portalrender+1;
portal->next = NULL;
R_PortalStoreClipValues(x1, x2, ceilingclipsave, floorclipsave, frontscalesave);
portal->ceilingclip = ceilingclipsave;
portal->floorclip = floorclipsave;
portal->frontscale = frontscalesave;
portal->start = x1;
portal->end = x2;
portalline = true; // this tells R_StoreWallRange that curline is a portal seg
portal->viewx = viewx;
portal->viewy = viewy;
portal->viewz = viewz;
portal->viewangle = viewangle;
if (!portal_base)
{
portal_base = portal;
portal_cap = portal;
portalclipline = &lines[portal->clipline];
viewsector = portalclipline->frontsector;
}
else
{
portal_cap->next = portal;
portal_cap = portal;
portalclipline = NULL;
viewsector = R_PointInSubsector(viewx, viewy)->sector;
}
}
static void Mask_Pre (maskcount_t* m)
{
m->drawsegs[0] = ds_p - drawsegs;
m->vissprites[0] = visspritecount;
m->viewx = viewx;
m->viewy = viewy;
m->viewz = viewz;
m->viewsector = viewsector;
}
static void Mask_Post (maskcount_t* m)
{
m->drawsegs[1] = ds_p - drawsegs;
m->vissprites[1] = visspritecount;
}
// ================
// R_RenderView
// ================
@ -1131,8 +1034,8 @@ void R_AddPortal(INT32 line1, INT32 line2, INT32 x1, INT32 x2)
void R_RenderPlayerView(player_t *player)
{
portal_pair *portal;
const boolean skybox = (skyboxmo[0] && cv_skybox.value);
UINT8 nummasks = 1;
maskcount_t* masks = malloc(sizeof(maskcount_t));
if (cv_homremoval.value && player == &players[displayplayer]) // if this is display player 1
{
@ -1142,38 +1045,7 @@ void R_RenderPlayerView(player_t *player)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 32+(timeinmap&15));
}
// load previous saved value of skyVisible for the player
if (splitscreen && player == &players[secondarydisplayplayer])
skyVisible = skyVisible2;
else
skyVisible = skyVisible1;
portalrender = 0;
portal_base = portal_cap = NULL;
if (skybox && skyVisible)
{
R_SkyboxFrame(player);
R_ClearClipSegs();
R_ClearDrawSegs();
R_ClearPlanes();
R_ClearSprites();
#ifdef FLOORSPLATS
R_ClearVisibleFloorSplats();
#endif
R_RenderBSPNode((INT32)numnodes - 1);
R_ClipSprites();
R_DrawPlanes();
#ifdef FLOORSPLATS
R_DrawVisibleFloorSplats();
#endif
R_DrawMasked();
}
R_SetupFrame(player, skybox);
skyVisible = false;
R_SetupFrame(player);
framecount++;
validcount++;
@ -1185,19 +1057,21 @@ void R_RenderPlayerView(player_t *player)
#ifdef FLOORSPLATS
R_ClearVisibleFloorSplats();
#endif
Portal_InitList();
// check for new console commands.
NetUpdate();
// The head node is the last node output.
Mask_Pre(&masks[nummasks - 1]);
curdrawsegs = ds_p;
//profile stuff ---------------------------------------------------------
#ifdef TIMING
mytotal = 0;
ProfZeroTimer();
#endif
R_RenderBSPNode((INT32)numnodes - 1);
R_ClipSprites();
#ifdef TIMING
RDMSR(0x10, &mycount);
mytotal += mycount; // 64bit add
@ -1205,54 +1079,66 @@ void R_RenderPlayerView(player_t *player)
CONS_Debug(DBG_RENDER, "RenderBSPNode: 0x%d %d\n", *((INT32 *)&mytotal + 1), (INT32)mytotal);
#endif
//profile stuff ---------------------------------------------------------
Mask_Post(&masks[nummasks - 1]);
// PORTAL RENDERING
for(portal = portal_base; portal; portal = portal_base)
R_ClipSprites(drawsegs, NULL);
// Add skybox portals caused by sky visplanes.
if (cv_skybox.value && skyboxmo[0])
Portal_AddSkyboxPortals();
// Portal rendering. Hijacks the BSP traversal.
if (portal_base)
{
// render the portal
CONS_Debug(DBG_RENDER, "Rendering portal from line %d to %d\n", portal->line1, portal->line2);
portalrender = portal->pass;
portal_t *portal;
R_PortalFrame(&lines[portal->line1], &lines[portal->line2], portal);
for(portal = portal_base; portal; portal = portal_base)
{
portalrender = portal->pass; // Recursiveness depth.
R_PortalClearClipSegs(portal->start, portal->end);
R_ClearFFloorClips();
R_PortalRestoreClipValues(portal->start, portal->end, portal->ceilingclip, portal->floorclip, portal->frontscale);
// Apply the viewpoint stored for the portal.
R_PortalFrame(portal);
validcount++;
// Hack in the clipsegs to delimit the starting
// clipping for sprites and possibly other similar
// future items.
R_PortalClearClipSegs(portal->start, portal->end);
R_RenderBSPNode((INT32)numnodes - 1);
R_ClipSprites();
//R_DrawPlanes();
//R_DrawMasked();
// Hack in the top/bottom clip values for the window
// that were previously stored.
Portal_ClipApply(portal);
// okay done. free it.
portalcullsector = NULL; // Just in case...
portal_base = portal->next;
Z_Free(portal->ceilingclip);
Z_Free(portal->floorclip);
Z_Free(portal->frontscale);
Z_Free(portal);
validcount++;
masks = realloc(masks, (++nummasks)*sizeof(maskcount_t));
Mask_Pre(&masks[nummasks - 1]);
curdrawsegs = ds_p;
// Render the BSP from the new viewpoint, and clip
// any sprites with the new clipsegs and window.
R_RenderBSPNode((INT32)numnodes - 1);
Mask_Post(&masks[nummasks - 1]);
R_ClipSprites(ds_p - (masks[nummasks - 1].drawsegs[1] - masks[nummasks - 1].drawsegs[0]), portal);
Portal_Remove(portal);
}
}
// END PORTAL RENDERING
R_DrawPlanes();
#ifdef FLOORSPLATS
R_DrawVisibleFloorSplats();
#endif
// draw mid texture and sprite
// And now 3D floors/sides!
R_DrawMasked();
R_DrawMasked(masks, nummasks);
// Check for new console commands.
NetUpdate();
// save value to skyVisible1 or skyVisible2
// this is so that P1 can't affect whether P2 can see a skybox or not, or vice versa
if (splitscreen && player == &players[secondarydisplayplayer])
skyVisible2 = skyVisible;
else
skyVisible1 = skyVisible;
free(masks);
}
// =========================================================================

View file

@ -94,7 +94,7 @@ void R_ExecuteSetViewSize(void);
void R_SkyboxFrame(player_t *player);
void R_SetupFrame(player_t *player, boolean skybox);
void R_SetupFrame(player_t *player);
// Called by G_Drawer.
void R_RenderPlayerView(player_t *player);

View file

@ -23,6 +23,8 @@
#include "r_state.h"
#include "r_splats.h" // faB(21jan):testing
#include "r_sky.h"
#include "r_portal.h"
#include "v_video.h"
#include "w_wad.h"
#include "z_zone.h"
@ -43,9 +45,8 @@
//#define QUINCUNX
//SoM: 3/23/2000: Use Boom visplane hashing.
#define MAXVISPLANES 512
static visplane_t *visplanes[MAXVISPLANES];
visplane_t *visplanes[MAXVISPLANES];
static visplane_t *freetail;
static visplane_t **freehead = &freetail;
@ -112,50 +113,6 @@ void R_InitPlanes(void)
// FIXME: unused
}
// R_PortalStoreClipValues
// Saves clipping values for later. -Red
void R_PortalStoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale)
{
INT32 i;
for (i = 0; i < end-start; i++)
{
*ceil = ceilingclip[start+i];
ceil++;
*floor = floorclip[start+i];
floor++;
*scale = frontscale[start+i];
scale++;
}
}
// R_PortalRestoreClipValues
// Inverse of the above. Restores the old value!
void R_PortalRestoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale)
{
INT32 i;
for (i = 0; i < end-start; i++)
{
ceilingclip[start+i] = *ceil;
ceil++;
floorclip[start+i] = *floor;
floor++;
frontscale[start+i] = *scale;
scale++;
}
// HACKS FOLLOW
for (i = 0; i < start; i++)
{
floorclip[i] = -1;
ceilingclip[i] = (INT16)viewheight;
}
for (i = end; i < vid.width; i++)
{
floorclip[i] = -1;
ceilingclip[i] = (INT16)viewheight;
}
}
//
// R_MapPlane
//
@ -348,6 +305,23 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2)
#endif
}
void R_ClearFFloorClips (void)
{
INT32 i, p;
// opening / clipping determination
for (i = 0; i < viewwidth; i++)
{
for (p = 0; p < MAXFFLOORS; p++)
{
ffloor[p].f_clip[i] = (INT16)viewheight;
ffloor[p].c_clip[i] = -1;
}
}
numffloors = 0;
}
//
// R_ClearPlanes
// At begining of frame.
@ -370,8 +344,6 @@ void R_ClearPlanes(void)
}
}
numffloors = 0;
for (i = 0; i < MAXVISPLANES; i++)
for (*freehead = visplanes[i], visplanes[i] = NULL;
freehead && *freehead ;)
@ -723,16 +695,6 @@ static void R_DrawSkyPlane(visplane_t *pl)
INT32 x;
INT32 angle;
// If we're not supposed to draw the sky (e.g. for skyboxes), don't do anything!
// This probably utterly ruins sky rendering for FOFs and polyobjects, unfortunately
if (!viewsky)
{
// Mark that the sky was visible here for next tic
// (note: this is a hack and it sometimes can cause HOMs to appear for a tic IIRC)
skyVisible = true;
return;
}
// Reset column drawer function (note: couldn't we just call walldrawerfunc directly?)
// (that is, unless we'll need to switch drawers in future for some reason)
wallcolfunc = walldrawerfunc;

View file

@ -18,6 +18,8 @@
#include "r_data.h"
#include "p_polyobj.h"
#define MAXVISPLANES 512
//
// Now what is a visplane, anyway?
// Simple: kinda floor/ceiling polygon optimised for SRB2 rendering.
@ -53,6 +55,7 @@ typedef struct visplane_s
#endif
} visplane_t;
extern visplane_t *visplanes[MAXVISPLANES];
extern visplane_t *floorplane;
extern visplane_t *ceilingplane;
@ -72,9 +75,8 @@ extern fixed_t *yslope;
extern lighttable_t **planezlight;
void R_InitPlanes(void);
void R_PortalStoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale);
void R_PortalRestoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale);
void R_ClearPlanes(void);
void R_ClearFFloorClips (void);
void R_MapPlane(INT32 y, INT32 x1, INT32 x2);
void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2);
@ -122,4 +124,6 @@ typedef struct planemgr_s
extern visffloor_t ffloor[MAXFFLOORS];
extern INT32 numffloors;
void Portal_AddSkyboxPortals (void);
#endif

333
src/r_portal.c Normal file
View file

@ -0,0 +1,333 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 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_portal.c
/// \brief Software renderer portals.
#include "r_portal.h"
#include "r_plane.h"
#include "r_main.h"
#include "doomstat.h"
#include "p_spec.h" // Skybox viewpoints
#include "z_zone.h"
#include "r_things.h"
#include "r_sky.h"
UINT8 portalrender; /**< When rendering a portal, it establishes the depth of the current BSP traversal. */
// Linked list for portals.
portal_t *portal_base, *portal_cap;
line_t *portalclipline;
INT32 portalclipstart, portalclipend;
boolean portalline; // is curline a portal seg?
void Portal_InitList (void)
{
portalrender = 0;
portal_base = portal_cap = NULL;
}
/** Store the clipping window for a portal in its given range.
*
* The window is copied from the current window at the time
* the function is called, so it is useful for converting one-sided
* lines into portals.
*/
void Portal_ClipRange (portal_t* portal)
{
INT32 start = portal->start;
INT32 end = portal->end;
INT16 *ceil = portal->ceilingclip;
INT16 *floor = portal->floorclip;
fixed_t *scale = portal->frontscale;
INT32 i;
for (i = 0; i < end-start; i++)
{
*ceil = ceilingclip[start+i];
ceil++;
*floor = floorclip[start+i];
floor++;
*scale = frontscale[start+i];
scale++;
}
}
/** Apply the clipping window from a portal.
*/
void Portal_ClipApply (const portal_t* portal)
{
INT32 i;
INT32 start = portal->start;
INT32 end = portal->end;
INT16 *ceil = portal->ceilingclip;
INT16 *floor = portal->floorclip;
fixed_t *scale = portal->frontscale;
for (i = 0; i < end-start; i++)
{
ceilingclip[start+i] = *ceil;
ceil++;
floorclip[start+i] = *floor;
floor++;
frontscale[start+i] = *scale;
scale++;
}
// HACKS FOLLOW
for (i = 0; i < start; i++)
{
floorclip[i] = -1;
ceilingclip[i] = (INT16)viewheight;
}
for (i = end; i < vid.width; i++)
{
floorclip[i] = -1;
ceilingclip[i] = (INT16)viewheight;
}
}
static portal_t* Portal_Add (const INT16 x1, const INT16 x2)
{
portal_t *portal = Z_Malloc(sizeof(portal_t), PU_LEVEL, NULL);
INT16 *ceilingclipsave = Z_Malloc(sizeof(INT16)*(x2-x1 + 1), PU_LEVEL, NULL);
INT16 *floorclipsave = Z_Malloc(sizeof(INT16)*(x2-x1 + 1), PU_LEVEL, NULL);
fixed_t *frontscalesave = Z_Malloc(sizeof(fixed_t)*(x2-x1 + 1), PU_LEVEL, NULL);
// Linked list.
if (!portal_base)
{
portal_base = portal;
portal_cap = portal;
}
else
{
portal_cap->next = portal;
portal_cap = portal;
}
portal->next = NULL;
// Store clipping values so they can be restored once the portal is rendered.
portal->ceilingclip = ceilingclipsave;
portal->floorclip = floorclipsave;
portal->frontscale = frontscalesave;
portal->start = x1;
portal->end = x2;
// Increase recursion level.
portal->pass = portalrender+1;
return portal;
}
void Portal_Remove (portal_t* portal)
{
portal_base = portal->next;
Z_Free(portal->ceilingclip);
Z_Free(portal->floorclip);
Z_Free(portal->frontscale);
Z_Free(portal);
}
/** Creates a portal out of two lines and a determined screen range.
*
* line1 determines the entrance, and line2 the exit.
* x1 and x2 determine the screen's column bounds.
* The view's offset from the entry line center is obtained,
* and then rotated&translated to the exit line's center.
* When the portal renders, it will create the illusion of
* the two lines being seamed together.
*/
void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2)
{
portal_t* portal = Portal_Add(x1, x2);
// Offset the portal view by the linedef centers
line_t* start = &lines[line1];
line_t* dest = &lines[line2];
angle_t dangle = R_PointToAngle2(0,0,dest->dx,dest->dy) - R_PointToAngle2(start->dx,start->dy,0,0);
fixed_t disttopoint;
angle_t angtopoint;
vertex_t dest_c, start_c;
// looking glass center
start_c.x = (start->v1->x + start->v2->x) / 2;
start_c.y = (start->v1->y + start->v2->y) / 2;
// other side center
dest_c.x = (dest->v1->x + dest->v2->x) / 2;
dest_c.y = (dest->v1->y + dest->v2->y) / 2;
disttopoint = R_PointToDist2(start_c.x, start_c.y, viewx, viewy);
angtopoint = R_PointToAngle2(start_c.x, start_c.y, viewx, viewy);
angtopoint += dangle;
portal->viewx = dest_c.x + FixedMul(FINECOSINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
portal->viewy = dest_c.y + FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
portal->viewz = viewz + dest->frontsector->floorheight - start->frontsector->floorheight;
portal->viewangle = viewangle + dangle;
portal->clipline = line2;
Portal_ClipRange(portal);
portalline = true; // this tells R_StoreWallRange that curline is a portal seg
}
/** Store the clipping window for a portal using a visplane.
*
* Since visplanes top/bottom windows work in an identical way,
* it can just be copied almost directly.
*/
static void Portal_ClipVisplane (const visplane_t* plane, portal_t* portal)
{
INT16 start = portal->start;
INT16 end = portal->end;
INT32 i;
for (i = 0; i < end - start; i++)
{
// Invalid column.
if (plane->top[i + start] == 65535)
{
portal->ceilingclip[i] = -1;
portal->floorclip[i] = -1;
continue;
}
portal->ceilingclip[i] = plane->top[i + start] - 1;
portal->floorclip[i] = plane->bottom[i + start] + 1;
portal->frontscale[i] = INT32_MAX;
}
}
extern INT32 viewwidth;
static boolean TrimVisplaneBounds (const visplane_t* plane, INT16* start, INT16* end)
{
*start = plane->minx;
*end = plane->maxx + 1;
// Visplanes have 1-px pads on their sides (extra columns).
// Trim them, else it may render out of bounds.
if (*end > viewwidth)
*end = viewwidth;
if (!(*start < *end))
return true;
/** Trims a visplane's horizontal gap to match its render area.
*
* Visplanes' minx/maxx may sometimes exceed the area they're
* covering. This merely adjusts the boundaries to the next
* valid area.
*/
while (plane->bottom[*start] == 0 && plane->top[*start] == 65535 && *start < *end)
{
(*start)++;
}
while (plane->bottom[*end - 1] == 0 && plane->top[*start] == 65535 && *end > *start)
{
(*end)--;
}
return false;
}
/** Creates a skybox portal out of a visplane.
*
* Applies the necessary offsets and rotation to give
* a depth illusion to the skybox.
*/
void Portal_AddSkybox (const visplane_t* plane)
{
INT16 start, end;
mapheader_t *mh;
portal_t* portal;
if (TrimVisplaneBounds(plane, &start, &end))
return;
portal = Portal_Add(start, end);
Portal_ClipVisplane(plane, portal);
portal->viewx = skyboxmo[0]->x;
portal->viewy = skyboxmo[0]->y;
portal->viewz = skyboxmo[0]->z;
portal->viewangle = viewangle + skyboxmo[0]->angle;
mh = mapheaderinfo[gamemap-1];
// If a relative viewpoint exists, offset the viewpoint.
if (skyboxmo[1])
{
fixed_t x = 0, y = 0;
angle_t ang = skyboxmo[0]->angle>>ANGLETOFINESHIFT;
if (mh->skybox_scalex > 0)
x = (viewx - skyboxmo[1]->x) / mh->skybox_scalex;
else if (mh->skybox_scalex < 0)
x = (viewx - skyboxmo[1]->x) * -mh->skybox_scalex;
if (mh->skybox_scaley > 0)
y = (viewy - skyboxmo[1]->y) / mh->skybox_scaley;
else if (mh->skybox_scaley < 0)
y = (viewy - skyboxmo[1]->y) * -mh->skybox_scaley;
// Apply transform to account for the skybox viewport angle.
portal->viewx += FixedMul(x,FINECOSINE(ang)) - FixedMul(y, FINESINE(ang));
portal->viewy += FixedMul(x, FINESINE(ang)) + FixedMul(y,FINECOSINE(ang));
}
if (mh->skybox_scalez > 0)
portal->viewz += viewz / mh->skybox_scalez;
else if (mh->skybox_scalez < 0)
portal->viewz += viewz * -mh->skybox_scalez;
portal->clipline = -1;
}
/** Creates portals for the currently existing sky visplanes.
* The visplanes are also removed and cleared from the list.
*/
void Portal_AddSkyboxPortals (void)
{
visplane_t *pl;
INT32 i;
UINT16 count = 0;
for (i = 0; i < MAXVISPLANES; i++, pl++)
{
for (pl = visplanes[i]; pl; pl = pl->next)
{
if (pl->picnum == skyflatnum)
{
Portal_AddSkybox(pl);
pl->minx = 0;
pl->maxx = -1;
count++;
}
}
}
CONS_Debug(DBG_RENDER, "Skybox portals: %d\n", count);
}

59
src/r_portal.h Normal file
View file

@ -0,0 +1,59 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 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_portal.h
/// \brief Software renderer portal struct, functions, linked list extern.
#ifndef __R_PORTAL__
#define __R_PORTAL__
#include "r_data.h"
#include "r_plane.h" // visplanes
/** Portal structure for the software renderer.
*/
typedef struct portal_s
{
struct portal_s *next;
// Viewport.
fixed_t viewx;
fixed_t viewy;
fixed_t viewz;
angle_t viewangle;
UINT8 pass; /**< Keeps track of the portal's recursion depth. */
INT32 clipline; /**< Optional clipline for line-based portals. */
// Clipping information.
INT32 start; /**< First horizontal pixel coordinate to draw at. */
INT32 end; /**< Last horizontal pixel coordinate to draw at. */
INT16 *ceilingclip; /**< Temporary screen top clipping array. */
INT16 *floorclip; /**< Temporary screen bottom clipping array. */
fixed_t *frontscale;/**< Temporary screen bottom clipping array. */
} portal_t;
extern portal_t* portal_base;
extern portal_t* portal_cap;
extern UINT8 portalrender;
extern line_t *portalclipline;
extern INT32 portalclipstart, portalclipend;
void Portal_InitList (void);
void Portal_Remove (portal_t* portal);
void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2);
void Portal_AddSkybox (const visplane_t* plane);
void Portal_ClipRange (portal_t* portal);
void Portal_ClipApply (const portal_t* portal);
void Portal_AddSkyboxPortals (void);
#endif

View file

@ -15,6 +15,7 @@
#include "r_local.h"
#include "r_sky.h"
#include "r_portal.h"
#include "r_splats.h"
#include "w_wad.h"
@ -1737,6 +1738,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (ds_p == drawsegs+maxdrawsegs)
{
size_t curpos = curdrawsegs - drawsegs;
size_t pos = ds_p - drawsegs;
size_t newmax = maxdrawsegs ? maxdrawsegs*2 : 128;
if (firstseg)
@ -1744,6 +1746,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
drawsegs = Z_Realloc(drawsegs, newmax*sizeof (*drawsegs), PU_STATIC, NULL);
ds_p = drawsegs + pos;
maxdrawsegs = newmax;
curdrawsegs = drawsegs + curpos;
if (firstseg)
firstseg = drawsegs + (size_t)firstseg;
}

View file

@ -80,14 +80,8 @@ extern side_t *sides;
//
extern fixed_t viewx, viewy, viewz;
extern angle_t viewangle, aimingangle;
extern boolean viewsky, skyVisible;
extern boolean skyVisible1, skyVisible2; // saved values of skyVisible for P1 and P2, for splitscreen
extern sector_t *viewsector;
extern player_t *viewplayer;
extern UINT8 portalrender;
extern sector_t *portalcullsector;
extern line_t *portalclipline;
extern INT32 portalclipstart, portalclipend;
extern consvar_t cv_allowmlook;
extern consvar_t cv_maxportals;

View file

@ -24,6 +24,7 @@
#include "i_video.h" // rendermode
#include "r_things.h"
#include "r_plane.h"
#include "r_portal.h"
#include "p_tick.h"
#include "p_local.h"
#include "p_slopes.h"
@ -444,7 +445,7 @@ void R_AddSpriteDefs(UINT16 wadnum)
//
// GAME FUNCTIONS
//
static UINT32 visspritecount;
UINT32 visspritecount;
static UINT32 clippedvissprites;
static vissprite_t *visspritechunks[MAXVISSPRITES >> VISSPRITECHUNKBITS] = {NULL};
@ -1249,7 +1250,7 @@ static void R_ProjectSprite(mobj_t *thing)
}
// PORTAL SPRITE CLIPPING
if (portalrender)
if (portalrender && portalclipline)
{
if (x2 < portalclipstart || x1 > portalclipend)
return;
@ -1351,8 +1352,8 @@ static void R_ProjectSprite(mobj_t *thing)
{
if (vis->x1 < portalclipstart)
vis->x1 = portalclipstart;
if (vis->x2 > portalclipend)
vis->x2 = portalclipend;
if (vis->x2 >= portalclipend)
vis->x2 = portalclipend-1;
}
vis->xscale = xscale; //SoM: 4/17/2000
@ -1517,7 +1518,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
return;
// PORTAL SPRITE CLIPPING
if (portalrender)
if (portalrender && portalclipline)
{
if (x2 < portalclipstart || x1 > portalclipend)
return;
@ -1569,8 +1570,8 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
{
if (vis->x1 < portalclipstart)
vis->x1 = portalclipstart;
if (vis->x2 > portalclipend)
vis->x2 = portalclipend;
if (vis->x2 >= portalclipend)
vis->x2 = portalclipend-1;
}
vis->xscale = xscale; //SoM: 4/17/2000
@ -1696,9 +1697,7 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel)
//
// R_SortVisSprites
//
static vissprite_t vsprsortedhead;
void R_SortVisSprites(void)
static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 end)
{
UINT32 i, linkedvissprites = 0;
vissprite_t *ds, *dsprev, *dsnext, *dsfirst;
@ -1707,20 +1706,17 @@ void R_SortVisSprites(void)
fixed_t bestscale;
INT32 bestdispoffset;
if (!visspritecount)
return;
unsorted.next = unsorted.prev = &unsorted;
dsfirst = R_GetVisSprite(0);
dsfirst = R_GetVisSprite(start);
// The first's prev and last's next will be set to
// nonsense, but are fixed in a moment
for (i = 0, dsnext = dsfirst, ds = NULL; i < visspritecount; i++)
for (i = start, dsnext = dsfirst, ds = NULL; i < end; i++)
{
dsprev = ds;
ds = dsnext;
if (i < visspritecount - 1) dsnext = R_GetVisSprite(i + 1);
if (i < end - 1) dsnext = R_GetVisSprite(i + 1);
ds->next = dsnext;
ds->prev = dsprev;
@ -1798,8 +1794,8 @@ void R_SortVisSprites(void)
}
// pull the vissprites out by scale
vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead;
for (i = 0; i < visspritecount-linkedvissprites; i++)
vsprsortedhead->next = vsprsortedhead->prev = vsprsortedhead;
for (i = start; i < end-linkedvissprites; i++)
{
bestscale = bestdispoffset = INT32_MAX;
for (ds = unsorted.next; ds != &unsorted; ds = ds->next)
@ -1824,10 +1820,10 @@ void R_SortVisSprites(void)
}
best->next->prev = best->prev;
best->prev->next = best->next;
best->next = &vsprsortedhead;
best->prev = vsprsortedhead.prev;
vsprsortedhead.prev->next = best;
vsprsortedhead.prev = best;
best->next = vsprsortedhead;
best->prev = vsprsortedhead->prev;
vsprsortedhead->prev->next = best;
vsprsortedhead->prev = best;
}
}
@ -1837,28 +1833,28 @@ void R_SortVisSprites(void)
static drawnode_t *R_CreateDrawNode(drawnode_t *link);
static drawnode_t nodebankhead;
static drawnode_t nodehead;
static void R_CreateDrawNodes(void)
static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean tempskip)
{
drawnode_t *entry;
drawseg_t *ds;
INT32 i, p, best, x1, x2;
fixed_t bestdelta, delta;
vissprite_t *rover;
static vissprite_t vsprsortedhead;
drawnode_t *r2;
visplane_t *plane;
INT32 sintersect;
fixed_t scale = 0;
// Add the 3D floors, thicksides, and masked textures...
for (ds = ds_p; ds-- > drawsegs ;)
for (ds = drawsegs + mask->drawsegs[1]; ds-- > drawsegs + mask->drawsegs[0];)
{
if (ds->numthicksides)
{
for (i = 0; i < ds->numthicksides; i++)
{
entry = R_CreateDrawNode(&nodehead);
entry = R_CreateDrawNode(head);
entry->thickseg = ds;
entry->ffloor = ds->thicksides[i];
}
@ -1873,7 +1869,7 @@ static void R_CreateDrawNodes(void)
;
else {
// Put it in!
entry = R_CreateDrawNode(&nodehead);
entry = R_CreateDrawNode(head);
entry->plane = plane;
entry->seg = ds;
}
@ -1882,7 +1878,7 @@ static void R_CreateDrawNodes(void)
#endif
if (ds->maskedtexturecol)
{
entry = R_CreateDrawNode(&nodehead);
entry = R_CreateDrawNode(head);
entry->seg = ds;
}
if (ds->numffloorplanes)
@ -1913,7 +1909,7 @@ static void R_CreateDrawNodes(void)
}
if (best != -1)
{
entry = R_CreateDrawNode(&nodehead);
entry = R_CreateDrawNode(head);
entry->plane = ds->ffloorplanes[best];
entry->seg = ds;
ds->ffloorplanes[best] = NULL;
@ -1924,6 +1920,9 @@ static void R_CreateDrawNodes(void)
}
}
if (tempskip)
return;
#ifdef POLYOBJECTS_PLANES
// find all the remaining polyobject planes and add them on the end of the list
// probably this is a terrible idea if we wanted them to be sorted properly
@ -1940,17 +1939,19 @@ static void R_CreateDrawNodes(void)
PolyObjects[i].visplane = NULL;
continue;
}
entry = R_CreateDrawNode(&nodehead);
entry = R_CreateDrawNode(head);
entry->plane = plane;
// note: no seg is set, for what should be obvious reasons
PolyObjects[i].visplane = NULL;
}
#endif
if (visspritecount == 0)
// No vissprites in this mask?
if (mask->vissprites[1] - mask->vissprites[0] == 0)
return;
R_SortVisSprites();
R_SortVisSprites(&vsprsortedhead, mask->vissprites[0], mask->vissprites[1]);
for (rover = vsprsortedhead.prev; rover != &vsprsortedhead; rover = rover->prev)
{
if (rover->szt > vid.height || rover->sz < 0)
@ -1958,7 +1959,7 @@ static void R_CreateDrawNodes(void)
sintersect = (rover->x1 + rover->x2) / 2;
for (r2 = nodehead.next; r2 != &nodehead; r2 = r2->next)
for (r2 = head->next; r2 != head; r2 = r2->next)
{
if (r2->plane)
{
@ -2097,9 +2098,9 @@ static void R_CreateDrawNodes(void)
}
}
}
if (r2 == &nodehead)
if (r2 == head)
{
entry = R_CreateDrawNode(&nodehead);
entry = R_CreateDrawNode(head);
entry->sprite = rover;
}
}
@ -2141,25 +2142,24 @@ static void R_DoneWithNode(drawnode_t *node)
(node->prev = &nodebankhead)->next = node;
}
static void R_ClearDrawNodes(void)
static void R_ClearDrawNodes(drawnode_t* head)
{
drawnode_t *rover;
drawnode_t *next;
for (rover = nodehead.next; rover != &nodehead ;)
for (rover = head->next; rover != head;)
{
next = rover->next;
R_DoneWithNode(rover);
rover = next;
}
nodehead.next = nodehead.prev = &nodehead;
head->next = head->prev = head;
}
void R_InitDrawNodes(void)
{
nodebankhead.next = nodebankhead.prev = &nodebankhead;
nodehead.next = nodehead.prev = &nodehead;
}
//
@ -2185,7 +2185,7 @@ static void R_DrawPrecipitationSprite(vissprite_t *spr)
// R_ClipSprites
// Clips vissprites without drawing, so that portals can work. -Red
void R_ClipSprites(void)
void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
{
vissprite_t *spr;
for (; clippedvissprites < visspritecount; clippedvissprites++)
@ -2211,7 +2211,7 @@ void R_ClipSprites(void)
// and buggy, by going past LEFT end of array:
// for (ds = ds_p-1; ds >= drawsegs; ds--) old buggy code
for (ds = ds_p; ds-- > drawsegs ;)
for (ds = ds_p; ds-- > dsstart;)
{
// determine if the drawseg obscures the sprite
if (ds->x1 > spr->x2 ||
@ -2223,34 +2223,37 @@ void R_ClipSprites(void)
continue;
}
if (ds->portalpass > 0 && ds->portalpass <= portalrender)
continue; // is a portal
if (ds->portalpass != 66)
{
if (ds->portalpass > 0 && ds->portalpass <= portalrender)
continue; // is a portal
if (ds->scale1 > ds->scale2)
{
lowscale = ds->scale2;
scale = ds->scale1;
}
else
{
lowscale = ds->scale1;
scale = ds->scale2;
}
if (scale < spr->sortscale ||
(lowscale < spr->sortscale &&
!R_PointOnSegSide (spr->gx, spr->gy, ds->curline)))
{
// masked mid texture?
/*if (ds->maskedtexturecol)
R_RenderMaskedSegRange (ds, r1, r2);*/
// seg is behind sprite
continue;
}
}
r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1;
r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2;
if (ds->scale1 > ds->scale2)
{
lowscale = ds->scale2;
scale = ds->scale1;
}
else
{
lowscale = ds->scale1;
scale = ds->scale2;
}
if (scale < spr->sortscale ||
(lowscale < spr->sortscale &&
!R_PointOnSegSide (spr->gx, spr->gy, ds->curline)))
{
// masked mid texture?
/*if (ds->maskedtexturecol)
R_RenderMaskedSegRange (ds, r1, r2);*/
// seg is behind sprite
continue;
}
// clip this piece of the sprite
silhouette = ds->silhouette;
@ -2367,20 +2370,29 @@ void R_ClipSprites(void)
//Fab : 26-04-98: was -1, now clips against console bottom
spr->cliptop[x] = (INT16)con_clipviewtop;
}
if (portal)
{
for (x = spr->x1; x <= spr->x2; x++)
{
if (spr->clipbot[x] > portal->floorclip[x - portal->start])
spr->clipbot[x] = portal->floorclip[x - portal->start];
if (spr->cliptop[x] < portal->ceilingclip[x - portal->start])
spr->cliptop[x] = portal->ceilingclip[x - portal->start];
}
}
}
}
//
// R_DrawMasked
//
void R_DrawMasked(void)
static void R_DrawMaskedList (drawnode_t* head)
{
drawnode_t *r2;
drawnode_t *next;
R_CreateDrawNodes();
for (r2 = nodehead.next; r2 != &nodehead; r2 = r2->next)
for (r2 = head->next; r2 != head; r2 = r2->next)
{
if (r2->plane)
{
@ -2432,7 +2444,38 @@ void R_DrawMasked(void)
r2 = next;
}
}
R_ClearDrawNodes();
}
void R_DrawMasked(maskcount_t* masks, UINT8 nummasks)
{
drawnode_t heads[nummasks]; /**< Drawnode lists; as many as number of views/portals. */
INT8 i;
for (i = 0; i < nummasks; i++)
{
heads[i].next = heads[i].prev = &heads[i];
viewx = masks[i].viewx;
viewy = masks[i].viewy;
viewz = masks[i].viewz;
viewsector = masks[i].viewsector;
R_CreateDrawNodes(&masks[i], &heads[i], false);
}
//for (i = 0; i < nummasks; i++)
// CONS_Printf("Mask no.%d:\ndrawsegs: %d\n vissprites: %d\n\n", i, masks[i].drawsegs[1] - masks[i].drawsegs[0], masks[i].vissprites[1] - masks[i].vissprites[0]);
for (; nummasks > 0; nummasks--)
{
viewx = masks[nummasks - 1].viewx;
viewy = masks[nummasks - 1].viewy;
viewz = masks[nummasks - 1].viewz;
viewsector = masks[nummasks - 1].viewsector;
R_DrawMaskedList(&heads[nummasks - 1]);
R_ClearDrawNodes(&heads[nummasks - 1]);
}
}
// ==========================================================================

View file

@ -16,6 +16,7 @@
#include "sounds.h"
#include "r_plane.h"
#include "r_portal.h"
// "Left" and "Right" character symbols for additional rotation functionality
#define ROT_L ('L' - '0')
@ -45,7 +46,6 @@ extern fixed_t windowbottom;
void R_DrawMaskedColumn(column_t *column);
void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight);
void R_SortVisSprites(void);
//faB: find sprites in wadfile, replace existing, add new ones
// (only sprites from namelist are added or replaced)
@ -55,8 +55,21 @@ void R_AddSpriteDefs(UINT16 wadnum);
void R_AddSprites(sector_t *sec, INT32 lightlevel);
void R_InitSprites(void);
void R_ClearSprites(void);
void R_ClipSprites(void);
void R_DrawMasked(void);
void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
/** Used to count the amount of masked elements
* per portal to later group them in separate
* drawnode lists.
*/
typedef struct
{
size_t drawsegs[2];
size_t vissprites[2];
fixed_t viewx, viewy, viewz; /**< View z stored at the time of the BSP traversal for the view/portal. Masked sorting/drawing needs it. */
sector_t* viewsector;
} maskcount_t;
void R_DrawMasked(maskcount_t* masks, UINT8 nummasks);
// -----------
// SKINS STUFF
@ -207,6 +220,7 @@ typedef struct drawnode_s
extern INT32 numskins;
extern skin_t skins[MAXSKINS];
extern UINT32 visspritecount;
void SetPlayerSkin(INT32 playernum,const char *skinname);
void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002