Isometric Mode and Robin (#2618)

* Feature-complete isometric mode fork.

* Dithered transparency condition tweaks.

* Dithered transparency for non-corpse monsters only (and missiles).

* SpectatorCamera vertical shift.

* Including math.h in hw_sprites.cpp to keep visual studio happy (it couldn't find M_SQRT2 definition).

* Defining MY_SQRT2 in hw_sprites.cpp to keep visual studio happy (it couldn't find M_SQRT2 definition).

* Defining MY_SQRT2 in r_utility.cpp also to keep visual studio happy.

* retrigger checks

* Have correct sprite angle-frame face the camera with orthographic projection enabled.

* Dithered Transparency now works properly on 3D floors. Moved that dither-trans flag setting code within hw_bsp.cpp to handle double-processing of linedefs. Added helper functions to FRenderViewpoint class 'bool IsOrtho()' and 'bool IsAllowedOoB()' to clean up checks everywhere in the code.

* Fixed indents. Added bbox property to subsector struct and use it instead of BSP nodes and Clippers (creating a bbox around viewpoint and checking for overlap) in orthographic mode when no fog of war is active. Turns out to be much faster. Though you need really big maps (Winter's Fury MAP01) to see a difference in fps.

* Non-linux checks don't like uint. Changed to unsigned int.

* Small change of a float to camera.zs. Ignore for testing. Should make no difference.

* Update actor.h to remain mergeable

RF2_NOMIPMAP was introduced, so I had to displace RF_ISOMETRICSPRITES to next bit.
This commit is contained in:
dileepvr 2024-08-11 08:04:40 -06:00 committed by GitHub
parent b044baf850
commit 95b264bdb6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 819 additions and 84 deletions

View file

@ -2816,7 +2816,7 @@ void DAutomap::drawPlayers ()
int numarrowlines;
double vh = players[consoleplayer].viewheight;
DVector2 pos = players[consoleplayer].camera->InterpolatedPosition(r_viewpoint.TicFrac).XY();
DVector2 pos = players[consoleplayer].mo->InterpolatedPosition(r_viewpoint.TicFrac).XY();
pt.x = pos.X;
pt.y = pos.Y;
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
@ -2826,7 +2826,7 @@ void DAutomap::drawPlayers ()
}
else
{
angle = players[consoleplayer].camera->InterpolatedAngles(r_viewpoint.TicFrac).Yaw;
angle = players[consoleplayer].mo->InterpolatedAngles(r_viewpoint.TicFrac).Yaw;
}
if (am_cheat != 0 && CheatMapArrow.Size() > 0)

View file

@ -54,3 +54,6 @@ EXTERN_CVAR(Int, gl_shadowmap_filter)
EXTERN_CVAR(Bool, gl_brightfog)
EXTERN_CVAR(Bool, gl_lightadditivesurfaces)
EXTERN_CVAR(Bool, gl_notexturefill)
EXTERN_CVAR(Bool, r_radarclipper)
EXTERN_CVAR(Bool, r_dithertransparency)

View file

@ -25,7 +25,7 @@ enum ERenderEffect
EFF_SPHEREMAP,
EFF_BURN,
EFF_STENCIL,
EFF_DITHERTRANS,
MAX_EFFECTS
};

View file

@ -299,6 +299,7 @@ const FEffectShader effectshaders[] =
{ "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" },
{ "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
{ "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
{ "dithertrans", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", "#define NO_ALPHATEST\n#define DITHERTRANS\n" },
};
int DFrameBuffer::GetShaderCount()

View file

@ -146,11 +146,27 @@ float VREyeInfo::getShift() const
return vr_swap_eyes ? -res : res;
}
VSMatrix VREyeInfo::GetProjection(float fov, float aspectRatio, float fovRatio) const
VSMatrix VREyeInfo::GetProjection(float fov, float aspectRatio, float fovRatio, bool iso_ortho) const
{
VSMatrix result;
if (mShiftFactor == 0)
if (iso_ortho) // Orthographic projection for isometric viewpoint
{
double zNear = -3.0/fovRatio; // screen->GetZNear();
double zFar = screen->GetZFar();
double fH = tan(DEG2RAD(fov) / 2) / fovRatio;
double fW = fH * aspectRatio * mScaleFactor;
double left = -fW;
double right = fW;
double bottom = -fH;
double top = fH;
VSMatrix fmat(1);
fmat.ortho((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar);
return fmat;
}
else if (mShiftFactor == 0)
{
float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio)));
result.perspective(fovy, aspectRatio, screen->GetZNear(), screen->GetZFar());

View file

@ -27,7 +27,7 @@ struct VREyeInfo
float mShiftFactor;
float mScaleFactor;
VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const;
VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio, bool iso_ortho) const;
DVector3 GetViewShift(float yaw) const;
private:
float getShift() const;

View file

@ -72,6 +72,13 @@ public:
m_Box[BOXTOP] = pos.Y;
}
bool CheckOverlap(const FBoundingBox &box2)
{
bool hori = (Left() > box2.Right()) || (Right() < box2.Left());
bool vert = (Bottom() > box2.Top()) || (Top() < box2.Bottom());
return !(hori || vert); // [DVR] For alternative space partition
}
inline double Top () const { return m_Box[BOXTOP]; }
inline double Bottom () const { return m_Box[BOXBOTTOM]; }
inline double Left () const { return m_Box[BOXLEFT]; }

View file

@ -1819,6 +1819,7 @@ MapFlagHandlers[] =
{ "disableskyboxao", MITYPE_CLRFLAG3, LEVEL3_SKYBOXAO, 0 },
{ "avoidmelee", MITYPE_SETFLAG3, LEVEL3_AVOIDMELEE, 0 },
{ "attenuatelights", MITYPE_SETFLAG3, LEVEL3_ATTENUATE, 0 },
{ "nofogofwar", MITYPE_SETFLAG3, LEVEL3_NOFOGOFWAR, 0 },
{ "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes
{ "nopassover", MITYPE_COMPATFLAG, COMPATF_NO_PASSMOBJ, 0 },
{ "passover", MITYPE_CLRCOMPATFLAG, COMPATF_NO_PASSMOBJ, 0 },

View file

@ -269,7 +269,8 @@ enum ELevelFlags : unsigned int
LEVEL3_NOSHADOWMAP = 0x00010000, // disables shadowmaps for a given level.
LEVEL3_AVOIDMELEE = 0x00020000, // global flag needed for proper MBF support.
LEVEL3_NOJUMPDOWN = 0x00040000, // only for MBF21. Inverse of MBF's dog_jumping flag.
LEVEL3_LIGHTCREATED = 0x00080000, // a light had been created in the last frame
LEVEL3_LIGHTCREATED = 0x00080000, // a light had been created in the last frame
LEVEL3_NOFOGOFWAR = 0x00100000, // disables effect of r_radarclipper CVAR on this map
};

View file

@ -303,6 +303,7 @@ struct secplane_t
DVector3 normal;
double D, negiC; // negative iC because that also saves a negation in all methods using this.
public:
bool dithertransflag; // Render plane with dithering transparency shader (gets reset every frame)
friend FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p, secplane_t *def);
void set(double aa, double bb, double cc, double dd)
@ -1179,6 +1180,8 @@ enum
WALLF_ABSLIGHTING_TOP = WALLF_ABSLIGHTING_TIER << 0, // Top tier light is absolute instead of relative
WALLF_ABSLIGHTING_MID = WALLF_ABSLIGHTING_TIER << 1, // Mid tier light is absolute instead of relative
WALLF_ABSLIGHTING_BOTTOM = WALLF_ABSLIGHTING_TIER << 2, // Bottom tier light is absolute instead of relative
WALLF_DITHERTRANS = 8192, // Render with dithering transparency shader (gets reset every frame)
};
struct side_t
@ -1654,6 +1657,7 @@ struct subsector_t
uint32_t numlines;
uint16_t flags;
short mapsection;
FBoundingBox bbox; // [DVR] For alternative space culling in orthographic projection with no fog of war
// subsector related GL data
int validcount;

View file

@ -3262,6 +3262,25 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position)
Level->aabbTree = new DoomLevelAABBTree(Level);
Level->levelMesh = new DoomLevelMesh(*Level);
// [DVR] Populate subsector->bbox for alternative space culling in orthographic projection with no fog of war
subsector_t* sub = &Level->subsectors[0];
seg_t* seg;
for (unsigned int kk = 0; kk < Level->subsectors.Size(); kk++)
{
sub[kk].bbox.ClearBox();
unsigned int count = sub[kk].numlines;
seg = sub[kk].firstline;
while(count--)
{
if((seg->v1 != nullptr) && (seg->v2 != nullptr))
{
sub[kk].bbox.AddToBox(seg->v1->fPos());
sub[kk].bbox.AddToBox(seg->v2->fPos());
}
seg++;
}
}
}
//==========================================================================

View file

