// 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_plane.c /// \brief Here is a core component: drawing the floors and ceilings, /// while maintaining a per column clipping list only. /// Moreover, the sky areas have to be determined. #include "doomdef.h" #include "console.h" #include "g_game.h" #include "p_setup.h" // levelflats #include "p_slopes.h" #include "r_data.h" #include "r_local.h" #include "r_state.h" #include "r_splats.h" // faB(21jan):testing #include "r_sky.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" #include "p_tick.h" #ifdef TIMING #include "p5prof.h" INT64 mycount; INT64 mytotal = 0; UINT32 nombre = 100000; #endif // // opening // // Quincunx antialiasing of flats! //#define QUINCUNX // good night sweet prince //#define SHITPLANESPARENCY //SoM: 3/23/2000: Use Boom visplane hashing. #define MAXVISPLANES 512 static visplane_t *visplanes[MAXVISPLANES]; static visplane_t *freetail; static visplane_t **freehead = &freetail; visplane_t *floorplane; visplane_t *ceilingplane; static visplane_t *currentplane; visffloor_t ffloor[MAXFFLOORS]; INT32 numffloors; //SoM: 3/23/2000: Boom visplane hashing routine. #define visplane_hash(picnum,lightlevel,height) \ ((unsigned)((picnum)*3+(lightlevel)+(height)*7) & (MAXVISPLANES-1)) //SoM: 3/23/2000: Use boom opening limit removal size_t maxopenings; INT16 *openings, *lastopening; /// \todo free leak // // Clip values are the solid pixel bounding the range. // floorclip starts out SCREENHEIGHT // ceilingclip starts out -1 // INT16 floorclip[MAXVIDWIDTH], ceilingclip[MAXVIDWIDTH]; fixed_t frontscale[MAXVIDWIDTH]; // // spanstart holds the start of a plane span // initialized to 0 at start // static INT32 spanstart[MAXVIDHEIGHT]; // // texture mapping // lighttable_t **planezlight; static fixed_t planeheight; //added : 10-02-98: yslopetab is what yslope used to be, // yslope points somewhere into yslopetab, // now (viewheight/2) slopes are calculated above and // below the original viewheight for mouselook // (this is to calculate yslopes only when really needed) // (when mouselookin', yslope is moving into yslopetab) // Check R_SetupFrame, R_SetViewSize for more... fixed_t yslopetab[MAXVIDHEIGHT*16]; fixed_t *yslope; fixed_t basexscale, baseyscale; fixed_t cachedheight[MAXVIDHEIGHT]; fixed_t cacheddistance[MAXVIDHEIGHT]; fixed_t cachedxstep[MAXVIDHEIGHT]; fixed_t cachedystep[MAXVIDHEIGHT]; static fixed_t xoffs, yoffs; // // R_InitPlanes // Only at game startup. // 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 // // Uses global vars: // basexscale // baseyscale // centerx // viewx // viewy // viewsin // viewcos // viewheight #ifndef NOWATER static INT32 bgofs; static INT32 wtofs=0; static INT32 waterofs; static boolean itswater; #endif #ifndef NOWATER static void R_DrawTranslucentWaterSpan_8(void) { UINT32 xposition; UINT32 yposition; UINT32 xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; UINT8 *dsrc; size_t count; // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest // can be used for the fraction part. This allows calculation of the memory address in the // texture with two shifts, an OR and one AND. (see below) // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one // bit per power of two (obviously) // Ok, because I was able to eliminate the variable spot below, this function is now FASTER // than the original span renderer. Whodathunkit? xposition = ds_xfrac << nflatshiftup; yposition = (ds_yfrac + waterofs) << nflatshiftup; xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; dsrc = screens[1] + (ds_y+bgofs)*vid.width + ds_x1; count = ds_x2 - ds_x1 + 1; while (count >= 8) { // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't // have the uber complicated math to calculate it now, so that was a memory write we didn't // need! dest[0] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; dest[1] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; dest[2] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; dest[3] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; dest[4] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; dest[5] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; dest[6] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; dest[7] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; dest += 8; count -= 8; } while (count--) { *dest++ = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; } } #endif void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { angle_t angle, planecos, planesin; fixed_t distance, span; size_t pindex; #ifdef RANGECHECK if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight) I_Error("R_MapPlane: %d, %d at %d", x1, x2, y); #endif // from r_splats's R_RenderFloorSplat if (x1 >= vid.width) x1 = 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; #ifndef NOWATER if (itswater) { const INT32 yay = (wtofs + (distance>>9) ) & 8191; // ripples da water texture bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS; angle = (currentplane->viewangle + currentplane->plangle + xtoviewangle[x1])>>ANGLETOFINESHIFT; angle = (angle + 2048) & 8191; // 90 degrees ds_xfrac += FixedMul(FINECOSINE(angle), (bgofs<=viewheight) bgofs = viewheight-y-1; if (y+bgofs<0) bgofs = -y; } #endif pindex = distance >> LIGHTZSHIFT; if (pindex >= MAXLIGHTZ) pindex = MAXLIGHTZ - 1; #ifdef ESLOPE if (currentplane->slope) ds_colormap = colormaps; else #endif ds_colormap = planezlight[pindex]; if (encoremap && !currentplane->noencore) ds_colormap += (256*32); if (currentplane->extra_colormap) ds_colormap = currentplane->extra_colormap->colormap + (ds_colormap - colormaps); ds_y = y; ds_x1 = x1; ds_x2 = x2; // profile drawer #ifdef TIMING ProfZeroTimer(); #endif spanfunc(); #ifdef TIMING RDMSR(0x10, &mycount); mytotal += mycount; // 64bit add if (!(nombre--)) I_Error("spanfunc() CPU Spy reports: 0x%d %d\n", *((INT32 *)&mytotal+1), (INT32)mytotal); #endif } // // R_ClearPlanes // At begining of frame. // void R_ClearPlanes(void) { INT32 i, p; angle_t angle; // opening / clipping determination for (i = 0; i < viewwidth; i++) { floorclip[i] = (INT16)viewheight; ceilingclip[i] = -1; frontscale[i] = INT32_MAX; for (p = 0; p < MAXFFLOORS; p++) { ffloor[p].f_clip[i] = (INT16)viewheight; ffloor[p].c_clip[i] = -1; } } numffloors = 0; for (i = 0; i < MAXVISPLANES; i++) for (*freehead = visplanes[i], visplanes[i] = NULL; freehead && *freehead ;) { freehead = &(*freehead)->next; } lastopening = openings; // texture calculation memset(cachedheight, 0, sizeof (cachedheight)); // left to right mapping angle = (viewangle-ANGLE_90)>>ANGLETOFINESHIFT; // scale will be unit scale at SCREENWIDTH/2 distance basexscale = FixedDiv (FINECOSINE(angle),centerxfrac); baseyscale = -FixedDiv (FINESINE(angle),centerxfrac); } static visplane_t *new_visplane(unsigned hash) { visplane_t *check = freetail; if (!check) { check = calloc(2, sizeof (*check)); if (check == NULL) I_Error("%s: Out of memory", "new_visplane"); // FIXME: ugly } else { freetail = freetail->next; if (!freetail) freehead = &freetail; } check->next = visplanes[hash]; visplanes[hash] = check; return check; } // // R_FindPlane: Seek a visplane having the identical values: // Same height, same flattexture, same lightlevel. // If not, allocates another of them. // visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t xoff, fixed_t yoff, angle_t plangle, extracolormap_t *planecolormap, ffloor_t *pfloor #ifdef POLYOBJECTS_PLANES , polyobj_t *polyobj #endif #ifdef ESLOPE , pslope_t *slope #endif , boolean noencore) { visplane_t *check; unsigned hash; #ifdef ESLOPE if (slope); else // Don't mess with this right now if a slope is involved #endif { xoff += viewx; yoff -= viewy; if (plangle != 0) { // Add the view offset, rotated by the plane angle. fixed_t cosinecomponent = FINECOSINE(plangle>>ANGLETOFINESHIFT); fixed_t sinecomponent = FINESINE(plangle>>ANGLETOFINESHIFT); fixed_t oldxoff = xoff; xoff = FixedMul(xoff,cosinecomponent)+FixedMul(yoff,sinecomponent); yoff = -FixedMul(oldxoff,sinecomponent)+FixedMul(yoff,cosinecomponent); } } #ifdef POLYOBJECTS_PLANES if (polyobj) { if (polyobj->angle != 0) { angle_t fineshift = polyobj->angle >> ANGLETOFINESHIFT; xoff -= FixedMul(FINECOSINE(fineshift), polyobj->centerPt.x)+FixedMul(FINESINE(fineshift), polyobj->centerPt.y); yoff -= FixedMul(FINESINE(fineshift), polyobj->centerPt.x)-FixedMul(FINECOSINE(fineshift), polyobj->centerPt.y); } else { xoff -= polyobj->centerPt.x; yoff += polyobj->centerPt.y; } } #endif // This appears to fix the Nimbus Ruins sky bug. if (picnum == skyflatnum && pfloor) { height = 0; // all skies map together lightlevel = 0; } // New visplane algorithm uses hash table hash = visplane_hash(picnum, lightlevel, height); for (check = visplanes[hash]; check; check = check->next) { #ifdef POLYOBJECTS_PLANES if (check->polyobj && pfloor) continue; if (polyobj != check->polyobj) continue; #endif if (height == check->height && picnum == check->picnum && lightlevel == check->lightlevel && xoff == check->xoffs && yoff == check->yoffs && planecolormap == check->extra_colormap && !pfloor && !check->ffloor && check->viewx == viewx && check->viewy == viewy && check->viewz == viewz && check->viewangle == viewangle && check->plangle == plangle #ifdef ESLOPE && check->slope == slope #endif && check->noencore == noencore) { return check; } } check = new_visplane(hash); check->height = height; check->picnum = picnum; check->lightlevel = lightlevel; check->minx = vid.width; check->maxx = -1; check->xoffs = xoff; check->yoffs = yoff; check->extra_colormap = planecolormap; check->ffloor = pfloor; check->viewx = viewx; check->viewy = viewy; check->viewz = viewz; check->viewangle = viewangle; check->plangle = plangle; #ifdef POLYOBJECTS_PLANES check->polyobj = polyobj; #endif #ifdef ESLOPE check->slope = slope; #endif check->noencore = noencore; memset(check->top, 0xff, sizeof (check->top)); memset(check->bottom, 0x00, sizeof (check->bottom)); return check; } // // R_CheckPlane: return same visplane or alloc a new one if needed // visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop) { INT32 intrl, intrh; INT32 unionl, unionh; INT32 x; if (start < pl->minx) { intrl = pl->minx; unionl = start; } else { unionl = pl->minx; intrl = start; } if (stop > pl->maxx) { intrh = pl->maxx; unionh = stop; } else { unionh = pl->maxx; intrh = stop; } // 0xff is not equal to -1 with shorts... for (x = intrl; x <= intrh; x++) if (pl->top[x] != 0xffff || pl->bottom[x] != 0x0000) break; if (x > intrh) /* Can use existing plane; extend range */ { pl->minx = unionl; pl->maxx = unionh; } else /* Cannot use existing plane; create a new one */ { unsigned hash = visplane_hash(pl->picnum, pl->lightlevel, pl->height); visplane_t *new_pl = new_visplane(hash); new_pl->height = pl->height; new_pl->picnum = pl->picnum; new_pl->lightlevel = pl->lightlevel; new_pl->xoffs = pl->xoffs; new_pl->yoffs = pl->yoffs; new_pl->extra_colormap = pl->extra_colormap; new_pl->ffloor = pl->ffloor; new_pl->viewx = pl->viewx; new_pl->viewy = pl->viewy; new_pl->viewz = pl->viewz; new_pl->viewangle = pl->viewangle; new_pl->plangle = pl->plangle; #ifdef POLYOBJECTS_PLANES new_pl->polyobj = pl->polyobj; #endif #ifdef ESLOPE new_pl->slope = pl->slope; #endif new_pl->noencore = pl->noencore; pl = new_pl; pl->minx = start; pl->maxx = stop; memset(pl->top, 0xff, sizeof pl->top); memset(pl->bottom, 0x00, sizeof pl->bottom); } return pl; } // // R_ExpandPlane // // This function basically expands the visplane or I_Errors. // The reason for this is that when creating 3D floor planes, there is no // need to create new ones with R_CheckPlane, because 3D floor planes // are created by subsector and there is no way a subsector can graphically // overlap. void R_ExpandPlane(visplane_t *pl, INT32 start, INT32 stop) { INT32 unionl, unionh; // INT32 x; #ifdef POLYOBJECTS_PLANES // Don't expand polyobject planes here - we do that on our own. if (pl->polyobj) return; #endif if (start < pl->minx) { unionl = start; } else { unionl = pl->minx; } if (stop > pl->maxx) { unionh = stop; } else { unionh = pl->maxx; } /* for (x = start; x <= stop; x++) if (pl->top[x] != 0xffff || pl->bottom[x] != 0x0000) break; if (x <= stop) I_Error("R_ExpandPlane: planes in same subsector overlap?!\nminx: %d, maxx: %d, start: %d, stop: %d\n", pl->minx, pl->maxx, start, stop); */ pl->minx = unionl, pl->maxx = unionh; } // // R_MakeSpans // void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2) { // Alam: from r_splats's R_RenderFloorSplat if (t1 >= vid.height) t1 = vid.height-1; if (b1 >= vid.height) b1 = vid.height-1; if (t2 >= vid.height) t2 = vid.height-1; if (b2 >= vid.height) b2 = vid.height-1; if (x-1 >= vid.width) x = vid.width; while (t1 < t2 && t1 <= b1) { R_MapPlane(t1, spanstart[t1], x - 1); t1++; } while (b1 > b2 && b1 >= t1) { R_MapPlane(b1, spanstart[b1], x - 1); b1--; } while (t2 < t1 && t2 <= b2) spanstart[t2++] = x; while (b2 > b1 && b2 >= t2) spanstart[b2--] = x; } void R_DrawPlanes(void) { visplane_t *pl; INT32 x; INT32 angle; INT32 i; spanfunc = basespanfunc; wallcolfunc = walldrawerfunc; for (i = 0; i < MAXVISPLANES; i++, pl++) { for (pl = visplanes[i]; pl; pl = pl->next) { // sky flat if (pl->picnum == skyflatnum) { if (!viewsky) { skyVisible = true; continue; } // use correct aspect ratio scale dc_iscale = skyscale; // Sky is always drawn full bright, // i.e. colormaps[0] is used. // Because of this hack, sky is not affected // by INVUL inverse mapping. dc_colormap = colormaps; if (encoremap) dc_colormap += (256*32); dc_texturemid = skytexturemid; dc_texheight = textureheight[skytexture] >>FRACBITS; for (x = pl->minx; x <= pl->maxx; x++) { dc_yl = pl->top[x]; dc_yh = pl->bottom[x]; if (dc_yl <= dc_yh) { angle = (pl->viewangle + xtoviewangle[x])>>ANGLETOSKYSHIFT; dc_iscale = FixedMul(skyscale, FINECOSINE(xtoviewangle[x]>>ANGLETOFINESHIFT)); dc_x = x; dc_source = R_GetColumn(texturetranslation[skytexture], angle); wallcolfunc(); } } continue; } if (pl->ffloor != NULL #ifdef POLYOBJECTS_PLANES || pl->polyobj != NULL #endif ) continue; R_DrawSinglePlane(pl); } } #ifndef NOWATER waterofs = (leveltime & 1)*16384; wtofs = leveltime * 140; #endif } void R_DrawSinglePlane(visplane_t *pl) { INT32 light = 0; INT32 x; INT32 stop, angle; size_t size; ffloor_t *rover; if (!(pl->minx <= pl->maxx)) return; #ifndef NOWATER itswater = false; #endif spanfunc = basespanfunc; #ifdef POLYOBJECTS_PLANES if (pl->polyobj && pl->polyobj->translucency != 0) { spanfunc = R_DrawTranslucentSpan_8; // Hacked up support for alpha value in software mode Tails 09-24-2002 (sidenote: ported to polys 10-15-2014, there was no time travel involved -Red) if (pl->polyobj->translucency >= 10) return; // Don't even draw it else if (pl->polyobj->translucency > 0) ds_transmap = transtables + ((pl->polyobj->translucency-1)<extra_colormap && pl->extra_colormap->fog)) #else if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2)) #endif light = (pl->lightlevel >> LIGHTSEGSHIFT); else light = LIGHTLEVELS-1; } else #endif if (pl->ffloor) { // Don't draw planes that shouldn't be drawn. for (rover = pl->ffloor->target->ffloors; rover; rover = rover->next) { if ((pl->ffloor->flags & FF_CUTEXTRA) && (rover->flags & FF_EXTRA)) { if (pl->ffloor->flags & FF_EXTRA) { // The plane is from an extra 3D floor... Check the flags so // there are no undesired cuts. if (((pl->ffloor->flags & (FF_FOG|FF_SWIMMABLE)) == (rover->flags & (FF_FOG|FF_SWIMMABLE))) && pl->height < *rover->topheight && pl->height > *rover->bottomheight) return; } } } if (pl->ffloor->flags & FF_TRANSLUCENT) { spanfunc = R_DrawTranslucentSpan_8; // Hacked up support for alpha value in software mode Tails 09-24-2002 if (pl->ffloor->alpha < 12) return; // Don't even draw it else if (pl->ffloor->alpha < 38) ds_transmap = transtables + ((tr_trans90-1)<ffloor->alpha < 64) ds_transmap = transtables + ((tr_trans80-1)<ffloor->alpha < 89) ds_transmap = transtables + ((tr_trans70-1)<ffloor->alpha < 115) ds_transmap = transtables + ((tr_trans60-1)<ffloor->alpha < 140) ds_transmap = transtables + ((tr_trans50-1)<ffloor->alpha < 166) ds_transmap = transtables + ((tr_trans40-1)<ffloor->alpha < 192) ds_transmap = transtables + ((tr_trans30-1)<ffloor->alpha < 217) ds_transmap = transtables + ((tr_trans20-1)<ffloor->alpha < 243) ds_transmap = transtables + ((tr_trans10-1)<extra_colormap && pl->extra_colormap->fog)) #else if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2)) #endif light = (pl->lightlevel >> LIGHTSEGSHIFT); else light = LIGHTLEVELS-1; } else if (pl->ffloor->flags & FF_FOG) { spanfunc = R_DrawFogSpan_8; light = (pl->lightlevel >> LIGHTSEGSHIFT); } else light = (pl->lightlevel >> LIGHTSEGSHIFT); #ifndef NOWATER if (pl->ffloor->flags & FF_RIPPLE #ifdef ESLOPE && !pl->slope #endif ) { INT32 top, bottom; UINT8 *scr; itswater = true; if (spanfunc == R_DrawTranslucentSpan_8) { spanfunc = R_DrawTranslucentWaterSpan_8; // Copy the current scene, ugh top = pl->high-8; bottom = pl->low+8; if (top < 0) top = 0; if (bottom > vid.height) bottom = vid.height; if (splitscreen > 2 && viewplayer == &players[displayplayers[3]]) // Only copy the part of the screen we need scr = (screens[0] + (top+(viewheight))*vid.width + viewwidth); else if ((splitscreen == 1 && viewplayer == &players[displayplayers[1]]) || (splitscreen > 1 && viewplayer == &players[displayplayers[2]])) scr = (screens[0] + (top+(viewheight))*vid.width); else if (splitscreen > 1 && viewplayer == &players[displayplayers[1]]) scr = (screens[0] + ((top)*vid.width) + viewwidth); else scr = (screens[0] + ((top)*vid.width)); VID_BlitLinearScreen(scr, screens[1]+((top)*vid.width), vid.width, bottom-top, vid.width, vid.width); } } #endif } else light = (pl->lightlevel >> LIGHTSEGSHIFT); #ifdef ESLOPE if (!pl->slope) // Don't mess with angle on slopes! We'll handle this ourselves later #endif if (viewangle != pl->viewangle+pl->plangle) { memset(cachedheight, 0, sizeof (cachedheight)); angle = (pl->viewangle+pl->plangle-ANGLE_90)>>ANGLETOFINESHIFT; basexscale = FixedDiv(FINECOSINE(angle),centerxfrac); baseyscale = -FixedDiv(FINESINE(angle),centerxfrac); viewangle = pl->viewangle+pl->plangle; } currentplane = pl; ds_source = (UINT8 *) W_CacheLumpNum(levelflats[pl->picnum].lumpnum, PU_STATIC); // Stay here until Z_ChangeTag size = W_LumpLength(levelflats[pl->picnum].lumpnum); switch (size) { case 4194304: // 2048x2048 lump nflatmask = 0x3FF800; nflatxshift = 21; nflatyshift = 10; nflatshiftup = 5; break; case 1048576: // 1024x1024 lump nflatmask = 0xFFC00; nflatxshift = 22; nflatyshift = 12; nflatshiftup = 6; break; case 262144:// 512x512 lump' nflatmask = 0x3FE00; nflatxshift = 23; nflatyshift = 14; nflatshiftup = 7; break; case 65536: // 256x256 lump nflatmask = 0xFF00; nflatxshift = 24; nflatyshift = 16; nflatshiftup = 8; break; case 16384: // 128x128 lump nflatmask = 0x3F80; nflatxshift = 25; nflatyshift = 18; nflatshiftup = 9; break; case 1024: // 32x32 lump nflatmask = 0x3E0; nflatxshift = 27; nflatyshift = 22; nflatshiftup = 11; break; default: // 64x64 lump nflatmask = 0xFC0; nflatxshift = 26; nflatyshift = 20; nflatshiftup = 10; break; } xoffs = pl->xoffs; yoffs = pl->yoffs; planeheight = abs(pl->height - pl->viewz); if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; if (light < 0) light = 0; #ifdef ESLOPE if (pl->slope) { // Potentially override other stuff for now cus we're mean. :< But draw a slope plane! // I copied ZDoom's code and adapted it to SRB2... -fickle floatv3_t p, m, n; float ang; float vx, vy, vz; // compiler complains when P_GetZAt is used in FLOAT_TO_FIXED directly // use this as a temp var to store P_GetZAt's return value each time fixed_t temp; // Okay, look, don't ask me why this works, but without this setup there's a disgusting-looking misalignment with the textures. -fickle const float fudge = ((1<plangle & (ANGLE_90-1)); if (hack) { /* Essentially: We can't & the components along the regular axes when the plane is rotated. This is because the distance on each regular axis in order to loop is different. We rotate them, & the components, add them together, & them again, and then rotate them back. These three seperate & operations are done per axis in order to prevent overflows. toast 10/04/17 --- ...of coooourse, this still isn't perfect. but it looks... merely kind of grody, rather than completely wrong? idk. i'm just backporting this to kart right now. if anyone else wants to ever try dig around: it's drifting towards 0,0, and no, multiplying by fudge doesn't fix it. toast 27/09/18 */ const fixed_t cosinecomponent = FINECOSINE(hack>>ANGLETOFINESHIFT); const fixed_t sinecomponent = FINESINE(hack>>ANGLETOFINESHIFT); const fixed_t modmask = ((1 << (32-nflatshiftup)) - 1); fixed_t ox = (FixedMul(pl->slope->o.x,cosinecomponent) & modmask) - (FixedMul(pl->slope->o.y,sinecomponent) & modmask); fixed_t oy = (-FixedMul(pl->slope->o.x,sinecomponent) & modmask) - (FixedMul(pl->slope->o.y,cosinecomponent) & modmask); temp = ox & modmask; oy &= modmask; ox = FixedMul(temp,cosinecomponent)+FixedMul(oy,-sinecomponent); // negative sine for opposite direction oy = -FixedMul(temp,-sinecomponent)+FixedMul(oy,cosinecomponent); temp = xoffs; xoffs = (FixedMul(temp,cosinecomponent) & modmask) + (FixedMul(yoffs,sinecomponent) & modmask); yoffs = (-FixedMul(temp,sinecomponent) & modmask) + (FixedMul(yoffs,cosinecomponent) & modmask); temp = xoffs & modmask; yoffs &= modmask; xoffs = FixedMul(temp,cosinecomponent)+FixedMul(yoffs,-sinecomponent); // ditto yoffs = -FixedMul(temp,-sinecomponent)+FixedMul(yoffs,cosinecomponent); xoffs -= (pl->slope->o.x - ox); yoffs += (pl->slope->o.y + oy); } else { xoffs &= ((1 << (32-nflatshiftup))-1); yoffs &= ((1 << (32-nflatshiftup))-1); xoffs -= (pl->slope->o.x + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); yoffs += (pl->slope->o.y + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); } xoffs = (fixed_t)(xoffs*fudge); yoffs = (fixed_t)(yoffs/fudge); vx = FIXED_TO_FLOAT(pl->viewx+xoffs); vy = FIXED_TO_FLOAT(pl->viewy-yoffs); vz = FIXED_TO_FLOAT(pl->viewz); temp = P_GetZAt(pl->slope, pl->viewx, pl->viewy); zeroheight = FIXED_TO_FLOAT(temp); #define ANG2RAD(angle) ((float)((angle)*M_PIl)/ANGLE_180) // p is the texture origin in view space // Don't add in the offsets at this stage, because doing so can result in // errors if the flat is rotated. ang = ANG2RAD(ANGLE_270 - pl->viewangle); p.x = vx * cos(ang) - vy * sin(ang); p.z = vx * sin(ang) + vy * cos(ang); temp = P_GetZAt(pl->slope, -xoffs, yoffs); p.y = FIXED_TO_FLOAT(temp) - vz; // m is the v direction vector in view space ang = ANG2RAD(ANGLE_180 - (pl->viewangle + pl->plangle)); m.x = cos(ang); m.z = sin(ang); // n is the u direction vector in view space n.x = sin(ang); n.z = -cos(ang); ang = ANG2RAD(pl->plangle); temp = P_GetZAt(pl->slope, pl->viewx + FLOAT_TO_FIXED(sin(ang)), pl->viewy + FLOAT_TO_FIXED(cos(ang))); m.y = FIXED_TO_FLOAT(temp) - zeroheight; temp = P_GetZAt(pl->slope, pl->viewx + FLOAT_TO_FIXED(cos(ang)), pl->viewy - FLOAT_TO_FIXED(sin(ang))); n.y = FIXED_TO_FLOAT(temp) - zeroheight; m.x /= fudge; m.y /= fudge; m.z /= fudge; n.x *= fudge; n.y *= fudge; n.z *= fudge; // Eh. I tried making this stuff fixed-point and it exploded on me. Here's a macro for the only floating-point vector function I recall using. #define CROSS(d, v1, v2) \ d.x = (v1.y * v2.z) - (v1.z * v2.y);\ d.y = (v1.z * v2.x) - (v1.x * v2.z);\ d.z = (v1.x * v2.y) - (v1.y * v2.x) CROSS(ds_su, p, m); CROSS(ds_sv, p, n); CROSS(ds_sz, m, n); #undef CROSS ds_su.z *= focallengthf; ds_sv.z *= focallengthf; ds_sz.z *= focallengthf; // Premultiply the texture vectors with the scale factors #define SFMULT 65536.f*(1<top[pl->maxx+1] = 0xffff; pl->top[pl->minx-1] = 0xffff; pl->bottom[pl->maxx+1] = 0x0000; pl->bottom[pl->minx-1] = 0x0000; stop = pl->maxx + 1; if (viewx != pl->viewx || viewy != pl->viewy) { viewx = pl->viewx; viewy = pl->viewy; } if (viewz != pl->viewz) viewz = pl->viewz; for (x = pl->minx; x <= stop; x++) { R_MakeSpans(x, pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); } /* QUINCUNX anti-aliasing technique (sort of) Normally, Quincunx antialiasing staggers pixels in a 5-die pattern like so: o o o o o To simulate this, we offset the plane by FRACUNIT/4 in each direction, and draw at 50% translucency. The result is a 'smoothing' of the texture while using the palette colors. */ #ifdef QUINCUNX if (spanfunc == R_DrawSpan_8) { INT32 i; ds_transmap = transtables + ((tr_trans50-1)<xoffs; yoffs = pl->yoffs; switch(i) { case 0: xoffs -= FRACUNIT/4; yoffs -= FRACUNIT/4; break; case 1: xoffs -= FRACUNIT/4; yoffs += FRACUNIT/4; break; case 2: xoffs += FRACUNIT/4; yoffs -= FRACUNIT/4; break; case 3: xoffs += FRACUNIT/4; yoffs += FRACUNIT/4; break; } planeheight = abs(pl->height - pl->viewz); if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; if (light < 0) light = 0; planezlight = zlight[light]; // set the maximum value for unsigned pl->top[pl->maxx+1] = 0xffff; pl->top[pl->minx-1] = 0xffff; pl->bottom[pl->maxx+1] = 0x0000; pl->bottom[pl->minx-1] = 0x0000; stop = pl->maxx + 1; for (x = pl->minx; x <= stop; x++) R_MakeSpans(x, pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); } } #endif Z_ChangeTag(ds_source, PU_CACHE); } void R_PlaneBounds(visplane_t *plane) { INT32 i; INT32 hi, low; hi = plane->top[plane->minx]; low = plane->bottom[plane->minx]; for (i = plane->minx + 1; i <= plane->maxx; i++) { if (plane->top[i] < hi) hi = plane->top[i]; if (plane->bottom[i] > low) low = plane->bottom[i]; } plane->high = hi; plane->low = low; }