void R_ProjectSprite()

in doom_py/src/vizdoom/src/r_things.cpp [659:1066]


void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling)
{
	fixed_t				fx, fy, fz;
	fixed_t 			tr_x;
	fixed_t 			tr_y;
	
	fixed_t				gzt;				// killough 3/27/98
	fixed_t				gzb;				// [RH] use bottom of sprite, not actor
	fixed_t 			tx, tx2;
	fixed_t 			tz;

	fixed_t 			xscale = FRACUNIT, yscale = FRACUNIT;
	
	int 				x1;
	int 				x2;

	FTextureID			picnum;
	FTexture			*tex;
	FVoxelDef			*voxel;
	
	vissprite_t*		vis;
	
	fixed_t 			iscale;

	sector_t*			heightsec;			// killough 3/27/98

	// Don't waste time projecting sprites that are definitely not visible.
	if (thing == NULL ||
		(thing->renderflags & RF_INVISIBLE) ||
		!thing->RenderStyle.IsVisible(thing->alpha) ||
		!thing->IsVisibleToPlayer())
	{
		return;
	}

	// [RH] Interpolate the sprite's position to make it look smooth
	fixedvec3 pos = thing->InterpolatedPosition(r_TicFrac);
	fx = pos.x;
	fy = pos.y;
	fz = pos.z + thing->GetBobOffset(r_TicFrac);

	tex = NULL;
	voxel = NULL;

	int spritenum = thing->sprite;
	fixed_t spritescaleX = thing->scaleX;
	fixed_t spritescaleY = thing->scaleY;
	int renderflags = thing->renderflags;
	if (spritescaleY < 0)
	{
		spritescaleY = -spritescaleY;
		renderflags ^= RF_YFLIP;
	}
	if (thing->player != NULL)
	{
		P_CheckPlayerSprite(thing, spritenum, spritescaleX, spritescaleY);
	}

	if (thing->picnum.isValid())
	{
		picnum = thing->picnum;

		tex = TexMan(picnum);
		if (tex->UseType == FTexture::TEX_Null)
		{
			return;
		}

		if (tex->Rotations != 0xFFFF)
		{
			// choose a different rotation based on player view
			spriteframe_t *sprframe = &SpriteFrames[tex->Rotations];
			angle_t ang = R_PointToAngle (fx, fy);
			angle_t rot;
			if (sprframe->Texture[0] == sprframe->Texture[1])
			{
				rot = (ang - thing->angle + (angle_t)(ANGLE_45/2)*9) >> 28;
			}
			else
			{
				rot = (ang - thing->angle + (angle_t)(ANGLE_45/2)*9-(angle_t)(ANGLE_180/16)) >> 28;
			}
			picnum = sprframe->Texture[rot];
			if (sprframe->Flip & (1 << rot))
			{
				renderflags ^= RF_XFLIP;
			}
			tex = TexMan[picnum];	// Do not animate the rotation
		}
	}
	else
	{
		// decide which texture to use for the sprite
#ifdef RANGECHECK
		if (spritenum >= (signed)sprites.Size () || spritenum < 0)
		{
			DPrintf ("R_ProjectSprite: invalid sprite number %u\n", spritenum);
			return;
		}
#endif
		spritedef_t *sprdef = &sprites[spritenum];
		if (thing->frame >= sprdef->numframes)
		{
			// If there are no frames at all for this sprite, don't draw it.
			return;
		}
		else
		{
			//picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0];
			// choose a different rotation based on player view
			spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame];
			angle_t ang = R_PointToAngle (fx, fy);
			angle_t rot;
			if (sprframe->Texture[0] == sprframe->Texture[1])
			{
				rot = (ang - thing->angle + (angle_t)(ANGLE_45/2)*9) >> 28;
			}
			else
			{
				rot = (ang - thing->angle + (angle_t)(ANGLE_45/2)*9-(angle_t)(ANGLE_180/16)) >> 28;
			}
			picnum = sprframe->Texture[rot];
			if (sprframe->Flip & (1 << rot))
			{
				renderflags ^= RF_XFLIP;
			}
			tex = TexMan[picnum];	// Do not animate the rotation
			if (r_drawvoxels)
			{
				voxel = sprframe->Voxel;
			}
		}
	}
	if (spritescaleX < 0)
	{
		spritescaleX = -spritescaleX;
		renderflags ^= RF_XFLIP;
	}
	if (voxel == NULL && (tex == NULL || tex->UseType == FTexture::TEX_Null))
	{
		return;
	}

	if ((renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE)
	{
		R_ProjectWallSprite(thing, fx, fy, fz, picnum, spritescaleX, spritescaleY, renderflags);
		return;
	}

	// transform the origin point
	tr_x = fx - viewx;
	tr_y = fy - viewy;

	tz = DMulScale20 (tr_x, viewtancos, tr_y, viewtansin);

	// thing is behind view plane?
	if (voxel == NULL && tz < MINZ)
		return;

	tx = DMulScale16 (tr_x, viewsin, -tr_y, viewcos);

	// [RH] Flip for mirrors
	if (MirrorFlags & RF_XFLIP)
	{
		tx = -tx;
	}
	tx2 = tx >> 4;

	// too far off the side?
	// if it's a voxel, it can be further off the side
	if ((voxel == NULL && (abs(tx) >> 6) > abs(tz)) ||
		(voxel != NULL && (abs(tx) >> 7) > abs(tz)))
	{
		return;
	}

	if (voxel == NULL)
	{
		// [RH] Added scaling
		int scaled_to = tex->GetScaledTopOffset();
		int scaled_bo = scaled_to - tex->GetScaledHeight();
		gzt = fz + spritescaleY * scaled_to;
		gzb = fz + spritescaleY * scaled_bo;
	}
	else
	{
		xscale = FixedMul(spritescaleX, voxel->Scale);
		yscale = FixedMul(spritescaleY, voxel->Scale);
		gzt = fz + MulScale8(yscale, voxel->Voxel->Mips[0].PivotZ) - thing->floorclip;
		gzb = fz + MulScale8(yscale, voxel->Voxel->Mips[0].PivotZ - (voxel->Voxel->Mips[0].SizeZ << 8));
		if (gzt <= gzb)
			return;
	}

	// killough 3/27/98: exclude things totally separated
	// from the viewer, by either water or fake ceilings
	// killough 4/11/98: improve sprite clipping for underwater/fake ceilings

	heightsec = thing->Sector->GetHeightSec();

	if (heightsec != NULL)	// only clip things which are in special sectors
	{
		if (fakeside == FAKED_AboveCeiling)
		{
			if (gzt < heightsec->ceilingplane.ZatPoint (fx, fy))
				return;
		}
		else if (fakeside == FAKED_BelowFloor)
		{
			if (gzb >= heightsec->floorplane.ZatPoint (fx, fy))
				return;
		}
		else
		{
			if (gzt < heightsec->floorplane.ZatPoint (fx, fy))
				return;
			if (!(heightsec->MoreFlags & SECF_FAKEFLOORONLY) && gzb >= heightsec->ceilingplane.ZatPoint (fx, fy))
				return;
		}
	}

	if (voxel == NULL)
	{
		xscale = DivScale12 (centerxfrac, tz);

		// [RH] Reject sprites that are off the top or bottom of the screen
		if (MulScale12 (globaluclip, tz) > viewz - gzb ||
			MulScale12 (globaldclip, tz) < viewz - gzt)
		{
			return;
		}

		// [RH] Flip for mirrors
		renderflags ^= MirrorFlags & RF_XFLIP;

		// calculate edges of the shape
		const fixed_t thingxscalemul = DivScale16(spritescaleX, tex->xScale);

		tx -= ((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul;
		x1 = centerx + MulScale32 (tx, xscale);

		// off the right side?
		if (x1 >= WindowRight)
			return;

		tx += tex->GetWidth() * thingxscalemul;
		x2 = centerx + MulScale32 (tx, xscale);

		// off the left side or too small?
		if ((x2 < WindowLeft || x2 <= x1))
			return;

		xscale = FixedDiv(FixedMul(spritescaleX, xscale), tex->xScale);
		iscale = (tex->GetWidth() << FRACBITS) / (x2 - x1);

		fixed_t yscale = SafeDivScale16(spritescaleY, tex->yScale);

		// store information in a vissprite
		vis = R_NewVisSprite();

		vis->xscale = xscale;
		vis->yscale = Scale(InvZtoScale, yscale, tz << 4);
		vis->idepth = (unsigned)DivScale32(1, tz) >> 1;	// tz is 20.12, so idepth ought to be 12.20, but signed math makes it 13.19
		vis->floorclip = FixedDiv (thing->floorclip, yscale);
		vis->texturemid = (tex->TopOffset << FRACBITS) - FixedDiv (viewz - fz + thing->floorclip, yscale);
		vis->x1 = x1 < WindowLeft ? WindowLeft : x1;
		vis->x2 = x2 > WindowRight ? WindowRight : x2;
		vis->angle = thing->angle;

		if (renderflags & RF_XFLIP)
		{
			vis->startfrac = (tex->GetWidth() << FRACBITS) - 1;
			vis->xiscale = -iscale;
		}
		else
		{
			vis->startfrac = 0;
			vis->xiscale = iscale;
		}

		if (vis->x1 > x1)
			vis->startfrac += vis->xiscale * (vis->x1 - x1);
	}
	else
	{
		vis = R_NewVisSprite();

		vis->xscale = xscale;
		vis->yscale = yscale;
		vis->x1 = WindowLeft;
		vis->x2 = WindowRight;
		vis->idepth = (unsigned)DivScale32(1, MAX(tz, MINZ)) >> 1;
		vis->floorclip = thing->floorclip;

		fz -= thing->floorclip;

		vis->angle = thing->angle + voxel->AngleOffset;

		int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin;
		if (voxelspin != 0)
		{
			double ang = double(I_FPSTime()) * voxelspin / 1000;
			vis->angle -= angle_t(ang * (4294967296.f / 360));
		}

		vis->vx = viewx;
		vis->vy = viewy;
		vis->vz = viewz;
		vis->vang = viewangle;
	}

	// killough 3/27/98: save sector for special clipping later
	vis->heightsec = heightsec;
	vis->sector = thing->Sector;

	vis->depth = tz;
	vis->gx = fx;
	vis->gy = fy;
	vis->gz = fz;
	vis->gzb = gzb;		// [RH] use gzb, not thing->z
	vis->gzt = gzt;		// killough 3/27/98
	vis->deltax = fx - viewx;
	vis->deltay = fy - viewy;
	vis->renderflags = renderflags;
	if(thing->flags5 & MF5_BRIGHT) vis->renderflags |= RF_FULLBRIGHT; // kg3D
	vis->Style.RenderStyle = thing->RenderStyle;
	vis->FillColor = thing->fillcolor;
	vis->Translation = thing->Translation;		// [RH] thing translation table
	vis->FakeFlatStat = fakeside;
	vis->Style.alpha = thing->alpha;
	vis->fakefloor = fakefloor;
	vis->fakeceiling = fakeceiling;
	vis->ColormapNum = 0;
	vis->bInMirror = MirrorFlags & RF_XFLIP;
	vis->bSplitSprite = false;

	if (voxel != NULL)
	{
		vis->voxel = voxel->Voxel;
		vis->bIsVoxel = true;
		vis->bWallSprite = false;
		DrewAVoxel = true;
	}
	else
	{
		vis->pic = tex;
		vis->bIsVoxel = false;
		vis->bWallSprite = false;
	}

	// The software renderer cannot invert the source without inverting the overlay
	// too. That means if the source is inverted, we need to do the reverse of what
	// the invert overlay flag says to do.
	INTBOOL invertcolormap = (vis->Style.RenderStyle.Flags & STYLEF_InvertOverlay);

	if (vis->Style.RenderStyle.Flags & STYLEF_InvertSource)
	{
		invertcolormap = !invertcolormap;
	}

	FDynamicColormap *mybasecolormap = basecolormap;

	// Sprites that are added to the scene must fade to black.
	if (vis->Style.RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0)
	{
		mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate);
	}

	if (vis->Style.RenderStyle.Flags & STYLEF_FadeToBlack)
	{
		if (invertcolormap)
		{ // Fade to white
			mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255,255,255), mybasecolormap->Desaturate);
			invertcolormap = false;
		}
		else
		{ // Fade to black
			mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0,0,0), mybasecolormap->Desaturate);
		}
	}

	// get light level
	if (fixedcolormap != NULL)
	{ // fixed map
		vis->Style.colormap = fixedcolormap;
	}
	else
	{
		if (invertcolormap)
		{
			mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate);
		}
		if (fixedlightlev >= 0)
		{
			vis->Style.colormap = mybasecolormap->Maps + fixedlightlev;
		}
		else if (!foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT)))
		{ // full bright
			vis->Style.colormap = mybasecolormap->Maps;
		}
		else
		{ // diminished light
			vis->ColormapNum = GETPALOOKUP(
				(fixed_t)DivScale12 (r_SpriteVisibility, MAX(tz, MINZ)), spriteshade);
			vis->Style.colormap = mybasecolormap->Maps + (vis->ColormapNum << COLORMAPSHIFT);
		}
	}
}