diff --git a/src/polyrenderer/scene/poly_decal.cpp b/src/polyrenderer/scene/poly_decal.cpp index d619c13f5..6ffd24439 100644 --- a/src/polyrenderer/scene/poly_decal.cpp +++ b/src/polyrenderer/scene/poly_decal.cpp @@ -52,92 +52,132 @@ void RenderPolyDecal::Render(const TriMatrix &worldToClip, const PolyClipPlane & if (tex == nullptr || tex->UseType == FTexture::TEX_Null) return; - double edge_right = tex->GetWidth(); - double edge_left = tex->LeftOffset; - edge_right = (edge_right - edge_left) * decal->ScaleX; - edge_left *= decal->ScaleX; + sector_t *front, *back; + GetDecalSectors(decal, line, &front, &back); - double dcx, dcy; - decal->GetXY(line->sidedef, dcx, dcy); - DVector2 decal_pos = { dcx, dcy }; + // Calculate unclipped position and UV coordinates + + double edge_left = tex->LeftOffset * decal->ScaleX; + double edge_right = (tex->GetWidth() - tex->LeftOffset) * decal->ScaleX; DVector2 angvec = (line->v2->fPos() - line->v1->fPos()).Unit(); DVector2 normal = { angvec.Y, -angvec.X }; - decal_pos += normal; - + double dcx, dcy; + decal->GetXY(line->sidedef, dcx, dcy); + DVector2 decal_pos = DVector2(dcx, dcy) + normal; DVector2 decal_left = decal_pos - edge_left * angvec; DVector2 decal_right = decal_pos + edge_right * angvec; - // Determine actor z - double zpos = decal->Z; - sector_t *back = (line->backsector != nullptr) ? line->backsector : line->frontsector; + bool flipTextureX = (decal->RenderFlags & RF_XFLIP) == RF_XFLIP; + double u_left = flipTextureX ? 1.0 : 0.0; + double u_right = flipTextureX ? 1.0 - tex->Scale.X : tex->Scale.X; + double u_unit = (u_right - u_left) / (edge_left + edge_right); - // for 3d-floor segments use the model sector as reference - sector_t *front; - if ((decal->RenderFlags&RF_CLIPMASK) == RF_CLIPMID) front = decal->Sector; - else front = line->frontsector; + double zpos = GetDecalZ(decal, line, front, back); + double spriteHeight = decal->ScaleY / tex->Scale.Y * tex->GetHeight(); + double ztop = zpos + spriteHeight - spriteHeight * 0.5; + double zbottom = zpos - spriteHeight * 0.5; - switch (decal->RenderFlags & RF_RELMASK) + double v_top = 0.0; + double v_bottom = tex->Scale.Y; + double v_unit = (v_bottom - v_top) / (zbottom - ztop); + + // Clip decal to wall part + + double walltopz, wallbottomz; + GetWallZ(decal, line, front, back, walltopz, wallbottomz); + + double clip_left_v1 = (decal_left - line->v1->fPos()) | angvec; + double clip_right_v1 = (decal_right - line->v1->fPos()) | angvec; + double clip_left_v2 = (decal_left - line->v2->fPos()) | angvec; + double clip_right_v2 = (decal_right - line->v2->fPos()) | angvec; + + if ((clip_left_v1 <= 0.0 && clip_right_v1 <= 0.0) || (clip_left_v2 >= 0.0 && clip_right_v2 >= 0.0)) + return; + + if (clip_left_v1 < 0.0) { - default: - zpos = decal->Z; - break; - case RF_RELUPPER: - if (line->linedef->flags & ML_DONTPEGTOP) - zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); - else - zpos = decal->Z + back->GetPlaneTexZ(sector_t::ceiling); - break; - case RF_RELLOWER: - if (line->linedef->flags & ML_DONTPEGBOTTOM) - zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); - else - zpos = decal->Z + back->GetPlaneTexZ(sector_t::floor); - break; - case RF_RELMID: - if (line->linedef->flags & ML_DONTPEGBOTTOM) - zpos = decal->Z + front->GetPlaneTexZ(sector_t::floor); - else - zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + decal_left -= angvec * clip_left_v1; + u_left -= u_unit * clip_left_v1; + } + if (clip_right_v1 < 0.0) + { + decal_right -= angvec * clip_right_v1; + u_right -= u_unit * clip_right_v1; + } + if (clip_left_v2 > 0.0) + { + decal_left -= angvec * clip_left_v2; + u_left -= u_unit * clip_left_v2; + } + if (clip_right_v2 > 0.0) + { + decal_right -= angvec * clip_right_v2; + u_right -= u_unit * clip_right_v2; } - DVector2 spriteScale = { decal->ScaleX, decal->ScaleY }; - double thingxscalemul = spriteScale.X / tex->Scale.X; - double thingyscalemul = spriteScale.Y / tex->Scale.Y; - double spriteHeight = thingyscalemul * tex->GetHeight(); + double clip_top_floor = ztop - wallbottomz; + double clip_bottom_floor = zbottom - wallbottomz; + double clip_top_ceiling = ztop - walltopz; + double clip_bottom_ceiling = zbottom - walltopz; - bool flipTextureX = (decal->RenderFlags & RF_XFLIP) == RF_XFLIP; + if ((clip_top_floor <= 0.0 && clip_bottom_floor <= 0.0) || (clip_top_ceiling >= 0.0 && clip_bottom_ceiling >= 0.0)) + return; - DVector2 points[2] = { decal_left, decal_right }; + if (clip_top_floor < 0.0) + { + ztop -= clip_top_floor; + v_top -= v_unit * clip_top_floor; + } + if (clip_bottom_floor < 0.0) + { + zbottom -= clip_bottom_floor; + v_bottom -= v_unit * clip_bottom_floor; + } + if (clip_top_ceiling > 0.0) + { + ztop -= clip_top_ceiling; + v_top -= v_unit * clip_top_ceiling; + } + if (clip_bottom_ceiling > 0.0) + { + zbottom -= clip_bottom_ceiling; + v_bottom -= v_unit * clip_bottom_ceiling; + } + + // Generate vertices for the decal TriVertex *vertices = PolyRenderer::Instance()->FrameMemory.AllocMemory(4); + vertices[0].x = (float)decal_left.X; + vertices[0].y = (float)decal_left.Y; + vertices[0].z = (float)ztop; + vertices[0].w = 1.0f; + vertices[0].u = (float)u_left; + vertices[0].v = (float)v_top; + vertices[1].x = (float)decal_right.X; + vertices[1].y = (float)decal_right.Y; + vertices[1].z = (float)ztop; + vertices[1].w = 1.0f; + vertices[1].u = (float)u_right; + vertices[1].v = (float)v_top; + vertices[2].x = (float)decal_right.X; + vertices[2].y = (float)decal_right.Y; + vertices[2].z = (float)zbottom; + vertices[2].w = 1.0f; + vertices[2].u = (float)u_right; + vertices[2].v = (float)v_bottom; + vertices[3].x = (float)decal_left.X; + vertices[3].y = (float)decal_left.Y; + vertices[3].z = (float)zbottom; + vertices[3].w = 1.0f; + vertices[3].u = (float)u_left; + vertices[3].v = (float)v_bottom; + + // Light calculations bool foggy = false; int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.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)(zpos + spriteHeight * offsets[i].second - spriteHeight * 0.5); - vertices[i].w = 1.0f; - vertices[i].u = (float)(offsets[i].first * tex->Scale.X); - vertices[i].v = (float)((1.0f - offsets[i].second) * tex->Scale.Y); - if (flipTextureX) - vertices[i].u = 1.0f - vertices[i].u; - } - bool fullbrightSprite = (decal->RenderFlags & RF_FULLBRIGHT) == RF_FULLBRIGHT; int lightlevel = fullbrightSprite ? 255 : front->lightlevel + actualextralight; @@ -155,3 +195,90 @@ void RenderPolyDecal::Render(const TriMatrix &worldToClip, const PolyClipPlane & args.SetWriteDepth(false); args.DrawArray(vertices, 4, PolyDrawMode::TriangleFan); } + +void RenderPolyDecal::GetDecalSectors(DBaseDecal *decal, const seg_t *line, sector_t **front, sector_t **back) +{ + // for 3d-floor segments use the model sector as reference + if ((decal->RenderFlags&RF_CLIPMASK) == RF_CLIPMID) + *front = decal->Sector; + else + *front = line->frontsector; + + *back = (line->backsector != nullptr) ? line->backsector : line->frontsector; +} + +double RenderPolyDecal::GetDecalZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back) +{ + switch (decal->RenderFlags & RF_RELMASK) + { + default: + return decal->Z; + case RF_RELUPPER: + if (line->linedef->flags & ML_DONTPEGTOP) + return decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + else + return decal->Z + back->GetPlaneTexZ(sector_t::ceiling); + case RF_RELLOWER: + if (line->linedef->flags & ML_DONTPEGBOTTOM) + return decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + else + return decal->Z + back->GetPlaneTexZ(sector_t::floor); + break; + case RF_RELMID: + if (line->linedef->flags & ML_DONTPEGBOTTOM) + return decal->Z + front->GetPlaneTexZ(sector_t::floor); + else + return decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } +} + +void RenderPolyDecal::GetWallZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back, double &walltopz, double &wallbottomz) +{ + double frontceilz1 = front->ceilingplane.ZatPoint(line->v1); + double frontfloorz1 = front->floorplane.ZatPoint(line->v1); + double frontceilz2 = front->ceilingplane.ZatPoint(line->v2); + double frontfloorz2 = front->floorplane.ZatPoint(line->v2); + if (back == nullptr) + { + walltopz = MAX(frontceilz1, frontceilz2); + wallbottomz = MIN(frontfloorz1, frontfloorz2); + } + else + { + double backceilz1 = back->ceilingplane.ZatPoint(line->v1); + double backfloorz1 = back->floorplane.ZatPoint(line->v1); + double backceilz2 = back->ceilingplane.ZatPoint(line->v2); + double backfloorz2 = back->floorplane.ZatPoint(line->v2); + double topceilz1 = frontceilz1; + double topceilz2 = frontceilz2; + double topfloorz1 = MAX(MIN(backceilz1, frontceilz1), frontfloorz1); + double topfloorz2 = MAX(MIN(backceilz2, frontceilz2), frontfloorz2); + double bottomceilz1 = MIN(MAX(frontfloorz1, backfloorz1), frontceilz1); + double bottomceilz2 = MIN(MAX(frontfloorz2, backfloorz2), frontceilz2); + double bottomfloorz1 = frontfloorz1; + double bottomfloorz2 = frontfloorz2; + double middleceilz1 = topfloorz1; + double middleceilz2 = topfloorz2; + double middlefloorz1 = MIN(bottomceilz1, middleceilz1); + double middlefloorz2 = MIN(bottomceilz2, middleceilz2); + + switch (decal->RenderFlags & RF_RELMASK) + { + default: + walltopz = MAX(frontceilz1, frontceilz2); + wallbottomz = MIN(frontfloorz1, frontfloorz2); + break; + case RF_RELUPPER: + walltopz = MAX(topceilz1, topceilz2); + wallbottomz = MIN(topfloorz1, topfloorz2); + break; + case RF_RELLOWER: + walltopz = MAX(bottomceilz1, bottomceilz2); + wallbottomz = MIN(bottomfloorz1, bottomfloorz2); + break; + case RF_RELMID: + walltopz = MAX(middleceilz1, middleceilz2); + wallbottomz = MIN(middlefloorz1, middlefloorz2); + } + } +} diff --git a/src/polyrenderer/scene/poly_decal.h b/src/polyrenderer/scene/poly_decal.h index 0cab330ca..b10e8a6b0 100644 --- a/src/polyrenderer/scene/poly_decal.h +++ b/src/polyrenderer/scene/poly_decal.h @@ -31,4 +31,8 @@ public: private: void Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, DBaseDecal *decal, const seg_t *line, uint32_t stencilValue); + + void GetDecalSectors(DBaseDecal *decal, const seg_t *line, sector_t **front, sector_t **back); + double GetDecalZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back); + void GetWallZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back, double &walltopz, double &wallbottomz); };