Consolidated isometric mode commits.

This commit is contained in:
Dileep V. Reddy 2024-08-09 19:58:26 -06:00 committed by Magnus Norddahl
commit 8a5e7449c1
33 changed files with 870 additions and 98 deletions

View file

@ -2822,7 +2822,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))
@ -2832,7 +2832,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(Bool, gl_debug_breakpoint)
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

@ -32,6 +32,7 @@ enum ERenderEffect
EFF_BURN,
EFF_STENCIL,
EFF_PORTAL,
EFF_DITHERTRANS,
MAX_EFFECTS
};

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,7 @@ VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key)
{ "burn", "shaders/scene/frag_burn.glsl", nullptr, nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
{ "stencil", "shaders/scene/frag_stencil.glsl", nullptr, nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
{ "portal", "shaders/scene/frag_portal.glsl", nullptr, nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
{ "dithertrans", "shaders/scene/frag_main.glsl", "shaders/scene/material_default.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_normal.glsl", "#define NO_ALPHATEST\n#define DITHERTRANS\n" },
};
const auto& desc = effectshaders[key.SpecialEffect];

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

@ -1823,6 +1823,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 },
{ "nousersave", MITYPE_SETVKDFLAG, VKDLEVELFLAG_NOUSERSAVE, 0 },
{ "noautomap", MITYPE_SETVKDFLAG, VKDLEVELFLAG_NOAUTOMAP, 0 },
{ "noautosaveonenter", MITYPE_SETVKDFLAG, VKDLEVELFLAG_NOAUTOSAVEONENTER, 0 },

View file

@ -270,6 +270,7 @@ enum ELevelFlags : unsigned int
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_NOFOGOFWAR = 0x00100000, // disables effect of r_radarclipper CVAR on this map
// VKDoom custom flags
VKDLEVELFLAG_NOUSERSAVE = 0x00000001,

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)
@ -1186,6 +1187,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
@ -1664,6 +1667,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

@ -3525,4 +3525,23 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position)
}
Level->aabbTree = new DoomLevelAABBTree(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

@ -165,7 +165,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

@ -52,11 +52,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
@ -272,6 +285,20 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip, FRenderState& state)
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(drawctx, 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)
@ -283,25 +310,53 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip, FRenderState& state)
{
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
{
@ -328,7 +383,8 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip, FRenderState& state)
if (hw_CheckClip(seg->sidedef, currentsector, backsector))
{
clipper.SafeAddClipRange(startAngle, endAngle);
if(!Viewpoint.IsAllowedOoB() && !(seg->sidedef->Flags & WALLF_DITHERTRANS))
clipper.SafeAddClipRange(startAngle, endAngle);
}
}
}
@ -529,13 +585,14 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector, FRenderState
{
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)
{
@ -669,6 +726,51 @@ void HWDrawInfo::DoSubsector(subsector_t * sub, FRenderState& state)
fakesector=hw_FakeFlat(drawctx, sector, in_area, false);
if(Viewpoint.IsAllowedOoB() && sector->isSecret() && sector->wasSecret() && !r_radarclipper) return;
// cull everything if subsector outside all clippers
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);
@ -726,6 +828,20 @@ void HWDrawInfo::DoSubsector(subsector_t * sub, FRenderState& state)
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)
@ -847,19 +963,62 @@ void HWDrawInfo::RenderBSPNode (void *node, FRenderState& state)
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), state);
}
// No need for clipping inside frustum if no fog of war (How is this faster!)
void HWDrawInfo::RenderOrthoNoFog(FRenderState& state)
{
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], state);
}
}
}
}
void HWDrawInfo::RenderBSP(void *node, bool drawpsprites, FRenderState& state)
{
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 != nullptr)
{
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.
@ -870,7 +1029,8 @@ void HWDrawInfo::RenderBSP(void *node, bool drawpsprites, FRenderState& state)
auto future = renderPool.push([&](int id) {
WorkerThread();
});
RenderBSPNode(node, state);
if (Viewpoint.IsOrtho() && ((Level->flags3 & LEVEL3_NOFOGOFWAR) || !r_radarclipper)) RenderOrthoNoFog(state);
else RenderBSPNode(node, state);
jobQueue.AddJob(RenderJob::TerminateJob, nullptr, nullptr);
Bsp.Unclock();
@ -880,9 +1040,21 @@ void HWDrawInfo::RenderBSP(void *node, bool drawpsprites, FRenderState& state)
}
else
{
RenderBSPNode(node, state);
if (Viewpoint.IsOrtho() && ((Level->flags3 & LEVEL3_NOFOGOFWAR) || !r_radarclipper)) RenderOrthoNoFog(state);
else RenderBSPNode(node, state);
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, state);

View file

@ -186,7 +186,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;
@ -354,6 +354,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
@ -371,11 +382,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));
@ -388,6 +416,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());
}
//-----------------------------------------------------------------------------
//
@ -421,7 +521,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);
@ -430,7 +530,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

