diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd7eb65ee..5f81a30f4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,6 +118,7 @@ add_executable(BLANKART MACOSX_BUNDLE WIN32 lua_botvarslib.c lua_hudlib.c lua_hudlib_drawlist.c + lua_colorlib.c lua_followerlib.c lua_terrainlib.c lua_waypointslib.c diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 327927239..245904258 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5705,6 +5705,9 @@ static INT16 Consistancy(consistancy_t *c) mo = (mobj_t *)th; + if (!MobjIsNetSynced(mo)) + continue; + if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) { c->mobjtype -= mo->type; @@ -5734,11 +5737,11 @@ static INT16 Consistancy(consistancy_t *c) c->mobjstate -= mo->target->state - states; c->mobjtics += mo->target->tics; c->mobjsprite -= mo->target->sprite; - c->mobjframe += mo->target->frame; + //c->mobjframe += mo->target->frame; } else ConsistancyXOR(c, 0x3333); - if (mo->tracer && mo->tracer->type != MT_OVERLAY) + if (mo->tracer && MobjIsNetSynced(mo->tracer)) { c->mobjtype += mo->tracer->type; c->position[0] -= mo->tracer->x; @@ -5754,12 +5757,12 @@ static INT16 Consistancy(consistancy_t *c) c->mobjstate -= mo->tracer->state - states; c->mobjtics += mo->tracer->tics; c->mobjsprite -= mo->tracer->sprite; - c->mobjframe += mo->tracer->frame; + //c->mobjframe += mo->tracer->frame; } else ConsistancyXOR(c, 0xAAAA); // SRB2Kart: We use hnext & hprev very extensively - if (mo->hnext && mo->hnext->type != MT_OVERLAY) + if (mo->hnext && MobjIsNetSynced(mo->hnext)) { c->mobjtype += mo->hnext->type; c->position[0] -= mo->hnext->x; @@ -5775,11 +5778,11 @@ static INT16 Consistancy(consistancy_t *c) c->mobjstate -= mo->hnext->state - states; c->mobjtics += mo->hnext->tics; c->mobjsprite -= mo->hnext->sprite; - c->mobjframe += mo->hnext->frame; + //c->mobjframe += mo->hnext->frame; } else ConsistancyXOR(c, 0x5555); - if (mo->hprev && mo->hprev->type != MT_OVERLAY) + if (mo->hprev && MobjIsNetSynced(mo->hprev)) { c->mobjtype += mo->hprev->type; c->position[0] -= mo->hprev->x; @@ -5795,14 +5798,14 @@ static INT16 Consistancy(consistancy_t *c) c->mobjstate -= mo->hprev->state - states; c->mobjtics += mo->hprev->tics; c->mobjsprite -= mo->hprev->sprite; - c->mobjframe += mo->hprev->frame; + //c->mobjframe += mo->hprev->frame; } else ConsistancyXOR(c, 0xCCCC); c->mobjstate -= mo->state - states; c->mobjtics += mo->tics; c->mobjsprite -= mo->sprite; - c->mobjframe += mo->frame; + //c->mobjframe += mo->frame; } } } diff --git a/src/deh_soc.c b/src/deh_soc.c index c3b1ff5a2..1127229c3 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2063,6 +2063,17 @@ static void readmenuitem(MYFILE *f, menuitem_t *menuitem) } else if (fastcmp(word, "SUBMENU")) { +#ifndef HAVE_DISCORDRPC + { + const char *testname = word2; + if (fastncmp("MN_", testname, 3)) + testname += 3; + if (fastcmp(testname, "OP_DISCORD") || fastcmp(testname, "MISC_DISCORDREQUESTS")) + { + continue; + } + } +#endif menutype_t mn = get_menutype(word2); if (mn == MAXMENUTYPES) { diff --git a/src/dehacked.c b/src/dehacked.c index 8a3c67656..ffbfcfb91 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -675,6 +675,18 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) continue; // dedis don't need menus, silly! } +#ifndef HAVE_DISCORDRPC + { + const char *testname = word2; + if (fastncmp("MN_", testname, 3)) + testname += 3; + if (fastcmp(testname, "OP_DISCORD") || fastcmp(testname, "MISC_DISCORDREQUESTS")) + { + ignoremenulines(f); + continue; + } + } +#endif if (i == 0 && word2[0] != '0') // If word2 isn't a number i = get_menutype(word2); // find a huditem by name if (i >= 1 && i < nummenutypes) diff --git a/src/g_game.c b/src/g_game.c index 78f982309..d512b3bac 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1703,17 +1703,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } - fixed_t deadZoneY = cv_deadzoney[forplayer].value; - const double deadZoneYDecimal = (double) deadZoneY / FRACUNIT; joystickvector.xaxis = G_PlayerInputAnalog(forplayer, gc_turnright, false, DEADZONE_X) - G_PlayerInputAnalog(forplayer, gc_turnleft, false, DEADZONE_X); - if (deadZoneYDecimal <= 0) - { - joystickvector.yaxis = G_PlayerInputAnalog(forplayer, gc_aimbackward, false, DEADZONE_Y) - G_PlayerInputAnalog(forplayer, gc_aimforward, false, DEADZONE_Y); - } - else - { - joystickvector.yaxis = 0; - } + joystickvector.yaxis = 0; G_HandleAxisDeadZone(forplayer, &joystickvector); // tilt control never has deadzone @@ -1731,11 +1722,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // This mean that the turn axis will still be gradient but up/down will be 0 // until the stick is pushed far enough // - // When the deadzone is set to 0 or lower, the aim axis returns to analog to avoid breaking the chasecam and freecam controls - if (deadZoneYDecimal > 0) - { - joystickvector.yaxis = G_PlayerInputAnalog(forplayer, gc_aimbackward, false, DEADZONE_Y) - G_PlayerInputAnalog(forplayer, gc_aimforward, false, DEADZONE_Y); - } + joystickvector.yaxis = G_PlayerInputAnalog(forplayer, gc_aimbackward, false, DEADZONE_Y) - G_PlayerInputAnalog(forplayer, gc_aimforward, false, DEADZONE_Y); if (encoremode) { diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c index 8d2e40431..e0a3572b3 100644 --- a/src/hardware/hw_bsp.c +++ b/src/hardware/hw_bsp.c @@ -478,7 +478,7 @@ static void HWR_AddPolyObjectPlanes(void) sector_t *polyobjsector; INT32 light = 0; - // Polyobject Planes need their own function for drawing because they don't have extrasubsectors by themselves + // Polyobject Planes need their own function for drawing because they don't have poly_subsectors by themselves // It should be okay because polyobjects should always be convex anyway for (i = 0; i < numpolys; i++) @@ -567,9 +567,9 @@ static void HWR_Subsector(size_t num) // CONS_Printf("subsector %d in portal\n", (INT32)num); #ifdef PARANOIA //no risk while developing, enough debugging nights! - if (num >= addsubsector) + if (num >= num_poly_subsector) I_Error("HWR_Subsector: ss %s with numss = %s, addss = %s\n", - sizeu1(num), sizeu2(numsubsectors), sizeu3(addsubsector)); + sizeu1(num), sizeu2(numsubsectors), sizeu3(num_poly_subsector)); /*if (num >= numsubsectors) I_Error("HWR_Subsector: ss %i with numss = %i", @@ -668,7 +668,7 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetLevelFlat(&levelflats[gl_frontsector->floorpic], R_NoEncore(gl_frontsector, &levelflats[gl_frontsector->floorpic], false)); - HWR_RenderPlane(sub, &extrasubsectors[num], false, + HWR_RenderPlane(sub, &poly_subsectors[num], false, // Hack to make things continue to work around slopes. locFloorHeight == cullFloorHeight ? locFloorHeight : gl_frontsector->floorheight, // We now return you to your regularly scheduled rendering. @@ -685,7 +685,7 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetLevelFlat(&levelflats[gl_frontsector->ceilingpic], R_NoEncore(gl_frontsector, &levelflats[gl_frontsector->ceilingpic], true)); - HWR_RenderPlane(sub, &extrasubsectors[num], true, + HWR_RenderPlane(sub, &poly_subsectors[num], true, // Hack to make things continue to work around slopes. locCeilingHeight == cullCeilingHeight ? locCeilingHeight : gl_frontsector->ceilingheight, // We now return you to your regularly scheduled rendering. @@ -725,7 +725,7 @@ static void HWR_Subsector(size_t num) alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap); HWR_AddTransparentFloor(0, - &extrasubsectors[num], + &poly_subsectors[num], false, *rover->bottomheight, *gl_frontsector->lightlist[light].lightlevel, @@ -740,7 +740,7 @@ static void HWR_Subsector(size_t num) light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); HWR_AddTransparentFloor(&levelflats[*rover->bottompic], - &extrasubsectors[num], + &poly_subsectors[num], false, *rover->bottomheight, *gl_frontsector->lightlist[light].lightlevel, @@ -751,7 +751,7 @@ static void HWR_Subsector(size_t num) { HWR_GetLevelFlat(&levelflats[*rover->bottompic], R_NoEncore(gl_frontsector, &levelflats[*rover->bottompic], false)); light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); - HWR_RenderPlane(sub, &extrasubsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->bottompic], + HWR_RenderPlane(sub, &poly_subsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->bottompic], rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap); } } @@ -773,7 +773,7 @@ static void HWR_Subsector(size_t num) alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap); HWR_AddTransparentFloor(0, - &extrasubsectors[num], + &poly_subsectors[num], true, *rover->topheight, *gl_frontsector->lightlist[light].lightlevel, @@ -788,7 +788,7 @@ static void HWR_Subsector(size_t num) light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); HWR_AddTransparentFloor(&levelflats[*rover->toppic], - &extrasubsectors[num], + &poly_subsectors[num], true, *rover->topheight, *gl_frontsector->lightlist[light].lightlevel, @@ -799,7 +799,7 @@ static void HWR_Subsector(size_t num) { HWR_GetLevelFlat(&levelflats[*rover->toppic], R_NoEncore(gl_frontsector, &levelflats[*rover->toppic], true)); light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); - HWR_RenderPlane(sub, &extrasubsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, true) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->toppic], + HWR_RenderPlane(sub, &poly_subsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, true) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->toppic], rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap); } } diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index b2843728d..381f13a57 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -299,9 +299,6 @@ struct FSurfaceInfo }; typedef struct FSurfaceInfo FSurfaceInfo; -#define GL_DEFAULTMIX 0x00000000 -#define GL_DEFAULTFOG 0x19000000 - // Various settings and states for the rendering backend. enum hwdsetspecialstate { diff --git a/src/hardware/hw_drawnodes.c b/src/hardware/hw_drawnodes.c index f0030aeef..f48eec096 100644 --- a/src/hardware/hw_drawnodes.c +++ b/src/hardware/hw_drawnodes.c @@ -37,7 +37,7 @@ typedef struct typedef struct { - extrasubsector_t *xsub; + poly_subsector_t *xsub; boolean isceiling; fixed_t fixedheight; INT32 lightlevel; @@ -143,7 +143,7 @@ void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, INT32 te } // This will likely turn into a copy of HWR_Add3DWater and replace it. -void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap) +void HWR_AddTransparentFloor(levelflat_t *levelflat, poly_subsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap) { planeinfo_t *planeinfo = HWR_CreateDrawNode(DRAWNODE_PLANE); diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index af954d486..1ec637413 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -54,7 +54,7 @@ typedef struct typedef struct { poly_t *planepoly; // the generated convex polygon -} extrasubsector_t; +} poly_subsector_t; // needed for sprite rendering // equivalent of the software renderer's vissprites @@ -117,16 +117,14 @@ typedef struct gl_vissprite_s void HWR_ObjectLightLevelPost(gl_vissprite_t *spr, const sector_t *sector, INT32 *lightlevel, boolean model); // -------- -// hw_bsp.c +// hw_map.c // -------- -extern extrasubsector_t *extrasubsectors; -extern size_t addsubsector; +extern poly_subsector_t *poly_subsectors; +extern size_t num_poly_subsector; void HWR_InitPolyPool(void); void HWR_FreePolyPool(void); -void HWR_FreeExtraSubsectors(void); - // -------- // hw_cache.c // -------- @@ -176,7 +174,7 @@ extern INT32 textureformat; // -------- // hw_plane.c // -------- -void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap); +void HWR_RenderPlane(subsector_t *subsector, poly_subsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap); void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, FBITFIELD blendmode, UINT8 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap); @@ -185,7 +183,7 @@ void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fixed_t // hw_drawnodes.c // -------- void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, INT32 texnum, boolean noencore, FBITFIELD blend, boolean fogwall, INT32 lightlevel, extracolormap_t *wallcolormap); -void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap); +void HWR_AddTransparentFloor(levelflat_t *levelflat, poly_subsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap); void HWR_AddTransparentPolyobjectFloor(levelflat_t *levelflat, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap); void HWR_PushDrawNodeState(void); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index ea9bfb591..71560d3a9 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -398,8 +398,8 @@ void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *col RGBA_t poly_color, tint_color, fade_color; poly_color.rgba = 0xFFFFFFFF; - tint_color.rgba = (colormap != NULL) ? (UINT32)colormap->rgba : GL_DEFAULTMIX; - fade_color.rgba = (colormap != NULL) ? (UINT32)colormap->fadergba : GL_DEFAULTFOG; + tint_color.rgba = (colormap != NULL) ? (UINT32)colormap->rgba : 0x00000000; + fade_color.rgba = (colormap != NULL) ? (UINT32)colormap->fadergba : 0xFF000000; // Crappy backup coloring if you can't do shaders if (!HWR_UseShader()) @@ -413,7 +413,7 @@ void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *col blue = (float)poly_color.s.blue; // 48 is just an arbritrary value that looked relatively okay. - tint_alpha = (float)(sqrt(tint_color.s.alpha) * 48) / 255.0f; + tint_alpha = (float)(sqrt((float)tint_color.s.alpha / 10.2) * 48) / 255.0f; // 8 is roughly the brightness of the "close" color in Software, and 16 the brightness of the "far" color. // 8 is too bright for dark levels, and 16 is too dark for bright levels. @@ -469,7 +469,7 @@ UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap) // Let's see if RGBA_t realcolor, surfcolor; INT32 alpha; - realcolor.rgba = (colormap != NULL) ? colormap->rgba : GL_DEFAULTMIX; + realcolor.rgba = (colormap != NULL) ? colormap->rgba : 0x00000000; if (HWR_UseShader()) { @@ -1255,6 +1255,17 @@ consvar_t cv_glanisotropicmode = CVAR_INIT ("gr_anisotropicmode", "1", CV_SAVE|C consvar_t cv_glsolvetjoin = CVAR_INIT ("gr_solvetjoin", "On", 0, CV_OnOff, NULL); +consvar_t cv_glpolytile = CVAR_INIT ("gr_polytile", "On", 0, CV_OnOff, NULL); + +CV_PossibleValue_t grpolyshape_cons_t[] = { + {0, "Subsector"}, + {1, "Fat"}, + {2, "Trim"}, + {3, "NotConvex"}, + {0, NULL} +}; +consvar_t cv_glpolyshape = CVAR_INIT ("gr_polygon_shape", "Trim", CV_SAVE, grpolyshape_cons_t, NULL); + consvar_t cv_glbatching = CVAR_INIT ("gr_batching", "On", 0, CV_OnOff, NULL); CV_PossibleValue_t glpalettedepth_cons_t[] = {{16, "16 bits"}, {24, "24 bits"}, {0, NULL}}; @@ -1365,7 +1376,10 @@ void HWR_AddCommands(void) CV_RegisterVar(&cv_glallowshaders); CV_RegisterVar(&cv_glfiltermode); + CV_RegisterVar(&cv_glsolvetjoin); + CV_RegisterVar(&cv_glpolytile); + CV_RegisterVar(&cv_glpolyshape); CV_RegisterVar(&cv_glbatching); @@ -1399,6 +1413,7 @@ void HWR_Startup(void) textureformat = patchformat = GL_TEXFMT_RGBA; HWR_InitPolyPool(); + HWR_AddSessionCommands(); HWR_InitMapTextures(); HWR_InitModels(); @@ -1445,7 +1460,6 @@ void HWR_Switch(void) void HWR_Shutdown(void) { CONS_Printf("HWR_Shutdown()\n"); - HWR_FreeExtraSubsectors(); HWR_FreePolyPool(); HWR_FreeMapTextures(); GL_FlushScreenTextures(); diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 81e69fbfe..879f3c40b 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -110,11 +110,13 @@ extern consvar_t cv_glmodellighting; extern consvar_t cv_glfiltermode; extern consvar_t cv_glanisotropicmode; extern consvar_t cv_fovchange; -extern consvar_t cv_glsolvetjoin; + extern consvar_t cv_glshearing; extern consvar_t cv_glspritebillboarding; extern consvar_t cv_glskydome; +extern consvar_t cv_glsolvetjoin, cv_glpolytile, cv_glpolyshape; + extern consvar_t cv_glbatching; extern consvar_t cv_glpaletterendering; extern consvar_t cv_glpalettedepth; diff --git a/src/hardware/hw_map.c b/src/hardware/hw_map.c index ddfb262d1..f70457ebf 100644 --- a/src/hardware/hw_map.c +++ b/src/hardware/hw_map.c @@ -1,39 +1,596 @@ // BLANKART //----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2016 by DooM Legacy Team. +// Copyright (C) 1999-2019 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file hw_bsp.c +/// \file hw_map.c /// \brief convert SRB2 map -#include "../doomdef.h" -#include "../doomstat.h" #ifdef HWRENDER + +#include + #include "hw_glob.h" +#include "hw_main.h" #include "../r_local.h" #include "../z_zone.h" #include "../console.h" #include "../v_video.h" #include "../m_menu.h" #include "../i_system.h" -#include "../m_argv.h" #include "../i_video.h" -#include "../w_wad.h" -#include "../p_setup.h" // levelfadecol + +#define FIXED_TO_FLOAT_MULT (1.0f / 65536.0f) + +//#define DEBUG_HWBSP + +//#define DEBUG_TRACE +#ifdef DEBUG_TRACE +static int trigger_bsp_sector = 0xFFFFFFFF; +static int trigger_subsector = 0xFFFFFFFF; + // 0xFFFFFFF2 to trace all subsectors +static byte trigger_trace = 0; +#endif + +#define POLYTILE + // -------------------------------------------------------------------------- // This is global data for planes rendering // -------------------------------------------------------------------------- -extrasubsector_t *extrasubsectors = NULL; +// ---- Polygon vertexes +// Separate allocations for level map vertexes, and BSP vertexes. -// newsubsectors are subsectors without segs, added for the plane polygons -#define NEWSUBSECTORS 50 -static size_t totsubsectors; -size_t addsubsector; +// Floating poly for level map vertexes. +// Can be indexed by level map vertex number. +polyvertex_t* poly_vert = NULL; + +// Create float poly vert from level map vertexes. +// These are freed by Z_Free(PU_HWRPLANE). +static void create_poly_vert(void) +{ + polyvertex_t * pv; + size_t size = sizeof(polyvertex_t) * numvertexes; + size_t i; + + poly_vert = Z_Malloc(size, PU_HWRPLANE, NULL); + pv = &poly_vert[0]; + + for (i = 0; i < numvertexes; i++) + { + pv->x = FIXED_TO_FLOAT(vertexes[i].x); + pv->y = FIXED_TO_FLOAT(vertexes[i].y); + pv++; + } +} + +// Return true if p1 is a level map polyvertex in the poly_vert structure. +static inline boolean in_poly_vert(polyvertex_t * p1) +{ + return((p1 >= poly_vert) && (p1 < &poly_vert[numvertexes])); +} + +#define POLYSTORE_NUM_VERT 256 +typedef struct polyvertex_store_s { + struct polyvertex_store_s *next; // linked list + INT32 num_vert_used; + polyvertex_t pv[POLYSTORE_NUM_VERT]; +} polyvertex_store_t; + +// Extra poly vertex for BSP splits, segs, and divlines. +polyvertex_store_t *polyvert_store = NULL; + +// --- Same vertex + +// If two vertex coords have a x and y difference of less than 1 FRACUNIT, +// they could be considered the same point. +// Note: hardcoded value, 1.0f could be anything else. +//#define SAME_DIST 1.5f +// Dist 0.4999 cures HOM in Freedoom map09 +#define SAME_DIST 0.4999f +// ep : the max difference in x or y. +static inline boolean SameVertex(polyvertex_t* p1, polyvertex_t* p2, float ep) +{ + if (fabsf(p2->x - p1->x) > ep) + return false; + if (fabsf(p2->y - p1->y) > ep) + return false; + // p1 and p2 are considered the same vertex + return true; +} + +// Get a polyvertex from the BSP polyvertex store. +// These are freed by Z_Free( PU_HWRPLANE ). +static polyvertex_t *new_polyvertex(void) +{ + polyvertex_store_t *psp = polyvert_store; + + if (!polyvert_store + || (polyvert_store->num_vert_used >= POLYSTORE_NUM_VERT)) + { + // Need another storage unit. + polyvert_store = Z_Malloc(sizeof(polyvertex_store_t), PU_HWRPLANE, NULL); + polyvert_store->next = psp; // link for search + polyvert_store->num_vert_used = 0; + } + + // Return the next polyvertex in the storage unit. + return &polyvert_store->pv[polyvert_store->num_vert_used++]; +} + +// Search both the vertex lists for a close vertex. +// ep: how close they must be to be the same ( 0.001 to 1.5 ) +static polyvertex_t *find_close_polyvertex(float x, float y, float ep) +{ + polyvertex_store_t *psv; + polyvertex_t *pv; + INT32 i; + + // Search level map vertexes. + pv = poly_vert; + for (i = numvertexes; i > 0; i--) + { + const float dx = pv->x - x; + const float dy = pv->y - y; + if (dx*dx + dy*dy < ep*ep) + return pv; // close enough to be the same vertex + pv++; + } + + // Search extra BSP vertexes. + psv = polyvert_store; + while (psv) + { + // Search all vertex in a polyvertex_store_t + pv = psv->pv; + for (i = psv->num_vert_used; i > 0; i--) + { + const float dx = pv->x - x; + const float dy = pv->y - y; + if (dx*dx + dy*dy < ep*ep) + return pv; // close enough to be the same vertex + pv++; + } + psv = psv->next; + } + + return NULL; // none found +} + +// Store a new polyvertex. +// Search for an existing vertex that is within ep. +// Otherwise make a new extra vertex. +// ep: how close an existing vertex must be to be the same ( 0.001 to 1.5 ) +static inline polyvertex_t *store_polyvertex(polyvertex_t *vert, float ep) +{ + const float fx = vert->x; + const float fy = vert->y; + + polyvertex_t *vp = find_close_polyvertex(fx, fy, ep); + + if (!vp) + { + vp = new_polyvertex(); // new BSP polyvertex + vp->x = fx; + vp->y = fy; + } + + return vp; +} + +// Create a polyvertex for the vertex. +// v1 : fixed point vertex +// ep: how close an existing vertex must be to be the same ( 0.001 to 1.5 ) +static inline polyvertex_t *store_vertex(vertex_t *v1, float ep) +{ + const float fx = FIXED_TO_FLOAT(v1->x); + const float fy = FIXED_TO_FLOAT(v1->y); + polyvertex_t * vp = find_close_polyvertex(fx, fy, ep); + + if (!vp) + { + vp = new_polyvertex(); // new BSP polyvertex + vp->x = fx; + vp->y = fy; + } + + return vp; +} + +// ---- Working polygons +// Have ptr to vertex instead of copy, to make handling same vertex easier. +// Polygons are stored in clockwise vertex order. + +// Working poly +// A convex 'plane' polygon, clockwise order +typedef struct { + INT32 num_alloc, numpts; // allocation size and how many used + polyvertex_t **ppts; // ptr to array of ptrs + // Allocate with Z_Malloc, PU_HWRPLANE +#ifdef DEBUG_TRACE + UINT32 id1, id2, id3; // Tracking poly history +#endif +} wpoly_t; + +#ifdef DEBUG_TRACE +UINT32 poly_id = 1; +#endif + +#ifdef DEBUG_HWBSP +static void polyvertex_dump(polyvertex_t *pv) +{ + int j; + fixed_t x1 = pv->x * FRACUNIT; + fixed_t y1 = pv->y * FRACUNIT; + for (j = 0; j < numvertexes; j++) + { + vertex_t *vt = &vertexes[j]; + if (abs(x1 - vt->x) + abs(y1 - vt->y) < 2 ) + { + CONS_Debug(DBG_RENDER, "V%i(%6.2f, %6.2f)", j, pv->x, pv->y); + return; + } + } + + CONS_Debug(DBG_RENDER, "(%6.2f, %6.2f)", pv->x, pv->y); +} + +static void wpoly_dump(const char *str, wpoly_t *poly) +{ + int i, cnt = 0; + + cnt = strlen( str); +#ifdef DEBUG_TRACE + CONS_Debug(DBG_RENDER, " %s id=%i,%i,%i: ", str, poly->id1, poly->id2, poly->id3); + cnt += 23; +#else + CONS_Debug(DBG_RENDER, " %s: ", str); +#endif + for (i = 0; inumpts; i++) + { + if (cnt > 120 ) + { + cnt = 6; + CONS_Debug(DBG_RENDER, "\n"); + } + polyvertex_dump( poly->ppts[i]); + cnt+=20; + } + CONS_Debug(DBG_RENDER, "\n"); +} +#endif + +// Most basic initialize. +static void wpoly_init_0(wpoly_t * wpoly) +{ + wpoly->numpts = 0; + wpoly->num_alloc = 0; + wpoly->ppts = NULL; +} + +// Initialize at an initial size. +// num_alloc : num vertex, greater than 0 +static void wpoly_init_alloc(INT32 num_alloc, wpoly_t * wpoly) +{ + wpoly->numpts = 0; + wpoly->num_alloc = num_alloc; + wpoly->ppts = Z_Malloc((sizeof(void*) * num_alloc), PU_HWRPLANE, NULL); +} + +// Frees the allocation used by the wpoly. +static void wpoly_free(wpoly_t * wpoly) +{ + wpoly->num_alloc = 0; + wpoly->numpts = 0; + + if (wpoly->ppts) + { + Z_Free(wpoly->ppts); + wpoly->ppts = NULL; + } +} + +// Move all vertex from one poly to another. +// from_poly : source, is left empty +// to_poly : previous content is lost +static void wpoly_move(wpoly_t * from_poly, /*OUT*/ wpoly_t * to_poly) +{ + if (to_poly->ppts) + wpoly_free(to_poly); + + *to_poly = *from_poly; // copy ptrs and sizes + // Content moved, cannot free. + from_poly->ppts = NULL; + from_poly->num_alloc = 0; + from_poly->numpts = 0; + // from_poly is empty. +} + +// Append a range from one poly to another poly. +// Does not alloc more, will limit copy to allocation size. +static void wpoly_append(wpoly_t *src_poly, INT32 copy_from, INT32 copy_cnt, /*OUT*/ wpoly_t *dest_poly) +{ + polyvertex_t **pvp; + INT32 n; + +#ifdef DEBUG_HWBSP + if (copy_cnt > src_poly->numpts) + { + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, exceeds src bounds, copy_from= %i, copy_cnt= %i, src numpts= %i\n", + copy_from, copy_cnt, src_poly->numpts); + } +#endif + + // Prevent writes beyond our allocation. + if (copy_cnt > dest_poly->num_alloc - dest_poly->numpts) + { +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, exceeds dest allocation, copy_cnt= %i, copy_to= %i, dest numpts= %i\n", + copy_cnt, dest_poly->numpts+1, dest_poly->numpts); +#endif + copy_cnt = dest_poly->num_alloc - dest_poly->numpts; // limit the append + } + + if (copy_cnt <= 0) + { +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, zero copy cnt, copy_cnt= %i\n", + copy_cnt); +#endif + return; + } + + pvp = &dest_poly->ppts[dest_poly->numpts]; // append + dest_poly->numpts += copy_cnt; // before copy_cnt gets decremented + + n = src_poly->numpts - copy_from; // vertexes to end of poly + if (copy_cnt > n) // copy to end of poly, then rollover + { + // Copy first portion, up to end of poly. + memcpy( pvp, &(src_poly->ppts[copy_from]), n*sizeof(void*)); + pvp += n; + // Rollover to start of src_poly + copy_cnt -= n; + copy_from = 0; + } + +#ifdef DEBUG_HWBSP + if (copy_from + copy_cnt > src_poly->numpts) // after wrap + { + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, exceeds src bounds, copy_from= %i, copy_cnt= %i, src numpts= %i\n", + copy_from, copy_cnt, src_poly->numpts); + } +#endif +#ifdef DEBUG_HWBSP + if ((pvp + copy_cnt) > (dest_poly->ppts + dest_poly->numpts)) // after wrap + { + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, exceeds dest bounds, copy_to= %i, copy_cnt= %i, numpts= %i\n", + (pvp - dest_poly->ppts), copy_cnt, dest_poly->numpts); + } +#endif + + if (copy_cnt > 0) + { + memcpy(pvp, &(src_poly->ppts[copy_from]), copy_cnt*sizeof(void*)); + } +} + +// Insert some new vertex, and then, +// copy some of another poly to the destination poly. +// v1, v2 : polyvertex to be inserted as first vertex of poly, in this order +// src_poly : copy from src_poly +// copy_from, copy_cnt : the indexes of the vertexes to copy +// dest_poly : the destination poly +static void wpoly_split_copy(polyvertex_t * v1, polyvertex_t * v2, + wpoly_t * src_poly, INT32 copy_from, INT32 copy_cnt, + /*OUT*/ wpoly_t * dest_poly) +{ + polyvertex_t **pvp; + INT32 n = 0; + + // Count the dest vertexes. + if (v1) + n++; + + if (v2) + n++; + + // Free old content, new allocation. + wpoly_free(dest_poly); +#ifdef DEBUG_TRACE + dest_poly->id3 = dest_poly->id2; + dest_poly->id2 = dest_poly->id1; + dest_poly->id1 = poly_id++; +#endif + wpoly_init_alloc(n + copy_cnt, dest_poly); + + // First two points of the dest_poly are the dividing seg. + dest_poly->numpts = n; // v1 and v2 + pvp = dest_poly->ppts; + + if (v1) + *pvp++ = v1; + + if (v2) + *pvp++ = v2; + + wpoly_append(src_poly, copy_from, copy_cnt, /*OUT*/ dest_poly); + } + +// Insert vertexes into the destination poly, cutout some vertexes, save some. +// v1, v2 : polyvertex to be inserted as first vertex of poly, in this order +// v_from, v_cnt : the indexes of the vertexes to save +// xpoly : the source and destination poly +static void wpoly_insert_cut(polyvertex_t *v1, polyvertex_t *v2, INT32 v_from, INT32 v_cnt, /*INOUT*/ wpoly_t *xpoly) +{ + wpoly_t tmp_poly; + + tmp_poly = *xpoly; // save ptrs and sizes + xpoly->ppts = NULL; // so does not get freed + wpoly_split_copy(v1, v2, &tmp_poly, v_from, v_cnt, /*OUT*/ xpoly); + wpoly_free(&tmp_poly); // release saved poly content +} + + +// Insert a vertex into the destination poly, at a position. +// v1 : polyvertex to be inserted +// v_at : the index where v1 is inserted +// xpoly : the source and destination poly +static void wpoly_insert_vert(polyvertex_t *v1, INT32 v_at, /*INOUT*/ wpoly_t *xpoly) +{ + wpoly_t tmp_poly; + INT32 numpts = xpoly->numpts; + + if (v_at > numpts) + return; + + tmp_poly = *xpoly; // save ptrs and sizes + // Copy back from tmp_poly to xpoly +#ifdef DEBUG_TRACE + xpoly->id3 = xpoly->id2; + xpoly->id2 = xpoly->id1; + xpoly->id1 = poly_id++; + + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "Insert creates poly id=%i,%i,%i\n", xpoly->id1, xpoly->id2, xpoly->id3); + } +#endif + wpoly_init_alloc(numpts + 1, xpoly); + xpoly->numpts = numpts + 1; + + if (v_at > 0) + { + memcpy(&(xpoly->ppts[0]), &(tmp_poly.ppts[0]), sizeof(void*) * v_at); + } + + xpoly->ppts[v_at] = v1; // insert + + if (v_at < numpts) + { + memcpy(&(xpoly->ppts[v_at + 1]), &(tmp_poly.ppts[v_at]), sizeof(void*) * (numpts - v_at)); + } + + wpoly_free(&tmp_poly); // release saved poly content +} + +// ---- Subsectors + +// Array of poly_subsector_t, +// Index by bsp subsector num, 0.. num_poly_subsector-1 +poly_subsector_t *poly_subsectors = NULL; +wpoly_t *wpoly_subsectors = NULL; // working subsectors + + +// extra subsectors are subsectors without segs, added for the plane polygons +#define NUM_EXTRA_SUBSECTORS 50 +size_t num_poly_subsector = 0; +size_t num_alloc_poly_subsector = 0; + +// ========================================================================== +// FLOOR & CEILING CONVEX POLYS GENERATION +// ========================================================================== + +#ifdef DEBUG_HWBSP +//debug counters +static int nobackpoly_cnt = 0; +static int skipcut_cnt = 0; +static int total_subsecpoly_cnt = 0; +#endif + +// -------------------------------------------------------------------------- +// Polygon fast alloc / free +// -------------------------------------------------------------------------- + +static void HWR_Freepolysubsectors(void); + +// only between levels, clear poly pool +static void HWR_ClearPolys(void) +{ + Z_FreeTag(PU_HWRPLANE); + poly_vert = NULL; + polyvert_store = NULL; +} + +// allocate pool for fast alloc of polys +void HWR_InitPolyPool(void) +{ + HWR_ClearPolys(); +} + +void HWR_FreePolyPool(void) +{ + HWR_Freepolysubsectors(); + HWR_ClearPolys(); +} + +static poly_t* HWR_AllocPoly(INT32 numpts) +{ + poly_t* p; + size_t size; + + size = sizeof(poly_t) + sizeof(polyvertex_t) * numpts; + p = Z_Malloc(size, PU_HWRPLANE, NULL); + p->numpts = numpts; + + return p; +} + +#ifdef DEBUG_HWBSP +// print poly for debugging +void pwpoly(wpoly_t *poly) +{ + int i; + for (i = 0; i < poly->numpts; i++) + { + if (poly->ppts[i]) + printf("(%6.2f,%6.2f)", poly->ppts[i]->x, poly->ppts[i]->y); + } + + printf("\n"); +} + +// print poly for debugging +void ppoly(poly_t *poly) +{ + int i; + for (i = 0; i < poly->numpts; i++) + printf("(%6.2f,%6.2f)", poly->pts[i].x, poly->pts[i].y); + printf("\n"); +} +#endif + +// The BSP has the partition lines that define the subsectors. They do not +// exist anywhere else. + +// The subsectors of the BSP only have segs that are parts of linedefs. +// The subsector segs are not in any special order. +// Subsectors with 0 segs are skipped in building the BSP, so those are missing. +// Such subsectors are defined only by the dividing lines. +// The subsector of the BSP often encloses some adjoining void space. + +// Deep water in BSP: The deep water sector uses a linedef referencing a +// remote sector. The BSP will have extra dividing line polygon splits that +// are useless. The BSP builder got confused by the linedefs with remote +// sector references. +// This will result in an attempted polygon split that misses entirely. + + +// --- Divide line + +typedef enum +{ + DVL_none, // no divide + DVL_v1, // divide at v1 end of segment + DVL_mid, // divide between v1 and v2 + DVL_v2, // divide at v2 end of segment +} divline_e; typedef struct { @@ -41,138 +598,73 @@ typedef struct float dx, dy; } fdivline_t; -// ========================================================================== -// FLOOR & CEILING CONVEX POLYS GENERATION -// ========================================================================== - -//debug counters -static INT32 nobackpoly = 0; -static INT32 skipcut = 0; -static INT32 totalsubsecpolys = 0; - -// -------------------------------------------------------------------------- -// Polygon fast alloc / free -// -------------------------------------------------------------------------- -//hurdler: quick fix for those who wants to play with larger wad - -#define ZPLANALLOC -#ifndef ZPLANALLOC -//#define POLYPOOLSIZE 1024000 // may be much over what is needed -/// \todo check out how much is used -static size_t POLYPOOLSIZE = 1024000; - -static UINT8 *gl_polypool = NULL; -static UINT8 *gl_ppcurrent; -static size_t gl_ppfree; -#endif - -// only between levels, clear poly pool -static void HWR_ClearPolys(void) +#ifdef DEBUG_HWBSP +static void fdivline_dump(const char * str, fdivline_t * dl) { -#ifndef ZPLANALLOC - gl_ppcurrent = gl_polypool; - gl_ppfree = POLYPOOLSIZE; -#endif + polyvertex_t v1; + v1.x = dl->x; + v1.y = dl->y; + CONS_Debug(DBG_RENDER, "%s", str); + polyvertex_dump(&v1); + CONS_Debug(DBG_RENDER, " to "); + v1.x += dl->dx; + v1.y += dl->dy; + polyvertex_dump(&v1); + CONS_Debug(DBG_RENDER, "slope (%f, %f)\n", dl->dx, dl->dy); } +#endif -// allocate pool for fast alloc of polys -void HWR_InitPolyPool(void) +typedef struct { -#ifndef ZPLANALLOC - INT32 pnum; + polyvertex_t divpt; + polyvertex_t * vertex; // when same as segment endpoint + float divfrac; // how far along the partline vector is the crossing point + int before, after; // index modifiers for hitting a vertex + boolean at_vert; // crossing point is at a vertex +} div_result_t; - //hurdler: quick fix for those who wants to play with larger wad - if ((pnum = M_CheckParm("-polypoolsize"))) - POLYPOOLSIZE = atoi(myargv[pnum+1])*1024; // (in kb) - - CONS_Debug(DBG_RENDER, "HWR_InitPolyPool(): allocating %d bytes\n", POLYPOOLSIZE); - gl_polypool = malloc(POLYPOOLSIZE); - if (!gl_polypool) - I_Error("HWR_InitPolyPool(): couldn't malloc polypool\n"); - HWR_ClearPolys(); -#endif -} - -void HWR_FreePolyPool(void) +#ifdef DEBUG_HWBSP +static void divresult_dump(const char *str, div_result_t *dr) { -#ifndef ZPLANALLOC - if (gl_polypool) - free(gl_polypool); - gl_polypool = NULL; -#endif + CONS_Debug(DBG_RENDER, "%s", str); + CONS_Debug(DBG_RENDER, " CROSS %6.4f BEFORE v1+%i AFTER v1+%i ", dr->divfrac, dr->before, dr->after); + + if (dr->at_vert) + { + CONS_Debug(DBG_RENDER, " AT"); + } + + if (dr->vertex) + { + CONS_Debug(DBG_RENDER, " SEGPT"); + polyvertex_dump(dr->vertex); + } + else + { + CONS_Debug(DBG_RENDER, " PT"); + polyvertex_dump( &dr->divpt); + } + + CONS_Debug(DBG_RENDER, "\n"); } - -static poly_t *HWR_AllocPoly(INT32 numpts) -{ - poly_t *p; - size_t size = sizeof (poly_t) + sizeof (polyvertex_t) * numpts; -#ifdef ZPLANALLOC - p = Z_Malloc(size, PU_HWRPLANE, NULL); -#else -#ifdef PARANOIA - if (!gl_polypool) - I_Error("Used gl_polypool without init!\n"); - if (!gl_ppcurrent) - I_Error("gl_ppcurrent == NULL!\n"); #endif - if (gl_ppfree < size) - I_Error("HWR_AllocPoly(): no more memory %u bytes left, %u bytes needed\n\n%s\n", - gl_ppfree, size, "You can try the param -polypoolsize 2048 (or higher if needed)"); - - p = (poly_t *)gl_ppcurrent; - gl_ppcurrent += size; - gl_ppfree -= size; -#endif - p->numpts = numpts; - return p; -} - -static polyvertex_t *HWR_AllocVertex(void) -{ - polyvertex_t *p; - size_t size = sizeof (polyvertex_t); -#ifdef ZPLANALLOC - p = Z_Malloc(size, PU_HWRPLANE, NULL); -#else - if (gl_ppfree < size) - I_Error("HWR_AllocVertex(): no more memory %u bytes left, %u bytes needed\n\n%s\n", - gl_ppfree, size, "You can try the param -polypoolsize 2048 (or higher if needed)"); - - p = (polyvertex_t *)gl_ppcurrent; - gl_ppcurrent += size; - gl_ppfree -= size; -#endif - return p; -} - -/// \todo polygons should be freed in reverse order for efficiency, -/// for now don't free because it doesn't free in reverse order -static void HWR_FreePoly(poly_t *poly) -{ -#ifdef ZPLANALLOC - Z_Free(poly); -#else - const size_t size = sizeof (poly_t) + sizeof (polyvertex_t) * poly->numpts; - memset(poly, 0x00, size); - //mempoly -= polysize; -#endif -} - - -// Return interception along bsp line, +// Return interception along bsp line (partline), // with the polygon segment -// -static float bspfrac; -static polyvertex_t *fracdivline(fdivline_t *bsp, polyvertex_t *v1, - polyvertex_t *v2) + +// BOOMEDIT.WAD has a vertex error of .21 +#define DIVLINE_VERTEX_DIFF 0.45f + +// partline : the dividing line +// p1, p2 : the polygon segment +// result : the result of the division +static divline_e fracdivline(fdivline_t* partline, polyvertex_t* v1, polyvertex_t* v2, + /*OUT*/ div_result_t * result) { - static polyvertex_t pt; double frac; - double num; - double den; - double v1x,v1y,v1dx,v1dy; - double v2x,v2y,v2dx,v2dy; + double den; // numerator, denominator + double v1x, v1y, v1dx, v1dy; // polygon side vector, v1->v2 + double v3x, v3y, v3dx, v3dy; // partline vector // a segment of a polygon v1x = v1->x; @@ -181,395 +673,1304 @@ static polyvertex_t *fracdivline(fdivline_t *bsp, polyvertex_t *v1, v1dy = v2->y - v1->y; // the bsp partition line - v2x = bsp->x; - v2y = bsp->y; - v2dx = bsp->dx; - v2dy = bsp->dy; + v3x = partline->x; + v3y = partline->y; + v3dx = partline->dx; + v3dy = partline->dy; - den = v2dy*v1dx - v2dx*v1dy; - if (fabsf((float)den) < 1.0E-36f) // avoid checking exactly for 0.0 - return NULL; // parallel + den = v3dy*v1dx - v3dx*v1dy; + if (fabs(den) < 1.0E-36f) // avoid check of float for exact 0 + return DVL_none; // partline and polygon side are effectively parallel // first check the frac along the polygon segment, // (do not accept hit with the extensions) - num = (v2x - v1x)*v2dy + (v1y - v2y)*v2dx; - frac = num / den; - if (frac < 0.0l || frac > 1.0l) - return NULL; + const double num1 = (v3x - v1x)*v3dy + (v1y - v3y)*v3dx; + frac = num1 / den; + + // 0= cross at v1, 1.0= cross at v2 + if (frac < 0.0 || frac > 1.0) // double + return DVL_none; // not within the polygon side // now get the frac along the BSP line // which is useful to determine what is left, what is right - num = (v2x - v1x)*v1dy + (v1y - v2y)*v1dx; - frac = num / den; - bspfrac = (float)frac; + double num2 = (v3x - v1x)*v1dy + (v1y - v3y)*v1dx; + result->divfrac = num2 / den; // how far along partline vector - // find the interception point along the partition line - pt.x = (float)(v2x + v2dx*frac); - pt.y = (float)(v2y + v2dy*frac); + // [WDJ] find the interception point along the segment. + // It should be slightly more accurate because it is always closer to the + // crossing point than arbitrary positions on the partition line. + result->divpt.x = v1x + v1dx*frac; + result->divpt.y = v1y + v1dy*frac; - return &pt; + // Determine if dividing point is one of the end vertex. + // Set before and after indexes, relative to v1 index. + if (frac < 0.05 // double + && SameVertex(&result->divpt, v1, DIVLINE_VERTEX_DIFF)) + { + result->vertex = v1; + result->before = -1; // before v1 + result->after = 1; // at v2 + result->at_vert = true; + return DVL_v1; + } + + if (frac > 0.95 // double + && SameVertex(&result->divpt, v2, DIVLINE_VERTEX_DIFF)) + { + result->vertex = v2; + result->before = 0; // at v1 + result->after = 2; // after v2 + result->at_vert = true; + return DVL_v2; + } + + // Middle split + result->vertex = NULL; + result->before = 0; // at v1 + result->after = 1; // at v2 + result->at_vert = false; + return DVL_mid; } -// if two vertice coords have a x and/or y difference -// of less or equal than 1 FRACUNIT, they are considered the same -// point. Note: hardcoded value, 1.0f could be anything else. -static boolean SameVertice (polyvertex_t *p1, polyvertex_t *p2) +// Adapted from function in prboom. +// Point is to rightside of divline when result > 0, +// but result is multiplied by length of divline. +// Returns near 0, when point is on, or nearly on, the divline. +static inline float point_rightside(fdivline_t *dl, polyvertex_t *v4) { -#if 0 - float diff; - diff = p2->x - p1->x; - if (diff < -1.5f || diff > 1.5f) - return false; - diff = p2->y - p1->y; - if (diff < -1.5f || diff > 1.5f) - return false; -#elif 0 - if (p1->x != p2->x) - return false; - if (p1->y != p2->y) - return false; -#elif 0 - if (fabsf( p2->x - p1->x ) > 1.0E-36f ) - return false; - if (fabsf( p2->y - p1->y ) > 1.0E-36f ) - return false; -#else -#define DIVLINE_VERTEX_DIFF 0.45f - float ep = DIVLINE_VERTEX_DIFF; - if (fabsf( p2->x - p1->x ) > ep ) - return false; - if (fabsf( p2->y - p1->y ) > ep ) - return false; -#endif - // p1 and p2 are considered the same vertex - return true; + // Cross product of dl and vector dl->(x,y) to v4, + // is > 0 when v4 is to right side of divline. + // Viewed along divline from vertex, looking towards positive dx,dy. + // If divline is rotated until dy>0 and dx = 0, then true when rotated + // vertex position is to the right of the divline (v4->x > dl->x). + return (float) + ( (((double)(v4->x) - (double)(dl->x)) * (double)(dl->dy)) + - (((double)(v4->y) - (double)(dl->y)) * (double)(dl->dx))); } +// Return the cross product of the vector p1->p2, and p1->v4. +// The cross product is > 0 when v4 is to the right side of the vector. +// If the coordinates are rotated until the vector dy>0 and dx = 0, then the +// cross product is > 0 when v4 is to the right of the vector. +static inline double cross_product(polyvertex_t *p1, polyvertex_t *p2, polyvertex_t *v4) +{ + return + ( ((double)(v4->x) - (double)(p1->x)) * ((double)(p2->y) - (double)(p1->y)) + - ((double)(v4->y) - (double)(p1->y)) * ((double)(p2->x) - (double)(p1->x)) + ); +} -// split a _CONVEX_ polygon in two convex polygons +#ifdef POLYTILE +// Polytile list +// SplitPoly searches for the other poly with the same vertexes when it +// splits a poly segment, and adds the same vertex there too. +// This prevents any cracks from forming. + +// Hold all polygons that tile the level map. +#define POLYTILE_NUM_POLY 256 +typedef struct polytile_store_s +{ + struct polytile_store_s *next; // link for search + INT32 num_tile_used; + wpoly_t *tile[POLYTILE_NUM_POLY]; +} polytile_store_t; + +// These are freed by Z_FreeTag(PU_HWRPLANE). --lug: no theyre not lmao +polytile_store_t *polytile_store = NULL; +polytile_store_t *polytile_free = NULL; + +// Cleanup after usage. +static void polytile_clean(void) +{ + while (polytile_store) + { + polytile_store_t *ptp = polytile_store; + polytile_store = ptp->next; + Z_Free(ptp); + } + + // null this just in case, we dont want any dingalinging pointers + polytile_free = NULL; +} + +// Save the poly ptr within the polytile lists. +static void polytile_enter(wpoly_t * poly) +{ + polytile_store_t *ptp = polytile_store; + + if (!cv_glpolytile.value) + return; + + if (poly->numpts == 0) + return; // do not enter NULL poly + + if (!polytile_store + || (polytile_store->num_tile_used >= POLYTILE_NUM_POLY)) + { + // Need another storage unit. + if (polytile_free) + { + polytile_store = polytile_free; + polytile_free = polytile_free->next; + } + else + { + polytile_store = Z_Malloc(sizeof(polytile_store_t), PU_HWRPLANE, NULL); + } + + polytile_store->next = ptp; // link for search + polytile_store->num_tile_used = 0; + } + + polytile_store->tile[polytile_store->num_tile_used++] = poly; +} + +static void polytile_remove(wpoly_t * poly) +{ + polytile_store_t *ptp; + wpoly_t **wpp; + wpoly_t *lp; + int i; + + // Search poly tiling. + ptp = polytile_store; + + while (ptp) + { + // Search for poly in all polytile_store_t + wpp = &ptp->tile[0]; + for (i = ptp->num_tile_used-1; i >= 0; i--) + { + if (*wpp == poly) + goto found; + wpp++; + } + ptp = ptp->next; + } + return; // not found + +found: + // Removing it gets complicated due to need to condense the list for searching. + // Move the last poly to the empty spot. There must be a last poly entered. + lp = polytile_store->tile[polytile_store->num_tile_used-1]; + *wpp = lp; // keep store compacted (ok if *wpp == lp already) + + // Remove last poly spot. + polytile_store->num_tile_used--; + if (polytile_store->num_tile_used == 0) + { + // Went empty, put on free list. + ptp = polytile_store; + polytile_store = ptp->next; + ptp->next = polytile_free; + polytile_free = ptp; + } +} + +// Seach polytile and add the new vertex between the vertex of the poly side. +// newvert : add this vertex +// poly : the poly being split +// i1, i2 : indexes of the split side +static void add_vertex_between(polyvertex_t * newvert, wpoly_t * poly, INT32 i1, INT32 i2) +{ + polytile_store_t *ptp; + wpoly_t *wp; + INT32 t, j2; + polyvertex_t *s1, *s2; + polyvertex_t *v1 = poly->ppts[i1]; + polyvertex_t *v2 = poly->ppts[i2]; + + if (!cv_glpolytile.value) + return; + +// shut up compiler +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" + // Do not need to enter a vertex in vert or horz segments. + // Those do not cause problems with cracks. + if ((newvert->x == v1->x) && (newvert->x == v2->x)) + return; + + if ((newvert->y == v1->y) && (newvert->y == v2->y)) + return; +#pragma GCC diagnostic pop + + // Search poly tiling. + ptp = polytile_store; + while (ptp) + { + // Search for poly in all polytile_store_t + for (t = ptp->num_tile_used-1; t >= 0; t--) + { + wp = ptp->tile[t]; + if (wp == poly) + continue; // we already know this poly has v1,v2. + + // Search this poly for v1,v2 vertex in opposite order. + s1 = wp->ppts[wp->numpts-1]; // last vertex + for (j2 = 0; j2 < wp->numpts; j2++) + { + s2 = wp->ppts[j2]; + + if (s2 == v1) + { + if (s1 == v2) + goto found; + + break; // cannot find v1 a second time in same poly + } + + s1 = s2; + } + + } + ptp = ptp->next; + } + return; // not found + +found: +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, " Insert vertex:" ); + polyvertex_dump( newvert); + CONS_Debug(DBG_RENDER, " found in %i:", wp->id1); + polyvertex_dump( v1); + polyvertex_dump( v2); + CONS_Debug(DBG_RENDER, "\n"); + } +#endif + // Insert newvert between s1 and s2 (opposite order or v1 v2). + // (j2-1) is vertex index of s1, j2 is vertex index of s2. + wpoly_insert_vert(newvert, j2, /*INOUT*/ wp); + // Result is in the same wp. +} +#endif + +// Split a _CONVEX_ polygon in two convex polygons. +// poly : polygon to be split by divline // outputs: // frontpoly : polygon on right side of bsp line // backpoly : polygon on left side // -static void SplitPoly (fdivline_t *bsp, //splitting parametric line - poly_t *poly, //the convex poly we split - poly_t **frontpoly, //return one poly here - poly_t **backpoly) //return the other here +// Called from: HWR_WalkBSPNode +static void SplitPoly(fdivline_t *dlnp, wpoly_t *poly, /*OUT*/ wpoly_t *frontpoly, wpoly_t *backpoly) { - INT32 i,j; - polyvertex_t *pv; + // Split poly at A and B. + wpoly_t *polyA; // the poly from A to B, clockwise + wpoly_t *polyB; // the poly from B to A, clockwise + INT32 n, i, j; +#ifdef POLYTILE + INT32 A_before_wrap, B_after_wrap; +#endif + divline_e dle; + div_result_t A = {}, B = {}; // dividing points + div_result_t *result = NULL; - INT32 ps = -1,pe = -1; - INT32 nptfront,nptback; - polyvertex_t vs = {0,0,0}; - polyvertex_t ve = {0,0,0}; - polyvertex_t lastpv = {0,0,0}; - float fracs = 0.0f,frace = 0.0f; //used to tell which poly is on - // the front side of the bsp partition line - INT32 psonline = 0, peonline = 0; +#ifdef DEBUG_TRACE + if (trigger_trace) + { + fdivline_dump("SplitPoly: divline", dlnp); + wpoly_dump("Poly", poly); + } +#endif + if (poly->numpts < 3) + goto poly_degenerate; // not a poly, cannot split it + + result = &A; // Setup to get crossing point A for (i = 0; i < poly->numpts; i++) { - j = i + 1; - if (j == poly->numpts) j = 0; + // i, j are one side of the poly + j = i+1; + if (j == poly->numpts) + j = 0; // wrap poly - // start & end points - pv = fracdivline(bsp, &poly->pts[i], &poly->pts[j]); - - if (pv == NULL) + // Find A and B points + dle = fracdivline(dlnp, poly->ppts[i], poly->ppts[j], /*OUT*/ result); + if (dle == DVL_none) continue; - if (ps < 0) + // have dividing pt + if (result == &A) { - // first point - ps = i; - vs = *pv; - fracs = bspfrac; - } - else - { - //the partition line traverse a junction between two segments - // or the two points are so close, they can be considered as one - // thus, don't accept, since split 2 must be another vertex - if (SameVertice(pv, &lastpv)) - { - if (pe < 0) - { - ps = i; - psonline = 1; - } - else - { - pe = i; - peonline = 1; - } - } - else - { - if (pe < 0) - { - pe = i; - ve = *pv; - frace = bspfrac; - } - else - { - // a frac, not same vertice as last one - // we already got pt2 so pt 2 is not on the line, - // so we probably got back to the start point - // which is on the line - if (SameVertice(pv, &vs)) - psonline = 1; - break; - } - } + // Split at A + // Dependent upon dle, setup in fracdivline. + A.before += i; + A.after += i; + result = &B; // Setup to get crossing point B + continue; } - // remember last point intercept to detect identical points - lastpv = *pv; + // The partition line can cross at a vertex, between two segments, + // or the two points are so close, they can be considered as one. + // Crossing point B must be another vertex. + + // When ( dle == DVL_v1 || dle == DVL_v2 ) then test for same vertex. + // It is NULL for DVL_mid. + // When dividing point is at a vertex, it is found at next segment too. + if (B.vertex // ( dle == DVL_v1 || dle == DVL_v2 ) + && B.vertex == A.vertex) + continue; + + // Split at B + // Dependent upon dle, setup in fracdivline. + B.before += i; + B.after += i; // linear, no rollover + goto split_poly; // got 2 points } - // no split: the partition line is either parallel and + goto no_split; + +split_poly: +#ifdef POLYTILE + A_before_wrap = (A.before < 0)? (A.before + poly->numpts) : A.before; + B_after_wrap = (B.after >= poly->numpts)? (B.after - poly->numpts) : B.after; +#endif + + // Less aggressive same vertex, to avoid kinking line. +//#define SAME_VERTEX_DIST 0.01f +#define SAME_VERTEX_DIST 0.08f + if (A.vertex == NULL) + { + A.vertex = store_polyvertex(&A.divpt, SAME_VERTEX_DIST); +#ifdef POLYTILE + add_vertex_between(A.vertex, poly, A_before_wrap, A.after); +#endif + } + + if (B.vertex == NULL) + { + B.vertex = store_polyvertex(&B.divpt, SAME_VERTEX_DIST); +#ifdef POLYTILE + add_vertex_between(B.vertex, poly, B.before, B_after_wrap); +#endif + } + + // The frontpoly is the one on the 'right' side + // of the partition line. + if (A.divfrac > B.divfrac) + { + polyA = frontpoly; + polyB = backpoly; + } + else + { + polyA = backpoly; + polyB = frontpoly; + } + + // Form PolyA + // Number of points from A to B clockwise. + n = B.before - A.after + 1; + if (n > 0) + { + // B, A, poly from A to B clockwise + wpoly_split_copy(B.vertex, A.vertex, poly, A.after, n, /*OUT*/ polyA); + } + else + polyA->numpts = 0; + + // Form PolyB + // Number of points from B to A clockwise. + n = A.before + poly->numpts - B.after + 1; + if (n > 0) + { + // A, B, poly from B to A clockwise + wpoly_split_copy(A.vertex, B.vertex, poly, + ((B.after < poly->numpts)? B.after : (B.after - poly->numpts)), + n, /*OUT*/ polyB); + } + else + polyB->numpts = 0; + +#ifdef DEBUG_HWBSP + // Test that frontpoly is to the right + if (frontpoly->numpts >= 2 + && (point_rightside( dlnp, frontpoly->ppts[2] ) < -0.5f )) + CONS_Printf( EMSG_warn, "SplitPoly: frontpoly on left side\n"); + + // Test that backpoly is to the left + if (backpoly->numpts >= 2 + && (point_rightside( dlnp, backpoly->ppts[2] ) > 0.5f )) + CONS_Printf( EMSG_warn, "SplitPoly: backpoly on right side\n"); +#endif +#ifdef DEBUG_TRACE + frontpoly->id3 = poly->id2; + frontpoly->id2 = poly->id1; + backpoly->id3 = poly->id2; + backpoly->id2 = poly->id1; + + if (trigger_trace) + { + wpoly_dump( " Front Poly", frontpoly); + wpoly_dump( " Back Poly", backpoly); + } +#endif + + return; + +no_split: + // no split : the partition line is either parallel and // aligned with one of the poly segments, or the line is totally // out of the polygon and doesn't traverse it (happens if the bsp // is fooled by some trick where the sidedefs don't point to // the right sectors) - if (ps < 0) + if (result == &A) { - //I_Error("SplitPoly: did not split polygon (%d %d)\n" - // "debugpos %d",ps,pe,debugpos); +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "DEBUG: SplitPoly: divline missed entirely\n"); +#endif +#ifdef DEBUG_TRACE + // no results to dump +#endif // this eventually happens with 'broken' BSP's that accept // linedefs where each side point the same sector, that is: // the deep water effect with the original Doom - - /// \todo make sure front poly is to front of partition line? - - *frontpoly = poly; - *backpoly = NULL; - return; } - - if (pe < 0) + else if (A.vertex == NULL) { - //I_Error("SplitPoly: only one point for split line (%d %d)", ps, pe); - *frontpoly = poly; - *backpoly = NULL; - return; - } - if (pe <= ps) - I_Error("SplitPoly: invalid splitting line (%d %d)", ps, pe); - - // number of points on each side, _not_ counting those - // that may lie just one the line - nptback = pe - ps - peonline; - nptfront = poly->numpts - peonline - psonline - nptback; - - if (nptback > 0) - *backpoly = HWR_AllocPoly(2 + nptback); - else - *backpoly = NULL; - if (nptfront > 0) - *frontpoly = HWR_AllocPoly(2 + nptfront); - else - *frontpoly = NULL; - - // generate FRONT poly - if (*frontpoly) - { - pv = (*frontpoly)->pts; - *pv++ = vs; - *pv++ = ve; - i = pe; - do +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, + "DEBUG: SplitPoly: one new divide point %d (%6.2f,%6.2f) %d\n", + A.before, A.divpt.x, A.divpt.y, A.after); +#endif +#ifdef DEBUG_TRACE + if (trigger_trace) { - if (++i == poly->numpts) - i = 0; - *pv++ = poly->pts[i]; - } while (i != ps && --nptfront); + // Found a crossing A point, but did not find a B crossing. Should not happen. + divresult_dump( " CROSSING-A", &A); + } +#endif } - - // generate BACK poly - if (*backpoly) + else { - pv = (*backpoly)->pts; - *pv++ = ve; - *pv++ = vs; - i = ps; - do +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, + "DEBUG: SplitPoly: intersect at one vertex, %d\n", + A.before + 1); +#endif +#ifdef DEBUG_TRACE + if (trigger_trace) { - if (++i == poly->numpts) - i = 0; - *pv++ = poly->pts[i]; - } while (i != pe && --nptback); + // Found A point at vertex, but did not find a B crossing. Just touched a vertex. + divresult_dump( " CROSSING-A", &A); + } +#endif } - // make sure frontpoly is the one on the 'right' side - // of the partition line - if (fracs > frace) + // [WDJ] The front and back MUST match the BSP determination, else it will assign + // this poly to the wrong sector. + // May have 1 point on the line, and other points might be very close to the line. + // Can only trust a test of a point that is NOT on the line. + // Using a tstdist = 0.5f worked, but it might break on very small poly. + // Avactor.wad had polys with d = 9.09, 5552, 1.64, 1011, 14252, 0.0136, 0.00634, 14655, 3, + // -0.3044, 7, 1.53, etc.., + // and one poly with d = -0.023 with the rest of the points d > 0. + // One 14 point poly with divline len=8, had to test 9 points to find d = 36. + // Typical: divline len=70 d=6828, divline len=1.0 d=35, divline len=1.414 d=5. + // The result of point_rightside is proportional to the length of divline. + // The divline length can be from 1.414 to 14000. + const float tstd = (sqrtf((dlnp->dx * dlnp->dx) + (dlnp->dy * dlnp->dy))) / 2.0f; + float sumd = 0; // accumulated left or rightness. + + for (i = 0; i < poly->numpts; i++) { - poly_t *swappoly; - swappoly = *backpoly; - *backpoly = *frontpoly; - *frontpoly = swappoly; + // Look for a point that is obviously to the left or right. + float d = point_rightside(dlnp, poly->ppts[i]); // d > 0 is right + sumd += d; + + if (d > tstd) + goto poly_rightside; + + if (d < -tstd) + goto poly_leftside; } - HWR_FreePoly (poly); + // Did not find an obvious left or right point, then + if (sumd < 0) + goto poly_leftside; + +poly_rightside: + // poly is to rightside of divline. + wpoly_move(poly, frontpoly); + backpoly->numpts = 0; + return; + +poly_leftside: + // poly is to leftside of divline. + wpoly_move(poly, backpoly); + frontpoly->numpts = 0; + return; + +poly_degenerate: + // This cannot lead to any subsector polys. +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "DEBUG: SplitPoly: degenerate poly has %d points\n", poly->numpts); +#endif + frontpoly->numpts = 0; + backpoly->numpts = 0; + return; +} + +// The BSP creates convex polygons, up to the point where it presents +// the subsector. The subsector segs may adjoin void space, and if that +// is cut out of the subsector polygon it might not be convex. +// Where the cutting seg does not entirely traverse the polygon, +// it should only cut over its length, not entirely to the other side +// of the polygon. Another seg, and maybe more, must be present to +// finish the cut to the other side of the polygon. There is no +// assurance that these segs will keep the polygon convex. +// Example: BOOMEDIT.WAD, subsector 206 is not convex after cutting segs. +// It is cut by seg linedef 104, and seg linedef 110. + +#define CUTOUT_NON_CONVEX 1 + +#ifdef CUTOUT_NON_CONVEX +// Force convex solution. +static void enforce_convex(wpoly_t * poly) +{ + polyvertex_t *rv1, *rv2, *rv3; + INT32 i1, i2, i3; + INT32 numpts = poly->numpts; + + for (i2 = 0; i2 < numpts;) + { + // Check that angle at i2 is less than 180. + i3 = i2 + 1; + if (i3 == numpts) + i3 = 0; + + rv3 = poly->ppts[i3]; + + i1 = i2 - 1; + if (i1 < 0) + i1 += numpts; + + rv1 = poly->ppts[i1]; + rv2 = poly->ppts[i2]; + + // cross product to check angle + if (cross_product( rv1, rv2, rv3 ) < 0) + { + // Remove the i2 vertex, to make the polygon more convex. + if ((i2+1) < numpts) + { // Shuffle down by 1 + memmove(&poly->ppts[i2], &poly->ppts[i2+1], sizeof(void*)*(numpts - (i2+1))); + } + numpts--; + + if (numpts <= 3) + break; + + // Have to repeat from i1 vertex, because that vertex angle + // changed too. + i2 = (i1 > 0) ? i1 : 0; + continue; + } + + i2++; + } + + poly->numpts = numpts; +} +#endif + +#ifdef CUTOUT_NON_CONVEX +// Some of the segs will only partially cross the polygon, but connect with +// another seg, and together will cross it, or form a corner. +// Trying to cut with these leads to an incomplete or non-convex polygon. +// Then latter seg cuts will have 3 or 4 crossings. +// Deal with linking the segs together separate from the seg cutting operation. + +typedef struct loose_seg_s +{ + struct loose_seg_s *next; + seg_t *seg; // the seg + polyvertex_t *p1, *p2; // clockwise order +} loose_seg_t; + +typedef struct seg_chain_s +{ + struct seg_chain_s *next; + loose_seg_t *first_seg; // head of seg-chain + loose_seg_t *last_seg; // tail of seg-chain + polyvertex_t *p1, *p2; // ends in clockwise order + boolean loose1, loose2; // loose ends of the seg-chain + INT32 num_seg; +} seg_chain_t; + +// Nothing to gain by making this a parameter, this saves param passing. +// Easier to deal with releasing memory. +static seg_chain_t *seg_chain = NULL; // Z_Malloc + +static void free_first_seg_chain(void) +{ + seg_chain_t *sctp; + loose_seg_t *lsp, *lsp2; + + sctp = seg_chain; + + if (sctp) + { + seg_chain = sctp->next; + // Free the list of loose segs + lsp = sctp->first_seg; + + while (lsp) + { + lsp2 = lsp->next; + Z_Free(lsp); + lsp = lsp2; + } + + Z_Free(sctp); + } +} + +// Clear out the seg_chain structure +static void clear_seg_chains(void) +{ + while (seg_chain) + free_first_seg_chain(); +} + +// The seg-chains may be in portions, due to entry order. +static void condense_seg_chains(void) +{ + seg_chain_t *sctp; + seg_chain_t *sctp2; + seg_chain_t *sctp2_prev; + polyvertex_t *pv2; + + // Search seg-chains for combinations. + sctp = seg_chain; + while (sctp) + { + if (sctp->loose2) // p2 is loose + { + // Search for pv2 as loose first in another seg-chain + pv2 = sctp->p2; + sctp2_prev = NULL; + sctp2 = seg_chain; + + while(sctp2) + { + if (sctp2 != sctp + && sctp2->loose1 + && sctp2->p1 == pv2) + goto combine; + + sctp2_prev = sctp2; + sctp2 = sctp2->next; + } + } + + sctp = sctp->next; + + continue; + +combine: + // Append sctp2 to last of sctp seg-chain. + sctp->last_seg->next = sctp2->first_seg; + sctp->last_seg = sctp2->last_seg; + sctp->p2 = sctp2->p2; + sctp->loose2 = sctp2->loose2; + sctp->num_seg += sctp2->num_seg; + + // Remove sctp2 + if (sctp2_prev) + sctp2_prev->next = sctp2->next; + else + seg_chain = sctp2->next; + + Z_Free( sctp2); + + continue; // with same sctp + } +} + +// Save loose segs in the seg_chain structure. +// B_A_order : the vertex order is B A for clockwise +static void save_loose_seg(seg_t *loose_seg, + polyvertex_t *vA, polyvertex_t *vB, + boolean looseA, boolean looseB, + boolean B_A_order) +{ + loose_seg_t *lsp; + seg_chain_t *sctp; + polyvertex_t *pv1, *pv2; + boolean loose1, loose2; + + // B_A_order is determined by the order of the polygon sides. + if (B_A_order) + { + pv1 = vB; + pv2 = vA; + loose1 = looseB; + loose2 = looseA; + } + else + { + pv1 = vA; + pv2 = vB; + loose1 = looseA; + loose2 = looseB; + } + + lsp = Z_Malloc(sizeof(loose_seg_t), PU_HWRPLANE, NULL); + lsp->seg = loose_seg; + lsp->p1 = pv1; + lsp->p2 = pv2; + lsp->next = NULL; + + // Search seg-chains + sctp = seg_chain; + while (sctp) + { + if (loose2 + && sctp->loose1 + && sctp->p1 == pv2) + { + // first of seg-chain + lsp->next = sctp->first_seg; + sctp->first_seg = lsp; + sctp->p1 = pv1; + sctp->loose1 = loose1; + sctp->num_seg++; + return; + } + + if (loose1 + && sctp->loose2 + && sctp->p2 == pv1 ) + { + // last of seg-chain + lsp->next = NULL; + sctp->last_seg->next = lsp; + sctp->last_seg = lsp; + sctp->p2 = pv2; + sctp->loose2 = loose2; + sctp->num_seg++; + return; + } + + sctp = sctp->next; + } + + // Need new seg-chain + sctp = Z_Malloc(sizeof(seg_chain_t), PU_HWRPLANE, NULL); + sctp->next = NULL; + sctp->num_seg = 1; + sctp->first_seg = lsp; + sctp->last_seg = lsp; + sctp->p1 = pv1; + sctp->p2 = pv2; + sctp->loose1 = loose1; + sctp->loose2 = loose2; + sctp->next = seg_chain; + seg_chain = sctp; } -// use each seg of the poly as a partition line, keep only the -// part of the convex poly to the front of the seg (that is, -// the part inside the sector), the part behind the seg, is -// the void space and is cut out -// -static poly_t *CutOutSubsecPoly(seg_t *lseg, INT32 count, poly_t *poly) +//#define SEG_CHAIN_2 + +// Apply the list of loose end seg chains. +// Return true if a possible non-convex cut is made. +static boolean apply_seg_chains(wpoly_t *poly) { - INT32 i, j; + boolean check_convex = false; + wpoly_t comb_poly; // combine seg-chain and poly + loose_seg_t *lsp; + seg_chain_t *sctp; + polyvertex_t *rv1, *rv2; + INT32 n, i1, i2; + INT32 numpts = poly->numpts; - polyvertex_t *pv; - - INT32 nump = 0, ps, pe; - polyvertex_t vs = {0, 0, 0}, ve = {0, 0, 0}, - p1 = {0, 0, 0}, p2 = {0, 0, 0}; - float fracs = 0.0f; - - fdivline_t cutseg; // x, y, dx, dy as start of node_t struct - - poly_t *temppoly; - - // for each seg of the subsector - for (; count--; lseg++) + // All the seg-chains are part of the polygon. If a seg-chain is + // outside the polygon, the polygon must be expanded to include it. + // Otherwise there will be cracks in the floor. + for (;;) { - line_t *line = lseg->linedef; + sctp = seg_chain; + if (!sctp) + break; - if (lseg->glseg) + rv1 = sctp->p1; + rv2 = sctp->p2; + i1 = i2 = numpts + 20; // invalid + +#ifndef SEG_CHAIN_2 + if (sctp->loose1 || sctp->loose2) + { + // FIXME: This needs to be handled. + goto reject; + } +#endif + +#ifdef SEG_CHAIN_2 + if (! sctp->loose1) +#endif + { + // Find original crossing point. + for ( i1 = 0; i1 < numpts; i1++) + { + if (poly->ppts[i1] == rv1) + break; + } + } + +#ifdef SEG_CHAIN_2 + if (! sctp->loose2) +#endif + { + // Find original crossing point. + for (i2 = 0; i2 < numpts; i2++) + { + if (poly->ppts[i2] == rv2) + break; + } + } + + if ((i1 < numpts) && (i2 < numpts)) + { + // Insert points are still there. + // Alloc the right size comb_poly. + i2 ++; // copy after i2 + if (i2 >= numpts) + i2 -= numpts; + + n = i1 - i2; // not inclusive of i1 or i2 + if (n < 0) + n += numpts; + + wpoly_init_alloc(sctp->num_seg + 1 + n, &comb_poly); +#ifdef DEBUG_TRACE + comb_poly.id3 = poly->id2; + comb_poly.id2 = poly->id1; + comb_poly.id1 = poly_id++; +#endif + // Copy the seg-chain into the comb_poly. + polyvertex_t ** ppv = comb_poly.ppts; + + for (lsp = sctp->first_seg; lsp; lsp = lsp->next) + { + *ppv++ = lsp->p1; // rv1 to before rv2 + } + + *ppv = rv2; + comb_poly.numpts = sctp->num_seg + 1; + + // It is valid for n == 0, with num_seg > 1, + // and both end points on the same poly side. + if (n > 0) + { + // Save i2 to i1, which starts after rv2, to the comb_poly. + wpoly_append(poly, i2, n, /*OUT*/ &comb_poly); + } + + wpoly_move(&comb_poly, /*OUT*/ poly); // empty comb_poly + numpts = poly->numpts; + check_convex = true; + } +reject: + free_first_seg_chain(); + } + + return check_convex; +} + +#endif + +// Use each seg of the poly as a partition line, keep only the +// part of the convex poly to the front of the seg (that is, +// the part inside the sector). The part behind the seg, is +// the void space and is cut out. +// +// poly : surrounding convex polygon, non-destructive +// ssindex : subsec index, 0..(numsubsectors-1) +// Called from: HWR_SubsecPoly +static void CutOutSubsecPoly(INT32 ssindex, /*INOUT*/ wpoly_t* poly) +{ + subsector_t* sub; + seg_t *lseg; // array of seg + INT16 segcount; // number of seg in the array + + polyvertex_t *rv1, *rv2; // A,B or B,A + boolean cut_at_vert; + +#ifdef CUTOUT_NON_CONVEX + boolean check_convex = false; + boolean looseA, looseB; +#endif + + vertex_t *v1, *v2; + polyvertex_t p1, p2; + fdivline_t cutseg; // x,y,dx,dy as start of node_t struct + divline_e dle; + + div_result_t A = {}, B = {}; // dividing points + div_result_t *result = NULL; + INT32 poly_num_pts, ps, n; + INT32 i1, i2; + + poly_num_pts = poly->numpts; + sub = &subsectors[ssindex]; + segcount = sub->numlines; + lseg = &segs[sub->firstline]; + + // For each seg of the subsector + for (; segcount--; lseg++) + { + //x,y,dx,dy (like a divline) + const line_t *line = lseg->linedef; + + if (!line || lseg->glseg) continue; - //x,y,dx,dy (like a divline) - p1.x = FIXED_TO_FLOAT(lseg->side ? line->v2->x : line->v1->x); - p1.y = FIXED_TO_FLOAT(lseg->side ? line->v2->y : line->v1->y); - p2.x = FIXED_TO_FLOAT(lseg->side ? line->v1->x : line->v2->x); - p2.y = FIXED_TO_FLOAT(lseg->side ? line->v1->y : line->v2->y); + if (line->sidenum[1] != 0xffff) + { + if (sides[line->sidenum[0]].sector == sides[line->sidenum[1]].sector) + { + // Segs that are self-ref linedef do not cutout the subsector. +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "CutOutSubsecPoly: self ref line %i\n", line - lines); +#endif + continue; + } + } + + if (lseg->side) + { // side 1 + v1 = line->v2; + v2 = line->v1; + } + else + { // side 0 + v1 = line->v1; + v2 = line->v2; + } + + p1.x = FIXED_TO_FLOAT(v1->x); + p1.y = FIXED_TO_FLOAT(v1->y); + p2.x = FIXED_TO_FLOAT(v2->x); + p2.y = FIXED_TO_FLOAT(v2->y); cutseg.x = p1.x; cutseg.y = p1.y; cutseg.dx = p2.x - p1.x; cutseg.dy = p2.y - p1.y; - // see if it cuts the convex poly - ps = -1; - pe = -1; - for (i = 0; i < poly->numpts; i++) + // See if it cuts the convex poly + result = &A; // Setup to get crossing point A + + for (i1 = 0; i1 < poly_num_pts; i1++) { - j = i + 1; - if (j == poly->numpts) - j = 0; + i2 = i1 + 1; + if (i2 >= poly_num_pts) + i2 = 0; - pv = fracdivline(&cutseg, &poly->pts[i], &poly->pts[j]); + // i1, i2 are one side of the poly + dle = fracdivline(&cutseg, poly->ppts[i1], poly->ppts[i2], /*OUT*/ result); - if (pv == NULL) + if (dle == DVL_none) continue; - if (ps < 0) + // have dividing pt + if (result == &A) { - ps = i; - vs = *pv; - fracs = bspfrac; + // Split at A + // Dependent upon dle, setup in fracdivline. + A.before += i1; + A.after += i1; + result = &B; // Setup to get crossing point B + continue; } - else - { - //frac 1 on previous segment, - // 0 on the next, - //the split line goes through one of the convex poly - // vertices, happens quite often since the convex - // poly is already adjacent to the subsector segs - // on most borders - if (SameVertice(pv, &vs)) - continue; - if (fracs <= bspfrac) - { - nump = 2 + poly->numpts - (i-ps); - pe = ps; - ps = i; - ve = *pv; - } - else - { - nump = 2 + (i-ps); - pe = i; - ve = vs; - vs = *pv; - } - //found 2nd point - break; - } + // The partition line can cross at a vertex, between two segments, + // or the two points are so close, they can be considered as one. + // Crossing point B must be another vertex. + + // When ( dle == DVL_v1 || dle == DVL_v2 ) then test for + // the same vertex. It is NULL for DVL_mid. + // When dividing point is at a vertex, is found at next segment too. + if (B.vertex // ( dle == DVL_v1 || dle == DVL_v2 ) + && B.vertex == A.vertex) + continue; + + // Split at B + // Dependent upon dle, setup in fracdivline. + B.before += i1; + B.after += i1; // linear, no rollover + goto cut_poly; // got 2 points } - // there was a split - if (ps >= 0) + // need 2 points + if (result == &B) { - //need 2 points - if (pe >= 0) - { - // generate FRONT poly - temppoly = HWR_AllocPoly(nump); - pv = temppoly->pts; - *pv++ = vs; - *pv++ = ve; - do - { - if (++ps == poly->numpts) - ps = 0; - *pv++ = poly->pts[ps]; - } while (ps != pe); - HWR_FreePoly(poly); - poly = temppoly; - } //hmmm... maybe we should NOT accept this, but this happens // only when the cut is not needed it seems (when the cut // line is aligned to one of the borders of the poly, and // only some times..) - else - skipcut++; - // I_Error("CutOutPoly: only one point for split line (%d %d) %d", ps, pe, debugpos); + // [WDJ] This happens often (27 times in Doom2 map01). + +#ifdef DEBUG_HWBSP + skipcut_cnt++; + //CONS_Debug(DBG_RENDER, + //"DEBUG: CutOutPoly: only one point for split line (%d %d)\n",ps,pe); +#endif + // It must have crossed at one polygon vertex. + // That is the only way to touch at only one point. + // The seg is usually slightly misaligned with the polygon side, + // leaving a crack in front of a wall. + // Let the seg-chains take care of it. It is important to + // extend the poly to all seg walls to eliminate cracks. + if (A.vertex == NULL) + { + A.vertex = store_polyvertex(&A.divpt, 0.25f); + } + + // Store the other vertex + B.vertex = store_polyvertex(((A.divfrac > 0.5) ? &p1 : &p2), 0.25f); + + // Get another point in the poly to rv1 + i2 = A.after; + + if (i2 >= poly_num_pts) + i2 = 0; + + rv1 = poly->ppts[i2]; + save_loose_seg(lseg, A.vertex, B.vertex, false, true, + cross_product(A.vertex, B.vertex, rv1) < 0); + + continue; } + continue; // no split by this segment + +cut_poly: + // Cuts that miss the polygon. + if ((A.divfrac < 0.0) && (B.divfrac < 0.0)) + continue; + + if ((A.divfrac > 1.0) && (B.divfrac > 1.0)) + continue; + + // Skip cuts that duplicate an existing side. + // Does not matter if goes across poly or not. + cut_at_vert = A.at_vert && B.at_vert; // cuts are at existing vertexes + + if (cut_at_vert) + { + // Skip if both crossings are on same polygon segment. + if (A.after + 1 == B.after) + continue; + + if (A.after + poly_num_pts == B.after + 1) + continue; + } + +#ifdef CUTOUT_NON_CONVEX +#define FRAC_EP 0.01f + // Cuts that may make the polygon non-convex. + looseA = (A.divfrac < (0.0 - FRAC_EP)) || (A.divfrac > (1.0f + FRAC_EP)); + + if (looseA) + { + // Cannot make normal cut, A end is loose. + if (cv_glpolyshape.value == 1) + continue; // fat polygons + + // Get vertex at A end of seg, v1 or v2 + A.vertex = store_polyvertex(((A.divfrac < 0.0) ? &p1 : &p2), 0.25f); + } + + looseB = (B.divfrac < (0.0 - FRAC_EP)) || (B.divfrac > (1.0f + FRAC_EP)); + + if (looseB) + { + // Cannot make normal cut, B end is loose. + if (cv_glpolyshape.value == 1) + continue; // fat polygons + + // Get vertex at B end of seg, v1 or v2 + B.vertex = store_polyvertex(((B.divfrac < 0.0 ) ? &p1 : &p2), 0.25f); + + if (looseA) // from A end + { + // Both ends are loose. + // All that can be done is to save the seg + save_loose_seg(lseg, A.vertex, B.vertex, true, true, + (B.divfrac < A.divfrac )); + continue; + } + } +#else + // Reject cuts that could make the polygon non-convex. + if ((A.divfrac < 0.0) || (A.divfrac > 1.0)) + continue; + + if ((B.divfrac < 0.0) || (B.divfrac > 1.0)) + continue; +#endif + + // Store new crossing vertex. + if (A.vertex == NULL) + { + A.vertex = store_polyvertex(&A.divpt, 0.25f); + } + + if (B.vertex == NULL) + { + B.vertex = store_polyvertex(&B.divpt, 0.25f); + } + +#ifdef CUTOUT_NON_CONVEX + if (looseA || looseB) + { + // Cutseg does not completely cross the poly. + // Save the seg + save_loose_seg(lseg, A.vertex, B.vertex, looseA, looseB, + (B.divfrac < A.divfrac)); + + // Insert the crossing vertex into the poly, as a marker. + // Must occur after the crossing vertex A, B are stored. + if (looseA) + { + // A is loose, B is crossing point. + if (B.at_vert) + continue; // Point is at existing vertex + + rv1 = B.vertex; + ps = B.before + 1; // insert point + } + else + { + // B is loose, A is crossing point. + if (A.at_vert) + continue; // Point is at existing vertex + + rv1 = A.vertex; + ps = A.before + 1; // insert point + } + rv2 = NULL; + n = poly_num_pts; + } + else +#endif + { + // Cutseg cuts across poly, at two points. + // Save poly on rightside of cutting seg. + if (B.divfrac < A.divfrac) + { + // B, A, poly from A to B clockwise + // Number of points from A to B clockwise. + n = B.before - A.after + 1; + ps = A.after; + rv1 = B.vertex; + rv2 = A.vertex; + } + else + { + // A, B, poly from B to A clockwise + // Number of points from B to A clockwise. + n = A.before + poly_num_pts - B.after + 1; + ps = B.after; + rv1 = A.vertex; + rv2 = B.vertex; + } + + // If cut at existing vertexes, and it has the same number of pts, + // then the cut is already an existing side, + // and the polygon is not going to change. + if (cut_at_vert && ((n+2) == poly_num_pts)) + continue; + } + + if (ps >= poly_num_pts) + ps -= poly_num_pts; + + wpoly_insert_cut( rv1, rv2, ps, n, poly); + poly_num_pts = poly->numpts; } - return poly; + +#ifdef CUTOUT_NON_CONVEX + // After all normal seg cuts, deal with the loose end segs. + if (seg_chain) + { + // Have some loose end segs. + condense_seg_chains(); + check_convex = apply_seg_chains( poly); + clear_seg_chains(); + } + + if (check_convex + && (cv_glpolyshape.value == 2) // Trim polygons + && (poly_num_pts > 3)) + { + // Need to check convex. + // Force convex solution, or split into convex. + enforce_convex(poly); + } +#endif } + // At this point, the poly should be convex and the exact -// layout of the subsector, it is not always the case, +// layout of the subsector. It is not always the case, // so continue to cut off the poly into smaller parts with // each seg of the subsector. // -static inline void HWR_SubsecPoly(INT32 num, poly_t *poly) +// ssindex : subsec index, 0..(numsubsectors-1) +// poly : surrounding convex polygon, non-destructive +// Called from HWR_WalkBSPNode +static void HWR_SubsecPoly(int ssindex, wpoly_t* poly) { - INT16 count; - subsector_t *sub; - seg_t *lseg; - - sub = &subsectors[num]; - count = sub->numlines; - lseg = &segs[sub->firstline]; - - if (poly) +#ifdef DEBUG_HWBSP +#ifdef DEBUG_TRACE { - poly = CutOutSubsecPoly (lseg,count,poly); - totalsubsecpolys++; - //extra data for this subsector - extrasubsectors[num].planepoly = poly; + subsector_t* sub = &subsectors[ssindex]; + + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "HWR_SubsecPoly: sector %i, subsector %i, poly->numpts= %i\n", sub->sector - sectors, ssindex, poly->numpts); + } } +#else + if (poly->numpts <= 0) + { + subsector_t* sub = &subsectors[ssindex]; + CONS_Debug(DBG_RENDER, "HWR_SubsecPoly: sector %i, subsector %i, poly->numpts= %i\n", sub->sector - sectors, ssindex, poly->numpts); + } +#endif +#endif + if (poly->numpts <= 0) + return; + + if (cv_glpolyshape.value > 0) + { + // Trim the subsector with the segs. + CutOutSubsecPoly(ssindex, /*INOUT*/ poly); + } + +#ifdef DEBUG_TRACE + if (trigger_trace) + { + wpoly_dump("Subsec poly", poly); + } +#endif +#ifdef DEBUG_HWBSP + total_subsecpoly_cnt++; +#endif } -// the bsp divline have not enouth presition -// search for the segs source of this divline -static inline void SearchDivline(node_t *bsp, fdivline_t *divline) +// The bsp divline does not have enough precision. +// Search for the segs source of this divline. +static inline void set_divline(node_t* bsp, fdivline_t *divline) { - divline->x = FIXED_TO_FLOAT(bsp->x); - divline->y = FIXED_TO_FLOAT(bsp->y); + divline->x = FIXED_TO_FLOAT(bsp->x); + divline->y = FIXED_TO_FLOAT(bsp->y); divline->dx = FIXED_TO_FLOAT(bsp->dx); divline->dy = FIXED_TO_FLOAT(bsp->dy); } @@ -605,422 +2006,921 @@ static void loading_status(void) #endif // poly : the convex polygon that encloses all child subsectors -static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *bbox) +// Recursive +// bspnum : children[] +// leafnode : & children [] +// Call HWR_WalkBSPNode_degen for degenerate case. +// Called from HWR_CreatePlanePolygons at load time. +static void HWR_WalkBSPNode(INT32 bspnum, wpoly_t* poly, UINT16 *leafnode, fixed_t *bbox) { - node_t *bsp; - poly_t *backpoly, *frontpoly; + node_t* bsp; + wpoly_t backpoly; + wpoly_t frontpoly; fdivline_t fdivline; polyvertex_t *pt; + size_t subsecnum; // subsector index INT32 i; + (void)leafnode; // unused + +#ifdef DEBUG_TRACE + if (trigger_bsp_sector == 0xFFFFFFF2 ) + { + trigger_trace = 2; // trace all + } +#endif + // Found a subsector? if (bspnum & NF_SUBSECTOR) { - if (bspnum == -1) + // Subsector leaf: bspnum is a subsector number. + subsecnum = bspnum & ~NF_SUBSECTOR; + if (subsecnum >= numsubsectors) + goto bad_subsector; + +#ifdef DEBUG_TRACE + if (trigger_bsp_sector < numsectors + && subsectors[subsecnum].sector == & sectors[trigger_bsp_sector] ) { - // BP: i think this code is useless and wrong because - // - bspnum==-1 happens only when numsubsectors == 0 - // - it can't happens in bsp recursive call since bspnum is a INT32 and children is UINT16 - // - the BSP is complet !! (there just can have subsector without segs) (i am not sure of this point) - - // do we have a valid polygon ? - if (poly && poly->numpts > 2) - { - CONS_Debug(DBG_RENDER, "Adding a new subsector\n"); - if (addsubsector == numsubsectors + NEWSUBSECTORS) - I_Error("WalkBSPNode: not enough addsubsectors\n"); - else if (addsubsector > 0x7fff) - I_Error("WalkBSPNode: addsubsector > 0x7fff\n"); - *leafnode = (UINT16)((UINT16)addsubsector | NF_SUBSECTOR); - extrasubsectors[addsubsector].planepoly = poly; - addsubsector++; - } - - //add subsectors without segs here? - //HWR_SubsecPoly(0, NULL); + CONS_Debug(DBG_RENDER, "BSP TRIGGER SECTOR %i: \n", trigger_bsp_sector); + trigger_trace = 1; } - else - { - HWR_SubsecPoly(bspnum & ~NF_SUBSECTOR, poly); - - //Hurdler: implement a loading status -#ifdef HWR_LOADING_SCREEN - if (ls_count-- <= 0) - { - ls_count = numsubsectors/50; - loading_status(); - } #endif - } + HWR_SubsecPoly(subsecnum, poly); + + // Add the poly points into the bounding box. M_ClearBox(bbox); - poly = extrasubsectors[bspnum & ~NF_SUBSECTOR].planepoly; - - for (i = 0, pt = poly->pts; i < poly->numpts; i++,pt++) - M_AddToBox(bbox, FLOAT_TO_FIXED(pt->x), FLOAT_TO_FIXED(pt->y)); - + for (i = 0; i < poly->numpts; i++) + { + pt = poly->ppts[i]; + M_AddToBox(bbox, (fixed_t)(pt->x * FRACUNIT), (fixed_t)(pt->y * FRACUNIT)); + } +#ifdef POLYTILE + polytile_remove(poly); +#endif + wpoly_move(poly, /*OUT*/ & wpoly_subsectors[subsecnum]); + // poly is empty +#ifdef POLYTILE + polytile_enter(&wpoly_subsectors[subsecnum]); +#endif +#ifdef DEBUG_TRACE + if (trigger_trace == 1) // only turn off specifc subsector traces + trigger_trace = 0; +#endif +#ifdef HWR_LOADING_SCREEN + //Hurdler: implement a loading status + if (ls_count-- <= 0) + { + ls_count = numsubsectors/50; + loading_status(); + } +#endif return; } - bsp = &nodes[bspnum]; - SearchDivline(bsp, &fdivline); - SplitPoly(&fdivline, poly, &frontpoly, &backpoly); - poly = NULL; + // Node reference: bspnum is another node of the tree. + if (bspnum >= (INT32)numnodes) + goto bad_node; + bsp = &nodes[bspnum]; + set_divline(bsp, /*OUT*/ &fdivline); + wpoly_init_0(&frontpoly); + wpoly_init_0(&backpoly); +#ifdef POLYTILE + polytile_remove(poly); +#endif + SplitPoly (&fdivline, poly, &frontpoly, &backpoly); +#ifdef POLYTILE + polytile_enter(&frontpoly); + polytile_enter(&backpoly); +#endif + +#ifdef DEBUG_HWBSP //debug - if (!backpoly) - nobackpoly++; + if (backpoly.numpts == 0) + nobackpoly_cnt++; +#endif // Recursively divide front space. - if (frontpoly) + if (frontpoly.numpts) { - WalkBSPNode(bsp->children[0], frontpoly, &bsp->children[0],bsp->bbox[0]); +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "BSP-FRONT %i:\n", frontpoly.id1); + } +#endif + HWR_WalkBSPNode(bsp->children[0], &frontpoly, &bsp->children[0], bsp->bbox[0]); // copy child bbox - memcpy(bbox, bsp->bbox[0], 4*sizeof (fixed_t)); + memcpy(bbox, bsp->bbox[0], 4*sizeof(fixed_t)); } else - I_Error("WalkBSPNode: no front poly?"); + { +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "BSP-FRONT %i EMPTY:\n", backpoly.id1); + } +#endif + // [WDJ] Having no front poly is as likely as no back poly, since + // logic in Split Poly was changed to check poly direction. + // I_Error ("HWR_WalkBSPNode: no front poly, bspnum= %d\n", bspnum); // I_SoftError + } // Recursively divide back space. - if (backpoly) + if (backpoly.numpts) { +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "BSP-BACK %i:\n", backpoly.id1); + } +#endif // Correct back bbox to include floor/ceiling convex polygon - WalkBSPNode(bsp->children[1], backpoly, &bsp->children[1], bsp->bbox[1]); + HWR_WalkBSPNode(bsp->children[1], &backpoly, &bsp->children[1], bsp->bbox[1]); // enlarge bbox with second child M_AddToBox(bbox, bsp->bbox[1][BOXLEFT ], - bsp->bbox[1][BOXTOP ]); + bsp->bbox[1][BOXTOP ]); M_AddToBox(bbox, bsp->bbox[1][BOXRIGHT ], - bsp->bbox[1][BOXBOTTOM]); + bsp->bbox[1][BOXBOTTOM]); } + else + { +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "BSP-BACK %i EMPTY:\n", backpoly.id1); + } +#endif + } + + wpoly_free(&backpoly); + wpoly_free(&frontpoly); + return; + +bad_subsector: + I_Error("HWR_WalkBSPNode : bad secnum %lu, numsectors=%lu -1\n", + subsecnum, numsubsectors); + +bad_node: + // frontpoly and backpoly are empty, and were not init. + I_Error("HWR_WalkBSPNode : bad node num %i, numnodes=%lu -1\n", + bspnum, numnodes); } -// FIXME: use Z_Malloc() STATIC ? -void HWR_FreeExtraSubsectors(void) +static void HWR_Freepolysubsectors(void) { - if (extrasubsectors) - free(extrasubsectors); - extrasubsectors = NULL; + Z_Free(poly_subsectors); + poly_subsectors = NULL; } -#define MAXDIST 1.5f -// BP: can't move vertex: DON'T change polygon geometry! (convex) +#define MAXDIST (1.5f) +// BP: can't move vertex : DON'T change polygon geometry ! (convex) //#define MOVEVERTEX -static boolean PointInSeg(polyvertex_t *a,polyvertex_t *v1,polyvertex_t *v2) +// Is vertex va within the seg v1, v2 +static boolean PointInSeg(polyvertex_t* va, polyvertex_t* v1, polyvertex_t* v2) { - register float ax,ay,bx,by,cx,cy,d,norm; - register polyvertex_t *p; + register float ax, ay, bx, by, cx, cy, d, norm; - // check bbox of the seg first - if (v1->x > v2->x) + // check bbox of the seg first (without altering v1, v2) + if (v2->x > v1->x) { - p = v1; - v1 = v2; - v2 = p; + // check if x within seg box v1..v2 + if ((va->x + MAXDIST) < v1->x) goto not_in; + if ((va->x - MAXDIST) > v2->x) goto not_in; + } + else + { + // check if x within seg box v2..v1 + if ((va->x + MAXDIST) < v2->x) goto not_in; + if ((va->x - MAXDIST) > v1->x) goto not_in; } - if (a->x < v1->x-MAXDIST || a->x > v2->x+MAXDIST) - return false; - - if (v1->y > v2->y) + if (v2->y > v1->y) { - p = v1; - v1 = v2; - v2 = p; + // check if x within seg box v1..v2 + if ((va->y + MAXDIST) < v1->y) goto not_in; + if ((va->y - MAXDIST) > v2->y) goto not_in; + } + else + { + // check if x within seg box v2..v1 + if ((va->y + MAXDIST) < v2->y) goto not_in; + if ((va->y - MAXDIST) > v1->y) goto not_in; } - if (a->y < v1->y-MAXDIST || a->y > v2->y+MAXDIST) - return false; - // v1 = origine - ax= v2->x-v1->x; - ay= v2->y-v1->y; - norm = (float)hypot(ax, ay); - ax /= norm; - ay /= norm; - bx = a->x-v1->x; - by = a->y-v1->y; - //d = a.b - d =ax*bx+ay*by; + // v1 = origin + ax = v2->x - v1->x; + ay = v2->y - v1->y; + norm = hypotf(ax, ay); // length of seg +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" + if (norm != 0) // yes, this can be exactly 0 +#pragma GCC diagnostic pop + { + ax /= norm; + ay /= norm; // unit vector along seg, v1->v2 + } + bx = va->x - v1->x; + by = va->y - v1->y; // vector v1->va + + // d = (a DOT b), (product of lengths * cosine( angle )) + d = ax*bx+ay*by; // bound of the seg if (d < 0 || d > norm) + { + // Also excludes some va within MAXDIST of v1 or v2 + goto not_in; + } + + // Cross product. + if (((by * ax) - (bx * ay)) <= 0) + { + // The vertex is to the rightside of the seg, so adding + // it to the polygon would worsen the crack. return false; - //c = d.1a-b + } + + // measure the error in vector bx,by as difference squared sum + //c = (d * unit_vector_seg) - b cx = ax*d-bx; cy = ay*d-by; #ifdef MOVEVERTEX - if (cx*cx+cy*cy <= MAXDIST*MAXDIST) + if (cx*cx+cy*cy<=MAXDIST*MAXDIST) { - // ajust a little the point position - a->x = ax*d+v1->x; - a->y = ay*d+v1->y; - // anyway the correction is not enouth + // adjust a little the point position + a->x=ax*d+v1->x; + a->y=ay*d+v1->y; + // anyway the correction is not enough return true; } return false; #else return cx*cx+cy*cy <= MAXDIST*MAXDIST; #endif +not_in: + return false; } -static INT32 numsplitpoly; +// [WDJ] The poly forming code has attempted to improve the polygons. +// Snapping of divide points to existing vertexes is one, and it may +// contribute to cracks in the floor tiling. Snapping to an existing +// vertex may pull the edge of a polygon away from the adjoining polygon. +// Inaccuracies in the division lines may also contribute to this. +// This code attempts to find such cracks and fix the polygons to cover them. -static void SearchSegInBSP(INT32 bspnum,polyvertex_t *p,poly_t *poly) +static int num_T_vertex_fixed; + +// A structure to pass in BSP recursion, reducing it to one parameter. +typedef struct { + fixed_t max_x, min_x, max_y, min_y; + wpoly_t *poly; // our poly + polyvertex_t *pt; // T-split vertex + polyvertex_t *before, *after; // shared vertex before and after it + //int our_secnum, find_secnum; // sectors + int pt_index; +} split_T_t; + +// Dist 0.4999 cures HOM in Freedoom map09 +#define SEARCHSEG_VERTEX_DIST 0.4999f + +// Recursive descent in BSP. +static void SearchSegInBSP(INT32 bspnum, split_T_t * stp) { - poly_t *q; - INT32 j,k; + wpoly_t *wq = stp->poly; + polyvertex_t *pt = stp->pt; + size_t subsecnum; + INT32 numpts, i1, i2; - if (bspnum & NF_SUBSECTOR) + const fixed_t maxy = stp->max_y; + const fixed_t maxx = stp->max_x; + const fixed_t miny = stp->min_y; + const fixed_t minx = stp->min_x; + + for (;;) { - if (bspnum != -1) - { - bspnum &= ~NF_SUBSECTOR; - q = extrasubsectors[bspnum].planepoly; - if (poly == q || !q) - return; - for (j = 0; j < q->numpts; j++) - { - k = j+1; - if (k == q->numpts) k = 0; - if (!SameVertice(p, &q->pts[j]) - && !SameVertice(p, &q->pts[k]) - && PointInSeg(p, &q->pts[j], - &q->pts[k])) - { - poly_t *newpoly = HWR_AllocPoly(q->numpts+1); - INT32 n; + if (bspnum & NF_SUBSECTOR) + goto got_subsector; - for (n = 0; n <= j; n++) - newpoly->pts[n] = q->pts[n]; - newpoly->pts[k] = *p; - for (n = k+1; n < newpoly->numpts; n++) - newpoly->pts[n] = q->pts[n-1]; - numsplitpoly++; - extrasubsectors[bspnum].planepoly = - newpoly; - HWR_FreePoly(q); - return; - } - } - } - return; + const node_t *node = &nodes[bspnum]; + const fixed_t *bbox0 = node->bbox[0]; + const fixed_t *bbox1 = node->bbox[1]; + + // Not a subsector, visit left and right children. + if ( (bbox0[BOXBOTTOM] <= maxy) + && (bbox0[BOXTOP ] >= miny) + && (bbox0[BOXLEFT ] <= maxx) + && (bbox0[BOXRIGHT ] >= minx) + ) + SearchSegInBSP(node->children[0], stp); + + if (! ((bbox1[BOXBOTTOM] <= maxy) + && (bbox1[BOXTOP ] >= miny) + && (bbox1[BOXLEFT ] <= maxx) + && (bbox1[BOXRIGHT ] >= minx) + ) ) + break; + + // Tail recursion within loop. + bspnum = node->children[1]; } - if ((FIXED_TO_FLOAT(nodes[bspnum].bbox[0][BOXBOTTOM])-MAXDIST <= p->y) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[0][BOXTOP ])+MAXDIST >= p->y) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[0][BOXLEFT ])-MAXDIST <= p->x) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[0][BOXRIGHT ])+MAXDIST >= p->x)) - SearchSegInBSP(nodes[bspnum].children[0],p,poly); + return; - if ((FIXED_TO_FLOAT(nodes[bspnum].bbox[1][BOXBOTTOM])-MAXDIST <= p->y) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[1][BOXTOP ])+MAXDIST >= p->y) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[1][BOXLEFT ])-MAXDIST <= p->x) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[1][BOXRIGHT ])+MAXDIST >= p->x)) - SearchSegInBSP(nodes[bspnum].children[1],p,poly); +got_subsector: + subsecnum = bspnum & ~NF_SUBSECTOR; + if (subsecnum >= numsubsectors) + return; + + // For every subsector polygon different than poly + wq = &wpoly_subsectors[subsecnum]; + if (wq == stp->poly || !wq) + return; + + numpts = wq->numpts; + if (numpts == 0) + return; + + // For all the vertex. + for (i1 = 0; i1 < numpts; i1++) + { + if (wq->ppts[i1] == pt) + continue; + + i2 = i1+1; + + if (i2 == numpts) + i2 = 0; + + if (wq->ppts[i2] == pt) + continue; + + if (PointInSeg(pt, wq->ppts[i1], wq->ppts[i2])) + { + goto add_pt; + } + } + // May have to search more than one polygon to find correct neighbor polygon. + return; + +add_pt: + // Insert the vertex pt into the polygon of the bsp subsector. + wpoly_insert_vert(pt, i2, /*INOUT*/ wq); + num_T_vertex_fixed++; + //stp->max_y = - 0x7ffffff0; // exit all the way + return; } // search for T-intersection problem -// BP : It can be mush more faster doing this at the same time of the splitpoly -// but we must use a different structure : polygone pointing on segs -// segs pointing on polygone and on vertex (too mush complicated, well not -// realy but i am soo lasy), the methode discibed is also better for segs presition -static INT32 SolveTProblem(void) -{ - poly_t *p; - INT32 i; - size_t l; +// BP : It can be much more faster doing this at the same time of the splitpoly +// but we must use a different structure : polygon pointing on segs +// segs pointing on polygon and on vertex (too much complicated, well not +// really but I am soo lazy), the method described is also better for segs precision - if (cv_glsolvetjoin.value == 0) - return 0; +static void SolveTProblem(void) +{ + split_T_t splitt; // parameter to search + wpoly_t *wp; + INT32 numpts, i, j; + size_t ssnum; + + if (!cv_glsolvetjoin.value) + return; CONS_Debug(DBG_RENDER, "Solving T-joins. This may take a while. Please wait...\n"); -#ifdef HWR_LOADING_SCREEN - CON_Drawer(); //let the user know what we are doing - I_FinishUpdate(); // page flip or blit buffer -#endif - numsplitpoly = 0; + num_T_vertex_fixed = 0; - for (l = 0; l < addsubsector; l++) + // For every subsector + for (ssnum = 0; ssnum < num_poly_subsector; ssnum++) { - p = extrasubsectors[l].planepoly; - if (p) - for (i = 0; i < p->numpts; i++) - SearchSegInBSP((INT32)numnodes-1, &p->pts[i], p); + wp = &wpoly_subsectors[ssnum]; + if (wp->numpts == 0) + continue; + + splitt.poly = wp; + numpts = wp->numpts; + + // For all vertex in the subsector + for (i = 0; i < numpts; i++) + { +#ifdef DEBUG_HWBSP + if (wp->ppts == NULL) + { + CONS_Debug(DBG_RENDER, "SolveT: NULL vertex, subsector= %d\n", ssnum); + } +#endif + // No need to process polyvertex from the level map. + if (in_poly_vert(wp->ppts[i])) + continue; + + // This is a vertex added by a split. + splitt.pt_index = i; + splitt.pt = wp->ppts[i]; + splitt.max_x = (fixed_t)((wp->ppts[i]->x + MAXDIST) * 0x10000); + splitt.min_x = (fixed_t)((wp->ppts[i]->x - MAXDIST) * 0x10000); + splitt.max_y = (fixed_t)((wp->ppts[i]->y + MAXDIST) * 0x10000); + splitt.min_y = (fixed_t)((wp->ppts[i]->y - MAXDIST) * 0x10000); + + j = i - 1; + + if (j < 0) + j += numpts; + + splitt.before = wp->ppts[j]; + + j = i + 1; + + if (j >= numpts) + j -= numpts; + + splitt.after = wp->ppts[j]; + + // Check added polyvertex due to SplitPoly. + // lug: handle "degenerate" case so bspnum is never -1 here either + SearchSegInBSP(((numnodes > 0) ? numnodes-1 : (0 | NF_SUBSECTOR)), &splitt); + } } - //CONS_Debug(DBG_RENDER, "numsplitpoly %d\n", numsplitpoly); - return numsplitpoly; +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "DEBUG: SolveT: div polygon line= %d\n", num_T_vertex_fixed); +#endif } -#define NEARDIST (0.75f) -#define MYMAX (10000000000000.0f) +// lug: dont modify our actual sectors, that´ll break maps and causes desynchs! +//#define CHANGESUBSEC -/* Adjust true segs (from the segs lump) to be exactely the same as - * plane polygone segs - * This also convert fixed_t point of segs in float (in moste case - * it share the same vertice - */ +#ifdef CHANGESUBSEC +// [WDJ] Have a subsector poly with a suspect sector. +// Return the correct sector for the subsector poly. +static sector_t *find_poly_sector(wpoly_t *ssp) +{ + // Examine every linedef for the closest along the x or y axis. + // Use this linedef to assign the sector to the poly subsector. + + // There should be no two-sided linedefs actually within the subsector. + // If there were any one-sided linedefs within the subsector, they would + // have been segs, and would have decided the issue already. + polyvertex_t ap; // avg of subsector points + line_t *best_lp = NULL; + fixed_t best_dd = 0x7fffffff; + fixed_t px, py; + INT32 j; + size_t k; + + // Find an average point, not on a line, within the sector. + // If it is on the poly boundary, it can confuse the linedef exclusion tests. + ap.x = ap.y = 0.0; + for (j = 0; j < ssp->numpts; j++) + { + ap.x += ssp->ppts[j]->x; + ap.y += ssp->ppts[j]->y; + } + // Average of poly points. + ap.x /= ssp->numpts; + ap.y /= ssp->numpts; + px = (int)(ap.x * 0x10000); + py = (int)(ap.y * 0x10000); + +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "Find Poly Sector: (%6.2f,%6.2f)\n", ap.x, ap.y); +#endif + + // Find closest linedef to the test point, along x and y axis. + // Testing only one axis would work. + // But a linedef may be much closer along the other axis, so we test along both X and Y. + for (k = 0; k < numlines; k++) + { + line_t *lp = &lines[k]; + + if (lp->frontsector == lp->backsector) + continue; // self-ref lines lie. + + // Only consider linedef that bracket the test point. + fixed_t dx1 = px - lp->v1->x; + fixed_t dy1 = py - lp->v1->y; + fixed_t dx2 = px - lp->v2->x; + fixed_t dy2 = py - lp->v2->y; + + // (dx1 XOR dx2) < 0 means that one was < 0 and the other was > 0, + // which means it bracketed the point px,py. + + // Eqn of line: x = x1 + a * dx, y = y1 + a * dy + // At px: a = (px - x1) / dx + // dd = abs( (py - y1) - ( (px - x1) * dy / dx)) + if (((dx1 ^ dx2) < 0) && (lp->dx != 0)) // bracket px, and line not vert. + { + // Distance to line, measured along x-axis. + // This calc has a tendency to overflow, so use INT64. + INT64 dy3 = ((INT64)dx1) * lp->dy / lp->dx; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "FPS X: line= %i (%6.2f,%6.2f) to (%6.2f,%6.2f) dx,dy=(%6.2f,%6.2f) \n", + k, FIXED_TO_FLOAT(lp->v1->x), FIXED_TO_FLOAT(lp->v1->y), FIXED_TO_FLOAT(lp->v2->x), FIXED_TO_FLOAT(lp->v2->y), + FIXED_TO_FLOAT(lp->dx), FIXED_TO_FLOAT(lp->dy)); +#endif + if ((dy3 > INT32_MIN) && (dy3 < INT32_MAX)) // within fixed_t range + { + fixed_t dd = abs(dy1 - (fixed_t)dy3); +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "X dd=%6.2f\n", FIXED_TO_FLOAT(dd)); +#endif + if (dd < best_dd ) + { + best_dd = dd; + best_lp = lp; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "BEST=%i X dist=%i\n", k, dd>>16); +#endif + } + } + } + + if (((dy1 ^ dy2) < 0) && (lp->dy != 0)) // bracket py, and line not horz. + { + // Distance to line, measured along y-axis. + // This calc has a tendency to overflow, so use INT64. + INT64 dx3 = ((INT64)dy1) * lp->dx / lp->dy; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "FPS Y: line= %i (%6.2f,%6.2f) to (%6.2f,%6.2f) dx,dy=(%6.2f,%6.2f) \n", + k, FIXED_TO_FLOAT(lp->v1->x), FIXED_TO_FLOAT(lp->v1->y), FIXED_TO_FLOAT(lp->v2->x), FIXED_TO_FLOAT(lp->v2->y), + FIXED_TO_FLOAT(lp->dx), FIXED_TO_FLOAT(lp->dy)); +#endif + if ((dx3 > INT32_MIN) && (dx3 < INT32_MAX)) // within fixed_t range + { + fixed_t dd = abs(dx1 - (fixed_t)dx3); +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "Y dd=%6.2f\n", FIXED_TO_FLOAT(dd)); +#endif + if (dd < best_dd ) + { + best_dd = dd; + best_lp = lp; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "BEST=%i Y dist=%i\n", k, dd>>16); +#endif + } + } + } + } + + if (best_lp == NULL) + return NULL; + + // cross product with best_lp, to detect ap on rightside + double crpd = ((((double)(ap.x)) - FIXED_TO_FLOAT(best_lp->v1->x)) * FIXED_TO_FLOAT(best_lp->dy)) + - ((((double)(ap.y)) - FIXED_TO_FLOAT(best_lp->v1->y)) * FIXED_TO_FLOAT(best_lp->dx)); + + sector_t *fnd_sector = (crpd >= 0) + ? best_lp->frontsector // rightside of linedef + : best_lp->backsector; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "crpd=%6.2f sector=%i\n", crpd, fnd_sector - sectors); +#endif + return fnd_sector; +} +#endif + +#define VERTEX_NEAR_DIST (0.75f) +// Only needs to be reasonably larger than VERTEX_NEAR_DIST. +#define INITIAL_MAX (10000000000000.0f) +#define SEG_SAME_VERT (0.5f) + +// Adds polyvertex_t references to the segs. +// [WDJ] 2013/12 Removed writes of polyvertex_t* to vertex_t*, it now has its +// own ptrs. Fewer reverse conversions are needed. static void AdjustSegs(void) { - size_t i, count; +#ifdef DEBUG_HWBSP + int missed_seg_cnt = 0; + int fixed_segsec_cnt = 0; + int lost_segsec_cnt = 0; +#endif + INT16 segcount; + size_t ssnum; INT32 j; - seg_t *lseg; - poly_t *p; +#ifdef CHANGESUBSEC + sector_t *ss_sector, *poly_sector, *lseg_sector; +#endif + seg_t* lseg; + wpoly_t *wp; INT32 v1found = 0, v2found = 0; float nearv1, nearv2; - for (i = 0; i < numsubsectors; i++) + // for all segs in all sectors + for (ssnum = 0; ssnum < numsubsectors; ssnum++) { - count = subsectors[i].numlines; - lseg = &segs[subsectors[i].firstline]; - p = extrasubsectors[i].planepoly; - //if (!p) - //continue; - for (; count--; lseg++) + wp = &wpoly_subsectors[ssnum]; + if (wp->numpts == 0) + continue; + + segcount = subsectors[ssnum].numlines; +#ifdef CHANGESUBSEC + ss_sector = subsectors[ssnum].sector; +#endif + lseg = &segs[subsectors[ssnum].firstline]; +#ifdef CHANGESUBSEC + poly_sector = NULL; +#endif + for (; segcount--; lseg++) { - float distv1,distv2,tmp; - nearv1 = nearv2 = MYMAX; + polyvertex_t sv1, sv2; // seg v1, v2 + float distv1, distv2, tmp; // Don't touch polyobject segs. We'll compensate // for this when we go about drawing them. if (lseg->polyseg) continue; - if (p) { - for (j = 0; j < p->numpts; j++) + const line_t *line = lseg->linedef; + + if (!line) + continue; + + if (line->sidenum[1] != 0xffff) + { + if (sides[line->sidenum[0]].sector == sides[line->sidenum[1]].sector) { - distv1 = p->pts[j].x - FIXED_TO_FLOAT(lseg->v1->x); - tmp = p->pts[j].y - FIXED_TO_FLOAT(lseg->v1->y); - distv1 = distv1*distv1+tmp*tmp; - if (distv1 <= nearv1) - { - v1found = j; - nearv1 = distv1; - } - // the same with v2 - distv2 = p->pts[j].x - FIXED_TO_FLOAT(lseg->v2->x); - tmp = p->pts[j].y - FIXED_TO_FLOAT(lseg->v2->y); - distv2 = distv2*distv2+tmp*tmp; - if (distv2 <= nearv2) - { - v2found = j; - nearv2 = distv2; - } + // Segs that are self-ref linedef do not cutout the subsector. +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "AdjustSegs: self ref line %i\n", line - lines); +#endif + continue; } } - if (p && nearv1 <= NEARDIST*NEARDIST) - // share vertice with segs - lseg->pv1 = &(p->pts[v1found]); + +#ifdef CHANGESUBSEC + if (line->sidenum[lseg->side] != 0xffff) + { + // Get the sector from the seg. + lseg_sector = sides[line->sidenum[lseg->side]].sector; +#ifdef DEBUG_HWBSP + int secnum = lseg_sector - sectors; + if (lseg_sector != ss_sector) + CONS_Debug(DBG_RENDER, "AdjustSegs: seg line sector = %i, subsector sector = %i\n", + secnum, ss_sector - sectors); + if (poly_sector && (lseg_sector != poly_sector)) + CONS_Debug(DBG_RENDER, "AdjustSegs: seg line sector = %i, and %i\n", + secnum, poly_sector - sectors); +#endif + poly_sector = lseg_sector; + } +#endif + + sv1.x = FIXED_TO_FLOAT(lseg->v1->x); + sv1.y = FIXED_TO_FLOAT(lseg->v1->y); + sv2.x = FIXED_TO_FLOAT(lseg->v2->x); + sv2.y = FIXED_TO_FLOAT(lseg->v2->y); + + nearv1 = nearv2 = INITIAL_MAX; + // find nearest existing poly pts to seg v1, v2 + for (j = 0; j < wp->numpts; j++) + { + distv1 = wp->ppts[j]->x - sv1.x; + tmp = wp->ppts[j]->y - sv1.y; + distv1 = distv1*distv1 + tmp*tmp; + + if (distv1 <= nearv1) + { + v1found = j; + nearv1 = distv1; + } + + // the same with v2 + distv2 = wp->ppts[j]->x - sv2.x; + tmp = wp->ppts[j]->y - sv2.y; + distv2 = distv2*distv2 + tmp*tmp; + + if (distv2 <= nearv2) + { + v2found = j; + nearv2 = distv2; + } + } + + // close enough to be considered the same ? + if (nearv1 <= VERTEX_NEAR_DIST*VERTEX_NEAR_DIST) + { + // share vertex with segs + lseg->pv1 = wp->ppts[v1found]; + } else { // BP: here we can do better, using PointInSeg and compute - // the right point position also split a polygone side to - // solve a T-intersection, but too mush work + // the right point position also split a polygon side to + // solve a T-intersection, but too much work - // convert fixed vertex to float vertex - polyvertex_t *pv = HWR_AllocVertex(); - pv->x = FIXED_TO_FLOAT(lseg->v1->x); - pv->y = FIXED_TO_FLOAT(lseg->v1->y); - lseg->pv1 = pv; + lseg->pv1 = store_polyvertex(&sv1, SEG_SAME_VERT); + } + if (nearv2 <= VERTEX_NEAR_DIST*VERTEX_NEAR_DIST) + { + lseg->pv2 = wp->ppts[v2found]; } - if (p && nearv2 <= NEARDIST*NEARDIST) - lseg->pv2 = &(p->pts[v2found]); else { - polyvertex_t *pv = HWR_AllocVertex(); - pv->x = FIXED_TO_FLOAT(lseg->v2->x); - pv->y = FIXED_TO_FLOAT(lseg->v2->y); - lseg->pv2 = pv; + lseg->pv2 = store_polyvertex(&sv2, SEG_SAME_VERT); } // recompute length { - float x,y; - x = ((polyvertex_t *)lseg->pv2)->x - ((polyvertex_t *)lseg->pv1)->x - + FIXED_TO_FLOAT(FRACUNIT/2); - y = ((polyvertex_t *)lseg->pv2)->y - ((polyvertex_t *)lseg->pv1)->y - + FIXED_TO_FLOAT(FRACUNIT/2); - lseg->flength = (float)hypot(x, y); + const polyvertex_t *pv1 = (polyvertex_t *)lseg->pv1; + const polyvertex_t *pv2 = (polyvertex_t *)lseg->pv2; + + // [WDJ] FIXED_TO_FLOAT_MULT used to add 1/2 of lsb of fixed_t fraction. + float x = pv2->x - pv1->x + (0.5*FIXED_TO_FLOAT_MULT); + float y = pv2->y - pv1->y + (0.5*FIXED_TO_FLOAT_MULT); + lseg->flength = hypotf(x, y); + // BP: debug see this kind of segs - //if (nearv2 > NEARDIST*NEARDIST || nearv1 > NEARDIST*NEARDIST) - // lseg->length = 1; + //if (nearv2>VERTEX_NEAR_DIST*VERTEX_NEAR_DIST || nearv1>VERTEX_NEAR_DIST*VERTEX_NEAR_DIST) + //lseg->flength=1; } } + +#ifdef CHANGESUBSEC + // Fix bad subsector sector references. + if (poly_sector == NULL) + { + poly_sector = find_poly_sector(wp); +#ifdef DEBUG_HWBSP + lost_segsec_cnt++; +#endif + } + + if (poly_sector && (poly_sector != ss_sector)) + { + subsectors[ssnum].sector = poly_sector; +#ifdef DEBUG_HWBSP + fixed_segsec_cnt++; +#endif + } +#endif + } + + // check for missed segs, not in any polygon + for (j = 0; (size_t)j < numsegs; j++) + { + lseg = &segs[j]; + + // Don't touch polyobject segs. We'll compensate + // for this when we go about drawing them. + if (lseg->polyseg) + continue; + + if (!(lseg->pv1 && lseg->pv2)) + { + CONS_Debug(DBG_RENDER, "Seg %i, not in any polygon.\n", j); + } +#ifdef DEBUG_HWBSP + if ((!lseg->pv1) || (!lseg->pv2)) + missed_seg_cnt++; +#endif + if (!lseg->pv1) + { + lseg->pv1 = store_vertex(lseg->v1, SEG_SAME_VERT); + } + + if (!lseg->pv2) + { + lseg->pv2 = store_vertex(lseg->v2, SEG_SAME_VERT); + } + } +#ifdef DEBUG_HWBSP + + CONS_Debug(DBG_RENDER, "DEBUG: Lost seg sector cnt = %i\n", lost_segsec_cnt); + CONS_Debug(DBG_RENDER, "DEBUG: Fixed seg sector cnt = %i\n", fixed_segsec_cnt); + CONS_Debug(DBG_RENDER, "DEBUG: Missed seg vertex cnt = %i\n", missed_seg_cnt); +#endif +} + +// Generate drawing polygons from wpoly_t versions. +static void finalize_polygons(void) +{ + wpoly_t *wpoly; + poly_t *dpoly; // drawing poly + polyvertex_t *pv; + size_t ssnum; + INT16 ps; + + // For all segs in all sectors. + for (ssnum = 0; ssnum < numsubsectors; ssnum++) + { + wpoly = &wpoly_subsectors[ssnum]; + +#ifdef DEBUG_TRACE + if (trigger_subsector == 0xFFFFFFF2 || trigger_subsector == ssnum) + wpoly_dump( "Finalize:", wpoly); +#endif + + // Generate poly in poly_t format. + // Vertex in wpoly_t are ptr, but in poly_t they are a copy of the vertex. + dpoly = HWR_AllocPoly(wpoly->numpts); + poly_subsectors[ssnum].planepoly = dpoly; + pv = dpoly->pts; + + for (ps = 0; ps < wpoly->numpts; ps++) + { + *pv++ = *(wpoly->ppts[ps]); // copy of each vertex + } } } - -// call this routine after the BSP of a Doom wad file is loaded, -// and it will generate all the convex polys for the hardware renderer +// Call this routine after the BSP of a Doom wad file is loaded, +// and it will generate all the convex polys for the hardware renderer. +// Called from P_SetupLevel void HWR_CreatePlanePolygons(INT32 bspnum) { - poly_t *rootp; - polyvertex_t *rootpv; + wpoly_t rootp; + polyvertex_t** rootpv; size_t i; fixed_t rootbbox[4]; + (void)bspnum; + CONS_Debug(DBG_RENDER, "Creating polygons, please wait...\n"); + #ifdef HWR_LOADING_SCREEN ls_count = ls_percent = 0; // reset the loading status CON_Drawer(); //let the user know what we are doing I_FinishUpdate(); // page flip or blit buffer #endif - HWR_ClearPolys(); - + // Enter all vertexes into the root bounding box. // find min/max boundaries of map - //CONS_Debug(DBG_RENDER, "Looking for boundaries of map...\n"); + CONS_Debug(DBG_RENDER, "Looking for boundaries of map...\n"); M_ClearBox(rootbbox); - for (i = 0;i < numvertexes; i++) + for (i = 0; i < numvertexes; i++) M_AddToBox(rootbbox, vertexes[i].x, vertexes[i].y); - //CONS_Debug(DBG_RENDER, "Generating subsector polygons... %d subsectors\n", numsubsectors); + CONS_Debug(DBG_RENDER, "Generating subsector polygons... %lu subsectors\n", numsubsectors); - HWR_FreeExtraSubsectors(); // allocate extra data for each subsector present in map - totsubsectors = numsubsectors + NEWSUBSECTORS; - extrasubsectors = calloc(totsubsectors, sizeof (*extrasubsectors)); - if (extrasubsectors == NULL) - I_Error("couldn't malloc extrasubsectors totsubsectors %s\n", sizeu1(totsubsectors)); + num_alloc_poly_subsector = numsubsectors + NUM_EXTRA_SUBSECTORS; + poly_subsectors = Z_Calloc(sizeof(poly_subsector_t) * num_alloc_poly_subsector, PU_STATIC, NULL); // set all data in to 0 or NULL !!! + + wpoly_subsectors = Z_Calloc(sizeof(wpoly_t) * num_alloc_poly_subsector, PU_HWRPLANE, NULL); // allocate table for back to front drawing of subsectors - /*gl_drawsubsectors = (INT16 *)malloc(sizeof (*gl_drawsubsectors) * totsubsectors); - if (!gl_drawsubsectors) - I_Error("couldn't malloc gl_drawsubsectors\n");*/ + /*gr_drawsubsectors = (short*)malloc(sizeof(*gr_drawsubsectors) * num_alloc_poly_subsector); + if (!gr_drawsubsectors) + I_Error("couldn't malloc gr_drawsubsectors\n");*/ + + // The level map polyvertexes + create_poly_vert(); // number of the first new subsector that might be added - addsubsector = numsubsectors; + num_poly_subsector = numsubsectors; // construct the initial convex poly that encloses the full map - rootp = HWR_AllocPoly(4); - rootpv = rootp->pts; + wpoly_init_alloc(4, &rootp); // alloc space for 4 pts +#ifdef DEBUG_TRACE + rootp.id1 = poly_id++; + rootp.id2 = 0; + rootp.id3 = 0; +#endif + rootp.numpts = 4; + rootpv = rootp.ppts; + rootpv[0] = new_polyvertex(); + rootpv[1] = new_polyvertex(); + rootpv[2] = new_polyvertex(); + rootpv[3] = new_polyvertex(); + // clockwise polygon + // 0=lower_left, 1=upper_left, 2=upper_right, 3=lower_right + rootpv[0]->x = rootpv[1]->x = FIXED_TO_FLOAT(rootbbox[BOXLEFT ]); + rootpv[2]->x = rootpv[3]->x = FIXED_TO_FLOAT(rootbbox[BOXRIGHT ]); + rootpv[1]->y = rootpv[2]->y = FIXED_TO_FLOAT(rootbbox[BOXTOP ]); + rootpv[0]->y = rootpv[3]->y = FIXED_TO_FLOAT(rootbbox[BOXBOTTOM]); - rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXLEFT ]); - rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXBOTTOM]); //lr - rootpv++; - rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXLEFT ]); - rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXTOP ]); //ur - rootpv++; - rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXRIGHT ]); - rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXTOP ]); //ul - rootpv++; - rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXRIGHT ]); - rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXBOTTOM]); //ll - rootpv++; + // start at head node of bsp tree + // [WDJ] Intercept degenerate case, so BSP node is never -1. + HWR_WalkBSPNode(((numnodes > 0) ? numnodes-1 + : (0 | NF_SUBSECTOR)), // Degenerate, sector 0 + &rootp, NULL, rootbbox); - WalkBSPNode(bspnum, rootp, NULL,rootbbox); + SolveTProblem(); - i = SolveTProblem(); - //CONS_Debug(DBG_RENDER, "%d point divides a polygon line\n",i); AdjustSegs(); +#ifdef POLYTILE + polytile_clean(); +#endif + finalize_polygons(); // wpoly_t to drawing polygons + Z_Free(wpoly_subsectors); + wpoly_subsectors = NULL; + +#ifdef DEBUG_HWBSP //debug debug.. - //if (nobackpoly) - // CONS_Debug(DBG_RENDER, "no back polygon %u times\n",nobackpoly); - //"(should happen only with the deep water trick)" - //if (skipcut) - // CONS_Debug(DBG_RENDER, "%u cuts were skipped because of only one point\n",skipcut); + if (nobackpoly_cnt) + { + // should happen only with the deep water trick + CONS_Debug(DBG_RENDER, "DEBUG: no back polygon cnt= %d\n", nobackpoly_cnt); + } - //CONS_Debug(DBG_RENDER, "done: %u total subsector convex polygons\n", totalsubsecpolys); + if (skipcut_cnt) + CONS_Debug(DBG_RENDER, "DEBUG: cuts skipped because of only one point= %d\n", skipcut_cnt); + + CONS_Debug(DBG_RENDER, "DEBUG: total subsector convex polygons= %d\n", total_subsecpoly_cnt); +#endif } #endif //HWRENDER diff --git a/src/hardware/hw_plane.c b/src/hardware/hw_plane.c index 179895f1d..f4c862e5b 100644 --- a/src/hardware/hw_plane.c +++ b/src/hardware/hw_plane.c @@ -40,7 +40,7 @@ static FUINT HWR_CalcSlopeLight(FUINT lightnum, pslope_t *slope, const sector_t // -----------------+ // HWR_RenderPlane : Render a floor or ceiling convex polygon // -----------------+ -void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap) +void HWR_RenderPlane(subsector_t *subsector, poly_subsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap) { polyvertex_t * pv; float height; //constant y for all points on the convex flat polygon diff --git a/src/hardware/hw_shaders.h b/src/hardware/hw_shaders.h index 3978f6207..e92cc969f 100644 --- a/src/hardware/hw_shaders.h +++ b/src/hardware/hw_shaders.h @@ -101,7 +101,7 @@ #define GLSL_SOFTWARE_TINT_EQUATION \ "if (mix(tint_color.a, 0.0, brightmap_mix) > 0.0) {\n" \ "float color_bright = sqrt((base_color.r * base_color.r) + (base_color.g * base_color.g) + (base_color.b * base_color.b));\n" \ - "float strength = sqrt(9.0 * mix(tint_color.a, 0.0, brightmap_mix));\n" \ + "float strength = sqrt(mix(tint_color.a, 0.0, brightmap_mix));\n" \ "final_color.r = clamp((color_bright * (tint_color.r * strength)) + (base_color.r * (1.0 - strength)), 0.0, 1.0);\n" \ "final_color.g = clamp((color_bright * (tint_color.g * strength)) + (base_color.g * (1.0 - strength)), 0.0, 1.0);\n" \ "final_color.b = clamp((color_bright * (tint_color.b * strength)) + (base_color.b * (1.0 - strength)), 0.0, 1.0);\n" \ @@ -283,16 +283,16 @@ #define GLSL_WATER_TEXEL \ "float water_z = (gl_FragCoord.z / gl_FragCoord.w) / 2.0;\n" \ "float a = -pi * (water_z * freq) + (leveltime * speed);\n" \ - "float sdistort = sin(a) * amp;\n" \ - "float cdistort = cos(a) * amp;\n" \ + "float sdistort = sin(a) * amp * 1.5;\n" \ + "float cdistort = cos(a) * amp * 2.2;\n" \ "vec4 texel = texture2D(tex, vec2(gl_TexCoord[0].s - sdistort, gl_TexCoord[0].t - cdistort));\n" #define GLSL_WATER_FRAGMENT_SHADER \ "#version 120\n" \ GLSL_FLOOR_FUDGES \ - "const float freq = 0.025;\n" \ + "const float freq = 0.03;\n" \ "const float amp = 0.025;\n" \ - "const float speed = 2.0;\n" \ + "const float speed = 1.6;\n" \ "const float pi = 3.14159;\n" \ "#ifdef SRB2_PALETTE_RENDERING\n" \ "uniform sampler2D tex;\n" \ diff --git a/src/k_kart.c b/src/k_kart.c index ca145239b..7b780284a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7160,6 +7160,19 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->airdroppredelay++; } +void K_KillAirDrop(player_t *player, p_airdropflags_t airdropflags) +{ + if (!player) + return; + + if (player->airdropflags & airdropflags) + { + player->airdroptime = 0; + player->airdroppredelay = 0; + player->airdropflags &= ~PAF_AIRDROP_MASK; + } +} + // Returns the bumpspark value as an enum. INT32 K_GetBumpSpark(void) { diff --git a/src/k_kart.h b/src/k_kart.h index 6cfd73707..4cd6d4305 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -414,6 +414,8 @@ typedef enum AIRDROP_FUSION, } airdroptype_t; +void K_KillAirDrop(player_t *player, p_airdropflags_t airdropflags); + boolean K_NullDriftTiltEnabled(void); #define RECOVERYDASHADD (TICRATE/2) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index d38d5d41d..4f43b76a7 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -232,6 +232,8 @@ static const struct { {META_PATCH, "patch_t"}, {META_COLORMAP, "colormap"}, + {META_EXTRACOLORMAP,"extracolormap_t"}, + {META_LIGHTTABLE, "lighttable_t"}, {META_CAMERA, "camera_t"}, {META_ACTION, "action"}, @@ -1793,7 +1795,6 @@ static int lib_pGetSectorLightLevelAt(lua_State *L) return 1; } -/* Maybe color lib someday.... static int lib_pGetSectorColormapAt(lua_State *L) { boolean has_sector = false; @@ -1817,7 +1818,6 @@ static int lib_pGetSectorColormapAt(lua_State *L) LUA_PushUserdata(L, exc, META_EXTRACOLORMAP); return 1; } -*/ static int lib_pDoSpring(lua_State *L) { @@ -4375,6 +4375,18 @@ static int lib_kSafeRespawnPositionEx(lua_State *L) return 1; } +static int lib_kKillAirDrop(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + p_airdropflags_t airdropflags = luaL_checkinteger(L, 2); + + if (!player) + return LUA_ErrInvalid(L, "player_t"); + + K_KillAirDrop(player, airdropflags); + return 0; +} + // Checks if Rings are applicable. static int lib_kRingsActive(lua_State *L) { @@ -5589,7 +5601,7 @@ static luaL_Reg lib[] = { {"P_FloorzAtPos",lib_pFloorzAtPos}, {"P_CeilingzAtPos",lib_pCeilingzAtPos}, {"P_GetSectorLightLevelAt",lib_pGetSectorLightLevelAt}, - //{"P_GetSectorColormapAt",lib_pGetSectorColormapAt}, + {"P_GetSectorColormapAt",lib_pGetSectorColormapAt}, {"P_DoSpring",lib_pDoSpring}, // p_inter @@ -5770,6 +5782,7 @@ static luaL_Reg lib[] = { {"K_SetHyudoroCooldown",lib_kSetHyuCountdown}, {"K_SafeRespawnPosition",lib_kSafeRespawnPosition}, {"K_SafeRespawnPositionEx",lib_kSafeRespawnPositionEx}, + {"K_KillAirDrop", lib_kKillAirDrop}, {"K_GetCollideAngle",lib_kGetCollideAngle}, diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c new file mode 100644 index 000000000..6e0f51c3b --- /dev/null +++ b/src/lua_colorlib.c @@ -0,0 +1,647 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2021-2022 by "Lactozilla". +// Copyright (C) 2014-2023 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 lua_colorlib.c +/// \brief color and colormap libraries for Lua scripting + +#include "doomdef.h" +#include "fastcmp.h" +#include "r_data.h" +#include "v_video.h" +#include "k_color.h" + +#include "lua_script.h" +#include "lua_libs.h" + +#define COLORLIB_USE_LOOKUP + +#ifdef COLORLIB_USE_LOOKUP + static colorlookup_t colormix_lut; + #define GetNearestColor(r, g, b) GetColorLUT(&colormix_lut, r, g, b) +#else + #define GetNearestColor(r, g, b) NearestPaletteColor(r, g, b, pMasterPalette) +#endif + +//////////////// +// Color library +//////////////// + +static int lib_colorPaletteToRgb(lua_State *L) +{ + RGBA_t color = V_GetMasterColor((UINT8)luaL_checkinteger(L, 1)); + lua_pushinteger(L, color.s.red); + lua_pushinteger(L, color.s.green); + lua_pushinteger(L, color.s.blue); + return 3; +} + +#define IS_HEX_CHAR(x) ((x >= '0' && x <= '9') || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F')) +#define ARE_HEX_CHARS(str, i) IS_HEX_CHAR(str[i]) && IS_HEX_CHAR(str[i + 1]) + +static UINT32 hex2int(char x) +{ + if (x >= '0' && x <= '9') + return x - '0'; + else if (x >= 'a' && x <= 'f') + return x - 'a' + 10; + else if (x >= 'A' && x <= 'F') + return x - 'A' + 10; + + return 0; +} + +static UINT8 GetHTMLColorLength(const char *str) +{ + if (str[0] == '#') + str++; + return strlen(str) >= 8 ? 4 : 3; +} + +static UINT8 ParseHTMLColor(const char *str, UINT8 *rgba, size_t numc) +{ + const char *hex = str; + + if (hex[0] == '#') + hex++; + else if (!IS_HEX_CHAR(hex[0])) + return 0; + + size_t len = strlen(hex); + + if (len == 3) + { + // Shorthand like #09C + for (unsigned i = 0; i < 3; i++) + { + if (!IS_HEX_CHAR(hex[i])) + return 0; + + UINT32 hx = hex2int(hex[i]); + *rgba++ = (hx * 16) + hx; + } + + return 3; + } + else if (len == 6 || len == 8) + { + if (numc != 4) + len = 6; + + // A triplet like #0099CC + for (unsigned i = 0; i < len; i += 2) + { + if (!ARE_HEX_CHARS(hex, i)) + return false; + + *rgba++ = (hex2int(hex[i]) * 16) + hex2int(hex[i + 1]); + } + + return len; + } + + return 0; +} + +static UINT8 GetPackedRGBA(UINT32 colors, UINT8 *rgba) +{ + if (colors > 0xFFFFFF) + { + rgba[0] = (colors >> 24) & 0xFF; + rgba[1] = (colors >> 16) & 0xFF; + rgba[2] = (colors >> 8) & 0xFF; + rgba[3] = colors & 0xFF; + return 4; + } + else + { + rgba[0] = (colors >> 16) & 0xFF; + rgba[1] = (colors >> 8) & 0xFF; + rgba[2] = colors & 0xFF; + rgba[3] = 0xFF; + return 3; + } +} + +static UINT8 GetArgsRGBA(lua_State *L, UINT8 index, INT32 *r, INT32 *g, INT32 *b, INT32 *a) +{ + UINT8 rgba[4] = { 0, 0, 0, 255 }; + UINT8 num = 0; + + if (lua_gettop(L) == 1 && lua_type(L, index) == LUA_TNUMBER) + { + num = GetPackedRGBA(luaL_checkinteger(L, 1), rgba); + + *r = rgba[0]; + *g = rgba[1]; + *b = rgba[2]; + if (a) + *a = rgba[3]; + } + else if (lua_type(L, index) == LUA_TSTRING) + { + const char *str = lua_tostring(L, index); + UINT8 parsed = ParseHTMLColor(str, rgba, GetHTMLColorLength(str)); + if (!parsed) + luaL_error(L, "Malformed HTML color '%s'", str); + + num = parsed == 8 ? 4 : 3; + + *r = rgba[0]; + *g = rgba[1]; + *b = rgba[2]; + if (a) + *a = rgba[3]; + } + else + { + INT32 temp; + +#define CHECKINT(i) luaL_checkinteger(L, i) +#define GETCOLOR(c, i, desc) { \ + temp = CHECKINT(i); \ + if (temp < 0 || temp > 255) \ + luaL_error(L, desc " channel %d out of range (0 - 255)", temp); \ + c = temp; \ + num++; \ + } + + GETCOLOR(*r, index + 0, "red color"); + GETCOLOR(*g, index + 1, "green color"); + GETCOLOR(*b, index + 2, "blue color"); +#undef CHECKINT +#define CHECKINT(i) luaL_optinteger(L, i, 255) + if (a) + GETCOLOR(*a, index + 3, "alpha"); +#undef CHECKINT +#undef GETCOLOR + + num = 3 + (lua_type(L, index + 3) == LUA_TNUMBER); + } + + return num; +} + +static int lib_colorRgbToPalette(lua_State *L) +{ + INT32 r, g, b; + GetArgsRGBA(L, 1, &r, &g, &b, NULL); + +#ifdef COLORLIB_USE_LOOKUP + InitColorLUT(&colormix_lut, pMasterPalette, false); +#endif + + lua_pushinteger(L, GetNearestColor(r, g, b)); + return 1; +} + +#define SCALE_UINT8_TO_FIXED(val) FixedDiv(val * FRACUNIT, 255 * FRACUNIT) +#define SCALE_FIXED_TO_UINT8(val) FixedRound(FixedMul(val, 255 * FRACUNIT)) / FRACUNIT + +static fixed_t hue2rgb(fixed_t p, fixed_t q, fixed_t t) +{ + if (t < 0) + t += FRACUNIT; + if (t > FRACUNIT) + t -= FRACUNIT; + + fixed_t out; + + if (t < FRACUNIT / 6) + out = p + FixedMul(FixedMul(q - p, 6 * FRACUNIT), t); + else if (t < FRACUNIT / 2) + out = q; + else if (t < 2 * FRACUNIT / 3) + out = p + FixedMul(FixedMul(q - p, 2 * FRACUNIT / 3 - t), 6 * FRACUNIT); + else + out = p; + + return out; +} + +static int lib_colorHslToRgb(lua_State *L) +{ + fixed_t h, s, l; + +#define GETHSL(c, i, desc) \ + c = luaL_checkinteger(L, i); \ + if (c < 0 || c > 255) \ + luaL_error(L, desc " %d out of range (0 - 255)", c) + + GETHSL(h, 1, "hue"); + GETHSL(s, 2, "saturation"); + GETHSL(l, 3, "value"); +#undef GETHSL + + if (!s) + { + lua_pushinteger(L, l); + lua_pushinteger(L, l); + lua_pushinteger(L, l); + } + else + { + h = SCALE_UINT8_TO_FIXED(h); + s = SCALE_UINT8_TO_FIXED(s); + l = SCALE_UINT8_TO_FIXED(l); + + fixed_t q, p; + + if (l < FRACUNIT/2) + q = FixedMul(l, FRACUNIT + s); + else + q = l + s - FixedMul(l, s); + + p = l * 2 - q; + + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h + FRACUNIT/3))); + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h))); + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h - FRACUNIT/3))); + } + + return 3; +} + +static int lib_colorRgbToHsl(lua_State *L) +{ + INT32 ir, ig, ib; + GetArgsRGBA(L, 1, &ir, &ig, &ib, NULL); + + fixed_t r = SCALE_UINT8_TO_FIXED(ir); + fixed_t g = SCALE_UINT8_TO_FIXED(ig); + fixed_t b = SCALE_UINT8_TO_FIXED(ib); + + fixed_t cmin = min(min(r, g), b); + fixed_t cmax = max(max(r, g), b); + + fixed_t h, s, l = (cmax + cmin) / 2; + fixed_t delta = cmax - cmin; + + if (!delta) + h = s = 0; + else + { + if (l > FRACUNIT / 2) + s = FixedDiv(delta, (FRACUNIT * 2) - cmax - cmin); + else + s = FixedDiv(delta, cmax + cmin); + + if (r > g && r > b) + { + h = FixedDiv(g - b, delta); + + if (g < b) + h += FRACUNIT * 6; + } + else + { + h = FixedDiv(r - g, delta); + + if (g > b) + h += FRACUNIT * 2; + else + h += FRACUNIT * 4; + } + + h = FixedDiv(h, FRACUNIT * 6); + } + + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(h)); + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(s)); + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(l)); + + return 3; +} + +static int lib_colorHexToRgb(lua_State *L) +{ + UINT8 rgba[4] = { 0, 0, 0, 255 }; + + const char *str = luaL_checkstring(L, 1); + UINT8 parsed = ParseHTMLColor(str, rgba, 4), num = 3; + if (!parsed) + luaL_error(L, "Malformed HTML color '%s'", str); + else if (parsed == 8) + num++; + + lua_pushinteger(L, rgba[0]); + lua_pushinteger(L, rgba[1]); + lua_pushinteger(L, rgba[2]); + if (num == 4) + lua_pushinteger(L, rgba[3]); + + return num; +} + +static int lib_colorRgbToHex(lua_State *L) +{ + INT32 r, g, b, a; + UINT8 num = GetArgsRGBA(L, 1, &r, &g, &b, &a); + + char buffer[10]; + if (num >= 4) + snprintf(buffer, sizeof buffer, "#%02X%02X%02X%02X", r, g, b, a); + else + snprintf(buffer, sizeof buffer, "#%02X%02X%02X", r, g, b); + + lua_pushstring(L, buffer); + return 1; +} + +static int lib_colorPackRgb(lua_State *L) +{ + INT32 r, g, b; + + GetArgsRGBA(L, 1, &r, &g, &b, NULL); + + UINT32 packed = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + + lua_pushinteger(L, packed); + return 1; +} + +static int lib_colorPackRgba(lua_State *L) +{ + INT32 r, g, b, a; + + GetArgsRGBA(L, 1, &r, &g, &b, &a); + + UINT32 packed = ((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF); + + lua_pushinteger(L, packed); + return 1; +} + +static int lib_colorUnpackRgb(lua_State *L) +{ + UINT8 rgba[4]; + + UINT8 num = GetPackedRGBA(lua_tointeger(L, 1), rgba); + + for (UINT8 i = 0; i < num; i++) + { + lua_pushinteger(L, rgba[i]); + } + + return num; +} + +static luaL_Reg color_lib[] = { + {"paletteToRgb", lib_colorPaletteToRgb}, + {"rgbToPalette", lib_colorRgbToPalette}, + {"hslToRgb", lib_colorHslToRgb}, + {"rgbToHsl", lib_colorRgbToHsl}, + {"hexToRgb", lib_colorHexToRgb}, + {"rgbToHex", lib_colorRgbToHex}, + {"packRgb", lib_colorPackRgb}, + {"packRgba", lib_colorPackRgba}, + {"unpackRgb", lib_colorUnpackRgb}, + {NULL, NULL} +}; + +///////////////////////// +// extracolormap userdata +///////////////////////// + +enum extracolormap_e { + extracolormap_red = 0, + extracolormap_green, + extracolormap_blue, + extracolormap_alpha, + extracolormap_color, + extracolormap_fade_red, + extracolormap_fade_green, + extracolormap_fade_blue, + extracolormap_fade_alpha, + extracolormap_fade_color, + extracolormap_fade_start, + extracolormap_fade_end, + extracolormap_colormap +}; + +static const char *const extracolormap_opt[] = { + "red", + "green", + "blue", + "alpha", + "color", + "fade_red", + "fade_green", + "fade_blue", + "fade_alpha", + "fade_color", + "fade_start", + "fade_end", + "colormap", + NULL}; + +static int extracolormap_get(lua_State *L) +{ + extracolormap_t *exc = *((extracolormap_t **)luaL_checkudata(L, 1, META_EXTRACOLORMAP)); + enum extracolormap_e field = luaL_checkoption(L, 2, NULL, extracolormap_opt); + + switch (field) + { + case extracolormap_red: + lua_pushinteger(L, R_GetRgbaR(exc->rgba)); + break; + case extracolormap_green: + lua_pushinteger(L, R_GetRgbaG(exc->rgba)); + break; + case extracolormap_blue: + lua_pushinteger(L, R_GetRgbaB(exc->rgba)); + break; + case extracolormap_alpha: + lua_pushinteger(L, R_GetRgbaA(exc->rgba)); + break; + case extracolormap_color: + lua_pushinteger(L, R_GetRgbaR(exc->rgba)); + lua_pushinteger(L, R_GetRgbaG(exc->rgba)); + lua_pushinteger(L, R_GetRgbaB(exc->rgba)); + lua_pushinteger(L, R_GetRgbaA(exc->rgba)); + return 4; + case extracolormap_fade_red: + lua_pushinteger(L, R_GetRgbaR(exc->fadergba)); + break; + case extracolormap_fade_green: + lua_pushinteger(L, R_GetRgbaG(exc->fadergba)); + break; + case extracolormap_fade_blue: + lua_pushinteger(L, R_GetRgbaB(exc->fadergba)); + break; + case extracolormap_fade_alpha: + lua_pushinteger(L, R_GetRgbaA(exc->fadergba)); + break; + case extracolormap_fade_color: + lua_pushinteger(L, R_GetRgbaR(exc->fadergba)); + lua_pushinteger(L, R_GetRgbaG(exc->fadergba)); + lua_pushinteger(L, R_GetRgbaB(exc->fadergba)); + lua_pushinteger(L, R_GetRgbaA(exc->fadergba)); + return 4; + case extracolormap_fade_start: + lua_pushinteger(L, exc->fadestart); + break; + case extracolormap_fade_end: + lua_pushinteger(L, exc->fadeend); + break; + case extracolormap_colormap: + LUA_PushUserdata(L, exc->colormap, META_LIGHTTABLE); + break; + } + return 1; +} + +static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba, int arg) +{ + if (lua_type(L, arg) == LUA_TSTRING) + { + const char *str = lua_tostring(L, arg); + UINT8 parsed = ParseHTMLColor(str, rgba, 4); + if (!parsed) + luaL_error(L, "Malformed HTML color '%s'", str); + } + else + { + GetPackedRGBA(lua_tointeger(L, arg), rgba); + } +} + +static int extracolormap_set(lua_State *L) +{ + extracolormap_t *exc = *((extracolormap_t **)luaL_checkudata(L, 1, META_EXTRACOLORMAP)); + enum extracolormap_e field = luaL_checkoption(L, 2, NULL, extracolormap_opt); + + UINT8 r = R_GetRgbaR(exc->rgba); + UINT8 g = R_GetRgbaG(exc->rgba); + UINT8 b = R_GetRgbaB(exc->rgba); + UINT8 a = R_GetRgbaA(exc->rgba); + + UINT8 fr = R_GetRgbaR(exc->fadergba); + UINT8 fg = R_GetRgbaG(exc->fadergba); + UINT8 fb = R_GetRgbaB(exc->fadergba); + UINT8 fa = R_GetRgbaA(exc->fadergba); + + UINT8 rgba[4]; + + INT32 old_rgba = exc->rgba, old_fade_rgba = exc->fadergba; // It's not unsigned? + UINT8 old_fade_start = exc->fadestart, old_fade_end = exc->fadeend; + +#define val luaL_checkinteger(L, 3) + + switch(field) + { + case extracolormap_red: + exc->rgba = R_PutRgbaRGBA(val, g, b, a); + break; + case extracolormap_green: + exc->rgba = R_PutRgbaRGBA(r, val, b, a); + break; + case extracolormap_blue: + exc->rgba = R_PutRgbaRGBA(r, g, val, a); + break; + case extracolormap_alpha: + exc->rgba = R_PutRgbaRGBA(r, g, b, val); + break; + case extracolormap_color: + rgba[0] = r; + rgba[1] = g; + rgba[2] = b; + rgba[3] = a; + GetExtraColormapRGBA(L, rgba, 3); + exc->rgba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]); + break; + case extracolormap_fade_red: + exc->fadergba = R_PutRgbaRGBA(val, fg, fb, fa); + break; + case extracolormap_fade_green: + exc->fadergba = R_PutRgbaRGBA(fr, val, fb, fa); + break; + case extracolormap_fade_blue: + exc->fadergba = R_PutRgbaRGBA(fr, fg, val, fa); + break; + case extracolormap_fade_alpha: + exc->fadergba = R_PutRgbaRGBA(fr, fg, fb, val); + break; + case extracolormap_fade_color: + rgba[0] = fr; + rgba[1] = fg; + rgba[2] = fb; + rgba[3] = fa; + GetExtraColormapRGBA(L, rgba, 3); + exc->fadergba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]); + break; + case extracolormap_fade_start: + if (val > 31) + return luaL_error(L, "fade start %d out of range (0 - 31)", val); + exc->fadestart = val; + break; + case extracolormap_fade_end: + if (val > 31) + return luaL_error(L, "fade end %d out of range (0 - 31)", val); + exc->fadeend = val; + break; + case extracolormap_colormap: + return luaL_error(L, LUA_QL("extracolormap_t") " field " LUA_QS " should not be set directly.", extracolormap_opt[field]); + } + +#undef val + + if (exc->rgba != old_rgba + || exc->fadergba != old_fade_rgba + || exc->fadestart != old_fade_start + || exc->fadeend != old_fade_end) + R_GenerateLightTable(exc, true); + + return 0; +} + +static int lighttable_get(lua_State *L) +{ + void **userdata; + + lighttable_t *table = *((lighttable_t **)luaL_checkudata(L, 1, META_LIGHTTABLE)); + UINT32 row = luaL_checkinteger(L, 2); + if (row < 1 || row > 34) + return luaL_error(L, "lighttable row %d out of range (1 - %d)", row, 34); + + userdata = lua_newuserdata(L, sizeof(void *)); + *userdata = &table[256 * (row - 1)]; + luaL_getmetatable(L, META_COLORMAP); + lua_setmetatable(L, -2); + + return 1; +} + +static int lighttable_len(lua_State *L) +{ + lua_pushinteger(L, NUM_PALETTE_ENTRIES); + return 1; +} + +int LUA_ColorLib(lua_State *L) +{ + luaL_newmetatable(L, META_EXTRACOLORMAP); + lua_pushcfunction(L, extracolormap_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, extracolormap_set); + lua_setfield(L, -2, "__newindex"); + lua_pop(L, 1); + + luaL_newmetatable(L, META_LIGHTTABLE); + lua_pushcfunction(L, lighttable_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lighttable_len); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + + luaL_register(L, "color", color_lib); + + return 0; +} diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index edc324c24..2f2c304f7 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -748,6 +748,7 @@ static int libd_drawAffine(lua_State *L) lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); huddrawlist_h list = lua_touserdata(L, -1); + (void)list; lua_pop(L, 1); //if (LUA_HUD_IsDrawListValid(list)) diff --git a/src/lua_libs.h b/src/lua_libs.h index aaa9725fe..90fe68bca 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -96,6 +96,8 @@ extern lua_State *gL; #define META_HUDINFO "HUDINFO_T*" #define META_PATCH "PATCH_T*" #define META_COLORMAP "COLORMAP" +#define META_EXTRACOLORMAP "EXTRACOLORMAP_T*" +#define META_LIGHTTABLE "LIGHTTABLE_T*" #define META_CAMERA "CAMERA_T*" #define META_ACTION "actionf_p1*" @@ -152,6 +154,7 @@ int LUA_TagLib(lua_State *L); int LUA_PolyObjLib(lua_State *L); int LUA_BlockmapLib(lua_State *L); int LUA_HudLib(lua_State *L); +int LUA_ColorLib(lua_State *L); int LUA_FollowerLib(lua_State *L); int LUA_BotVarsLib(lua_State *L); int LUA_TerrainLib(lua_State *L); diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 29a317876..532284e93 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -49,6 +49,7 @@ enum sector_e { sector_ffloors, sector_fslope, sector_cslope, + sector_colormap, sector_flags, sector_specialflags, sector_damagetype, @@ -85,6 +86,7 @@ static const char *const sector_opt[] = { "ffloors", "f_slope", "c_slope", + "colormap", "flags", "specialflags", "damagetype", @@ -750,6 +752,9 @@ static int sector_get(lua_State *L) case sector_cslope: // c_slope LUA_PushUserdata(L, sector->c_slope, META_SLOPE); return 1; + case sector_colormap: // extra_colormap + LUA_PushUserdata(L, sector->extra_colormap, META_EXTRACOLORMAP); + return 1; case sector_flags: // flags lua_pushinteger(L, sector->flags); return 1; diff --git a/src/lua_script.c b/src/lua_script.c index 0144e09d8..a23487eda 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -61,6 +61,7 @@ static lua_CFunction liblist[] = { LUA_PolyObjLib, // polyobj_t LUA_BlockmapLib, // blockmap stuff LUA_HudLib, // HUD stuff + LUA_ColorLib, // general color functions LUA_FollowerLib, // follower_t, followers[] LUA_BotVarsLib, // botvars_t LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t diff --git a/src/m_misc.cpp b/src/m_misc.cpp index cb1789e19..50ce1ffa4 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -2556,3 +2556,37 @@ UINT32 FNV1a_QuickCaseHash(const char *message, size_t size) return hash; } + +// Returns true if the string is empty. +boolean M_IsStringEmpty(const char *s) +{ + const char *ch = s; + + if (s == NULL || s[0] == '\0') + return true; + + for (;;ch++) + { + if (!(*ch)) + break; + if (!isspace((*ch))) + return false; + } + + return true; +} + +// Rounds off floating numbers and checks for 0 - 255 bounds +int M_RoundUp(double number) +{ + if (number > 255.0l) + return 255; + if (number < 0.0l) + return 0; + + if ((int)number <= (int)(number - 0.5f)) + return (int)number + 1; + + return (int)number; +} + diff --git a/src/m_misc.h b/src/m_misc.h index 10c1eeed3..9e5dfea63 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -126,6 +126,10 @@ FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size); // Hashes some message using FNV-1a UINT32 FNV1a_QuickCaseHash(const char *message, size_t size); +boolean M_IsStringEmpty(const char *s); + +int M_RoundUp(double number); + #include "w_wad.h" extern char configfile[MAX_WADPATH]; diff --git a/src/p_enemy.c b/src/p_enemy.c index 061d5526a..e3ba64846 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -11066,6 +11066,9 @@ void A_SPBChase(void *thing) specialring->colorized = true; specialring->color = SKINCOLOR_RED; specialring->flags |= MF_NOCLIPHEIGHT; + + // But don't let first pick these up. + specialring->extravalue3 = 1; } } diff --git a/src/p_inter.c b/src/p_inter.c index 547619eff..c0da4b857 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -629,7 +629,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->extravalue1) return; - // No picking up rings while SPB is targetting you + // No picking up rings while your rings are locked. if (player->pflags & PF_RINGLOCK) return; @@ -637,6 +637,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->threshold > 0 || P_PlayerInPain(player)) return; + // Don't pick up SPB rings in first. + if (special->extravalue3 && player->position == 1) + return; + if (!(P_CanPickupItem(player, PICKUPITEM_RING))) return; diff --git a/src/p_mobj.c b/src/p_mobj.c index 7cc8bd686..63c939319 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3406,12 +3406,7 @@ void P_MobjCheckWater(mobj_t *mobj) mobj->momz = FixedMul(mobj->momz, FixedDiv(2*FRACUNIT, 5*FRACUNIT)); // kill momentum significantly, to make the goo feel thick. // Kill heavy airdrop too so you don't get softlocked. - if (mobj->player && (mobj->player->airdropflags & PAF_AIRDROP_HEAVY)) - { - mobj->player->airdroptime = 0; - mobj->player->airdroppredelay = 0; - mobj->player->airdropflags &= ~PAF_AIRDROP_MASK; - } + K_KillAirDrop(mobj->player, PAF_AIRDROP_HEAVY); } else if (wasinwater && P_MobjFlip(mobj) * mobj->momz > 0) { diff --git a/src/p_saveg.c b/src/p_saveg.c index a6d8f6fa1..f67d299a3 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2081,6 +2081,35 @@ static void DiffMobj(const mobj_t *mobj, UINT32 diff[]) DIFF(mobj->voice, MD3_VOICE); } +boolean MobjIsNetSynced(mobj_t *mobj) +{ + // Ignore stationary hoops - these will be respawned from mapthings. + if (mobj->type == MT_HOOP) + return false; + + // These are NEVER saved. + if (mobj->type == MT_HOOPCOLLIDE) + return false; + + // This hoop has already been collected. + if (mobj->type == MT_HOOPCENTER && mobj->threshold == 4242) + return false; + + // MT_SPARK: used for debug stuff + if (mobj->type == MT_SPARK) + return false; + + // MT_FOLLOWERHORN: So it turns out hornmod is fundamentally incompatible with netsync + if (mobj->type == MT_FOLLOWERHORN) + return false; + + // This is a non-synched visual effect mobj + if (mobj->flags2 & MF2_DONTSYNC) + return false; + + return true; +} + static thinker_t *SyncMobjThinker(savebuffer_t *save, actionf_p1 thinker, thinker_t *th, UINT8 type) { mobj_t *mobj = (mobj_t *)th; @@ -2089,24 +2118,7 @@ static thinker_t *SyncMobjThinker(savebuffer_t *save, actionf_p1 thinker, thinke if (save->write) { - // Ignore stationary hoops - these will be respawned from mapthings. - if (mobj->type == MT_HOOP) - return th; - - // These are NEVER saved. - if (mobj->type == MT_HOOPCOLLIDE) - return th; - - // This hoop has already been collected. - if (mobj->type == MT_HOOPCENTER && mobj->threshold == 4242) - return th; - - // MT_SPARK: used for debug stuff - if (mobj->type == MT_SPARK) - return th; - - // This is a non-synched visual effect mobj - if (mobj->flags2 & MF2_DONTSYNC) + if (!MobjIsNetSynced(mobj)) return th; // Scrap all of that. If we're a hoop center, this is ALL we're saving. @@ -3822,9 +3834,7 @@ static void P_RelinkPointers(savebuffer_t *save) mobj = (mobj_t *)currentthinker; - if (UNLIKELY(mobj->type == MT_HOOP || mobj->type == MT_HOOPCOLLIDE || mobj->type == MT_HOOPCENTER - // MT_SPARK: used for debug stuff - || mobj->type == MT_SPARK)) + if (!MobjIsNetSynced(mobj)) continue; RELINK(&mobj->tracer); @@ -4538,11 +4548,7 @@ void P_SaveNetGame(savebuffer_t *save, boolean resending) continue; mobj = (mobj_t *)th; - if (UNLIKELY(mobj->type == MT_HOOP || mobj->type == MT_HOOPCOLLIDE || mobj->type == MT_HOOPCENTER - // MT_SPARK: used for debug stuff - || mobj->type == MT_SPARK - // MT_FOLLOWERHORN: So it turns out hornmod is fundamentally incompatible with netsync - || mobj->type == MT_FOLLOWERHORN || mobj->flags2 & MF2_DONTSYNC)) + if (!MobjIsNetSynced(mobj)) continue; mobj->mobjnum = i++; } diff --git a/src/p_saveg.h b/src/p_saveg.h index a57578bc2..de0527dbe 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -61,6 +61,8 @@ boolean P_SaveBufferFromLump(savebuffer_t *save, lumpnum_t lump); size_t P_SaveBufferFromFile(savebuffer_t *save, char const *name); void P_SaveBufferFree(savebuffer_t *save); +boolean MobjIsNetSynced(mobj_t *mobj); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/p_setup.c b/src/p_setup.c index a06ea14db..2631db202 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3873,7 +3873,7 @@ static inline float P_SegLengthFloat(seg_t *seg) dx = FIXED_TO_FLOAT(seg->v2->x - seg->v1->x); dy = FIXED_TO_FLOAT(seg->v2->y - seg->v1->y); - return (float)hypot(dx, dy); + return hypotf(dx, dy); } #endif @@ -4033,7 +4033,7 @@ static void P_LoadSegs(UINT8 *data) seg->length = P_SegLength(seg); #ifdef HWRENDER - seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0; + seg->flength = P_SegLengthFloat(seg); #endif seg->glseg = false; @@ -9051,21 +9051,20 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // Load the ccheckpoints and waypoints please! if (gametyperules & GTR_CIRCUIT && gamestate != GS_TITLESCREEN) { - - if ((K_SetupWaypointList() == false)) + if ((K_SetupWaypointList() == false)) + { + if (!K_UsingLegacyCheckpoints()) { - if (!K_UsingLegacyCheckpoints()) - { - CONS_Alert(CONS_ERROR, "Waypoints were not able to be setup and legacy checkpoints do not exist! Player positions will not work correctly.\n"); - } + CONS_Alert(CONS_ERROR, "Waypoints were not able to be setup and legacy checkpoints do not exist! Player positions will not work correctly.\n"); } + } } #ifdef HWRENDER // not win32 only 19990829 by Kin gl_maploaded = false; - // Lactozilla: Free extrasubsectors regardless of renderer. - HWR_FreeExtraSubsectors(); + // Lactozilla: Free poly_subsectors regardless of renderer. + HWR_FreePolyPool(); // Create plane polygons. if (rendermode == render_opengl) diff --git a/src/p_user.c b/src/p_user.c index f5a740e10..e74ae7b92 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1692,6 +1692,9 @@ static void P_CheckBouncySectors(player_t *player) { player->mo->momx = -FixedMul(player->mo->momx,rover->bouncestrength); player->mo->momy = -FixedMul(player->mo->momy,rover->bouncestrength); + + // Kill heavy airdrop too so you don't get softlocked. + K_KillAirDrop(player, PAF_AIRDROP_HEAVY); } else { @@ -1731,6 +1734,10 @@ static void P_CheckBouncySectors(player_t *player) player->mo->momx = momentum.x; player->mo->momy = momentum.y; player->mo->momz = momentum.z; + + // Kill heavy airdrop too so you don't get softlocked. + K_KillAirDrop(player, PAF_AIRDROP_HEAVY); + } goto bouncydone; } @@ -3884,8 +3891,12 @@ static angle_t P_GetCameraPitchRollAngle(mobj_t *mobj, player_t *viewPlayer) } else { - // Regular Mobjs don't air tilt. - viewingAngle = mobj->angle; + // For regular objects, use the camera; just not the *player's* camera. + //... Unless you are a Nights bumber... + if (mobj->type == MT_NIGHTSBUMPER) + viewingAngle = mobj->angle; + else + viewingAngle = R_PointToAngleFloat(mobj->x, mobj->y); } pitchMul = -FINESINE(viewingAngle >> ANGLETOFINESHIFT); diff --git a/src/r_data.c b/src/r_data.c index b4ae9eeae..d17ba2901 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -387,7 +387,7 @@ extracolormap_t *R_CreateDefaultColormap(boolean lighttable) exc->fadeend = 31; exc->flags = 0; exc->rgba = 0; - exc->fadergba = 0x19000000; + exc->fadergba = 0xFF000000; exc->colormap = lighttable ? R_CreateLightTable(exc) : NULL; #ifdef EXTRACOLORMAPLUMPS exc->lump = LUMPERROR; @@ -502,7 +502,7 @@ boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, && !flags) ) && (!checkrgba ? true : rgba == 0) - && (!checkfadergba ? true : fadergba == 0x19000000) + && (!checkfadergba ? true : (unsigned)fadergba == 0xFF000000) #ifdef EXTRACOLORMAPLUMPS && lump == LUMPERROR && extra_colormap->lumpname[0] == 0 @@ -603,7 +603,7 @@ extracolormap_t *R_ColormapForName(char *name) if (lump == LUMPERROR) I_Error("R_ColormapForName: Cannot find colormap lump %.8s\n", name); - exc = R_GetColormapFromListByValues(0, 0x19000000, 0, 31, 0, lump); + exc = R_GetColormapFromListByValues(0, 0xFF000000, 0, 31, 0, lump); if (exc) return exc; @@ -623,7 +623,7 @@ extracolormap_t *R_ColormapForName(char *name) exc->fadeend = 31; exc->flags = 0; exc->rgba = 0; - exc->fadergba = 0x19000000; + exc->fadergba = 0xFF000000; R_AddColormapToList(exc); @@ -642,27 +642,42 @@ extracolormap_t *R_ColormapForName(char *name) static double brightChange[256], map[256][3]; static double deltas[256][3]; // for old fade colormaps -static int RoundUp(double number); +static colorlookup_t lighttable_lut; + +static UINT8 LightTableNearest(UINT8 r, UINT8 g, UINT8 b) +{ + return NearestColor(r, g, b); +} + +static UINT8 LightTableNearest_LUT(UINT8 r, UINT8 g, UINT8 b) +{ + return GetColorLUT(&lighttable_lut, r, g, b); +} lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) { - double cmaskr, cmaskg, cmaskb, cdestr, cdestg, cdestb, cdestbright; + extra_colormap->colormap = Z_MallocAlign((256 * 34) + 10, PU_LEVEL, NULL, 8); + R_GenerateLightTable(extra_colormap, false); + return extra_colormap->colormap; +} + +void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup) +{ + double cmaskr, cmaskg, cmaskb, cdestr, cdestg, cdestb; double maskamt = 0, othermask = 0; - double fmaskamt = 0, fothermask = 0; UINT8 cr = R_GetRgbaR(extra_colormap->rgba), - cg = R_GetRgbaG(extra_colormap->rgba), - cb = R_GetRgbaB(extra_colormap->rgba), - ca = R_GetRgbaA(extra_colormap->rgba), - cfr = R_GetRgbaR(extra_colormap->fadergba), - cfg = R_GetRgbaG(extra_colormap->fadergba), - cfb = R_GetRgbaB(extra_colormap->fadergba), - cfa = R_GetRgbaA(extra_colormap->fadergba); + cg = R_GetRgbaG(extra_colormap->rgba), + cb = R_GetRgbaB(extra_colormap->rgba), + ca = R_GetRgbaA(extra_colormap->rgba), + cfr = R_GetRgbaR(extra_colormap->fadergba), + cfg = R_GetRgbaG(extra_colormap->fadergba), + cfb = R_GetRgbaB(extra_colormap->fadergba); + // cfa = R_GetRgbaA(extra_colormap->fadergba); // unused in software UINT8 fadestart = extra_colormap->fadestart, - fadedist = extra_colormap->fadeend - extra_colormap->fadestart; + fadedist = extra_colormap->fadeend - extra_colormap->fadestart; - lighttable_t *lighttable = NULL; size_t i; ///////////////////// @@ -672,7 +687,7 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) cmaskg = cg; cmaskb = cb; - maskamt = (double)(ca/24.0l); + maskamt = (double)(ca/255.0l); othermask = 1 - maskamt; maskamt /= 0xff; @@ -686,27 +701,39 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) cdestr = cfr; cdestg = cfg; cdestb = cfb; - cdestbright = sqrt((cfr*cfr) + (cfg*cfg) + (cfb*cfb)); - fmaskamt = (double)(cfa/24.0l); - fothermask = 1 - fmaskamt; - //fmaskamt /= 0xff; + // fade alpha unused in software + // maskamt = (double)(cfa/255.0l); + // othermask = 1 - maskamt; + // maskamt /= 0xff; - (void)fothermask; // unused, but don't feel like commenting it out + // cdestr *= maskamt; + // cdestg *= maskamt; + // cdestb *= maskamt; ///////////////////// // This code creates the colormap array used by software renderer ///////////////////// { - double r, g, b, cbrightness, cbest, cdist; + double r, g, b, cbrightness; int p; - lighttable_t *colormap_p; + char *colormap_p; + + UINT8 (*NearestColorFunc)(UINT8, UINT8, UINT8); + + if (uselookup) + { + InitColorLUT(&lighttable_lut, pMasterPalette, false); + NearestColorFunc = LightTableNearest_LUT; + } + else + NearestColorFunc = LightTableNearest; // Initialise the map and delta arrays // map[i] stores an RGB color (as double) for index i, // which is then converted to SRB2's palette later - // brightChange[i] is the value added/subtracted every step for the fade; - // map[i]'s values are in/decremented by it after each use + // deltas[i] stores a corresponding fade delta between the RGB color and the final fade color; + // map[i]'s values are decremented by after each use for (i = 0; i < 256; i++) { r = pMasterPalette[i].s.red; @@ -717,122 +744,55 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) map[i][0] = (cbrightness * cmaskr) + (r * othermask); if (map[i][0] > 255.0l) map[i][0] = 255.0l; + deltas[i][0] = (map[i][0] - cdestr) / (double)fadedist; map[i][1] = (cbrightness * cmaskg) + (g * othermask); if (map[i][1] > 255.0l) map[i][1] = 255.0l; + deltas[i][1] = (map[i][1] - cdestg) / (double)fadedist; map[i][2] = (cbrightness * cmaskb) + (b * othermask); if (map[i][2] > 255.0l) map[i][2] = 255.0l; - - if (extra_colormap->flags & CMF_NEWFADE) - { - // Get the "best" color. - // Our brightest color's value, if we're fading to a darker color, - // or our (inverted) darkest color's value, if we're fading to a brighter color. - if (cbrightness < cdestbright) - { - cbest = 255.0l - min(r, min(g, b)); - cdist = 255.0l - max(cdestr, max(cdestg, cdestb)); - } - else - { - cbest = max(r, max(g, b)); - cdist = min(cdestr, min(cdestg, cdestb)); - } - - // Add/subtract this value during fading. - brightChange[i] = (fabs(cbest - cdist) / (double)fadedist) * fmaskamt; - } - else - { - deltas[i][0] = (map[i][0] - cdestr) / (double)fadedist; - deltas[i][1] = (map[i][1] - cdestg) / (double)fadedist; - deltas[i][2] = (map[i][2] - cdestb) / (double)fadedist; - } + deltas[i][2] = (map[i][2] - cdestb) / (double)fadedist; } // Now allocate memory for the actual colormap array itself! // aligned on 8 bit for asm code - colormap_p = Z_MallocAlign((COLORMAP_SIZE * (encoremap ? 2 : 1)) + 10, PU_LEVEL, NULL, 8); - lighttable = (UINT8 *)colormap_p; + colormap_p = (char *)extra_colormap->colormap; // Calculate the palette index for each palette index, for each light level // (as well as the two unused colormap lines we inherited from Doom) - for (p = 0; p < LIGHTLEVELS; p++) + for (p = 0; p < 34; p++) { for (i = 0; i < 256; i++) { - *colormap_p = NearestColor((UINT8)RoundUp(map[i][0]), - (UINT8)RoundUp(map[i][1]), - (UINT8)RoundUp(map[i][2])); + *colormap_p = NearestColorFunc((UINT8)M_RoundUp(map[i][0]), + (UINT8)M_RoundUp(map[i][1]), + (UINT8)M_RoundUp(map[i][2])); colormap_p++; if ((UINT32)p < fadestart) continue; - - if (extra_colormap->flags & CMF_NEWFADE) - { - // Add/subtract towards the destination color. - if (fabs(map[i][0] - cdestr) <= brightChange[i]) - map[i][0] = cdestr; - else if (map[i][0] > cdestr) - map[i][0] -= brightChange[i]; - else - map[i][0] += brightChange[i]; - - if (fabs(map[i][1] - cdestg) <= brightChange[i]) - map[i][1] = cdestg; - else if (map[i][1] > cdestg) - map[i][1] -= brightChange[i]; - else - map[i][1] += brightChange[i]; - - if (fabs(map[i][2] - cdestb) <= brightChange[i]) - map[i][2] = cdestb; - else if (map[i][2] > cdestb) - map[i][2] -= brightChange[i]; - else - map[i][2] += brightChange[i]; - } + #define ABS2(x) ((x) < 0 ? -(x) : (x)) + if (ABS2(map[i][0] - cdestr) > ABS2(deltas[i][0])) + map[i][0] -= deltas[i][0]; else - { - if (fabs(map[i][0] - cdestr) > fabs(deltas[i][0])) - map[i][0] -= deltas[i][0]; - else - map[i][0] = cdestr; + map[i][0] = cdestr; - if (fabs(map[i][1] - cdestg) > fabs(deltas[i][1])) - map[i][1] -= deltas[i][1]; - else - map[i][1] = cdestg; + if (ABS2(map[i][1] - cdestg) > ABS2(deltas[i][1])) + map[i][1] -= deltas[i][1]; + else + map[i][1] = cdestg; - if (fabs(map[i][2] - cdestb) > fabs(deltas[i][1])) // typo alert! - map[i][2] -= deltas[i][2]; - else - map[i][2] = cdestb; - } - } - } - - if (encoremap) - { - lighttable_t *colormap_p2 = lighttable; - - for (p = 0; p < LIGHTLEVELS; p++) - { - for (i = 0; i < 256; i++) - { - *colormap_p = colormap_p2[encoremap[i]]; - colormap_p++; - } - colormap_p2 += 256; + if (ABS2(map[i][2] - cdestb) > ABS2(deltas[i][1])) + map[i][2] -= deltas[i][2]; + else + map[i][2] = cdestb; + #undef ABS2 } } } - - return lighttable; } extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3) @@ -841,7 +801,7 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3) UINT8 cr = 0, cg = 0, cb = 0, ca = 0, cfr = 0, cfg = 0, cfb = 0, cfa = 25; UINT32 fadestart = 0, fadeend = 31; UINT8 flags = 0; - INT32 rgba = 0, fadergba = 0x19000000; + INT32 rgba = 0, fadergba = 0xFF000000; #define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0) #define ALPHA2INT(x) (x >= 'a' && x <= 'z' ? x - 'a' : x >= 'A' && x <= 'Z' ? x - 'A' : x >= '0' && x <= '9' ? 25 : 0) @@ -849,13 +809,13 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3) // Get base colormap value // First alpha-only, then full value if (p1[0] >= 'a' && p1[0] <= 'z' && !p1[1]) - ca = (p1[0] - 'a'); + ca = ((p1[0] - 'a') * 102) / 10; else if (p1[0] == '#' && p1[1] >= 'a' && p1[1] <= 'z' && !p1[2]) - ca = (p1[1] - 'a'); + ca = ((p1[1] - 'a') * 102) / 10; else if (p1[0] >= 'A' && p1[0] <= 'Z' && !p1[1]) - ca = (p1[0] - 'A'); + ca = ((p1[0] - 'A') * 102) / 10; else if (p1[0] == '#' && p1[1] >= 'A' && p1[1] <= 'Z' && !p1[2]) - ca = (p1[1] - 'A'); + ca = ((p1[1] - 'A') * 102) / 10; else if (p1[0] == '#') { // For each subsequent value, the value before it must exist @@ -871,20 +831,20 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3) cb = ((HEX2INT(p1[5]) * 16) + HEX2INT(p1[6])); if (p1[7] >= 'a' && p1[7] <= 'z') - ca = (p1[7] - 'a'); + ca = ((p1[7] - 'a') * 102) / 10; else if (p1[7] >= 'A' && p1[7] <= 'Z') - ca = (p1[7] - 'A'); + ca = ((p1[7] - 'A') * 102) / 10; else - ca = 25; + ca = 255; } else - ca = 25; + ca = 255; } else - ca = 25; + ca = 255; } else - ca = 25; + ca = 255; } #define NUMFROMCHAR(c) (c >= '0' && c <= '9' ? c - '0' : 0) @@ -914,13 +874,13 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3) // Get fade (dark) colormap value // First alpha-only, then full value if (p3[0] >= 'a' && p3[0] <= 'z' && !p3[1]) - cfa = (p3[0] - 'a'); + cfa = ((p3[0] - 'a') * 102) / 10; else if (p3[0] == '#' && p3[1] >= 'a' && p3[1] <= 'z' && !p3[2]) - cfa = (p3[1] - 'a'); + cfa = ((p3[1] - 'a') * 102) / 10; else if (p3[0] >= 'A' && p3[0] <= 'Z' && !p3[1]) - cfa = (p3[0] - 'A'); + cfa = ((p3[0] - 'A') * 102) / 10; else if (p3[0] == '#' && p3[1] >= 'A' && p3[1] <= 'Z' && !p3[2]) - cfa = (p3[1] - 'A'); + cfa = ((p3[1] - 'A') * 102) / 10; else if (p3[0] == '#') { // For each subsequent value, the value before it must exist @@ -936,20 +896,20 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3) cfb = ((HEX2INT(p3[5]) * 16) + HEX2INT(p3[6])); if (p3[7] >= 'a' && p3[7] <= 'z') - cfa = (p3[7] - 'a'); + cfa = ((p3[7] - 'a') * 102) / 10; else if (p3[7] >= 'A' && p3[7] <= 'Z') - cfa = (p3[7] - 'A'); + cfa = ((p3[7] - 'A') * 102) / 10; else - cfa = 25; + cfa = 255; } else - cfa = 25; + cfa = 255; } else - cfa = 25; + cfa = 255; } else - cfa = 25; + cfa = 255; } #undef ALPHA2INT #undef HEX2INT @@ -1166,20 +1126,6 @@ UINT8 NearestPaletteColor(UINT8 r, UINT8 g, UINT8 b, RGBA_t *palette) return (UINT8)bestcolor; } -// Rounds off floating numbers and checks for 0 - 255 bounds -static int RoundUp(double number) -{ - if (number > 255.0l) - return 255; - if (number < 0.0l) - return 0; - - if ((int)number <= (int)(number - 0.5f)) - return (int)number + 1; - - return (int)number; -} - #ifdef EXTRACOLORMAPLUMPS const char *R_NameForColormap(extracolormap_t *extra_colormap) { diff --git a/src/r_data.h b/src/r_data.h index 0751ca1ee..03c1dc9a6 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -93,6 +93,7 @@ typedef enum TMCF_OVERRIDE = 1<<13, } textmapcolormapflags_t; +void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup); lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap); extracolormap_t * R_CreateColormapFromLinedef(char *p1, char *p2, char *p3); extracolormap_t* R_CreateColormap(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags); diff --git a/src/r_draw_column.cpp b/src/r_draw_column.cpp index 5aa3b9195..f9a3b9aae 100644 --- a/src/r_draw_column.cpp +++ b/src/r_draw_column.cpp @@ -589,9 +589,9 @@ static void R_DrawAffineColumnTemplate(drawcolumndata_t *dc) intptr_t frac; // Looks familiar. const intptr_t fracstep = dc->iscale; - const intptr_t heightmask = dc->sourcelength-1; // CPhipps - specify type - constexpr INT32 npow2min = -1; - const INT32 npow2max = dc->sourcelength; + //const intptr_t heightmask = dc->sourcelength-1; // CPhipps - specify type + //constexpr INT32 npow2min = -1; + //const INT32 npow2max = dc->sourcelength; // Framebuffer destination address. // SoM: MAGIC diff --git a/src/r_main.cpp b/src/r_main.cpp index 1f6e9f080..c5849d980 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -14,6 +14,7 @@ /// See tables.c, too. #include +#include #include "doomdef.h" #include "g_game.h" @@ -51,6 +52,9 @@ extern consvar_t cv_debugrender_freezebsp; // Fineangles in the SCREENWIDTH wide window. #define FIELDOFVIEW 2048 +// Double-precision Pi value +#define DOUBLE_PI 3.141592653589793 + // increment every time a check is made size_t validcount = 1; @@ -144,8 +148,6 @@ static CV_PossibleValue_t secbright_cons_t[] = {{0, "MIN"}, {255, "MAX"}, {0, NU static void Fov_OnChange(void); -consvar_t cv_tailspickup = CVAR_INIT ("tailspickup", "On", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL); - // if enabled, load all graphics at level load consvar_t cv_precachetextures = CVAR_INIT ("precachetextures", "On", CV_SAVE, CV_OnOff, NULL); @@ -202,9 +204,6 @@ consvar_t cv_fakerollangle = CVAR_INIT ("fakerollangle", "Off", CV_SAVE, CV_OnOf consvar_t cv_affineprescale = CVAR_INIT ("affineprescale", "Off", CV_SAVE, CV_OnOff, NULL); consvar_t cv_affinemosaic = CVAR_INIT ("affinemosaic", "Off", CV_SAVE, CV_OnOff, NULL); -static CV_PossibleValue_t affineangle_cons_t[] = {{0, "MIN"}, {360 * FRACUNIT, "MAX"}, {0, NULL}}; -static CV_PossibleValue_t affinetest_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Auto"}, {0, NULL}}; - void SplitScreen_OnChange(void) { UINT8 i; @@ -390,6 +389,29 @@ angle_t R_PointToAngle2(fixed_t pviewx, fixed_t pviewy, fixed_t x, fixed_t y) 0; } +#define ANGLE_180_DOUBLE (static_cast(ANGLE_180)) +#define ANGLE_360_DOUBLE (ANGLE_180_DOUBLE * 2) + +// For absolute accuracy. Please don't use this outside of renderers. +angle_t R_PointToAngleFloat2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + double deltax = FixedToDouble(x2 - x1); + double deltay = FixedToDouble(y2 - y1); + + // Converted to degrees + double _arctan = (atan2(deltay, deltax) * ANGLE_180_DOUBLE) / DOUBLE_PI; + + while (_arctan < 0) + { + _arctan += ANGLE_360_DOUBLE; + } + + return static_cast(_arctan); +} + +#undef ANGLE_180_DOUBLE +#undef ANGLE_360_DOUBLE + // // R_ScaleFromGlobalAngle // Returns the texture mapping scale for the current line (horizontal span) @@ -1773,7 +1795,6 @@ void R_RegisterEngineStuff(void) UINT8 i; CV_RegisterVar(&cv_gravity); - CV_RegisterVar(&cv_tailspickup); CV_RegisterVar(&cv_allowmlook); CV_RegisterVar(&cv_homremoval); diff --git a/src/r_main.h b/src/r_main.h index 26bcecf97..d5af7669a 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -83,9 +83,11 @@ FUNCINLINE static ATTRINLINE INT32 R_PointOnSideFast(fixed_t x, fixed_t y, const INT32 R_PointOnSegSide(fixed_t x, fixed_t y, const seg_t *line); #define R_PointToAngle(x, y) R_PointToAngle2(viewx, viewy, x, y) +#define R_PointToAngleFloat(x, y) (R_PointToAngleFloat2(viewx, viewy, x, y)) angle_t R_PointToAnglePlayer(player_t *player, fixed_t x, fixed_t y); angle_t R_PointToAngle64(INT64 x, INT64 y); angle_t R_PointToAngle2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1); +angle_t R_PointToAngleFloat2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); #define R_PointToDist(x, y) R_PointToDist2(viewx, viewy, x, y) #define R_PointToDist2(px2, py2, px1, py1) FixedHypot((px1) - (px2), (py1) - (py2)) @@ -153,7 +155,6 @@ extern consvar_t cv_drawdist, cv_drawdist_precip; extern consvar_t cv_playerfade; extern consvar_t cv_fov[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_skybox; -extern consvar_t cv_tailspickup; extern consvar_t cv_debugfinishline; extern consvar_t cv_secbright; diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c index ca854dcaf..8d7b08355 100644 --- a/src/r_patchrotation.c +++ b/src/r_patchrotation.c @@ -38,8 +38,12 @@ angle_t R_GetPitchRollAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstate_ } else { - // Regular Mobjs don't air tilt. - viewingAngle = mobj->angle; + // For regular objects, use the camera; just not the *player's* camera. + //... Unless you are a Nights bumber... + if (mobj->type == MT_NIGHTSBUMPER) + viewingAngle = mobj->angle; + else + viewingAngle = R_PointToAngleFloat(mobj->x, mobj->y); } pitchMul = -FINESINE(viewingAngle >> ANGLETOFINESHIFT); @@ -164,6 +168,7 @@ vector2_t* R_RotateSpriteOffsetsByPitchRoll( { fixed_t rotcos, rotsin, finx, finy; vector2_t xvec, yvec; + (void)affine; // input offsets fixed_t xoffs, yoffs, xpiv, ypiv; diff --git a/src/r_things.cpp b/src/r_things.cpp index 289f97c95..15843c241 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2081,7 +2081,7 @@ static void R_ProjectSprite(mobj_t *thing) // Affines boolean affinesprite = (((thing->player != NULL) || R_ThingIsAffineSprite(thing)) && (!splat)); affine_t affine_transform = {0}; - affine_bounding_t affine_bounds = {0}; + affine_bounding_t affine_bounds = {}; vector2_t affine_scale = {0}; vector2_t affine_distscale = {0}; f_vector2_t affine_pivotoffsetdiff = {0}; @@ -2928,7 +2928,7 @@ static void R_ProjectSprite(mobj_t *thing) vis->viewpoint.z = viewz; vis->viewpoint.angle = viewangle; - vis->affine = {0}; + vis->affine = {}; if (affinesprite) { diff --git a/src/w_wad.c b/src/w_wad.c index b2ca0d1d5..aab5e5f58 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -625,21 +625,13 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen { const char *temp = trimname-1; // CONS_Printf("wadnumtemp: %s\n", temp); -#if (defined(_WIN32)) while (temp >= filename+5 && *temp != PATHSEP[0]) temp--; if (((trimname-1) - temp) == 8 && temp >= filename+5 - && !strncmp(temp-5, PATHSEP"Temp", 5)) -#elif (defined(__linux__)) - while (temp >= filename+4 && *temp != PATHSEP[0]) - temp--; - - if (((trimname-1) - temp) == 8 - && temp >= filename+4 - && !strncmp(temp-4, PATHSEP"tmp", 4)) -#endif + && (!strncmp(temp-5, PATHSEP"Temp", 5) + || !strncmp(temp-4, PATHSEP"tmp", 4))) { filename = wadfiles[ ((wadnamelump & ~UINT16_MAX) >> 16)