From 082d9206e89113533f4f6f7a8798f872e9a1ea39 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 6 Sep 2022 12:36:59 -0700 Subject: [PATCH 1/4] Add a hitbox renderer to Software mode renderhitbox - Tangible - collision activating objects, minus rings - All - every object - Intangible - the opposite of Tangible, also no rings - Rings - rings --- src/Sourcefile | 1 + src/d_netcmd.c | 1 + src/p_mobj.h | 2 +- src/r_bbox.c | 303 +++++++++++++++++++++++++++++++++++++++++++++++++ src/r_things.c | 8 ++ src/r_things.h | 6 + src/screen.h | 2 +- 7 files changed, 321 insertions(+), 2 deletions(-) create mode 100644 src/r_bbox.c diff --git a/src/Sourcefile b/src/Sourcefile index 986b75f74..ea5f2d0d5 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -65,6 +65,7 @@ r_skins.c r_sky.c r_splats.c r_things.c +r_bbox.c r_textures.c r_patch.c r_patchrotation.c diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 76034f927..df31f52f6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1008,6 +1008,7 @@ void D_RegisterClientCommands(void) // screen.c CV_RegisterVar(&cv_fullscreen); CV_RegisterVar(&cv_renderview); + CV_RegisterVar(&cv_renderhitbox); CV_RegisterVar(&cv_vhseffect); CV_RegisterVar(&cv_shittyscreen); CV_RegisterVar(&cv_renderer); diff --git a/src/p_mobj.h b/src/p_mobj.h index 3b947bec6..d6b176828 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -122,7 +122,7 @@ typedef enum MF_AMBIENT = 1<<10, // Slide this object when it hits a wall. MF_SLIDEME = 1<<11, - // Player cheat. + // Don't collide with walls or solid objects. Two MF_NOCLIP objects can't touch each other at all! MF_NOCLIP = 1<<12, // Allow moves to any height, no gravity. For active floaters. MF_FLOAT = 1<<13, diff --git a/src/r_bbox.c b/src/r_bbox.c new file mode 100644 index 000000000..ef72488fb --- /dev/null +++ b/src/r_bbox.c @@ -0,0 +1,303 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2021 by Sonic Team Junior. +// Copyright (C) 2022 by Kart Krew. +// +// 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_bbox.c +/// \brief Boundary box (cube) renderer + +#include "doomdef.h" +#include "command.h" +#include "r_fps.h" +#include "r_local.h" +#include "screen.h" // cv_renderhitbox +#include "v_video.h" // V_DrawFill + +enum { + RENDERHITBOX_OFF, + RENDERHITBOX_TANGIBLE, + RENDERHITBOX_ALL, + RENDERHITBOX_INTANGIBLE, + RENDERHITBOX_RINGS, +}; + +static CV_PossibleValue_t renderhitbox_cons_t[] = { + {RENDERHITBOX_OFF, "Off"}, + {RENDERHITBOX_TANGIBLE, "Tangible"}, + {RENDERHITBOX_ALL, "All"}, + {RENDERHITBOX_INTANGIBLE, "Intangible"}, + {RENDERHITBOX_RINGS, "Rings"}, + {0}}; + +consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", 0, renderhitbox_cons_t, NULL); + +struct bbox_col { + INT32 x; + INT32 y; + INT32 h; +}; + +struct bbox_config { + fixed_t height; + fixed_t tz; + struct bbox_col col[4]; + UINT8 color; +}; + +static inline void +raster_bbox_seg +( INT32 x, + fixed_t y, + fixed_t h, + UINT8 pixel) +{ + y /= FRACUNIT; + + if (y < 0) + y = 0; + + h = y + (FixedCeil(abs(h)) / FRACUNIT); + + if (h >= viewheight) + h = viewheight; + + while (y < h) + { + topleft[x + y * vid.width] = pixel; + y++; + } +} + +static void +draw_bbox_col +( struct bbox_config * bb, + int p, + fixed_t tx, + fixed_t ty) +{ + struct bbox_col *col = &bb->col[p]; + + fixed_t xscale = FixedDiv(projection[viewssnum], ty); + fixed_t yscale = FixedDiv(projectiony[viewssnum], ty); + + col->x = (centerxfrac + FixedMul(tx, xscale)) / FRACUNIT; + col->y = (centeryfrac - FixedMul(bb->tz, yscale)); + col->h = FixedMul(bb->height, yscale); + + // Using this function is TOO EASY! + V_DrawFill( + viewwindowx + col->x, + viewwindowy + col->y / FRACUNIT, 1, + col->h / FRACUNIT, V_NOSCALESTART | bb->color); +} + +static void +draw_bbox_row +( struct bbox_config * bb, + int p1, + int p2) +{ + struct bbox_col + *a = &bb->col[p1], + *b = &bb->col[p2]; + + INT32 x1, x2; // left, right + INT32 dx; // width + + fixed_t y1, y2; // top, bottom + fixed_t s1, s2; // top and bottom increment + + if (a->x > b->x) + { + struct bbox_col *c = a; + a = b; + b = c; + } + + x1 = a->x; + x2 = b->x; + + if (x2 >= viewwidth) + x2 = viewwidth - 1; + + if (x1 == x2 || x1 >= viewwidth || x2 < 0) + return; + + dx = x2 - x1; + + y1 = a->y; + y2 = b->y; + s1 = (y2 - y1) / dx; + + y2 = y1 + a->h; + s2 = ((b->y + b->h) - y2) / dx; + + // FixedCeil needs a minimum!!! :D :D + + if (s1 == 0) + s1 = 1; + + if (s2 == 0) + s2 = 1; + + if (x1 < 0) + { + y1 -= x1 * s1; + y2 -= x1 * s2; + x1 = 0; + } + + while (x1 < x2) + { + raster_bbox_seg(x1, y1, s1, bb->color); + raster_bbox_seg(x1, y2, s2, bb->color); + + y1 += s1; + y2 += s2; + + x1++; + } +} + +static UINT8 +get_bbox_color (mobj_t *thing) +{ + UINT32 flags = thing->flags; + + if (thing->player) + return 255; // 0FF + + if (flags & (MF_NOCLIPTHING)) + return 7; // BFBFBF + + if (flags & (MF_SPECIAL)) + return 73; // FF0 + + if (flags & (MF_BOSS|MF_MISSILE|MF_ENEMY|MF_PAIN)) + return 35; // F00 + + if (flags & (MF_NOCLIP)) + return 152; // 00F + + return 0; // FFF +} + +void R_DrawThingBoundingBox(mobj_t *thing) +{ + fixed_t rs, rc; // radius offsets + fixed_t gx, gy; // origin + fixed_t tx, ty; // translated coordinates + + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + struct bbox_config bb = {0}; + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + + rs = FixedMul(thing->radius, viewsin); + rc = FixedMul(thing->radius, viewcos); + + gx = interp.x - viewx; + gy = interp.y - viewy; + + tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos); + ty = FixedMul(gx, viewcos) + FixedMul(gy, viewsin); + + bb.height = thing->height; + bb.tz = (interp.z + bb.height) - viewz; + bb.color = get_bbox_color(thing); + + // 1--3 + // | | + // 0--2 + + // left + + tx -= rs; + ty -= rc; + + draw_bbox_col(&bb, 0, tx + rc, ty - rs); // bottom + draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top + + // right + + tx += rs + rs; + ty += rc + rc; + + draw_bbox_col(&bb, 2, tx + rc, ty - rs); // bottom + draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top + + // connect all four columns + + draw_bbox_row(&bb, 0, 1); + draw_bbox_row(&bb, 1, 3); + draw_bbox_row(&bb, 3, 2); + draw_bbox_row(&bb, 2, 0); +} + +static boolean is_tangible (mobj_t *thing) +{ + // These objects can never touch another + if (thing->flags & (MF_NOCLIPTHING)) + { + return false; + } + + // These objects probably do nothing! :D + if ((thing->flags & (MF_SPECIAL|MF_SOLID|MF_SHOOTABLE + |MF_PUSHABLE|MF_BOSS|MF_MISSILE|MF_SPRING + |MF_MONITOR|MF_ENEMY|MF_PAIN|MF_STICKY + |MF_PICKUPFROMBELOW)) == 0U) + { + return false; + } + + return true; +} + +boolean R_ThingBoundingBoxVisible(mobj_t *thing) +{ + INT32 cvmode = cv_renderhitbox.value; + + switch (cvmode) + { + case RENDERHITBOX_OFF: + return false; + + case RENDERHITBOX_ALL: + return true; + + case RENDERHITBOX_INTANGIBLE: + return !is_tangible(thing); + + case RENDERHITBOX_TANGIBLE: + // Exclude rings from here, lots of them! + if (thing->type == MT_RING) + { + return false; + } + + return is_tangible(thing); + + case RENDERHITBOX_RINGS: + return (thing->type == MT_RING); + + default: + return false; + } +} diff --git a/src/r_things.c b/src/r_things.c index d0fd57600..ca4e6ecf7 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2950,6 +2950,14 @@ static void R_DrawSprite(vissprite_t *spr) R_DrawFloorSplat(spr); else R_DrawVisSprite(spr); + + if (R_ThingBoundingBoxVisible(spr->mobj)) + { + // fuck you fuck you fuck you FUCK YOU + // (shadows are linked to their mobj) + if (!(spr->cut & SC_SHADOW)) + R_DrawThingBoundingBox(spr->mobj); + } } // Special drawer for precipitation sprites Tails 08-18-2002 diff --git a/src/r_things.h b/src/r_things.h index bd7449d3e..ef5ceedd0 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -66,8 +66,14 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel); void R_InitSprites(void); void R_ClearSprites(void); +boolean R_ThingBoundingBoxVisible(mobj_t *thing); +void R_DrawThingBoundingBox(mobj_t *thing); + boolean R_ThingVisible (mobj_t *thing); +boolean R_ThingWithinDist (mobj_t *thing, + fixed_t draw_dist); + boolean R_ThingVisibleWithinDist (mobj_t *thing, fixed_t draw_dist); diff --git a/src/screen.h b/src/screen.h index 268b11e95..8abf77224 100644 --- a/src/screen.h +++ b/src/screen.h @@ -201,7 +201,7 @@ extern CV_PossibleValue_t cv_renderer_t[]; extern INT32 scr_bpp; extern UINT8 *scr_borderpatch; // patch used to fill the view borders -extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen; +extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_renderhitbox, cv_fullscreen; extern consvar_t cv_vhseffect, cv_shittyscreen; // wait for page flipping to end or not From 5a58b3ca3add9d9964e00a7bf9a84987e76b1767 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 10 Sep 2022 21:27:37 -0700 Subject: [PATCH 2/4] Refactor hitbox renderer to project vissprites Properly accounts for portals (skyboxes). --- src/r_bbox.c | 60 ++++++------------ src/r_things.c | 166 +++++++++++++++++++++++++++++++++++++++++++------ src/r_things.h | 4 +- 3 files changed, 169 insertions(+), 61 deletions(-) diff --git a/src/r_bbox.c b/src/r_bbox.c index ef72488fb..2812429a7 100644 --- a/src/r_bbox.c +++ b/src/r_bbox.c @@ -14,7 +14,6 @@ #include "doomdef.h" #include "command.h" -#include "r_fps.h" #include "r_local.h" #include "screen.h" // cv_renderhitbox #include "v_video.h" // V_DrawFill @@ -166,11 +165,11 @@ draw_bbox_row } static UINT8 -get_bbox_color (mobj_t *thing) +get_bbox_color (vissprite_t *vis) { - UINT32 flags = thing->flags; + UINT32 flags = vis->mobjflags; - if (thing->player) + if (vis->mobj->player) return 255; // 0FF if (flags & (MF_NOCLIPTHING)) @@ -188,39 +187,21 @@ get_bbox_color (mobj_t *thing) return 0; // FFF } -void R_DrawThingBoundingBox(mobj_t *thing) +void R_DrawThingBoundingBox(vissprite_t *vis) { - fixed_t rs, rc; // radius offsets - fixed_t gx, gy; // origin - fixed_t tx, ty; // translated coordinates + // radius offsets + fixed_t rs = vis->scale; + fixed_t rc = vis->xscale; - // uncapped/interpolation - interpmobjstate_t interp = {0}; + // translated coordinates + fixed_t tx = vis->gx; + fixed_t ty = vis->gy; - struct bbox_config bb = {0}; - - // do interpolation - if (R_UsingFrameInterpolation() && !paused) - { - R_InterpolateMobjState(thing, rendertimefrac, &interp); - } - else - { - R_InterpolateMobjState(thing, FRACUNIT, &interp); - } - - rs = FixedMul(thing->radius, viewsin); - rc = FixedMul(thing->radius, viewcos); - - gx = interp.x - viewx; - gy = interp.y - viewy; - - tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos); - ty = FixedMul(gx, viewcos) + FixedMul(gy, viewsin); - - bb.height = thing->height; - bb.tz = (interp.z + bb.height) - viewz; - bb.color = get_bbox_color(thing); + struct bbox_config bb = { + .height = vis->thingheight, + .tz = vis->texturemid, + .color = get_bbox_color(vis), + }; // 1--3 // | | @@ -228,18 +209,15 @@ void R_DrawThingBoundingBox(mobj_t *thing) // left - tx -= rs; - ty -= rc; - - draw_bbox_col(&bb, 0, tx + rc, ty - rs); // bottom + draw_bbox_col(&bb, 0, tx, ty); // bottom draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top // right - tx += rs + rs; - ty += rc + rc; + tx += rs; + ty += rc; - draw_bbox_col(&bb, 2, tx + rc, ty - rs); // bottom + draw_bbox_col(&bb, 2, tx, ty); // bottom draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top // connect all four columns diff --git a/src/r_things.c b/src/r_things.c index ca4e6ecf7..d7d2a12b5 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1449,6 +1449,104 @@ static void R_ProjectDropShadow( objectsdrawn++; } +static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis) +{ + fixed_t gx, gy; + fixed_t tx, tz; + + vissprite_t *box; + + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + if (!R_ThingBoundingBoxVisible(thing)) + { + return; + } + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + + // 1--3 + // | | + // 0--2 + + // start in the (0) corner + gx = interp.x - thing->radius - viewx; + gy = interp.y - thing->radius - viewy; + + tz = FixedMul(gx, viewcos) + FixedMul(gy, viewsin); + + // thing is behind view plane? + // if parent vis is visible, ignore this + if (!vis && (tz < FixedMul(MINZ, interp.scale))) + { + return; + } + + tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos); + + // too far off the side? + if (!vis && abs(tx) > FixedMul(tz, fovtan[viewssnum])<<2) + { + return; + } + + box = R_NewVisSprite(); + box->mobj = thing; + box->mobjflags = thing->flags; + box->thingheight = thing->height; + box->cut = SC_BBOX; + + box->gx = tx; + box->gy = tz; + + box->scale = 2 * FixedMul(thing->radius, viewsin); + box->xscale = 2 * FixedMul(thing->radius, viewcos); + + box->pz = interp.z; + box->pzt = box->pz + box->thingheight; + + box->gzt = box->pzt; + box->gz = box->pz; + box->texturemid = box->gzt - viewz; + + if (vis) + { + box->x1 = vis->x1; + box->x2 = vis->x2; + box->szt = vis->szt; + box->sz = vis->sz; + + box->sortscale = vis->sortscale; // link sorting to sprite + box->dispoffset = vis->dispoffset + 5; + + box->cut |= SC_LINKDRAW; + } + else + { + fixed_t xscale = FixedDiv(projection[viewssnum], tz); + fixed_t yscale = FixedDiv(projectiony[viewssnum], tz); + fixed_t top = (centeryfrac - FixedMul(box->texturemid, yscale)); + + box->x1 = (centerxfrac + FixedMul(box->gx, xscale)) / FRACUNIT; + box->x2 = box->x1; + + box->szt = top / FRACUNIT; + box->sz = (top + FixedMul(box->thingheight, yscale)) / FRACUNIT; + + box->sortscale = yscale; + box->dispoffset = 0; + } +} + // // R_ProjectSprite // Generates a vissprite for a thing @@ -2195,6 +2293,8 @@ static void R_ProjectSprite(mobj_t *thing) R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz); } + R_ProjectBoundingBox(oldthing, vis); + // Debug ++objectsdrawn; } @@ -2429,8 +2529,26 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) limit_dist = (fixed_t)(cv_drawdist.value) * mapobjectscale; for (thing = sec->thinglist; thing; thing = thing->snext) { - if (R_ThingVisibleWithinDist(thing, limit_dist)) - R_ProjectSprite(thing); + if (R_ThingWithinDist(thing, limit_dist)) + { + const INT32 oldobjectsdrawn = objectsdrawn; + + if (R_ThingVisible(thing)) + { + R_ProjectSprite(thing); + } + + // I'm so smart :^) + if (objectsdrawn == oldobjectsdrawn) + { + /* + Object is invisible OR is off screen but + render its bbox even if the latter because + radius could be bigger than sprite. + */ + R_ProjectBoundingBox(thing, NULL); + } + } } // no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off @@ -2503,6 +2621,10 @@ static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 e if (dsfirst->cut & SC_SHADOW) continue; + // don't connect to your bounding box! + if (dsfirst->cut & SC_BBOX) + continue; + // don't connect if it's not the tracer if (dsfirst->mobj != ds->mobj) continue; @@ -2946,18 +3068,12 @@ static void R_DrawSprite(vissprite_t *spr) mfloorclip = spr->clipbot; mceilingclip = spr->cliptop; - if (spr->cut & SC_SPLAT) + if (spr->cut & SC_BBOX) + R_DrawThingBoundingBox(spr); + else if (spr->cut & SC_SPLAT) R_DrawFloorSplat(spr); else R_DrawVisSprite(spr); - - if (R_ThingBoundingBoxVisible(spr->mobj)) - { - // fuck you fuck you fuck you FUCK YOU - // (shadows are linked to their mobj) - if (!(spr->cut & SC_SHADOW)) - R_DrawThingBoundingBox(spr->mobj); - } } // Special drawer for precipitation sprites Tails 08-18-2002 @@ -3253,6 +3369,12 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal) INT32 x1 = (spr->cut & SC_SPLAT) ? 0 : spr->x1; INT32 x2 = (spr->cut & SC_SPLAT) ? viewwidth : spr->x2; + if (spr->cut & SC_BBOX) + { + // Do not clip bounding boxes + continue; + } + if (x2 < cx) { drawsegs_xrange = drawsegs_xranges[1].items; @@ -3291,20 +3413,26 @@ boolean R_ThingVisible (mobj_t *thing) return true; } +boolean R_ThingWithinDist (mobj_t *thing, fixed_t limit_dist) +{ + const fixed_t dist = R_PointToDist(thing->x, thing->y); + + if (limit_dist && dist > limit_dist) + { + return false; + } + + return true; +} + +// For OpenGL, TODO: REMOVE!! boolean R_ThingVisibleWithinDist (mobj_t *thing, fixed_t limit_dist) { - fixed_t approx_dist; - if (! R_ThingVisible(thing)) return false; - approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y); - - if (limit_dist && approx_dist > limit_dist) - return false; - - return true; + return R_ThingWithinDist(thing, limit_dist); } /* Check if precipitation may be drawn from our current view. */ diff --git a/src/r_things.h b/src/r_things.h index ef5ceedd0..085d15512 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -67,7 +67,6 @@ void R_InitSprites(void); void R_ClearSprites(void); boolean R_ThingBoundingBoxVisible(mobj_t *thing); -void R_DrawThingBoundingBox(mobj_t *thing); boolean R_ThingVisible (mobj_t *thing); @@ -140,6 +139,7 @@ typedef enum SC_SPLAT = 1<<11, // srb2kart SC_SEMIBRIGHT = 1<<12, + SC_BBOX = 1<<13, // masks SC_CUTMASK = SC_TOP|SC_BOTTOM, SC_FLAGMASK = ~SC_CUTMASK @@ -226,6 +226,8 @@ extern UINT32 visspritecount; void R_ClipSprites(drawseg_t* dsstart, portal_t* portal); void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* portal); +void R_DrawThingBoundingBox(vissprite_t *spr); + UINT8 *R_GetSpriteTranslation(vissprite_t *vis); // ---------- From 7094a064dada13691d3d9d0499dc928a6bb2fce9 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 11 Sep 2022 06:25:08 -0700 Subject: [PATCH 3/4] Do not render waypoint or spectator self hitbox Visual clutter and waypoint debugger exists. :) If you are a spectator, the hitbox exists right ontop of you and hitboxes don't render correctly if they are too close to the viewpoint. --- src/r_bbox.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/r_bbox.c b/src/r_bbox.c index 2812429a7..d5d2bf77c 100644 --- a/src/r_bbox.c +++ b/src/r_bbox.c @@ -252,6 +252,18 @@ boolean R_ThingBoundingBoxVisible(mobj_t *thing) { INT32 cvmode = cv_renderhitbox.value; + if (thing->type == MT_WAYPOINT) + { + // Waypoints debugger serves this purpose + return false; + } + + if (thing == r_viewmobj) + { + // Rendering bbox right on top causes anomalies + return false; + } + switch (cvmode) { case RENDERHITBOX_OFF: From 7719cf27fa154749e617aebeafa49b2698692bba Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 11 Sep 2022 07:08:10 -0700 Subject: [PATCH 4/4] Remedy some quirky rendering of hitboxes if your viewpoint is too close It's not correct but it's better than before. --- src/r_bbox.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/r_bbox.c b/src/r_bbox.c index d5d2bf77c..969aeacff 100644 --- a/src/r_bbox.c +++ b/src/r_bbox.c @@ -82,8 +82,13 @@ draw_bbox_col { struct bbox_col *col = &bb->col[p]; - fixed_t xscale = FixedDiv(projection[viewssnum], ty); - fixed_t yscale = FixedDiv(projectiony[viewssnum], ty); + fixed_t xscale, yscale; + + if (ty < FRACUNIT) // projection breaks down here + ty = FRACUNIT; + + xscale = FixedDiv(projection[viewssnum], ty); + yscale = FixedDiv(projectiony[viewssnum], ty); col->x = (centerxfrac + FixedMul(tx, xscale)) / FRACUNIT; col->y = (centeryfrac - FixedMul(bb->tz, yscale));