bool FTraceInfo::CheckPlane (const secplane_t &plane) { double den = plane.Normal() | Vec; if (den != 0) { double num = (plane.Normal() | Start) + plane.fD(); double hitdist = -num / den; if (hitdist > EnterDist && hitdist < MaxDist) { Results->HitPos = Start + Vec * hitdist; SetSourcePosition(); Results->Distance = hitdist; Results->Fraction = hitdist / MaxDist; return true; } } return false; }
/* ================== Sound::SetSourcePosition ================== */ void Sound::SetSourcePosition(Vec2 pos) { SetSourcePosition(pos.x, pos.y, 0.f); }
bool FTraceInfo::ThingCheck(intercept_t *in, double dist, DVector3 hit) { if (hit.Z > in->d.thing->Top()) { // trace enters above actor if (Vec.Z >= 0) return true; // Going up: can't hit // Does it hit the top of the actor? dist = (in->d.thing->Top() - Start.Z) / Vec.Z; if (dist > MaxDist) return true; in->frac = dist / MaxDist; hit = Start + Vec * dist; // calculated coordinate is outside the actor's bounding box if (fabs(hit.X - in->d.thing->X()) > in->d.thing->radius || fabs(hit.Y - in->d.thing->Y()) > in->d.thing->radius) return true; } else if (hit.Z < in->d.thing->Z()) { // trace enters below actor if (Vec.Z <= 0) return true; // Going down: can't hit // Does it hit the bottom of the actor? dist = (in->d.thing->Z() - Start.Z) / Vec.Z; if (dist > MaxDist) return true; in->frac = dist / MaxDist; hit = Start + Vec * dist; // calculated coordinate is outside the actor's bounding box if (fabs(hit.X - in->d.thing->X()) > in->d.thing->radius || fabs(hit.Y - in->d.thing->Y()) > in->d.thing->radius) return true; } if (CurSector->e->XFloor.ffloors.Size()) { // check for 3D floor hits first. double ff_floor = CurSector->floorplane.ZatPoint(hit); double ff_ceiling = CurSector->ceilingplane.ZatPoint(hit); if (hit.Z > ff_ceiling && CurSector->PortalBlocksMovement(sector_t::ceiling)) // actor is hit above the current ceiling { Results->HitType = TRACE_HitCeiling; Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); } else if (hit.Z < ff_floor && CurSector->PortalBlocksMovement(sector_t::floor)) // actor is hit below the current floor { Results->HitType = TRACE_HitFloor; Results->HitTexture = CurSector->GetTexture(sector_t::floor); } else goto cont1; // the trace hit a 3D floor before the thing. // Calculate an intersection and abort. Results->Sector = &level.sectors[CurSector->sectornum]; if (!CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor)) { Results->HitType = TRACE_HitNone; } if (TraceCallback != NULL) { switch (TraceCallback(*Results, TraceCallbackData)) { case TRACE_Continue: return true; case TRACE_Stop: return false; case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; case TRACE_Skip: Results->HitType = TRACE_HitNone; return true; } } else { return false; } } cont1: Results->HitType = TRACE_HitActor; Results->HitPos = hit; SetSourcePosition(); Results->Distance = dist; Results->Fraction = in->frac; Results->Actor = in->d.thing; if (TraceCallback != NULL) { 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; return true; default: return true; } } else { return false; } }
bool FTraceInfo::TraceTraverse (int ptflags) { // Do a 3D floor check in the starting sector Setup3DFloors(); FPathTraverse it(Start.X, Start.Y, Vec.X * MaxDist, Vec.Y * MaxDist, ptflags | PT_DELTA, startfrac); intercept_t *in; int lastsplashsector = -1; while ((in = it.Next())) { // Deal with splashes in 3D floors (but only run once per sector, not each iteration - and stop if something was found.) if (Results->Crossed3DWater == NULL && lastsplashsector != CurSector->sectornum) { for (auto rover : CurSector->e->XFloor.ffloors) { if ((rover->flags & FF_EXISTS) && (rover->flags&FF_SWIMMABLE)) { if (Check3DFloorPlane(rover, false)) { // only consider if the plane is above the actual floor. if (rover->top.plane->ZatPoint(Results->HitPos) > CurSector->floorplane.ZatPoint(Results->HitPos)) { Results->Crossed3DWater = rover; Results->Crossed3DWaterPos = Results->HitPos; Results->Distance = 0; } } } } lastsplashsector = CurSector->sectornum; } double dist = MaxDist * in->frac; DVector3 hit = Start + Vec * dist; // Crossed a floor portal? if (Vec.Z < 0 && !CurSector->PortalBlocksMovement(sector_t::floor)) { // calculate position where the portal is crossed double portz = CurSector->GetPortalPlaneZ(sector_t::floor); if (hit.Z < portz && limitz > portz) { limitz = portz; EnterSectorPortal(it, sector_t::floor, (portz - Start.Z) / (Vec.Z * MaxDist), CurSector); continue; } } // ... or a ceiling portal? else if (Vec.Z > 0 && !CurSector->PortalBlocksMovement(sector_t::ceiling)) { // calculate position where the portal is crossed double portz = CurSector->GetPortalPlaneZ(sector_t::ceiling); if (hit.Z > portz && limitz < portz) { limitz = portz; EnterSectorPortal(it, sector_t::ceiling, (portz - Start.Z) / (Vec.Z * MaxDist), CurSector); continue; } } if (in->isaline) { if (in->d.line->isLinePortal() && P_PointOnLineSidePrecise(Start, in->d.line) == 0) { sector_t *entersector = in->d.line->backsector; if (entersector == NULL || (hit.Z >= entersector->floorplane.ZatPoint(hit) && hit.Z <= entersector->ceilingplane.ZatPoint(hit))) { FLinePortal *port = in->d.line->getPortal(); // The caller cannot handle portals without global offset. if (port->mType == PORTT_LINKED || !(TraceFlags & TRACE_PortalRestrict)) { EnterLinePortal(it, in); continue; } } } if (!LineCheck(in, dist, hit)) break; } else if ((in->d.thing->flags & ActorMask) && in->d.thing != IgnoreThis) { if (!ThingCheck(in, dist, hit)) break; } } if (Results->HitType == TRACE_HitNone) { if (CurSector->PortalBlocksMovement(sector_t::floor) && CheckSectorPlane(CurSector, true)) { Results->HitType = TRACE_HitFloor; Results->HitTexture = CurSector->GetTexture(sector_t::floor); } else if (CurSector->PortalBlocksMovement(sector_t::ceiling) && CheckSectorPlane(CurSector, false)) { Results->HitType = TRACE_HitCeiling; Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); } } // check for intersection with floor/ceiling Results->Sector = &level.sectors[CurSector->sectornum]; if (Results->CrossedWater == NULL && CurSector->heightsec != NULL && CurSector->heightsec->floorplane.ZatPoint(Start) < Start.Z && CurSector->heightsec->floorplane.ZatPoint(Results->HitPos) >= Results->HitPos.Z) { // Save the result so that the water check doesn't destroy it. FTraceResults *res = Results; FTraceResults hsecResult; Results = &hsecResult; if (CheckSectorPlane(CurSector->heightsec, true)) { Results->CrossedWater = &level.sectors[CurSector->sectornum]; Results->CrossedWaterPos = Results->HitPos; Results->Distance = 0; } Results = res; } if (Results->HitType == TRACE_HitNone && Results->Distance == 0) { Results->HitPos = Start + Vec * MaxDist; SetSourcePosition(); Results->Distance = MaxDist; Results->Fraction = 1.; } return Results->HitType != TRACE_HitNone; }
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; } }