@ -502,6 +502,7 @@ enum ActorRenderFlag2
RF2_FLIPSPRITEOFFSETY = 0x0020,
RF2_CAMFOLLOWSPLAYER = 0x0040, // Matches the cam's base position and angles to the main viewpoint.
RF2_NOMIPMAP = 0x0080, // [Nash] forces no mipmapping on sprites. Useful for tiny sprites that need to remain visually crisp
RF2_ISOMETRICSPRITES = 0x0100,
};
// This translucency value produces the closest match to Heretic's TINTTAB.
@ -692,8 +693,10 @@ struct FDropItem
enum EViewPosFlags // [MC] Flags for SetViewPos.
{
VPSF_ABSOLUTEOFFSET = 1 << 1, // Don't include angles.
VPSF_ABSOLUTEPOS = 1 << 2, // Use absolute position.
VPSF_ABSOLUTEOFFSET = 1 << 1, // Don't include angles.
VPSF_ABSOLUTEPOS = 1 << 2, // Use absolute position.
VPSF_ALLOWOUTOFBOUNDS = 1 << 3, // Allow viewpoint to go out of bounds (hardware renderer only).
VPSF_ORTHOGRAPHIC = 1 << 4, // Use orthographic projection (hardware renderer only).
};
enum EAnimOverrideFlags
@ -1123,6 +1126,8 @@ public:
DAngle SpriteAngle;
DAngle SpriteRotation;
DVector2 AutomapOffsets; // Offset the actors' sprite view on the automap by these coordinates.
float isoscaleY; // Y-scale to compensate for Y-billboarding for isometric sprites
float isotheta; // Rotation angle to compensate for Y-billboarding for isometric sprites
DRotator Angles;
DRotator ViewAngles; // Angle offsets for cameras
TObjPtr<DViewPosition*> ViewPos; // Position offsets for cameras

View file

@ -82,6 +82,7 @@
#include "p_blockmap.h"
#include "p_3dmidtex.h"
#include "vm.h"
#include "d_main.h"
#include "decallib.h"
@ -5577,33 +5578,42 @@ void R_OffsetView(FRenderViewpoint& viewPoint, const DVector3& dir, const double
{
const DAngle baseYaw = dir.Angle();
FTraceResults trace = {};
if (Trace(viewPoint.Pos, viewPoint.sector, dir, distance, 0u, 0u, nullptr, trace))
if (viewPoint.IsAllowedOoB() && V_IsHardwareRenderer())
{
viewPoint.Pos += dir * distance;
viewPoint.sector = viewPoint.ViewLevel->PointInRenderSubsector(viewPoint.Pos)->sector;
}
else if (Trace(viewPoint.Pos, viewPoint.sector, dir, distance, 0u, 0u, nullptr, trace))
{
viewPoint.Pos = trace.HitPos - trace.HitVector * min<double>(5.0, trace.Distance);
viewPoint.sector = viewPoint.ViewLevel->PointInRenderSubsector(viewPoint.Pos)->sector;
viewPoint.Angles.Yaw += deltaangle(baseYaw, trace.SrcAngleFromTarget);
}
else
{
viewPoint.Pos = trace.HitPos;
viewPoint.sector = trace.Sector;
viewPoint.Angles.Yaw += deltaangle(baseYaw, trace.SrcAngleFromTarget);
}
viewPoint.Angles.Yaw += deltaangle(baseYaw, trace.SrcAngleFromTarget);
// TODO: Why does this even need to be done? Please fix tracers already.
if (dir.Z < 0.0)
if (!viewPoint.IsAllowedOoB() || !V_IsHardwareRenderer())
{
while (!viewPoint.sector->PortalBlocksMovement(sector_t::floor) && viewPoint.Pos.Z < viewPoint.sector->GetPortalPlaneZ(sector_t::floor))
if (dir.Z < 0.0)
{
viewPoint.Pos += viewPoint.sector->GetPortalDisplacement(sector_t::floor);
viewPoint.sector = viewPoint.sector->GetPortal(sector_t::floor)->mDestination;
while (!viewPoint.sector->PortalBlocksMovement(sector_t::floor) && viewPoint.Pos.Z < viewPoint.sector->GetPortalPlaneZ(sector_t::floor))
{
viewPoint.Pos += viewPoint.sector->GetPortalDisplacement(sector_t::floor);
viewPoint.sector = viewPoint.sector->GetPortal(sector_t::floor)->mDestination;
}
}
}
else if (dir.Z > 0.0)
{
while (!viewPoint.sector->PortalBlocksMovement(sector_t::ceiling) && viewPoint.Pos.Z > viewPoint.sector->GetPortalPlaneZ(sector_t::ceiling))
else if (dir.Z > 0.0)
{
viewPoint.Pos += viewPoint.sector->GetPortalDisplacement(sector_t::ceiling);
viewPoint.sector = viewPoint.sector->GetPortal(sector_t::ceiling)->mDestination;
while (!viewPoint.sector->PortalBlocksMovement(sector_t::ceiling) && viewPoint.Pos.Z > viewPoint.sector->GetPortalPlaneZ(sector_t::ceiling))
{
viewPoint.Pos += viewPoint.sector->GetPortalDisplacement(sector_t::ceiling);
viewPoint.sector = viewPoint.sector->GetPortal(sector_t::ceiling)->mDestination;
}
}
}
}

View file

@ -633,6 +633,9 @@ cont:
case TRACE_Stop:
return false;
case TRACE_ContinueOutOfBounds:
return true;
case TRACE_Abort:
Results->HitType = TRACE_HitNone;
return false;
@ -732,6 +735,7 @@ bool FTraceInfo::ThingCheck(intercept_t *in, double dist, DVector3 hit)
switch (TraceCallback(*Results, TraceCallbackData))
{
case TRACE_Continue: return true;
case TRACE_ContinueOutOfBounds: 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;

View file

@ -109,6 +109,7 @@ enum ETraceStatus
TRACE_Continue, // continue the trace, returning this hit if there are none further along
TRACE_Skip, // continue the trace; do not return this hit
TRACE_Abort, // stop the trace, returning no hits
TRACE_ContinueOutOfBounds, // continue the trace through walls; don't use this for railguns
};
bool Trace(const DVector3 &start, sector_t *sector, const DVector3 &direction, double maxDist,

View file

@ -163,7 +163,11 @@ sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bou
di->Viewpoint.FieldOfView = DAngle::fromDeg(fov); // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint)
// Stereo mode specific perspective projection
di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio);
float inv_iso_dist = 1.0f;
bool iso_ortho = (camera->ViewPos != NULL) && (camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC);
if (iso_ortho && (camera->ViewPos->Offset.Length() > 0)) inv_iso_dist = 1.0/camera->ViewPos->Offset.Length();
di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio * inv_iso_dist, iso_ortho);
// Stereo mode specific viewpoint adjustment
vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees());
di->SetupView(RenderState, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false);

View file

@ -51,11 +51,24 @@
CVAR(Bool, gl_multithread, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
EXTERN_CVAR(Float, r_actorspriteshadowdist)
EXTERN_CVAR(Bool, r_radarclipper)
EXTERN_CVAR(Bool, r_dithertransparency)
thread_local bool isWorkerThread;
ctpl::thread_pool renderPool(1);
bool inited = false;
const int MAXDITHERACTORS = 20; // Maximum number of enemies that can set dither-transparency flags
AActor* RenderedTargets[MAXDITHERACTORS];
int RTnum;
void ClearDitherTargets()
{
RTnum = 0; // Number of rendered enemies/targets
for (int ii = 0; ii < MAXDITHERACTORS; ii++)
RenderedTargets[ii] = nullptr;
}
struct RenderJob
{
enum
@ -269,6 +282,20 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip)
auto &clipper = *mClipper;
angle_t startAngle = clipper.GetClipAngle(seg->v2);
angle_t endAngle = clipper.GetClipAngle(seg->v1);
auto &clipperr = *rClipper;
angle_t startAngleR = clipperr.PointToPseudoAngle(seg->v2->fX(), seg->v2->fY());
angle_t endAngleR = clipperr.PointToPseudoAngle(seg->v1->fX(), seg->v1->fY());
if(r_radarclipper && !(Level->flags3 & LEVEL3_NOFOGOFWAR) && (startAngleR - endAngleR >= ANGLE_180))
{
if (!seg->backsector) clipperr.SafeAddClipRange(startAngleR, endAngleR);
else if((seg->sidedef != nullptr) && !uint8_t(seg->sidedef->Flags & WALLF_POLYOBJ) && (currentsector->sectornum != seg->backsector->sectornum))
{
if (in_area == area_default) in_area = hw_CheckViewArea(seg->v2, seg->v1, seg->frontsector, seg->backsector);
backsector = hw_FakeFlat(seg->backsector, in_area, true);
if (hw_CheckClip(seg->sidedef, currentsector, backsector)) clipperr.SafeAddClipRange(startAngleR, endAngleR);
}
}
// Back side, i.e. backface culling - read: endAngle >= startAngle!
if (startAngle-endAngle<ANGLE_180)
@ -280,25 +307,53 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip)
{
if (!(currentsubsector->flags & SSECMF_DRAWN))
{
if (clipper.SafeCheckRange(startAngle, endAngle))
if (clipper.SafeCheckRange(startAngle, endAngle) && (!r_radarclipper || (Level->flags3 & LEVEL3_NOFOGOFWAR)))
{
currentsubsector->flags |= SSECMF_DRAWN;
currentsubsector->flags |= SSECMF_DRAWN;
}
if ((r_radarclipper || !(Level->flags3 & LEVEL3_NOFOGOFWAR)) && clipperr.SafeCheckRange(startAngleR, endAngleR))
{
currentsubsector->flags |= SSECMF_DRAWN;
}
}
return;
}
if (!clipper.SafeCheckRange(startAngle, endAngle))
if (!clipper.SafeCheckRange(startAngle, endAngle))
{
return;
}
currentsubsector->flags |= SSECMF_DRAWN;
auto &clipperv = *vClipper;
angle_t startPitch = clipperv.PointToPseudoPitch(seg->v1->fX(), seg->v1->fY(), currentsector->floorplane.ZatPoint(seg->v1));
angle_t endPitch = clipperv.PointToPseudoPitch(seg->v1->fX(), seg->v1->fY(), currentsector->ceilingplane.ZatPoint(seg->v1));
angle_t startPitch2 = clipperv.PointToPseudoPitch(seg->v2->fX(), seg->v2->fY(), currentsector->floorplane.ZatPoint(seg->v2));
angle_t endPitch2 = clipperv.PointToPseudoPitch(seg->v2->fX(), seg->v2->fY(), currentsector->ceilingplane.ZatPoint(seg->v2));
angle_t temp;
// Wall can be tilted from viewpoint perspective. Find vertical extent on screen in psuedopitch units (0 to 2, bottom to top)
if(int(startPitch) > int(startPitch2)) // Handle zero crossing
{
temp = startPitch; startPitch = startPitch2; startPitch2 = temp; // exchange
}
if(int(endPitch) > int(endPitch2)) // Handle zero crossing
{
temp = endPitch; endPitch = endPitch2; endPitch2 = temp; // exchange
}
if (!clipperv.SafeCheckRange(startPitch, endPitch2))
{
return;
}
if (!r_radarclipper || (Level->flags3 & LEVEL3_NOFOGOFWAR) || clipperr.SafeCheckRange(startAngleR, endAngleR))
currentsubsector->flags |= SSECMF_DRAWN;
uint8_t ispoly = uint8_t(seg->sidedef->Flags & WALLF_POLYOBJ);
if (!seg->backsector)
{
clipper.SafeAddClipRange(startAngle, endAngle);
if(!Viewpoint.IsAllowedOoB())
if (!(seg->sidedef->Flags & WALLF_DITHERTRANS)) clipper.SafeAddClipRange(startAngle, endAngle);
}
else if (!ispoly) // Two-sided polyobjects never obstruct the view
{
@ -325,7 +380,8 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip)
if (hw_CheckClip(seg->sidedef, currentsector, backsector))
{
clipper.SafeAddClipRange(startAngle, endAngle);
if(!Viewpoint.IsAllowedOoB() && !(seg->sidedef->Flags & WALLF_DITHERTRANS))
clipper.SafeAddClipRange(startAngle, endAngle);
}
}
}
@ -526,13 +582,14 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector)
{
sector_t * sec=sub->sector;
// Handle all things in sector.
const auto &vp = Viewpoint;
const auto &vp = Viewpoint;
for (auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext)
{
auto thing = p->m_thing;
if (thing->validcount == validcount) continue;
thing->validcount = validcount;
if(Viewpoint.IsAllowedOoB() && thing->Sector->isSecret() && thing->Sector->wasSecret() && !r_radarclipper) continue; // This covers things that are touching non-secret sectors
FIntCVar *cvar = thing->GetInfo()->distancecheck;
if (cvar != nullptr && *cvar >= 0)
{
@ -666,6 +723,51 @@ void HWDrawInfo::DoSubsector(subsector_t * sub)
fakesector=hw_FakeFlat(sector, in_area, false);
if(Viewpoint.IsAllowedOoB() && sector->isSecret() && sector->wasSecret() && !r_radarclipper) return;
// cull everything if subsector outside vertical clipper
if ((sub->polys == nullptr) && (!Viewpoint.IsOrtho() || !((Level->flags3 & LEVEL3_NOFOGOFWAR) || !r_radarclipper)))
{
auto &clipper = *mClipper;
auto &clipperv = *vClipper;
auto &clipperr = *rClipper;
int count = sub->numlines;
seg_t * seg = sub->firstline;
bool anglevisible = false;
bool pitchvisible = false;
bool radarvisible = false;
angle_t pitchtemp;
angle_t pitchmin = ANGLE_90;
angle_t pitchmax = 0;
while (count--)
{
if((seg->v1 != nullptr) && (seg->v2 != nullptr))
{
angle_t startAngle = clipper.GetClipAngle(seg->v2);
angle_t endAngle = clipper.GetClipAngle(seg->v1);
if (startAngle-endAngle >= ANGLE_180) anglevisible |= clipper.SafeCheckRange(startAngle, endAngle);
angle_t startAngleR = clipperr.PointToPseudoAngle(seg->v2->fX(), seg->v2->fY());
angle_t endAngleR = clipperr.PointToPseudoAngle(seg->v1->fX(), seg->v1->fY());
if (startAngleR-endAngleR >= ANGLE_180)
radarvisible |= (clipperr.SafeCheckRange(startAngleR, endAngleR) || (Level->flags3 & LEVEL3_NOFOGOFWAR) || ((sub->flags & SSECMF_DRAWN) && !deathmatch));
pitchmin = clipperv.PointToPseudoPitch(seg->v1->fX(), seg->v1->fY(), sector->floorplane.ZatPoint(seg->v1));
pitchmax = clipperv.PointToPseudoPitch(seg->v1->fX(), seg->v1->fY(), sector->ceilingplane.ZatPoint(seg->v1));
pitchvisible |= clipperv.SafeCheckRange(pitchmin, pitchmax);
if (pitchvisible && anglevisible && radarvisible) break;
pitchtemp = clipperv.PointToPseudoPitch(seg->v2->fX(), seg->v2->fY(), sector->floorplane.ZatPoint(seg->v2));
if (int(pitchmin) > int(pitchtemp)) pitchmin = pitchtemp;
pitchtemp = clipperv.PointToPseudoPitch(seg->v2->fX(), seg->v2->fY(), sector->ceilingplane.ZatPoint(seg->v2));
if (int(pitchmax) < int(pitchtemp)) pitchmax = pitchtemp;
pitchvisible |= clipperv.SafeCheckRange(pitchmin, pitchmax);
if (pitchvisible && anglevisible && radarvisible) break;
}
seg++;
}
// Skip subsector if outside vertical or horizontal clippers or is in unexplored territory (fog of war)
if(!pitchvisible || !anglevisible || (!radarvisible && r_radarclipper)) return;
}
if (mClipPortal)
{
int clipres = mClipPortal->ClipSubsector(sub);
@ -723,6 +825,20 @@ void HWDrawInfo::DoSubsector(subsector_t * sub)
SetupSprite.Unclock();
}
}
if (r_dithertransparency && Viewpoint.IsAllowedOoB() && (RTnum < MAXDITHERACTORS))
{
// [DVR] Not parallelizable due to variables RTnum and RenderedTargets[]
for (auto p = sector->touching_renderthings; p != nullptr; p = p->m_snext)
{
auto thing = p->m_thing;
if (thing->validcount == validcount) continue; // Don't double count
if (((thing->flags3 & MF3_ISMONSTER) && !(thing->flags & MF_CORPSE)) || (thing->flags & MF_MISSILE))
{
if (RTnum < MAXDITHERACTORS) RenderedTargets[RTnum++] = thing;
else break;
}
}
}
}
if (gl_render_flats)
@ -843,19 +959,62 @@ void HWDrawInfo::RenderBSPNode (void *node)
if (!(no_renderflags[bsp->Index()] & SSRF_SEEN))
return;
}
if (Viewpoint.IsOrtho())
{
if (!vClipper->CheckBoxOrthoPitch(bsp->bbox[side]))
{
if (!(no_renderflags[bsp->Index()] & SSRF_SEEN))
return;
}
}
node = bsp->children[side];
}
DoSubsector ((subsector_t *)((uint8_t *)node - 1));
}
// No need for clipping inside frustum if no fog of war (How is this faster!)
void HWDrawInfo::RenderOrthoNoFog()
{
if (Viewpoint.IsOrtho() && ((Level->flags3 & LEVEL3_NOFOGOFWAR) || !r_radarclipper))
{
double vxdbl = Viewpoint.camera->X();
double vydbl = Viewpoint.camera->Y();
double ext = Viewpoint.camera->ViewPos->Offset.Length() ?
3.0 * Viewpoint.camera->ViewPos->Offset.Length() : 100.0;
FBoundingBox viewbox(vxdbl, vydbl, ext);
for (unsigned int kk = 0; kk < Level->subsectors.Size(); kk++)
{
if (Level->subsectors[kk].bbox.CheckOverlap(viewbox))
{
DoSubsector (&Level->subsectors[kk]);
}
}
}
}
void HWDrawInfo::RenderBSP(void *node, bool drawpsprites)
{
ClearDitherTargets();
Bsp.Clock();
// Give the DrawInfo the viewpoint in fixed point because that's what the nodes are.
viewx = FLOAT2FIXED(Viewpoint.Pos.X);
viewy = FLOAT2FIXED(Viewpoint.Pos.Y);
if (r_radarclipper && !(Level->flags3 & LEVEL3_NOFOGOFWAR) && Viewpoint.IsAllowedOoB() && (Viewpoint.camera->ViewPos->Flags & VPSF_ABSOLUTEOFFSET))
{
if (Viewpoint.camera->tracer != NULL)
{
viewx = FLOAT2FIXED(Viewpoint.camera->tracer->X());
viewy = FLOAT2FIXED(Viewpoint.camera->tracer->Y());
}
else
{
viewx = FLOAT2FIXED(Viewpoint.camera->X());
viewy = FLOAT2FIXED(Viewpoint.camera->Y());
}
}
validcount++; // used for processing sidedefs only once by the renderer.
@ -866,7 +1025,8 @@ void HWDrawInfo::RenderBSP(void *node, bool drawpsprites)
auto future = renderPool.push([&](int id) {
WorkerThread();
});
RenderBSPNode(node);
if (Viewpoint.IsOrtho() && ((Level->flags3 & LEVEL3_NOFOGOFWAR) || !r_radarclipper)) RenderOrthoNoFog();
else RenderBSPNode(node);
jobQueue.AddJob(RenderJob::TerminateJob, nullptr, nullptr);
Bsp.Unclock();
@ -876,9 +1036,21 @@ void HWDrawInfo::RenderBSP(void *node, bool drawpsprites)
}
else
{
RenderBSPNode(node);
if (Viewpoint.IsOrtho() && ((Level->flags3 & LEVEL3_NOFOGOFWAR) || !r_radarclipper)) RenderOrthoNoFog();
else RenderBSPNode(node);
Bsp.Unclock();
}
// Make rendered targets set dither transparency flags on level geometry for next pass
// Can't do this inside DoSubsector() because both Trace() and P_CheckSight() affect 'validcount' global variable
for (int ii = 0; ii < MAXDITHERACTORS; ii++)
{
if ( RenderedTargets[ii] && P_CheckSight(players[consoleplayer].mo, RenderedTargets[ii], 0) )
{
SetDitherTransFlags(RenderedTargets[ii]);
}
}
// Process all the sprites on the current portal's back side which touch the portal.
if (mCurrentPortal != nullptr) mCurrentPortal->RenderAttached(this);

