- change ProjectedWallTexcoords to use gradients for its texture coordinate calculations

- change SpriteDrawerArgs to draw a full sprite instead of one column at a time
- add r_noaccel cvar to allow forced software rendering of the psprites (useful for debugging and also one person on the forum actually requested this feature)
- remove FWallTmapVals and calculate texture coordinates directly from FWallCoords
- move portal clipping out of the inner sprite drawing loop
This commit is contained in:
Magnus Norddahl 2019-11-20 04:50:24 +01:00
parent 1085287af1
commit 236b476933
16 changed files with 679 additions and 848 deletions

View file

@ -81,7 +81,7 @@ namespace swrenderer
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
return;
if (WallC.Init(Thread, pt1, pt2, 32.0 / (1 << 12)))
if (WallC.Init(Thread, pt1, pt2, line))
return;
RenderPortal *renderportal = Thread->Portal.get();

View file

@ -87,7 +87,7 @@ namespace swrenderer
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
return;
if (WallC.Init(Thread, pt1, pt2, 32.0 / (1 << 12)))
if (WallC.Init(Thread, pt1, pt2, line))
return;
RenderPortal *renderportal = Thread->Portal.get();
@ -108,8 +108,6 @@ namespace swrenderer
if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && P_ClipLineToPortal(line->linedef, renderportal->CurrentPortal->dst, Thread->Viewport->viewpoint.Pos))
return;
WallT.InitFromLine(Thread, line);
mFrontCeilingZ1 = mFrontSector->ceilingplane.ZatPoint(line->v1);
mFrontFloorZ1 = mFrontSector->floorplane.ZatPoint(line->v1);
mFrontCeilingZ2 = mFrontSector->ceilingplane.ZatPoint(line->v2);
@ -306,7 +304,6 @@ namespace swrenderer
draw_segment->drawsegclip.CurrentPortalUniq = renderportal->CurrentPortalUniq;
draw_segment->WallC = WallC;
draw_segment->tmapvals = WallT;
draw_segment->x1 = start;
draw_segment->x2 = stop;
draw_segment->curline = mLineSegment;
@ -396,7 +393,7 @@ namespace swrenderer
if (pic)
{
draw_segment->SetHasTranslucentMidTexture();
draw_segment->texcoords.ProjectTranslucent(Thread->Viewport.get(), mFrontSector, mBackSector, mLineSegment, WallC.sx1, WallC.sx2, WallT, pic);
draw_segment->texcoords.ProjectTranslucent(Thread->Viewport.get(), mFrontSector, mBackSector, mLineSegment, WallC, pic);
draw_segment->drawsegclip.silhouette |= SIL_TOP | SIL_BOTTOM;
}
}
@ -878,7 +875,7 @@ namespace swrenderer
if (!viewactive) return;
ProjectedWallTexcoords texcoords;
texcoords.ProjectTop(Thread->Viewport.get(), mFrontSector, mBackSector, mLineSegment, WallC.sx1, WallC.sx2, WallT, mTopTexture);
texcoords.ProjectTop(Thread->Viewport.get(), mFrontSector, mBackSector, mLineSegment, WallC, mTopTexture);
RenderWallPart renderWallpart(Thread);
renderWallpart.Render(mFrontSector, mLineSegment, WallC, mTopTexture, x1, x2, walltop.ScreenY, wallupper.ScreenY, texcoords, false, false, OPAQUE);
@ -890,7 +887,7 @@ namespace swrenderer
if (!viewactive) return;
ProjectedWallTexcoords texcoords;
texcoords.ProjectMid(Thread->Viewport.get(), mFrontSector, mLineSegment, WallC.sx1, WallC.sx2, WallT, mMiddleTexture);
texcoords.ProjectMid(Thread->Viewport.get(), mFrontSector, mLineSegment, WallC, mMiddleTexture);
RenderWallPart renderWallpart(Thread);
renderWallpart.Render(mFrontSector, mLineSegment, WallC, mMiddleTexture, x1, x2, walltop.ScreenY, wallbottom.ScreenY, texcoords, false, false, OPAQUE);
@ -903,85 +900,9 @@ namespace swrenderer
if (!viewactive) return;
ProjectedWallTexcoords texcoords;
texcoords.ProjectBottom(Thread->Viewport.get(), mFrontSector, mBackSector, mLineSegment, WallC.sx1, WallC.sx2, WallT, mBottomTexture);
texcoords.ProjectBottom(Thread->Viewport.get(), mFrontSector, mBackSector, mLineSegment, WallC, mBottomTexture);
RenderWallPart renderWallpart(Thread);
renderWallpart.Render(mFrontSector, mLineSegment, WallC, mBottomTexture, x1, x2, walllower.ScreenY, wallbottom.ScreenY, texcoords, false, false, OPAQUE);
}
////////////////////////////////////////////////////////////////////////////
// Transform and clip coordinates. Returns true if it was clipped away
bool FWallCoords::Init(RenderThread *thread, const DVector2 &pt1, const DVector2 &pt2, double too_close)
{
auto viewport = thread->Viewport.get();
RenderPortal *renderportal = thread->Portal.get();
tleft.X = float(pt1.X * viewport->viewpoint.Sin - pt1.Y * viewport->viewpoint.Cos);
tright.X = float(pt2.X * viewport->viewpoint.Sin - pt2.Y * viewport->viewpoint.Cos);
tleft.Y = float(pt1.X * viewport->viewpoint.TanCos + pt1.Y * viewport->viewpoint.TanSin);
tright.Y = float(pt2.X * viewport->viewpoint.TanCos + pt2.Y * viewport->viewpoint.TanSin);
if (renderportal->MirrorFlags & RF_XFLIP)
{
float t = -tleft.X;
tleft.X = -tright.X;
tright.X = t;
swapvalues(tleft.Y, tright.Y);
}
float fsx1, fsz1, fsx2, fsz2;
if (tleft.X >= -tleft.Y)
{
if (tleft.X > tleft.Y) return true; // left edge is off the right side
if (tleft.Y == 0) return true;
fsx1 = viewport->CenterX + tleft.X * viewport->CenterX / tleft.Y;
fsz1 = tleft.Y;
}
else
{
if (tright.X < -tright.Y) return true; // wall is off the left side
float den = tleft.X - tright.X - tright.Y + tleft.Y;
if (den == 0) return true;
fsx1 = 0;
fsz1 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X + tleft.Y) / den;
}
if (fsz1 < too_close)
return true;
if (tright.X <= tright.Y)
{
if (tright.X < -tright.Y) return true; // right edge is off the left side
if (tright.Y == 0) return true;
fsx2 = viewport->CenterX + tright.X * viewport->CenterX / tright.Y;
fsz2 = tright.Y;
}
else
{
if (tleft.X > tleft.Y) return true; // wall is off the right side
float den = tright.Y - tleft.Y - tright.X + tleft.X;
if (den == 0) return true;
fsx2 = viewwidth;
fsz2 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X - tleft.Y) / den;
}
if (fsz2 < too_close)
return true;
sx1 = xs_RoundToInt(fsx1);
sx2 = xs_RoundToInt(fsx2);
float delta = fsx2 - fsx1;
float t1 = (sx1 + 0.5f - fsx1) / delta;
float t2 = (sx2 + 0.5f - fsx1) / delta;
float invZ1 = 1.0f / fsz1;
float invZ2 = 1.0f / fsz2;
sz1 = 1.0f / (invZ1 * (1.0f - t1) + invZ2 * t1);
sz2 = 1.0f / (invZ1 * (1.0f - t2) + invZ2 * t2);
return sx2 <= sx1;
}
}

View file

