diff --git a/src/playsim/d_player.h b/src/playsim/d_player.h index 5b14793e7f..675813873e 100644 --- a/src/playsim/d_player.h +++ b/src/playsim/d_player.h @@ -375,6 +375,7 @@ public: int chickenPeck = 0; // chicken peck countdown int jumpTics = 0; // delay the next jump for a moment bool onground = 0; // Identifies if this player is on the ground or other object + bool crossingPortal = 0; // Crossing a portal (disables sprite from showing up) int respawn_time = 0; // [RH] delay respawning until this tic TObjPtr camera = MakeObjPtr(nullptr); // [RH] Whose eyes this player sees through diff --git a/src/playsim/p_local.h b/src/playsim/p_local.h index 45de6c4623..468a19d8c0 100644 --- a/src/playsim/p_local.h +++ b/src/playsim/p_local.h @@ -51,6 +51,7 @@ struct FCheckPosition; struct FTranslatedLineTarget; struct FLinePortal; class DViewPosition; +struct FRenderViewpoint; #include @@ -396,7 +397,7 @@ void P_PlaySpawnSound(AActor *missile, AActor *spawner); void P_AimCamera (AActor *t1, DVector3 &, DAngle &, sector_t *&sec, bool &unlinked); // [MC] Aiming for ViewPos -void P_AdjustViewPos(AActor *t1, DVector3 orig, DVector3 &, sector_t *&sec, bool &unlinked, DViewPosition *VP); +void P_AdjustViewPos(AActor *t1, DVector3 orig, DVector3 &, sector_t *&sec, bool &unlinked, DViewPosition *VP, FRenderViewpoint *view); // [RH] Means of death diff --git a/src/playsim/p_map.cpp b/src/playsim/p_map.cpp index c51ef2fb4f..572ec6cc44 100644 --- a/src/playsim/p_map.cpp +++ b/src/playsim/p_map.cpp @@ -2636,13 +2636,15 @@ bool P_TryMove(AActor *thing, const DVector2 &pos, auto p = thing->Level->GetConsolePlayer(); if (p) p->viewz += hit.pos.Z; // needs to be done here because otherwise the renderer will not catch the change. P_TranslatePortalAngle(ld, hit.angle); + if (thing->player && (port->mType == PORTT_INTERACTIVE || port->mType == PORTT_TELEPORT)) + thing->player->crossingPortal = true; } R_AddInterpolationPoint(hit); } if (port->mType == PORTT_LINKED) { continue; - } + } } break; } @@ -5600,24 +5602,47 @@ void P_AimCamera(AActor *t1, DVector3 &campos, DAngle &camangle, sector_t *&Came camangle = trace.SrcAngleFromTarget - DAngle::fromDeg(180.); } +struct ViewPosPortal +{ + int counter; +}; + +static ETraceStatus VPos_CheckPortal(FTraceResults &res, void *userdata) +{ + //[MC] Mirror how third person works. + ViewPosPortal *pc = (ViewPosPortal *)userdata; + + if (res.HitType == TRACE_CrossingPortal) + { + res.HitType = TRACE_HitNone; // Needed to force the trace to continue appropriately. + pc->counter++; + return TRACE_Skip; + } + if (res.HitType == TRACE_HitActor) + { + return TRACE_Skip; + } + return TRACE_Stop; +} + // [MC] Used for ViewPos. Uses code borrowed from P_AimCamera. -void P_AdjustViewPos(AActor *t1, DVector3 orig, DVector3 &campos, sector_t *&CameraSector, bool &unlinked, DViewPosition *VP) +void P_AdjustViewPos(AActor *t1, DVector3 orig, DVector3 &campos, sector_t *&CameraSector, bool &unlinked, DViewPosition *VP, FRenderViewpoint *view) { FTraceResults trace; + ViewPosPortal pc; + pc.counter = 0; const DVector3 vvec = campos - orig; const double distance = vvec.Length(); // Trace handles all of the portal crossing, which is why there is no usage of Vec#Offset(Z). - if (Trace(orig, t1->Sector, vvec.Unit(), distance, 0, 0, t1, trace) && + if (Trace(orig, CameraSector, vvec.Unit(), distance, 0, 0, t1, trace, TRACE_ReportPortals, VPos_CheckPortal, &pc) && trace.Distance > 5) - { - // Position camera slightly in front of hit thing campos = orig + vvec.Unit() * (trace.Distance - 5); - } else - { campos = trace.HitPos - trace.HitVector * 1 / 256.; - } + + + if (pc.counter > 2) view->noviewer = true; CameraSector = trace.Sector; unlinked = trace.unlinked; } diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index ddb5f047a1..87c58a0714 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -3771,7 +3771,8 @@ void AActor::Tick () } else { - + if (player) + player->crossingPortal = false; if (!player || !(player->cheats & CF_PREDICTING)) { // Handle powerup effects here so that the order is controlled diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index 2d6ba1ed73..be8289ef6b 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -770,13 +770,23 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t { return; } - // Some added checks if the camera actor is not supposed to be seen. It can happen that some portal setup has this actor in view in which case it may not be skipped here if (viewmaster == camera && !vp.showviewer) { + if (vp.noviewer || (viewmaster->player && viewmaster->player->crossingPortal)) return; DVector3 vieworigin = viewmaster->Pos(); if (thruportal == 1) vieworigin += di->Level->Displacements.getOffset(viewmaster->Sector->PortalGroup, sector->PortalGroup); if (fabs(vieworigin.X - vp.ActorPos.X) < 2 && fabs(vieworigin.Y - vp.ActorPos.Y) < 2) return; + + // Necessary in order to prevent sprite pop-ins with viewpos and models. + auto* sec = viewmaster->Sector; + if (sec && !sec->PortalBlocksMovement(sector_t::ceiling)) + { + double zh = sec->GetPortalPlaneZ(sector_t::ceiling); + double top = (viewmaster->player ? max(viewmaster->player->viewz, viewmaster->Top()) + 1 : viewmaster->Top()); + if (viewmaster->Z() < zh && top >= zh) + return; + } } // Thing is invisible if close to the camera. if (viewmaster->renderflags & RF_MAYBEINVISIBLE) @@ -858,7 +868,6 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t { return; } - if (!modelframe) { bool mirror = false; diff --git a/src/rendering/r_utility.cpp b/src/rendering/r_utility.cpp index 70a70ad51c..4daf7af7cc 100644 --- a/src/rendering/r_utility.cpp +++ b/src/rendering/r_utility.cpp @@ -600,6 +600,7 @@ void R_InterpolateView (FRenderViewpoint &viewpoint, player_t *player, double Fr else break; } } + if (moved) viewpoint.noviewer = true; } //========================================================================== @@ -947,7 +948,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor // Interpolation still happens with everything else though and seems to work fine. DefaultDraw = false; viewpoint.NoPortalPath = true; - P_AdjustViewPos(mo, orig, next, viewpoint.sector, unlinked, VP); + P_AdjustViewPos(mo, orig, next, viewpoint.sector, unlinked, VP, &viewpoint); if (viewpoint.sector->PortalGroup != oldsector->PortalGroup || (unlinked && ((iview->New.Pos.XY() - iview->Old.Pos.XY()).LengthSquared()) > 256 * 256)) { diff --git a/src/rendering/r_utility.h b/src/rendering/r_utility.h index 77254ebece..7ba6e738cf 100644 --- a/src/rendering/r_utility.h +++ b/src/rendering/r_utility.h @@ -44,7 +44,7 @@ struct FRenderViewpoint int extralight; // extralight to be added to this viewpoint bool showviewer; // show the camera actor? bool NoPortalPath; // Disable portal interpolation path for actor viewpos. - + bool noviewer; // Force camera sprite off for first person. void SetViewAngle(const FViewWindow &viewwindow); };