View file

@ -193,7 +193,7 @@ void Clipper::AddClipRange(angle_t start, angle_t end)
if (node->end < end)
{
node->end = end;
node->end = end; // [DVR] This never triggers because of previous while loop. Remove?
}
ClipNode *node2 = node->next;
@ -361,6 +361,17 @@ angle_t Clipper::AngleToPseudo(angle_t ang)
return xs_Fix<30>::ToFix(result);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
angle_t Clipper::PitchToPseudo(double ang)
{
return AngleToPseudo(DAngle::fromDeg(90.0-ang).BAMs()); // Pitch is positive when looking down
}
//-----------------------------------------------------------------------------
//
// ! Returns the pseudoangle between the line p1 to (infinity, p1.y) and the
@ -378,11 +389,28 @@ angle_t Clipper::PointToPseudoAngle(double x, double y)
{
double vecx = x - viewpoint->Pos.X;
double vecy = y - viewpoint->Pos.Y;
if ((viewpoint->camera != NULL) && amRadar)
{
if (viewpoint->camera->tracer != NULL)
{
vecx = x - viewpoint->camera->tracer->X();
vecy = y - viewpoint->camera->tracer->Y();
}
else
{
vecx = x - viewpoint->camera->X();
vecy = y - viewpoint->camera->Y();
}
}
if (vecx == 0 && vecy == 0)
{
return 0;
}
else if (!amRadar && viewpoint->IsOrtho())
{
return PointToPseudoOrthoAngle(x, y);
}
else
{
double result = vecy / (fabs(vecx) + fabs(vecy));
@ -395,6 +423,78 @@ angle_t Clipper::PointToPseudoAngle(double x, double y)
}
angle_t Clipper::PointToPseudoPitch(double x, double y, double z)
{
double vecx = x - viewpoint->Pos.X;
double vecy = y - viewpoint->Pos.Y;
double vecz = z - viewpoint->Pos.Z;
double result = 0;
if (vecx == 0 && vecy == 0 && vecz == 0)
{
return 0;
}
else if (viewpoint->IsOrtho())
{
return PointToPseudoOrthoPitch(x, y, z);
}
else
{
double result = vecz / (g_sqrt(vecx*vecx + vecy*vecy) + fabs(vecz)); // -ffast-math compile flag applies to this file, yes?
if ((vecx * viewpoint->TanCos + vecy * viewpoint->TanSin) <= 0.0) // Point is behind viewpoint
{
result = 2.0 - result;
}
return xs_Fix<30>::ToFix(result + 1.0); // range to 0 to 2 to 4 (bottom to top to suplex)
}
}
angle_t Clipper::PointToPseudoOrthoAngle(double x, double y)
{
DVector3 disp = DVector3( x, y, 0 ) - viewpoint->camera->Pos();
if (viewpoint->camera->ViewPos->Offset.XY().Length() == 0)
{
return AngleToPseudo( viewpoint->Angles.Yaw.BAMs() );
}
else
{
angle_t af = viewpoint->FrustAngle;
double xproj = disp.XY().Length() * deltaangle(disp.Angle(), viewpoint->Angles.Yaw).Sin();
xproj *= viewpoint->ScreenProj;
if (fabs(xproj) < 2.0)
{
return AngleToPseudo( viewpoint->Angles.Yaw.BAMs() - xproj * 0.5 * af );
}
else
{
return (xproj > 0.0 ? AngleToPseudo( viewpoint->Angles.Yaw.BAMs() - af ) : AngleToPseudo( viewpoint->Angles.Yaw.BAMs() + af ));
}
}
}
angle_t Clipper::PointToPseudoOrthoPitch(double x, double y, double z)
{
DVector3 disp = DVector3( x, y, z ) - viewpoint->camera->Pos();
if (viewpoint->camera->ViewPos->Offset.XY().Length() > 0)
{
double yproj = viewpoint->PitchSin * disp.XY().Length() * deltaangle(disp.Angle(), viewpoint->Angles.Yaw).Cos();
yproj += viewpoint->PitchCos * disp.Z;
yproj *= viewpoint->ScreenProj;
if (fabs(yproj) <= 1.5)
{
return PitchToPseudo(viewpoint->Angles.Pitch.Degrees() - yproj * 0.5 * viewpoint->FieldOfView.Degrees() );
}
else
{
double a2 = 0.75*viewpoint->FieldOfView.Degrees();
a2 *= ( yproj > 0.0 ? -1.0 : 1.0 );
return PitchToPseudo(viewpoint->Angles.Pitch.Degrees() + a2 );
}
}
else return PitchToPseudo(viewpoint->Angles.Pitch.Degrees());
}
//-----------------------------------------------------------------------------
//
@ -428,7 +528,7 @@ bool Clipper::CheckBox(const float *bspcoord)
// Find the corners of the box
// that define the edges from current viewpoint.
auto &vp = viewpoint;
auto &vp = viewpoint;
boxpos = (vp->Pos.X <= bspcoord[BOXLEFT] ? 0 : vp->Pos.X < bspcoord[BOXRIGHT ] ? 1 : 2) +
(vp->Pos.Y >= bspcoord[BOXTOP ] ? 0 : vp->Pos.Y > bspcoord[BOXBOTTOM] ? 4 : 8);
@ -437,7 +537,47 @@ bool Clipper::CheckBox(const float *bspcoord)
check = checkcoord[boxpos];
angle1 = PointToPseudoAngle (bspcoord[check[0]], bspcoord[check[1]]);
angle2 = PointToPseudoAngle (bspcoord[check[2]], bspcoord[check[3]]);
if (vp->IsOrtho())
{
if (angle2 != angle1) return true;
switch (boxpos) // Check if the closer corner is poking into the view area
{
case 0:
case 10:
if ( angle1 != PointToPseudoAngle (bspcoord[check[2]], bspcoord[check[1]]) ) return true;
break;
case 2:
case 8:
if ( angle1 != PointToPseudoAngle (bspcoord[check[0]], bspcoord[check[3]]) ) return true;
break;
default:
break;
}
}
return SafeCheckRange(angle2, angle1);
}
bool Clipper::CheckBoxOrthoPitch(const float *bspcoord)
{
angle_t pitchmin, pitchmax;
auto &vp = viewpoint;
if (!vp->IsOrtho()) return true;
angle_t pitchtemp;
double padding = 1.0/viewpoint->ScreenProj/viewpoint->PitchCos;
double camz = vp->camera->Pos().Z - padding;
pitchmin = PointToPseudoPitch (bspcoord[BOXLEFT], bspcoord[BOXTOP], camz);
pitchmax = PointToPseudoPitch (bspcoord[BOXLEFT], bspcoord[BOXTOP], camz + 2.0*padding);
for (int yi = BOXTOP; yi <= BOXBOTTOM; yi++)
for (int xi = BOXLEFT; xi <= BOXRIGHT; xi++)
{
pitchtemp = PointToPseudoPitch (bspcoord[xi], bspcoord[yi], camz);
if (pitchmin - pitchtemp < ANGLE_180) pitchmin = pitchtemp;
pitchtemp = PointToPseudoPitch (bspcoord[xi], bspcoord[yi], camz + 2.0*padding);
if (pitchtemp - pitchmax < ANGLE_180) pitchmax = pitchtemp;
}
return (pitchmax != pitchmin); // SafeCheckRange(pitchmin, pitchmax);
}

View file