@ -39,17 +39,6 @@ namespace swrenderer
class RenderThread;
struct VisiblePlane;
struct FWallCoords
{
FVector2 tleft; // coords at left of wall in view space rx1,ry1
FVector2 tright; // coords at right of wall in view space rx2,ry2
float sz1, sz2; // depth at left, right of wall in screen space yb1,yb2
short sx1, sx2; // x coords at left, right of wall in screen space xb1,xb2
bool Init(RenderThread *thread, const DVector2 &pt1, const DVector2 &pt2, double too_close);
};
class SWRenderLine : VisibleSegmentRenderer
{
public:
@ -107,7 +96,6 @@ namespace swrenderer
bool mDoorClosed;
FWallCoords WallC;
FWallTmapVals WallT;
// Wall segment variables:

View file

@ -218,7 +218,7 @@ namespace swrenderer
walllower.ClipBottom(x1, x2, ds->drawsegclip);
ProjectedWallTexcoords walltexcoords;
walltexcoords.Project3DFloor(Thread->Viewport.get(), rover, curline, ds->WallC.sx1, ds->WallC.sx2, ds->tmapvals, rw_pic);
walltexcoords.Project3DFloor(Thread->Viewport.get(), rover, curline, ds->WallC, rw_pic);
RenderWallPart renderWallpart(Thread);
renderWallpart.Render(lightsector, curline, ds->WallC, rw_pic, x1, x2, wallupper.ScreenY, walllower.ScreenY, walltexcoords, true, (rover->flags & FF_ADDITIVETRANS) != 0, Alpha);

View file

@ -120,16 +120,117 @@ namespace swrenderer
ProcessNormalWall(up, dwal, texcoords);
}
void RenderWallPart::ProcessNormalWall(const short *uwal, const short *dwal, const ProjectedWallTexcoords& texcoords)
static void DrawWallColumn32(RenderThread* thread, WallDrawerArgs& drawerargs, int x, int y1, int y2, uint32_t texelX, uint32_t texelY, uint32_t texelStepX, uint32_t texelStepY, FSoftwareTexture* pic, int texwidth, int texheight)
{
int fracbits = 32 - pic->GetHeightBits();
if (fracbits == 32)
{ // Hack for one pixel tall textures
fracbits = 0;
double xmagnitude = fabs(static_cast<int32_t>(texelStepX) * (1.0 / 0x1'0000'0000LL));
double ymagnitude = fabs(static_cast<int32_t>(texelStepY) * (1.0 / 0x1'0000'0000LL));
double magnitude = MAX(ymagnitude, xmagnitude);
double min_lod = -1000.0;
double lod = MAX(log2(magnitude) + r_lod_bias, min_lod);
bool magnifying = lod < 0.0f;
int mipmap_offset = 0;
int mip_width = texwidth;
int mip_height = texheight;
if (r_mipmap && pic->Mipmapped() && mip_width > 1 && mip_height > 1)
{
int level = (int)lod;
while (level > 0 && mip_width > 1 && mip_height > 1)
{
mipmap_offset += mip_width * mip_height;
level--;
mip_width = MAX(mip_width >> 1, 1);
mip_height = MAX(mip_height >> 1, 1);
}
}
const uint32_t* pixels = pic->GetPixelsBgra() + mipmap_offset;
fixed_t xxoffset = (texelX >> 16) * mip_width;
const uint8_t* source;
const uint8_t* source2;
uint32_t texturefracx;
bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter);
if (filter_nearest)
{
int tx = (xxoffset >> FRACBITS) % mip_width;
source = (uint8_t*)(pixels + tx * mip_height);
source2 = nullptr;
texturefracx = 0;
}
else
{
xxoffset -= FRACUNIT / 2;
int tx0 = (xxoffset >> FRACBITS) % mip_width;
if (tx0 < 0)
tx0 += mip_width;
int tx1 = (tx0 + 1) % mip_width;
source = (uint8_t*)(pixels + tx0 * mip_height);
source2 = (uint8_t*)(pixels + tx1 * mip_height);
texturefracx = (xxoffset >> (FRACBITS - 4)) & 15;
}
int count = y2 - y1;
drawerargs.SetDest(thread->Viewport.get(), x, y1);
drawerargs.SetCount(count);
drawerargs.SetTexture(source, source2, mip_height);
drawerargs.SetTextureUPos(texturefracx);
drawerargs.SetTextureVPos(texelY);
drawerargs.SetTextureVStep(texelStepY);
drawerargs.DrawColumn(thread);
}
static void DrawWallColumn8(RenderThread* thread, WallDrawerArgs& drawerargs, int x, int y1, int y2, uint32_t texelX, uint32_t texelY, uint32_t texelStepY, FSoftwareTexture* pic, int texwidth, int texheight, int fracbits, uint32_t uv_max)
{
texelY = (static_cast<uint64_t>(texelY)* texheight) >> (32 - fracbits);
texelStepY = (static_cast<uint64_t>(texelStepY)* texheight) >> (32 - fracbits);
const uint8_t* pixels = pic->GetColumn(DefaultRenderStyle(), ((texelX >> 16)* texwidth) >> 16, nullptr);
drawerargs.SetTexture(pixels, nullptr, texheight);
drawerargs.SetTextureVStep(texelStepY);
if (uv_max == 0 || texelStepY == 0) // power of two
{
int count = y2 - y1;
drawerargs.SetDest(thread->Viewport.get(), x, y1);
drawerargs.SetCount(count);
drawerargs.SetTextureVPos(texelY);
drawerargs.DrawColumn(thread);
}
else
{
uint32_t left = y2 - y1;
int y = y1;
while (left > 0)
{
uint32_t available = uv_max - texelY;
uint32_t next_uv_wrap = available / texelStepY;
if (available % texelStepY != 0)
next_uv_wrap++;
uint32_t count = MIN(left, next_uv_wrap);
drawerargs.SetDest(thread->Viewport.get(), x, y);
drawerargs.SetCount(count);
drawerargs.SetTextureVPos(texelY);
drawerargs.DrawColumn(thread);
y += count;
left -= count;
texelY += texelStepY * count;
if (texelY >= uv_max)
texelY -= uv_max;
}
}
}
void RenderWallPart::ProcessNormalWall(const short* uwal, const short* dwal, const ProjectedWallTexcoords& texcoords)
{
CameraLight* cameraLight = CameraLight::Instance();
RenderViewport* viewport = Thread->Viewport.get();
WallDrawerArgs drawerargs;
drawerargs.SetTextureFracBits(Thread->Viewport->RenderTarget->IsBgra() ? FRACBITS : fracbits);
// Textures that aren't masked can use the faster opaque drawer
if (!pic->GetTexture()->isMasked() && mask && alpha >= OPAQUE && !additive)
@ -141,13 +242,9 @@ namespace swrenderer
drawerargs.SetStyle(mask, additive, alpha, mLight.GetBaseColormap());
}
RenderViewport *viewport = Thread->Viewport.get();
CameraLight *cameraLight = CameraLight::Instance();
bool fixed = (cameraLight->FixedColormap() || cameraLight->FixedLightLevel() >= 0);
bool haslights = r_dynlights && light_list;
if (haslights)
{
float dx = WallC.tright.X - WallC.tleft.X;
@ -158,218 +255,73 @@ namespace swrenderer
drawerargs.dc_normal.Z = 0.0f;
}
double xmagnitude = 1.0;
int texwidth = pic->GetPhysicalWidth();
int texheight = pic->GetPhysicalHeight();
int fracbits = 32 - pic->GetHeightBits();
if (fracbits == 32) fracbits = 0; // One pixel tall textures
if (viewport->RenderTarget->IsBgra()) fracbits = 32;
drawerargs.SetTextureFracBits(fracbits);
uint32_t uv_max;
int uv_fracbits = 32 - pic->GetHeightBits();
if (uv_fracbits != 32)
uv_max = texheight << uv_fracbits;
float curlight = mLight.GetLightPos(x1);
float lightstep = mLight.GetLightStep();
if (viewport->RenderTarget->IsBgra())
float upos = texcoords.upos, ustepX = texcoords.ustepX, ustepY = texcoords.ustepY;
float vpos = texcoords.vpos, vstepX = texcoords.vstepX, vstepY = texcoords.vstepY;
float wpos = texcoords.wpos, wstepX = texcoords.wstepX, wstepY = texcoords.wstepY;
float startX = texcoords.startX;
upos += ustepX * (x1 + 0.5f - startX);
vpos += vstepX * (x1 + 0.5f - startX);
wpos += wstepX * (x1 + 0.5f - startX);
float centerY = Thread->Viewport->CenterY;
centerY -= 0.5f;
for (int x = x1; x < x2; x++)
{
for (int x = x1; x < x2; x++, curlight += lightstep)
int y1 = uwal[x];
int y2 = dwal[x];
if (y2 > y1)
{
int y1 = uwal[x];
int y2 = dwal[x];
if (y2 <= y1)
continue;
if (!fixed)
drawerargs.SetLight(curlight, mLight.GetLightLevel(), mLight.GetFoggy(), viewport);
if (x + 1 < x2) xmagnitude = fabs(FIXED2DBL(texcoords.UPos(x + 1)) - FIXED2DBL(texcoords.UPos(x)));
fixed_t xxoffset = (texcoords.UPos(x) + FLOAT2FIXED(xmagnitude * 0.5)) * pic->GetPhysicalScale();
// Normalize to 0-1 range:
double uv_stepd = texcoords.VStep(x) * texcoords.yscale;
double v = (texcoords.texturemid + uv_stepd * (y1 - viewport->CenterY + 0.5)) / pic->GetHeight();
v = v - floor(v);
double v_step = uv_stepd / pic->GetHeight();
if (std::isnan(v) || std::isnan(v_step)) // this should never happen, but it apparently does..
{
uv_stepd = 0.0;
v = 0.0;
v_step = 0.0;
}
// Convert to uint32_t:
uint32_t uv_pos = (uint32_t)(int64_t)(v * 0x100000000LL);
uint32_t uv_step = (uint32_t)(int64_t)(v_step * 0x100000000LL);
// Texture mipmap and filter selection:
double ymagnitude = fabs(uv_stepd);
double magnitude = MAX(ymagnitude, xmagnitude);
double min_lod = -1000.0;
double lod = MAX(log2(magnitude) + r_lod_bias, min_lod);
bool magnifying = lod < 0.0f;
int mipmap_offset = 0;
int mip_width = pic->GetPhysicalWidth();
int mip_height = pic->GetPhysicalHeight();
if (r_mipmap && pic->Mipmapped() && mip_width > 1 && mip_height > 1)
{
uint32_t xpos = (uint32_t)((((uint64_t)xxoffset) << FRACBITS) / mip_width);
int level = (int)lod;
while (level > 0 && mip_width > 1 && mip_height > 1)
{
mipmap_offset += mip_width * mip_height;
level--;
mip_width = MAX(mip_width >> 1, 1);
mip_height = MAX(mip_height >> 1, 1);
}
xxoffset = (xpos >> FRACBITS) * mip_width;
}
const uint32_t *pixels = pic->GetPixelsBgra() + mipmap_offset;
const uint8_t *source;
const uint8_t *source2;
uint32_t texturefracx;
uint32_t height;
bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter);
if (filter_nearest)
{
int tx = (xxoffset >> FRACBITS) % mip_width;
if (tx < 0)
tx += mip_width;
source = (uint8_t*)(pixels + tx * mip_height);
source2 = nullptr;
height = mip_height;
texturefracx = 0;
}
else
{
xxoffset -= FRACUNIT / 2;
int tx0 = (xxoffset >> FRACBITS) % mip_width;
if (tx0 < 0)
tx0 += mip_width;
int tx1 = (tx0 + 1) % mip_width;
source = (uint8_t*)(pixels + tx0 * mip_height);
source2 = (uint8_t*)(pixels + tx1 * mip_height);
height = mip_height;
texturefracx = (xxoffset >> (FRACBITS - 4)) & 15;
}
drawerargs.SetTexture(source, source2, height);
if (!fixed) drawerargs.SetLight(curlight, mLight.GetLightLevel(), mLight.GetFoggy(), viewport);
if (haslights)
SetLights(drawerargs, x, y1);
else
drawerargs.dc_num_lights = 0;
drawerargs.SetTextureUPos(texturefracx);
drawerargs.SetTextureVStep(uv_step);
float dy = (y1 - centerY);
float u = upos + ustepY * dy;
float v = vpos + vstepY * dy;
float w = wpos + wstepY * dy;
float scaleU = ustepX;
float scaleV = vstepY;
w = 1.0f / w;
u *= w;
v *= w;
scaleU *= w;
scaleV *= w;
int count = y2 - y1;
uint32_t texelX = (uint32_t)(int64_t)((u - std::floor(u)) * 0x1'0000'0000LL);
uint32_t texelY = (uint32_t)(int64_t)((v - std::floor(v)) * 0x1'0000'0000LL);
uint32_t texelStepX = (uint32_t)(int64_t)(scaleU * 0x1'0000'0000LL);
uint32_t texelStepY = (uint32_t)(int64_t)(scaleV * 0x1'0000'0000LL);
drawerargs.SetDest(viewport, x, y1);
drawerargs.SetCount(count);
drawerargs.SetTextureVPos(uv_pos);
drawerargs.DrawColumn(Thread);
if (fracbits != 32)
DrawWallColumn8(Thread, drawerargs, x, y1, y2, texelX, texelY, texelStepY, pic, texwidth, texheight, fracbits, uv_max);
else
DrawWallColumn32(Thread, drawerargs, x, y1, y2, texelX, texelY, texelStepX, texelStepY, pic, texwidth, texheight);
}
}
else
{
uint32_t height = pic->GetPhysicalHeight();
uint32_t uv_max;
int uv_fracbits = 32 - pic->GetHeightBits();
if (uv_fracbits != 32)
uv_max = height << uv_fracbits;
for (int x = x1; x < x2; x++, curlight += lightstep)
{
int y1 = uwal[x];
int y2 = dwal[x];
if (y2 <= y1)
continue;
if (!fixed)
drawerargs.SetLight(curlight, mLight.GetLightLevel(), mLight.GetFoggy(), viewport);
if (x + 1 < x2) xmagnitude = fabs(FIXED2DBL(texcoords.UPos(x + 1)) - FIXED2DBL(texcoords.UPos(x)));
uint32_t uv_pos;
uint32_t uv_step;
fixed_t xxoffset = (texcoords.UPos(x) + FLOAT2FIXED(xmagnitude * 0.5)) * pic->GetPhysicalScale();
if (uv_fracbits != 32)
{
// Find start uv in [0-base_height[ range.
// Not using xs_ToFixed because it rounds the result and we need something that always rounds down to stay within the range.
double uv_stepd = texcoords.VStep(x) * texcoords.yscale;
double v = (texcoords.texturemid + uv_stepd * (y1 - viewport->CenterY + 0.5)) / pic->GetHeight();
v = v - floor(v);
v *= height;
v *= (1 << uv_fracbits);
uv_pos = (uint32_t)(int64_t)v;
uv_step = xs_ToFixed(uv_fracbits, uv_stepd * pic->GetPhysicalScale());
if (uv_step == 0) // To prevent divide by zero elsewhere
uv_step = 1;
}
else
{ // Hack for one pixel tall textures
uv_pos = 0;
uv_step = 0;
uv_max = 1;
}
int col = xxoffset >> FRACBITS;
// If the texture's width isn't a power of 2, then we need to make it a
// positive offset for proper clamping.
int width;
if (col < 0 && (width = pic->GetPhysicalWidth()) != (1 << pic->GetWidthBits()))
{
col = width + (col % width);
}
drawerargs.SetTexture(pic->GetColumn(DefaultRenderStyle(), col, nullptr), nullptr, height);
if (haslights)
SetLights(drawerargs, x, y1);
else
drawerargs.dc_num_lights = 0;
drawerargs.SetTextureVStep(uv_step);
if (uv_max == 0 || uv_step == 0) // power of two
{
int count = y2 - y1;
drawerargs.SetDest(viewport, x, y1);
drawerargs.SetCount(count);
drawerargs.SetTextureVPos(uv_pos);
drawerargs.DrawColumn(Thread);
}
else
{
uint32_t left = y2 - y1;
int y = y1;
while (left > 0)
{
uint32_t available = uv_max - uv_pos;
uint32_t next_uv_wrap = available / uv_step;
if (available % uv_step != 0)
next_uv_wrap++;
uint32_t count = MIN(left, next_uv_wrap);
drawerargs.SetDest(viewport, x, y);
drawerargs.SetCount(count);
drawerargs.SetTextureVPos(uv_pos);
drawerargs.DrawColumn(Thread);
y += count;
left -= count;
uv_pos += uv_step * count;
if (uv_pos >= uv_max)
uv_pos -= uv_max;
}
}
}
upos += ustepX;
vpos += vstepX;
wpos += wstepX;
curlight += lightstep;
}
if (r_modelscene)

