Пример #1
0
//
// P_BoxOnLineSide
// Considers the line to be infinite
// Returns side 0 or 1, -1 if box crosses the line.
//
// killough 5/3/98: reformatted, cleaned up
//
int P_BoxOnLineSide(const fixed_t *tmbox, const line_t *ld)
{
   int p;

   switch (ld->slopetype)
   {
   default: // shut up compiler warnings -- killough
   case ST_HORIZONTAL:
      return
      (tmbox[BOXBOTTOM] > ld->v1->y) == (p = tmbox[BOXTOP] > ld->v1->y) ?
        p ^ (ld->dx < 0) : -1;
   case ST_VERTICAL:
      return
        (tmbox[BOXLEFT] < ld->v1->x) == (p = tmbox[BOXRIGHT] < ld->v1->x) ?
        p ^ (ld->dy < 0) : -1;
   case ST_POSITIVE:
      return
        P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld) ==
        (p = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld)) ? p : -1;
   case ST_NEGATIVE:
      return
        (P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld)) ==
        (p = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld)) ? p : -1;
    }
}
Пример #2
0
//
// Utility function which returns true if the divline dl crosses line
// Returns -1 if there's no crossing, or the starting point's 0 or 1 side.
//
int P_LineIsCrossed(const line_t &line, const divline_t &dl)
{
   int a;
   return (a = P_PointOnLineSide(dl.x, dl.y, &line)) !=
   P_PointOnLineSide(dl.x + dl.dx, dl.y + dl.dy, &line) &&
   P_PointOnDivlineSide(line.v1->x, line.v1->y, &dl) !=
   P_PointOnDivlineSide(line.v1->x + line.dx, line.v1->y + line.dy, &dl) ? a : -1;
}
Пример #3
0
int GLMirrorPortal::ClipSeg(seg_t *seg) 
{ 
	// this seg is completely behind the mirror!
	if (P_PointOnLineSide(seg->v1->x, seg->v1->y, linedef) &&
		P_PointOnLineSide(seg->v2->x, seg->v2->y, linedef)) 
	{
		return PClip_InFront;
	}
	return PClip_Inside; 
}
Пример #4
0
int GLMirrorPortal::ClipPoint(fixed_t x, fixed_t y) 
{ 
	if (P_PointOnLineSide(x, y, linedef)) 
	{
		return PClip_InFront;
	}
	return PClip_Inside; 
}
Пример #5
0
//
// Checks if a point is behind a subsector's 1-sided seg
//
bool P_IsInVoid(fixed_t x, fixed_t y, const subsector_t &ss)
{
   for(int i = 0; i < ss.numlines; ++i)
   {
      const seg_t &seg = segs[ss.firstline + i];
      if(seg.backsector)
         continue;
      if(P_PointOnLineSide(x, y, seg.linedef))
         return true;
   }
   return false;
}
Пример #6
0
int FBoundingBox::BoxOnLineSide (const line_t *ld) const
{
	int p1;
	int p2;
		
	if (ld->dx == 0)
	{ // ST_VERTICAL
		p1 = m_Box[BOXRIGHT] < ld->v1->x;
		p2 = m_Box[BOXLEFT] < ld->v1->x;
		if (ld->dy < 0)
		{
			p1 ^= 1;
			p2 ^= 1;
		}
	}
	else if (ld->dy == 0)
	{ // ST_HORIZONTAL:
		p1 = m_Box[BOXTOP] > ld->v1->y;
		p2 = m_Box[BOXBOTTOM] > ld->v1->y;
		if (ld->dx < 0)
		{
			p1 ^= 1;
			p2 ^= 1;
		}
	}
	else if ((ld->dy ^ ld->dx) >= 0)
	{ // ST_POSITIVE:
		p1 = P_PointOnLineSide (m_Box[BOXLEFT], m_Box[BOXTOP], ld);
		p2 = P_PointOnLineSide (m_Box[BOXRIGHT], m_Box[BOXBOTTOM], ld);
	}
	else
	{ // ST_NEGATIVE:
		p1 = P_PointOnLineSide (m_Box[BOXRIGHT], m_Box[BOXTOP], ld);
		p2 = P_PointOnLineSide (m_Box[BOXLEFT], m_Box[BOXBOTTOM], ld);
	}

	return (p1 == p2) ? p1 : -1;
}
Пример #7
0
//
// P_BoxOnLineSide
// Considers the line to be infinite
// Returns side 0 or 1, -1 if box crosses the line.
// killough 5/3/98: reformatted, cleaned up
//
int P_BoxOnLineSide(fixed_t *tmbox, line_t *ld)
{
    switch (ld->slopetype)
    {
        int     p;

        default:
        case ST_HORIZONTAL:
            return ((tmbox[BOXBOTTOM] > ld->v1->y) == (p = (tmbox[BOXTOP] > ld->v1->y)) ?
                p ^ (ld->dx < 0) : -1);

        case ST_VERTICAL:
            return ((tmbox[BOXLEFT] < ld->v1->x) == (p = (tmbox[BOXRIGHT] < ld->v1->x)) ?
                p ^ (ld->dy < 0) : -1);

        case ST_POSITIVE:
            return (P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld) ==
                (p = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld)) ? p : -1);

        case ST_NEGATIVE:
            return ((P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld)) ==
                (p = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld)) ? p : -1);
    }
}
Пример #8
0
//
// PTR_SlideTraverse
//
static boolean PTR_SlideTraverse(intercept_t * in)
{
    line_t *li;

    if(!in->isaline)
        Con_Error("PTR_SlideTraverse: not a line?");

    li = in->d.line;

    if(!(li->flags & ML_TWOSIDED))
    {
        if(P_PointOnLineSide(slidemo->x, slidemo->y, li))
        {
            // don't hit the back side
            return true;
        }
        goto isblocking;
    }

    // set openrange, opentop, openbottom
    P_LineOpening(li);

    if(openrange < slidemo->height)
        goto isblocking;		// doesn't fit

    if(opentop - slidemo->z < slidemo->height)
        goto isblocking;		// mobj is too high

    if(openbottom - slidemo->z > 24 * FRACUNIT)
        goto isblocking;		// too big a step up

    // this line doesn't block movement
    return true;

    // the line does block movement,
    // see if it is closer than best so far
isblocking:
    if(in->frac < bestslidefrac)
    {
        secondslidefrac = bestslidefrac;
        secondslideline = bestslideline;
        bestslidefrac = in->frac;
        bestslideline = li;
    }
    return false;				// stop
}
Пример #9
0
//
// P_WallMomSlide
// Adjusts the xmove / ymove
// so that the next move will slide along the wall.
//
static void P_WallMomSlide(line_t *ld)
{
    int     side;
    angle_t lineangle;
    angle_t moveangle;
    angle_t deltaangle;
    fixed_t movelen;
    fixed_t newlen;

    // First check the simple cases.
    if(ld->slopetype == ST_HORIZONTAL)
    {
        tmymove = 0;
        return;
    }
    if(ld->slopetype == ST_VERTICAL)
    {
        tmxmove = 0;
        return;
    }

    side = P_PointOnLineSide(slidemo->x, slidemo->y, ld);
    lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy);

    if(side == 1)
        lineangle += ANG180;

    moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove);
    deltaangle = moveangle - lineangle;

    if(deltaangle > ANG180)
        deltaangle += ANG180;

    lineangle >>= ANGLETOFINESHIFT;
    deltaangle >>= ANGLETOFINESHIFT;

    movelen = P_ApproxDistance(tmxmove, tmymove);
    newlen = FixedMul(movelen, finecosine[deltaangle]);

    tmxmove = FixedMul(newlen, finecosine[lineangle]);
    tmymove = FixedMul(newlen, finesine[lineangle]);
}
Пример #10
0
//
// R_AddLine
// Clips the given segment and adds any visible pieces to the line list.
//
static void R_AddLine(seg_t *line)
{
	INT32 x1, x2;
	angle_t angle1, angle2, span, tspan;
	static sector_t tempsec; // ceiling/water hack

	if (line->polyseg && !(line->polyseg->flags & POF_RENDERSIDES))
		return;

	curline = line;

	// OPTIMIZE: quickly reject orthogonal back sides.
	angle1 = R_PointToAngle(line->v1->x, line->v1->y);
	angle2 = R_PointToAngle(line->v2->x, line->v2->y);

	// Clip to view edges.
	span = angle1 - angle2;

	// Back side? i.e. backface culling?
	if (span >= ANGLE_180)
		return;

	// Global angle needed by segcalc.
	rw_angle1 = angle1;
	angle1 -= viewangle;
	angle2 -= viewangle;

	tspan = angle1 + clipangle;
	if (tspan > doubleclipangle)
	{
		tspan -= doubleclipangle;

		// Totally off the left edge?
		if (tspan >= span)
			return;

		angle1 = clipangle;
	}
	tspan = clipangle - angle2;
	if (tspan > doubleclipangle)
	{
		tspan -= doubleclipangle;

		// Totally off the left edge?
		if (tspan >= span)
			return;

		angle2 = -(signed)clipangle;
	}

	// The seg is in the view range, but not necessarily visible.
	angle1 = (angle1+ANGLE_90)>>ANGLETOFINESHIFT;
	angle2 = (angle2+ANGLE_90)>>ANGLETOFINESHIFT;
	x1 = viewangletox[angle1];
	x2 = viewangletox[angle2];

	// Does not cross a pixel?
	if (x1 >= x2)       // killough 1/31/98 -- change == to >= for robustness
		return;

	backsector = line->backsector;

	// Portal line
	if (line->linedef->special == 40 && P_PointOnLineSide(viewx, viewy, line->linedef) == 0)
	{
		if (portalrender < cv_maxportals.value)
		{
			// Find the other side!
			INT32 line2 = P_FindSpecialLineFromTag(40, line->linedef->tag, -1);
			if (line->linedef == &lines[line2])
				line2 = P_FindSpecialLineFromTag(40, line->linedef->tag, line2);
			if (line2 >= 0) // found it!
			{
				R_AddPortal(line->linedef-lines, line2, x1, x2); // Remember the lines for later rendering
				//return; // Don't fill in that space now!
				goto clipsolid;
			}
		}
		// Recursed TOO FAR (viewing a portal within a portal)
		// So uhhh, render it as a normal wall instead or something ???
	}

	// Single sided line?
	if (!backsector)
		goto clipsolid;

	backsector = R_FakeFlat(backsector, &tempsec, NULL, NULL, true);

	doorclosed = 0;

	// Closed door.
#ifdef ESLOPE
	// Just don't bother checking this if one side is sloped. This is probably inefficient, but it's better than
	// random renderer stopping around slopes...
	if (!(frontsector->f_slope || frontsector->c_slope || backsector->f_slope || backsector->c_slope))
#endif
	if (backsector->ceilingheight <= frontsector->floorheight
		|| backsector->floorheight >= frontsector->ceilingheight)
	{
		goto clipsolid;
	}

	// Check for automap fix. Store in doorclosed for r_segs.c
	doorclosed = R_DoorClosed();
	if (doorclosed)
		goto clipsolid;

	// Window.
	if (backsector->ceilingheight != frontsector->ceilingheight
		|| backsector->floorheight != frontsector->floorheight)
	{
		goto clippass;
	}

	// Reject empty lines used for triggers and special events.
	// Identical floor and ceiling on both sides, identical light levels on both sides,
	// and no middle texture.

	if (
#ifdef POLYOBJECTS
		!line->polyseg &&
#endif
		backsector->ceilingpic == frontsector->ceilingpic
		&& backsector->floorpic == frontsector->floorpic
#ifdef ESLOPE
		&& backsector->f_slope == frontsector->f_slope
		&& backsector->c_slope == frontsector->c_slope
#endif
		&& backsector->lightlevel == frontsector->lightlevel
		&& !curline->sidedef->midtexture
		// Check offsets too!
		&& backsector->floor_xoffs == frontsector->floor_xoffs
		&& backsector->floor_yoffs == frontsector->floor_yoffs
		&& backsector->floorpic_angle == frontsector->floorpic_angle
		&& backsector->ceiling_xoffs == frontsector->ceiling_xoffs
		&& backsector->ceiling_yoffs == frontsector->ceiling_yoffs
		&& backsector->ceilingpic_angle == frontsector->ceilingpic_angle
		// Consider altered lighting.
		&& backsector->floorlightsec == frontsector->floorlightsec
		&& backsector->ceilinglightsec == frontsector->ceilinglightsec
		// Consider colormaps
		&& backsector->extra_colormap == frontsector->extra_colormap
		&& ((!frontsector->ffloors && !backsector->ffloors)
		|| frontsector->tag == backsector->tag))
	{
		return;
	}


clippass:
	R_ClipPassWallSegment(x1, x2 - 1);
	return;

clipsolid:
	R_ClipSolidWallSegment(x1, x2 - 1);
}
Пример #11
0
int EV_Teleport( line_t *line,mobj_t *thing )
{
   int      i;
   int      tag;
   boolean  flag;
   mobj_t   *m,*fog;
   unsigned int	an;
   sector_t *sector;
   fixed_t   oldx, oldy, oldz;
   int       side;
	
   side = !P_PointOnLineSide (thing->x, thing->y, line);

   if(thing->flags & MF_MISSILE)
      return 0; /* don't teleport missiles */

   if(side == 1) /* don't teleport if hit back of line, */
      return 0;  /* so you can get out of teleporter */
	
   tag = line->tag;
   for(i = 0; i < numsectors; i++)
   {
      if (sectors[ i ].tag == tag )
      {
         for(m = mobjhead.next; m != &mobjhead; m = m->next)
         {
            // CALICO: skip removed mobjs
            if(m->latecall == P_RemoveMobjDeferred)
               continue;

            if(m->type != MT_TELEPORTMAN)
               continue; // not a teleportman

            sector = m->subsector->sector;
            if(sector-sectors != i )
               continue; // wrong sector

            oldx = thing->x;
            oldy = thing->y;
            oldz = thing->z;
            thing->flags |= MF_TELEPORT;
            P_Telefrag(thing, m->x, m->y);
            flag = P_TryMove (thing, m->x, m->y);
            thing->flags &= ~MF_TELEPORT;
            if(!flag)
               return 0; /* move is blocked */
            thing->z = thing->floorz;
            
            /* spawn teleport fog at source and destination */
            fog = P_SpawnMobj (oldx, oldy, oldz, MT_TFOG);
            S_StartSound(fog, sfx_telept);
            an  = m->angle >> ANGLETOFINESHIFT;
            fog = P_SpawnMobj (m->x+20*finecosine[an], m->y+20*finesine[an], thing->z, MT_TFOG);
            S_StartSound(fog, sfx_telept);
            if(thing->player)
               thing->reactiontime = 18;	/* don't move for a bit */
            thing->angle = m->angle;
            thing->momx = thing->momy = thing->momz = 0;
            return 1;
         }	
      }
   }
   return 0;
}
Пример #12
0
static void AddLine (seg_t *seg,sector_t * sector,subsector_t * polysub)
{
	angle_t startAngle, endAngle;
	sector_t * backsector = NULL;
	sector_t bs;

	ClipWall.Clock();
	if (GLPortal::mirrorline)
	{
		// this seg is completely behind the mirror!
		if (P_PointOnLineSide(seg->v1->x, seg->v1->y, GLPortal::mirrorline) &&
			P_PointOnLineSide(seg->v2->x, seg->v2->y, GLPortal::mirrorline)) 
		{
			ClipWall.Unclock();
			return;
		}
	}

	startAngle = seg->v2->GetViewAngle();
	endAngle = seg->v1->GetViewAngle();

	// Back side, i.e. backface culling	- read: endAngle >= startAngle!
	if (startAngle-endAngle<ANGLE_180 || !seg->linedef)  
	{
		ClipWall.Unclock();
		return;
	}

	if (!clipper.SafeCheckRange(startAngle, endAngle)) 
	{
		ClipWall.Unclock();
		return;
	}

	if (!seg->backsector)
	{
		clipper.SafeAddClipRange(startAngle, endAngle);
	}
	else if (!polysub)	// Two-sided polyobjects never obstruct the view
	{
		if (sector->sectornum == seg->backsector->sectornum)
		{
			FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::mid));
			if (!tex || tex->UseType==FTexture::TEX_Null) 
			{
				// nothing to do here!
				ClipWall.Unclock();
				seg->linedef->validcount=validcount;
				return;
			}
			backsector=sector;
		}
		else
		{
			// clipping checks are only needed when the backsector is not the same as the front sector
			gl_CheckViewArea(seg->v1, seg->v2, seg->frontsector, seg->backsector);

			backsector = gl_FakeFlat(seg->backsector, &bs, true);

			if (gl_CheckClip(seg->sidedef, sector, backsector))
			{
				clipper.SafeAddClipRange(startAngle, endAngle);
			}
		}
	}
	else 
	{
		// Backsector for polyobj segs is always the containing sector itself
		backsector = sector;
	}

	seg->linedef->flags |= ML_MAPPED;
	ClipWall.Unclock();

	if (!gl_render_segs)
	{
		// rendering per linedef as opposed per seg is significantly more efficient
		// so mark the linedef as rendered here and render it completely.
		if (seg->linedef->validcount!=validcount) seg->linedef->validcount=validcount;
		else return;
	}

	GLWall wall;

	SetupWall.Clock();

	wall.Process(seg, sector, backsector, polysub, gl_render_segs);
	rendered_lines++;

	SetupWall.Unclock();
}
Пример #13
0
bool gl_GetSpriteLight(AActor *self, fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec, int desaturation, float * out, line_t *line, int side)
{
	ADynamicLight *light;
	float frac, lr, lg, lb;
	float radius;
	bool changed = false;
	
	out[0]=out[1]=out[2]=0;

	for(int j=0;j<2;j++)
	{
		// Go through both light lists
		FLightNode * node = subsec->lighthead[j];
		while (node)
		{
			light=node->lightsource;
			//if (!light->owned || light->target == NULL || light->target->IsVisibleToPlayer())
			{
				if (!(light->flags2&MF2_DORMANT) &&
					(!(light->flags4&MF4_DONTLIGHTSELF) || light->target != self))
				{
					float dist = FVector3(FIXED2FLOAT(x - light->x), FIXED2FLOAT(y - light->y), FIXED2FLOAT(z - light->z)).Length();
					radius = light->GetRadius() * gl_lights_size;

					if (dist < radius)
					{
						frac = 1.0f - (dist / radius);

						if (frac > 0)
						{
							if (line != NULL)
							{
								if (P_PointOnLineSide(light->x, light->y, line) != side)
								{
									node = node->nextLight;
									continue;
								}
							}
							lr = light->GetRed() / 255.0f * gl_lights_intensity;
							lg = light->GetGreen() / 255.0f * gl_lights_intensity;
							lb = light->GetBlue() / 255.0f * gl_lights_intensity;
							if (light->IsSubtractive())
							{
								float bright = FVector3(lr, lg, lb).Length();
								FVector3 lightColor(lr, lg, lb);
								lr = (bright - lr) * -1;
								lg = (bright - lg) * -1;
								lb = (bright - lb) * -1;
							}

							out[0] += lr * frac;
							out[1] += lg * frac;
							out[2] += lb * frac;
							changed = true;
						}
					}
				}
			}
			node = node->nextLight;
		}
	}

	// Desaturate dynamic lighting if applicable
	if (desaturation>0 && desaturation<=CM_DESAT31)
	{
		float gray=(out[0]*77 + out[1]*143 + out[2]*37)/257;

		out[0]= (out[0]*(31-desaturation)+ gray*desaturation)/31;
		out[1]= (out[1]*(31-desaturation)+ gray*desaturation)/31;
		out[2]= (out[2]*(31-desaturation)+ gray*desaturation)/31;
	}
	return changed;
}
Пример #14
0
//
// A_PainShootSkull
// Spawn a lost soul and launch it at the target
//
void A_PainShootSkull (AActor *self, angle_t angle, const PClass *spawntype, int flags = 0, int limit = -1)
{
	fixed_t x, y, z;
	
	AActor *other;
	angle_t an;
	int prestep;

	if (spawntype == NULL) return;
	if (self->DamageType==NAME_Massacre) return;

	// [RH] check to make sure it's not too close to the ceiling
	if (self->z + self->height + 8*FRACUNIT > self->ceilingz)
	{
		if (self->flags & MF_FLOAT)
		{
			self->velz -= 2*FRACUNIT;
			self->flags |= MF_INFLOAT;
			self->flags4 |= MF4_VFRICTION;
		}
		return;
	}

	// [RH] make this optional
	if (limit == -1 && (i_compatflags & COMPATF_LIMITPAIN))
		limit = 21;

	if (limit)
	{
		// count total number of skulls currently on the level
		// if there are already 21 skulls on the level, don't spit another one
		int count = limit;
		FThinkerIterator iterator (spawntype);
		DThinker *othink;

		while ( (othink = iterator.Next ()) )
		{
			if (--count == 0)
				return;
		}
	}

	// okay, there's room for another one
	an = angle >> ANGLETOFINESHIFT;
	
	prestep = 4*FRACUNIT +
		3*(self->radius + GetDefaultByType(spawntype)->radius)/2;
	
	x = self->x + FixedMul (prestep, finecosine[an]);
	y = self->y + FixedMul (prestep, finesine[an]);
	z = self->z + 8*FRACUNIT;
				
	// Check whether the Lost Soul is being fired through a 1-sided	// phares
	// wall or an impassible line, or a "monsters can't cross" line.//   |
	// If it is, then we don't allow the spawn.						//   V

	FBoundingBox box(MIN(self->x, x), MIN(self->y, y), MAX(self->x, x), MAX(self->y, y));
	FBlockLinesIterator it(box);
	line_t *ld;

	while ((ld = it.Next()))
	{
		if (!(ld->flags & ML_TWOSIDED) ||
			(ld->flags & (ML_BLOCKING|ML_BLOCKMONSTERS|ML_BLOCKEVERYTHING)))
		{
			if (!(box.Left()   > ld->bbox[BOXRIGHT]  ||
				  box.Right()  < ld->bbox[BOXLEFT]   ||
				  box.Top()    < ld->bbox[BOXBOTTOM] ||
				  box.Bottom() > ld->bbox[BOXTOP]))
			{
				if (P_PointOnLineSide(self->x,self->y,ld) != P_PointOnLineSide(x,y,ld))
					return;  // line blocks trajectory				//   ^
			}
		}
	}

	other = Spawn (spawntype, x, y, z, ALLOW_REPLACE);

	// Check to see if the new Lost Soul's z value is above the
	// ceiling of its new sector, or below the floor. If so, kill it.

	if ((other->z >
         (other->Sector->ceilingplane.ZatPoint (other->x, other->y) - other->height)) ||
        (other->z < other->Sector->floorplane.ZatPoint (other->x, other->y)))
	{
		// kill it immediately
		P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);//  ^
		return;														//   |
	}																// phares

	// Check for movements.

	if (!P_CheckPosition (other, other->x, other->y))
	{
		// kill it immediately
		P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);		
		return;
	}

	// [RH] Lost souls hate the same things as their pain elementals
	other->CopyFriendliness (self, !(flags & PAF_NOTARGET));

	if (!(flags & PAF_NOSKULLATTACK))
		A_SkullAttack(other, SKULLSPEED);
}
Пример #15
0
static void RB_CarveDecal(rbDecal_t *decal)
{
    line_t *line;
    sector_t *sector;
    int side;
    int leftcount;
    int rightcount;
    byte pointsides[NUM_DECAL_POINTS];
    int i, j;
    
    sector = decal->ssect->sector;
    
    for(i = 0; i < sector->linecount; ++i)
    {
        boolean ok = false;

        line = sector->lines[i];
        
        if(line->backsector)
        {
            if(line->frontsector == line->backsector)
            {
                // ignore 2-sided lines
                continue;
            }
            
            if(decal->type == DCT_FLOOR)
            {
                if(line->backsector->floorheight == line->frontsector->floorheight)
                {
                    // ignore flat floor heights
                    continue;
                }
            }
            else if(decal->type == DCT_CEILING)
            {
                if(line->backsector->ceilingheight == line->frontsector->ceilingheight)
                {
                    // ignore flat ceiling heights
                    continue;
                }
            }
        }
        
        leftcount = 0;
        rightcount = 0;

        side = P_PointOnLineSide(decal->x, decal->y, line);
        memset(pointsides, 0, sizeof(NUM_DECAL_POINTS));

        for(j = 0; j < decal->numpoints; ++j)
        {
            pointsides[j] = P_PointOnLineSide(FLOAT2FIXED(decal->points[j].x),
                                              FLOAT2FIXED(decal->points[j].y),
                                              line);

            leftcount += pointsides[j];
            rightcount += pointsides[j] ^ 1;
        }

        if(leftcount == decal->numpoints || rightcount == decal->numpoints)
        {
            // all points are on one side
            continue;
        }
        
        //
        // some points are on the other side. begin cutting
        //
        for(j = 0; j < decal->numpoints; ++j)
        {
            int idx1 = j;
            int idx2 = j + 1;
            
            if(idx2 == decal->numpoints)
            {
                idx2 -= decal->numpoints;
            }
            
            // check if segments crosses sides
            if(pointsides[idx1] != pointsides[idx2])
            {
                float x, y;
                float mx, my;
                
                if(!RB_IntersectDecalSegment(decal->points[idx1].x,
                                             decal->points[idx1].y,
                                             decal->points[idx2].x,
                                             decal->points[idx2].y,
                                             line, &x, &y))
                {
                    // didn't actually intersect
                    continue;
                }

                ok = true;

                if(decal->numpoints+1 >= NUM_DECAL_POINTS)
                {
                    fprintf(stderr, "RB_CarveDecal: Vertex overflow\n");
                    return;
                }

                decal->numpoints++;

                mx = decal->points[idx2].x - decal->points[idx1].x;
                my = decal->points[idx2].y - decal->points[idx1].y;

                // make room for new vertex
                memmove(&decal->points[idx2 + 1], &decal->points[idx2],
                        (decal->numpoints - idx2 - 1) * sizeof(rbDecalVertex_t));
                
                decal->points[idx2].x = x;
                decal->points[idx2].y = y;
                decal->points[idx2].z = decal->points[0].z;

                if(mx == 0)
                {
                    // segment is perfectly straight, vertically.
                    // tu should be already known
                    decal->points[idx2].tu = decal->points[idx2+1].tu;
                }
                else
                {
                    float mu = (x - decal->points[idx1].x) / mx;
                    float tu2 = decal->points[idx2+1].tu;
                    float tu1 = decal->points[idx1].tu;

                    decal->points[idx2].tu = (tu2 - tu1) * mu + tu1;

                    // goddamnit this is not going to work...
                    if(decal->points[idx2].tu > 1) decal->points[idx2].tu = 1;
                    if(decal->points[idx2].tu < -1) decal->points[idx2].tu = -1;
                }

                if(my == 0)
                {
                    // segment is perfectly straight, horizontally.
                    // tv should be already known
                    decal->points[idx2].tv = decal->points[idx2+1].tv;
                }
                else
                {
                    float mu = (y - decal->points[idx1].y) / my;
                    float tv2 = decal->points[idx2+1].tv;
                    float tv1 = decal->points[idx1].tv;

                    decal->points[idx2].tv = (tv2 - tv1) * mu + tv1;

                    // goddamnit this is not going to work...
                    if(decal->points[idx2].tv > 1) decal->points[idx2].tv = 1;
                    if(decal->points[idx2].tv < -1) decal->points[idx2].tv = -1;
                }
                
                memmove(&pointsides[idx2 + 1], &pointsides[idx2], decal->numpoints - idx2 - 1);
                pointsides[idx2] = side;

                // skip new vertex
                j++;
            }
        }

        if(ok)
        {
            // discard verts that's on the other side
            for(j = 0; j < decal->numpoints; ++j)
            {
                if(pointsides[j] != side)
                {
                    // shift next item in array down
                    memmove(&decal->points[j], &decal->points[j + 1],
                            (decal->numpoints - j - 1) * sizeof(rbDecalVertex_t));
                    memmove(&pointsides[j], &pointsides[j + 1], decal->numpoints - j - 1);
                    
                    decal->numpoints--;
                    j--;
                }
            }
        }
    }
}
Пример #16
0
bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit)
{
	int lineside;
	sector_t *entersector;

	double ff, fc, bf = 0, bc = 0;

	if (in->d.line->frontsector->sectornum == CurSector->sectornum)
	{
		lineside = 0;
	}
	else if (in->d.line->backsector && in->d.line->backsector->sectornum == CurSector->sectornum)
	{
		lineside = 1;
	}
	else
	{ // Dammit. Why does Doom have to allow non-closed sectors?
		if (in->d.line->backsector == NULL)
		{
			lineside = 0;
			CurSector = in->d.line->frontsector;
		}
		else
		{
			lineside = P_PointOnLineSide(Start, in->d.line);
			CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector;
		}
	}

	if (!(in->d.line->flags & ML_TWOSIDED))
	{
		entersector = NULL;
	}
	else
	{
		entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector;

		// For backwards compatibility: Ignore lines with the same sector on both sides.
		// This is the way Doom.exe did it and some WADs (e.g. Alien Vendetta MAP15) need it.
		if (i_compatflags & COMPATF_TRACE && in->d.line->backsector == in->d.line->frontsector)
		{
			// We must check special activation here because the code below is never reached.
			if (TraceFlags & TRACE_PCross)
			{
				P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_PCross);
			}
			if (TraceFlags & TRACE_Impact)
			{
				P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact);
			}
			return true;
		}
	}

	ff = CurSector->floorplane.ZatPoint(hit);
	fc = CurSector->ceilingplane.ZatPoint(hit);

	if (entersector != NULL)
	{
		bf = entersector->floorplane.ZatPoint(hit);
		bc = entersector->ceilingplane.ZatPoint(hit);
	}

	sector_t *hsec = CurSector->GetHeightSec();
	if (Results->CrossedWater == NULL &&
		hsec != NULL &&
		Start.Z > hsec->floorplane.ZatPoint(Start) &&
		hit.Z <= hsec->floorplane.ZatPoint(hit))
	{
		// hit crossed a water plane
		if (CheckSectorPlane(hsec, true))
		{
			Results->CrossedWater = &level.sectors[CurSector->sectornum];
			Results->CrossedWaterPos = Results->HitPos;
			Results->Distance = 0;
		}
	}

	if (hit.Z <= ff)
	{
		// hit floor in front of wall
		Results->HitType = TRACE_HitFloor;
		Results->HitTexture = CurSector->GetTexture(sector_t::floor);
	}
	else if (hit.Z >= fc)
	{
		// hit ceiling in front of wall
		Results->HitType = TRACE_HitCeiling;
		Results->HitTexture = CurSector->GetTexture(sector_t::ceiling);
	}
	else if (entersector == NULL ||
		hit.Z < bf || hit.Z > bc ||
		in->d.line->flags & WallMask)
	{
		// hit the wall
		Results->HitType = TRACE_HitWall;
		Results->Tier =
			entersector == NULL ? TIER_Middle :
			hit.Z <= bf ? TIER_Lower :
			hit.Z >= bc ? TIER_Upper : TIER_Middle;
		if (TraceFlags & TRACE_Impact)
		{
			P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact);
		}
	}
	else
	{ 	// made it past the wall
		// check for 3D floors first
		if (entersector->e->XFloor.ffloors.Size())
		{
			memcpy(&DummySector[sectorsel], entersector, sizeof(sector_t));
			entersector = &DummySector[sectorsel];
			sectorsel ^= 1;

			for (auto rover : entersector->e->XFloor.ffloors)
			{
				int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH);

				if (entershootthrough != inshootthrough && rover->flags&FF_EXISTS)
				{
					double ff_bottom = rover->bottom.plane->ZatPoint(hit);
					double ff_top = rover->top.plane->ZatPoint(hit);

					// clip to the part of the sector we are in
					if (hit.Z > ff_top)
					{
						// 3D floor height is the same as the floor height. We need to test a second spot to see if it is above or below
						if (fabs(bf - ff_top) < EQUAL_EPSILON)
						{
							double cf = entersector->floorplane.ZatPoint(entersector->centerspot);
							double ffc = rover->top.plane->ZatPoint(entersector->centerspot);
							if (ffc > cf)
							{
								bf = ff_top - EQUAL_EPSILON;
							}
						}
						
						// above
						if (bf < ff_top)
						{
							entersector->floorplane = *rover->top.plane;
							entersector->SetTexture(sector_t::floor, *rover->top.texture, false);
							entersector->ClearPortal(sector_t::floor);
							bf = ff_top;
						}
					}
					else if (hit.Z < ff_bottom)
					{
						// 3D floor height is the same as the ceiling height. We need to test a second spot to see if it is above or below
						if (fabs(bc - ff_bottom) < EQUAL_EPSILON)
						{
							double cc = entersector->ceilingplane.ZatPoint(entersector->centerspot);
							double fcc = rover->bottom.plane->ZatPoint(entersector->centerspot);
							if (fcc < cc)
							{
								bc = ff_bottom + EQUAL_EPSILON;
							}
						}

						//below
						if (bc > ff_bottom)
						{
							entersector->ceilingplane = *rover->bottom.plane;
							entersector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false);
							entersector->ClearPortal(sector_t::ceiling);
							bc = ff_bottom;
						}
					}
					else
					{
						//hit the edge - equivalent to hitting the wall
						Results->HitType = TRACE_HitWall;
						Results->Tier = TIER_FFloor;
						Results->ffloor = rover;
						if ((TraceFlags & TRACE_Impact) && in->d.line->special)
						{
							P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact);
						}
						goto cont;
					}
				}
			}
		}

		Results->HitType = TRACE_HitNone;
		if (TraceFlags & TRACE_PCross)
		{
			P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_PCross);
		}
		if (TraceFlags & TRACE_Impact)
		{ // This is incorrect for "impact", but Hexen did this, so
		  // we need to as well, for compatibility
			P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact);
		}
	}