@ -29,10 +29,11 @@ class Clipper
ClipNode * clipnodes = nullptr;
ClipNode * cliphead = nullptr;
ClipNode * silhouette = nullptr; // will be preserved even when RemoveClipRange is called
const FRenderViewpoint *viewpoint = nullptr;
FRenderViewpoint *viewpoint = nullptr;
bool blocked = false;
static angle_t AngleToPseudo(angle_t ang);
static angle_t PitchToPseudo(double ang);
bool IsRangeVisible(angle_t startangle, angle_t endangle);
void RemoveRange(ClipNode * cn);
void AddClipRange(angle_t startangle, angle_t endangle);
@ -41,6 +42,7 @@ class Clipper
public:
bool amRadar = false;
Clipper();
void Clear();
@ -72,7 +74,7 @@ public:
return c;
}
void SetViewpoint(const FRenderViewpoint &vp)
void SetViewpoint(FRenderViewpoint &vp)
{
viewpoint = &vp;
}
@ -116,6 +118,10 @@ public:
SafeAddClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle));
}
void SafeAddClipRangeDegPitches(double startpitch, double endpitch)
{
SafeAddClipRange(PitchToPseudo(startpitch), PitchToPseudo(endpitch));
}
void SafeRemoveClipRange(angle_t startangle, angle_t endangle)
{
@ -146,10 +152,14 @@ public:
{
return blocked;
}
angle_t PointToPseudoAngle(double x, double y);
angle_t PointToPseudoAngle(double x, double y);
angle_t PointToPseudoPitch(double x, double y, double z);
angle_t PointToPseudoOrthoAngle(double x, double y);
angle_t PointToPseudoOrthoPitch(double x, double y, double z);
bool CheckBox(const float *bspcoord);
bool CheckBoxOrthoPitch(const float *bspcoord);
// Used to speed up angle calculations during clipping
inline angle_t GetClipAngle(vertex_t *v)

View file

@ -131,12 +131,19 @@ HWDrawInfo *HWDrawInfo::StartDrawInfo(FLevelLocals *lev, HWDrawInfo *parent, FRe
//==========================================================================
static Clipper staticClipper; // Since all scenes are processed sequentially we only need one clipper.
static Clipper staticVClipper; // Another clipper to clip vertically (used if (VPSF_ALLOWOUTOFBOUNDS & camera->viewpos->Flags)).
static Clipper staticRClipper; // Another clipper for radar (doesn't actually clip. Changes SSECMF_DRAWN setting).
static HWDrawInfo * gl_drawinfo; // This is a linked list of all active DrawInfos and needed to free the memory arena after the last one goes out of scope.
void HWDrawInfo::StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms)
{
staticClipper.Clear();
staticVClipper.Clear();
staticRClipper.Clear();
mClipper = &staticClipper;
vClipper = &staticVClipper;
rClipper = &staticRClipper;
rClipper->amRadar = true;
Viewpoint = parentvp;
lightmode = getRealLightmode(Level, true);
@ -169,6 +176,8 @@ void HWDrawInfo::StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uni
VPUniforms.mLightBlendMode = (level.info ? (int)level.info->lightblendmode : 0);
}
mClipper->SetViewpoint(Viewpoint);
vClipper->SetViewpoint(Viewpoint);
rClipper->SetViewpoint(Viewpoint);
ClearBuffers();
@ -257,7 +266,9 @@ void HWDrawInfo::ClearBuffers()
void HWDrawInfo::UpdateCurrentMapSection()
{
const int mapsection = Level->PointInRenderSubsector(Viewpoint.Pos)->mapsection;
int mapsection = Level->PointInRenderSubsector(Viewpoint.Pos)->mapsection;
if (Viewpoint.IsAllowedOoB())
mapsection = Level->PointInRenderSubsector(Viewpoint.camera->Pos())->mapsection;
CurrentMapSections.Set(mapsection);
}
@ -270,9 +281,11 @@ void HWDrawInfo::UpdateCurrentMapSection()
void HWDrawInfo::SetViewArea()
{
auto &vp = Viewpoint;
auto &vp = Viewpoint;
// The render_sector is better suited to represent the current position in GL
vp.sector = Level->PointInRenderSubsector(vp.Pos)->render_sector;
if (Viewpoint.IsAllowedOoB())
vp.sector = Level->PointInRenderSubsector(vp.camera->Pos())->render_sector;
// Get the heightsec state from the render sector, not the current one!
if (vp.sector->GetHeightSec())
@ -351,16 +364,14 @@ int HWDrawInfo::SetFullbrightFlags(player_t *player)
angle_t HWDrawInfo::FrustumAngle()
{
float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees());
// If pitch is larger than this you can look all around at an FOV of 90 degrees
if (fabs(Viewpoint.HWAngles.Pitch.Degrees()) > 89.0) return 0xffffffff;
// If the pitch is larger than this you can look all around at a FOV of 90°
if (tilt > 46.0f) return 0xffffffff;
double xratio = r_viewwindow.FocalTangent / Viewpoint.PitchCos;
double floatangle = 0.035 + atan ( xratio ) * 48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio); // this is radians
angle_t a1 = DAngle::fromRad(floatangle).BAMs();
// ok, this is a gross hack that barely works...
// but at least it doesn't overestimate too much...
double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees() * 48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0;
angle_t a1 = DAngle::fromDeg(floatangle).BAMs();
if (a1 >= ANGLE_180) return 0xffffffff;
if (a1 >= ANGLE_90) return 0xffffffff;
return a1;
}
@ -438,8 +449,12 @@ HWDecal *HWDrawInfo::AddDecal(bool onmirror)
void HWDrawInfo::CreateScene(bool drawpsprites)
{
const auto &vp = Viewpoint;
angle_t a1 = FrustumAngle();
angle_t a1 = FrustumAngle(); // horizontally clip the back of the viewport
mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1);
Viewpoint.FrustAngle = a1;
double a2 = 20.0 + 0.5*Viewpoint.FieldOfView.Degrees(); // FrustumPitch for vertical clipping
if (a2 > 179.0) a2 = 179.0;
vClipper->SafeAddClipRangeDegPitches(vp.HWAngles.Pitch.Degrees() - a2, vp.HWAngles.Pitch.Degrees() + a2); // clip the suplex range
// reset the portal manager
portalState.StartFrame();
@ -648,6 +663,79 @@ void HWDrawInfo::DrawCorona(FRenderState& state, ACorona* corona, double dist)
#endif
}
//==========================================================================
//
// TraceCallbackForDitherTransparency
// Toggles dither flag on anything that occludes the actor's
// position from viewpoint.
//
//==========================================================================
static ETraceStatus TraceCallbackForDitherTransparency(FTraceResults& res, void* userdata)
{
int* count = (int*)userdata;
double bf, bc;
(*count)++;
switch(res.HitType)
{
case TRACE_HitWall:
if (!(res.Line->sidedef[res.Side]->Flags & WALLF_DITHERTRANS))
{
bf = res.Line->sidedef[res.Side]->sector->floorplane.ZatPoint(res.HitPos.XY());
bc = res.Line->sidedef[res.Side]->sector->ceilingplane.ZatPoint(res.HitPos.XY());
if ((res.HitPos.Z <= bc) && (res.HitPos.Z >= bf)) res.Line->sidedef[res.Side]->Flags |= WALLF_DITHERTRANS;
}
break;
case TRACE_HitFloor:
res.Sector->floorplane.dithertransflag = true;
break;
case TRACE_HitCeiling:
res.Sector->ceilingplane.dithertransflag = true;
break;
case TRACE_HitActor:
default:
break;
}
return TRACE_ContinueOutOfBounds;
}
void HWDrawInfo::SetDitherTransFlags(AActor* actor)
{
if (actor && actor->Sector)
{
FTraceResults results;
double horix = Viewpoint.Sin * actor->radius;
double horiy = Viewpoint.Cos * actor->radius;
DVector3 actorpos = actor->Pos();
DVector3 vvec = actorpos - Viewpoint.Pos;
if (Viewpoint.IsOrtho())
{
vvec += Viewpoint.camera->Pos() - actorpos;
vvec *= 5.0; // Should be 4.0? (since zNear is behind screen by 3*dist in VREyeInfo::GetProjection())
}
double distance = vvec.Length() - actor->radius;
DVector3 campos = actorpos - vvec;
sector_t* startsec;
int count = 0;
vvec = vvec.Unit();
campos.X -= horix; campos.Y += horiy; campos.Z += actor->Height * 0.25;
for (int iter = 0; iter < 3; iter++)
{
startsec = Level->PointInRenderSubsector(campos)->sector;
Trace(campos, startsec, vvec, distance,
0, 0, actor, results, 0, TraceCallbackForDitherTransparency, &count);
campos.Z += actor->Height * 0.5;
Trace(campos, startsec, vvec, distance,
0, 0, actor, results, 0, TraceCallbackForDitherTransparency, &count);
campos.Z -= actor->Height * 0.5;
campos.X += horix; campos.Y -= horiy;
}
}
}
static ETraceStatus CheckForViewpointActor(FTraceResults& res, void* userdata)
{
FRenderViewpoint* data = (FRenderViewpoint*)userdata;
@ -803,13 +891,17 @@ void HWDrawInfo::DrawScene(int drawmode)
{
static int recursion = 0;
static int ssao_portals_available = 0;
const auto& vp = Viewpoint;
auto& vp = Viewpoint;
bool applySSAO = false;
if (drawmode == DM_MAINVIEW)
{
ssao_portals_available = gl_ssao_portals;
applySSAO = true;
if (r_dithertransparency && vp.IsAllowedOoB())
{
vp.camera->tracer ? SetDitherTransFlags(vp.camera->tracer) : SetDitherTransFlags(players[consoleplayer].mo);
}
}
else if (drawmode == DM_OFFSCREEN)
{
@ -864,6 +956,8 @@ void HWDrawInfo::ProcessScene(bool toscreen)
portalState.BeginScene();
int mapsection = Level->PointInRenderSubsector(Viewpoint.Pos)->mapsection;
if (Viewpoint.IsAllowedOoB())
mapsection = Level->PointInRenderSubsector(Viewpoint.camera->Pos())->mapsection;
CurrentMapSections.Set(mapsection);
DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN);

View file

@ -146,6 +146,8 @@ struct HWDrawInfo
HWPortal *mCurrentPortal;
//FRotator mAngles;
Clipper *mClipper;
Clipper *vClipper; // Vertical clipper
Clipper *rClipper; // Radar clipper
FRenderViewpoint Viewpoint;
HWViewpointUniforms VPUniforms; // per-viewpoint uniform state
TArray<HWPortal *> Portals;
@ -234,6 +236,7 @@ public:
HWPortal * FindPortal(const void * src);
void RenderBSPNode(void *node);
void RenderOrthoNoFog();
void RenderBSP(void *node, bool drawpsprites);
static HWDrawInfo *StartDrawInfo(FLevelLocals *lev, HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms);
@ -302,6 +305,8 @@ public:
void DrawCoronas(FRenderState& state);
void DrawCorona(FRenderState& state, ACorona* corona, double dist);
void SetDitherTransFlags(AActor* actor);
void ProcessLowerMinisegs(TArray<seg_t *> &lowersegs);
void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub);

View file

@ -320,7 +320,7 @@ void HWFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent)
state.SetObjectColor(FlatColor | 0xff000000);
state.SetAddColor(AddColor | 0xff000000);
state.ApplyTextureManipulation(TextureFx);
if (plane.plane.dithertransflag) state.SetEffect(EFF_DITHERTRANS);
if (hacktype & SSRF_PLANEHACK)
{
@ -372,6 +372,7 @@ void HWFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent)
state.SetObjectColor(0xffffffff);
state.SetAddColor(0);
state.ApplyTextureManipulation(nullptr);
if (plane.plane.dithertransflag) state.SetEffect(EFF_NONE);
}
//==========================================================================
@ -408,6 +409,8 @@ inline void HWFlat::PutFlat(HWDrawInfo *di, bool fog)
void HWFlat::Process(HWDrawInfo *di, sector_t * model, int whichplane, bool fog)
{
plane.GetFromSector(model, whichplane);
model->ceilingplane.dithertransflag = false; // Resetting this every frame
model->floorplane.dithertransflag = false; // Resetting this every frame
if (whichplane != int(ceiling))
{
// Flip the normal if the source plane has a different orientation than what we are about to render.
@ -506,7 +509,7 @@ void HWFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which)
uint8_t sink;
uint8_t &srf = hacktype? sink : di->section_renderflags[di->Level->sections.SectionIndex(section)];
const auto &vp = di->Viewpoint;
auto &vp = di->Viewpoint;
//
//
@ -515,7 +518,7 @@ void HWFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which)
//
//
//
if ((which & SSRF_RENDERFLOOR) && frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z && (!section || !(section->flags & FSection::DONTRENDERFLOOR)))
if (((which & SSRF_RENDERFLOOR) && frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z && (!section || !(section->flags & FSection::DONTRENDERFLOOR)))&& !(vp.IsOrtho() && (vp.PitchSin < 0.0)))
{
// process the original floor first.
@ -573,7 +576,7 @@ void HWFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which)
//
//
//
if ((which & SSRF_RENDERCEILING) && frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z && (!section || !(section->flags & FSection::DONTRENDERCEILING)))
if (((which & SSRF_RENDERCEILING) && frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z && (!section || !(section->flags & FSection::DONTRENDERCEILING))) && !(vp.IsOrtho() && (vp.PitchSin > 0.0)))
{
// process the original ceiling first.

View file

@ -563,7 +563,8 @@ bool HWMirrorPortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clippe
angle_t af = di->FrustumAngle();
if (af < ANGLE_180) clipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + af, vp.Angles.Yaw.BAMs() - af);
clipper->SafeAddClipRange(linedef->v1, linedef->v2);
if(!di->Viewpoint.IsAllowedOoB())
clipper->SafeAddClipRange(linedef->v1, linedef->v2);
return true;
}

