diff --git a/src/am_map.cpp b/src/am_map.cpp index d3057b467..91ed928d9 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -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) diff --git a/src/common/rendering/hwrenderer/data/hw_cvars.h b/src/common/rendering/hwrenderer/data/hw_cvars.h index b36dda98a..5190401bb 100644 --- a/src/common/rendering/hwrenderer/data/hw_cvars.h +++ b/src/common/rendering/hwrenderer/data/hw_cvars.h @@ -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) diff --git a/src/common/rendering/hwrenderer/data/hw_renderstate.h b/src/common/rendering/hwrenderer/data/hw_renderstate.h index 06f79a56a..1caf9e830 100644 --- a/src/common/rendering/hwrenderer/data/hw_renderstate.h +++ b/src/common/rendering/hwrenderer/data/hw_renderstate.h @@ -32,6 +32,7 @@ enum ERenderEffect EFF_BURN, EFF_STENCIL, EFF_PORTAL, + EFF_DITHERTRANS, MAX_EFFECTS }; diff --git a/src/common/rendering/hwrenderer/data/hw_vrmodes.cpp b/src/common/rendering/hwrenderer/data/hw_vrmodes.cpp index c51ce2ee9..8cb3c94e5 100644 --- a/src/common/rendering/hwrenderer/data/hw_vrmodes.cpp +++ b/src/common/rendering/hwrenderer/data/hw_vrmodes.cpp @@ -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()); diff --git a/src/common/rendering/hwrenderer/data/hw_vrmodes.h b/src/common/rendering/hwrenderer/data/hw_vrmodes.h index 26c9fd211..989281520 100644 --- a/src/common/rendering/hwrenderer/data/hw_vrmodes.h +++ b/src/common/rendering/hwrenderer/data/hw_vrmodes.h @@ -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; diff --git a/src/common/rendering/vulkan/shaders/vk_shader.cpp b/src/common/rendering/vulkan/shaders/vk_shader.cpp index 78dd19237..b73d9ba8c 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shader.cpp @@ -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]; diff --git a/src/common/utility/m_bbox.h b/src/common/utility/m_bbox.h index f117c290b..7f622a6ba 100644 --- a/src/common/utility/m_bbox.h +++ b/src/common/utility/m_bbox.h @@ -72,6 +72,13 @@ class FBoundingBox 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]; } diff --git a/src/gamedata/g_mapinfo.cpp b/src/gamedata/g_mapinfo.cpp index 5081fe68d..4822f35a8 100644 --- a/src/gamedata/g_mapinfo.cpp +++ b/src/gamedata/g_mapinfo.cpp @@ -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 }, diff --git a/src/gamedata/g_mapinfo.h b/src/gamedata/g_mapinfo.h index 17fae6255..1e9419739 100644 --- a/src/gamedata/g_mapinfo.h +++ b/src/gamedata/g_mapinfo.h @@ -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, diff --git a/src/gamedata/r_defs.h b/src/gamedata/r_defs.h index 04e32c748..54295a12b 100644 --- a/src/gamedata/r_defs.h +++ b/src/gamedata/r_defs.h @@ -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; diff --git a/src/maploader/maploader.cpp b/src/maploader/maploader.cpp index 51faf97a1..b819bd57d 100644 --- a/src/maploader/maploader.cpp +++ b/src/maploader/maploader.cpp @@ -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++; + } + } } diff --git a/src/playsim/actor.h b/src/playsim/actor.h index ca7c1d7f9..986e3e11a 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -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 @@ class AActor final : public DThinker 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 ViewPos; // Position offsets for cameras diff --git a/src/playsim/p_map.cpp b/src/playsim/p_map.cpp index a79f912ae..af8df92a7 100644 --- a/src/playsim/p_map.cpp +++ b/src/playsim/p_map.cpp @@ -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(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; + } } } } diff --git a/src/playsim/p_trace.cpp b/src/playsim/p_trace.cpp index 7e47a7323..ed37d5e34 100644 --- a/src/playsim/p_trace.cpp +++ b/src/playsim/p_trace.cpp @@ -633,6 +633,9 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec 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; diff --git a/src/playsim/p_trace.h b/src/playsim/p_trace.h index ef5318678..9035ae965 100644 --- a/src/playsim/p_trace.h +++ b/src/playsim/p_trace.h @@ -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, diff --git a/src/rendering/hwrenderer/hw_entrypoint.cpp b/src/rendering/hwrenderer/hw_entrypoint.cpp index 8e7c363a1..8a2a2bc8b 100644 --- a/src/rendering/hwrenderer/hw_entrypoint.cpp +++ b/src/rendering/hwrenderer/hw_entrypoint.cpp @@ -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); diff --git a/src/rendering/hwrenderer/scene/hw_bsp.cpp b/src/rendering/hwrenderer/scene/hw_bsp.cpp index 6dd4d04bb..5dd103ab3 100644 --- a/src/rendering/hwrenderer/scene/hw_bsp.cpp +++ b/src/rendering/hwrenderer/scene/hw_bsp.cpp @@ -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-endAngleflags & SSECMF_DRAWN)) { - if (clipper.SafeCheckRange(startAngle, endAngle)) + if (clipper.SafeCheckRange(startAngle, endAngle) && (!r_radarclipper || (Level->flags3 & LEVEL3_NOFOGOFWAR))) + { + currentsubsector->flags |= SSECMF_DRAWN; + } + if ((r_radarclipper || !(Level->flags3 & LEVEL3_NOFOGOFWAR)) && clipperr.SafeCheckRange(startAngleR, endAngleR)) { - currentsubsector->flags |= SSECMF_DRAWN; + 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); diff --git a/src/rendering/hwrenderer/scene/hw_clipper.cpp b/src/rendering/hwrenderer/scene/hw_clipper.cpp index b46e7837f..09031f8d4 100644 --- a/src/rendering/hwrenderer/scene/hw_clipper.cpp +++ b/src/rendering/hwrenderer/scene/hw_clipper.cpp @@ -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); +} diff --git a/src/rendering/hwrenderer/scene/hw_clipper.h b/src/rendering/hwrenderer/scene/hw_clipper.h index 4eeff16b3..1128fa71e 100644 --- a/src/rendering/hwrenderer/scene/hw_clipper.h +++ b/src/rendering/hwrenderer/scene/hw_clipper.h @@ -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 @@ class Clipper 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 @@ class Clipper 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 @@ class Clipper { 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) diff --git a/src/rendering/hwrenderer/scene/hw_drawcontext.h b/src/rendering/hwrenderer/scene/hw_drawcontext.h index 62e971de1..85bca991c 100644 --- a/src/rendering/hwrenderer/scene/hw_drawcontext.h +++ b/src/rendering/hwrenderer/scene/hw_drawcontext.h @@ -41,6 +41,8 @@ class HWDrawContext 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. diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp index 6211bcb0f..98f0214ca 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp @@ -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(portal); + auto ptl = static_cast(portal); ptl->AddSubsector(sub); } diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.h b/src/rendering/hwrenderer/scene/hw_drawinfo.h index 900104345..6114ec2b8 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.h +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.h @@ -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 Portals; @@ -254,6 +256,7 @@ struct HWDrawInfo 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 @@ struct HWDrawInfo void DrawCoronas(FRenderState& state); void DrawCorona(FRenderState& state, AActor* corona, float coronaFade, double dist); + void SetDitherTransFlags(AActor* actor); + void ProcessLowerMinisegs(TArray &lowersegs, FRenderState& state); void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub); diff --git a/src/rendering/hwrenderer/scene/hw_flats.cpp b/src/rendering/hwrenderer/scene/hw_flats.cpp index 9e1a58bfe..f3af5caab 100644 --- a/src/rendering/hwrenderer/scene/hw_flats.cpp +++ b/src/rendering/hwrenderer/scene/hw_flats.cpp @@ -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. diff --git a/src/rendering/hwrenderer/scene/hw_portal.cpp b/src/rendering/hwrenderer/scene/hw_portal.cpp index fe593719c..bdbaf8c6e 100644 --- a/src/rendering/hwrenderer/scene/hw_portal.cpp +++ b/src/rendering/hwrenderer/scene/hw_portal.cpp @@ -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; } diff --git a/src/rendering/hwrenderer/scene/hw_sky.cpp b/src/rendering/hwrenderer/scene/hw_sky.cpp index 2a65a9f01..7869bc284 100644 --- a/src/rendering/hwrenderer/scene/hw_sky.cpp +++ b/src/rendering/hwrenderer/scene/hw_sky.cpp @@ -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 diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index 3f84c6982..9304e09c3 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -64,6 +64,7 @@ extern TArray 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 diff --git a/src/rendering/hwrenderer/scene/hw_walls.cpp b/src/rendering/hwrenderer/scene/hw_walls.cpp index ecb5661ea..92f04b892 100644 --- a/src/rendering/hwrenderer/scene/hw_walls.cpp +++ b/src/rendering/hwrenderer/scene/hw_walls.cpp @@ -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 diff --git a/src/rendering/r_utility.cpp b/src/rendering/r_utility.cpp index 2a5d95586..3e40c3a0c 100644 --- a/src/rendering/r_utility.cpp +++ b/src/rendering/r_utility.cpp @@ -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) diff --git a/src/rendering/r_utility.h b/src/rendering/r_utility.h index 3013961eb..ee12ecf36 100644 --- a/src/rendering/r_utility.h +++ b/src/rendering/r_utility.h @@ -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 }; diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 570dd2a4e..3993cd745 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -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), diff --git a/wadsrc/static/shaders/scene/frag_main.glsl b/wadsrc/static/shaders/scene/frag_main.glsl index ba3987517..f0274aa1a 100644 --- a/wadsrc/static/shaders/scene/frag_main.glsl +++ b/wadsrc/static/shaders/scene/frag_main.glsl @@ -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); diff --git a/wadsrc/static/zscript/actors/shared/camera.zs b/wadsrc/static/zscript/actors/shared/camera.zs index d7826c051..463ad6178 100644 --- a/wadsrc/static/zscript/actors/shared/camera.zs +++ b/wadsrc/static/zscript/actors/shared/camera.zs @@ -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); + } + } +} diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index 38bf37a65..d99daa9e2 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -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, -}; \ No newline at end of file +};