cont:

	if (Results->HitType != TRACE_HitNone)
	{
		// We hit something, so figure out where exactly
		Results->Sector = &level.sectors[CurSector->sectornum];

		if (Results->HitType != TRACE_HitWall &&
			!CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor))
		{ // trace is parallel to the plane (or right on it)
			if (entersector == NULL)
			{
				Results->HitType = TRACE_HitWall;
				Results->Tier = TIER_Middle;
			}
			else
			{
				if (hit.Z <= bf || hit.Z >= bc)
				{
					Results->HitType = TRACE_HitWall;
					Results->Tier =
						hit.Z <= bf ? TIER_Lower :
						hit.Z >= bc ? TIER_Upper : TIER_Middle;
				}
				else
				{
					Results->HitType = TRACE_HitNone;
				}
			}
			if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact)
			{
				P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact);
			}
		}

		if (Results->HitType == TRACE_HitWall)
		{
			Results->HitPos = hit;
			SetSourcePosition();
			Results->Distance = dist;
			Results->Fraction = in->frac;
			Results->Line = in->d.line;
			Results->Side = lineside;
		}
	}

	if (TraceCallback != NULL && Results->HitType != TRACE_HitNone)
	{
		switch (TraceCallback(*Results, TraceCallbackData))
		{
		case TRACE_Stop:	return false;
		case TRACE_Abort:	Results->HitType = TRACE_HitNone; return false;
		case TRACE_Skip:	Results->HitType = TRACE_HitNone; break;
		default:			break;
		}
	}

	if (Results->HitType == TRACE_HitNone)
	{
		CurSector = entersector;
		EnterDist = dist;
		return true;
	}
	else
	{
		return false;
	}
}