View file

@ -48,6 +48,107 @@
namespace swrenderer
{
// Transform and clip coordinates. Returns true if it was clipped away
bool FWallCoords::Init(RenderThread* thread, const DVector2& pt1, const DVector2& pt2, seg_t* lineseg)
{
auto viewport = thread->Viewport.get();
RenderPortal* renderportal = thread->Portal.get();
tleft.X = float(pt1.X * viewport->viewpoint.Sin - pt1.Y * viewport->viewpoint.Cos);
tright.X = float(pt2.X * viewport->viewpoint.Sin - pt2.Y * viewport->viewpoint.Cos);
tleft.Y = float(pt1.X * viewport->viewpoint.TanCos + pt1.Y * viewport->viewpoint.TanSin);
tright.Y = float(pt2.X * viewport->viewpoint.TanCos + pt2.Y * viewport->viewpoint.TanSin);
if (renderportal->MirrorFlags & RF_XFLIP)
{
float t = -tleft.X;
tleft.X = -tright.X;
tright.X = t;
swapvalues(tleft.Y, tright.Y);
}
float fsx1, fsz1, fsx2, fsz2;
if (tleft.X >= -tleft.Y)
{
if (tleft.X > tleft.Y) return true; // left edge is off the right side
if (tleft.Y == 0) return true;
fsx1 = viewport->CenterX + tleft.X * viewport->CenterX / tleft.Y;
fsz1 = tleft.Y;
tx1 = 0.0f;
}
else
{
if (tright.X < -tright.Y) return true; // wall is off the left side
float den = tleft.X - tright.X - tright.Y + tleft.Y;
if (den == 0) return true;
fsx1 = 0;
tx1 = (tleft.X + tleft.Y) / den;
fsz1 = tleft.Y + (tright.Y - tleft.Y) * tx1;
}
if (fsz1 < TOO_CLOSE_Z)
return true;
if (tright.X <= tright.Y)
{
if (tright.X < -tright.Y) return true; // right edge is off the left side
if (tright.Y == 0) return true;
fsx2 = viewport->CenterX + tright.X * viewport->CenterX / tright.Y;
fsz2 = tright.Y;
tx2 = 1.0f;
}
else
{
if (tleft.X > tleft.Y) return true; // wall is off the right side
float den = tright.Y - tleft.Y - tright.X + tleft.X;
if (den == 0) return true;
fsx2 = viewwidth;
tx2 = (tleft.X - tleft.Y) / den;
fsz2 = tleft.Y + (tright.Y - tleft.Y) * tx2;
}
if (fsz2 < TOO_CLOSE_Z)
return true;
sx1 = xs_RoundToInt(fsx1);
sx2 = xs_RoundToInt(fsx2);
float delta = fsx2 - fsx1;
float t1 = (sx1 + 0.5f - fsx1) / delta;
float t2 = (sx2 + 0.5f - fsx1) / delta;
float invZ1 = 1.0f / fsz1;
float invZ2 = 1.0f / fsz2;
sz1 = 1.0f / (invZ1 * (1.0f - t1) + invZ2 * t1);
sz2 = 1.0f / (invZ1 * (1.0f - t2) + invZ2 * t2);
if (sx2 <= sx1)
return true;
if (lineseg && lineseg->linedef)
{
line_t* line = lineseg->linedef;
if (fabs(line->delta.X) > fabs(line->delta.Y))
{
t1 = (lineseg->v1->fX() - line->v1->fX()) / line->delta.X;
t2 = (lineseg->v2->fX() - line->v1->fX()) / line->delta.X;
}
else
{
t1 = (lineseg->v1->fY() - line->v1->fY()) / line->delta.Y;
t2 = (lineseg->v2->fY() - line->v1->fY()) / line->delta.Y;
}
tx1 = t1 + tx1 * (t2 - t1);
tx2 = t1 + tx2 * (t2 - t1);
}
return false;
}
////////////////////////////////////////////////////////////////////////////
ProjectedWallCull ProjectedWallLine::Project(RenderViewport *viewport, double z, const FWallCoords *wallc)
{
return Project(viewport, z, z, wallc);
@ -186,58 +287,7 @@ namespace swrenderer
/////////////////////////////////////////////////////////////////////////
void FWallTmapVals::InitFromWallCoords(RenderThread* thread, const FWallCoords* wallc)
{
const FVector2* left = &wallc->tleft;
const FVector2* right = &wallc->tright;
if (thread->Portal->MirrorFlags & RF_XFLIP)
{
swapvalues(left, right);
}
UoverZorg = left->X * thread->Viewport->CenterX;
UoverZstep = -left->Y;
InvZorg = (left->X - right->X) * thread->Viewport->CenterX;
InvZstep = right->Y - left->Y;
}
void FWallTmapVals::InitFromLine(RenderThread* thread, seg_t* line)
{
auto viewport = thread->Viewport.get();
auto renderportal = thread->Portal.get();
vertex_t* v1 = line->linedef->v1;
vertex_t* v2 = line->linedef->v2;
if (line->linedef->sidedef[0] != line->sidedef)
{
swapvalues(v1, v2);
}
DVector2 left = v1->fPos() - viewport->viewpoint.Pos;
DVector2 right = v2->fPos() - viewport->viewpoint.Pos;
double viewspaceX1 = left.X * viewport->viewpoint.Sin - left.Y * viewport->viewpoint.Cos;
double viewspaceX2 = right.X * viewport->viewpoint.Sin - right.Y * viewport->viewpoint.Cos;
double viewspaceY1 = left.X * viewport->viewpoint.TanCos + left.Y * viewport->viewpoint.TanSin;
double viewspaceY2 = right.X * viewport->viewpoint.TanCos + right.Y * viewport->viewpoint.TanSin;
if (renderportal->MirrorFlags & RF_XFLIP)
{
viewspaceX1 = -viewspaceX1;
viewspaceX2 = -viewspaceX2;
}
UoverZorg = float(viewspaceX1 * viewport->CenterX);
UoverZstep = float(-viewspaceY1);
InvZorg = float((viewspaceX1 - viewspaceX2) * viewport->CenterX);
InvZstep = float(viewspaceY2 - viewspaceY1);
}
/////////////////////////////////////////////////////////////////////////
void ProjectedWallTexcoords::ProjectTop(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic)
void ProjectedWallTexcoords::ProjectTop(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic)
{
side_t* sidedef = lineseg->sidedef;
line_t* linedef = lineseg->linedef;
@ -269,12 +319,12 @@ namespace swrenderer
}
texturemid += GetRowOffset(lineseg, pic, side_t::top);
Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::top), x1, x2, WallT);
xoffset = GetXOffset(lineseg, pic, side_t::top);
Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::top), WallC, pic, false);
}
void ProjectedWallTexcoords::ProjectMid(RenderViewport* viewport, sector_t* frontsector, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic)
void ProjectedWallTexcoords::ProjectMid(RenderViewport* viewport, sector_t* frontsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic)
{
side_t* sidedef = lineseg->sidedef;
line_t* linedef = lineseg->linedef;
@ -306,12 +356,12 @@ namespace swrenderer
}
texturemid += GetRowOffset(lineseg, pic, side_t::mid);
Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::mid), x1, x2, WallT);
xoffset = GetXOffset(lineseg, pic, side_t::mid);
Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::mid), WallC, pic, false);
}
void ProjectedWallTexcoords::ProjectBottom(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic)
void ProjectedWallTexcoords::ProjectBottom(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic)
{
side_t* sidedef = lineseg->sidedef;
line_t* linedef = lineseg->linedef;
@ -351,12 +401,12 @@ namespace swrenderer
}
texturemid += GetRowOffset(lineseg, pic, side_t::bottom);
Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::bottom), x1, x2, WallT);
xoffset = GetXOffset(lineseg, pic, side_t::bottom);
Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::bottom), WallC, pic, false);
}
void ProjectedWallTexcoords::ProjectTranslucent(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic)
void ProjectedWallTexcoords::ProjectTranslucent(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic)
{
line_t* linedef = lineseg->linedef;
side_t* sidedef = lineseg->sidedef;
@ -391,12 +441,12 @@ namespace swrenderer
}
texturemid += GetRowOffset(lineseg, pic, side_t::mid);
Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::mid), x1, x2, WallT);
xoffset = GetXOffset(lineseg, pic, side_t::mid);
Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::mid), WallC, pic, false);
}
void ProjectedWallTexcoords::Project3DFloor(RenderViewport* viewport, F3DFloor* rover, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic)
void ProjectedWallTexcoords::Project3DFloor(RenderViewport* viewport, F3DFloor* rover, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic)
{
// find positioning
side_t* scaledside;
@ -445,35 +495,89 @@ namespace swrenderer
texturemid += rowoffset;
}
Project(viewport, lineseg->sidedef->TexelLength * xscale, x1, x2, WallT);
Project(viewport, lineseg->sidedef->TexelLength * xscale, WallC, pic, false);
}
void ProjectedWallTexcoords::ProjectSprite(RenderViewport* viewport, double topZ, double scale, bool flipX, bool flipY, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic)
void ProjectedWallTexcoords::Project(RenderViewport *viewport, double walxrepeat, const FWallCoords& WallC, FSoftwareTexture* pic, bool flipx)
{
yscale = 1.0 / scale;
texturemid = pic->GetTopOffset(0) + (topZ - viewport->viewpoint.Pos.Z) * yscale;
if (flipY)
float texwidth = pic->GetWidth();
float texheight = pic->GetHeight();
float texU1 = FIXED2FLOAT(xoffset) / texwidth;
float texU2 = texU1 + walxrepeat / texwidth;
if (walxrepeat < 0.0)
{
yscale = -yscale;
texturemid -= pic->GetHeight();
texU1 += 1.0f;
texU2 += 1.0f;
}
if (flipx)
{
texU1 = 1.0f - texU1;
texU2 = 1.0f - texU2;
}
Project(viewport, pic->GetWidth(), x1, x2, WallT, flipX);
}
void ProjectedWallTexcoords::Project(RenderViewport *viewport, double walxrepeat, int x1, int x2, const FWallTmapVals &WallT, bool flipx)
{
this->walxrepeat = walxrepeat;
this->x1 = x1;
this->x2 = x2;
this->WallT = WallT;
this->flipx = flipx;
CenterX = viewport->CenterX;
WallTMapScale2 = viewport->WallTMapScale2;
float texV = texturemid / texheight;
// Set up some fake vertices as that makes it easier to calculate the gradients using code already known to work.
Vertex v1;
v1.x = WallC.sx1 + 0.5f;// WallC.tleft.X;
v1.y = 0.0f;
v1.w = WallC.sz1;//WallC.tleft.Y;
Vertex v2;
v2.x = WallC.sx2 + 0.5f;// WallC.tright.X;
v2.y = 0.0f;
v2.w = WallC.sz2;// WallC.tright.Y;
v1.u = texU1 * (1.0f - WallC.tx1) + texU2 * WallC.tx1;
v2.u = texU1 * (1.0f - WallC.tx2) + texU2 * WallC.tx2;
v1.v = texV;
v2.v = texV;
Vertex v3;
v3.x = v1.x;
v3.y = v1.y - 100.0f;
v3.w = v1.w;
v3.u = v1.u;
v3.v = v1.v + 1.0f / yscale * 100.0f / texheight;
// Project to screen space
v1.w = 1.0f / v1.w;
v2.w = 1.0f / v2.w;
v3.w = 1.0f / v3.w;
//v1.x = viewport->CenterX + v1.x * v1.w * viewport->CenterX;
//v2.x = viewport->CenterX + v2.x * v2.w * viewport->CenterX;
//v3.x = viewport->CenterX + v3.x * v3.w * viewport->CenterX;
v1.y = viewport->CenterY - v1.y * v1.w * viewport->InvZtoScale;
v2.y = viewport->CenterY - v2.y * v2.w * viewport->InvZtoScale;
v3.y = viewport->CenterY - v3.y * v3.w * viewport->InvZtoScale;
// Calculate gradients
float bottomX = (v2.x - v3.x) * (v1.y - v3.y) - (v1.x - v3.x) * (v2.y - v3.y);
float bottomY = (v1.x - v3.x) * (v2.y - v3.y) - (v2.x - v3.x) * (v1.y - v3.y);
wstepX = FindGradientX(bottomX, 1.0f, 1.0f, 1.0f, v1, v2, v3);
ustepX = FindGradientX(bottomX, v1.u, v2.u, v3.u, v1, v2, v3);
vstepX = FindGradientX(bottomX, v1.v, v2.v, v3.v, v1, v2, v3);
wstepY = FindGradientY(bottomY, 1.0f, 1.0f, 1.0f, v1, v2, v3);
ustepY = FindGradientY(bottomY, v1.u, v2.u, v3.u, v1, v2, v3);
vstepY = FindGradientY(bottomY, v1.v, v2.v, v3.v, v1, v2, v3);
startX = v1.x;
upos = v1.u * v1.w;
vpos = v1.v * v1.w;
wpos = v1.w;
}
#if 0
float ProjectedWallTexcoords::VStep(int x) const
{
return 0.0001f;
/*
float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - CenterX);
float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - CenterX);
float uGradient = WallT.UoverZstep;
@ -483,10 +587,13 @@ namespace swrenderer
float u = (uOverZ + uGradient * (x - x1)) / (invZ + zGradient * (x - x1));
return depthOrg + u * depthScale;
*/
}
fixed_t ProjectedWallTexcoords::UPos(int x) const
{
return 0;
/*
float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - CenterX);
float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - CenterX);
float uGradient = WallT.UoverZstep;
@ -510,7 +617,9 @@ namespace swrenderer
}
return value + xoffset;
*/
}
#endif
double ProjectedWallTexcoords::GetRowOffset(seg_t* lineseg, FSoftwareTexture* tex, side_t::ETexpart texpart)
{
@ -584,6 +693,8 @@ namespace swrenderer
void ProjectedWallLight::SetLightLeft(RenderThread *thread, const FWallCoords &wallc)
{
spritelight = false;
x1 = wallc.sx1;
CameraLight *cameraLight = CameraLight::Instance();

View file

@ -27,9 +27,21 @@
namespace swrenderer
{
struct FWallCoords;
struct DrawSegmentClipInfo;
struct FWallCoords
{
FVector2 tleft; // coords at left of wall in view space rx1,ry1
FVector2 tright; // coords at right of wall in view space rx2,ry2
float sz1, sz2; // depth at left, right of wall in screen space yb1,yb2
short sx1, sx2; // x coords at left, right of wall in screen space xb1,xb2
float tx1, tx2; // texture coordinate fractions
bool Init(RenderThread* thread, const DVector2& pt1, const DVector2& pt2, seg_t* lineseg = nullptr);
};
enum class ProjectedWallCull
{
Visible,
@ -50,53 +62,54 @@ namespace swrenderer
void ClipBottom(int x1, int x2, const DrawSegmentClipInfo& clip);
};
struct FWallTmapVals
{
void InitFromWallCoords(RenderThread* thread, const FWallCoords* wallc);
void InitFromLine(RenderThread* thread, seg_t* line);
private:
float UoverZorg, UoverZstep;
float InvZorg, InvZstep;
friend class ProjectedWallTexcoords;
};
class ProjectedWallTexcoords
{
public:
void ProjectTop(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic);
void ProjectMid(RenderViewport* viewport, sector_t* frontsector, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic);
void ProjectBottom(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic);
void ProjectTranslucent(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic);
void Project3DFloor(RenderViewport* viewport, F3DFloor* rover, seg_t* lineseg, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic);
void ProjectSprite(RenderViewport* viewport, double topZ, double scale, bool flipX, bool flipY, int x1, int x2, const FWallTmapVals& WallT, FSoftwareTexture* pic);
void ProjectTop(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic);
void ProjectMid(RenderViewport* viewport, sector_t* frontsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic);
void ProjectBottom(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic);
void ProjectTranslucent(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic);
void Project3DFloor(RenderViewport* viewport, F3DFloor* rover, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic);
float VStep(int x) const;
fixed_t UPos(int x) const;
// Gradients
float upos, ustepX, ustepY;
float vpos, vstepX, vstepY;
float wpos, wstepX, wstepY;
float startX;
private:
void Project(RenderViewport* viewport, double walxrepeat, int x1, int x2, const FWallTmapVals& WallT, bool flipx = false);
void Project(RenderViewport* viewport, double walxrepeat, const FWallCoords& WallC, FSoftwareTexture* pic, bool flipx);
static fixed_t GetXOffset(seg_t* lineseg, FSoftwareTexture* tex, side_t::ETexpart texpart);
static double GetRowOffset(seg_t* lineseg, FSoftwareTexture* tex, side_t::ETexpart texpart);
static double GetXScale(side_t* sidedef, FSoftwareTexture* tex, side_t::ETexpart texpart);
static double GetYScale(side_t* sidedef, FSoftwareTexture* tex, side_t::ETexpart texpart);
double CenterX;
double WallTMapScale2;
double walxrepeat;
int x1;
int x2;
FWallTmapVals WallT;
bool flipx;
float yscale = 1.0f;
fixed_t xoffset = 0;
double texturemid = 0.0f;
friend class RenderWallPart;
friend class SpriteDrawerArgs;
struct Vertex
{
float x, y, w;
float u, v;
};
float FindGradientX(float bottomX, float c0, float c1, float c2, const Vertex& v1, const Vertex& v2, const Vertex& v3)
{
c0 *= v1.w;
c1 *= v2.w;
c2 *= v3.w;
return ((c1 - c2) * (v1.y - v3.y) - (c0 - c2) * (v2.y - v3.y)) / bottomX;
}
float FindGradientY(float bottomY, float c0, float c1, float c2, const Vertex& v1, const Vertex& v2, const Vertex& v3)
{
c0 *= v1.w;
c1 *= v2.w;
c2 *= v3.w;
return ((c1 - c2) * (v1.x - v3.x) - (c0 - c2) * (v2.x - v3.x)) / bottomY;
}
};
class ProjectedWallLight
@ -108,14 +121,17 @@ namespace swrenderer
float GetLightPos(int x) const { return lightleft + lightstep * (x - x1); }
float GetLightStep() const { return lightstep; }
bool IsSpriteLight() const { return spritelight; }
void SetColormap(const sector_t *frontsector, seg_t *lineseg, lightlist_t *lit = nullptr);
void SetLightLeft(RenderThread *thread, const FWallCoords &wallc);
void SetSpriteLight() { lightleft = 0.0f; lightstep = 0.0f; spritelight = true; }
private:
int lightlevel;
bool foggy;
FDynamicColormap *basecolormap;
bool spritelight;
int x1;
float lightleft;

View file

@ -35,10 +35,10 @@ namespace swrenderer
class RenderThread;
struct VisiblePlane;
// The 3072 below is just an arbitrary value picked to avoid
// The 32 below is just an arbitrary value picked to avoid
// drawing lines the player is too close to that would overflow
// the texture calculations.
#define TOO_CLOSE_Z (3072.0 / (1<<12))
#define TOO_CLOSE_Z (32.0 / (1 << 12))
enum class WaterFakeSide
{

View file

@ -54,7 +54,6 @@ namespace swrenderer
short x1, x2;
FWallCoords WallC;
FWallTmapVals tmapvals;
ProjectedWallTexcoords texcoords;
DrawSegmentClipInfo drawsegclip;

View file

@ -148,10 +148,8 @@ namespace swrenderer
decal_left = decal_pos - edge_left * angvec - thread->Viewport->viewpoint.Pos;
decal_right = decal_pos + edge_right * angvec - thread->Viewport->viewpoint.Pos;
CameraLight *cameraLight;
FWallCoords WallC;
if (WallC.Init(thread, decal_left, decal_right, TOO_CLOSE_Z))
if (WallC.Init(thread, decal_left, decal_right))
return;
x1 = WallC.sx1;
@ -240,43 +238,19 @@ namespace swrenderer
usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate);
}
float lightpos = light.GetLightPos(x1);
cameraLight = CameraLight::Instance();
// Draw it
FWallTmapVals WallT;
WallT.InitFromWallCoords(thread, &WallC);
ProjectedWallTexcoords walltexcoords;
walltexcoords.ProjectSprite(thread->Viewport.get(), zpos, decal->ScaleY, decal->RenderFlags & RF_XFLIP, decal->RenderFlags & RF_YFLIP, x1, x2, WallT, WallSpriteTile);
do
{
int x = x1;
ColormapLight cmlight;
cmlight.SetColormap(thread, MINZ, light.GetLightLevel(), light.GetFoggy(), usecolormap, decal->RenderFlags & RF_FULLBRIGHT, false, false, false, false);
SpriteDrawerArgs drawerargs;
bool visible = drawerargs.SetStyle(thread->Viewport.get(), decal->RenderStyle, (float)decal->Alpha, decal->Translation, decal->AlphaColor, cmlight);
bool calclighting = cameraLight->FixedLightLevel() < 0 && !cameraLight->FixedColormap();
if (visible)
{
thread->PrepareTexture(WallSpriteTile, decal->RenderStyle);
bool sprflipvert = (decal->RenderFlags & RF_YFLIP);
while (x < x2)
{
if (calclighting)
{ // calculate lighting
drawerargs.SetLight(lightpos, light.GetLightLevel(), light.GetFoggy(), thread->Viewport.get());
}
drawerargs.DrawMaskedColumn(thread, x, WallSpriteTile, walltexcoords, sprflipvert, mfloorclip, mceilingclip, decal->RenderStyle);
lightpos += light.GetLightStep();
x++;
}
drawerargs.DrawMasked(thread, zpos + WallSpriteTile->GetTopOffset(0) * decal->ScaleY, decal->ScaleY, decal->RenderFlags & RF_XFLIP, decal->RenderFlags & RF_YFLIP, WallC, light, WallSpriteTile, mfloorclip, mceilingclip, decal->RenderStyle);
}
// If this sprite is RF_CLIPFULL on a two-sided line, needrepeat will

View file

@ -70,6 +70,8 @@ EXTERN_CVAR(Bool, r_drawplayersprites)
EXTERN_CVAR(Bool, r_deathcamera)
EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor)
CVAR(Bool, r_noaccel, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
namespace swrenderer
{
RenderPlayerSprites::RenderPlayerSprites(RenderThread *thread)
@ -325,7 +327,7 @@ namespace swrenderer
if (vis.x1 > x1)
vis.startfrac += vis.xiscale*(vis.x1 - x1);
noaccel = false;
noaccel = r_noaccel;
FDynamicColormap *colormap_to_use = nullptr;
if (pspr->GetID() < PSP_TARGETCENTER)
{
@ -489,46 +491,23 @@ namespace swrenderer
void NoAccelPlayerSprite::Render(RenderThread *thread)
{
if (xscale == 0 || fabs(yscale) < (1.0f / 32000.0f))
{ // scaled to 0; can't see
return;
}
SpriteDrawerArgs drawerargs;
bool visible = drawerargs.SetStyle(thread->Viewport.get(), RenderStyle, Alpha, Translation, FillColor, Light);
if (!visible)
return;
double spryscale = yscale;
bool sprflipvert = false;
fixed_t iscale = FLOAT2FIXED(1 / yscale);
auto viewport = thread->Viewport.get();
double sprtopscreen;
double centerY = viewheight / 2;
double y1, y2;
if (renderflags & RF_YFLIP)
{
sprflipvert = true;
spryscale = -spryscale;
iscale = -iscale;
sprtopscreen = viewport->CenterY + (texturemid - pic->GetHeight()) * spryscale;
y1 = centerY + (texturemid - pic->GetHeight()) * (-yscale);
y2 = y1 + pic->GetHeight() * (-yscale);
}
else
{
sprflipvert = false;
sprtopscreen = viewport->CenterY - texturemid * spryscale;
}
// clip to screen bounds
short *mfloorclip = screenheightarray;
short *mceilingclip = zeroarray;
fixed_t frac = startfrac;
thread->PrepareTexture(pic, RenderStyle);
for (int x = x1; x < x2; x++)
{
drawerargs.DrawMaskedColumn(thread, x, iscale, pic, frac + xiscale / 2, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, RenderStyle, false);
frac += xiscale;
y1 = centerY - texturemid * yscale;
y2 = y1 + pic->GetHeight() * yscale;
}
drawerargs.DrawMasked2D(thread, x1, x2, y1, y2, pic, RenderStyle);
}
}

View file

@ -67,7 +67,6 @@
#include "a_dynlight.h"
#include "r_data/r_vanillatrans.h"
EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor)
EXTERN_CVAR(Bool, gl_light_sprites)
namespace swrenderer
@ -75,32 +74,20 @@ namespace swrenderer
void RenderSprite::Project(RenderThread *thread, AActor *thing, const DVector3 &pos, FTexture *ttex, const DVector2 &spriteScale, int renderflags, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int lightlevel, bool foggy, FDynamicColormap *basecolormap)
{
FSoftwareTexture *tex = ttex->GetSoftwareTexture();
// transform the origin point
double tr_x = pos.X - thread->Viewport->viewpoint.Pos.X;
double tr_y = pos.Y - thread->Viewport->viewpoint.Pos.Y;
double tz = tr_x * thread->Viewport->viewpoint.TanCos + tr_y * thread->Viewport->viewpoint.TanSin;
auto viewport = thread->Viewport.get();
// thing is behind view plane?
if (tz < MINZ)
const double thingxscalemul = spriteScale.X / tex->GetScale().X;
// Calculate billboard line for the sprite
DVector2 dir = { viewport->viewpoint.Sin, -viewport->viewpoint.Cos };
DVector2 pt1 = pos.XY() - viewport->viewpoint.Pos.XY() - dir * (((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->GetLeftOffsetSW() - 1) : tex->GetLeftOffsetSW()) * thingxscalemul);
DVector2 pt2 = pt1 + dir * (tex->GetWidth() * thingxscalemul);
FWallCoords wallc;
if (wallc.Init(thread, pt1, pt2))
return;
double tx = tr_x * thread->Viewport->viewpoint.Sin - tr_y * thread->Viewport->viewpoint.Cos;
// [RH] Flip for mirrors
RenderPortal *renderportal = thread->Portal.get();
if (renderportal->MirrorFlags & RF_XFLIP)
{
tx = -tx;
}
//tx2 = tx >> 4;
// too far off the side?
if (fabs(tx / 64) > fabs(tz))
{
return;
}
// [RH] Added scaling
int scaled_to = tex->GetScaledTopOffsetSW();
int scaled_bo = scaled_to - tex->GetScaledHeight();
@ -134,86 +121,41 @@ namespace swrenderer
}
}
auto viewport = thread->Viewport.get();
double xscale = viewport->CenterX / tz;
// [RH] Reject sprites that are off the top or bottom of the screen
if (viewport->globaluclip * tz > viewport->viewpoint.Pos.Z - gzb || viewport->globaldclip * tz < viewport->viewpoint.Pos.Z - gzt)
{
return;
}
// [RH] Flip for mirrors
auto renderportal = thread->Portal.get();
renderflags ^= renderportal->MirrorFlags & RF_XFLIP;
// [SP] SpriteFlip
if (thing->renderflags & RF_SPRITEFLIP)
renderflags ^= RF_XFLIP;
// calculate edges of the shape
const double thingxscalemul = spriteScale.X / tex->GetScale().X;
tx -= ((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->GetLeftOffsetSW() - 1) : tex->GetLeftOffsetSW()) * thingxscalemul;
double dtx1 = tx * xscale;
int x1 = viewport->viewwindow.centerx + xs_RoundToInt(dtx1);
// off the right side?
if (x1 >= renderportal->WindowRight)
return;
tx += tex->GetWidth() * thingxscalemul;
int x2 = viewport->viewwindow.centerx + xs_RoundToInt(tx * xscale);
// off the left side or too small?
if ((x2 < renderportal->WindowLeft || x2 <= x1))
return;
xscale = spriteScale.X * xscale / tex->GetScale().X;
fixed_t iscale = (fixed_t)(FRACUNIT / xscale); // Round towards zero to avoid wrapping in edge cases
double yscale = spriteScale.Y / tex->GetScale().Y;
// store information in a vissprite
RenderSprite *vis = thread->FrameMemory->NewObject<RenderSprite>();
vis->wallc = wallc;
vis->SpriteScale = yscale;
vis->CurrentPortalUniq = renderportal->CurrentPortalUniq;
vis->xscale = FLOAT2FIXED(xscale);
vis->yscale = float(viewport->InvZtoScale * yscale / tz);
vis->idepth = float(1 / tz);
vis->yscale = float(viewport->InvZtoScale * yscale / wallc.sz1);
vis->idepth = float(1 / wallc.sz1);
vis->floorclip = thing->Floorclip / yscale;
vis->texturemid = tex->GetTopOffsetSW() - (viewport->viewpoint.Pos.Z - pos.Z + thing->Floorclip) / yscale;
vis->x1 = x1 < renderportal->WindowLeft ? renderportal->WindowLeft : x1;
vis->x2 = x2 > renderportal->WindowRight ? renderportal->WindowRight : x2;
//vis->Angle = thing->Angles.Yaw;
if (renderflags & RF_XFLIP)
{
vis->startfrac = (tex->GetWidth() << FRACBITS) - 1;
vis->xiscale = -iscale;
}
else
{
vis->startfrac = 0;
vis->xiscale = iscale;
}
vis->startfrac += (fixed_t)(vis->xiscale * (vis->x1 - viewport->viewwindow.centerx + 0.5 - dtx1));
// killough 3/27/98: save sector for special clipping later
vis->x1 = wallc.sx1 < renderportal->WindowLeft ? renderportal->WindowLeft : wallc.sx1;
vis->x2 = wallc.sx2 > renderportal->WindowRight ? renderportal->WindowRight : wallc.sx2;
vis->heightsec = heightsec;
vis->sector = thing->Sector;
vis->section = thing->section;
vis->depth = (float)tz;
vis->depth = (float)wallc.sz1;
vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z };
vis->gzb = (float)gzb; // [RH] use gzb, not thing->z
vis->gzt = (float)gzt; // killough 3/27/98
vis->gzb = (float)gzb;
vis->gzt = (float)gzt;
vis->deltax = float(pos.X - viewport->viewpoint.Pos.X);
vis->deltay = float(pos.Y - viewport->viewpoint.Pos.Y);
vis->renderflags = renderflags;
if (thing->flags5 & MF5_BRIGHT)
vis->renderflags |= RF_FULLBRIGHT; // kg3D
vis->renderflags |= RF_FULLBRIGHT;
vis->RenderStyle = thing->RenderStyle;
if (r_UseVanillaTransparency)
{
@ -226,11 +168,7 @@ namespace swrenderer
vis->Alpha = float(thing->Alpha);
vis->fakefloor = fakefloor;
vis->fakeceiling = fakeceiling;
//vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP;
//vis->bSplitSprite = false;
vis->pic = tex;
vis->foggy = foggy;
// The software renderer cannot invert the source without inverting the overlay
@ -300,74 +238,36 @@ namespace swrenderer
vis->dynlightcolor = 0;
}
vis->Light.SetColormap(thread, tz, lightlevel, foggy, basecolormap, fullbright, invertcolormap, fadeToBlack, false, false);
vis->Light.SetColormap(thread, wallc.sz1, lightlevel, foggy, basecolormap, fullbright, invertcolormap, fadeToBlack, false, false);
thread->SpriteList->Push(vis);
}
void RenderSprite::Render(RenderThread *thread, short *mfloorclip, short *mceilingclip, int, int, Fake3DTranslucent)
{
auto vis = this;
fixed_t frac;
FSoftwareTexture *tex;
int x2;
fixed_t xiscale;
double spryscale, sprtopscreen;
bool sprflipvert;
if (vis->xscale == 0 || fabs(vis->yscale) < (1.0f / 32000.0f))
{ // scaled to 0; can't see
return;
}
SpriteDrawerArgs drawerargs;
drawerargs.SetDynamicLight(dynlightcolor);
bool visible = drawerargs.SetStyle(thread->Viewport.get(), vis->RenderStyle, vis->Alpha, vis->Translation, vis->FillColor, vis->Light);
bool visible = drawerargs.SetStyle(thread->Viewport.get(), RenderStyle, Alpha, Translation, FillColor, Light);
if (visible)
{
tex = vis->pic;
spryscale = vis->yscale;
sprflipvert = false;
fixed_t iscale = FLOAT2FIXED(1 / vis->yscale);
frac = vis->startfrac;
xiscale = vis->xiscale;
double texturemid = vis->texturemid;
auto viewport = thread->Viewport.get();
if (vis->renderflags & RF_YFLIP)
RenderTranslucentPass *translucentPass = thread->TranslucentPass.get();
short portalfloorclip[MAXWIDTH];
int x2 = wallc.sx2;
for (int x = wallc.sx1; x < x2; x++)
{
sprflipvert = true;
spryscale = -spryscale;
iscale = -iscale;
texturemid -= vis->pic->GetHeight();
sprtopscreen = viewport->CenterY + texturemid * spryscale;
}
else
{
sprflipvert = false;
sprtopscreen = viewport->CenterY - texturemid * spryscale;
if (translucentPass->ClipSpriteColumnWithPortals(x, this))
portalfloorclip[x] = mceilingclip[x];
else
portalfloorclip[x] = mfloorclip[x];
}
int x = vis->x1;
x2 = vis->x2;
thread->PrepareTexture(pic, RenderStyle);
if (x < x2)
{
RenderTranslucentPass *translucentPass = thread->TranslucentPass.get();
ProjectedWallLight mlight;
mlight.SetSpriteLight();
thread->PrepareTexture(tex, vis->RenderStyle);
while (x < x2)
{
if (!translucentPass->ClipSpriteColumnWithPortals(x, vis))
drawerargs.DrawMaskedColumn(thread, x, iscale, tex, frac, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, vis->RenderStyle, false);
x++;
frac += xiscale;
}
}
drawerargs.SetBaseColormap(Light.BaseColormap);
drawerargs.DrawMasked(thread, gzt - floorclip, SpriteScale, renderflags & RF_XFLIP, renderflags & RF_YFLIP, wallc, mlight, pic, portalfloorclip, mceilingclip, RenderStyle);
}
}
}

