Add dynamic lights to pal slope drawer

This commit is contained in:
Magnus Norddahl 2023-02-18 22:05:23 +01:00 committed by Christoph Oelckers
parent 087050c201
commit 18bc384807
5 changed files with 269 additions and 43 deletions

View file

@ -1814,6 +1814,66 @@ namespace swrenderer
return RGB256k.All[((lit_r >> 2) << 12) | ((lit_g >> 2) << 6) | (lit_b >> 2)];
}
uint8_t SWPalDrawers::AddLightsTilted(const DrawerLight* lights, int num_lights, float viewpos_x, float viewpos_y, float viewpos_z, float nx, float ny, float nz, uint8_t fg, uint8_t material)
{
uint32_t lit_r = 0;
uint32_t lit_g = 0;
uint32_t lit_b = 0;
// Screen space to view space
viewpos_z = 1.0f / viewpos_z;
viewpos_x *= viewpos_z;
viewpos_y *= viewpos_y;
for (int i = 0; i < num_lights; i++)
{
uint32_t light_color_r = RPART(lights[i].color);
uint32_t light_color_g = GPART(lights[i].color);
uint32_t light_color_b = BPART(lights[i].color);
// L = light-pos
// dist = sqrt(dot(L, L))
// attenuation = 1 - min(dist * (1/radius), 1)
float Lx = lights[i].x - viewpos_x;
float Ly = lights[i].y - viewpos_y;
float Lz = lights[i].z - viewpos_z;
float dist2 = Lx * Lx + Ly * Ly + Lz * Lz;
#ifdef NO_SSE
float rcp_dist = 1.0f / (dist2 * 0.01f);
#else
float rcp_dist = _mm_cvtss_f32(_mm_rsqrt_ss(_mm_load_ss(&dist2)));
#endif
float dist = dist2 * rcp_dist;
float distance_attenuation = (256.0f - min(dist * lights[i].radius, 256.0f));
// The simple light type
float simple_attenuation = distance_attenuation;
// The point light type
// diffuse = dot(N,L) * attenuation
float dotNL = max(nx * Lx + ny * Ly + nz * Lz, 0.0f);
float point_attenuation = dotNL * rcp_dist * distance_attenuation;
uint32_t attenuation = (uint32_t)(lights[i].z == 0.0f ? simple_attenuation : point_attenuation);
lit_r += (light_color_r * attenuation) >> 8;
lit_g += (light_color_g * attenuation) >> 8;
lit_b += (light_color_b * attenuation) >> 8;
}
if (lit_r == 0 && lit_g == 0 && lit_b == 0)
return fg;
uint32_t material_r = GPalette.BaseColors[material].r;
uint32_t material_g = GPalette.BaseColors[material].g;
uint32_t material_b = GPalette.BaseColors[material].b;
lit_r = min<uint32_t>(GPalette.BaseColors[fg].r + ((lit_r * material_r) >> 8), 255);
lit_g = min<uint32_t>(GPalette.BaseColors[fg].g + ((lit_g * material_g) >> 8), 255);
lit_b = min<uint32_t>(GPalette.BaseColors[fg].b + ((lit_b * material_b) >> 8), 255);
return RGB256k.All[((lit_r >> 2) << 12) | ((lit_g >> 2) << 6) | (lit_b >> 2)];
}
void SWPalDrawers::DrawSpan(const SpanDrawerArgs& args)
{
const uint8_t* _source = args.TexturePixels();
@ -2728,62 +2788,139 @@ namespace swrenderer
x1 = 0;
width++;
while (width >= SPANSIZE)
if (args.dc_num_lights == 0)
{
iz += izstep;
uz += uzstep;
vz += vzstep;
double endz = 1.f / iz;
double endu = uz*endz;
double endv = vz*endz;
uint32_t stepu = (uint32_t)int64_t((endu - startu) * INVSPAN);
uint32_t stepv = (uint32_t)int64_t((endv - startv) * INVSPAN);
u = (uint32_t)(int64_t(startu) + pviewx);
v = (uint32_t)(int64_t(startv) + pviewy);
for (i = SPANSIZE - 1; i >= 0; i--)
while (width >= SPANSIZE)
{
fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]);
x1++;
u += stepu;
v += stepv;
}
startu = endu;
startv = endv;
width -= SPANSIZE;
}
if (width > 0)
{
if (width == 1)
{
u = (uint32_t)int64_t(startu);
v = (uint32_t)int64_t(startv);
fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]);
}
else
{
double left = width;
iz += plane_sz[0] * left;
uz += plane_su[0] * left;
vz += plane_sv[0] * left;
iz += izstep;
uz += uzstep;
vz += vzstep;
double endz = 1.f / iz;
double endu = uz*endz;
double endv = vz*endz;
left = 1.f / left;
uint32_t stepu = (uint32_t)int64_t((endu - startu) * left);
uint32_t stepv = (uint32_t)int64_t((endv - startv) * left);
double endu = uz * endz;
double endv = vz * endz;
uint32_t stepu = (uint32_t)int64_t((endu - startu) * INVSPAN);
uint32_t stepv = (uint32_t)int64_t((endv - startv) * INVSPAN);
u = (uint32_t)(int64_t(startu) + pviewx);
v = (uint32_t)(int64_t(startv) + pviewy);
for (; width != 0; width--)
for (i = SPANSIZE - 1; i >= 0; i--)
{
fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]);
x1++;
u += stepu;
v += stepv;
}
startu = endu;
startv = endv;
width -= SPANSIZE;
}
if (width > 0)
{
if (width == 1)
{
u = (uint32_t)int64_t(startu);
v = (uint32_t)int64_t(startv);
fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]);
}
else
{
double left = width;
iz += plane_sz[0] * left;
uz += plane_su[0] * left;
vz += plane_sv[0] * left;
double endz = 1.f / iz;
double endu = uz * endz;
double endv = vz * endz;
left = 1.f / left;
uint32_t stepu = (uint32_t)int64_t((endu - startu) * left);
uint32_t stepv = (uint32_t)int64_t((endv - startv) * left);
u = (uint32_t)(int64_t(startu) + pviewx);
v = (uint32_t)(int64_t(startv) + pviewy);
for (; width != 0; width--)
{
fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]);
x1++;
u += stepu;
v += stepv;
}
}
}
}
else
{
auto lights = args.dc_lights;
auto num_lights = args.dc_num_lights;
auto normal = args.dc_normal;
auto viewpos = args.dc_viewpos;
auto dc_viewpos_step = args.dc_viewpos_step;
while (width >= SPANSIZE)
{
iz += izstep;
uz += uzstep;
vz += vzstep;
double endz = 1.f / iz;
double endu = uz * endz;
double endv = vz * endz;
uint32_t stepu = (uint32_t)int64_t((endu - startu) * INVSPAN);
uint32_t stepv = (uint32_t)int64_t((endv - startv) * INVSPAN);
u = (uint32_t)(int64_t(startu) + pviewx);
v = (uint32_t)(int64_t(startv) + pviewy);
for (i = SPANSIZE - 1; i >= 0; i--)
{
uint8_t material = _source[(v >> vshift) | ((u >> ushift) & umask)];
uint8_t fg = *(tiltlighting[x1] + material);
fb[x1] = AddLightsTilted(lights, num_lights, viewpos.X, viewpos.Y, viewpos.Z, normal.X, normal.Y, normal.Z, fg, material);
x1++;
u += stepu;
v += stepv;
viewpos += dc_viewpos_step;
}
startu = endu;
startv = endv;
width -= SPANSIZE;
}
if (width > 0)
{
if (width == 1)
{
u = (uint32_t)int64_t(startu);
v = (uint32_t)int64_t(startv);
uint8_t material = _source[(v >> vshift) | ((u >> ushift) & umask)];
uint8_t fg = *(tiltlighting[x1] + material);
fb[x1] = AddLightsTilted(lights, num_lights, viewpos.X, viewpos.Y, viewpos.Z, normal.X, normal.Y, normal.Z, fg, material);
}
else
{
double left = width;
iz += plane_sz[0] * left;
uz += plane_su[0] * left;
vz += plane_sv[0] * left;
double endz = 1.f / iz;
double endu = uz * endz;
double endv = vz * endz;
left = 1.f / left;
uint32_t stepu = (uint32_t)int64_t((endu - startu) * left);
uint32_t stepv = (uint32_t)int64_t((endv - startv) * left);
u = (uint32_t)(int64_t(startu) + pviewx);
v = (uint32_t)(int64_t(startv) + pviewy);
for (; width != 0; width--)
{
uint8_t material = _source[(v >> vshift) | ((u >> ushift) & umask)];
uint8_t fg = *(tiltlighting[x1] + material);
fb[x1] = AddLightsTilted(lights, num_lights, viewpos.X, viewpos.Y, viewpos.Z, normal.X, normal.Y, normal.Z, fg, material);
x1++;
u += stepu;
v += stepv;
viewpos += dc_viewpos_step;
}
}
}
}
#endif

