From 0b1108425d4a95815eaf6bf0fdf2c9aaa10b8683 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 8 Jan 2020 20:49:14 +0000 Subject: [PATCH] Hexafaceted sprite angle support (See merge request description coming soon). Also, some minor memory efficiency fixes to both rotsprite and ordinary sprite rotations. --- src/hardware/hw_main.c | 23 ++++----- src/lua_hudlib.c | 8 ++-- src/r_data.c | 27 ++++++++--- src/r_defs.h | 26 ++++++----- src/r_patch.c | 10 ++-- src/r_things.c | 104 +++++++++++++++++++++++++---------------- src/r_things.h | 27 ++++++++--- 7 files changed, 136 insertions(+), 89 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 2f986085a..80ca623ca 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5493,7 +5493,7 @@ static void HWR_ProjectSprite(mobj_t *thing) spriteinfo_t *sprinfo; size_t lumpoff; unsigned rot; - UINT8 flip; + UINT16 flip; boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP)); angle_t ang; @@ -5579,12 +5579,7 @@ static void HWR_ProjectSprite(mobj_t *thing) flip = sprframe->flip; // Will only be 0x00 or 0xFF if (papersprite && ang < ANGLE_180) - { - if (flip) - flip = 0; - else - flip = 255; - } + flip ^= 0xFFFF; } else { @@ -5593,6 +5588,11 @@ static void HWR_ProjectSprite(mobj_t *thing) rot = 6; // F7 slot else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left rot = 2; // F3 slot + else if (sprframe->rotate & SRF_3DGE) // 16-angle mode + { + rot = (ang+ANGLE_180+ANGLE_11hh)>>28; + rot = ((rot & 1)<<3)|(rot>>1); + } else // Normal behaviour rot = (ang+ANGLE_202h)>>29; @@ -5601,12 +5601,7 @@ static void HWR_ProjectSprite(mobj_t *thing) flip = sprframe->flip & (1<skin && ((skin_t *)thing->skin)->flags & SF_HIRES) @@ -5621,7 +5616,7 @@ static void HWR_ProjectSprite(mobj_t *thing) if (thing->rollangle) { rollangle = R_GetRollAngle(thing->rollangle); - if (!sprframe->rotsprite.cached[rot]) + if (!(sprframe->rotsprite.cached & (1<sprite, (thing->frame & FF_FRAMEMASK), sprinfo, sprframe, rot, flip); rotsprite = sprframe->rotsprite.patch[rot][rollangle]; if (rotsprite != NULL) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 58af64359..f451944e3 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -468,11 +468,11 @@ static int libd_getSpritePatch(lua_State *L) // convert WAD editor angle numbers (1-8) to internal angle numbers (0-7) // keep 0 the same since we'll make it default to angle 1 (which is internally 0) - // in case somebody didn't know that angle 0 really just maps all 8 angles to the same patch + // in case somebody didn't know that angle 0 really just maps all 8/16 angles to the same patch if (angle != 0) angle--; - if (angle >= 8) // out of range? + if (angle >= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range? return 0; // push both the patch and it's "flip" value @@ -563,11 +563,11 @@ static int libd_getSprite2Patch(lua_State *L) // convert WAD editor angle numbers (1-8) to internal angle numbers (0-7) // keep 0 the same since we'll make it default to angle 1 (which is internally 0) - // in case somebody didn't know that angle 0 really just maps all 8 angles to the same patch + // in case somebody didn't know that angle 0 really just maps all 8/16 angles to the same patch if (angle != 0) angle--; - if (angle >= 8) // out of range? + if (angle >= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range? return 0; // push both the patch and it's "flip" value diff --git a/src/r_data.c b/src/r_data.c index 986b65dea..26fc0043c 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -2710,14 +2710,29 @@ void R_PrecacheLevel(void) for (j = 0; j < sprites[i].numframes; j++) { sf = &sprites[i].spriteframes[j]; - for (k = 0; k < 8; k++) +#define cacheang(a) {\ + lump = sf->lumppat[a];\ + if (devparm)\ + spritememory += W_LumpLength(lump);\ + W_CachePatchNum(lump, PU_PATCH);\ + } + // see R_InitSprites for more about lumppat,lumpid + switch (sf->rotate) { - // see R_InitSprites for more about lumppat,lumpid - lump = sf->lumppat[k]; - if (devparm) - spritememory += W_LumpLength(lump); - W_CachePatchNum(lump, PU_PATCH); + case SRF_SINGLE: + cacheang(0); + break; + case SRF_2D: + cacheang(2); + cacheang(6); + break; + default: + k = (sf->rotate & SRF_3DGE ? 16 : 8); + while (k--) + cacheang(k); + break; } +#undef cacheang } } free(spritepresent); diff --git a/src/r_defs.h b/src/r_defs.h index c7c198d66..a841bca08 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -727,10 +727,10 @@ typedef struct #ifdef ROTSPRITE typedef struct { - patch_t *patch[8][ROTANGLES]; - boolean cached[8]; + patch_t *patch[16][ROTANGLES]; + UINT16 cached; #ifdef HWRENDER - aatree_t *hardware_patch[8]; + aatree_t *hardware_patch[16]; #endif/*HWRENDER*/ } rotsprite_t; #endif/*ROTSPRITE*/ @@ -739,9 +739,11 @@ typedef enum { SRF_SINGLE = 0, // 0-angle for all rotations SRF_3D = 1, // Angles 1-8 - SRF_LEFT = 2, // Left side uses single patch - SRF_RIGHT = 4, // Right side uses single patch - SRF_2D = SRF_LEFT|SRF_RIGHT, // 6 + SRF_3DGE = 2, // 3DGE, ZDoom and Doom Legacy all have 16-angle support. Why not us? + SRF_3DMASK = SRF_3D|SRF_3DGE, // 3 + SRF_LEFT = 4, // Left side uses single patch + SRF_RIGHT = 8, // Right side uses single patch + SRF_2D = SRF_LEFT|SRF_RIGHT, // 12 SRF_NONE = 0xff // Initial value } spriterotateflags_t; // SRF's up! @@ -767,7 +769,7 @@ typedef struct patchinfo_s patchinfo_t; // Sprites are patches with a special naming convention so they can be // recognized by R_InitSprites. // The base name is NNNNFx or NNNNFxFx, with x indicating the rotation, -// x = 0, 1-8, L/R +// x = 0, 1-8, 9+A-G, L/R // The sprite and frame specified by a thing_t is range checked at run time. // A sprite is a patch_t that is assumed to represent a three dimensional // object and may have multiple rotations predrawn. @@ -784,12 +786,12 @@ typedef struct // name eight times. UINT8 rotate; // see spriterotateflags_t above - // Lump to use for view angles 0-7. - lumpnum_t lumppat[8]; // lump number 16 : 16 wad : lump - size_t lumpid[8]; // id in the spriteoffset, spritewidth, etc. tables + // Lump to use for view angles 0-7/15. + lumpnum_t lumppat[16]; // lump number 16 : 16 wad : lump + size_t lumpid[16]; // id in the spriteoffset, spritewidth, etc. tables - // Flip bits (1 = flip) to use for view angles 0-7. - UINT8 flip; + // Flip bits (1 = flip) to use for view angles 0-7/15. + UINT16 flip; #ifdef ROTSPRITE rotsprite_t rotsprite; diff --git a/src/r_patch.c b/src/r_patch.c index fbb64e362..7fb764d22 100644 --- a/src/r_patch.c +++ b/src/r_patch.c @@ -1159,7 +1159,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp #define ROTSPRITE_XCENTER (newwidth / 2) #define ROTSPRITE_YCENTER (newheight / 2) - if (!sprframe->rotsprite.cached[rot]) + if (!(sprframe->rotsprite.cached & (1<rotsprite.cached[rot] = true; + sprframe->rotsprite.cached |= (1<numframes; frame++) { spriteframe_t *sprframe = &spritedef->spriteframes[frame]; - for (rot = 0; rot < 8; rot++) + for (rot = 0; rot < 16; rot++) { - if (sprframe->rotsprite.cached[rot]) + if (sprframe->rotsprite.cached & (1<rotsprite.cached[rot] = false; + sprframe->rotsprite.cached &= ~(1<= 64 || !(R_ValidSpriteAngle(rotation))) - I_Error("R_InstallSpriteLump: Bad frame characters in lump %s", W_CheckNameForNum(lumppat)); - if (maxframe ==(size_t)-1 || frame > maxframe) maxframe = frame; // rotsprite #ifdef ROTSPRITE - for (r = 0; r < 8; r++) + sprtemp[frame].rotsprite.cached = 0; + for (r = 0; r < 16; r++) { - sprtemp[frame].rotsprite.cached[r] = false; for (ang = 0; ang < ROTANGLES; ang++) sprtemp[frame].rotsprite.patch[r][ang] = NULL; #ifdef HWRENDER @@ -136,16 +133,16 @@ static void R_InstallSpriteLump(UINT16 wad, // graphics patch // the lump should be used for all rotations if (sprtemp[frame].rotate == SRF_SINGLE) CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has multiple rot = 0 lump\n", spritename, cn); - else if (sprtemp[frame].rotate != SRF_NONE) // Let's bundle 1-8 and L/R rotations into one debug message. + else if (sprtemp[frame].rotate != SRF_NONE) // Let's bundle 1-8/16 and L/R rotations into one debug message. CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has rotations and a rot = 0 lump\n", spritename, cn); sprtemp[frame].rotate = SRF_SINGLE; - for (r = 0; r < 8; r++) + for (r = 0; r < 16; r++) { sprtemp[frame].lumppat[r] = lumppat; sprtemp[frame].lumpid[r] = lumpid; } - sprtemp[frame].flip = flipped ? 0xFF : 0; // 11111111 in binary + sprtemp[frame].flip = flipped ? 0xFFFF : 0; // 1111111111111111 in binary return; } @@ -154,54 +151,67 @@ static void R_InstallSpriteLump(UINT16 wad, // graphics patch UINT8 rightfactor = ((rotation == ROT_R) ? 4 : 0); // the lump should be used for half of all rotations - if (sprtemp[frame].rotate == SRF_SINGLE) + if (sprtemp[frame].rotate == SRF_NONE) + sprtemp[frame].rotate = SRF_SINGLE; + else if (sprtemp[frame].rotate == SRF_SINGLE) CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has L/R rotations and a rot = 0 lump\n", spritename, cn); else if (sprtemp[frame].rotate == SRF_3D) CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has both L/R and 1-8 rotations\n", spritename, cn); + else if (sprtemp[frame].rotate == SRF_3DGE) + CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has both L/R and 1-G rotations\n", spritename, cn); else if ((sprtemp[frame].rotate & SRF_LEFT) && (rotation == ROT_L)) CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has multiple L rotations\n", spritename, cn); else if ((sprtemp[frame].rotate & SRF_RIGHT) && (rotation == ROT_R)) CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has multiple R rotations\n", spritename, cn); - if (sprtemp[frame].rotate == SRF_NONE) - sprtemp[frame].rotate = SRF_SINGLE; - sprtemp[frame].rotate |= ((rotation == ROT_R) ? SRF_RIGHT : SRF_LEFT); - if (sprtemp[frame].rotate == (SRF_3D|SRF_2D)) - sprtemp[frame].rotate = SRF_2D; // SRF_3D|SRF_2D being enabled at the same time doesn't HURT in the current sprite angle implementation, but it DOES mean more to check in some of the helper functions. Let's not allow this scenario to happen. + if ((sprtemp[frame].rotate & SRF_2D) == SRF_2D) + sprtemp[frame].rotate &= ~SRF_3DMASK; // SRF_3D|SRF_2D being enabled at the same time doesn't HURT in the current sprite angle implementation, but it DOES mean more to check in some of the helper functions. Let's not allow this scenario to happen. - for (r = 0; r < 4; r++) // Thanks to R_PrecacheLevel, we can't leave sprtemp[*].lumppat[*] == LUMPERROR... so we load into the front/back angle too. + // load into every relevant angle, including the front one + for (r = 0; r < 4; r++) { sprtemp[frame].lumppat[r + rightfactor] = lumppat; sprtemp[frame].lumpid[r + rightfactor] = lumpid; + sprtemp[frame].lumppat[r + rightfactor + 8] = lumppat; + sprtemp[frame].lumpid[r + rightfactor + 8] = lumpid; + } if (flipped) - sprtemp[frame].flip |= (0x0F< 3) // Right side - sprtemp[frame].rotate = (SRF_3D | (sprtemp[frame].rotate & SRF_LEFT)); // Continue allowing L frame changeover - else // if (rotation <= 3) // Left side - sprtemp[frame].rotate = (SRF_3D | (sprtemp[frame].rotate & SRF_RIGHT)); // Continue allowing R frame changeover + { + // SRF_3D|SRF_3DGE being enabled at the same time doesn't HURT in the current sprite angle implementation, but it DOES mean more to check in some of the helper functions. Let's not allow this scenario to happen. + UINT8 threedrot = (rotation > 7) ? SRF_3DGE : (sprtemp[frame].rotate & SRF_3DMASK); + if (!threedrot) + threedrot = SRF_3D; + + if (rotation == 0 || rotation == 4) // Front or back... + sprtemp[frame].rotate = threedrot; // Prevent L and R changeover + else if ((rotation & 7) > 3) // Right side + sprtemp[frame].rotate = (threedrot | (sprtemp[frame].rotate & SRF_LEFT)); // Continue allowing L frame changeover + else // if ((rotation & 7) <= 3) // Left side + sprtemp[frame].rotate = (threedrot | (sprtemp[frame].rotate & SRF_RIGHT)); // Continue allowing R frame changeover + } if (sprtemp[frame].lumppat[rotation] != LUMPERROR) - CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s: %c%c has two lumps mapped to it\n", spritename, cn, '1'+rotation); + CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s: %c%c has two lumps mapped to it\n", spritename, cn, cr); // lumppat & lumpid are the same for original Doom, but different // when using sprites in pwad : the lumppat points the new graphics @@ -262,9 +272,9 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, if (memcmp(lumpinfo[l].name,sprname,4)==0) { frame = R_Char2Frame(lumpinfo[l].name[4]); - rotation = (UINT8)(lumpinfo[l].name[5] - '0'); + rotation = R_Char2Rotation(lumpinfo[l].name[5]); - if (frame >= 64 || !(R_ValidSpriteAngle(rotation))) // Give an actual NAME error -_-... + if (frame >= 64 || rotation == 255) // Give an actual NAME error -_-... { CONS_Alert(CONS_WARNING, M_GetText("Bad sprite name: %s\n"), W_CheckNameForNumPwad(wadnum,l)); continue; @@ -311,7 +321,13 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, if (lumpinfo[l].name[6]) { frame = R_Char2Frame(lumpinfo[l].name[6]); - rotation = (UINT8)(lumpinfo[l].name[7] - '0'); + rotation = R_Char2Rotation(lumpinfo[l].name[7]); + + if (frame >= 64 || rotation == 255) // Give an actual NAME error -_-... + { + CONS_Alert(CONS_WARNING, M_GetText("Bad sprite name: %s\n"), W_CheckNameForNumPwad(wadnum,l)); + continue; + } R_InstallSpriteLump(wadnum, l, numspritelumps, frame, rotation, 1); } @@ -372,18 +388,19 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, case SRF_2D: // both Left and Right rotations // we test to see whether the left and right slots are present if ((sprtemp[frame].lumppat[2] == LUMPERROR) || (sprtemp[frame].lumppat[6] == LUMPERROR)) - I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations", + I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations (L-R mode)", sprname, R_Frame2Char(frame)); break; default: - // must have all 8 frames - for (rotation = 0; rotation < 8; rotation++) + // must have all 8/16 frames + rotation = ((sprtemp[frame].rotate & SRF_3DGE) ? 16 : 8); + while (rotation--) // we test the patch lump, or the id lump whatever // if it was not loaded the two are LUMPERROR if (sprtemp[frame].lumppat[rotation] == LUMPERROR) - I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations", - sprname, R_Frame2Char(frame)); + I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations (1-%c mode)", + sprname, R_Frame2Char(frame), ((sprtemp[frame].rotate & SRF_3DGE) ? 'G' : '8')); break; } } @@ -1097,7 +1114,7 @@ static void R_ProjectSprite(mobj_t *thing) size_t lump; size_t rot; - UINT8 flip; + UINT16 flip; boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP)); INT32 lindex; @@ -1227,7 +1244,7 @@ static void R_ProjectSprite(mobj_t *thing) // use single rotation for all views rot = 0; //Fab: for vis->patch below lump = sprframe->lumpid[0]; //Fab: see note above - flip = sprframe->flip; // Will only be 0x00 or 0xFF + flip = sprframe->flip; // Will only be 0 or 0xFFFF } else { @@ -1238,6 +1255,11 @@ static void R_ProjectSprite(mobj_t *thing) rot = 6; // F7 slot else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left rot = 2; // F3 slot + else if (sprframe->rotate & SRF_3DGE) // 16-angle mode + { + rot = (ang+ANGLE_180+ANGLE_11hh)>>28; + rot = ((rot & 1)<<3)|(rot>>1); + } else // Normal behaviour rot = (ang+ANGLE_202h)>>29; @@ -1260,7 +1282,7 @@ static void R_ProjectSprite(mobj_t *thing) if (thing->rollangle) { rollangle = R_GetRollAngle(thing->rollangle); - if (!sprframe->rotsprite.cached[rot]) + if (!(sprframe->rotsprite.cached & (1<sprite, (thing->frame & FF_FRAMEMASK), sprinfo, sprframe, rot, flip); rotsprite = sprframe->rotsprite.patch[rot][rollangle]; if (rotsprite != NULL) diff --git a/src/r_things.h b/src/r_things.h index 8e4a543c3..b4ef66a62 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -20,10 +20,6 @@ #include "r_portal.h" #include "r_defs.h" -// "Left" and "Right" character symbols for additional rotation functionality -#define ROT_L ('L' - '0') -#define ROT_R ('R' - '0') - // number of sprite lumps for spritewidth,offset,topoffset lookup tables // Fab: this is a hack : should allocate the lookup tables per sprite #define MAXVISSPRITES 2048 // added 2-2-98 was 128 @@ -270,7 +266,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE UINT8 R_Char2Frame(char cn) if (cn == '+') return '\\' - 'A'; // PK3 can't use backslash, so use + instead return cn - 'A'; #else - if (cn >= 'A' && cn <= 'Z') return cn - 'A'; + if (cn >= 'A' && cn <= 'Z') return (cn - 'A'); if (cn >= '0' && cn <= '9') return (cn - '0') + 26; if (cn >= 'a' && cn <= 'z') return (cn - 'a') + 36; if (cn == '!') return 62; @@ -279,9 +275,26 @@ FUNCMATH FUNCINLINE static ATTRINLINE UINT8 R_Char2Frame(char cn) #endif } -FUNCMATH FUNCINLINE static ATTRINLINE boolean R_ValidSpriteAngle(UINT8 rotation) +// "Left" and "Right" character symbols for additional rotation functionality +#define ROT_L 17 +#define ROT_R 18 + +FUNCMATH FUNCINLINE static ATTRINLINE char R_Rotation2Char(UINT8 rot) { - return ((rotation <= 8) || (rotation == ROT_L) || (rotation == ROT_R)); + if (rot <= 9) return '0' + rot; + if (rot <= 16) return 'A' + (rot - 10); + if (rot == ROT_L) return 'L'; + if (rot == ROT_R) return 'R'; + return '\xFF'; +} + +FUNCMATH FUNCINLINE static ATTRINLINE UINT8 R_Char2Rotation(char cn) +{ + if (cn >= '0' && cn <= '9') return (cn - '0'); + if (cn >= 'A' && cn <= 'G') return (cn - 'A') + 10; + if (cn == 'L') return ROT_L; + if (cn == 'R') return ROT_R; + return 255; } #endif //__R_THINGS__