mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-25 05:21:02 +00:00
- Clip decals to the wall part
This commit is contained in:
parent
948e50e458
commit
45388d8a31
2 changed files with 197 additions and 66 deletions
|
@ -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<TriVertex>(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<float, float> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue