diff --git a/src/am_map.cpp b/src/am_map.cpp index 8414b598d0..c6b10edf04 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -2808,7 +2808,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)) @@ -2818,7 +2818,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 cf23649799..7f84ba09e2 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(Int, gl_shadowmap_filter) EXTERN_CVAR(Bool, gl_brightfog) EXTERN_CVAR(Bool, gl_lightadditivesurfaces) EXTERN_CVAR(Bool, gl_notexturefill) + +EXTERN_CVAR(Bool, r_radarclipper) +EXTERN_CVAR(Bool, r_dithertransparency) diff --git a/src/common/rendering/hwrenderer/data/hw_renderstate.h b/src/common/rendering/hwrenderer/data/hw_renderstate.h index 05fd6bcbf1..41e4b50a37 100644 --- a/src/common/rendering/hwrenderer/data/hw_renderstate.h +++ b/src/common/rendering/hwrenderer/data/hw_renderstate.h @@ -25,7 +25,7 @@ enum ERenderEffect EFF_SPHEREMAP, EFF_BURN, EFF_STENCIL, - + EFF_DITHERTRANS, MAX_EFFECTS }; diff --git a/src/common/rendering/hwrenderer/data/hw_shaderpatcher.cpp b/src/common/rendering/hwrenderer/data/hw_shaderpatcher.cpp index 9049e5090e..d32b09e233 100644 --- a/src/common/rendering/hwrenderer/data/hw_shaderpatcher.cpp +++ b/src/common/rendering/hwrenderer/data/hw_shaderpatcher.cpp @@ -299,6 +299,7 @@ const FEffectShader effectshaders[] = { "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" }, { "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" }, { "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" }, + { "dithertrans", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", "#define NO_ALPHATEST\n#define DITHERTRANS\n" }, }; int DFrameBuffer::GetShaderCount() diff --git a/src/common/rendering/hwrenderer/data/hw_vrmodes.cpp b/src/common/rendering/hwrenderer/data/hw_vrmodes.cpp index c51ce2ee90..8cb3c94e5c 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 26c9fd211e..58d9e06c1c 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/gamedata/g_mapinfo.cpp b/src/gamedata/g_mapinfo.cpp index dfe90b2c95..8c10838cd1 100644 --- a/src/gamedata/g_mapinfo.cpp +++ b/src/gamedata/g_mapinfo.cpp @@ -1819,6 +1819,7 @@ MapFlagHandlers[] = { "disableskyboxao", MITYPE_CLRFLAG3, LEVEL3_SKYBOXAO, 0 }, { "avoidmelee", MITYPE_SETFLAG3, LEVEL3_AVOIDMELEE, 0 }, { "attenuatelights", MITYPE_SETFLAG3, LEVEL3_ATTENUATE, 0 }, + { "nofogofwar", MITYPE_SETFLAG3, LEVEL3_NOFOGOFWAR, 0 }, { "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes { "nopassover", MITYPE_COMPATFLAG, COMPATF_NO_PASSMOBJ, 0 }, { "passover", MITYPE_CLRCOMPATFLAG, COMPATF_NO_PASSMOBJ, 0 }, diff --git a/src/gamedata/g_mapinfo.h b/src/gamedata/g_mapinfo.h index b56d3b6329..d7d60f2225 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 }; diff --git a/src/gamedata/r_defs.h b/src/gamedata/r_defs.h index 3333a0c7a8..7206777be4 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) @@ -1179,6 +1180,8 @@ enum WALLF_ABSLIGHTING_TOP = WALLF_ABSLIGHTING_TIER << 0, // Top tier light is absolute instead of relative WALLF_ABSLIGHTING_MID = WALLF_ABSLIGHTING_TIER << 1, // Mid tier light is absolute instead of relative WALLF_ABSLIGHTING_BOTTOM = WALLF_ABSLIGHTING_TIER << 2, // Bottom tier light is absolute instead of relative + + WALLF_DITHERTRANS = 8192, // Render with dithering transparency shader (gets reset every frame) }; struct side_t diff --git a/src/playsim/actor.h b/src/playsim/actor.h index ee87461491..47180d56dd 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -501,6 +501,7 @@ enum ActorRenderFlag2 RF2_FLIPSPRITEOFFSETX = 0x0010, RF2_FLIPSPRITEOFFSETY = 0x0020, RF2_CAMFOLLOWSPLAYER = 0x0040, // Matches the cam's base position and angles to the main viewpoint. + RF2_ISOMETRICSPRITES = 0x0080, }; // This translucency value produces the closest match to Heretic's TINTTAB. @@ -693,6 +694,8 @@ enum EViewPosFlags // [MC] Flags for SetViewPos. { 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 @@ -1119,6 +1122,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 ViewPos; // Position offsets for cameras diff --git a/src/playsim/p_map.cpp b/src/playsim/p_map.cpp index 53329b96fb..90989b6a78 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.camera->ViewPos && (viewPoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS) && 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.camera->ViewPos || !(viewPoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS) || !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 7e47a73234..ed37d5e34b 100644 --- a/src/playsim/p_trace.cpp +++ b/src/playsim/p_trace.cpp @@ -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; diff --git a/src/playsim/p_trace.h b/src/playsim/p_trace.h index ef53186781..9035ae965f 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 e80d7ef669..bfa0d8b1ce 100644 --- a/src/rendering/hwrenderer/hw_entrypoint.cpp +++ b/src/rendering/hwrenderer/hw_entrypoint.cpp @@ -163,7 +163,11 @@ sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bou di->Viewpoint.FieldOfView = DAngle::fromDeg(fov); // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint) // Stereo mode specific perspective projection - di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio); + float inv_iso_dist = 1.0f; + bool iso_ortho = (camera->ViewPos != NULL) && (camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC); + if (iso_ortho && (camera->ViewPos->Offset.Length() > 0)) inv_iso_dist = 1.0/camera->ViewPos->Offset.Length(); + di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio * inv_iso_dist, iso_ortho); + // Stereo mode specific viewpoint adjustment vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees()); di->SetupView(RenderState, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); diff --git a/src/rendering/hwrenderer/scene/hw_bsp.cpp b/src/rendering/hwrenderer/scene/hw_bsp.cpp index 12439669c2..3db813ce25 100644 --- a/src/rendering/hwrenderer/scene/hw_bsp.cpp +++ b/src/rendering/hwrenderer/scene/hw_bsp.cpp @@ -51,6 +51,8 @@ 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); @@ -269,6 +271,20 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip) auto &clipper = *mClipper; angle_t startAngle = clipper.GetClipAngle(seg->v2); angle_t endAngle = clipper.GetClipAngle(seg->v1); + auto &clipperr = *rClipper; + angle_t startAngleR = clipperr.PointToPseudoAngle(seg->v2->fX(), seg->v2->fY()); + angle_t endAngleR = clipperr.PointToPseudoAngle(seg->v1->fX(), seg->v1->fY()); + + if(r_radarclipper && !(Level->flags3 & LEVEL3_NOFOGOFWAR) && (startAngleR - endAngleR >= ANGLE_180)) + { + if (!seg->backsector) clipperr.SafeAddClipRange(startAngleR, endAngleR); + else if((seg->sidedef != nullptr) && !uint8_t(seg->sidedef->Flags & WALLF_POLYOBJ) && (currentsector->sectornum != seg->backsector->sectornum)) + { + if (in_area == area_default) in_area = hw_CheckViewArea(seg->v2, seg->v1, seg->frontsector, seg->backsector); + backsector = hw_FakeFlat(seg->backsector, in_area, true); + if (hw_CheckClip(seg->sidedef, currentsector, backsector)) clipperr.SafeAddClipRange(startAngleR, endAngleR); + } + } // Back side, i.e. backface culling - read: endAngle >= startAngle! if (startAngle-endAngleflags & 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.camera->ViewPos != NULL) && (Viewpoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS))) + if (!(seg->sidedef->Flags & WALLF_DITHERTRANS)) clipper.SafeAddClipRange(startAngle, endAngle); } else if (!ispoly) // Two-sided polyobjects never obstruct the view { @@ -325,7 +369,8 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip) if (hw_CheckClip(seg->sidedef, currentsector, backsector)) { - clipper.SafeAddClipRange(startAngle, endAngle); + if(!((Viewpoint.camera->ViewPos != NULL) && (Viewpoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS))) + if (!(seg->sidedef->Flags & WALLF_DITHERTRANS)) clipper.SafeAddClipRange(startAngle, endAngle); } } } @@ -533,6 +578,7 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector) if (thing->validcount == validcount) continue; thing->validcount = validcount; + if((Viewpoint.camera->ViewPos != NULL) && (Viewpoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS) && thing->Sector->isSecret() && thing->Sector->wasSecret() && !r_radarclipper) continue; // This covers things that are touching non-secret sectors FIntCVar *cvar = thing->GetInfo()->distancecheck; if (cvar != nullptr && *cvar >= 0) { @@ -666,6 +712,51 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) fakesector=hw_FakeFlat(sector, in_area, false); + if((Viewpoint.camera->ViewPos != NULL) && (Viewpoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS) && sector->isSecret() && sector->wasSecret() && !r_radarclipper) return; + + // cull everything if subsector outside vertical clipper + if (sub->polys == nullptr) + { + 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); @@ -712,7 +803,7 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) if (gl_render_things && (sector->touching_renderthings || sector->sectorportal_thinglist)) { - if (multithread) + if (multithread) { jobQueue.AddJob(RenderJob::SpriteJob, sub, nullptr); } @@ -723,6 +814,23 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) SetupSprite.Unclock(); } } + if (r_dithertransparency) + { + // [DVR] Not parallelizable due to line->validcount race coundition + for (auto p = sector->touching_renderthings; p != nullptr; p = p->m_snext) + { + auto thing = p->m_thing; + if (((thing->flags & MF_SHOOTABLE) && (thing->health > 0)) || (thing->flags & MF_MISSILE)) + { + if ( P_CheckSight(players[consoleplayer].mo, thing, 0) ) + { + SetDitherTransFlags(thing); + } + } + } + validcount++; // [DVR] Need this since Trace() and P_CheckSight() get called + // and they set line->validcount = validcount, preventing lines from being processed. + } } if (gl_render_flats) @@ -843,6 +951,14 @@ void HWDrawInfo::RenderBSPNode (void *node) if (!(no_renderflags[bsp->Index()] & SSRF_SEEN)) return; } + if ((Viewpoint.camera->ViewPos != NULL) && (Viewpoint.camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC)) + { + if (!vClipper->CheckBoxOrthoPitch(bsp->bbox[side])) + { + if (!(no_renderflags[bsp->Index()] & SSRF_SEEN)) + return; + } + } node = bsp->children[side]; } @@ -856,6 +972,19 @@ void HWDrawInfo::RenderBSP(void *node, bool drawpsprites) // 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.camera->ViewPos != NULL) && (Viewpoint.camera->ViewPos->Flags & (VPSF_ABSOLUTEOFFSET | VPSF_ALLOWOUTOFBOUNDS))) + { + if (Viewpoint.camera->tracer != NULL) + { + viewx = FLOAT2FIXED(Viewpoint.camera->tracer->X()); + viewy = FLOAT2FIXED(Viewpoint.camera->tracer->Y()); + } + else + { + viewx = FLOAT2FIXED(Viewpoint.camera->X()); + viewy = FLOAT2FIXED(Viewpoint.camera->Y()); + } + } validcount++; // used for processing sidedefs only once by the renderer. diff --git a/src/rendering/hwrenderer/scene/hw_clipper.cpp b/src/rendering/hwrenderer/scene/hw_clipper.cpp index fd69e267c2..dd5164d3be 100644 --- a/src/rendering/hwrenderer/scene/hw_clipper.cpp +++ b/src/rendering/hwrenderer/scene/hw_clipper.cpp @@ -193,7 +193,7 @@ void Clipper::AddClipRange(angle_t start, angle_t end) if (node->end < end) { - node->end = end; + node->end = end; // [DVR] This never triggers because of previous while loop. Remove? } ClipNode *node2 = node->next; @@ -361,6 +361,17 @@ angle_t Clipper::AngleToPseudo(angle_t ang) return xs_Fix<30>::ToFix(result); } +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +angle_t Clipper::PitchToPseudo(double ang) +{ + return AngleToPseudo(DAngle::fromDeg(90.0-ang).BAMs()); // Pitch is positive when looking down +} + //----------------------------------------------------------------------------- // // ! Returns the pseudoangle between the line p1 to (infinity, p1.y) and the @@ -378,11 +389,28 @@ angle_t Clipper::PointToPseudoAngle(double x, double y) { double vecx = x - viewpoint->Pos.X; double vecy = y - viewpoint->Pos.Y; + if ((viewpoint->camera != NULL) && amRadar) + { + if (viewpoint->camera->tracer != NULL) + { + vecx = x - viewpoint->camera->tracer->X(); + vecy = y - viewpoint->camera->tracer->Y(); + } + else + { + vecx = x - viewpoint->camera->X(); + vecy = y - viewpoint->camera->Y(); + } + } if (vecx == 0 && vecy == 0) { return 0; } + else if (!amRadar && (viewpoint->camera->ViewPos != NULL) && (viewpoint->camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC)) + { + return PointToPseudoOrthoAngle(x, y); + } else { double result = vecy / (fabs(vecx) + fabs(vecy)); @@ -395,6 +423,78 @@ angle_t Clipper::PointToPseudoAngle(double x, double y) } +angle_t Clipper::PointToPseudoPitch(double x, double y, double z) +{ + double vecx = x - viewpoint->Pos.X; + double vecy = y - viewpoint->Pos.Y; + double vecz = z - viewpoint->Pos.Z; + double result = 0; + + if (vecx == 0 && vecy == 0 && vecz == 0) + { + return 0; + } + else if ((viewpoint->camera->ViewPos != NULL) && (viewpoint->camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC)) + { + 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 @@ angle_t Clipper::PointToPseudoAngle(double x, double y) bool Clipper::CheckBox(const float *bspcoord) { - angle_t angle1, angle2; + angle_t angle1, angle2; int boxpos; const uint8_t* check; @@ -437,7 +537,47 @@ bool Clipper::CheckBox(const float *bspcoord) check = checkcoord[boxpos]; angle1 = PointToPseudoAngle (bspcoord[check[0]], bspcoord[check[1]]); angle2 = PointToPseudoAngle (bspcoord[check[2]], bspcoord[check[3]]); + + if ((vp->camera->ViewPos != NULL) && (vp->camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC)) + { + 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->camera->ViewPos != NULL) && (vp->camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC))) 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 e8da8172cf..07c424ff85 100644 --- a/src/rendering/hwrenderer/scene/hw_clipper.h +++ b/src/rendering/hwrenderer/scene/hw_clipper.h @@ -33,6 +33,7 @@ class Clipper bool blocked = false; static angle_t AngleToPseudo(angle_t ang); + static angle_t PitchToPseudo(double ang); bool IsRangeVisible(angle_t startangle, angle_t endangle); void RemoveRange(ClipNode * cn); void AddClipRange(angle_t startangle, angle_t endangle); @@ -41,6 +42,7 @@ class Clipper public: + bool amRadar = false; Clipper(); void Clear(); @@ -116,6 +118,10 @@ public: SafeAddClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle)); } + void SafeAddClipRangeDegPitches(double startpitch, double endpitch) + { + SafeAddClipRange(PitchToPseudo(startpitch), PitchToPseudo(endpitch)); + } void SafeRemoveClipRange(angle_t startangle, angle_t endangle) { @@ -148,8 +154,12 @@ public: } 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_drawinfo.cpp b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp index 2275d9a113..191f4c1a12 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp @@ -131,12 +131,19 @@ HWDrawInfo *HWDrawInfo::StartDrawInfo(FLevelLocals *lev, HWDrawInfo *parent, FRe //========================================================================== static Clipper staticClipper; // Since all scenes are processed sequentially we only need one clipper. +static Clipper staticVClipper; // Another clipper to clip vertically (used if (VPSF_ALLOWOUTOFBOUNDS & camera->viewpos->Flags)). +static Clipper staticRClipper; // Another clipper for radar (doesn't actually clip. Changes SSECMF_DRAWN setting). static HWDrawInfo * gl_drawinfo; // This is a linked list of all active DrawInfos and needed to free the memory arena after the last one goes out of scope. void HWDrawInfo::StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms) { staticClipper.Clear(); + staticVClipper.Clear(); + staticRClipper.Clear(); mClipper = &staticClipper; + vClipper = &staticVClipper; + rClipper = &staticRClipper; + rClipper->amRadar = true; Viewpoint = parentvp; lightmode = getRealLightmode(Level, true); @@ -169,6 +176,8 @@ void HWDrawInfo::StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uni VPUniforms.mLightBlendMode = (level.info ? (int)level.info->lightblendmode : 0); } mClipper->SetViewpoint(Viewpoint); + vClipper->SetViewpoint(Viewpoint); + rClipper->SetViewpoint(Viewpoint); ClearBuffers(); @@ -257,7 +266,9 @@ void HWDrawInfo::ClearBuffers() void HWDrawInfo::UpdateCurrentMapSection() { - const int mapsection = Level->PointInRenderSubsector(Viewpoint.Pos)->mapsection; + int mapsection = Level->PointInRenderSubsector(Viewpoint.Pos)->mapsection; + if ((Viewpoint.camera->ViewPos != NULL) && (Viewpoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS)) + mapsection = Level->PointInRenderSubsector(Viewpoint.camera->Pos())->mapsection; CurrentMapSections.Set(mapsection); } @@ -273,6 +284,8 @@ void HWDrawInfo::SetViewArea() 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 ((vp.camera->ViewPos != NULL) && (vp.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS)) + vp.sector = Level->PointInRenderSubsector(vp.camera->Pos())->render_sector; // Get the heightsec state from the render sector, not the current one! if (vp.sector->GetHeightSec()) @@ -351,16 +364,14 @@ int HWDrawInfo::SetFullbrightFlags(player_t *player) angle_t HWDrawInfo::FrustumAngle() { - float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees()); + // If pitch is larger than this you can look all around at an FOV of 90 degrees + if (fabs(Viewpoint.HWAngles.Pitch.Degrees()) > 89.0) return 0xffffffff; - // If the pitch is larger than this you can look all around at a FOV of 90° - if (tilt > 46.0f) return 0xffffffff; + double xratio = r_viewwindow.FocalTangent / Viewpoint.PitchCos; + double floatangle = 0.035 + atan ( xratio ) * 48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio); // this is radians + angle_t a1 = DAngle::fromRad(floatangle).BAMs(); - // ok, this is a gross hack that barely works... - // but at least it doesn't overestimate too much... - double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees() * 48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0; - angle_t a1 = DAngle::fromDeg(floatangle).BAMs(); - if (a1 >= ANGLE_180) return 0xffffffff; + if (a1 >= ANGLE_90) return 0xffffffff; return a1; } @@ -438,8 +449,12 @@ HWDecal *HWDrawInfo::AddDecal(bool onmirror) void HWDrawInfo::CreateScene(bool drawpsprites) { const auto &vp = Viewpoint; - angle_t a1 = FrustumAngle(); + angle_t a1 = FrustumAngle(); // horizontally clip the back of the viewport mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1); + Viewpoint.FrustAngle = a1; + double a2 = 20.0 + 0.5*Viewpoint.FieldOfView.Degrees(); // FrustumPitch for vertical clipping + if (a2 > 179.0) a2 = 179.0; + vClipper->SafeAddClipRangeDegPitches(vp.HWAngles.Pitch.Degrees() - a2, vp.HWAngles.Pitch.Degrees() + a2); // clip the suplex range // reset the portal manager portalState.StartFrame(); @@ -648,6 +663,79 @@ void HWDrawInfo::DrawCorona(FRenderState& state, ACorona* corona, double dist) #endif } +//========================================================================== +// +// TraceCallbackForDitherTransparency +// Toggles dither flag on anything that occludes the actor's +// position from viewpoint. +// +//========================================================================== + +static ETraceStatus TraceCallbackForDitherTransparency(FTraceResults& res, void* userdata) +{ + int* count = (int*)userdata; + double bf, bc; + (*count)++; + switch(res.HitType) + { + case TRACE_HitWall: + if (!(res.Line->sidedef[res.Side]->Flags & WALLF_DITHERTRANS)) + { + bf = res.Line->sidedef[res.Side]->sector->floorplane.ZatPoint(res.HitPos.XY()); + bc = res.Line->sidedef[res.Side]->sector->ceilingplane.ZatPoint(res.HitPos.XY()); + if ((res.HitPos.Z <= bc) && (res.HitPos.Z >= bf)) res.Line->sidedef[res.Side]->Flags |= WALLF_DITHERTRANS; + } + break; + case TRACE_HitFloor: + res.Sector->floorplane.dithertransflag = true; + break; + case TRACE_HitCeiling: + res.Sector->ceilingplane.dithertransflag = true; + break; + case TRACE_HitActor: + default: + break; + } + + return TRACE_ContinueOutOfBounds; +} + + +void HWDrawInfo::SetDitherTransFlags(AActor* actor) +{ + if (actor && actor->Sector) + { + FTraceResults results; + double horix = Viewpoint.Sin * actor->radius; + double horiy = Viewpoint.Cos * actor->radius; + DVector3 actorpos = actor->Pos(); + DVector3 vvec = actorpos - Viewpoint.Pos; + if (Viewpoint.camera->ViewPos && (Viewpoint.camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC)) + { + 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, + MF_SOLID, ML_BLOCKEVERYTHING, actor, results, 0, TraceCallbackForDitherTransparency, &count); + campos.Z += actor->Height * 0.5; + Trace(campos, startsec, vvec, distance, + MF_SOLID, ML_BLOCKEVERYTHING, actor, results, 0, TraceCallbackForDitherTransparency, &count); + campos.Z -= actor->Height * 0.5; + campos.X += horix; campos.Y -= horiy; + } + } +} + static ETraceStatus CheckForViewpointActor(FTraceResults& res, void* userdata) { FRenderViewpoint* data = (FRenderViewpoint*)userdata; @@ -810,6 +898,10 @@ void HWDrawInfo::DrawScene(int drawmode) { ssao_portals_available = gl_ssao_portals; applySSAO = true; + if (r_dithertransparency) + { + vp.camera->tracer ? SetDitherTransFlags(vp.camera->tracer) : SetDitherTransFlags(players[consoleplayer].mo); + } } else if (drawmode == DM_OFFSCREEN) { @@ -864,6 +956,8 @@ void HWDrawInfo::ProcessScene(bool toscreen) portalState.BeginScene(); int mapsection = Level->PointInRenderSubsector(Viewpoint.Pos)->mapsection; + if ((Viewpoint.camera->ViewPos != NULL) && (Viewpoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS)) + mapsection = Level->PointInRenderSubsector(Viewpoint.camera->Pos())->mapsection; CurrentMapSections.Set(mapsection); DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN); diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.h b/src/rendering/hwrenderer/scene/hw_drawinfo.h index 0d298d9eaa..e32fec8c4a 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.h +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.h @@ -146,6 +146,8 @@ struct HWDrawInfo HWPortal *mCurrentPortal; //FRotator mAngles; Clipper *mClipper; + Clipper *vClipper; // Vertical clipper + Clipper *rClipper; // Radar clipper FRenderViewpoint Viewpoint; HWViewpointUniforms VPUniforms; // per-viewpoint uniform state TArray Portals; @@ -302,6 +304,8 @@ public: void DrawCoronas(FRenderState& state); void DrawCorona(FRenderState& state, ACorona* corona, double dist); + void SetDitherTransFlags(AActor* actor); + void ProcessLowerMinisegs(TArray &lowersegs); 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 459fe64452..999172f466 100644 --- a/src/rendering/hwrenderer/scene/hw_flats.cpp +++ b/src/rendering/hwrenderer/scene/hw_flats.cpp @@ -320,7 +320,7 @@ void HWFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent) state.SetObjectColor(FlatColor | 0xff000000); state.SetAddColor(AddColor | 0xff000000); state.ApplyTextureManipulation(TextureFx); - + if (plane.plane.dithertransflag) state.SetEffect(EFF_DITHERTRANS); if (hacktype & SSRF_PLANEHACK) { @@ -372,6 +372,7 @@ void HWFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent) state.SetObjectColor(0xffffffff); state.SetAddColor(0); state.ApplyTextureManipulation(nullptr); + if (plane.plane.dithertransflag) state.SetEffect(EFF_NONE); } //========================================================================== @@ -408,6 +409,8 @@ inline void HWFlat::PutFlat(HWDrawInfo *di, bool fog) void HWFlat::Process(HWDrawInfo *di, sector_t * model, int whichplane, bool fog) { plane.GetFromSector(model, whichplane); + model->ceilingplane.dithertransflag = false; // Resetting this every frame + model->floorplane.dithertransflag = false; // Resetting this every frame if (whichplane != int(ceiling)) { // Flip the normal if the source plane has a different orientation than what we are about to render. @@ -515,7 +518,7 @@ void HWFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which) // // // - if ((which & SSRF_RENDERFLOOR) && frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z && (!section || !(section->flags & FSection::DONTRENDERFLOOR))) + if (((which & SSRF_RENDERFLOOR) && frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z && (!section || !(section->flags & FSection::DONTRENDERFLOOR)))&& !((vp.camera->ViewPos != NULL) && (vp.camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC) && (vp.PitchSin < 0.0))) { // process the original floor first. @@ -573,7 +576,7 @@ void HWFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which) // // // - if ((which & SSRF_RENDERCEILING) && frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z && (!section || !(section->flags & FSection::DONTRENDERCEILING))) + if (((which & SSRF_RENDERCEILING) && frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z && (!section || !(section->flags & FSection::DONTRENDERCEILING))) && !((vp.camera->ViewPos != NULL) && (vp.camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC) && (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 a453827f35..fe3e710d60 100644 --- a/src/rendering/hwrenderer/scene/hw_portal.cpp +++ b/src/rendering/hwrenderer/scene/hw_portal.cpp @@ -563,7 +563,8 @@ bool HWMirrorPortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clippe angle_t af = di->FrustumAngle(); if (af < ANGLE_180) clipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + af, vp.Angles.Yaw.BAMs() - af); - clipper->SafeAddClipRange(linedef->v1, linedef->v2); + if(!((di->Viewpoint.camera->ViewPos != NULL) && (di->Viewpoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS))) + 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 dfa954530f..394699ae6f 100644 --- a/src/rendering/hwrenderer/scene/hw_sky.cpp +++ b/src/rendering/hwrenderer/scene/hw_sky.cpp @@ -130,6 +130,10 @@ void HWSkyInfo::init(HWDrawInfo *di, sector_t* sec, int skypos, int sky1, PalEnt void HWWall::SkyPlane(HWWallDispatcher *di, sector_t *sector, int plane, bool allowreflect) { int ptype = -1; + if(di->di->Viewpoint.camera != NULL && di->di->Viewpoint.camera->ViewPos != NULL) + { + if(di->di->Viewpoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS) return; + } 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 37b4d1a29b..386779ca2a 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -489,7 +489,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(); @@ -509,6 +509,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.camera->ViewPos != NULL) && (di->Viewpoint.camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC)) + { + 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); @@ -902,6 +912,7 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t { bool mirror = false; DAngle ang = (thingpos - vp.Pos).Angle(); + if (thing->renderflags2 & RF2_ISOMETRICSPRITES) ang = vp.Angles.Yaw; FTextureID patch; // [ZZ] add direct picnum override if (isPicnumOverride) @@ -995,14 +1006,32 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t if (thing->renderflags & RF_SPRITEFLIP) // [SP] Flip back thing->renderflags ^= RF_XFLIP; - r.Scale(sprscale.X, isSpriteShadow ? sprscale.Y * 0.15 : sprscale.Y); + // If sprite is isometric, do both vertical scaling and partial rotation to face the camera to compensate for Y-billboarding. + // Using just rotation (about z=0) might cause tall+slender (high aspect ratio) sprites to clip out of collision box + // at the top and clip into whatever is behind them from the viewpoint's perspective. - [DVR] + thing->isoscaleY = 1.0; + thing->isotheta = vp.HWAngles.Pitch.Degrees(); + if (thing->renderflags2 & RF2_ISOMETRICSPRITES) + { + float floordist = thing->radius * vp.floordistfact; + floordist -= 0.5 * r.width * vp.cotfloor; + float sineisotheta = floordist / r.height; + double scl = g_sqrt( 1.0 + sineisotheta * sineisotheta - 2.0 * vp.PitchSin * sineisotheta ); + if ((thing->radius > 0.0) && (scl > fabs(vp.PitchCos))) + { + thing->isoscaleY = scl / ( fabs(vp.PitchCos) > 0.01 ? fabs(vp.PitchCos) : 0.01 ); + thing->isotheta = 180.0 * asin( sineisotheta / thing->isoscaleY ) / M_PI; + } + } + + r.Scale(sprscale.X, isSpriteShadow ? sprscale.Y * 0.15 * thing->isoscaleY : sprscale.Y * thing->isoscaleY); float rightfac = -r.left; float leftfac = rightfac - r.width; 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) @@ -1021,6 +1050,13 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t x2 = x - viewvecY*rightfac; y1 = y + viewvecX*leftfac; y2 = y + viewvecX*rightfac; + if (thing->renderflags2 & RF2_ISOMETRICSPRITES) // If sprites are drawn from an isometric perspective + { + x1 -= viewvecX * thing->radius * M_SQRT2; + x2 -= viewvecX * thing->radius * M_SQRT2; + y1 -= viewvecY * thing->radius * M_SQRT2; + y2 -= viewvecY * thing->radius * M_SQRT2; + } break; } case RF_FLATSPRITE: @@ -1061,6 +1097,7 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t } depth = (float)((x - vp.Pos.X) * vp.TanCos + (y - vp.Pos.Y) * vp.TanSin); + if(thing->renderflags2 & RF2_ISOMETRICSPRITES) depth = depth * vp.PitchCos - vp.PitchSin * z2; // Helps with stacking actors with small xy offsets if (isSpriteShadow) depth += 1.f/65536.f; // always sort shadows behind the sprite. // light calculation diff --git a/src/rendering/hwrenderer/scene/hw_walls.cpp b/src/rendering/hwrenderer/scene/hw_walls.cpp index a2fd3c3b25..316c59c43b 100644 --- a/src/rendering/hwrenderer/scene/hw_walls.cpp +++ b/src/rendering/hwrenderer/scene/hw_walls.cpp @@ -83,10 +83,16 @@ void SetSplitPlanes(FRenderState& state, const secplane_t& top, const secplane_t void HWWall::RenderWall(FRenderState &state, int textured) { + if (seg->sidedef->Flags & WALLF_DITHERTRANS) state.SetEffect(EFF_DITHERTRANS); assert(vertcount > 0); state.SetLightIndex(dynlightindex); state.Draw(DT_TriangleFan, vertindex, vertcount); vertexcount += vertcount; + if (seg->sidedef->Flags & WALLF_DITHERTRANS) + { + state.SetEffect(EFF_NONE); + seg->sidedef->Flags &= ~WALLF_DITHERTRANS; // reset this every frame + } } //========================================================================== @@ -2175,6 +2181,8 @@ void HWWall::Process(HWWallDispatcher *di, seg_t *seg, sector_t * frontsector, s } bool isportal = seg->linedef->isVisualPortal() && seg->sidedef == seg->linedef->sidedef[0]; + // Don't render portal insides if in orthographic mode + isportal &= !(di->di->Viewpoint.camera->ViewPos && (di->di->Viewpoint.camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC)); //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 b78cf955d0..f4dd214d3b 100644 --- a/src/rendering/r_utility.cpp +++ b/src/rendering/r_utility.cpp @@ -66,6 +66,7 @@ #include "i_system.h" #include "v_draw.h" #include "i_interface.h" +#include "d_main.h" // EXTERNAL DATA DECLARATIONS ---------------------------------------------- @@ -103,6 +104,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 +158,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; @@ -554,37 +562,40 @@ 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.camera->ViewPos || !(viewPoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS) || !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; + 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; + } } } } @@ -628,7 +639,7 @@ void R_InterpolateView(FRenderViewpoint& viewPoint, const player_t* const player // Now that we have the current interpolated position, offset from that directly (for view offset + chase cam). if (!posOfs.isZero()) { - const double distance = posOfs.Length(); + const double distance = posOfs.Length(); posOfs /= distance; R_OffsetView(viewPoint, posOfs, distance); } @@ -682,10 +693,20 @@ void FRenderViewpoint::SetViewAngle(const FViewWindow& viewWindow) TanSin = viewWindow.FocalTangent * Sin; TanCos = viewWindow.FocalTangent * Cos; + PitchSin = Angles.Pitch.Sin(); + PitchCos = Angles.Pitch.Cos(); + + floordistfact = M_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 ((camera->ViewPos != NULL) && (camera->ViewPos->Flags & VPSF_ORTHOGRAPHIC) && (camera->ViewPos->Offset.XY().Length() > 0.0)) + ScreenProj = 1.34396 / camera->ViewPos->Offset.Length(); // [DVR] Estimated. +/-1 should be top/bottom of screen. + } //========================================================================== @@ -1019,14 +1040,17 @@ 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 + bool disembodied = false; + if (viewPoint.camera->ViewPos != NULL) disembodied = viewPoint.camera->ViewPos->Flags & VPSF_ALLOWOUTOFBOUNDS; + if (viewPoint.sector->PortalBlocksMovement(sector_t::ceiling) && (!disembodied || !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) && (!disembodied || !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 3013961ebf..ea0f3961c8 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. diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 997ab78b1a..d30ced7454 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -384,6 +384,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(RF2, FLIPSPRITEOFFSETX, AActor, renderflags2), DEFINE_FLAG(RF2, FLIPSPRITEOFFSETY, AActor, renderflags2), DEFINE_FLAG(RF2, CAMFOLLOWSPLAYER, AActor, renderflags2), + DEFINE_FLAG(RF2, ISOMETRICSPRITES, AActor, renderflags2), // Bounce flags DEFINE_FLAG2(BOUNCE_Walls, BOUNCEONWALLS, AActor, BounceFlags), diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 32891e564e..257fcb8a20 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -48,6 +48,23 @@ const int TEXF_Detailmap = 0x20000; const int TEXF_Glowmap = 0x40000; const int TEXF_ClampY = 0x80000; +//=========================================================================== +// +// RGB to HSV +// +//=========================================================================== + +vec3 rgb2hsv(vec3 c) +{ + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + //=========================================================================== // // Color to grayscale @@ -870,6 +887,27 @@ void main() frag.rgb = frag.rgb + uFogColor.rgb; } FragColor = frag; + +#ifdef DITHERTRANS + int index = (int(pixelpos.x) % 8) * 8 + int(pixelpos.y) % 8; + const float DITHER_THRESHOLDS[64] = + { + 1.0 / 65.0, 33.0 / 65.0, 9.0 / 65.0, 41.0 / 65.0, 3.0 / 65.0, 35.0 / 65.0, 11.0 / 65.0, 43.0 / 65.0, + 49.0 / 65.0, 17.0 / 65.0, 57.0 / 65.0, 25.0 / 65.0, 51.0 / 65.0, 19.0 / 65.0, 59.0 / 65.0, 27.0 / 65.0, + 13.0 / 65.0, 45.0 / 65.0, 5.0 / 65.0, 37.0 / 65.0, 15.0 / 65.0, 47.0 / 65.0, 7.0 / 65.0, 39.0 / 65.0, + 61.0 / 65.0, 29.0 / 65.0, 53.0 / 65.0, 21.0 / 65.0, 63.0 / 65.0, 31.0 / 65.0, 55.0 / 65.0, 23.0 / 65.0, + 4.0 / 65.0, 36.0 / 65.0, 12.0 / 65.0, 44.0 / 65.0, 2.0 / 65.0, 34.0 / 65.0, 10.0 / 65.0, 42.0 / 65.0, + 52.0 / 65.0, 20.0 / 65.0, 60.0 / 65.0, 28.0 / 65.0, 50.0 / 65.0, 18.0 / 65.0, 58.0 / 65.0, 26.0 / 65.0, + 16.0 / 65.0, 48.0 / 65.0, 8.0 / 65.0, 40.0 / 65.0, 14.0 / 65.0, 46.0 / 65.0, 6.0 / 65.0, 38.0 / 65.0, + 64.0 / 65.0, 32.0 / 65.0, 56.0 / 65.0, 24.0 / 65.0, 62.0 / 65.0, 30.0 / 65.0, 54.0 / 65.0, 22.0 /65.0 + }; + + vec3 fragHSV = rgb2hsv(FragColor.rgb); + float brightness = clamp(1.5*fragHSV.z, 0.1, 1.0); + if (DITHER_THRESHOLDS[index] < brightness) discard; + else FragColor *= 0.5; +#endif + #ifdef GBUFFER_PASS FragFog = vec4(AmbientOcclusionColor(), 1.0); FragNormal = vec4(vEyeNormal.xyz * 0.5 + 0.5, 1.0); diff --git a/wadsrc/static/shaders_gles/glsl/main.fp b/wadsrc/static/shaders_gles/glsl/main.fp index 99e64ca76e..90429f8e06 100644 --- a/wadsrc/static/shaders_gles/glsl/main.fp +++ b/wadsrc/static/shaders_gles/glsl/main.fp @@ -493,6 +493,11 @@ void main() if (frag.a <= uAlphaThreshold) discard; #endif +#ifdef DITHERTRANS + int index = (int(pixelpos.x) % 2) * 2 + int(pixelpos.y) % 2; + if (index != 2) discard; +#endif + #if (DEF_FOG_2D == 0) // check for special 2D 'fog' mode. { float fogdist = 0.0; diff --git a/wadsrc/static/zscript/actors/shared/camera.zs b/wadsrc/static/zscript/actors/shared/camera.zs index d7826c0517..362ec43056 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.5*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 != (0., 0., 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 92120c36df..ce8c606283 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -422,6 +422,8 @@ enum EViewPosFlags { 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 @@ -1495,4 +1497,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 +};