View file

@ -13,9 +13,8 @@ namespace swrenderer
void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ, Fake3DTranslucent clip3DFloor) override;
private:
fixed_t xscale = 0;
fixed_t startfrac = 0; // horizontal position of x1
fixed_t xiscale = 0; // negative if flipped
FWallCoords wallc;
double SpriteScale;
uint32_t Translation = 0;
uint32_t FillColor = 0;

View file

@ -94,7 +94,7 @@ namespace swrenderer
right.Y = left.Y + x2 * angsin;
// Is it off-screen?
if (wallc.Init(thread, left, right, TOO_CLOSE_Z))
if (wallc.Init(thread, left, right))
return;
RenderPortal *renderportal = thread->Portal.get();
@ -180,35 +180,25 @@ namespace swrenderer
if (!visible)
return;
float lightleft = float(thread->Light->WallVis(spr->wallc.sz1, foggy));
float lightstep = float((thread->Light->WallVis(spr->wallc.sz2, foggy) - lightleft) / (spr->wallc.sx2 - spr->wallc.sx1));
float light = lightleft + (x1 - spr->wallc.sx1) * lightstep;
CameraLight *cameraLight = CameraLight::Instance();
bool calclighting = cameraLight->FixedLightLevel() < 0 && !cameraLight->FixedColormap();
ProjectedWallLight mlight;
mlight.SetLightLeft(thread, wallc);
// Draw it
auto WallSpriteTile = spr->pic;
FWallTmapVals WallT;
WallT.InitFromWallCoords(thread, &spr->wallc);
ProjectedWallTexcoords walltexcoords;
walltexcoords.ProjectSprite(thread->Viewport.get(), spr->gzt, spr->yscale, spr->renderflags & RF_XFLIP, spr->renderflags & RF_YFLIP, x1, x2, WallT, WallSpriteTile);
RenderTranslucentPass *translucentPass = thread->TranslucentPass.get();
thread->PrepareTexture(WallSpriteTile, spr->RenderStyle);
bool sprflipvert = (spr->renderflags & RF_YFLIP);
RenderTranslucentPass* translucentPass = thread->TranslucentPass.get();
short floorclip[MAXWIDTH];
for (int x = x1; x < x2; x++)
{
if (calclighting)
{
drawerargs.SetBaseColormap(spr->Light.BaseColormap);
drawerargs.SetLight(light, spr->sector->lightlevel, spr->foggy, thread->Viewport.get());
}
if (!translucentPass->ClipSpriteColumnWithPortals(x, spr))
drawerargs.DrawMaskedColumn(thread, x, WallSpriteTile, walltexcoords, sprflipvert, mfloorclip, mceilingclip, spr->RenderStyle);
light += lightstep;
if (translucentPass->ClipSpriteColumnWithPortals(x, spr))
floorclip[x] = mceilingclip[x];
else
floorclip[x] = mfloorclip[x];
}
drawerargs.SetBaseColormap(spr->Light.BaseColormap);
drawerargs.DrawMasked(thread, spr->gzt, spr->yscale, spr->renderflags & RF_XFLIP, spr->renderflags & RF_YFLIP, spr->wallc, mlight, WallSpriteTile, floorclip, mceilingclip, spr->RenderStyle);
}
}

