From 3673338644f9b4cfd372a332869896ac25287fdc Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Mon, 14 Nov 2016 14:19:48 +0100 Subject: [PATCH] Split r_poly into multiple files handling each aspect of rendering a scene --- src/CMakeLists.txt | 8 + src/r_main.cpp | 6 +- src/r_poly.cpp | 1482 +---------------------------------- src/r_poly.h | 159 +--- src/r_poly_cull.cpp | 259 ++++++ src/r_poly_cull.h | 61 ++ src/r_poly_particle.cpp | 33 + src/r_poly_particle.h | 29 + src/r_poly_plane.cpp | 196 +++++ src/r_poly_plane.h | 34 + src/r_poly_playersprite.cpp | 299 +++++++ src/r_poly_playersprite.h | 59 ++ src/r_poly_sky.cpp | 193 +++++ src/r_poly_sky.h | 45 ++ src/r_poly_sprite.cpp | 301 +++++++ src/r_poly_sprite.h | 37 + src/r_poly_wall.cpp | 334 ++++++++ src/r_poly_wall.h | 82 ++ src/r_poly_wallsprite.cpp | 35 + src/r_poly_wallsprite.h | 31 + src/r_swrenderer.cpp | 15 +- 21 files changed, 2107 insertions(+), 1591 deletions(-) create mode 100644 src/r_poly_cull.cpp create mode 100644 src/r_poly_cull.h create mode 100644 src/r_poly_particle.cpp create mode 100644 src/r_poly_particle.h create mode 100644 src/r_poly_plane.cpp create mode 100644 src/r_poly_plane.h create mode 100644 src/r_poly_playersprite.cpp create mode 100644 src/r_poly_playersprite.h create mode 100644 src/r_poly_sky.cpp create mode 100644 src/r_poly_sky.h create mode 100644 src/r_poly_sprite.cpp create mode 100644 src/r_poly_sprite.h create mode 100644 src/r_poly_wall.cpp create mode 100644 src/r_poly_wall.h create mode 100644 src/r_poly_wallsprite.cpp create mode 100644 src/r_poly_wallsprite.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index afe89f568..7933f80a7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1070,6 +1070,14 @@ set( FASTMATH_PCH_SOURCES r_swrenderer.cpp r_swrenderer2.cpp r_poly.cpp + r_poly_cull.cpp + r_poly_particle.cpp + r_poly_plane.cpp + r_poly_playersprite.cpp + r_poly_wall.cpp + r_poly_wallsprite.cpp + r_poly_sprite.cpp + r_poly_sky.cpp r_poly_triangle.cpp r_poly_intersection.cpp r_3dfloors.cpp diff --git a/src/r_main.cpp b/src/r_main.cpp index 149353a87..1ba89eecd 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -59,7 +59,6 @@ #include "v_font.h" #include "r_data/colormaps.h" #include "p_maputl.h" -#include "r_swrenderer2.h" #include "r_poly.h" #include "p_setup.h" #include "version.h" @@ -922,8 +921,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) } else { - static RenderPolyBsp bsp; - bsp.Render(); + RenderPolyScene::Instance()->Render(); } R_3D_ResetClip(); // reset clips (floor/ceiling) camera->renderflags = savedflags; @@ -955,7 +953,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) NetUpdate (); MaskedCycles.Clock(); - if (!r_newrenderer || !r_swtruecolor) + if (!r_newrenderer) R_DrawMasked (); MaskedCycles.Unclock(); diff --git a/src/r_poly.cpp b/src/r_poly.cpp index 94e27cd43..42cc84e5f 100644 --- a/src/r_poly.cpp +++ b/src/r_poly.cpp @@ -1,5 +1,5 @@ /* -** Experimental Doom software renderer +** Polygon Doom software renderer ** Copyright (c) 2016 Magnus Norddahl ** ** This software is provided 'as-is', without any express or implied @@ -26,38 +26,27 @@ #include "sbar.h" #include "r_data/r_translate.h" #include "r_poly.h" -#include "r_draw.h" -#include "r_plane.h" // for yslope -#include "r_sky.h" // for skyflatnum -#include "r_things.h" // for pspritexscale - -EXTERN_CVAR(Bool, r_drawplayersprites) -EXTERN_CVAR(Bool, r_deathcamera) -EXTERN_CVAR(Bool, st_scale) CVAR(Bool, r_debug_cull, 0, 0) ///////////////////////////////////////////////////////////////////////////// -void RenderPolyBsp::Render() +void RenderPolyScene::Render() { if (!r_swtruecolor) // Disable pal rendering for now return; // Setup working buffers PolyVertexBuffer::Clear(); - ClearSolidSegments(); SectorSpriteRanges.clear(); SectorSpriteRanges.resize(numsectors); SortedSprites.clear(); TranslucentObjects.clear(); - PvsSectors.clear(); - ScreenSprites.clear(); PolyStencilBuffer::Instance()->Clear(viewwidth, viewheight, 0); PolySubsectorGBuffer::Instance()->Resize(dc_pitch, viewheight); NextSubsectorDepth = 0; - // Perspective correct: + // Setup perspective matrix: float ratio = WidescreenRatio; float fovratio = (WidescreenRatio >= 1.3f) ? 1.333333f : ratio; float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(FieldOfView.Radians() / 2) / fovratio)).Degrees); @@ -67,56 +56,35 @@ void RenderPolyBsp::Render() TriMatrix::rotate((float)(ViewAngle - 90).Radians(), 0.0f, -1.0f, 0.0f) * TriMatrix::swapYZ() * TriMatrix::translate((float)-ViewPos.X, (float)-ViewPos.Y, (float)-ViewPos.Z); - worldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; + WorldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; - // Y shearing like the Doom renderer: - //worldToClip = TriMatrix::viewToClip() * TriMatrix::worldToView(); - - frustumPlanes = FrustumPlanes(worldToClip); - - // Cull front to back - if (numnodes == 0) - { - PvsSectors.push_back(subsectors); - MaxCeilingHeight = subsectors->sector->ceilingplane.Zat0(); - MinFloorHeight = subsectors->sector->floorplane.Zat0(); - } - else - { - MaxCeilingHeight = 0.0; - MinFloorHeight = 0.0; - RenderNode(nodes + numnodes - 1); // The head node is the last node output. - } - - // Render front to back - ClearSolidSegments(); + Cull.CullScene(WorldToClip); if (r_debug_cull) { - for (auto it = PvsSectors.rbegin(); it != PvsSectors.rend(); ++it) + for (auto it = Cull.PvsSectors.rbegin(); it != Cull.PvsSectors.rend(); ++it) RenderSubsector(*it); } else { - for (auto it = PvsSectors.begin(); it != PvsSectors.end(); ++it) + for (auto it = Cull.PvsSectors.begin(); it != Cull.PvsSectors.end(); ++it) RenderSubsector(*it); } - skydome.Render(worldToClip); + skydome.Render(WorldToClip); RenderTranslucent(); - RenderPlayerSprites(); + PlayerSprites.Render(); DrawerCommandQueue::WaitForWorkers(); - RenderScreenSprites(); // To do: should be called by FSoftwareRenderer::DrawRemainingPlayerSprites instead of here + RenderRemainingPlayerSprites(); // To do: should be called by FSoftwareRenderer::DrawRemainingPlayerSprites instead of here } -void RenderPolyBsp::RenderScreenSprites() +void RenderPolyScene::RenderRemainingPlayerSprites() { - for (auto &sprite : ScreenSprites) - sprite.Render(); + PlayerSprites.RenderRemainingSprites(); } -void RenderPolyBsp::RenderSubsector(subsector_t *sub) +void RenderPolyScene::RenderSubsector(subsector_t *sub) { sector_t *frontsector = sub->sector; frontsector->MoreFlags |= SECF_DRAWN; @@ -125,15 +93,16 @@ void RenderPolyBsp::RenderSubsector(subsector_t *sub) if (sub->sector->CenterFloor() != sub->sector->CenterCeiling()) { - RenderPlane(sub, subsectorDepth, true); - RenderPlane(sub, subsectorDepth, false); + RenderPolyPlane plane; + plane.Render(WorldToClip, sub, subsectorDepth, true, Cull.MaxCeilingHeight); + plane.Render(WorldToClip, sub, subsectorDepth, false, Cull.MinFloorHeight); } for (uint32_t i = 0; i < sub->numlines; i++) { seg_t *line = &sub->firstline[i]; if (line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) - AddLine(line, frontsector, subsectorDepth); + RenderLine(line, frontsector, subsectorDepth); } SpriteRange sprites = GetSpritesForSector(sub->sector); @@ -143,202 +112,11 @@ void RenderPolyBsp::RenderSubsector(subsector_t *sub) TranslucentObjects.push_back({ thing, sub, subsectorDepth }); } - TranslucentObjects.insert(TranslucentObjects.end(), TempTranslucentWalls.begin(), TempTranslucentWalls.end()); - TempTranslucentWalls.clear(); + TranslucentObjects.insert(TranslucentObjects.end(), SubsectorTranslucentWalls.begin(), SubsectorTranslucentWalls.end()); + SubsectorTranslucentWalls.clear(); } -void RenderPolyBsp::RenderTranslucent() -{ - for (auto it = TranslucentObjects.rbegin(); it != TranslucentObjects.rend(); ++it) - { - auto &obj = *it; - if (!obj.thing) - obj.wall.Render(worldToClip); - else if ((obj.thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) - AddWallSprite(obj.thing, obj.sub, obj.subsectorDepth); - else - AddSprite(obj.thing, obj.sub, obj.subsectorDepth); - } -} - -void RenderPolyBsp::RenderPlane(subsector_t *sub, uint32_t subsectorDepth, bool ceiling) -{ - sector_t *frontsector = sub->sector; - - FTextureID picnum = frontsector->GetTexture(ceiling ? sector_t::ceiling : sector_t::floor); - FTexture *tex = TexMan(picnum); - if (tex->UseType == FTexture::TEX_Null) - return; - - bool isSky = picnum == skyflatnum; - double skyHeight = ceiling ? MaxCeilingHeight : MinFloorHeight; - - TriUniforms uniforms; - uniforms.objectToClip = worldToClip; - uniforms.light = (uint32_t)(frontsector->lightlevel / 255.0f * 256.0f); - if (fixedlightlev >= 0) - uniforms.light = (uint32_t)(fixedlightlev / 255.0f * 256.0f); - else if (fixedcolormap) - uniforms.light = 256; - uniforms.flags = 0; - uniforms.subsectorDepth = isSky ? SkySubsectorDepth : subsectorDepth; - - /* - double vis = r_FloorVisibility / (plane.Zat0() - ViewPos.Z); - if (fixedlightlev >= 0) - R_SetDSColorMapLight(sector->ColorMap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap) - R_SetDSColorMapLight(fixedcolormap, 0, 0); - else - R_SetDSColorMapLight(sector->ColorMap, (float)(vis * fabs(CenterY - y)), LIGHT2SHADE(sector->lightlevel)); - */ - - TriVertex *vertices = PolyVertexBuffer::GetVertices(sub->numlines); - if (!vertices) - return; - - if (ceiling) - { - for (uint32_t i = 0; i < sub->numlines; i++) - { - seg_t *line = &sub->firstline[i]; - vertices[sub->numlines - 1 - i] = PlaneVertex(line->v1, frontsector, isSky ? skyHeight : frontsector->ceilingplane.ZatPoint(line->v1)); - } - } - else - { - for (uint32_t i = 0; i < sub->numlines; i++) - { - seg_t *line = &sub->firstline[i]; - vertices[i] = PlaneVertex(line->v1, frontsector, isSky ? skyHeight : frontsector->floorplane.ZatPoint(line->v1)); - } - } - - PolyDrawArgs args; - args.uniforms = uniforms; - args.vinput = vertices; - args.vcount = sub->numlines; - args.mode = TriangleDrawMode::Fan; - args.ccw = true; - args.clipleft = 0; - args.cliptop = 0; - args.clipright = viewwidth; - args.clipbottom = viewheight; - args.stenciltestvalue = 0; - args.stencilwritevalue = 1; - - if (!isSky) - { - args.SetTexture(tex); - PolyTriangleDrawer::draw(args, TriDrawVariant::Draw); - PolyTriangleDrawer::draw(args, TriDrawVariant::Stencil); - } - else - { - args.stencilwritevalue = 255; - PolyTriangleDrawer::draw(args, TriDrawVariant::Stencil); - - for (uint32_t i = 0; i < sub->numlines; i++) - { - TriVertex *wallvert = PolyVertexBuffer::GetVertices(4); - if (!wallvert) - return; - - seg_t *line = &sub->firstline[i]; - - bool closedSky = false; - if (line->backsector) - { - sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector; - - double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1); - double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1); - double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2); - double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2); - - double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1); - double backfloorz1 = backsector->floorplane.ZatPoint(line->v1); - double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2); - double backfloorz2 = backsector->floorplane.ZatPoint(line->v2); - - double topceilz1 = frontceilz1; - double topceilz2 = frontceilz2; - double topfloorz1 = MIN(backceilz1, frontceilz1); - double topfloorz2 = MIN(backceilz2, frontceilz2); - double bottomceilz1 = MAX(frontfloorz1, backfloorz1); - double bottomceilz2 = MAX(frontfloorz2, backfloorz2); - double bottomfloorz1 = frontfloorz1; - double bottomfloorz2 = frontfloorz2; - double middleceilz1 = topfloorz1; - double middleceilz2 = topfloorz2; - double middlefloorz1 = MIN(bottomceilz1, middleceilz1); - double middlefloorz2 = MIN(bottomceilz2, middleceilz2); - - bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum; - bool bothSkyFloor = frontsector->GetTexture(sector_t::floor) == skyflatnum && backsector->GetTexture(sector_t::floor) == skyflatnum; - - bool closedSector = backceilz1 == backfloorz1 && backceilz2 == backfloorz2; - closedSky = (ceiling && bothSkyCeiling && closedSector) || (!ceiling && bothSkyFloor && closedSector); - if (!closedSky) - { - bool topwall = (topceilz1 > topfloorz1 || topceilz2 > topfloorz2) && line->sidedef && !bothSkyCeiling; - bool bottomwall = (bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && line->sidedef && !bothSkyFloor; - if ((ceiling && !topwall) || (!ceiling && !bottomwall)) - continue; - } - } - - if (ceiling) - { - wallvert[0] = PlaneVertex(line->v1, frontsector, skyHeight); - wallvert[1] = PlaneVertex(line->v2, frontsector, skyHeight); - if (!closedSky) - { - wallvert[2] = PlaneVertex(line->v2, frontsector, frontsector->ceilingplane.ZatPoint(line->v2)); - wallvert[3] = PlaneVertex(line->v1, frontsector, frontsector->ceilingplane.ZatPoint(line->v1)); - } - else - { - wallvert[2] = PlaneVertex(line->v2, frontsector, frontsector->floorplane.ZatPoint(line->v2)); - wallvert[3] = PlaneVertex(line->v1, frontsector, frontsector->floorplane.ZatPoint(line->v1)); - } - } - else - { - if (!closedSky) - { - wallvert[0] = PlaneVertex(line->v1, frontsector, frontsector->floorplane.ZatPoint(line->v1)); - wallvert[1] = PlaneVertex(line->v2, frontsector, frontsector->floorplane.ZatPoint(line->v2)); - } - else - { - wallvert[0] = PlaneVertex(line->v1, frontsector, frontsector->ceilingplane.ZatPoint(line->v1)); - wallvert[1] = PlaneVertex(line->v2, frontsector, frontsector->ceilingplane.ZatPoint(line->v2)); - } - wallvert[2] = PlaneVertex(line->v2, frontsector, skyHeight); - wallvert[3] = PlaneVertex(line->v1, frontsector, skyHeight); - } - - args.vinput = wallvert; - args.vcount = 4; - PolyTriangleDrawer::draw(args, TriDrawVariant::Stencil); - } - } -} - -TriVertex RenderPolyBsp::PlaneVertex(vertex_t *v1, sector_t *sector, double height) -{ - TriVertex v; - v.x = (float)v1->fPos().X; - v.y = (float)v1->fPos().Y; - v.z = (float)height; - v.w = 1.0f; - v.varying[0] = v.x / 64.0f; - v.varying[1] = 1.0f - v.y / 64.0f; - return v; -} - -SpriteRange RenderPolyBsp::GetSpritesForSector(sector_t *sector) +SpriteRange RenderPolyScene::GetSpritesForSector(sector_t *sector) { if (SectorSpriteRanges.size() < sector->sectornum || sector->sectornum < 0) return SpriteRange(); @@ -358,7 +136,7 @@ SpriteRange RenderPolyBsp::GetSpritesForSector(sector_t *sector) return range; } -void RenderPolyBsp::AddLine(seg_t *line, sector_t *frontsector, uint32_t subsectorDepth) +void RenderPolyScene::RenderLine(seg_t *line, sector_t *frontsector, uint32_t subsectorDepth) { // Reject lines not facing viewer DVector2 pt1 = line->v1->fPos() - ViewPos; @@ -368,1063 +146,44 @@ void RenderPolyBsp::AddLine(seg_t *line, sector_t *frontsector, uint32_t subsect // Cull wall if not visible int sx1, sx2; - bool hasSegmentRange = GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2); - if (hasSegmentRange && IsSegmentCulled(sx1, sx2)) + bool hasSegmentRange = Cull.GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2); + if (hasSegmentRange && Cull.IsSegmentCulled(sx1, sx2)) return; - double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1); - double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1); - double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2); - double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2); - - RenderPolyWall wall; - wall.Line = line; - wall.Colormap = frontsector->ColorMap; - wall.Masked = false; - wall.SubsectorDepth = subsectorDepth; - - if (line->backsector == nullptr) + // Render wall, and update culling info if its an occlusion blocker + if (RenderPolyWall::RenderLine(WorldToClip, line, frontsector, subsectorDepth, SubsectorTranslucentWalls)) { - if (line->sidedef) - { - wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2); - wall.TopZ = frontceilz1; - wall.BottomZ = frontfloorz1; - wall.UnpeggedCeil = frontceilz1; - wall.Texpart = side_t::mid; - wall.Render(worldToClip); - if (hasSegmentRange) - MarkSegmentCulled(sx1, sx2); - } - } - else - { - sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector; - - double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1); - double backfloorz1 = backsector->floorplane.ZatPoint(line->v1); - double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2); - double backfloorz2 = backsector->floorplane.ZatPoint(line->v2); - - double topceilz1 = frontceilz1; - double topceilz2 = frontceilz2; - double topfloorz1 = MIN(backceilz1, frontceilz1); - double topfloorz2 = MIN(backceilz2, frontceilz2); - double bottomceilz1 = MAX(frontfloorz1, backfloorz1); - double bottomceilz2 = MAX(frontfloorz2, backfloorz2); - double bottomfloorz1 = frontfloorz1; - double bottomfloorz2 = frontfloorz2; - double middleceilz1 = topfloorz1; - double middleceilz2 = topfloorz2; - double middlefloorz1 = MIN(bottomceilz1, middleceilz1); - double middlefloorz2 = MIN(bottomceilz2, middleceilz2); - - bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum; - bool bothSkyFloor = frontsector->GetTexture(sector_t::floor) == skyflatnum && backsector->GetTexture(sector_t::floor) == skyflatnum; - - if ((topceilz1 > topfloorz1 || topceilz2 > topfloorz2) && line->sidedef && !bothSkyCeiling) - { - wall.SetCoords(line->v1->fPos(), line->v2->fPos(), topceilz1, topfloorz1, topceilz2, topfloorz2); - wall.TopZ = topceilz1; - wall.BottomZ = topfloorz1; - wall.UnpeggedCeil = topceilz1; - wall.Texpart = side_t::top; - wall.Render(worldToClip); - } - - if ((bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && line->sidedef && !bothSkyFloor) - { - wall.SetCoords(line->v1->fPos(), line->v2->fPos(), bottomceilz1, bottomfloorz2, bottomceilz2, bottomfloorz2); - wall.TopZ = bottomceilz1; - wall.BottomZ = bottomfloorz2; - wall.UnpeggedCeil = topceilz1; - wall.Texpart = side_t::bottom; - wall.Render(worldToClip); - } - - if (line->sidedef) - { - FTexture *midtex = TexMan(line->sidedef->GetTexture(side_t::mid), true); - if (midtex && midtex->UseType != FTexture::TEX_Null) - { - wall.SetCoords(line->v1->fPos(), line->v2->fPos(), middleceilz1, middlefloorz1, middleceilz2, middlefloorz2); - wall.TopZ = middleceilz1; - wall.BottomZ = middlefloorz1; - wall.UnpeggedCeil = topceilz1; - wall.Texpart = side_t::mid; - wall.Masked = true; - TempTranslucentWalls.push_back({ wall }); - } - } + if (hasSegmentRange) + Cull.MarkSegmentCulled(sx1, sx2); } } -bool RenderPolyBsp::IsThingCulled(AActor *thing) +void RenderPolyScene::RenderTranslucent() { - FIntCVar *cvar = thing->GetClass()->distancecheck; - if (cvar != nullptr && *cvar >= 0) + for (auto it = TranslucentObjects.rbegin(); it != TranslucentObjects.rend(); ++it) { - double dist = (thing->Pos() - ViewPos).LengthSquared(); - double check = (double)**cvar; - if (dist >= check * check) - return true; - } - - // Don't waste time projecting sprites that are definitely not visible. - if (thing == nullptr || - (thing->renderflags & RF_INVISIBLE) || - !thing->RenderStyle.IsVisible(thing->Alpha) || - !thing->IsVisibleToPlayer()) - { - return true; - } - - return false; -} - -void RenderPolyBsp::AddSprite(AActor *thing, subsector_t *sub, uint32_t subsectorDepth) -{ - if (IsThingCulled(thing)) - return; - - DVector3 pos = thing->InterpolatedPosition(r_TicFracF); - pos.Z += thing->GetBobOffset(r_TicFracF); - - bool flipTextureX = false; - FTexture *tex = GetSpriteTexture(thing, flipTextureX); - if (tex == nullptr) - return; - DVector2 spriteScale = thing->Scale; - double thingxscalemul = spriteScale.X / tex->Scale.X; - double thingyscalemul = spriteScale.Y / tex->Scale.Y; - - if (flipTextureX) - pos.X -= (tex->GetWidth() - tex->LeftOffset) * thingxscalemul; - else - pos.X -= tex->LeftOffset * thingxscalemul; - - //pos.Z -= tex->TopOffset * thingyscalemul; - pos.Z -= (tex->GetHeight() - tex->TopOffset) * thingyscalemul + thing->Floorclip; - - double spriteHalfWidth = thingxscalemul * tex->GetWidth() * 0.5; - double spriteHeight = thingyscalemul * tex->GetHeight(); - - pos.X += spriteHalfWidth; - - DVector2 points[2] = - { - { pos.X - ViewSin * spriteHalfWidth, pos.Y + ViewCos * spriteHalfWidth }, - { pos.X + ViewSin * spriteHalfWidth, pos.Y - ViewCos * spriteHalfWidth } - }; - - // Is this sprite inside? (To do: clip the points) - for (int i = 0; i < 2; i++) - { - for (uint32_t i = 0; i < sub->numlines; i++) + auto &obj = *it; + if (!obj.thing) { - seg_t *line = &sub->firstline[i]; - double nx = line->v1->fY() - line->v2->fY(); - double ny = line->v2->fX() - line->v1->fX(); - double d = -(line->v1->fX() * nx + line->v1->fY() * ny); - if (pos.X * nx + pos.Y * ny + d > 0.0) - return; + obj.wall.Render(WorldToClip); } - } - - //double depth = 1.0; - //visstyle_t visstyle = GetSpriteVisStyle(thing, depth); - // Rumor has it that AlterWeaponSprite needs to be called with visstyle passed in somewhere around here.. - //R_SetColorMapLight(visstyle.BaseColormap, 0, visstyle.ColormapNum << FRACBITS); - - TriVertex *vertices = PolyVertexBuffer::GetVertices(4); - if (!vertices) - return; - - bool foggy = false; - int actualextralight = foggy ? 0 : extralight << 4; - - std::pair offsets[4] = - { - { 0.0f, 1.0f }, - { 1.0f, 1.0f }, - { 1.0f, 0.0f }, - { 0.0f, 0.0f }, - }; - - for (int i = 0; i < 4; i++) - { - auto &p = (i == 0 || i == 3) ? points[0] : points[1]; - - vertices[i].x = (float)p.X; - vertices[i].y = (float)p.Y; - vertices[i].z = (float)(pos.Z + spriteHeight * offsets[i].second); - vertices[i].w = 1.0f; - vertices[i].varying[0] = (float)(offsets[i].first * tex->Scale.X); - vertices[i].varying[1] = (float)((1.0f - offsets[i].second) * tex->Scale.Y); - if (flipTextureX) - vertices[i].varying[0] = 1.0f - vertices[i].varying[0]; - } - - TriUniforms uniforms; - uniforms.objectToClip = worldToClip; - uniforms.light = (uint32_t)((thing->Sector->lightlevel + actualextralight) / 255.0f * 256.0f); - uniforms.flags = 0; - uniforms.subsectorDepth = subsectorDepth; - - PolyDrawArgs args; - args.uniforms = uniforms; - args.vinput = vertices; - args.vcount = 4; - args.mode = TriangleDrawMode::Fan; - args.ccw = true; - args.clipleft = 0; - args.cliptop = 0; - args.clipright = viewwidth; - args.clipbottom = viewheight; - args.stenciltestvalue = 0; - args.stencilwritevalue = 1; - args.SetTexture(tex); - PolyTriangleDrawer::draw(args, TriDrawVariant::DrawSubsector); -} - -void RenderPolyBsp::AddWallSprite(AActor *thing, subsector_t *sub, uint32_t subsectorDepth) -{ - if (IsThingCulled(thing)) - return; -} - -visstyle_t RenderPolyBsp::GetSpriteVisStyle(AActor *thing, double z) -{ - visstyle_t visstyle; - - bool foggy = false; - int actualextralight = foggy ? 0 : extralight << 4; - int spriteshade = LIGHT2SHADE(thing->Sector->lightlevel + actualextralight); - - visstyle.RenderStyle = thing->RenderStyle; - visstyle.Alpha = float(thing->Alpha); - visstyle.ColormapNum = 0; - - // The software renderer cannot invert the source without inverting the overlay - // too. That means if the source is inverted, we need to do the reverse of what - // the invert overlay flag says to do. - bool invertcolormap = (visstyle.RenderStyle.Flags & STYLEF_InvertOverlay) != 0; - - if (visstyle.RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - FDynamicColormap *mybasecolormap = thing->Sector->ColorMap; - - // Sprites that are added to the scene must fade to black. - if (visstyle.RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); - } - - if (visstyle.RenderStyle.Flags & STYLEF_FadeToBlack) - { - if (invertcolormap) - { // Fade to white - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255, 255, 255), mybasecolormap->Desaturate); - invertcolormap = false; - } - else - { // Fade to black - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0, 0, 0), mybasecolormap->Desaturate); - } - } - - // get light level - if (fixedcolormap != nullptr) - { // fixed map - visstyle.BaseColormap = fixedcolormap; - visstyle.ColormapNum = 0; - } - else - { - if (invertcolormap) + else if ((obj.thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - visstyle.BaseColormap = mybasecolormap; - visstyle.ColormapNum = fixedlightlev >> COLORMAPSHIFT; - } - else if (!foggy && ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT))) - { // full bright - visstyle.BaseColormap = mybasecolormap; - visstyle.ColormapNum = 0; - } - else - { // diminished light - double minz = double((2048 * 4) / double(1 << 20)); - visstyle.ColormapNum = GETPALOOKUP(r_SpriteVisibility / MAX(z, minz), spriteshade); - visstyle.BaseColormap = mybasecolormap; - } - } - - return visstyle; -} - -FTexture *RenderPolyBsp::GetSpriteTexture(AActor *thing, /*out*/ bool &flipX) -{ - flipX = false; - if (thing->picnum.isValid()) - { - FTexture *tex = TexMan(thing->picnum); - if (tex->UseType == FTexture::TEX_Null) - { - return nullptr; - } - - if (tex->Rotations != 0xFFFF) - { - // choose a different rotation based on player view - spriteframe_t *sprframe = &SpriteFrames[tex->Rotations]; - DVector3 pos = thing->InterpolatedPosition(r_TicFracF); - pos.Z += thing->GetBobOffset(r_TicFracF); - DAngle ang = (pos - ViewPos).Angle(); - angle_t rot; - if (sprframe->Texture[0] == sprframe->Texture[1]) - { - rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28; - } - else - { - rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - } - flipX = (sprframe->Flip & (1 << rot)) != 0; - tex = TexMan[sprframe->Texture[rot]]; // Do not animate the rotation - } - return tex; - } - else - { - // decide which texture to use for the sprite - int spritenum = thing->sprite; - if (spritenum >= (signed)sprites.Size() || spritenum < 0) - return nullptr; - - spritedef_t *sprdef = &sprites[spritenum]; - if (thing->frame >= sprdef->numframes) - { - // If there are no frames at all for this sprite, don't draw it. - return nullptr; + RenderPolyWallSprite wallspr; + wallspr.Render(obj.thing, obj.sub, obj.subsectorDepth); } else { - //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; - // choose a different rotation based on player view - spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; - DVector3 pos = thing->InterpolatedPosition(r_TicFracF); - pos.Z += thing->GetBobOffset(r_TicFracF); - DAngle ang = (pos - ViewPos).Angle(); - angle_t rot; - if (sprframe->Texture[0] == sprframe->Texture[1]) - { - rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28; - } - else - { - rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - } - flipX = (sprframe->Flip & (1 << rot)) != 0; - return TexMan[sprframe->Texture[rot]]; // Do not animate the rotation + RenderPolySprite spr; + spr.Render(WorldToClip, obj.thing, obj.sub, obj.subsectorDepth); } } } -void RenderPolyBsp::RenderNode(void *node) +RenderPolyScene *RenderPolyScene::Instance() { - while (!((size_t)node & 1)) // Keep going until found a subsector - { - node_t *bsp = (node_t *)node; - - // Decide which side the view point is on. - int side = PointOnSide(ViewPos, bsp); - - // Recursively divide front space (toward the viewer). - RenderNode(bsp->children[side]); - - // Possibly divide back space (away from the viewer). - side ^= 1; - if (!CheckBBox(bsp->bbox[side])) - return; - - node = bsp->children[side]; - } - - // Mark that we need to render this - subsector_t *sub = (subsector_t *)((BYTE *)node - 1); - MaxCeilingHeight = MAX(MaxCeilingHeight, sub->sector->ceilingplane.Zat0()); - MinFloorHeight = MIN(MinFloorHeight, sub->sector->floorplane.Zat0()); - PvsSectors.push_back(sub); - - // Update culling info for further bsp clipping - for (uint32_t i = 0; i < sub->numlines; i++) - { - seg_t *line = &sub->firstline[i]; - if ((line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) && line->backsector == nullptr) - { - int sx1, sx2; - if (GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2)) - { - MarkSegmentCulled(sx1, sx2); - } - } - } -} - -void RenderPolyBsp::RenderPlayerSprites() -{ - if (!r_drawplayersprites || - !camera || - !camera->player || - (players[consoleplayer].cheats & CF_CHASECAM) || - (r_deathcamera && camera->health <= 0)) - return; - - float bobx, boby; - P_BobWeapon(camera->player, &bobx, &boby, r_TicFracF); - - // Interpolate the main weapon layer once so as to be able to add it to other layers. - double wx, wy; - DPSprite *weapon = camera->player->FindPSprite(PSP_WEAPON); - if (weapon) - { - if (weapon->firstTic) - { - wx = weapon->x; - wy = weapon->y; - } - else - { - wx = weapon->oldx + (weapon->x - weapon->oldx) * r_TicFracF; - wy = weapon->oldy + (weapon->y - weapon->oldy) * r_TicFracF; - } - } - else - { - wx = 0; - wy = 0; - } - - for (DPSprite *sprite = camera->player->psprites; sprite != nullptr; sprite = sprite->GetNext()) - { - // [RH] Don't draw the targeter's crosshair if the player already has a crosshair set. - // It's possible this psprite's caller is now null but the layer itself hasn't been destroyed - // because it didn't tick yet (if we typed 'take all' while in the console for example). - // In this case let's simply not draw it to avoid crashing. - if ((sprite->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && sprite->GetCaller() != nullptr) - { - RenderPlayerSprite(sprite, camera, bobx, boby, wx, wy, r_TicFracF); - } - } -} - -void RenderPolyBsp::RenderPlayerSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac) -{ - // decide which patch to use - if ((unsigned)sprite->GetSprite() >= (unsigned)sprites.Size()) - { - DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite number %i\n", sprite->GetSprite()); - return; - } - - spritedef_t *def = &sprites[sprite->GetSprite()]; - if (sprite->GetFrame() >= def->numframes) - { - DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite frame %i : %i\n", sprite->GetSprite(), sprite->GetFrame()); - return; - } - - spriteframe_t *frame = &SpriteFrames[def->spriteframes + sprite->GetFrame()]; - FTextureID picnum = frame->Texture[0]; - bool flip = (frame->Flip & 1) != 0; - - FTexture *tex = TexMan(picnum); - if (tex->UseType == FTexture::TEX_Null) - return; - - // Can't interpolate the first tic. - if (sprite->firstTic) - { - sprite->firstTic = false; - sprite->oldx = sprite->x; - sprite->oldy = sprite->y; - } - - double sx = sprite->oldx + (sprite->x - sprite->oldx) * ticfrac; - double sy = sprite->oldy + (sprite->y - sprite->oldy) * ticfrac; - - if (sprite->Flags & PSPF_ADDBOB) - { - sx += bobx; - sy += boby; - } - - if (sprite->Flags & PSPF_ADDWEAPON && sprite->GetID() != PSP_WEAPON) - { - sx += wx; - sy += wy; - } - - // calculate edges of the shape - double tx = sx - BaseXCenter; - - tx -= tex->GetScaledLeftOffset(); - int x1 = xs_RoundToInt(CenterX + tx * pspritexscale); - - // off the right side - if (x1 > viewwidth) - return; - - tx += tex->GetScaledWidth(); - int x2 = xs_RoundToInt(CenterX + tx * pspritexscale); - - // off the left side - if (x2 <= 0) - return; - - double texturemid = (BaseYCenter - sy) * tex->Scale.Y + tex->TopOffset; - - // Adjust PSprite for fullscreen views - if (camera->player && (RenderTarget != screen || viewheight == RenderTarget->GetHeight() || (RenderTarget->GetWidth() > (BaseXCenter * 2) && !st_scale))) - { - AWeapon *weapon = dyn_cast(sprite->GetCaller()); - if (weapon != nullptr && weapon->YAdjust != 0) - { - if (RenderTarget != screen || viewheight == RenderTarget->GetHeight()) - { - texturemid -= weapon->YAdjust; - } - else - { - texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust; - } - } - } - - // Move the weapon down for 1280x1024. - if (sprite->GetID() < PSP_TARGETCENTER) - { - texturemid -= AspectPspriteOffset(WidescreenRatio); - } - - int clipped_x1 = MAX(x1, 0); - int clipped_x2 = MIN(x2, viewwidth); - double xscale = pspritexscale / tex->Scale.X; - double yscale = pspriteyscale / tex->Scale.Y; - uint32_t translation = 0; // [RH] Use default colors - - double xiscale, startfrac; - if (flip) - { - xiscale = -pspritexiscale * tex->Scale.X; - startfrac = 1; - } - else - { - xiscale = pspritexiscale * tex->Scale.X; - startfrac = 0; - } - - if (clipped_x1 > x1) - startfrac += xiscale * (clipped_x1 - x1); - - bool noaccel = false; - - FDynamicColormap *basecolormap = viewsector->ColorMap; - FDynamicColormap *colormap_to_use = basecolormap; - - visstyle_t visstyle; - visstyle.ColormapNum = 0; - visstyle.BaseColormap = basecolormap; - visstyle.Alpha = 0; - visstyle.RenderStyle = STYLE_Normal; - - bool foggy = false; - int actualextralight = foggy ? 0 : extralight << 4; - int spriteshade = LIGHT2SHADE(owner->Sector->lightlevel + actualextralight); - double minz = double((2048 * 4) / double(1 << 20)); - visstyle.ColormapNum = GETPALOOKUP(r_SpriteVisibility / minz, spriteshade); - - if (sprite->GetID() < PSP_TARGETCENTER) - { - // Lots of complicated style and noaccel stuff - } - - // Check for hardware-assisted 2D. If it's available, and this sprite is not - // fuzzy, don't draw it until after the switch to 2D mode. - if (!noaccel && RenderTarget == screen && (DFrameBuffer *)screen->Accel2D) - { - FRenderStyle style = visstyle.RenderStyle; - style.CheckFuzz(); - if (style.BlendOp != STYLEOP_Fuzz) - { - PolyScreenSprite screenSprite; - screenSprite.Pic = tex; - screenSprite.X1 = viewwindowx + x1; - screenSprite.Y1 = viewwindowy + viewheight / 2 - texturemid * yscale - 0.5; - screenSprite.Width = tex->GetWidth() * xscale; - screenSprite.Height = tex->GetHeight() * yscale; - screenSprite.Translation = TranslationToTable(translation); - screenSprite.Flip = xiscale < 0; - screenSprite.visstyle = visstyle; - screenSprite.Colormap = colormap_to_use; - ScreenSprites.push_back(screenSprite); - return; - } - } - - //R_DrawVisSprite(vis); -} - -void RenderPolyBsp::ClearSolidSegments() -{ - SolidSegments.clear(); - SolidSegments.reserve(SolidCullScale + 2); - SolidSegments.push_back({ -0x7fff, -SolidCullScale }); - SolidSegments.push_back({ SolidCullScale , 0x7fff }); -} - -bool RenderPolyBsp::IsSegmentCulled(int x1, int x2) const -{ - int next = 0; - while (SolidSegments[next].X2 <= x2) - next++; - return (x1 >= SolidSegments[next].X1 && x2 <= SolidSegments[next].X2); -} - -void RenderPolyBsp::MarkSegmentCulled(int x1, int x2) -{ - if (x1 >= x2) - return; - - int cur = 1; - while (true) - { - if (SolidSegments[cur].X1 <= x1 && SolidSegments[cur].X2 >= x2) // Already fully marked - { - break; - } - else if (cur + 1 != SolidSegments.size() && SolidSegments[cur].X2 >= x1 && SolidSegments[cur].X1 <= x2) // Merge segments - { - // Find last segment - int merge = cur; - while (merge + 2 != SolidSegments.size() && SolidSegments[merge + 1].X1 <= x2) - merge++; - - // Apply new merged range - SolidSegments[cur].X1 = MIN(SolidSegments[cur].X1, x1); - SolidSegments[cur].X2 = MAX(SolidSegments[merge].X2, x2); - - // Remove additional segments we merged with - if (merge > cur) - SolidSegments.erase(SolidSegments.begin() + (cur + 1), SolidSegments.begin() + (merge + 1)); - - break; - } - else if (SolidSegments[cur].X1 > x1) // Insert new segment - { - SolidSegments.insert(SolidSegments.begin() + cur, { x1, x2 }); - break; - } - cur++; - } -} - -int RenderPolyBsp::PointOnSide(const DVector2 &pos, const node_t *node) -{ - return DMulScale32(FLOAT2FIXED(pos.Y) - node->y, node->dx, node->x - FLOAT2FIXED(pos.X), node->dy) > 0; -} - -bool RenderPolyBsp::CheckBBox(float *bspcoord) -{ - // Start using a quick frustum AABB test: - - AxisAlignedBoundingBox aabb(Vec3f(bspcoord[BOXLEFT], bspcoord[BOXBOTTOM], (float)ViewPos.Z - 1000.0f), Vec3f(bspcoord[BOXRIGHT], bspcoord[BOXTOP], (float)ViewPos.Z + 1000.0f)); - auto result = IntersectionTest::frustum_aabb(frustumPlanes, aabb); - if (result == IntersectionTest::outside) - return false; - - // Occlusion test using solid segments: - - int boxx; - int boxy; - int boxpos; - - double x1, y1, x2, y2; - - // Find the corners of the box - // that define the edges from current viewpoint. - if (ViewPos.X <= bspcoord[BOXLEFT]) - boxx = 0; - else if (ViewPos.X < bspcoord[BOXRIGHT]) - boxx = 1; - else - boxx = 2; - - if (ViewPos.Y >= bspcoord[BOXTOP]) - boxy = 0; - else if (ViewPos.Y > bspcoord[BOXBOTTOM]) - boxy = 1; - else - boxy = 2; - - boxpos = (boxy << 2) + boxx; - if (boxpos == 5) - return true; - - static const int checkcoord[12][4] = - { - { 3,0,2,1 }, - { 3,0,2,0 }, - { 3,1,2,0 }, - { 0 }, - { 2,0,2,1 }, - { 0,0,0,0 }, - { 3,1,3,0 }, - { 0 }, - { 2,0,3,1 }, - { 2,1,3,1 }, - { 2,1,3,0 } - }; - - x1 = bspcoord[checkcoord[boxpos][0]]; - y1 = bspcoord[checkcoord[boxpos][1]]; - x2 = bspcoord[checkcoord[boxpos][2]]; - y2 = bspcoord[checkcoord[boxpos][3]]; - - int sx1, sx2; - if (GetSegmentRangeForLine(x1, y1, x2, y2, sx1, sx2)) - return !IsSegmentCulled(sx1, sx2); - else - return true; -} - -bool RenderPolyBsp::GetSegmentRangeForLine(double x1, double y1, double x2, double y2, int &sx1, int &sx2) const -{ - double znear = 5.0; - - // Transform to 2D view space: - x1 = x1 - ViewPos.X; - y1 = y1 - ViewPos.Y; - x2 = x2 - ViewPos.X; - y2 = y2 - ViewPos.Y; - double rx1 = x1 * ViewSin - y1 * ViewCos; - double rx2 = x2 * ViewSin - y2 * ViewCos; - double ry1 = x1 * ViewCos + y1 * ViewSin; - double ry2 = x2 * ViewCos + y2 * ViewSin; - - // Cull if line is entirely behind view - if (ry1 < znear && ry2 < znear) return false; - - // Clip line, if needed - double t1 = 0.0f, t2 = 1.0f; - if (ry1 < znear) - t1 = clamp((znear - ry1) / (ry2 - ry1), 0.0, 1.0); - if (ry2 < znear) - t2 = clamp((znear - ry1) / (ry2 - ry1), 0.0, 1.0); - if (t1 != 0.0 || t2 != 1.0) - { - double nx1 = rx1 * (1.0 - t1) + rx2 * t1; - double ny1 = ry1 * (1.0 - t1) + ry2 * t1; - double nx2 = rx1 * (1.0 - t2) + rx2 * t2; - double ny2 = ry1 * (1.0 - t2) + ry2 * t2; - rx1 = nx1; - rx2 = nx2; - ry1 = ny1; - ry2 = ny2; - } - - sx1 = (int)floor(clamp(rx1 / ry1 * (SolidCullScale / 3), (double)-SolidCullScale, (double)SolidCullScale)); - sx2 = (int)floor(clamp(rx2 / ry2 * (SolidCullScale / 3), (double)-SolidCullScale, (double)SolidCullScale)); - - if (sx1 > sx2) - std::swap(sx1, sx2); - return sx1 != sx2; -} - -///////////////////////////////////////////////////////////////////////////// - -void RenderPolyWall::Render(const TriMatrix &worldToClip) -{ - FTexture *tex = GetTexture(); - if (!tex) - return; - - PolyWallTextureCoords texcoords(tex, Line, Texpart, TopZ, BottomZ, UnpeggedCeil); - - TriVertex *vertices = PolyVertexBuffer::GetVertices(4); - if (!vertices) - return; - - vertices[0].x = (float)v1.X; - vertices[0].y = (float)v1.Y; - vertices[0].z = (float)ceil1; - vertices[0].w = 1.0f; - vertices[0].varying[0] = (float)texcoords.u1; - vertices[0].varying[1] = (float)texcoords.v1; - - vertices[1].x = (float)v2.X; - vertices[1].y = (float)v2.Y; - vertices[1].z = (float)ceil2; - vertices[1].w = 1.0f; - vertices[1].varying[0] = (float)texcoords.u2; - vertices[1].varying[1] = (float)texcoords.v1; - - vertices[2].x = (float)v2.X; - vertices[2].y = (float)v2.Y; - vertices[2].z = (float)floor2; - vertices[2].w = 1.0f; - vertices[2].varying[0] = (float)texcoords.u2; - vertices[2].varying[1] = (float)texcoords.v2; - - vertices[3].x = (float)v1.X; - vertices[3].y = (float)v1.Y; - vertices[3].z = (float)floor1; - vertices[3].w = 1.0f; - vertices[3].varying[0] = (float)texcoords.u1; - vertices[3].varying[1] = (float)texcoords.v2; - - TriUniforms uniforms; - uniforms.objectToClip = worldToClip; - uniforms.light = (uint32_t)(GetLightLevel() / 255.0f * 256.0f); - uniforms.flags = 0; - uniforms.subsectorDepth = SubsectorDepth; - - PolyDrawArgs args; - args.uniforms = uniforms; - args.vinput = vertices; - args.vcount = 4; - args.mode = TriangleDrawMode::Fan; - args.ccw = true; - args.clipleft = 0; - args.cliptop = 0; - args.clipright = viewwidth; - args.clipbottom = viewheight; - args.stenciltestvalue = 0; - args.stencilwritevalue = 1; - args.SetTexture(tex); - - if (!Masked) - { - PolyTriangleDrawer::draw(args, TriDrawVariant::Draw); - PolyTriangleDrawer::draw(args, TriDrawVariant::Stencil); - } - else - { - PolyTriangleDrawer::draw(args, TriDrawVariant::DrawSubsector); - } -} - -FTexture *RenderPolyWall::GetTexture() -{ - FTexture *tex = TexMan(Line->sidedef->GetTexture(Texpart), true); - if (tex == nullptr || tex->UseType == FTexture::TEX_Null) - return nullptr; - else - return tex; -} - -int RenderPolyWall::GetLightLevel() -{ - if (fixedlightlev >= 0 || fixedcolormap) - { - return 255; - } - else - { - bool foggy = false; - int actualextralight = foggy ? 0 : extralight << 4; - return Line->sidedef->GetLightLevel(foggy, Line->frontsector->lightlevel) + actualextralight; - } -} - -/* -float RenderPolyWall::GetLight(short x) -{ - if (fixedlightlev >= 0 || fixedcolormap) - return 0.0f; - else - return (float)(r_WallVisibility / Coords.Z(x)); -} -*/ - -///////////////////////////////////////////////////////////////////////////// - -PolyWallTextureCoords::PolyWallTextureCoords(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil) -{ - CalcU(tex, line, texpart); - CalcV(tex, line, texpart, topz, bottomz, unpeggedceil); -} - -void PolyWallTextureCoords::CalcU(FTexture *tex, const seg_t *line, side_t::ETexpart texpart) -{ - double lineLength = line->sidedef->TexelLength; - double lineStart = 0.0; - - bool entireSegment = ((line->linedef->v1 == line->v1) && (line->linedef->v2 == line->v2) || (line->linedef->v2 == line->v1) && (line->linedef->v1 == line->v2)); - if (!entireSegment) - { - lineLength = (line->v2->fPos() - line->v1->fPos()).Length(); - lineStart = (line->v1->fPos() - line->linedef->v1->fPos()).Length(); - } - - int texWidth = tex->GetWidth(); - double uscale = line->sidedef->GetTextureXScale(texpart) * tex->Scale.X; - u1 = lineStart + line->sidedef->GetTextureXOffset(texpart); - u2 = u1 + lineLength; - u1 *= uscale; - u2 *= uscale; - u1 /= texWidth; - u2 /= texWidth; -} - -void PolyWallTextureCoords::CalcV(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil) -{ - double vscale = line->sidedef->GetTextureYScale(texpart) * tex->Scale.Y; - - double yoffset = line->sidedef->GetTextureYOffset(texpart); - if (tex->bWorldPanning) - yoffset *= vscale; - - switch (texpart) - { - default: - case side_t::mid: - CalcVMidPart(tex, line, topz, bottomz, vscale, yoffset); - break; - case side_t::top: - CalcVTopPart(tex, line, topz, bottomz, vscale, yoffset); - break; - case side_t::bottom: - CalcVBottomPart(tex, line, topz, bottomz, unpeggedceil, vscale, yoffset); - break; - } - - int texHeight = tex->GetHeight(); - v1 /= texHeight; - v2 /= texHeight; -} - -void PolyWallTextureCoords::CalcVTopPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset) -{ - bool pegged = (line->linedef->flags & ML_DONTPEGTOP) == 0; - if (pegged) // bottom to top - { - int texHeight = tex->GetHeight(); - v1 = -yoffset; - v2 = v1 + (topz - bottomz); - v1 *= vscale; - v2 *= vscale; - v1 = texHeight - v1; - v2 = texHeight - v2; - std::swap(v1, v2); - } - else // top to bottom - { - v1 = yoffset; - v2 = v1 + (topz - bottomz); - v1 *= vscale; - v2 *= vscale; - } -} - -void PolyWallTextureCoords::CalcVMidPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset) -{ - bool pegged = (line->linedef->flags & ML_DONTPEGBOTTOM) == 0; - if (pegged) // top to bottom - { - v1 = yoffset; - v2 = v1 + (topz - bottomz); - v1 *= vscale; - v2 *= vscale; - } - else // bottom to top - { - int texHeight = tex->GetHeight(); - v1 = yoffset; - v2 = v1 + (topz - bottomz); - v1 *= vscale; - v2 *= vscale; - v1 = texHeight - v1; - v2 = texHeight - v2; - std::swap(v1, v2); - } -} - -void PolyWallTextureCoords::CalcVBottomPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset) -{ - bool pegged = (line->linedef->flags & ML_DONTPEGBOTTOM) == 0; - if (pegged) // top to bottom - { - v1 = yoffset; - v2 = v1 + (topz - bottomz); - v1 *= vscale; - v2 *= vscale; - } - else - { - v1 = yoffset + (unpeggedceil - topz); - v2 = v1 + (topz - bottomz); - v1 *= vscale; - v2 *= vscale; - } -} - -///////////////////////////////////////////////////////////////////////////// - -void PolyScreenSprite::Render() -{ - FSpecialColormap *special = nullptr; - FColormapStyle colormapstyle; - PalEntry overlay = 0; - bool usecolormapstyle = false; - if (visstyle.BaseColormap >= &SpecialColormaps[0] && - visstyle.BaseColormap < &SpecialColormaps[SpecialColormaps.Size()]) - { - special = static_cast(visstyle.BaseColormap); - } - else if (Colormap->Color == PalEntry(255, 255, 255) && - Colormap->Desaturate == 0) - { - overlay = Colormap->Fade; - overlay.a = BYTE(visstyle.ColormapNum * 255 / NUMCOLORMAPS); - } - else - { - usecolormapstyle = true; - colormapstyle.Color = Colormap->Color; - colormapstyle.Fade = Colormap->Fade; - colormapstyle.Desaturate = Colormap->Desaturate; - colormapstyle.FadeLevel = visstyle.ColormapNum / float(NUMCOLORMAPS); - } - - screen->DrawTexture(Pic, - X1, - Y1, - DTA_DestWidthF, Width, - DTA_DestHeightF, Height, - DTA_Translation, Translation, - DTA_FlipX, Flip, - DTA_TopOffset, 0, - DTA_LeftOffset, 0, - DTA_ClipLeft, viewwindowx, - DTA_ClipTop, viewwindowy, - DTA_ClipRight, viewwindowx + viewwidth, - DTA_ClipBottom, viewwindowy + viewheight, - DTA_AlphaF, visstyle.Alpha, - DTA_RenderStyle, visstyle.RenderStyle, - DTA_FillColor, FillColor, - DTA_SpecialColormap, special, - DTA_ColorOverlay, overlay.d, - DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : nullptr, - TAG_DONE); + static RenderPolyScene scene; + return &scene; } ///////////////////////////////////////////////////////////////////////////// @@ -1450,164 +209,3 @@ void PolyVertexBuffer::Clear() { NextBufferVertex = 0; } - -///////////////////////////////////////////////////////////////////////////// - -TriVertex PolySkyDome::SetVertex(float xx, float yy, float zz, float uu, float vv) -{ - TriVertex v; - v.x = xx; - v.y = yy; - v.z = zz; - v.w = 1.0f; - v.varying[0] = uu; - v.varying[1] = vv; - return v; -} - -TriVertex PolySkyDome::SetVertexXYZ(float xx, float yy, float zz, float uu, float vv) -{ - TriVertex v; - v.x = xx; - v.y = zz; - v.z = yy; - v.w = 1.0f; - v.varying[0] = uu; - v.varying[1] = vv; - return v; -} - -void PolySkyDome::SkyVertex(int r, int c, bool zflip) -{ - static const FAngle maxSideAngle = 60.f; - static const float scale = 10000.; - - FAngle topAngle = (c / (float)mColumns * 360.f); - FAngle sideAngle = maxSideAngle * (float)(mRows - r) / (float)mRows; - float height = sideAngle.Sin(); - float realRadius = scale * sideAngle.Cos(); - FVector2 pos = topAngle.ToVector(realRadius); - float z = (!zflip) ? scale * height : -scale * height; - - float u, v; - //uint32_t color = r == 0 ? 0xffffff : 0xffffffff; - - // And the texture coordinates. - if (!zflip) // Flipped Y is for the lower hemisphere. - { - u = (-c / (float)mColumns); - v = (r / (float)mRows); - } - else - { - u = (-c / (float)mColumns); - v = 1.0f + ((mRows - r) / (float)mRows); - } - - if (r != 4) z += 300; - - // And finally the vertex. - TriVertex vert; - vert = SetVertexXYZ(-pos.X, z - 1.f, pos.Y, u * 4.0f, v * 1.2f + 0.5f/*, color*/); - mVertices.Push(vert); -} - -void PolySkyDome::CreateSkyHemisphere(bool zflip) -{ - int r, c; - - mPrimStart.Push(mVertices.Size()); - - for (c = 0; c < mColumns; c++) - { - SkyVertex(1, c, zflip); - } - - // The total number of triangles per hemisphere can be calculated - // as follows: rows * columns * 2 + 2 (for the top cap). - for (r = 0; r < mRows; r++) - { - mPrimStart.Push(mVertices.Size()); - for (c = 0; c <= mColumns; c++) - { - SkyVertex(r + zflip, c, zflip); - SkyVertex(r + 1 - zflip, c, zflip); - } - } -} - -void PolySkyDome::CreateDome() -{ - mColumns = 128; - mRows = 4; - CreateSkyHemisphere(false); - CreateSkyHemisphere(true); - mPrimStart.Push(mVertices.Size()); -} - -void PolySkyDome::RenderRow(PolyDrawArgs &args, int row) -{ - args.vinput = &mVertices[mPrimStart[row]]; - args.vcount = mPrimStart[row + 1] - mPrimStart[row]; - args.mode = TriangleDrawMode::Strip; - args.ccw = false; - PolyTriangleDrawer::draw(args, TriDrawVariant::Draw); -} - -void PolySkyDome::RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap) -{ - uint32_t solid = skytex->GetSkyCapColor(bottomCap); - if (!r_swtruecolor) - solid = RGB32k.RGB[(RPART(solid) >> 3)][(GPART(solid) >> 3)][(BPART(solid) >> 3)]; - - args.vinput = &mVertices[mPrimStart[row]]; - args.vcount = mPrimStart[row + 1] - mPrimStart[row]; - args.mode = TriangleDrawMode::Fan; - args.ccw = bottomCap; - args.solidcolor = solid; - PolyTriangleDrawer::draw(args, TriDrawVariant::Fill); -} - -void PolySkyDome::Render(const TriMatrix &worldToClip) -{ - FTextureID sky1tex, sky2tex; - if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) - sky1tex = sky2texture; - else - sky1tex = sky1texture; - sky2tex = sky2texture; - - FTexture *frontskytex = TexMan(sky1tex, true); - FTexture *backskytex = nullptr; - if (level.flags & LEVEL_DOUBLESKY) - backskytex = TexMan(sky2tex, true); - - TriMatrix objectToWorld = TriMatrix::translate((float)ViewPos.X, (float)ViewPos.Y, (float)ViewPos.Z); - - TriUniforms uniforms; - uniforms.objectToClip = worldToClip * objectToWorld; - uniforms.light = 256; - uniforms.flags = 0; - uniforms.subsectorDepth = RenderPolyBsp::SkySubsectorDepth; - - int rc = mRows + 1; - - PolyDrawArgs args; - args.uniforms = uniforms; - args.clipleft = 0; - args.cliptop = 0; - args.clipright = viewwidth; - args.clipbottom = viewheight; - args.stenciltestvalue = 255; - args.stencilwritevalue = 1; - args.SetTexture(frontskytex); - - RenderCapColorRow(args, frontskytex, 0, false); - RenderCapColorRow(args, frontskytex, rc, true); - - for (int i = 1; i <= mRows; i++) - { - RenderRow(args, i); - RenderRow(args, rc + i); - } -} diff --git a/src/r_poly.h b/src/r_poly.h index 6c5051904..b182a582d 100644 --- a/src/r_poly.h +++ b/src/r_poly.h @@ -1,5 +1,5 @@ /* -** Experimental Doom software renderer +** Polygon Doom software renderer ** Copyright (c) 2016 Magnus Norddahl ** ** This software is provided 'as-is', without any express or implied @@ -31,60 +31,13 @@ #include "r_main.h" #include "r_poly_triangle.h" #include "r_poly_intersection.h" - -// DScreen accelerated sprite to be rendered -class PolyScreenSprite -{ -public: - void Render(); - - FTexture *Pic = nullptr; - double X1 = 0.0; - double Y1 = 0.0; - double Width = 0.0; - double Height = 0.0; - FRemapTable *Translation = nullptr; - bool Flip = false; - visstyle_t visstyle; - uint32_t FillColor = 0; - FDynamicColormap *Colormap = nullptr; -}; - -class RenderPolyWall -{ -public: - void Render(const TriMatrix &worldToClip); - - void SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2) - { - this->v1 = v1; - this->v2 = v2; - this->ceil1 = ceil1; - this->floor1 = floor1; - this->ceil2 = ceil2; - this->floor2 = floor2; - } - - DVector2 v1; - DVector2 v2; - double ceil1 = 0.0; - double floor1 = 0.0; - double ceil2 = 0.0; - double floor2 = 0.0; - - const seg_t *Line = nullptr; - side_t::ETexpart Texpart = side_t::mid; - double TopZ = 0.0; - double BottomZ = 0.0; - double UnpeggedCeil = 0.0; - FSWColormap *Colormap = nullptr; - bool Masked = false; - uint32_t SubsectorDepth = 0; - -private: - FTexture *GetTexture(); - int GetLightLevel(); -}; +#include "r_poly_wall.h" +#include "r_poly_sprite.h" +#include "r_poly_wallsprite.h" +#include "r_poly_playersprite.h" +#include "r_poly_plane.h" +#include "r_poly_sky.h" +#include "r_poly_cull.h" // Used for sorting things by distance to the camera class PolySortedSprite @@ -119,110 +72,34 @@ public: int Count = 0; }; -class PolySkyDome -{ -public: - PolySkyDome() { CreateDome(); } - void Render(const TriMatrix &worldToClip); - -private: - TArray mVertices; - TArray mPrimStart; - int mRows, mColumns; - - void SkyVertex(int r, int c, bool yflip); - void CreateSkyHemisphere(bool zflip); - void CreateDome(); - void RenderRow(PolyDrawArgs &args, int row); - void RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap); - - TriVertex SetVertex(float xx, float yy, float zz, float uu = 0, float vv = 0); - TriVertex SetVertexXYZ(float xx, float yy, float zz, float uu = 0, float vv = 0); -}; - -// Renders a GL BSP tree in a scene -class RenderPolyBsp +// Renders a scene +class RenderPolyScene { public: void Render(); - void RenderScreenSprites(); + void RenderRemainingPlayerSprites(); static const uint32_t SkySubsectorDepth = 0x7fffffff; + static RenderPolyScene *Instance(); + private: - void RenderNode(void *node); void RenderSubsector(subsector_t *sub); - void RenderPlane(subsector_t *sub, uint32_t subsectorDepth, bool ceiling); - void AddLine(seg_t *line, sector_t *frontsector, uint32_t subsectorDepth); - TriVertex PlaneVertex(vertex_t *v1, sector_t *sector, double height); + void RenderLine(seg_t *line, sector_t *frontsector, uint32_t subsectorDepth); void RenderTranslucent(); - void AddSprite(AActor *thing, subsector_t *sub, uint32_t subsectorDepth); - void AddWallSprite(AActor *thing, subsector_t *sub, uint32_t subsectorDepth); - bool IsThingCulled(AActor *thing); - visstyle_t GetSpriteVisStyle(AActor *thing, double z); - FTexture *GetSpriteTexture(AActor *thing, /*out*/ bool &flipX); SpriteRange GetSpritesForSector(sector_t *sector); - void RenderPlayerSprites(); - void RenderPlayerSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac); - - int PointOnSide(const DVector2 &pos, const node_t *node); - - // Checks BSP node/subtree bounding box. - // Returns true if some part of the bbox might be visible. - bool CheckBBox(float *bspcoord); - bool GetSegmentRangeForLine(double x1, double y1, double x2, double y2, int &sx1, int &sx2) const; - void MarkSegmentCulled(int x1, int x2); - bool IsSegmentCulled(int x1, int x2) const; - void ClearSolidSegments(); - - std::vector PvsSectors; + TriMatrix WorldToClip; + PolyCull Cull; uint32_t NextSubsectorDepth = 0; - double MaxCeilingHeight = 0.0; - double MinFloorHeight = 0.0; - - TriMatrix worldToClip; - FrustumPlanes frustumPlanes; - std::vector SectorSpriteRanges; std::vector SortedSprites; std::vector TranslucentObjects; - - std::vector TempTranslucentWalls; - - std::vector ScreenSprites; - - const int BaseXCenter = 160; - const int BaseYCenter = 100; - - struct SolidSegment - { - SolidSegment(int x1, int x2) : X1(x1), X2(x2) { } - int X1, X2; - }; - - std::vector SolidSegments; - const int SolidCullScale = 3000; + std::vector SubsectorTranslucentWalls; PolySkyDome skydome; -}; - -// Texture coordinates for a wall -class PolyWallTextureCoords -{ -public: - PolyWallTextureCoords(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); - - double u1, u2; - double v1, v2; - -private: - void CalcU(FTexture *tex, const seg_t *line, side_t::ETexpart texpart); - void CalcV(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); - void CalcVTopPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset); - void CalcVMidPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset); - void CalcVBottomPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset); + RenderPolyPlayerSprites PlayerSprites; }; class PolyVertexBuffer diff --git a/src/r_poly_cull.cpp b/src/r_poly_cull.cpp new file mode 100644 index 000000000..3df07abf9 --- /dev/null +++ b/src/r_poly_cull.cpp @@ -0,0 +1,259 @@ +/* +** Potential visible set (PVS) handling +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_poly_cull.h" +#include "r_poly.h" + +void PolyCull::CullScene(const TriMatrix &worldToClip) +{ + ClearSolidSegments(); + PvsSectors.clear(); + frustumPlanes = FrustumPlanes(worldToClip); + + // Cull front to back + if (numnodes == 0) + { + PvsSectors.push_back(subsectors); + MaxCeilingHeight = subsectors->sector->ceilingplane.Zat0(); + MinFloorHeight = subsectors->sector->floorplane.Zat0(); + } + else + { + MaxCeilingHeight = 0.0; + MinFloorHeight = 0.0; + CullNode(nodes + numnodes - 1); // The head node is the last node output. + } + + ClearSolidSegments(); +} + +void PolyCull::CullNode(void *node) +{ + while (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + + // Decide which side the view point is on. + int side = PointOnSide(ViewPos, bsp); + + // Recursively divide front space (toward the viewer). + CullNode(bsp->children[side]); + + // Possibly divide back space (away from the viewer). + side ^= 1; + if (!CheckBBox(bsp->bbox[side])) + return; + + node = bsp->children[side]; + } + + // Mark that we need to render this + subsector_t *sub = (subsector_t *)((BYTE *)node - 1); + MaxCeilingHeight = MAX(MaxCeilingHeight, sub->sector->ceilingplane.Zat0()); + MinFloorHeight = MIN(MinFloorHeight, sub->sector->floorplane.Zat0()); + PvsSectors.push_back(sub); + + // Update culling info for further bsp clipping + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + if ((line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) && line->backsector == nullptr) + { + int sx1, sx2; + if (GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2)) + { + MarkSegmentCulled(sx1, sx2); + } + } + } +} + +void PolyCull::ClearSolidSegments() +{ + SolidSegments.clear(); + SolidSegments.reserve(SolidCullScale + 2); + SolidSegments.push_back({ -0x7fff, -SolidCullScale }); + SolidSegments.push_back({ SolidCullScale , 0x7fff }); +} + +bool PolyCull::IsSegmentCulled(int x1, int x2) const +{ + int next = 0; + while (SolidSegments[next].X2 <= x2) + next++; + return (x1 >= SolidSegments[next].X1 && x2 <= SolidSegments[next].X2); +} + +void PolyCull::MarkSegmentCulled(int x1, int x2) +{ + if (x1 >= x2) + return; + + int cur = 1; + while (true) + { + if (SolidSegments[cur].X1 <= x1 && SolidSegments[cur].X2 >= x2) // Already fully marked + { + break; + } + else if (cur + 1 != SolidSegments.size() && SolidSegments[cur].X2 >= x1 && SolidSegments[cur].X1 <= x2) // Merge segments + { + // Find last segment + int merge = cur; + while (merge + 2 != SolidSegments.size() && SolidSegments[merge + 1].X1 <= x2) + merge++; + + // Apply new merged range + SolidSegments[cur].X1 = MIN(SolidSegments[cur].X1, x1); + SolidSegments[cur].X2 = MAX(SolidSegments[merge].X2, x2); + + // Remove additional segments we merged with + if (merge > cur) + SolidSegments.erase(SolidSegments.begin() + (cur + 1), SolidSegments.begin() + (merge + 1)); + + break; + } + else if (SolidSegments[cur].X1 > x1) // Insert new segment + { + SolidSegments.insert(SolidSegments.begin() + cur, { x1, x2 }); + break; + } + cur++; + } +} + +int PolyCull::PointOnSide(const DVector2 &pos, const node_t *node) +{ + return DMulScale32(FLOAT2FIXED(pos.Y) - node->y, node->dx, node->x - FLOAT2FIXED(pos.X), node->dy) > 0; +} + +bool PolyCull::CheckBBox(float *bspcoord) +{ + // Start using a quick frustum AABB test: + + AxisAlignedBoundingBox aabb(Vec3f(bspcoord[BOXLEFT], bspcoord[BOXBOTTOM], (float)ViewPos.Z - 1000.0f), Vec3f(bspcoord[BOXRIGHT], bspcoord[BOXTOP], (float)ViewPos.Z + 1000.0f)); + auto result = IntersectionTest::frustum_aabb(frustumPlanes, aabb); + if (result == IntersectionTest::outside) + return false; + + // Occlusion test using solid segments: + + int boxx; + int boxy; + int boxpos; + + double x1, y1, x2, y2; + + // Find the corners of the box + // that define the edges from current viewpoint. + if (ViewPos.X <= bspcoord[BOXLEFT]) + boxx = 0; + else if (ViewPos.X < bspcoord[BOXRIGHT]) + boxx = 1; + else + boxx = 2; + + if (ViewPos.Y >= bspcoord[BOXTOP]) + boxy = 0; + else if (ViewPos.Y > bspcoord[BOXBOTTOM]) + boxy = 1; + else + boxy = 2; + + boxpos = (boxy << 2) + boxx; + if (boxpos == 5) + return true; + + static const int checkcoord[12][4] = + { + { 3,0,2,1 }, + { 3,0,2,0 }, + { 3,1,2,0 }, + { 0 }, + { 2,0,2,1 }, + { 0,0,0,0 }, + { 3,1,3,0 }, + { 0 }, + { 2,0,3,1 }, + { 2,1,3,1 }, + { 2,1,3,0 } + }; + + x1 = bspcoord[checkcoord[boxpos][0]]; + y1 = bspcoord[checkcoord[boxpos][1]]; + x2 = bspcoord[checkcoord[boxpos][2]]; + y2 = bspcoord[checkcoord[boxpos][3]]; + + int sx1, sx2; + if (GetSegmentRangeForLine(x1, y1, x2, y2, sx1, sx2)) + return !IsSegmentCulled(sx1, sx2); + else + return true; +} + +bool PolyCull::GetSegmentRangeForLine(double x1, double y1, double x2, double y2, int &sx1, int &sx2) const +{ + double znear = 5.0; + + // Transform to 2D view space: + x1 = x1 - ViewPos.X; + y1 = y1 - ViewPos.Y; + x2 = x2 - ViewPos.X; + y2 = y2 - ViewPos.Y; + double rx1 = x1 * ViewSin - y1 * ViewCos; + double rx2 = x2 * ViewSin - y2 * ViewCos; + double ry1 = x1 * ViewCos + y1 * ViewSin; + double ry2 = x2 * ViewCos + y2 * ViewSin; + + // Cull if line is entirely behind view + if (ry1 < znear && ry2 < znear) return false; + + // Clip line, if needed + double t1 = 0.0f, t2 = 1.0f; + if (ry1 < znear) + t1 = clamp((znear - ry1) / (ry2 - ry1), 0.0, 1.0); + if (ry2 < znear) + t2 = clamp((znear - ry1) / (ry2 - ry1), 0.0, 1.0); + if (t1 != 0.0 || t2 != 1.0) + { + double nx1 = rx1 * (1.0 - t1) + rx2 * t1; + double ny1 = ry1 * (1.0 - t1) + ry2 * t1; + double nx2 = rx1 * (1.0 - t2) + rx2 * t2; + double ny2 = ry1 * (1.0 - t2) + ry2 * t2; + rx1 = nx1; + rx2 = nx2; + ry1 = ny1; + ry2 = ny2; + } + + sx1 = (int)floor(clamp(rx1 / ry1 * (SolidCullScale / 3), (double)-SolidCullScale, (double)SolidCullScale)); + sx2 = (int)floor(clamp(rx2 / ry2 * (SolidCullScale / 3), (double)-SolidCullScale, (double)SolidCullScale)); + + if (sx1 > sx2) + std::swap(sx1, sx2); + return sx1 != sx2; +} diff --git a/src/r_poly_cull.h b/src/r_poly_cull.h new file mode 100644 index 000000000..a79355f68 --- /dev/null +++ b/src/r_poly_cull.h @@ -0,0 +1,61 @@ +/* +** Potential visible set (PVS) handling +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_poly_triangle.h" +#include "r_poly_intersection.h" + +class PolyCull +{ +public: + void CullScene(const TriMatrix &worldToClip); + + bool GetSegmentRangeForLine(double x1, double y1, double x2, double y2, int &sx1, int &sx2) const; + void MarkSegmentCulled(int x1, int x2); + bool IsSegmentCulled(int x1, int x2) const; + + std::vector PvsSectors; + double MaxCeilingHeight = 0.0; + double MinFloorHeight = 0.0; + +private: + struct SolidSegment + { + SolidSegment(int x1, int x2) : X1(x1), X2(x2) { } + int X1, X2; + }; + + void CullNode(void *node); + int PointOnSide(const DVector2 &pos, const node_t *node); + + // Checks BSP node/subtree bounding box. + // Returns true if some part of the bbox might be visible. + bool CheckBBox(float *bspcoord); + + void ClearSolidSegments(); + + std::vector SolidSegments; + const int SolidCullScale = 3000; + + FrustumPlanes frustumPlanes; +}; diff --git a/src/r_poly_particle.cpp b/src/r_poly_particle.cpp new file mode 100644 index 000000000..06b963e4f --- /dev/null +++ b/src/r_poly_particle.cpp @@ -0,0 +1,33 @@ +/* +** Particle drawing +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_poly_particle.h" +#include "r_poly.h" + +void RenderPolyParticle::Render() +{ +} diff --git a/src/r_poly_particle.h b/src/r_poly_particle.h new file mode 100644 index 000000000..5573a7e48 --- /dev/null +++ b/src/r_poly_particle.h @@ -0,0 +1,29 @@ +/* +** Handling drawing a particle +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +class RenderPolyParticle +{ +public: + void Render(); +}; diff --git a/src/r_poly_plane.cpp b/src/r_poly_plane.cpp new file mode 100644 index 000000000..ac9622110 --- /dev/null +++ b/src/r_poly_plane.cpp @@ -0,0 +1,196 @@ +/* +** Handling drawing a plane (ceiling, floor) +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_poly_plane.h" +#include "r_poly.h" +#include "r_sky.h" // for skyflatnum + +void RenderPolyPlane::Render(const TriMatrix &worldToClip, subsector_t *sub, uint32_t subsectorDepth, bool ceiling, double skyHeight) +{ + sector_t *frontsector = sub->sector; + + FTextureID picnum = frontsector->GetTexture(ceiling ? sector_t::ceiling : sector_t::floor); + FTexture *tex = TexMan(picnum); + if (tex->UseType == FTexture::TEX_Null) + return; + + bool isSky = picnum == skyflatnum; + + TriUniforms uniforms; + uniforms.objectToClip = worldToClip; + uniforms.light = (uint32_t)(frontsector->lightlevel / 255.0f * 256.0f); + if (fixedlightlev >= 0) + uniforms.light = (uint32_t)(fixedlightlev / 255.0f * 256.0f); + else if (fixedcolormap) + uniforms.light = 256; + uniforms.flags = 0; + uniforms.subsectorDepth = isSky ? RenderPolyScene::SkySubsectorDepth : subsectorDepth; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(sub->numlines); + if (!vertices) + return; + + if (ceiling) + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[sub->numlines - 1 - i] = PlaneVertex(line->v1, frontsector, isSky ? skyHeight : frontsector->ceilingplane.ZatPoint(line->v1)); + } + } + else + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[i] = PlaneVertex(line->v1, frontsector, isSky ? skyHeight : frontsector->floorplane.ZatPoint(line->v1)); + } + } + + PolyDrawArgs args; + args.uniforms = uniforms; + args.vinput = vertices; + args.vcount = sub->numlines; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.clipleft = 0; + args.cliptop = 0; + args.clipright = viewwidth; + args.clipbottom = viewheight; + args.stenciltestvalue = 0; + args.stencilwritevalue = 1; + + if (!isSky) + { + args.SetTexture(tex); + PolyTriangleDrawer::draw(args, TriDrawVariant::Draw); + PolyTriangleDrawer::draw(args, TriDrawVariant::Stencil); + } + else + { + args.stencilwritevalue = 255; + PolyTriangleDrawer::draw(args, TriDrawVariant::Stencil); + + for (uint32_t i = 0; i < sub->numlines; i++) + { + TriVertex *wallvert = PolyVertexBuffer::GetVertices(4); + if (!wallvert) + return; + + seg_t *line = &sub->firstline[i]; + + bool closedSky = false; + if (line->backsector) + { + sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector; + + double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1); + double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1); + double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2); + double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2); + + double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1); + double backfloorz1 = backsector->floorplane.ZatPoint(line->v1); + double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2); + double backfloorz2 = backsector->floorplane.ZatPoint(line->v2); + + double topceilz1 = frontceilz1; + double topceilz2 = frontceilz2; + double topfloorz1 = MIN(backceilz1, frontceilz1); + double topfloorz2 = MIN(backceilz2, frontceilz2); + double bottomceilz1 = MAX(frontfloorz1, backfloorz1); + double bottomceilz2 = MAX(frontfloorz2, backfloorz2); + double bottomfloorz1 = frontfloorz1; + double bottomfloorz2 = frontfloorz2; + double middleceilz1 = topfloorz1; + double middleceilz2 = topfloorz2; + double middlefloorz1 = MIN(bottomceilz1, middleceilz1); + double middlefloorz2 = MIN(bottomceilz2, middleceilz2); + + bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum; + bool bothSkyFloor = frontsector->GetTexture(sector_t::floor) == skyflatnum && backsector->GetTexture(sector_t::floor) == skyflatnum; + + bool closedSector = backceilz1 == backfloorz1 && backceilz2 == backfloorz2; + closedSky = (ceiling && bothSkyCeiling && closedSector) || (!ceiling && bothSkyFloor && closedSector); + if (!closedSky) + { + bool topwall = (topceilz1 > topfloorz1 || topceilz2 > topfloorz2) && line->sidedef && !bothSkyCeiling; + bool bottomwall = (bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && line->sidedef && !bothSkyFloor; + if ((ceiling && !topwall) || (!ceiling && !bottomwall)) + continue; + } + } + + if (ceiling) + { + wallvert[0] = PlaneVertex(line->v1, frontsector, skyHeight); + wallvert[1] = PlaneVertex(line->v2, frontsector, skyHeight); + if (!closedSky) + { + wallvert[2] = PlaneVertex(line->v2, frontsector, frontsector->ceilingplane.ZatPoint(line->v2)); + wallvert[3] = PlaneVertex(line->v1, frontsector, frontsector->ceilingplane.ZatPoint(line->v1)); + } + else + { + wallvert[2] = PlaneVertex(line->v2, frontsector, frontsector->floorplane.ZatPoint(line->v2)); + wallvert[3] = PlaneVertex(line->v1, frontsector, frontsector->floorplane.ZatPoint(line->v1)); + } + } + else + { + if (!closedSky) + { + wallvert[0] = PlaneVertex(line->v1, frontsector, frontsector->floorplane.ZatPoint(line->v1)); + wallvert[1] = PlaneVertex(line->v2, frontsector, frontsector->floorplane.ZatPoint(line->v2)); + } + else + { + wallvert[0] = PlaneVertex(line->v1, frontsector, frontsector->ceilingplane.ZatPoint(line->v1)); + wallvert[1] = PlaneVertex(line->v2, frontsector, frontsector->ceilingplane.ZatPoint(line->v2)); + } + wallvert[2] = PlaneVertex(line->v2, frontsector, skyHeight); + wallvert[3] = PlaneVertex(line->v1, frontsector, skyHeight); + } + + args.vinput = wallvert; + args.vcount = 4; + PolyTriangleDrawer::draw(args, TriDrawVariant::Stencil); + } + } +} + +TriVertex RenderPolyPlane::PlaneVertex(vertex_t *v1, sector_t *sector, double height) +{ + TriVertex v; + v.x = (float)v1->fPos().X; + v.y = (float)v1->fPos().Y; + v.z = (float)height; + v.w = 1.0f; + v.varying[0] = v.x / 64.0f; + v.varying[1] = 1.0f - v.y / 64.0f; + return v; +} diff --git a/src/r_poly_plane.h b/src/r_poly_plane.h new file mode 100644 index 000000000..b4f2087e8 --- /dev/null +++ b/src/r_poly_plane.h @@ -0,0 +1,34 @@ +/* +** Handling drawing a plane (ceiling, floor) +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_poly_triangle.h" + +class RenderPolyPlane +{ +public: + void Render(const TriMatrix &worldToClip, subsector_t *sub, uint32_t subsectorDepth, bool ceiling, double skyHeight); + +private: + TriVertex PlaneVertex(vertex_t *v1, sector_t *sector, double height); +}; diff --git a/src/r_poly_playersprite.cpp b/src/r_poly_playersprite.cpp new file mode 100644 index 000000000..1a657d1a2 --- /dev/null +++ b/src/r_poly_playersprite.cpp @@ -0,0 +1,299 @@ +/* +** Handling drawing a player sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_poly_playersprite.h" +#include "r_poly.h" +#include "r_things.h" // for pspritexscale + +EXTERN_CVAR(Bool, r_drawplayersprites) +EXTERN_CVAR(Bool, r_deathcamera) +EXTERN_CVAR(Bool, st_scale) + +void RenderPolyPlayerSprites::Render() +{ + // In theory, everything in this function could be moved to RenderRemainingSprites. + // Just in case there's some hack elsewhere that relies on this happening as part + // of the main rendering we do it exactly as the old software renderer did. + + ScreenSprites.clear(); + + if (!r_drawplayersprites || + !camera || + !camera->player || + (players[consoleplayer].cheats & CF_CHASECAM) || + (r_deathcamera && camera->health <= 0)) + return; + + float bobx, boby; + P_BobWeapon(camera->player, &bobx, &boby, r_TicFracF); + + // Interpolate the main weapon layer once so as to be able to add it to other layers. + double wx, wy; + DPSprite *weapon = camera->player->FindPSprite(PSP_WEAPON); + if (weapon) + { + if (weapon->firstTic) + { + wx = weapon->x; + wy = weapon->y; + } + else + { + wx = weapon->oldx + (weapon->x - weapon->oldx) * r_TicFracF; + wy = weapon->oldy + (weapon->y - weapon->oldy) * r_TicFracF; + } + } + else + { + wx = 0; + wy = 0; + } + + for (DPSprite *sprite = camera->player->psprites; sprite != nullptr; sprite = sprite->GetNext()) + { + // [RH] Don't draw the targeter's crosshair if the player already has a crosshair set. + // It's possible this psprite's caller is now null but the layer itself hasn't been destroyed + // because it didn't tick yet (if we typed 'take all' while in the console for example). + // In this case let's simply not draw it to avoid crashing. + if ((sprite->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && sprite->GetCaller() != nullptr) + { + RenderSprite(sprite, camera, bobx, boby, wx, wy, r_TicFracF); + } + } +} + +void RenderPolyPlayerSprites::RenderRemainingSprites() +{ + for (auto &sprite : ScreenSprites) + sprite.Render(); +} + +void RenderPolyPlayerSprites::RenderSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac) +{ + // decide which patch to use + if ((unsigned)sprite->GetSprite() >= (unsigned)sprites.Size()) + { + DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite number %i\n", sprite->GetSprite()); + return; + } + + spritedef_t *def = &sprites[sprite->GetSprite()]; + if (sprite->GetFrame() >= def->numframes) + { + DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite frame %i : %i\n", sprite->GetSprite(), sprite->GetFrame()); + return; + } + + spriteframe_t *frame = &SpriteFrames[def->spriteframes + sprite->GetFrame()]; + FTextureID picnum = frame->Texture[0]; + bool flip = (frame->Flip & 1) != 0; + + FTexture *tex = TexMan(picnum); + if (tex->UseType == FTexture::TEX_Null) + return; + + // Can't interpolate the first tic. + if (sprite->firstTic) + { + sprite->firstTic = false; + sprite->oldx = sprite->x; + sprite->oldy = sprite->y; + } + + double sx = sprite->oldx + (sprite->x - sprite->oldx) * ticfrac; + double sy = sprite->oldy + (sprite->y - sprite->oldy) * ticfrac; + + if (sprite->Flags & PSPF_ADDBOB) + { + sx += bobx; + sy += boby; + } + + if (sprite->Flags & PSPF_ADDWEAPON && sprite->GetID() != PSP_WEAPON) + { + sx += wx; + sy += wy; + } + + // calculate edges of the shape + double tx = sx - BaseXCenter; + + tx -= tex->GetScaledLeftOffset(); + int x1 = xs_RoundToInt(CenterX + tx * pspritexscale); + + // off the right side + if (x1 > viewwidth) + return; + + tx += tex->GetScaledWidth(); + int x2 = xs_RoundToInt(CenterX + tx * pspritexscale); + + // off the left side + if (x2 <= 0) + return; + + double texturemid = (BaseYCenter - sy) * tex->Scale.Y + tex->TopOffset; + + // Adjust PSprite for fullscreen views + if (camera->player && (RenderTarget != screen || viewheight == RenderTarget->GetHeight() || (RenderTarget->GetWidth() > (BaseXCenter * 2) && !st_scale))) + { + AWeapon *weapon = dyn_cast(sprite->GetCaller()); + if (weapon != nullptr && weapon->YAdjust != 0) + { + if (RenderTarget != screen || viewheight == RenderTarget->GetHeight()) + { + texturemid -= weapon->YAdjust; + } + else + { + texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust; + } + } + } + + // Move the weapon down for 1280x1024. + if (sprite->GetID() < PSP_TARGETCENTER) + { + texturemid -= AspectPspriteOffset(WidescreenRatio); + } + + int clipped_x1 = MAX(x1, 0); + int clipped_x2 = MIN(x2, viewwidth); + double xscale = pspritexscale / tex->Scale.X; + double yscale = pspriteyscale / tex->Scale.Y; + uint32_t translation = 0; // [RH] Use default colors + + double xiscale, startfrac; + if (flip) + { + xiscale = -pspritexiscale * tex->Scale.X; + startfrac = 1; + } + else + { + xiscale = pspritexiscale * tex->Scale.X; + startfrac = 0; + } + + if (clipped_x1 > x1) + startfrac += xiscale * (clipped_x1 - x1); + + bool noaccel = false; + + FDynamicColormap *basecolormap = viewsector->ColorMap; + FDynamicColormap *colormap_to_use = basecolormap; + + visstyle_t visstyle; + visstyle.ColormapNum = 0; + visstyle.BaseColormap = basecolormap; + visstyle.Alpha = 0; + visstyle.RenderStyle = STYLE_Normal; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + int spriteshade = LIGHT2SHADE(owner->Sector->lightlevel + actualextralight); + double minz = double((2048 * 4) / double(1 << 20)); + visstyle.ColormapNum = GETPALOOKUP(r_SpriteVisibility / minz, spriteshade); + + if (sprite->GetID() < PSP_TARGETCENTER) + { + // Lots of complicated style and noaccel stuff + } + + // Check for hardware-assisted 2D. If it's available, and this sprite is not + // fuzzy, don't draw it until after the switch to 2D mode. + if (!noaccel && RenderTarget == screen && (DFrameBuffer *)screen->Accel2D) + { + FRenderStyle style = visstyle.RenderStyle; + style.CheckFuzz(); + if (style.BlendOp != STYLEOP_Fuzz) + { + PolyScreenSprite screenSprite; + screenSprite.Pic = tex; + screenSprite.X1 = viewwindowx + x1; + screenSprite.Y1 = viewwindowy + viewheight / 2 - texturemid * yscale - 0.5; + screenSprite.Width = tex->GetWidth() * xscale; + screenSprite.Height = tex->GetHeight() * yscale; + screenSprite.Translation = TranslationToTable(translation); + screenSprite.Flip = xiscale < 0; + screenSprite.visstyle = visstyle; + screenSprite.Colormap = colormap_to_use; + ScreenSprites.push_back(screenSprite); + return; + } + } + + //R_DrawVisSprite(vis); +} + +void PolyScreenSprite::Render() +{ + FSpecialColormap *special = nullptr; + FColormapStyle colormapstyle; + PalEntry overlay = 0; + bool usecolormapstyle = false; + if (visstyle.BaseColormap >= &SpecialColormaps[0] && + visstyle.BaseColormap < &SpecialColormaps[SpecialColormaps.Size()]) + { + special = static_cast(visstyle.BaseColormap); + } + else if (Colormap->Color == PalEntry(255, 255, 255) && + Colormap->Desaturate == 0) + { + overlay = Colormap->Fade; + overlay.a = BYTE(visstyle.ColormapNum * 255 / NUMCOLORMAPS); + } + else + { + usecolormapstyle = true; + colormapstyle.Color = Colormap->Color; + colormapstyle.Fade = Colormap->Fade; + colormapstyle.Desaturate = Colormap->Desaturate; + colormapstyle.FadeLevel = visstyle.ColormapNum / float(NUMCOLORMAPS); + } + + screen->DrawTexture(Pic, + X1, + Y1, + DTA_DestWidthF, Width, + DTA_DestHeightF, Height, + DTA_Translation, Translation, + DTA_FlipX, Flip, + DTA_TopOffset, 0, + DTA_LeftOffset, 0, + DTA_ClipLeft, viewwindowx, + DTA_ClipTop, viewwindowy, + DTA_ClipRight, viewwindowx + viewwidth, + DTA_ClipBottom, viewwindowy + viewheight, + DTA_AlphaF, visstyle.Alpha, + DTA_RenderStyle, visstyle.RenderStyle, + DTA_FillColor, FillColor, + DTA_SpecialColormap, special, + DTA_ColorOverlay, overlay.d, + DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : nullptr, + TAG_DONE); +} diff --git a/src/r_poly_playersprite.h b/src/r_poly_playersprite.h new file mode 100644 index 000000000..e7384e200 --- /dev/null +++ b/src/r_poly_playersprite.h @@ -0,0 +1,59 @@ +/* +** Handling drawing a player sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +class PolyScreenSprite; +class DPSprite; + +class RenderPolyPlayerSprites +{ +public: + void Render(); + void RenderRemainingSprites(); + +private: + void RenderSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac); + + const int BaseXCenter = 160; + const int BaseYCenter = 100; + + std::vector ScreenSprites; +}; + +// DScreen accelerated sprite to be rendered +class PolyScreenSprite +{ +public: + void Render(); + + FTexture *Pic = nullptr; + double X1 = 0.0; + double Y1 = 0.0; + double Width = 0.0; + double Height = 0.0; + FRemapTable *Translation = nullptr; + bool Flip = false; + visstyle_t visstyle; + uint32_t FillColor = 0; + FDynamicColormap *Colormap = nullptr; +}; diff --git a/src/r_poly_sky.cpp b/src/r_poly_sky.cpp new file mode 100644 index 000000000..03a4bfdb2 --- /dev/null +++ b/src/r_poly_sky.cpp @@ -0,0 +1,193 @@ +/* +** Sky dome rendering +** Copyright(C) 2003-2016 Christoph Oelckers +** All rights reserved. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program. If not, see http:**www.gnu.org/licenses/ +** +** Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky. +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_poly_sky.h" +#include "r_poly.h" +#include "r_sky.h" // for skyflatnum + +PolySkyDome::PolySkyDome() +{ + CreateDome(); +} + +void PolySkyDome::Render(const TriMatrix &worldToClip) +{ + FTextureID sky1tex, sky2tex; + if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) + sky1tex = sky2texture; + else + sky1tex = sky1texture; + sky2tex = sky2texture; + + FTexture *frontskytex = TexMan(sky1tex, true); + FTexture *backskytex = nullptr; + if (level.flags & LEVEL_DOUBLESKY) + backskytex = TexMan(sky2tex, true); + + TriMatrix objectToWorld = TriMatrix::translate((float)ViewPos.X, (float)ViewPos.Y, (float)ViewPos.Z); + + TriUniforms uniforms; + uniforms.objectToClip = worldToClip * objectToWorld; + uniforms.light = 256; + uniforms.flags = 0; + uniforms.subsectorDepth = RenderPolyScene::SkySubsectorDepth; + + int rc = mRows + 1; + + PolyDrawArgs args; + args.uniforms = uniforms; + args.clipleft = 0; + args.cliptop = 0; + args.clipright = viewwidth; + args.clipbottom = viewheight; + args.stenciltestvalue = 255; + args.stencilwritevalue = 1; + args.SetTexture(frontskytex); + + RenderCapColorRow(args, frontskytex, 0, false); + RenderCapColorRow(args, frontskytex, rc, true); + + for (int i = 1; i <= mRows; i++) + { + RenderRow(args, i); + RenderRow(args, rc + i); + } +} + +void PolySkyDome::RenderRow(PolyDrawArgs &args, int row) +{ + args.vinput = &mVertices[mPrimStart[row]]; + args.vcount = mPrimStart[row + 1] - mPrimStart[row]; + args.mode = TriangleDrawMode::Strip; + args.ccw = false; + PolyTriangleDrawer::draw(args, TriDrawVariant::Draw); +} + +void PolySkyDome::RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap) +{ + uint32_t solid = skytex->GetSkyCapColor(bottomCap); + if (!r_swtruecolor) + solid = RGB32k.RGB[(RPART(solid) >> 3)][(GPART(solid) >> 3)][(BPART(solid) >> 3)]; + + args.vinput = &mVertices[mPrimStart[row]]; + args.vcount = mPrimStart[row + 1] - mPrimStart[row]; + args.mode = TriangleDrawMode::Fan; + args.ccw = bottomCap; + args.solidcolor = solid; + PolyTriangleDrawer::draw(args, TriDrawVariant::Fill); +} + +void PolySkyDome::CreateDome() +{ + mColumns = 128; + mRows = 4; + CreateSkyHemisphere(false); + CreateSkyHemisphere(true); + mPrimStart.Push(mVertices.Size()); +} + +void PolySkyDome::CreateSkyHemisphere(bool zflip) +{ + int r, c; + + mPrimStart.Push(mVertices.Size()); + + for (c = 0; c < mColumns; c++) + { + SkyVertex(1, c, zflip); + } + + // The total number of triangles per hemisphere can be calculated + // as follows: rows * columns * 2 + 2 (for the top cap). + for (r = 0; r < mRows; r++) + { + mPrimStart.Push(mVertices.Size()); + for (c = 0; c <= mColumns; c++) + { + SkyVertex(r + zflip, c, zflip); + SkyVertex(r + 1 - zflip, c, zflip); + } + } +} + +TriVertex PolySkyDome::SetVertex(float xx, float yy, float zz, float uu, float vv) +{ + TriVertex v; + v.x = xx; + v.y = yy; + v.z = zz; + v.w = 1.0f; + v.varying[0] = uu; + v.varying[1] = vv; + return v; +} + +TriVertex PolySkyDome::SetVertexXYZ(float xx, float yy, float zz, float uu, float vv) +{ + TriVertex v; + v.x = xx; + v.y = zz; + v.z = yy; + v.w = 1.0f; + v.varying[0] = uu; + v.varying[1] = vv; + return v; +} + +void PolySkyDome::SkyVertex(int r, int c, bool zflip) +{ + static const FAngle maxSideAngle = 60.f; + static const float scale = 10000.; + + FAngle topAngle = (c / (float)mColumns * 360.f); + FAngle sideAngle = maxSideAngle * (float)(mRows - r) / (float)mRows; + float height = sideAngle.Sin(); + float realRadius = scale * sideAngle.Cos(); + FVector2 pos = topAngle.ToVector(realRadius); + float z = (!zflip) ? scale * height : -scale * height; + + float u, v; + //uint32_t color = r == 0 ? 0xffffff : 0xffffffff; + + // And the texture coordinates. + if (!zflip) // Flipped Y is for the lower hemisphere. + { + u = (-c / (float)mColumns); + v = (r / (float)mRows); + } + else + { + u = (-c / (float)mColumns); + v = 1.0f + ((mRows - r) / (float)mRows); + } + + if (r != 4) z += 300; + + // And finally the vertex. + TriVertex vert; + vert = SetVertexXYZ(-pos.X, z - 1.f, pos.Y, u * 4.0f, v * 1.2f + 0.5f/*, color*/); + mVertices.Push(vert); +} diff --git a/src/r_poly_sky.h b/src/r_poly_sky.h new file mode 100644 index 000000000..dd4bd29ca --- /dev/null +++ b/src/r_poly_sky.h @@ -0,0 +1,45 @@ +/* +** Sky dome rendering +** Copyright(C) 2003-2016 Christoph Oelckers +** All rights reserved. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program. If not, see http:**www.gnu.org/licenses/ +** +** Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky. +*/ + +#pragma once + +#include "r_poly_triangle.h" + +class PolySkyDome +{ +public: + PolySkyDome(); + void Render(const TriMatrix &worldToClip); + +private: + TArray mVertices; + TArray mPrimStart; + int mRows, mColumns; + + void SkyVertex(int r, int c, bool yflip); + void CreateSkyHemisphere(bool zflip); + void CreateDome(); + void RenderRow(PolyDrawArgs &args, int row); + void RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap); + + TriVertex SetVertex(float xx, float yy, float zz, float uu = 0, float vv = 0); + TriVertex SetVertexXYZ(float xx, float yy, float zz, float uu = 0, float vv = 0); +}; diff --git a/src/r_poly_sprite.cpp b/src/r_poly_sprite.cpp new file mode 100644 index 000000000..b3a57ec47 --- /dev/null +++ b/src/r_poly_sprite.cpp @@ -0,0 +1,301 @@ +/* +** Handling drawing a sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_poly_sprite.h" +#include "r_poly.h" + +void RenderPolySprite::Render(const TriMatrix &worldToClip, AActor *thing, subsector_t *sub, uint32_t subsectorDepth) +{ + if (IsThingCulled(thing)) + return; + + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + pos.Z += thing->GetBobOffset(r_TicFracF); + + bool flipTextureX = false; + FTexture *tex = GetSpriteTexture(thing, flipTextureX); + if (tex == nullptr) + return; + DVector2 spriteScale = thing->Scale; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + + if (flipTextureX) + pos.X -= (tex->GetWidth() - tex->LeftOffset) * thingxscalemul; + else + pos.X -= tex->LeftOffset * thingxscalemul; + + //pos.Z -= tex->TopOffset * thingyscalemul; + pos.Z -= (tex->GetHeight() - tex->TopOffset) * thingyscalemul + thing->Floorclip; + + double spriteHalfWidth = thingxscalemul * tex->GetWidth() * 0.5; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + pos.X += spriteHalfWidth; + + DVector2 points[2] = + { + { pos.X - ViewSin * spriteHalfWidth, pos.Y + ViewCos * spriteHalfWidth }, + { pos.X + ViewSin * spriteHalfWidth, pos.Y - ViewCos * spriteHalfWidth } + }; + + // Is this sprite inside? (To do: clip the points) + for (int i = 0; i < 2; i++) + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + double nx = line->v1->fY() - line->v2->fY(); + double ny = line->v2->fX() - line->v1->fX(); + double d = -(line->v1->fX() * nx + line->v1->fY() * ny); + if (pos.X * nx + pos.Y * ny + d > 0.0) + return; + } + } + + //double depth = 1.0; + //visstyle_t visstyle = GetSpriteVisStyle(thing, depth); + // Rumor has it that AlterWeaponSprite needs to be called with visstyle passed in somewhere around here.. + //R_SetColorMapLight(visstyle.BaseColormap, 0, visstyle.ColormapNum << FRACBITS); + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + + std::pair offsets[4] = + { + { 0.0f, 1.0f }, + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + { 0.0f, 0.0f }, + }; + + for (int i = 0; i < 4; i++) + { + auto &p = (i == 0 || i == 3) ? points[0] : points[1]; + + vertices[i].x = (float)p.X; + vertices[i].y = (float)p.Y; + vertices[i].z = (float)(pos.Z + spriteHeight * offsets[i].second); + vertices[i].w = 1.0f; + vertices[i].varying[0] = (float)(offsets[i].first * tex->Scale.X); + vertices[i].varying[1] = (float)((1.0f - offsets[i].second) * tex->Scale.Y); + if (flipTextureX) + vertices[i].varying[0] = 1.0f - vertices[i].varying[0]; + } + + TriUniforms uniforms; + uniforms.objectToClip = worldToClip; + uniforms.light = (uint32_t)((thing->Sector->lightlevel + actualextralight) / 255.0f * 256.0f); + uniforms.flags = 0; + uniforms.subsectorDepth = subsectorDepth; + + PolyDrawArgs args; + args.uniforms = uniforms; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.clipleft = 0; + args.cliptop = 0; + args.clipright = viewwidth; + args.clipbottom = viewheight; + args.stenciltestvalue = 0; + args.stencilwritevalue = 1; + args.SetTexture(tex); + PolyTriangleDrawer::draw(args, TriDrawVariant::DrawSubsector); +} + +bool RenderPolySprite::IsThingCulled(AActor *thing) +{ + FIntCVar *cvar = thing->GetClass()->distancecheck; + if (cvar != nullptr && *cvar >= 0) + { + double dist = (thing->Pos() - ViewPos).LengthSquared(); + double check = (double)**cvar; + if (dist >= check * check) + return true; + } + + // Don't waste time projecting sprites that are definitely not visible. + if (thing == nullptr || + (thing->renderflags & RF_INVISIBLE) || + !thing->RenderStyle.IsVisible(thing->Alpha) || + !thing->IsVisibleToPlayer()) + { + return true; + } + + return false; +} + +visstyle_t RenderPolySprite::GetSpriteVisStyle(AActor *thing, double z) +{ + visstyle_t visstyle; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + int spriteshade = LIGHT2SHADE(thing->Sector->lightlevel + actualextralight); + + visstyle.RenderStyle = thing->RenderStyle; + visstyle.Alpha = float(thing->Alpha); + visstyle.ColormapNum = 0; + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + bool invertcolormap = (visstyle.RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + + if (visstyle.RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + FDynamicColormap *mybasecolormap = thing->Sector->ColorMap; + + // Sprites that are added to the scene must fade to black. + if (visstyle.RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); + } + + if (visstyle.RenderStyle.Flags & STYLEF_FadeToBlack) + { + if (invertcolormap) + { // Fade to white + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255, 255, 255), mybasecolormap->Desaturate); + invertcolormap = false; + } + else + { // Fade to black + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0, 0, 0), mybasecolormap->Desaturate); + } + } + + // get light level + if (fixedcolormap != nullptr) + { // fixed map + visstyle.BaseColormap = fixedcolormap; + visstyle.ColormapNum = 0; + } + else + { + if (invertcolormap) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); + } + if (fixedlightlev >= 0) + { + visstyle.BaseColormap = mybasecolormap; + visstyle.ColormapNum = fixedlightlev >> COLORMAPSHIFT; + } + else if (!foggy && ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT))) + { // full bright + visstyle.BaseColormap = mybasecolormap; + visstyle.ColormapNum = 0; + } + else + { // diminished light + double minz = double((2048 * 4) / double(1 << 20)); + visstyle.ColormapNum = GETPALOOKUP(r_SpriteVisibility / MAX(z, minz), spriteshade); + visstyle.BaseColormap = mybasecolormap; + } + } + + return visstyle; +} + +FTexture *RenderPolySprite::GetSpriteTexture(AActor *thing, /*out*/ bool &flipX) +{ + flipX = false; + if (thing->picnum.isValid()) + { + FTexture *tex = TexMan(thing->picnum); + if (tex->UseType == FTexture::TEX_Null) + { + return nullptr; + } + + if (tex->Rotations != 0xFFFF) + { + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[tex->Rotations]; + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + pos.Z += thing->GetBobOffset(r_TicFracF); + DAngle ang = (pos - ViewPos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + flipX = (sprframe->Flip & (1 << rot)) != 0; + tex = TexMan[sprframe->Texture[rot]]; // Do not animate the rotation + } + return tex; + } + else + { + // decide which texture to use for the sprite + int spritenum = thing->sprite; + if (spritenum >= (signed)sprites.Size() || spritenum < 0) + return nullptr; + + spritedef_t *sprdef = &sprites[spritenum]; + if (thing->frame >= sprdef->numframes) + { + // If there are no frames at all for this sprite, don't draw it. + return nullptr; + } + else + { + //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + pos.Z += thing->GetBobOffset(r_TicFracF); + DAngle ang = (pos - ViewPos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + flipX = (sprframe->Flip & (1 << rot)) != 0; + return TexMan[sprframe->Texture[rot]]; // Do not animate the rotation + } + } +} diff --git a/src/r_poly_sprite.h b/src/r_poly_sprite.h new file mode 100644 index 000000000..d60710d04 --- /dev/null +++ b/src/r_poly_sprite.h @@ -0,0 +1,37 @@ +/* +** Handling drawing a sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_poly_triangle.h" + +class RenderPolySprite +{ +public: + void Render(const TriMatrix &worldToClip, AActor *thing, subsector_t *sub, uint32_t subsectorDepth); + + static bool IsThingCulled(AActor *thing); + +private: + visstyle_t GetSpriteVisStyle(AActor *thing, double z); + FTexture *GetSpriteTexture(AActor *thing, /*out*/ bool &flipX); +}; diff --git a/src/r_poly_wall.cpp b/src/r_poly_wall.cpp new file mode 100644 index 000000000..815230052 --- /dev/null +++ b/src/r_poly_wall.cpp @@ -0,0 +1,334 @@ +/* +** Handling drawing a wall +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_poly_wall.h" +#include "r_poly.h" +#include "r_sky.h" // for skyflatnum + +bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, std::vector &translucentWallsOutput) +{ + double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1); + double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1); + double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2); + double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2); + + RenderPolyWall wall; + wall.Line = line; + wall.Colormap = frontsector->ColorMap; + wall.Masked = false; + wall.SubsectorDepth = subsectorDepth; + + if (line->backsector == nullptr) + { + if (line->sidedef) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2); + wall.TopZ = frontceilz1; + wall.BottomZ = frontfloorz1; + wall.UnpeggedCeil = frontceilz1; + wall.Texpart = side_t::mid; + wall.Render(worldToClip); + return true; + } + } + else + { + sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector; + + double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1); + double backfloorz1 = backsector->floorplane.ZatPoint(line->v1); + double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2); + double backfloorz2 = backsector->floorplane.ZatPoint(line->v2); + + double topceilz1 = frontceilz1; + double topceilz2 = frontceilz2; + double topfloorz1 = MIN(backceilz1, frontceilz1); + double topfloorz2 = MIN(backceilz2, frontceilz2); + double bottomceilz1 = MAX(frontfloorz1, backfloorz1); + double bottomceilz2 = MAX(frontfloorz2, backfloorz2); + double bottomfloorz1 = frontfloorz1; + double bottomfloorz2 = frontfloorz2; + double middleceilz1 = topfloorz1; + double middleceilz2 = topfloorz2; + double middlefloorz1 = MIN(bottomceilz1, middleceilz1); + double middlefloorz2 = MIN(bottomceilz2, middleceilz2); + + bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum; + + if ((topceilz1 > topfloorz1 || topceilz2 > topfloorz2) && line->sidedef && !bothSkyCeiling) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), topceilz1, topfloorz1, topceilz2, topfloorz2); + wall.TopZ = topceilz1; + wall.BottomZ = topfloorz1; + wall.UnpeggedCeil = topceilz1; + wall.Texpart = side_t::top; + wall.Render(worldToClip); + } + + if ((bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && line->sidedef) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), bottomceilz1, bottomfloorz2, bottomceilz2, bottomfloorz2); + wall.TopZ = bottomceilz1; + wall.BottomZ = bottomfloorz2; + wall.UnpeggedCeil = topceilz1; + wall.Texpart = side_t::bottom; + wall.Render(worldToClip); + } + + if (line->sidedef) + { + FTexture *midtex = TexMan(line->sidedef->GetTexture(side_t::mid), true); + if (midtex && midtex->UseType != FTexture::TEX_Null) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), middleceilz1, middlefloorz1, middleceilz2, middlefloorz2); + wall.TopZ = middleceilz1; + wall.BottomZ = middlefloorz1; + wall.UnpeggedCeil = topceilz1; + wall.Texpart = side_t::mid; + wall.Masked = true; + translucentWallsOutput.push_back({ wall }); + } + } + } + return false; +} + +void RenderPolyWall::Render(const TriMatrix &worldToClip) +{ + FTexture *tex = GetTexture(); + if (!tex) + return; + + PolyWallTextureCoords texcoords(tex, Line, Texpart, TopZ, BottomZ, UnpeggedCeil); + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + vertices[0].x = (float)v1.X; + vertices[0].y = (float)v1.Y; + vertices[0].z = (float)ceil1; + vertices[0].w = 1.0f; + vertices[0].varying[0] = (float)texcoords.u1; + vertices[0].varying[1] = (float)texcoords.v1; + + vertices[1].x = (float)v2.X; + vertices[1].y = (float)v2.Y; + vertices[1].z = (float)ceil2; + vertices[1].w = 1.0f; + vertices[1].varying[0] = (float)texcoords.u2; + vertices[1].varying[1] = (float)texcoords.v1; + + vertices[2].x = (float)v2.X; + vertices[2].y = (float)v2.Y; + vertices[2].z = (float)floor2; + vertices[2].w = 1.0f; + vertices[2].varying[0] = (float)texcoords.u2; + vertices[2].varying[1] = (float)texcoords.v2; + + vertices[3].x = (float)v1.X; + vertices[3].y = (float)v1.Y; + vertices[3].z = (float)floor1; + vertices[3].w = 1.0f; + vertices[3].varying[0] = (float)texcoords.u1; + vertices[3].varying[1] = (float)texcoords.v2; + + TriUniforms uniforms; + uniforms.objectToClip = worldToClip; + uniforms.light = (uint32_t)(GetLightLevel() / 255.0f * 256.0f); + uniforms.flags = 0; + uniforms.subsectorDepth = SubsectorDepth; + + PolyDrawArgs args; + args.uniforms = uniforms; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.clipleft = 0; + args.cliptop = 0; + args.clipright = viewwidth; + args.clipbottom = viewheight; + args.stenciltestvalue = 0; + args.stencilwritevalue = 1; + args.SetTexture(tex); + + if (!Masked) + { + PolyTriangleDrawer::draw(args, TriDrawVariant::Draw); + PolyTriangleDrawer::draw(args, TriDrawVariant::Stencil); + } + else + { + PolyTriangleDrawer::draw(args, TriDrawVariant::DrawSubsector); + } +} + +FTexture *RenderPolyWall::GetTexture() +{ + FTexture *tex = TexMan(Line->sidedef->GetTexture(Texpart), true); + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + return nullptr; + else + return tex; +} + +int RenderPolyWall::GetLightLevel() +{ + if (fixedlightlev >= 0 || fixedcolormap) + { + return 255; + } + else + { + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + return Line->sidedef->GetLightLevel(foggy, Line->frontsector->lightlevel) + actualextralight; + } +} + +///////////////////////////////////////////////////////////////////////////// + +PolyWallTextureCoords::PolyWallTextureCoords(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil) +{ + CalcU(tex, line, texpart); + CalcV(tex, line, texpart, topz, bottomz, unpeggedceil); +} + +void PolyWallTextureCoords::CalcU(FTexture *tex, const seg_t *line, side_t::ETexpart texpart) +{ + double lineLength = line->sidedef->TexelLength; + double lineStart = 0.0; + + bool entireSegment = ((line->linedef->v1 == line->v1) && (line->linedef->v2 == line->v2) || (line->linedef->v2 == line->v1) && (line->linedef->v1 == line->v2)); + if (!entireSegment) + { + lineLength = (line->v2->fPos() - line->v1->fPos()).Length(); + lineStart = (line->v1->fPos() - line->linedef->v1->fPos()).Length(); + } + + int texWidth = tex->GetWidth(); + double uscale = line->sidedef->GetTextureXScale(texpart) * tex->Scale.X; + u1 = lineStart + line->sidedef->GetTextureXOffset(texpart); + u2 = u1 + lineLength; + u1 *= uscale; + u2 *= uscale; + u1 /= texWidth; + u2 /= texWidth; +} + +void PolyWallTextureCoords::CalcV(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil) +{ + double vscale = line->sidedef->GetTextureYScale(texpart) * tex->Scale.Y; + + double yoffset = line->sidedef->GetTextureYOffset(texpart); + if (tex->bWorldPanning) + yoffset *= vscale; + + switch (texpart) + { + default: + case side_t::mid: + CalcVMidPart(tex, line, topz, bottomz, vscale, yoffset); + break; + case side_t::top: + CalcVTopPart(tex, line, topz, bottomz, vscale, yoffset); + break; + case side_t::bottom: + CalcVBottomPart(tex, line, topz, bottomz, unpeggedceil, vscale, yoffset); + break; + } + + int texHeight = tex->GetHeight(); + v1 /= texHeight; + v2 /= texHeight; +} + +void PolyWallTextureCoords::CalcVTopPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset) +{ + bool pegged = (line->linedef->flags & ML_DONTPEGTOP) == 0; + if (pegged) // bottom to top + { + int texHeight = tex->GetHeight(); + v1 = -yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + v1 = texHeight - v1; + v2 = texHeight - v2; + std::swap(v1, v2); + } + else // top to bottom + { + v1 = yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } +} + +void PolyWallTextureCoords::CalcVMidPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset) +{ + bool pegged = (line->linedef->flags & ML_DONTPEGBOTTOM) == 0; + if (pegged) // top to bottom + { + v1 = yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } + else // bottom to top + { + int texHeight = tex->GetHeight(); + v1 = yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + v1 = texHeight - v1; + v2 = texHeight - v2; + std::swap(v1, v2); + } +} + +void PolyWallTextureCoords::CalcVBottomPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset) +{ + bool pegged = (line->linedef->flags & ML_DONTPEGBOTTOM) == 0; + if (pegged) // top to bottom + { + v1 = yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } + else + { + v1 = yoffset + (unpeggedceil - topz); + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } +} diff --git a/src/r_poly_wall.h b/src/r_poly_wall.h new file mode 100644 index 000000000..c215bb611 --- /dev/null +++ b/src/r_poly_wall.h @@ -0,0 +1,82 @@ +/* +** Handling drawing a wall +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_poly_triangle.h" + +class PolyTranslucentObject; + +class RenderPolyWall +{ +public: + static bool RenderLine(const TriMatrix &worldToClip, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, std::vector &translucentWallsOutput); + + void Render(const TriMatrix &worldToClip); + + void SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2) + { + this->v1 = v1; + this->v2 = v2; + this->ceil1 = ceil1; + this->floor1 = floor1; + this->ceil2 = ceil2; + this->floor2 = floor2; + } + + DVector2 v1; + DVector2 v2; + double ceil1 = 0.0; + double floor1 = 0.0; + double ceil2 = 0.0; + double floor2 = 0.0; + + const seg_t *Line = nullptr; + side_t::ETexpart Texpart = side_t::mid; + double TopZ = 0.0; + double BottomZ = 0.0; + double UnpeggedCeil = 0.0; + FSWColormap *Colormap = nullptr; + bool Masked = false; + uint32_t SubsectorDepth = 0; + +private: + FTexture *GetTexture(); + int GetLightLevel(); +}; + +// Texture coordinates for a wall +class PolyWallTextureCoords +{ +public: + PolyWallTextureCoords(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); + + double u1, u2; + double v1, v2; + +private: + void CalcU(FTexture *tex, const seg_t *line, side_t::ETexpart texpart); + void CalcV(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); + void CalcVTopPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset); + void CalcVMidPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset); + void CalcVBottomPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset); +}; diff --git a/src/r_poly_wallsprite.cpp b/src/r_poly_wallsprite.cpp new file mode 100644 index 000000000..86ddd52a9 --- /dev/null +++ b/src/r_poly_wallsprite.cpp @@ -0,0 +1,35 @@ +/* +** Handling drawing a sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_poly_wallsprite.h" +#include "r_poly.h" + +void RenderPolyWallSprite::Render(AActor *thing, subsector_t *sub, uint32_t subsectorDepth) +{ + if (RenderPolySprite::IsThingCulled(thing)) + return; +} diff --git a/src/r_poly_wallsprite.h b/src/r_poly_wallsprite.h new file mode 100644 index 000000000..ce6917e58 --- /dev/null +++ b/src/r_poly_wallsprite.h @@ -0,0 +1,31 @@ +/* +** Handling drawing a wall sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_triangle.h" + +class RenderPolyWallSprite +{ +public: + void Render(AActor *thing, subsector_t *sub, uint32_t subsectorDepth); +}; diff --git a/src/r_swrenderer.cpp b/src/r_swrenderer.cpp index 97a207652..3ddfcb5f2 100644 --- a/src/r_swrenderer.cpp +++ b/src/r_swrenderer.cpp @@ -44,8 +44,11 @@ #include "r_data/voxels.h" #include "r_draw_rgba.h" #include "r_compiler/llvmdrawers.h" +#include "r_poly.h" EXTERN_CVAR(Bool, r_shadercolormaps) +EXTERN_CVAR(Bool, r_newrenderer) // [SP] dpJudas's new renderer +EXTERN_CVAR(Float, maxviewpitch) // [SP] CVAR from GZDoom void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio); void R_SetupColormap(player_t *); @@ -239,7 +242,14 @@ void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int wi void FSoftwareRenderer::DrawRemainingPlayerSprites() { - R_DrawRemainingPlayerSprites(); + if (!r_newrenderer) + { + R_DrawRemainingPlayerSprites(); + } + else + { + RenderPolyScene::Instance()->RenderRemainingPlayerSprites(); + } } //=========================================================================== @@ -250,9 +260,6 @@ void FSoftwareRenderer::DrawRemainingPlayerSprites() #define MAX_DN_ANGLE 56 // Max looking down angle #define MAX_UP_ANGLE 32 // Max looking up angle -EXTERN_CVAR(Bool, r_newrenderer) // [SP] dpJudas's new renderer -EXTERN_CVAR(Float, maxviewpitch) // [SP] CVAR from GZDoom - int FSoftwareRenderer::GetMaxViewPitch(bool down) { return (r_newrenderer) ? int(maxviewpitch) : (down ? MAX_DN_ANGLE : MAX_UP_ANGLE);