View file

@ -171,6 +171,7 @@ namespace swrenderer
inline static uint8_t AddLightsColumn(const DrawerLight* lights, int num_lights, float viewpos_z, uint8_t fg, uint8_t material);
inline static uint8_t AddLightsSpan(const DrawerLight* lights, int num_lights, float viewpos_z, uint8_t fg, uint8_t material);
inline static uint8_t AddLightsTilted(const DrawerLight* lights, int num_lights, float viewpos_x, float viewpos_y, float viewpos_z, float nx, float ny, float nz, uint8_t fg, uint8_t material);
inline static uint8_t AddLights(uint8_t fg, uint8_t material, uint32_t lit_r, uint32_t lit_g, uint32_t lit_b);
void CalcTiltedLighting(double lstart, double lend, int width, int planeshade, uint8_t* basecolormapdata);

View file

@ -93,7 +93,10 @@ namespace swrenderer
planeNormal.Y = worldNormal.X * viewport->viewpoint.Cos + worldNormal.Y * viewport->viewpoint.Sin;
planeNormal.Z = worldNormal.Z;
planeD = -planeNormal.Z * (pl->height.ZatPoint(viewport->viewpoint.Pos.X, viewport->viewpoint.Pos.Y) - viewport->viewpoint.Pos.Z);
if (Thread->Portal->MirrorFlags & RF_XFLIP)
planeNormal.X = -planeNormal.X;
light_list = pl->lights;
drawerargs.SetSolidColor(3);
drawerargs.SetTexture(Thread, texture);
@ -204,6 +207,84 @@ namespace swrenderer
void RenderSlopePlane::RenderLine(int y, int x1, int x2)
{
if (r_dynlights)
{
int tx = x1;
bool mirror = !!(Thread->Portal->MirrorFlags & RF_XFLIP);
if (mirror)
tx = viewwidth - tx - 1;
// Find row position in view space
DVector3 viewposX1 = Thread->Viewport->ScreenToViewPos(x1, y, planeNormal, planeD);
DVector3 viewposX2 = Thread->Viewport->ScreenToViewPos(x2, y, planeNormal, planeD);
// Convert to screen space
viewposX1.Z = 1.0 / viewposX1.Z;
viewposX1.X *= viewposX1.Z;
viewposX1.Y *= viewposX1.Z;
viewposX2.Z = 1.0 / viewposX2.Z;
viewposX2.X *= viewposX2.Z;
viewposX2.Y *= viewposX2.Z;
drawerargs.dc_viewpos.X = viewposX1.X;
drawerargs.dc_viewpos.Y = viewposX1.Y;
drawerargs.dc_viewpos.Z = viewposX1.Z;
drawerargs.dc_viewpos_step.X = viewposX2.X - viewposX1.X;
drawerargs.dc_viewpos_step.Y = viewposX2.Y - viewposX1.Y;
drawerargs.dc_viewpos_step.Z = viewposX2.Z - viewposX1.Z;
// Plane normal
drawerargs.dc_normal.X = planeNormal.X;
drawerargs.dc_normal.Y = planeNormal.Y;
drawerargs.dc_normal.Z = planeNormal.Z;
// Calculate max lights that can touch the row so we can allocate memory for the list
int max_lights = 0;
VisiblePlaneLight* cur_node = light_list;
while (cur_node)
{
if (cur_node->lightsource->IsActive())
max_lights++;
cur_node = cur_node->next;
}
drawerargs.dc_num_lights = 0;
drawerargs.dc_lights = Thread->FrameMemory->AllocMemory<DrawerLight>(max_lights);
// Setup lights for row
cur_node = light_list;
while (cur_node)
{
if (cur_node->lightsource->IsActive())
{
double lightX = cur_node->lightsource->X() - Thread->Viewport->viewpoint.Pos.X;
double lightY = cur_node->lightsource->Y() - Thread->Viewport->viewpoint.Pos.Y;
double lightZ = cur_node->lightsource->Z() - Thread->Viewport->viewpoint.Pos.Z;
float lx = (float)(lightX * Thread->Viewport->viewpoint.Sin - lightY * Thread->Viewport->viewpoint.Cos);
float ly = (float)(lightX * Thread->Viewport->viewpoint.TanCos + lightY * Thread->Viewport->viewpoint.TanSin);
float lz = (float)lightZ;
uint32_t red = cur_node->lightsource->GetRed();
uint32_t green = cur_node->lightsource->GetGreen();
uint32_t blue = cur_node->lightsource->GetBlue();
auto& light = drawerargs.dc_lights[drawerargs.dc_num_lights++];
light.x = lx;
light.y = ly;
light.z = lz;
light.radius = 256.0f / cur_node->lightsource->GetRadius();
light.color = (red << 16) | (green << 8) | blue;
}
cur_node = cur_node->next;
}
}
else
{
drawerargs.dc_num_lights = 0;
}
drawerargs.DrawTiltedSpan(Thread, y, x1, x2, plane_sz, plane_su, plane_sv, plane_shade, lightlevel, foggy, planelightfloat, pviewx, pviewy, basecolormap);
}
}

View file

@ -52,5 +52,6 @@ namespace swrenderer
DVector3 planeNormal;
double planeD;
VisiblePlaneLight* light_list;
};
}

View file

@ -82,6 +82,12 @@ namespace swrenderer
return (CenterY - screenY - 0.5) / FocalLengthY * viewZ;
}
DVector3 ScreenToViewPos(int screenX, int screenY, const DVector3& plane, double planeD)
{
double viewZ = -planeD / ((screenX + 0.5 - CenterX) / FocalLengthX * plane.X + (CenterY - screenY - 0.5) / FocalLengthY * plane.Y + plane.Z);
return DVector3(ScreenToViewX(screenX, viewZ), ScreenToViewY(screenY, viewZ), viewZ);
}
FLevelLocals *Level()
{
return viewpoint.ViewLevel;