View file

@ -43,221 +43,246 @@ namespace swrenderer
colfunc = &SWPixelFormatDrawers::DrawColumn;
}
void SpriteDrawerArgs::DrawMaskedColumn(RenderThread* thread, int x, FSoftwareTexture* WallSpriteTile, const ProjectedWallTexcoords& walltexcoords, bool sprflipvert, const short* mfloorclip, const short* mceilingclip, FRenderStyle style)
void SpriteDrawerArgs::DrawMasked(RenderThread* thread, double topZ, double scale, bool flipX, bool flipY, const FWallCoords& WallC, const ProjectedWallLight& light, FSoftwareTexture* tex, const short* mfloorclip, const short* mceilingclip, FRenderStyle style)
{
auto viewport = thread->Viewport.get();
auto cameraLight = CameraLight::Instance();
float iscale = walltexcoords.VStep(x) * walltexcoords.yscale;
double spryscale = 1 / iscale;
double sprtopscreen;
if (sprflipvert)
sprtopscreen = viewport->CenterY + walltexcoords.texturemid * spryscale;
else
sprtopscreen = viewport->CenterY - walltexcoords.texturemid * spryscale;
bool calclighting = cameraLight->FixedLightLevel() < 0 && !cameraLight->FixedColormap() && !light.IsSpriteLight();
DrawMaskedColumn(thread, x, FLOAT2FIXED(iscale), WallSpriteTile, walltexcoords.UPos(x), spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, style);
}
float wpos = 1.0f / WallC.sz1;
float wstepX = (1.0f / WallC.sz2 - wpos) / (WallC.sx2 - WallC.sx1);
void SpriteDrawerArgs::DrawMaskedColumn(RenderThread *thread, int x, fixed_t iscale, FSoftwareTexture *tex, fixed_t col, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, FRenderStyle style, bool unmasked)
{
if (x < thread->X1 || x >= thread->X2)
return;
col *= tex->GetPhysicalScale();
iscale *= tex->GetPhysicalScale();
spryscale /= tex->GetPhysicalScale();
auto viewport = thread->Viewport.get();
// Handle the linear filtered version in a different function to reduce chances of merge conflicts from zdoom.
if (viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input) // To do: add support to R_DrawColumnHoriz_rgba
float upos, ustepX;
if (flipX)
{
DrawMaskedColumnBgra(thread, x, iscale, tex, col, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, unmasked);
return;
upos = (1.0f - WallC.tx1) / WallC.sz1;
ustepX = ((1.0f - WallC.tx2) / WallC.sz2 - upos) / (WallC.sx2 - WallC.sx1);
}
else
{
upos = WallC.tx1 / WallC.sz1;
ustepX = (WallC.tx2 / WallC.sz2 - upos) / (WallC.sx2 - WallC.sx1);
}
float iscale = 1.0f / scale;
if (flipY)
iscale = -iscale;
float vstepY = iscale / WallC.sz1 / (viewport->InvZtoScale / WallC.sz1);
wpos += wstepX * 0.5f;
upos += ustepX * 0.5f;
int x1 = WallC.sx1;
int x2 = WallC.sx2;
float centerY = thread->Viewport->CenterY;
topZ -= thread->Viewport->viewpoint.Pos.Z;
if (flipY)
topZ -= tex->GetHeight() * scale;
int texwidth = tex->GetPhysicalWidth();
int texheight = tex->GetPhysicalHeight();
float lightpos = light.GetLightPos(x1);
float lightstep = light.GetLightStep();
dc_viewport = viewport;
dc_x = x;
dc_iscale = iscale;
dc_textureheight = tex->GetPhysicalHeight();
dc_textureheight = texheight;
const FSoftwareTextureSpan *span;
const uint8_t *column;
if (viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input)
column = (const uint8_t *)tex->GetColumnBgra(col >> FRACBITS, &span);
else
column = tex->GetColumn(style, col >> FRACBITS, &span);
bool bgra = viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input;
FSoftwareTextureSpan unmaskedSpan[2];
if (unmasked)
for (int x = x1; x < x2; x++)
{
span = unmaskedSpan;
unmaskedSpan[0].TopOffset = 0;
unmaskedSpan[0].Length = tex->GetPhysicalHeight();
unmaskedSpan[1].TopOffset = 0;
unmaskedSpan[1].Length = 0;
}
int pixelsize = viewport->RenderTarget->IsBgra() ? 4 : 1;
while (span->Length != 0)
{
const int length = span->Length;
const int top = span->TopOffset;
// calculate unclipped screen coordinates for post
dc_yl = (int)(sprtopscreen + spryscale * top + 0.5);
dc_yh = (int)(sprtopscreen + spryscale * (top + length) + 0.5) - 1;
if (sprflipvert)
if (calclighting)
{
swapvalues(dc_yl, dc_yh);
SetLight(lightpos, light.GetLightLevel(), light.GetFoggy(), thread->Viewport.get());
}
if (dc_yh >= mfloorclip[dc_x])
{
dc_yh = mfloorclip[dc_x] - 1;
}
if (dc_yl < mceilingclip[dc_x])
{
dc_yl = mceilingclip[dc_x];
}
float w = 1.0f / wpos;
float y1 = centerY - topZ * wpos * viewport->InvZtoScale;
float u = upos * w;
float scaleU = ustepX * w;
float scaleV = vstepY * w;
if (dc_yl <= dc_yh)
{
dc_texturefrac = FLOAT2FIXED((dc_yl + 0.5 - sprtopscreen) / spryscale);
dc_source = column;
dc_source2 = nullptr;
SetDest(viewport, dc_x, dc_yl);
dc_count = dc_yh - dc_yl + 1;
uint32_t texelX = (uint32_t)(int64_t)((u - std::floor(u)) * 0x1'0000'0000LL);
uint32_t texelStepX = (uint32_t)(int64_t)(scaleU * 0x1'0000'0000LL);
uint32_t texelStepY = (uint32_t)(int64_t)(scaleV * 0x1'0000'0000LL);
fixed_t maxfrac = ((top + length) << FRACBITS) - 1;
dc_texturefrac = MAX(dc_texturefrac, 0);
dc_texturefrac = MIN(dc_texturefrac, maxfrac);
if (dc_iscale > 0)
dc_count = MIN(dc_count, (maxfrac - dc_texturefrac + dc_iscale - 1) / dc_iscale);
else if (dc_iscale < 0)
dc_count = MIN(dc_count, (dc_texturefrac - dc_iscale) / (-dc_iscale));
DrawMaskedColumn(thread, x, y1, mceilingclip[x], mfloorclip[x], texelX, texelStepX, texelStepY, scaleV, flipY, tex, texwidth, texheight, bgra, style);
(thread->Drawers(dc_viewport)->*colfunc)(*this);
}
span++;
upos += ustepX;
wpos += wstepX;
lightpos += lightstep;
}
}
void SpriteDrawerArgs::DrawMaskedColumnBgra(RenderThread *thread, int x, fixed_t iscale, FSoftwareTexture *tex, fixed_t col, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked)
void SpriteDrawerArgs::DrawMasked2D(RenderThread* thread, double x0, double x1, double y0, double y1, FSoftwareTexture* tex, FRenderStyle style)
{
dc_viewport = thread->Viewport.get();
dc_x = x;
dc_iscale = iscale;
int sx0 = MAX((int)x0, 0);
int sx1 = MIN((int)x1, viewwidth);
int sy0 = MAX((int)y0, 0);
int sy1 = MIN((int)y1, viewheight);
// Normalize to 0-1 range:
double uv_stepd = FIXED2DBL(dc_iscale);
double v_step = uv_stepd / tex->GetPhysicalHeight();
if (sx0 >= sx1 || sy0 >= sy1)
return;
// Convert to uint32_t:
dc_iscale = (uint32_t)(v_step * (1 << 30));
float ustepX = 1.0f / (x1 - x0);
float vstepY = 1.0f / (y1 - y0);
float upos = ustepX * (sx0 + 0.5f - x0);
// Texture mipmap and filter selection:
fixed_t xoffset = col;
uint32_t texelStepX = (uint32_t)(int64_t)(ustepX * 0x1'0000'0000LL);
uint32_t texelStepY = (uint32_t)(int64_t)(vstepY * 0x1'0000'0000LL);
double xmagnitude = 1.0; // To do: pass this into R_DrawMaskedColumn
double ymagnitude = fabs(uv_stepd);
double magnitude = MAX(ymagnitude, xmagnitude);
double min_lod = -1000.0;
double lod = MAX(log2(magnitude) + r_lod_bias, min_lod);
bool magnifying = lod < 0.0f;
bool bgra = thread->Viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input;
int texwidth = tex->GetPhysicalWidth();
int texheight = tex->GetPhysicalHeight();
int mipmap_offset = 0;
int mip_width = tex->GetPhysicalWidth();
int mip_height = tex->GetPhysicalHeight();
uint32_t xpos = (uint32_t)((((uint64_t)xoffset) << FRACBITS) / mip_width);
if (r_mipmap && tex->Mipmapped() && mip_width > 1 && mip_height > 1)
vstepY *= texheight;
for (int x = sx0; x < sx1; x++)
{
int level = (int)lod;
while (level > 0 && mip_width > 1 && mip_height > 1)
{
mipmap_offset += mip_width * mip_height;
level--;
mip_width = MAX(mip_width >> 1, 1);
mip_height = MAX(mip_height >> 1, 1);
}
float u = upos;
uint32_t texelX = (uint32_t)(int64_t)((u - std::floor(u)) * 0x1'0000'0000LL);
DrawMaskedColumn(thread, x, sy0, sy0, sy1, texelX, texelStepX, texelStepY, vstepY, false, tex, texwidth, texheight, bgra, style);
upos += ustepX;
}
xoffset = (xpos >> FRACBITS) * mip_width;
}
const uint32_t *pixels = tex->GetPixelsBgra() + mipmap_offset;
bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter);
if (filter_nearest)
void SpriteDrawerArgs::DrawMaskedColumn(RenderThread* thread, int x, int y1, int cliptop, int clipbottom, uint32_t texelX, uint32_t texelStepX, uint32_t texelStepY, float scaleV, bool flipY, FSoftwareTexture* tex, int texwidth, int texheight, bool bgra, FRenderStyle style)
{
const FSoftwareTextureSpan* span;
if (bgra)
{
xoffset = MAX(MIN(xoffset, (mip_width << FRACBITS) - 1), 0);
double xmagnitude = fabs(static_cast<int32_t>(texelStepX)* (1.0 / 0x1'0000'0000LL));
double ymagnitude = fabs(static_cast<int32_t>(texelStepY)* (1.0 / 0x1'0000'0000LL));
double magnitude = MAX(ymagnitude, xmagnitude);
double min_lod = -1000.0;
double lod = MAX(log2(magnitude) + r_lod_bias, min_lod);
bool magnifying = lod < 0.0f;
int tx = xoffset >> FRACBITS;
dc_source = (uint8_t*)(pixels + tx * mip_height);
dc_source2 = nullptr;
dc_textureheight = mip_height;
dc_texturefracx = 0;
int mipmap_offset = 0;
int mip_width = texwidth;
int mip_height = texheight;
if (r_mipmap && tex->Mipmapped() && mip_width > 1 && mip_height > 1)
{
int level = (int)lod;
while (level > 0 && mip_width > 1 && mip_height > 1)
{
mipmap_offset += mip_width * mip_height;
level--;
mip_width = MAX(mip_width >> 1, 1);
mip_height = MAX(mip_height >> 1, 1);
}
}
const uint32_t* pixels = tex->GetPixelsBgra() + mipmap_offset;
fixed_t xoffset = (texelX >> 16)* mip_width;
bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter);
if (filter_nearest)
{
xoffset = MAX(MIN(xoffset, (mip_width << FRACBITS) - 1), 0);
int tx = xoffset >> FRACBITS;
dc_source = (uint8_t*)(pixels + tx * mip_height);
dc_source2 = nullptr;
dc_textureheight = mip_height;
dc_texturefracx = 0;
}
else
{
xoffset = MAX(MIN(xoffset - (FRACUNIT / 2), (mip_width << FRACBITS) - 1), 0);
int tx0 = xoffset >> FRACBITS;
int tx1 = MIN(tx0 + 1, mip_width - 1);
dc_source = (uint8_t*)(pixels + tx0 * mip_height);
dc_source2 = (uint8_t*)(pixels + tx1 * mip_height);
dc_textureheight = mip_height;
dc_texturefracx = (xoffset >> (FRACBITS - 4)) & 15;
}
int col = ((texelX >> 16)* texwidth) >> 16;
tex->GetColumnBgra(col, &span);
dc_iscale = (uint32_t)(int64_t)(scaleV / texheight * (1 << 30));
dc_x = x;
while (span->Length != 0)
{
const int length = span->Length;
const int top = span->TopOffset;
// calculate unclipped screen coordinates for post
dc_yl = (int)(y1 + top / scaleV + 0.5f);
dc_yh = (int)(y1 + (top + length) / scaleV + 0.5f);
if (flipY)
std::swap(dc_yl, dc_yh);
dc_yl = std::max(dc_yl, cliptop);
dc_yh = std::min(dc_yh, clipbottom);
if (dc_yl <= dc_yh)
{
double v = ((dc_yl + 0.5f - y1) * scaleV) / texheight;
dc_texturefrac = (uint32_t)(v * (1 << 30));
SetDest(dc_viewport, dc_x, dc_yl);
dc_count = dc_yh - dc_yl;
dc_yl--;
(thread->Drawers(dc_viewport)->*colfunc)(*this);
}
span++;
}
}
else
{
xoffset = MAX(MIN(xoffset - (FRACUNIT / 2), (mip_width << FRACBITS) - 1), 0);
int col = ((texelX >> 16)* texwidth) >> 16;
dc_source = tex->GetColumn(style, col, &span);
dc_source2 = nullptr;
int tx0 = xoffset >> FRACBITS;
int tx1 = MIN(tx0 + 1, mip_width - 1);
dc_source = (uint8_t*)(pixels + tx0 * mip_height);
dc_source2 = (uint8_t*)(pixels + tx1 * mip_height);
dc_textureheight = mip_height;
dc_texturefracx = (xoffset >> (FRACBITS - 4)) & 15;
}
dc_iscale = FLOAT2FIXED(scaleV);
dc_x = x;
// Grab the posts we need to draw
const FSoftwareTextureSpan *span;
tex->GetColumnBgra(col >> FRACBITS, &span);
FSoftwareTextureSpan unmaskedSpan[2];
if (unmasked)
{
span = unmaskedSpan;
unmaskedSpan[0].TopOffset = 0;
unmaskedSpan[0].Length = tex->GetPhysicalHeight();
unmaskedSpan[1].TopOffset = 0;
unmaskedSpan[1].Length = 0;
}
// Draw each span post
while (span->Length != 0)
{
const int length = span->Length;
const int top = span->TopOffset;
// calculate unclipped screen coordinates for post
dc_yl = (int)(sprtopscreen + spryscale * top + 0.5);
dc_yh = (int)(sprtopscreen + spryscale * (top + length) + 0.5) - 1;
if (sprflipvert)
while (span->Length != 0)
{
swapvalues(dc_yl, dc_yh);
}
const int length = span->Length;
const int top = span->TopOffset;
if (dc_yh >= mfloorclip[dc_x])
{
dc_yh = mfloorclip[dc_x] - 1;
}
if (dc_yl < mceilingclip[dc_x])
{
dc_yl = mceilingclip[dc_x];
}
// calculate unclipped screen coordinates for post
dc_yl = (int)(y1 + top / scaleV + 0.5f);
dc_yh = (int)(y1 + (top + length) / scaleV + 0.5f);
if (dc_yl <= dc_yh)
{
SetDest(dc_viewport, dc_x, dc_yl);
dc_count = dc_yh - dc_yl + 1;
if (flipY)
std::swap(dc_yl, dc_yh);
double v = ((dc_yl + 0.5 - sprtopscreen) / spryscale) / tex->GetPhysicalHeight();
dc_texturefrac = (uint32_t)(v * (1 << 30));
dc_yl = std::max(dc_yl, cliptop);
dc_yh = std::min(dc_yh, clipbottom);
(thread->Drawers(dc_viewport)->*colfunc)(*this);
if (dc_yl < dc_yh)
{
dc_texturefrac = FLOAT2FIXED((dc_yl + 0.5f - y1) * scaleV);
SetDest(thread->Viewport.get(), dc_x, dc_yl);
dc_count = dc_yh - dc_yl;
dc_yl--;
fixed_t maxfrac = ((top + length) << FRACBITS) - 1;
dc_texturefrac = MAX(dc_texturefrac, 0);
dc_texturefrac = MIN(dc_texturefrac, maxfrac);
if (dc_iscale > 0)
dc_count = MIN(dc_count, (maxfrac - dc_texturefrac + dc_iscale - 1) / dc_iscale);
else if (dc_iscale < 0)
dc_count = MIN(dc_count, (dc_texturefrac - dc_iscale) / (-dc_iscale));
(thread->Drawers(dc_viewport)->*colfunc)(*this);
}
span++;
}
span++;
}
}
@ -517,35 +542,10 @@ namespace swrenderer
return SetStyle(viewport, style, FLOAT2FIXED(alpha), translation, color, light);
}
void SpriteDrawerArgs::FillColumn(RenderThread *thread)
{
thread->Drawers(dc_viewport)->FillColumn(*this);
}
void SpriteDrawerArgs::DrawVoxelBlocks(RenderThread *thread, const VoxelBlock *blocks, int blockcount)
{
SetDest(thread->Viewport.get(), 0, 0);
thread->Drawers(dc_viewport)->DrawVoxelBlocks(*this, blocks, blockcount);
#if 0
if (dc_viewport->RenderTarget->IsBgra())
{
double v = vPos / (double)voxelsCount / FRACUNIT;
double vstep = vStep / (double)voxelsCount / FRACUNIT;
dc_texturefrac = (int)(v * (1 << 30));
dc_iscale = (int)(vstep * (1 << 30));
}
else
{
dc_texturefrac = vPos;
dc_iscale = vStep;
}
dc_texturefracx = 0;
dc_source = voxels;
dc_source2 = 0;
dc_textureheight = voxelsCount;
(thread->Drawers(dc_viewport)->*colfunc)(*this);
#endif
}
void SpriteDrawerArgs::SetDest(RenderViewport *viewport, int x, int y)