View file

@ -130,6 +130,7 @@ void HWSkyInfo::init(HWDrawInfo *di, sector_t* sec, int skypos, int sky1, PalEnt
void HWWall::SkyPlane(HWWallDispatcher *di, sector_t *sector, int plane, bool allowreflect)
{
int ptype = -1;
if (di->di && di->di->Viewpoint.IsAllowedOoB()) return; // Couldn't prevent sky portal occlusion. Skybox is bad in ortho too.
FSectorPortal *sportal = sector->ValidatePortal(plane);
if (sportal != nullptr && sportal->mFlags & PORTSF_INSKYBOX) sportal = nullptr; // no recursions, delete it here to simplify the following code

View file

@ -65,6 +65,7 @@ extern TArray<spriteframe_t> SpriteFrames;
extern uint32_t r_renderercaps;
const float LARGE_VALUE = 1e19f;
const float MY_SQRT2 = 1.41421356237309504880; // sqrt(2)
EXTERN_CVAR(Bool, r_debug_disable_vis_filter)
EXTERN_CVAR(Float, transsouls)
@ -494,7 +495,7 @@ bool HWSprite::CalculateVertices(HWDrawInfo* di, FVector3* v, DVector3* vp)
}
else // traditional "Y" billboard mode
{
if (doRoll || !offset.isZero())
if (doRoll || !offset.isZero() || (actor && (actor->renderflags2 & RF2_ISOMETRICSPRITES)))
{
mat.MakeIdentity();
@ -516,6 +517,16 @@ bool HWSprite::CalculateVertices(HWDrawInfo* di, FVector3* v, DVector3* vp)
mat.Translate(-center.X, -center.Z, -center.Y);
}
if (actor && (actor->renderflags2 & RF2_ISOMETRICSPRITES) && di->Viewpoint.IsOrtho())
{
float angleRad = (FAngle::fromDeg(270.) - HWAngles.Yaw).Radians();
mat.Translate(center.X, center.Z, center.Y);
mat.Translate(0.0, z2 - center.Z, 0.0);
mat.Rotate(-sin(angleRad), 0, cos(angleRad), -actor->isotheta);
mat.Translate(0.0, center.Z - z2, 0.0);
mat.Translate(-center.X, -center.Z, -center.Y);
}
v[0] = mat * FVector3(x1, z1, y1);
v[1] = mat * FVector3(x2, z1, y2);
v[2] = mat * FVector3(x1, z2, y1);
@ -911,6 +922,7 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
{
bool mirror = false;
DAngle ang = (thingpos - vp.Pos).Angle();
if (di->Viewpoint.IsOrtho()) ang = vp.Angles.Yaw;
FTextureID patch;
// [ZZ] add direct picnum override
if (isPicnumOverride)
@ -1004,7 +1016,25 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
if (thing->renderflags & RF_SPRITEFLIP) // [SP] Flip back
thing->renderflags ^= RF_XFLIP;
r.Scale(sprscale.X, isSpriteShadow ? sprscale.Y * 0.15 : sprscale.Y);
// If sprite is isometric, do both vertical scaling and partial rotation to face the camera to compensate for Y-billboarding.
// Using just rotation (about z=0) might cause tall+slender (high aspect ratio) sprites to clip out of collision box
// at the top and clip into whatever is behind them from the viewpoint's perspective. - [DVR]
thing->isoscaleY = 1.0;
thing->isotheta = vp.HWAngles.Pitch.Degrees();
if (thing->renderflags2 & RF2_ISOMETRICSPRITES)
{
float floordist = thing->radius * vp.floordistfact;
floordist -= 0.5 * r.width * vp.cotfloor;
float sineisotheta = floordist / r.height;
double scl = g_sqrt( 1.0 + sineisotheta * sineisotheta - 2.0 * vp.PitchSin * sineisotheta );
if ((thing->radius > 0.0) && (scl > fabs(vp.PitchCos)))
{
thing->isoscaleY = scl / ( fabs(vp.PitchCos) > 0.01 ? fabs(vp.PitchCos) : 0.01 );
thing->isotheta = 180.0 * asin( sineisotheta / thing->isoscaleY ) / M_PI;
}
}
r.Scale(sprscale.X, isSpriteShadow ? sprscale.Y * 0.15 * thing->isoscaleY : sprscale.Y * thing->isoscaleY);
if (thing->renderflags & (RF_ROLLSPRITE|RF_FLATSPRITE))
{
@ -1018,7 +1048,7 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
z1 = z - r.top;
z2 = z1 - r.height;
float spriteheight = sprscale.Y * r.height;
float spriteheight = sprscale.Y * r.height * thing->isoscaleY;
// Tests show that this doesn't look good for many decorations and corpses
if (spriteheight > 0 && gl_spriteclip > 0 && (thing->renderflags & RF_SPRITETYPEMASK) == RF_FACESPRITE)
@ -1037,6 +1067,13 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
x2 = x - viewvecY*rightfac;
y1 = y + viewvecX*leftfac;
y2 = y + viewvecX*rightfac;
if (thing->renderflags2 & RF2_ISOMETRICSPRITES) // If sprites are drawn from an isometric perspective
{
x1 -= viewvecX * thing->radius * MY_SQRT2;
x2 -= viewvecX * thing->radius * MY_SQRT2;
y1 -= viewvecY * thing->radius * MY_SQRT2;
y2 -= viewvecY * thing->radius * MY_SQRT2;
}
break;
}
case RF_FLATSPRITE:
@ -1077,6 +1114,7 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
}
depth = (float)((x - vp.Pos.X) * vp.TanCos + (y - vp.Pos.Y) * vp.TanSin);
if(thing->renderflags2 & RF2_ISOMETRICSPRITES) depth = depth * vp.PitchCos - vp.PitchSin * z2; // Helps with stacking actors with small xy offsets
if (isSpriteShadow) depth += 1.f/65536.f; // always sort shadows behind the sprite.
// light calculation

View file

@ -83,10 +83,16 @@ void SetSplitPlanes(FRenderState& state, const secplane_t& top, const secplane_t
void HWWall::RenderWall(FRenderState &state, int textured)
{
if (seg->sidedef->Flags & WALLF_DITHERTRANS) state.SetEffect(EFF_DITHERTRANS);
assert(vertcount > 0);
state.SetLightIndex(dynlightindex);
state.Draw(DT_TriangleFan, vertindex, vertcount);
vertexcount += vertcount;
if (seg->sidedef->Flags & WALLF_DITHERTRANS)
{
state.SetEffect(EFF_NONE);
seg->sidedef->Flags &= ~WALLF_DITHERTRANS; // reset this every frame
}
}
//==========================================================================
@ -2175,6 +2181,8 @@ void HWWall::Process(HWWallDispatcher *di, seg_t *seg, sector_t * frontsector, s
}
bool isportal = seg->linedef->isVisualPortal() && seg->sidedef == seg->linedef->sidedef[0];
// Don't render portal insides if in orthographic mode
if (di->di) isportal &= !(di->di->Viewpoint.IsOrtho());
//return;
// [GZ] 3D middle textures are necessarily two-sided, even if they lack the explicit two-sided flag

View file

@ -66,7 +66,9 @@
#include "i_system.h"
#include "v_draw.h"
#include "i_interface.h"
#include "d_main.h"
const float MY_SQRT2 = 1.41421356237309504880; // sqrt(2)
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern bool DrawFSHUD; // [RH] Defined in d_main.cpp
@ -103,6 +105,8 @@ CVAR (Bool, r_deathcamera, false, CVAR_ARCHIVE)
CVAR (Int, r_clearbuffer, 0, 0)
CVAR (Bool, r_drawvoxels, true, 0)
CVAR (Bool, r_drawplayersprites, true, 0) // [RH] Draw player sprites?
CVARD (Bool, r_radarclipper, false, CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_CHEAT, "Use the horizontal clipper from camera->tracer's perspective")
CVARD (Bool, r_dithertransparency, false, CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_CHEAT, "Use dithered-transparency shading for actor-occluding level geometry")
CUSTOM_CVAR(Float, r_quakeintensity, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 0.f) self = 0.f;
@ -155,9 +159,14 @@ FRenderViewpoint::FRenderViewpoint()
Sin = 0.0;
TanCos = 0.0;
TanSin = 0.0;
PitchCos = 0.0;
PitchSin = 0.0;
floordistfact = 0.0;
cotfloor = 0.0;
camera = nullptr;
sector = nullptr;
FieldOfView = DAngle::fromDeg(90.); // Angles in the SCREENWIDTH wide window
ScreenProj = 0.0;
TicFrac = 0.0;
FrameTime = 0;
extralight = 0;
@ -552,39 +561,42 @@ void R_InterpolateView(FRenderViewpoint& viewPoint, const player_t* const player
// Due to interpolation this is not necessarily the same as the sector the camera is in.
viewPoint.sector = viewLvl->PointInRenderSubsector(viewPoint.Pos)->sector;
bool moved = false;
while (!viewPoint.sector->PortalBlocksMovement(sector_t::ceiling))
if (!viewPoint.IsAllowedOoB() || !V_IsHardwareRenderer())
{
if (viewPoint.Pos.Z > viewPoint.sector->GetPortalPlaneZ(sector_t::ceiling))
bool moved = false;
while (!viewPoint.sector->PortalBlocksMovement(sector_t::ceiling))
{
const DVector2 offset = viewPoint.sector->GetPortalDisplacement(sector_t::ceiling);
viewPoint.Pos += offset;
viewPoint.ActorPos += offset;
viewPoint.sector = viewPoint.sector->GetPortal(sector_t::ceiling)->mDestination;
moved = true;
}
else
{
break;
}
}
if (!moved)
{
while (!viewPoint.sector->PortalBlocksMovement(sector_t::floor))
{
if (viewPoint.Pos.Z < viewPoint.sector->GetPortalPlaneZ(sector_t::floor))
if (viewPoint.Pos.Z > viewPoint.sector->GetPortalPlaneZ(sector_t::ceiling))
{
const DVector2 offset = viewPoint.sector->GetPortalDisplacement(sector_t::floor);
const DVector2 offset = viewPoint.sector->GetPortalDisplacement(sector_t::ceiling);
viewPoint.Pos += offset;
viewPoint.ActorPos += offset;
viewPoint.sector = viewPoint.sector->GetPortal(sector_t::floor)->mDestination;
viewPoint.sector = viewPoint.sector->GetPortal(sector_t::ceiling)->mDestination;
moved = true;
}
else
{
break;
}
}
if (!moved)
{
while (!viewPoint.sector->PortalBlocksMovement(sector_t::floor))
{
if (viewPoint.Pos.Z < viewPoint.sector->GetPortalPlaneZ(sector_t::floor))
{
const DVector2 offset = viewPoint.sector->GetPortalDisplacement(sector_t::floor);
viewPoint.Pos += offset;
viewPoint.ActorPos += offset;
viewPoint.sector = viewPoint.sector->GetPortal(sector_t::floor)->mDestination;
}
else
{
break;
}
}
}
}
if (P_NoInterpolation(player, viewPoint.camera))
@ -680,10 +692,46 @@ void FRenderViewpoint::SetViewAngle(const FViewWindow& viewWindow)
TanSin = viewWindow.FocalTangent * Sin;
TanCos = viewWindow.FocalTangent * Cos;
PitchSin = Angles.Pitch.Sin();
PitchCos = Angles.Pitch.Cos();
floordistfact = MY_SQRT2 + ( fabs(Cos) > fabs(Sin) ? 1.0/fabs(Cos) : 1.0/fabs(Sin) );
cotfloor = ( fabs(Cos) > fabs(Sin) ? fabs(Sin/Cos) : fabs(Cos/Sin) );
const DVector2 v = Angles.Yaw.ToVector();
ViewVector.X = v.X;
ViewVector.Y = v.Y;
HWAngles.Yaw = FAngle::fromDeg(270.0 - Angles.Yaw.Degrees());
if (IsOrtho() && (camera->ViewPos->Offset.XY().Length() > 0.0))
ScreenProj = 1.34396 / camera->ViewPos->Offset.Length(); // [DVR] Estimated. +/-1 should be top/bottom of screen.
}
//==========================================================================
//
// R_IsAllowedOoB()
// Checks if camera actor exists, has viewpos,
// and viewpos has VPSF_ALLOWOUTOFBOUNDS flag set.
//
//==========================================================================
bool FRenderViewpoint::IsAllowedOoB()
{
return (camera && camera->ViewPos && (camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS));
}
//==========================================================================
//
// R_IsOrtho()
// Checks if camera actor exists, has viewpos,
// and viewpos has VPSF_ORTHOGRAPHIC flag set.
//
//==========================================================================
bool FRenderViewpoint::IsOrtho()
{
return (camera && camera->ViewPos && (camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC));
}
//==========================================================================
@ -1017,14 +1065,15 @@ void R_SetupFrame(FRenderViewpoint& viewPoint, const FViewWindow& viewWindow, AA
viewPoint.SetViewAngle(viewWindow);
// Keep the view within the sector's floor and ceiling
if (viewPoint.sector->PortalBlocksMovement(sector_t::ceiling))
// But allow VPSF_ALLOWOUTOFBOUNDS camera viewpoints to go out of bounds when using hardware renderer
if (viewPoint.sector->PortalBlocksMovement(sector_t::ceiling) && (!viewPoint.IsAllowedOoB() || !V_IsHardwareRenderer()))
{
const double z = viewPoint.sector->ceilingplane.ZatPoint(viewPoint.Pos) - 4.0;
if (viewPoint.Pos.Z > z)
viewPoint.Pos.Z = z;
}
if (viewPoint.sector->PortalBlocksMovement(sector_t::floor))
if (viewPoint.sector->PortalBlocksMovement(sector_t::floor) && (!viewPoint.IsAllowedOoB() || !V_IsHardwareRenderer()))
{
const double z = viewPoint.sector->floorplane.ZatPoint(viewPoint.Pos) + 4.0;
if (viewPoint.Pos.Z < z)

View file

@ -33,10 +33,16 @@ struct FRenderViewpoint
double Sin; // sin(Angles.Yaw)
double TanCos; // FocalTangent * cos(Angles.Yaw)
double TanSin; // FocalTangent * sin(Angles.Yaw)
double PitchCos; // cos(Angles.Pitch)
double PitchSin; // sin(Angles.Pitch)
double floordistfact; // used for isometric sprites Y-billboarding compensation in hw_sprites.cpp
double cotfloor; // used for isometric sprites Y-billboarding compensation in hw_sprites.cpp
angle_t FrustAngle; // FrustumAngle() result
AActor *camera; // camera actor
sector_t *sector; // [RH] keep track of sector viewing from
DAngle FieldOfView; // current field of view
double ScreenProj; // Screen projection factor for orthographic projection
double TicFrac; // fraction of tic for interpolation
uint32_t FrameTime; // current frame's time in tics.
@ -45,6 +51,8 @@ struct FRenderViewpoint
bool showviewer; // show the camera actor?
bool bForceNoViewer; // Never show the camera Actor.
void SetViewAngle(const FViewWindow& viewWindow);
bool IsAllowedOoB(); // Checks if camera actor exists, has viewpos, and viewpos has VPSF_ALLOWOUTOFBOUNDS flag set
bool IsOrtho(); // Checks if camera actor exists, has viewpos, and viewpos has VPSF_ORTHOGRAPHIC flag set
};

View file

@ -385,6 +385,7 @@ static FFlagDef ActorFlagDefs[]=
DEFINE_FLAG(RF2, FLIPSPRITEOFFSETY, AActor, renderflags2),
DEFINE_FLAG(RF2, CAMFOLLOWSPLAYER, AActor, renderflags2),
DEFINE_FLAG(RF2, NOMIPMAP, AActor, renderflags2),
DEFINE_FLAG(RF2, ISOMETRICSPRITES, AActor, renderflags2),
// Bounce flags
DEFINE_FLAG2(BOUNCE_Walls, BOUNCEONWALLS, AActor, BounceFlags),

View file

@ -48,6 +48,23 @@ const int TEXF_Detailmap = 0x20000;
const int TEXF_Glowmap = 0x40000;
const int TEXF_ClampY = 0x80000;
//===========================================================================
//
// RGB to HSV
//
//===========================================================================
vec3 rgb2hsv(vec3 c)
{
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
//===========================================================================
//
// Color to grayscale
@ -870,6 +887,27 @@ void main()
frag.rgb = frag.rgb + uFogColor.rgb;
}
FragColor = frag;
#ifdef DITHERTRANS
int index = (int(pixelpos.x) % 8) * 8 + int(pixelpos.y) % 8;
const float DITHER_THRESHOLDS[64] =
{
1.0 / 65.0, 33.0 / 65.0, 9.0 / 65.0, 41.0 / 65.0, 3.0 / 65.0, 35.0 / 65.0, 11.0 / 65.0, 43.0 / 65.0,
49.0 / 65.0, 17.0 / 65.0, 57.0 / 65.0, 25.0 / 65.0, 51.0 / 65.0, 19.0 / 65.0, 59.0 / 65.0, 27.0 / 65.0,
13.0 / 65.0, 45.0 / 65.0, 5.0 / 65.0, 37.0 / 65.0, 15.0 / 65.0, 47.0 / 65.0, 7.0 / 65.0, 39.0 / 65.0,
61.0 / 65.0, 29.0 / 65.0, 53.0 / 65.0, 21.0 / 65.0, 63.0 / 65.0, 31.0 / 65.0, 55.0 / 65.0, 23.0 / 65.0,
4.0 / 65.0, 36.0 / 65.0, 12.0 / 65.0, 44.0 / 65.0, 2.0 / 65.0, 34.0 / 65.0, 10.0 / 65.0, 42.0 / 65.0,
52.0 / 65.0, 20.0 / 65.0, 60.0 / 65.0, 28.0 / 65.0, 50.0 / 65.0, 18.0 / 65.0, 58.0 / 65.0, 26.0 / 65.0,
16.0 / 65.0, 48.0 / 65.0, 8.0 / 65.0, 40.0 / 65.0, 14.0 / 65.0, 46.0 / 65.0, 6.0 / 65.0, 38.0 / 65.0,
64.0 / 65.0, 32.0 / 65.0, 56.0 / 65.0, 24.0 / 65.0, 62.0 / 65.0, 30.0 / 65.0, 54.0 / 65.0, 22.0 /65.0
};
vec3 fragHSV = rgb2hsv(FragColor.rgb);
float brightness = clamp(1.5*fragHSV.z, 0.1, 1.0);
if (DITHER_THRESHOLDS[index] < brightness) discard;
else FragColor *= 0.5;
#endif
#ifdef GBUFFER_PASS
FragFog = vec4(AmbientOcclusionColor(), 1.0);
FragNormal = vec4(vEyeNormal.xyz * 0.5 + 0.5, 1.0);

View file

@ -493,6 +493,11 @@ void main()
if (frag.a <= uAlphaThreshold) discard;
#endif
#ifdef DITHERTRANS
int index = (int(pixelpos.x) % 2) * 2 + int(pixelpos.y) % 2;
if (index != 2) discard;
#endif
#if (DEF_FOG_2D == 0) // check for special 2D 'fog' mode.
{
float fogdist = 0.0;

View file

@ -128,3 +128,87 @@ class AimingCamera : SecurityCamera
}
}
class SpectatorCamera : Actor
{
bool chasingtracer;
double lagdistance; // camera gives chase (lazy follow) if tracer gets > lagdistance away from camera.pos
int chasemode; // 0: chase until tracer centered, 1: same but only when tracer is moving, 2: stop chase if tracer within lagdistance
property lagdistance : lagdistance;
property chasingtracer : chasingtracer;
property chasemode : chasemode;
default
{
+NOBLOCKMAP
+NOGRAVITY
+NOINTERACTION
RenderStyle "None";
CameraHeight 0;
SpectatorCamera.chasingtracer false;
SpectatorCamera.lagdistance 0.0;
SpectatorCamera.chasemode 0;
}
void Init(double dist, double yaw, double inpitch, int inflags)
{
double zshift = 0.0;
if(tracer != NULL)
{
if(player != NULL) zshift = -0.25*tracer.height;
else zshift = 0.5*tracer.height;
}
else if (player != NULL && player.mo != NULL) zshift = -0.5*player.mo.height;
SetViewPos((-dist*Cos(yaw)*Cos(inpitch), -dist*Sin(yaw)*Cos(inpitch), dist*Sin(inpitch)+zshift), inflags);
LookAtSelf(inpitch);
}
void LookAtSelf(double inpitch)
{
if(ViewPos.Offset.length() > 0.)
{
Vector3 negviewpos = (-1.0) * ViewPos.Offset;
angle = negviewpos.Angle();
pitch = inpitch;
}
}
override void Tick()
{
if(tracer != NULL)
{
Vector3 distvec = tracer.pos - pos;
double dist = distvec.length();
if((dist <= 4 && chasingtracer) || lagdistance <= 0.0) // Keep tracer centered on screen
{
SetOrigin(tracer.pos, true);
chasingtracer = false;
}
else // Lazy follow tracer
{
if(dist >= 2*lagdistance)
{
SetOrigin(tracer.pos, true);
chasingtracer = false;
}
else if(dist > lagdistance && !chasingtracer) chasingtracer = true;
if(chasingtracer)
{
speed = tracer.vel.xy.length()/dist;
if((speed == 0.0) && (chasemode == 0)) speed = tracer.speed/dist;
SetOrigin(pos + 2*distvec*speed, true);
if(chasemode > 1) chasingtracer = false;
}
}
}
else if(player != NULL && player.mo != NULL)
{
cameraFOV = player.FOV;
SetOrigin(player.mo.pos, true);
}
}
}

View file

@ -421,8 +421,10 @@ enum EActivationFlags
// [MC] Flags for SetViewPos.
enum EViewPosFlags
{
VPSF_ABSOLUTEOFFSET = 1 << 1, // Don't include angles.
VPSF_ABSOLUTEPOS = 1 << 2, // Use absolute position.
VPSF_ABSOLUTEOFFSET = 1 << 1, // Don't include angles.
VPSF_ABSOLUTEPOS = 1 << 2, // Use absolute position.
VPSF_ALLOWOUTOFBOUNDS = 1 << 3, // Allow viewpoint to go out of bounds (hardware renderer only).
VPSF_ORTHOGRAPHIC = 1 << 4, // Use orthographic projection (hardware renderer only).
};
// Flags for A_TakeInventory and A_TakeFromTarget
@ -1501,4 +1503,4 @@ enum EModelFlags
MDL_MODELSAREATTACHMENTS = 1<<12, // any model index after 0 is treated as an attachment, and therefore will use the bone results of index 0
MDL_CORRECTPIXELSTRETCH = 1<<13, // ensure model does not distort with pixel stretch when pitch/roll is applied
MDL_FORCECULLBACKFACES = 1<<14,
};
};