@ -28,10 +28,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);
@ -40,6 +41,7 @@ class Clipper
public:
bool amRadar = false;
void Clear();
void Free(ClipNode *node)
@ -68,11 +70,11 @@ public:
c->next = c->prev = NULL;
return c;
}
void SetViewpoint(const FRenderViewpoint &vp)
{
viewpoint = &vp;
}
void SetViewpoint(FRenderViewpoint &vp)
{
viewpoint = &vp;
}
void SetSilhouette();
@ -100,19 +102,23 @@ public:
AddClipRange(startangle, endangle);
}
}
void SafeAddClipRange(const vertex_t *v1, const vertex_t *v2)
{
angle_t a2 = PointToPseudoAngle(v1->p.X, v1->p.Y);
angle_t a1 = PointToPseudoAngle(v2->p.X, v2->p.Y);
SafeAddClipRange(a1,a2);
}
void SafeAddClipRange(const vertex_t *v1, const vertex_t *v2)
{
angle_t a2 = PointToPseudoAngle(v1->p.X, v1->p.Y);
angle_t a1 = PointToPseudoAngle(v2->p.X, v2->p.Y);
SafeAddClipRange(a1,a2);
}
void SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle)
{
SafeAddClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle));
}
void SafeAddClipRangeDegPitches(double startpitch, double endpitch)
{
SafeAddClipRange(PitchToPseudo(startpitch), PitchToPseudo(endpitch));
}
void SafeRemoveClipRange(angle_t startangle, angle_t endangle)
{
@ -143,10 +149,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

@ -41,6 +41,8 @@ public:
FDrawInfoList di_list;
Clipper staticClipper; // Since all scenes are processed sequentially we only need one clipper.
Clipper staticVClipper; // Another clipper to clip vertically (used if (VPSF_ALLOWOUTOFBOUNDS & camera->viewpos->Flags)).
Clipper staticRClipper; // Another clipper for radar (doesn't actually clip. Changes SSECMF_DRAWN setting).
HWDrawInfo* gl_drawinfo = nullptr; // This is a linked list of all active DrawInfos and needed to free the memory arena after the last one goes out of scope.
FMemArena RenderDataAllocator; // Use large blocks to reduce allocation time.

View file

@ -89,7 +89,12 @@ HWDrawInfo *HWDrawInfo::StartDrawInfo(HWDrawContext* drawctx, FLevelLocals *lev,
void HWDrawInfo::StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms)
{
drawctx->staticClipper.Clear();
drawctx->staticVClipper.Clear();
drawctx->staticRClipper.Clear();
mClipper = &drawctx->staticClipper;
vClipper = &drawctx->staticVClipper;
rClipper = &drawctx->staticRClipper;
rClipper->amRadar = true;
Viewpoint = parentvp;
lightmode = getRealLightmode(Level, true);
@ -122,6 +127,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();
@ -167,10 +174,10 @@ HWDrawInfo *HWDrawInfo::EndDrawInfo()
void HWDrawInfo::ClearBuffers()
{
otherFloorPlanes.Clear();
otherCeilingPlanes.Clear();
floodFloorSegs.Clear();
floodCeilingSegs.Clear();
otherFloorPlanes.Clear();
otherCeilingPlanes.Clear();
floodFloorSegs.Clear();
floodCeilingSegs.Clear();
// clear all the lists that might not have been cleared already
MissingUpperTextures.Clear();
@ -212,7 +219,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);
}
@ -225,9 +234,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())
@ -306,16 +317,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;
}
@ -395,6 +404,10 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state)
const auto &vp = Viewpoint;
angle_t a1 = FrustumAngle();
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
drawctx->portalState.StartFrame();
@ -828,6 +841,122 @@ static ETraceStatus CheckForViewpointActor(FTraceResults& res, void* userdata)
return TRACE_Stop;
}
//==========================================================================
//
// 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:
if (res.HitPos.Z == res.Sector->floorplane.ZatPoint(res.HitPos))
{
res.Sector->floorplane.dithertransflag = true;
}
else if (res.Sector->e->XFloor.ffloors.Size()) // Maybe it was 3D floors
{
F3DFloor *rover;
int kk;
for (kk = 0; kk < (int)res.Sector->e->XFloor.ffloors.Size(); kk++)
{
rover = res.Sector->e->XFloor.ffloors[kk];
if ((rover->flags&(FF_EXISTS | FF_RENDERPLANES | FF_THISINSIDE)) == (FF_EXISTS | FF_RENDERPLANES))
{
if (res.HitPos.Z == rover->top.plane->ZatPoint(res.HitPos))
{
rover->top.plane->dithertransflag = true;
break; // Out of for loop
}
}
}
}
break;
case TRACE_HitCeiling:
if (res.HitPos.Z == res.Sector->ceilingplane.ZatPoint(res.HitPos))
{
res.Sector->ceilingplane.dithertransflag = true;
}
else if (res.Sector->e->XFloor.ffloors.Size()) // Maybe it was 3D floors
{
F3DFloor *rover;
int kk;
for (kk = 0; kk < (int)res.Sector->e->XFloor.ffloors.Size(); kk++)
{
rover = res.Sector->e->XFloor.ffloors[kk];
if ((rover->flags&(FF_EXISTS | FF_RENDERPLANES | FF_THISINSIDE)) == (FF_EXISTS | FF_RENDERPLANES))
{
if (res.HitPos.Z == rover->bottom.plane->ZatPoint(res.HitPos))
{
rover->bottom.plane->dithertransflag = true;
break; // Out of for loop
}
}
}
}
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;
}
}
}
void HWDrawInfo::DrawCoronas(FRenderState& state)
{
@ -969,13 +1098,17 @@ void HWDrawInfo::DrawScene(int drawmode, FRenderState& state)
{
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)
{
@ -1040,6 +1173,8 @@ void HWDrawInfo::ProcessScene(bool toscreen, FRenderState& state)
drawctx->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, state);
@ -1056,10 +1191,10 @@ void HWDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub)
auto portal = FindPortal(ptg);
if (!portal)
{
portal = new HWSectorStackPortal(&drawctx->portalState, ptg);
portal = new HWSectorStackPortal(&drawctx->portalState, ptg);
Portals.Push(portal);
}
auto ptl = static_cast<HWSectorStackPortal*>(portal);
auto ptl = static_cast<HWSectorStackPortal*>(portal);
ptl->AddSubsector(sub);
}