View file

@ -9,7 +9,8 @@ struct FLightNode;
namespace swrenderer
{
class RenderThread;
class ProjectedWallTexcoords;
struct FWallCoords;
class ProjectedWallLight;
class VoxelBlock
{
@ -29,14 +30,11 @@ namespace swrenderer
bool SetStyle(RenderViewport *viewport, FRenderStyle style, fixed_t alpha, int translation, uint32_t color, const ColormapLight &light);
bool SetStyle(RenderViewport *viewport, FRenderStyle style, float alpha, int translation, uint32_t color, const ColormapLight &light);
void SetDest(RenderViewport *viewport, int x, int y);
void SetCount(int count) { dc_count = count; }
void SetSolidColor(int color) { dc_color = color; dc_color_bgra = GPalette.BaseColors[color]; }
void SetDynamicLight(uint32_t color) { dynlightcolor = color; }
void DrawMaskedColumn(RenderThread* thread, int x, FSoftwareTexture* WallSpriteTile, const ProjectedWallTexcoords& walltexcoords, bool sprflipvert, const short* mfloorclip, const short* mceilingclip, FRenderStyle style);
void DrawMaskedColumn(RenderThread *thread, int x, fixed_t iscale, FSoftwareTexture *texture, fixed_t column, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, FRenderStyle style, bool unmasked = false);
void FillColumn(RenderThread *thread);
void DrawMasked(RenderThread* thread, double topZ, double scale, bool flipX, bool flipY, const FWallCoords& WallC, const ProjectedWallLight& light, FSoftwareTexture* texture, const short* mfloorclip, const short* mceilingclip, FRenderStyle style);
void DrawMasked2D(RenderThread *thread, double x0, double x1, double y0, double y1, FSoftwareTexture* texture, FRenderStyle style);
void DrawVoxelBlocks(RenderThread *thread, const VoxelBlock *blocks, int blockcount);
uint8_t *Dest() const { return dc_dest; }
@ -71,9 +69,13 @@ namespace swrenderer
RenderViewport *Viewport() const { return dc_viewport; }
private:
void DrawMaskedColumn(RenderThread* thread, int x, int y1, int cliptop, int clipbottom, uint32_t texelX, uint32_t texelStepX, uint32_t texelStepY, float scaleV, bool flipY, FSoftwareTexture* tex, int texwidth, int texheight, bool bgra, FRenderStyle style);
void SetDest(RenderViewport* viewport, int x, int y);
void SetCount(int count) { dc_count = count; }
bool SetBlendFunc(int op, fixed_t fglevel, fixed_t bglevel, int flags);
static fixed_t GetAlpha(int type, fixed_t alpha);
void DrawMaskedColumnBgra(RenderThread *thread, int x, fixed_t iscale, FSoftwareTexture *tex, fixed_t column, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked);
uint8_t *dc_dest = nullptr;
int dc_dest_y = 0;