mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-02-12 23:25:21 +00:00
Merged https://github.com/coelckers/gzdoom/pull/1517 (ViewPos - 3rd Edition)
This commit is contained in:
parent
3fd3a65baa
commit
2b367a6657
9 changed files with 217 additions and 30 deletions
|
@ -654,6 +654,42 @@ struct FDropItem
|
|||
int Amount;
|
||||
};
|
||||
|
||||
enum EViewPosFlags // [MC] Flags for SetViewPos.
|
||||
{
|
||||
VPSF_ABSOLUTEOFFSET = 1 << 1, // Don't include angles.
|
||||
VPSF_ABSOLUTEPOS = 1 << 2, // Use absolute position.
|
||||
};
|
||||
|
||||
class FViewPosition : public DObject
|
||||
{
|
||||
DECLARE_CLASS(FViewPosition, DObject);
|
||||
public:
|
||||
// Variables
|
||||
// Exposed to ZScript
|
||||
DVector3 Offset;
|
||||
int Flags;
|
||||
|
||||
// Functions
|
||||
FViewPosition()
|
||||
{
|
||||
Offset = { 0,0,0 };
|
||||
Flags = 0;
|
||||
}
|
||||
|
||||
void Set(DVector3 &off, int f = -1)
|
||||
{
|
||||
Offset = off;
|
||||
|
||||
if (f > -1)
|
||||
Flags = f;
|
||||
}
|
||||
|
||||
bool isZero()
|
||||
{
|
||||
return Offset.isZero();
|
||||
}
|
||||
};
|
||||
|
||||
const double MinVel = EQUAL_EPSILON;
|
||||
|
||||
// Map Object definition.
|
||||
|
@ -974,7 +1010,8 @@ public:
|
|||
DAngle SpriteAngle;
|
||||
DAngle SpriteRotation;
|
||||
DRotator Angles;
|
||||
DRotator ViewAngles; // Offsets for cameras
|
||||
DRotator ViewAngles; // Angle offsets for cameras
|
||||
FViewPosition *ViewPos; // Position offsets for cameras
|
||||
DVector2 Scale; // Scaling values; 1 is normal size
|
||||
double Alpha; // Since P_CheckSight makes an alpha check this can't be a float. It has to be a double.
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ struct secplane_t;
|
|||
struct FCheckPosition;
|
||||
struct FTranslatedLineTarget;
|
||||
struct FLinePortal;
|
||||
class FViewPosition;
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -392,6 +393,10 @@ void P_PlaySpawnSound(AActor *missile, AActor *spawner);
|
|||
// [RH] Position the chasecam
|
||||
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, FViewPosition *VP);
|
||||
|
||||
|
||||
// [RH] Means of death
|
||||
enum
|
||||
{
|
||||
|
|
|
@ -5462,6 +5462,27 @@ void P_AimCamera(AActor *t1, DVector3 &campos, DAngle &camangle, sector_t *&Came
|
|||
camangle = trace.SrcAngleFromTarget - 180.;
|
||||
}
|
||||
|
||||
// [MC] Used for ViewPos. Uses code borrowed from P_AimCamera.
|
||||
void P_AdjustViewPos(AActor *t1, DVector3 orig, DVector3 &campos, sector_t *&CameraSector, bool &unlinked, FViewPosition *VP)
|
||||
{
|
||||
FTraceResults trace;
|
||||
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) &&
|
||||
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.;
|
||||
}
|
||||
CameraSector = trace.Sector;
|
||||
unlinked = trace.unlinked;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
|
@ -369,6 +369,7 @@ void AActor::Serialize(FSerializer &arc)
|
|||
A("spawnorder", SpawnOrder)
|
||||
A("friction", Friction)
|
||||
A("SpriteOffset", SpriteOffset)
|
||||
("viewpos", ViewPos)
|
||||
A("userlights", UserLights);
|
||||
|
||||
SerializeTerrain(arc, "floorterrain", floorterrain, &def->floorterrain);
|
||||
|
@ -4952,6 +4953,12 @@ void AActor::OnDestroy ()
|
|||
UnlinkFromWorld (nullptr);
|
||||
flags |= MF_NOSECTOR|MF_NOBLOCKMAP;
|
||||
|
||||
if (ViewPos != nullptr)
|
||||
{
|
||||
ViewPos->Destroy();
|
||||
ViewPos = nullptr;
|
||||
}
|
||||
|
||||
// Transform any playing sound into positioned, non-actor sounds.
|
||||
S_RelinkSound (this, NULL);
|
||||
|
||||
|
|
|
@ -449,8 +449,8 @@ void R_InterpolateView (FRenderViewpoint &viewpoint, player_t *player, double Fr
|
|||
// Interpolating through line portals is a messy affair.
|
||||
// What needs be done is to store the portal transitions of the camera actor as waypoints
|
||||
// and then find out on which part of the path the current view lies.
|
||||
// Needless to say, this doesn't work for chasecam mode.
|
||||
if (!viewpoint.showviewer)
|
||||
// Needless to say, this doesn't work for chasecam mode or viewpos.
|
||||
if (!viewpoint.showviewer && !viewpoint.NoPortalPath)
|
||||
{
|
||||
double pathlen = 0;
|
||||
double zdiff = 0;
|
||||
|
@ -789,35 +789,113 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor
|
|||
iview->otic = nowtic;
|
||||
iview->Old = iview->New;
|
||||
}
|
||||
//==============================================================================================
|
||||
// Handles offsetting the camera with ChaseCam and/or viewpos.
|
||||
{
|
||||
AActor *mo = viewpoint.camera;
|
||||
FViewPosition *VP = mo->ViewPos;
|
||||
const DVector3 orig = { mo->Pos().XY(), mo->player ? mo->player->viewz : mo->Z() + mo->GetCameraHeight() };
|
||||
viewpoint.ActorPos = orig;
|
||||
|
||||
bool DefaultDraw = true;
|
||||
|
||||
if (player != NULL && gamestate != GS_TITLELEVEL &&
|
||||
((player->cheats & CF_CHASECAM) || (r_deathcamera && viewpoint.camera->health <= 0)))
|
||||
{
|
||||
sector_t *oldsector = viewpoint.ViewLevel->PointInRenderSubsector(iview->Old.Pos)->sector;
|
||||
// [RH] Use chasecam view
|
||||
DVector3 campos;
|
||||
DAngle camangle;
|
||||
P_AimCamera (viewpoint.camera, campos, camangle, viewpoint.sector, unlinked); // fixme: This needs to translate the angle, too.
|
||||
iview->New.Pos = campos;
|
||||
iview->New.Angles.Yaw = camangle;
|
||||
|
||||
viewpoint.showviewer = true;
|
||||
// Interpolating this is a very complicated thing because nothing keeps track of the aim camera's movement, so whenever we detect a portal transition
|
||||
// it's probably best to just reset the interpolation for this move.
|
||||
// Note that this can still cause problems with unusually linked portals
|
||||
if (viewpoint.sector->PortalGroup != oldsector->PortalGroup || (unlinked && ((iview->New.Pos.XY() - iview->Old.Pos.XY()).LengthSquared()) > 256*256))
|
||||
if (gamestate != GS_TITLELEVEL &&
|
||||
(player && ((player->cheats & CF_CHASECAM)) || (r_deathcamera && viewpoint.camera->health <= 0)))
|
||||
{
|
||||
iview->otic = nowtic;
|
||||
iview->Old = iview->New;
|
||||
r_NoInterpolate = true;
|
||||
// [RH] Use chasecam view
|
||||
DefaultDraw = false;
|
||||
DVector3 campos;
|
||||
DAngle camangle;
|
||||
P_AimCamera(viewpoint.camera, campos, camangle, viewpoint.sector, unlinked); // fixme: This needs to translate the angle, too.
|
||||
iview->New.Pos = campos;
|
||||
iview->New.Angles.Yaw = camangle;
|
||||
|
||||
viewpoint.showviewer = true;
|
||||
// Interpolating this is a very complicated thing because nothing keeps track of the aim camera's movement, so whenever we detect a portal transition
|
||||
// it's probably best to just reset the interpolation for this move.
|
||||
// Note that this can still cause problems with unusually linked portals
|
||||
if (viewpoint.sector->PortalGroup != oldsector->PortalGroup || (unlinked && ((iview->New.Pos.XY() - iview->Old.Pos.XY()).LengthSquared()) > 256 * 256))
|
||||
{
|
||||
iview->otic = nowtic;
|
||||
iview->Old = iview->New;
|
||||
r_NoInterpolate = true;
|
||||
}
|
||||
viewpoint.ActorPos = campos;
|
||||
}
|
||||
else if (VP) // No chase/death cam and player is alive, wants viewpos.
|
||||
{
|
||||
viewpoint.sector = viewpoint.ViewLevel->PointInRenderSubsector(iview->New.Pos.XY())->sector;
|
||||
viewpoint.showviewer = false;
|
||||
|
||||
// [MC] Ignores all portal portal transitions since it's meant to be absolute.
|
||||
// Modders must handle performing offsetting with the appropriate functions to get it to work.
|
||||
// Hint: Check P_AdjustViewPos.
|
||||
if (VP->Flags & VPSF_ABSOLUTEPOS)
|
||||
{
|
||||
iview->New.Pos = VP->Offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
DVector3 next = orig;
|
||||
|
||||
if (VP->isZero())
|
||||
{
|
||||
// Since viewpos isn't being used, it's safe to enable path interpolation
|
||||
viewpoint.NoPortalPath = false;
|
||||
}
|
||||
else if (VP->Flags & VPSF_ABSOLUTEOFFSET)
|
||||
{
|
||||
// No relativity added from angles.
|
||||
next += VP->Offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// [MC] Do NOT handle portals here! Trace must have the unportaled (absolute) position to
|
||||
// get the correct angle and distance. Trace automatically handles portals by itself.
|
||||
// Note: viewpos does not include view angles, and ViewZ/CameraHeight are applied before this.
|
||||
|
||||
DAngle yaw = mo->Angles.Yaw;
|
||||
DAngle pitch = mo->Angles.Pitch;
|
||||
DAngle roll = mo->Angles.Roll;
|
||||
DVector3 relx, rely, relz, Off = VP->Offset;
|
||||
DMatrix3x3 rot =
|
||||
DMatrix3x3(DVector3(0., 0., 1.), yaw.Cos(), yaw.Sin()) *
|
||||
DMatrix3x3(DVector3(0., 1., 0.), pitch.Cos(), pitch.Sin()) *
|
||||
DMatrix3x3(DVector3(1., 0., 0.), roll.Cos(), roll.Sin());
|
||||
relx = DVector3(1., 0., 0.)*rot;
|
||||
rely = DVector3(0., 1., 0.)*rot;
|
||||
relz = DVector3(0., 0., 1.)*rot;
|
||||
next += relx * Off.X + rely * Off.Y + relz * Off.Z;
|
||||
}
|
||||
|
||||
if (next != orig)
|
||||
{
|
||||
// [MC] Disable interpolation if the camera view is crossing through a portal. Sometimes
|
||||
// the player is made visible when crossing a portal and it's extremely jarring.
|
||||
// Also, disable the portal interpolation pathing entirely when using the viewpos feature.
|
||||
// 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);
|
||||
|
||||
if (viewpoint.sector->PortalGroup != oldsector->PortalGroup || (unlinked && ((iview->New.Pos.XY() - iview->Old.Pos.XY()).LengthSquared()) > 256 * 256))
|
||||
{
|
||||
iview->otic = nowtic;
|
||||
iview->Old = iview->New;
|
||||
r_NoInterpolate = true;
|
||||
}
|
||||
iview->New.Pos = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DefaultDraw)
|
||||
{
|
||||
iview->New.Pos = orig;
|
||||
viewpoint.sector = viewpoint.camera->Sector;
|
||||
viewpoint.showviewer = viewpoint.NoPortalPath = false;
|
||||
}
|
||||
viewpoint.ActorPos = campos;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewpoint.ActorPos = iview->New.Pos = { viewpoint.camera->Pos().XY(), viewpoint.camera->player ? viewpoint.camera->player->viewz : viewpoint.camera->Z() + viewpoint.camera->GetCameraHeight() };
|
||||
viewpoint.sector = viewpoint.camera->Sector;
|
||||
viewpoint.showviewer = false;
|
||||
}
|
||||
|
||||
// [MC] Apply the view angles first, which is the offsets. If the absolute isn't desired,
|
||||
|
|
|
@ -43,7 +43,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.
|
||||
|
||||
void SetViewAngle(const FViewWindow &viewwindow);
|
||||
|
||||
|
|
|
@ -1770,7 +1770,6 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, isFrozen, isFrozen)
|
|||
ACTION_RETURN_BOOL(isFrozen(self));
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// PlayerPawn functions
|
||||
|
@ -1796,7 +1795,31 @@ DEFINE_ACTION_FUNCTION_NATIVE(APlayerPawn, GetPrintableDisplayName, GetPrintable
|
|||
ACTION_RETURN_STRING(type->GetDisplayName());
|
||||
}
|
||||
|
||||
static void SetViewPos(AActor *self, double x, double y, double z, int flags)
|
||||
{
|
||||
if (!self->ViewPos)
|
||||
{
|
||||
self->ViewPos = Create<FViewPosition>();
|
||||
}
|
||||
|
||||
DVector3 pos = { x,y,z };
|
||||
self->ViewPos->Set(pos, flags);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetViewPos, SetViewPos)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_FLOAT(x);
|
||||
PARAM_FLOAT(y);
|
||||
PARAM_FLOAT(z);
|
||||
PARAM_INT(flags);
|
||||
SetViewPos(self, x, y, z, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
IMPLEMENT_CLASS(FViewPosition, false, false);
|
||||
DEFINE_FIELD_X(ViewPosition, FViewPosition, Offset)
|
||||
DEFINE_FIELD_X(ViewPosition, FViewPosition, Flags)
|
||||
|
||||
DEFINE_FIELD(DThinker, Level)
|
||||
DEFINE_FIELD(AActor, snext)
|
||||
|
@ -1975,6 +1998,7 @@ DEFINE_FIELD(AActor, friendlyseeblocks)
|
|||
DEFINE_FIELD(AActor, SpawnTime)
|
||||
DEFINE_FIELD(AActor, InventoryID)
|
||||
DEFINE_FIELD(AActor, ThruBits)
|
||||
DEFINE_FIELD(AActor, ViewPos)
|
||||
DEFINE_FIELD_NAMED(AActor, ViewAngles.Yaw, viewangle)
|
||||
DEFINE_FIELD_NAMED(AActor, ViewAngles.Pitch, viewpitch)
|
||||
DEFINE_FIELD_NAMED(AActor, ViewAngles.Roll, viewroll)
|
||||
|
|
|
@ -67,6 +67,12 @@ struct LinkContext
|
|||
voidptr render_list;
|
||||
}
|
||||
|
||||
class ViewPosition native
|
||||
{
|
||||
native readonly Vector3 Offset;
|
||||
native readonly int Flags;
|
||||
}
|
||||
|
||||
|
||||
class Actor : Thinker native
|
||||
{
|
||||
|
@ -89,6 +95,7 @@ class Actor : Thinker native
|
|||
// for some comments on these fields, see their native representations in actor.h.
|
||||
native readonly Actor snext; // next in sector list.
|
||||
native PlayerInfo Player;
|
||||
native readonly ViewPosition ViewPos; // Will be null until A_SetViewPos() is called for the first time.
|
||||
native readonly vector3 Pos;
|
||||
native vector3 Prev;
|
||||
native uint ThruBits;
|
||||
|
@ -1163,6 +1170,7 @@ class Actor : Thinker native
|
|||
native void A_SetViewAngle(double angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
|
||||
native void A_SetViewPitch(double pitch, int flags = 0, int ptr = AAPTR_DEFAULT);
|
||||
native void A_SetViewRoll(double roll, int flags = 0, int ptr = AAPTR_DEFAULT);
|
||||
native void SetViewPos(Vector3 offset, int flags = -1);
|
||||
deprecated("2.3", "User variables are deprecated in ZScript. Actor variables are directly accessible") native void A_SetUserVar(name varname, int value);
|
||||
deprecated("2.3", "User variables are deprecated in ZScript. Actor variables are directly accessible") native void A_SetUserArray(name varname, int index, int value);
|
||||
deprecated("2.3", "User variables are deprecated in ZScript. Actor variables are directly accessible") native void A_SetUserVarFloat(name varname, double value);
|
||||
|
|
|
@ -396,6 +396,13 @@ enum EActivationFlags
|
|||
|
||||
};
|
||||
|
||||
// [MC] Flags for SetViewPos.
|
||||
enum EViewPosFlags
|
||||
{
|
||||
VPSF_ABSOLUTEOFFSET = 1 << 1, // Don't include angles.
|
||||
VPSF_ABSOLUTEPOS = 1 << 2, // Use absolute position.
|
||||
};
|
||||
|
||||
// Flags for A_TakeInventory and A_TakeFromTarget
|
||||
enum ETakeFlags
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue