// R_things.c #include #include #include "DoomDef.h" #include "R_local.h" void R_DrawColumn (void); void R_DrawFuzzColumn (void); typedef struct { int x1, x2; int column; int topclip; int bottomclip; } maskdraw_t; /* Sprite rotation 0 is facing the viewer, rotation 1 is one angle turn CLOCKWISE around the axis. This is not the same as the angle, which increases counter clockwise (protractor). There was a lot of stuff grabbed wrong, so I changed it... */ fixed_t pspritescale, pspriteiscale; lighttable_t **spritelights; // constant arrays used for psprite clipping and initializing clipping short negonearray[SCREENWIDTH]; short screenheightarray[SCREENWIDTH]; /* =============================================================================== INITIALIZATION FUNCTIONS =============================================================================== */ // variables used to look up and range check thing_t sprites patches spritedef_t *sprites; int numsprites; spriteframe_t sprtemp[26]; int maxframe; char *spritename; /* ================= = = R_InstallSpriteLump = = Local function for R_InitSprites ================= */ void R_InstallSpriteLump (int lump, unsigned frame, unsigned rotation, boolean flipped) { int r; if (frame >= 26 || rotation > 8) I_Error ("R_InstallSpriteLump: Bad frame characters in lump %i", lump); if ((int)frame > maxframe) maxframe = frame; if (rotation == 0) { // the lump should be used for all rotations if (sprtemp[frame].rotate == false) I_Error ("R_InitSprites: Sprite %s frame %c has multip rot=0 lump" , spritename, 'A'+frame); if (sprtemp[frame].rotate == true) I_Error ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump" , spritename, 'A'+frame); sprtemp[frame].rotate = false; for (r=0 ; r<8 ; r++) { sprtemp[frame].lump[r] = lump - firstspritelump; sprtemp[frame].flip[r] = (byte)flipped; } return; } // the lump is only used for one rotation if (sprtemp[frame].rotate == false) I_Error ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump" , spritename, 'A'+frame); sprtemp[frame].rotate = true; rotation--; // make 0 based if (sprtemp[frame].lump[rotation] != -1) I_Error ("R_InitSprites: Sprite %s : %c : %c has two lumps mapped to it" ,spritename, 'A'+frame, '1'+rotation); sprtemp[frame].lump[rotation] = lump - firstspritelump; sprtemp[frame].flip[rotation] = (byte)flipped; } /* ================= = = R_InitSpriteDefs = = Pass a null terminated list of sprite names (4 chars exactly) to be used = Builds the sprite rotation matrixes to account for horizontally flipped = sprites. Will report an error if the lumps are inconsistant = Only called at startup = = Sprite lump names are 4 characters for the actor, a letter for the frame, = and a number for the rotation, A sprite that is flippable will have an = additional letter/number appended. The rotation character can be 0 to = signify no rotations ================= */ void R_InitSpriteDefs (char **namelist) { char **check; int i, l, intname, frame, rotation; int start, end; // count the number of sprite names check = namelist; while (*check != NULL) check++; numsprites = check-namelist; if (!numsprites) return; sprites = Z_Malloc(numsprites *sizeof(*sprites), PU_STATIC, NULL); start = firstspritelump-1; end = lastspritelump+1; // scan all the lump names for each of the names, noting the highest // frame letter // Just compare 4 characters as ints for (i=0 ; itopdelta != 0xff ; ) { // calculate unclipped screen coordinates for post topscreen = sprtopscreen + spryscale*column->topdelta; bottomscreen = topscreen + spryscale*column->length; dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS; dc_yh = (bottomscreen-1)>>FRACBITS; if (dc_yh >= mfloorclip[dc_x]) dc_yh = mfloorclip[dc_x]-1; if (dc_yl <= mceilingclip[dc_x]) dc_yl = mceilingclip[dc_x]+1; if(dc_yh >= baseclip && baseclip != -1) dc_yh = baseclip; if (dc_yl <= dc_yh) { dc_source = (byte *)column + 3; dc_texturemid = basetexturemid - (column->topdelta<topdelta; colfunc (); // either R_DrawColumn or R_DrawFuzzColumn } column = (column_t *)( (byte *)column + column->length + 4); } dc_texturemid = basetexturemid; } /* ================ = = R_DrawVisSprite = = mfloorclip and mceilingclip should also be set ================ */ void R_DrawVisSprite (vissprite_t *vis, int x1, int x2) { column_t *column; int texturecolumn; fixed_t frac; patch_t *patch; fixed_t baseclip; patch = W_CacheLumpNum(vis->patch+firstspritelump, PU_CACHE); dc_colormap = vis->colormap; // if(!dc_colormap) // colfunc = fuzzcolfunc; // NULL colormap = shadow draw if(vis->mobjflags&MF_SHADOW) { if(vis->mobjflags&MF_TRANSLATION) { colfunc = R_DrawTranslatedFuzzColumn; dc_translation = translationtables - 256 + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT-8)); } else { // Draw using shadow column function colfunc = fuzzcolfunc; } } else if(vis->mobjflags&MF_TRANSLATION) { // Draw using translated column function colfunc = R_DrawTranslatedColumn; dc_translation = translationtables - 256 + ( (vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT-8) ); } dc_iscale = abs(vis->xiscale)>>detailshift; dc_texturemid = vis->texturemid; frac = vis->startfrac; spryscale = vis->scale; sprtopscreen = centeryfrac - FixedMul(dc_texturemid,spryscale); // check to see if weapon is a vissprite if(vis->psprite) { dc_texturemid += FixedMul(((centery-viewheight/2)<xiscale); sprtopscreen += (viewheight/2-centery)<footclip && !vis->psprite) { sprbotscreen = sprtopscreen+FixedMul(patch->height<footclip<>FRACBITS; } else { baseclip = -1; } for (dc_x=vis->x1 ; dc_x<=vis->x2 ; dc_x++, frac += vis->xiscale) { texturecolumn = frac>>FRACBITS; #ifdef RANGECHECK if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) I_Error ("R_DrawSpriteRange: bad texturecolumn"); #endif column = (column_t *) ((byte *)patch + LONG(patch->columnofs[texturecolumn])); R_DrawMaskedColumn (column, baseclip); } colfunc = basecolfunc; } /* =================== = = R_ProjectSprite = = Generates a vissprite for a thing if it might be visible = =================== */ void R_ProjectSprite (mobj_t *thing) { fixed_t trx,try; fixed_t gxt,gyt; fixed_t tx,tz; fixed_t xscale; int x1, x2; spritedef_t *sprdef; spriteframe_t *sprframe; int lump; unsigned rot; boolean flip; int index; vissprite_t *vis; angle_t ang; fixed_t iscale; if(thing->flags2&MF2_DONTDRAW) { // Never make a vissprite when MF2_DONTDRAW is flagged. return; } // // transform the origin point // trx = thing->x - viewx; try = thing->y - viewy; gxt = FixedMul(trx,viewcos); gyt = -FixedMul(try,viewsin); tz = gxt-gyt; if (tz < MINZ) return; // thing is behind view plane xscale = FixedDiv(projection, tz); gxt = -FixedMul(trx,viewsin); gyt = FixedMul(try,viewcos); tx = -(gyt+gxt); if (abs(tx)>(tz<<2)) return; // too far off the side // // decide which patch to use for sprite reletive to player // #ifdef RANGECHECK if ((unsigned)thing->sprite >= numsprites) I_Error ("R_ProjectSprite: invalid sprite number %i ",thing->sprite); #endif sprdef = &sprites[thing->sprite]; #ifdef RANGECHECK if ( (thing->frame&FF_FRAMEMASK) >= sprdef->numframes ) I_Error ("R_ProjectSprite: invalid sprite frame %i : %i " ,thing->sprite, thing->frame); #endif sprframe = &sprdef->spriteframes[ thing->frame & FF_FRAMEMASK]; if (sprframe->rotate) { // choose a different rotation based on player view ang = R_PointToAngle (thing->x, thing->y); rot = (ang-thing->angle+(unsigned)(ANG45/2)*9)>>29; lump = sprframe->lump[rot]; flip = (boolean)sprframe->flip[rot]; } else { // use single rotation for all views lump = sprframe->lump[0]; flip = (boolean)sprframe->flip[0]; } // // calculate edges of the shape // tx -= spriteoffset[lump]; x1 = (centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS; if (x1 > viewwidth) return; // off the right side tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS) - 1; if (x2 < 0) return; // off the left side // // store information in a vissprite // vis = R_NewVisSprite (); vis->mobjflags = thing->flags; vis->psprite = false; vis->scale = xscale<gx = thing->x; vis->gy = thing->y; vis->gz = thing->z; vis->gzt = thing->z + spritetopoffset[lump]; // foot clipping if(thing->flags2&MF2_FEETARECLIPPED && thing->z <= thing->subsector->sector->floorheight) { vis->footclip = 10; } else vis->footclip = 0; vis->texturemid = vis->gzt - viewz - (vis->footclip<x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; iscale = FixedDiv (FRACUNIT, xscale); if (flip) { vis->startfrac = spritewidth[lump]-1; vis->xiscale = -iscale; } else { vis->startfrac = 0; vis->xiscale = iscale; } if (vis->x1 > x1) vis->startfrac += vis->xiscale*(vis->x1-x1); vis->patch = lump; // // get light level // // if (thing->flags & MF_SHADOW) // vis->colormap = NULL; // shadow draw // else ... if (fixedcolormap) vis->colormap = fixedcolormap; // fixed map else if (thing->frame & FF_FULLBRIGHT) vis->colormap = colormaps; // full bright else { // diminished light index = xscale>>(LIGHTSCALESHIFT-detailshift); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE-1; vis->colormap = spritelights[index]; } } /* ======================== = = R_AddSprites = ======================== */ void R_AddSprites (sector_t *sec) { mobj_t *thing; int lightnum; if (sec->validcount == validcount) return; // already added sec->validcount = validcount; lightnum = (sec->lightlevel >> LIGHTSEGSHIFT)+extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS-1]; else spritelights = scalelight[lightnum]; for (thing = sec->thinglist ; thing ; thing = thing->snext) R_ProjectSprite (thing); } /* ======================== = = R_DrawPSprite = ======================== */ int PSpriteSY[NUMWEAPONS] = { 0, // staff 5*FRACUNIT, // goldwand 15*FRACUNIT, // crossbow 15*FRACUNIT, // blaster 15*FRACUNIT, // skullrod 15*FRACUNIT, // phoenix rod 15*FRACUNIT, // mace 15*FRACUNIT, // gauntlets 15*FRACUNIT // beak }; void R_DrawPSprite (pspdef_t *psp) { fixed_t tx; int x1, x2; spritedef_t *sprdef; spriteframe_t *sprframe; int lump; boolean flip; vissprite_t *vis, avis; int tempangle; // // decide which patch to use // #ifdef RANGECHECK if ( (unsigned)psp->state->sprite >= numsprites) I_Error ("R_ProjectSprite: invalid sprite number %i " , psp->state->sprite); #endif sprdef = &sprites[psp->state->sprite]; #ifdef RANGECHECK if ( (psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) I_Error ("R_ProjectSprite: invalid sprite frame %i : %i " , psp->state->sprite, psp->state->frame); #endif sprframe = &sprdef->spriteframes[ psp->state->frame & FF_FRAMEMASK ]; lump = sprframe->lump[0]; flip = (boolean)sprframe->flip[0]; // // calculate edges of the shape // tx = psp->sx-160*FRACUNIT; tx -= spriteoffset[lump]; if(viewangleoffset) { tempangle = ((centerxfrac/1024)*(viewangleoffset>>ANGLETOFINESHIFT)); } else { tempangle = 0; } x1 = (centerxfrac + FixedMul (tx,pspritescale)+tempangle ) >>FRACBITS; if (x1 > viewwidth) return; // off the right side tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul (tx, pspritescale)+tempangle ) >>FRACBITS) - 1; if (x2 < 0) return; // off the left side // // store information in a vissprite // vis = &avis; vis->mobjflags = 0; vis->psprite = true; vis->texturemid = (BASEYCENTER<sy-spritetopoffset[lump]); if(viewheight == SCREENHEIGHT) { vis->texturemid -= PSpriteSY[players[consoleplayer].readyweapon]; } vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; vis->scale = pspritescale<xiscale = -pspriteiscale; vis->startfrac = spritewidth[lump]-1; } else { vis->xiscale = pspriteiscale; vis->startfrac = 0; } if (vis->x1 > x1) vis->startfrac += vis->xiscale*(vis->x1-x1); vis->patch = lump; if(viewplayer->powers[pw_invisibility] > 4*32 || viewplayer->powers[pw_invisibility] & 8) { // Invisibility vis->colormap = spritelights[MAXLIGHTSCALE-1]; vis->mobjflags |= MF_SHADOW; } else if(fixedcolormap) { // Fixed color vis->colormap = fixedcolormap; } else if(psp->state->frame & FF_FULLBRIGHT) { // Full bright vis->colormap = colormaps; } else { // local light vis->colormap = spritelights[MAXLIGHTSCALE-1]; } R_DrawVisSprite(vis, vis->x1, vis->x2); } /* ======================== = = R_DrawPlayerSprites = ======================== */ void R_DrawPlayerSprites (void) { int i, lightnum; pspdef_t *psp; // // get light level // lightnum = (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) +extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS-1]; else spritelights = scalelight[lightnum]; // // clip to screen bounds // mfloorclip = screenheightarray; mceilingclip = negonearray; // // add all active psprites // for (i=0, psp=viewplayer->psprites ; istate) R_DrawPSprite (psp); } /* ======================== = = R_SortVisSprites = ======================== */ vissprite_t vsprsortedhead; void R_SortVisSprites (void) { int i, count; vissprite_t *ds, *best; vissprite_t unsorted; fixed_t bestscale; count = vissprite_p - vissprites; unsorted.next = unsorted.prev = &unsorted; if (!count) return; for (ds=vissprites ; dsnext = ds+1; ds->prev = ds-1; } vissprites[0].prev = &unsorted; unsorted.next = &vissprites[0]; (vissprite_p-1)->next = &unsorted; unsorted.prev = vissprite_p-1; // // pull the vissprites out by scale // best = 0; // shut up the compiler warning vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; for (i=0 ; inext) { if (ds->scale < bestscale) { bestscale = ds->scale; best = ds; } } best->next->prev = best->prev; best->prev->next = best->next; best->next = &vsprsortedhead; best->prev = vsprsortedhead.prev; vsprsortedhead.prev->next = best; vsprsortedhead.prev = best; } } /* ======================== = = R_DrawSprite = ======================== */ void R_DrawSprite (vissprite_t *spr) { drawseg_t *ds; short clipbot[SCREENWIDTH], cliptop[SCREENWIDTH]; int x, r1, r2; fixed_t scale, lowscale; int silhouette; for (x = spr->x1 ; x<=spr->x2 ; x++) clipbot[x] = cliptop[x] = -2; // // scan drawsegs from end to start for obscuring segs // the first drawseg that has a greater scale is the clip seg // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) { // // determine if the drawseg obscures the sprite // if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || (!ds->silhouette && !ds->maskedtexturecol) ) continue; // doesn't cover sprite r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; if (ds->scale1 > ds->scale2) { lowscale = ds->scale2; scale = ds->scale1; } else { lowscale = ds->scale1; scale = ds->scale2; } if (scale < spr->scale || ( lowscale < spr->scale && !R_PointOnSegSide (spr->gx, spr->gy, ds->curline) ) ) { if (ds->maskedtexturecol) // masked mid texture R_RenderMaskedSegRange (ds, r1, r2); continue; // seg is behind sprite } // // clip this piece of the sprite // silhouette = ds->silhouette; if (spr->gz >= ds->bsilheight) silhouette &= ~SIL_BOTTOM; if (spr->gzt <= ds->tsilheight) silhouette &= ~SIL_TOP; if (silhouette == 1) { // bottom sil for (x=r1 ; x<=r2 ; x++) if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; } else if (silhouette == 2) { // top sil for (x=r1 ; x<=r2 ; x++) if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } else if (silhouette == 3) { // both for (x=r1 ; x<=r2 ; x++) { if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } } } // // all clipping has been performed, so draw the sprite // // check for unclipped columns for (x = spr->x1 ; x<=spr->x2 ; x++) { if (clipbot[x] == -2) clipbot[x] = viewheight; if (cliptop[x] == -2) cliptop[x] = -1; } mfloorclip = clipbot; mceilingclip = cliptop; R_DrawVisSprite (spr, spr->x1, spr->x2); } /* ======================== = = R_DrawMasked = ======================== */ void R_DrawMasked (void) { vissprite_t *spr; drawseg_t *ds; R_SortVisSprites (); if (vissprite_p > vissprites) { // draw all vissprites back to front for (spr = vsprsortedhead.next ; spr != &vsprsortedhead ; spr=spr->next) R_DrawSprite (spr); } // // render any remaining masked mid textures // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) if (ds->maskedtexturecol) R_RenderMaskedSegRange (ds, ds->x1, ds->x2); // // draw the psprites on top of everything // // Added for the sideviewing with an external device if (viewangleoffset <= 1024<= -1024<