diff --git a/src/rendering/swrenderer/drawers/r_draw_pal.cpp b/src/rendering/swrenderer/drawers/r_draw_pal.cpp index 22d89cb9391..3aca6797a28 100644 --- a/src/rendering/swrenderer/drawers/r_draw_pal.cpp +++ b/src/rendering/swrenderer/drawers/r_draw_pal.cpp @@ -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(GPalette.BaseColors[fg].r + ((lit_r * material_r) >> 8), 255); + lit_g = min(GPalette.BaseColors[fg].g + ((lit_g * material_g) >> 8), 255); + lit_b = min(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,61 +2788,138 @@ namespace swrenderer x1 = 0; width++; - while (width >= SPANSIZE) + if (args.dc_num_lights == 0) { - iz += izstep; - uz += uzstep; - vz += vzstep; + 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); + 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--) + 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) { - fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); - x1++; - u += stepu; - v += stepv; + 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; + } + } } - startu = endu; - startv = endv; - width -= SPANSIZE; } - if (width > 0) + else { - 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 + 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) { - 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)]); + 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; + } } } } diff --git a/src/rendering/swrenderer/drawers/r_draw_pal.h b/src/rendering/swrenderer/drawers/r_draw_pal.h index 9777560e60c..b3c54be184c 100644 --- a/src/rendering/swrenderer/drawers/r_draw_pal.h +++ b/src/rendering/swrenderer/drawers/r_draw_pal.h @@ -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); diff --git a/src/rendering/swrenderer/plane/r_slopeplane.cpp b/src/rendering/swrenderer/plane/r_slopeplane.cpp index 7a00e70ad23..d35901a5497 100644 --- a/src/rendering/swrenderer/plane/r_slopeplane.cpp +++ b/src/rendering/swrenderer/plane/r_slopeplane.cpp @@ -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(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); } } diff --git a/src/rendering/swrenderer/plane/r_slopeplane.h b/src/rendering/swrenderer/plane/r_slopeplane.h index b074492fc9e..b237237278f 100644 --- a/src/rendering/swrenderer/plane/r_slopeplane.h +++ b/src/rendering/swrenderer/plane/r_slopeplane.h @@ -52,5 +52,6 @@ namespace swrenderer DVector3 planeNormal; double planeD; + VisiblePlaneLight* light_list; }; } diff --git a/src/rendering/swrenderer/viewport/r_viewport.h b/src/rendering/swrenderer/viewport/r_viewport.h index a43da8f2550..2a38b00867b 100644 --- a/src/rendering/swrenderer/viewport/r_viewport.h +++ b/src/rendering/swrenderer/viewport/r_viewport.h @@ -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;