View file

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

View file

@ -334,7 +334,7 @@ void HWFlat::DrawFlat(HWFlatDispatcher *di, FRenderState &state, bool translucen
state.SetObjectColor(FlatColor | 0xff000000);
state.SetAddColor(AddColor | 0xff000000);
state.ApplyTextureManipulation(TextureFx);
if (plane.plane.dithertransflag) state.SetEffect(EFF_DITHERTRANS);
if (hacktype & SSRF_PLANEHACK)
{
@ -390,6 +390,7 @@ void HWFlat::DrawFlat(HWFlatDispatcher *di, FRenderState &state, bool translucen
state.SetObjectColor(0xffffffff);
state.SetAddColor(0);
state.ApplyTextureManipulation(nullptr);
if (plane.plane.dithertransflag) state.SetEffect(EFF_NONE);
}
//==========================================================================
@ -419,6 +420,8 @@ inline void HWFlat::PutFlat(HWFlatDispatcher *di, bool fog)
void HWFlat::Process(HWFlatDispatcher *di, FRenderState& state, 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.
@ -551,7 +554,7 @@ void HWFlat::ProcessSector(HWFlatDispatcher *di, FRenderState& state, sector_t *
//
//
//
if ((which & SSRF_RENDERFLOOR) && (!di->di || (frontsector->floorplane.ZatPoint(vp->Pos) <= vp->Pos.Z && (!section || !(section->flags & FSection::DONTRENDERFLOOR)))))
if (((which & SSRF_RENDERFLOOR) && (!di->di || (((frontsector->floorplane.ZatPoint(vp->Pos) <= vp->Pos.Z) && (!section || !(section->flags & FSection::DONTRENDERFLOOR)))))) && !(di->di && vp->IsOrtho() && (vp->PitchSin < 0.0)))
{
// process the original floor first.
@ -609,7 +612,7 @@ void HWFlat::ProcessSector(HWFlatDispatcher *di, FRenderState& state, sector_t *
//
//
//
if ((which & SSRF_RENDERCEILING) && ((!di->di || frontsector->ceilingplane.ZatPoint(vp->Pos) >= vp->Pos.Z && (!section || !(section->flags & FSection::DONTRENDERCEILING)))))
if (((which & SSRF_RENDERCEILING) && ((!di->di || ((frontsector->ceilingplane.ZatPoint(vp->Pos) >= vp->Pos.Z) && (!section || !(section->flags & FSection::DONTRENDERCEILING)))))) && !(di->di && vp->IsOrtho() && (vp->PitchSin > 0.0)))
{
// process the original ceiling first.

View file

@ -561,7 +561,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, FRenderState& state, 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

@ -64,6 +64,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)
@ -550,7 +551,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();
@ -572,6 +573,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);
@ -974,6 +985,7 @@ void HWSprite::Process(HWDrawInfo *di, FRenderState& state, AActor* thing, secto
{
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)
@ -1067,7 +1079,25 @@ void HWSprite::Process(HWDrawInfo *di, FRenderState& state, AActor* thing, secto
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))
{
@ -1081,7 +1111,7 @@ void HWSprite::Process(HWDrawInfo *di, FRenderState& state, AActor* thing, secto
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)
@ -1100,6 +1130,13 @@ void HWSprite::Process(HWDrawInfo *di, FRenderState& state, AActor* thing, secto
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:
@ -1140,6 +1177,7 @@ void HWSprite::Process(HWDrawInfo *di, FRenderState& state, AActor* thing, secto
}
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

@ -82,10 +82,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
}
}
//==========================================================================
@ -2041,6 +2047,8 @@ void HWWall::Process(HWWallDispatcher *di, FRenderState& state, seg_t *seg, sect
}
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

@ -1,3 +1,19 @@
//===========================================================================
//
// 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);
}
void main()
{
@ -13,6 +29,26 @@ void main()
FragColor = ProcessLightMode(material);
#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

@ -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
@ -1506,4 +1508,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,
};
};