From dd8a114bb821ccc5e3a15e34151de71a3d907ffd Mon Sep 17 00:00:00 2001
From: Magnus Norddahl <dpjudas@users.noreply.github.com>
Date: Thu, 21 Sep 2017 05:39:16 +0200
Subject: [PATCH] - Initial dynamic light support for softpoly

---
 src/polyrenderer/drawers/poly_draw_args.h     |  13 +++
 src/polyrenderer/drawers/poly_drawer32_sse2.h | 110 ++++++++++++++++--
 src/polyrenderer/drawers/poly_triangle.cpp    |   6 +
 src/polyrenderer/drawers/screen_triangle.h    |  14 ++-
 src/polyrenderer/scene/poly_plane.cpp         |  60 ++++++++++
 src/polyrenderer/scene/poly_plane.h           |   1 +
 src/polyrenderer/scene/poly_wall.cpp          |  61 ++++++++++
 src/polyrenderer/scene/poly_wall.h            |   2 +
 8 files changed, 257 insertions(+), 10 deletions(-)

diff --git a/src/polyrenderer/drawers/poly_draw_args.h b/src/polyrenderer/drawers/poly_draw_args.h
index c53b0260f3..6417669a04 100644
--- a/src/polyrenderer/drawers/poly_draw_args.h
+++ b/src/polyrenderer/drawers/poly_draw_args.h
@@ -55,6 +55,13 @@ struct TriVertex
 	float u, v;
 };
 
+struct PolyLight
+{
+	uint32_t color;
+	float x, y, z;
+	float radius;
+};
+
 class PolyDrawArgs
 {
 public:
@@ -73,6 +80,7 @@ public:
 	void SetStyle(const FRenderStyle &renderstyle, double alpha, uint32_t fillcolor, uint32_t translationID, FTexture *texture, bool fullbright);
 	void SetTransform(const TriMatrix *objectToClip) { mObjectToClip = objectToClip; }
 	void SetColor(uint32_t bgra, uint8_t palindex);
+	void SetLights(PolyLight *lights, int numLights) { mLights = lights; mNumLights = numLights; }
 	void DrawArray(PolyRenderThread *thread, const TriVertex *vertices, int vcount, PolyDrawMode mode = PolyDrawMode::Triangles);
 
 	const TriMatrix *ObjectToClip() const { return mObjectToClip; }
@@ -119,6 +127,9 @@ public:
 	bool NearestFilter() const { return mNearestFilter; }
 	bool FixedLight() const { return mFixedLight; }
 
+	PolyLight *Lights() const { return mLights; }
+	int NumLights() const { return mNumLights; }
+
 private:
 	const TriMatrix *mObjectToClip = nullptr;
 	const TriVertex *mVertices = nullptr;
@@ -155,6 +166,8 @@ private:
 	bool mSimpleShade = true;
 	bool mNearestFilter = true;
 	bool mFixedLight = false;
+	PolyLight *mLights = nullptr;
+	int mNumLights = 0;
 };
 
 class RectDrawArgs
diff --git a/src/polyrenderer/drawers/poly_drawer32_sse2.h b/src/polyrenderer/drawers/poly_drawer32_sse2.h
index a88b92bd6a..1cef537194 100644
--- a/src/polyrenderer/drawers/poly_drawer32_sse2.h
+++ b/src/polyrenderer/drawers/poly_drawer32_sse2.h
@@ -142,9 +142,64 @@ namespace TriScreenDrawerModes
 		}
 	}
 
-	template<typename ShadeModeT>
-	FORCEINLINE __m128i VECTORCALL Shade32(__m128i fgcolor, __m128i mlight, unsigned int ifgcolor0, unsigned int ifgcolor1, int desaturate, __m128i inv_desaturate, __m128i shade_fade, __m128i shade_light)
+	FORCEINLINE __m128i VECTORCALL AddLights(__m128i material, __m128i fgcolor, const PolyLight *lights, int num_lights, __m128 worldpos, __m128 worldnormal)
 	{
+		__m128i lit = _mm_setzero_si128();
+
+		for (int i = 0; i != num_lights; i++)
+		{
+			__m128 m256 = _mm_set1_ps(256.0f);
+			__m128 mSignBit = _mm_set1_ps(-0.0f);
+
+			__m128 lightpos = _mm_loadu_ps(&lights[i].x);
+			__m128 light_radius = _mm_load_ss(&lights[i].radius);
+
+			__m128 is_attenuated = _mm_cmpge_ss(light_radius, _mm_setzero_ps());
+			is_attenuated = _mm_shuffle_ps(is_attenuated, is_attenuated, _MM_SHUFFLE(0, 0, 0, 0));
+			light_radius = _mm_andnot_ps(mSignBit, light_radius);
+
+			// L = light-pos
+			// dist = sqrt(dot(L, L))
+			// distance_attenuation = 1 - MIN(dist * (1/radius), 1)
+			__m128 L = _mm_sub_ps(lightpos, worldpos);
+			__m128 dist2 = _mm_mul_ps(L, L);
+			dist2 = _mm_add_ss(dist2, _mm_add_ss(_mm_shuffle_ps(dist2, dist2, _MM_SHUFFLE(0, 0, 0, 1)), _mm_shuffle_ps(dist2, dist2, _MM_SHUFFLE(0, 0, 0, 2))));
+			__m128 rcp_dist = _mm_rsqrt_ss(dist2);
+			__m128 dist = _mm_mul_ss(dist2, rcp_dist);
+			__m128 distance_attenuation = _mm_sub_ss(m256, _mm_min_ss(_mm_mul_ss(dist, light_radius), m256));
+			distance_attenuation = _mm_shuffle_ps(distance_attenuation, distance_attenuation, _MM_SHUFFLE(0, 0, 0, 0));
+
+			// The simple light type
+			__m128 simple_attenuation = distance_attenuation;
+
+			// The point light type
+			// diffuse = dot(N,L) * attenuation
+			__m128 dotNL = _mm_mul_ps(worldnormal, L);
+			dotNL = _mm_add_ss(dotNL, _mm_add_ss(_mm_shuffle_ps(dotNL, dotNL, _MM_SHUFFLE(0, 0, 0, 1)), _mm_shuffle_ps(dotNL, dotNL, _MM_SHUFFLE(0, 0, 0, 2))));
+			__m128 point_attenuation = _mm_mul_ss(dotNL, distance_attenuation);
+			point_attenuation = _mm_shuffle_ps(point_attenuation, point_attenuation, _MM_SHUFFLE(0, 0, 0, 0));
+
+			__m128i attenuation = _mm_cvtps_epi32(_mm_or_ps(_mm_and_ps(is_attenuated, simple_attenuation), _mm_andnot_ps(is_attenuated, point_attenuation)));
+			attenuation = _mm_packs_epi32(_mm_shuffle_epi32(attenuation, _MM_SHUFFLE(0, 0, 0, 0)), _mm_shuffle_epi32(attenuation, _MM_SHUFFLE(1, 1, 1, 1)));
+
+			__m128i light_color = _mm_cvtsi32_si128(lights[i].color);
+			light_color = _mm_unpacklo_epi8(light_color, _mm_setzero_si128());
+			light_color = _mm_shuffle_epi32(light_color, _MM_SHUFFLE(1, 0, 1, 0));
+
+			lit = _mm_add_epi16(lit, _mm_srli_epi16(_mm_mullo_epi16(light_color, attenuation), 8));
+		}
+
+		lit = _mm_min_epi16(lit, _mm_set1_epi16(256));
+
+		fgcolor = _mm_add_epi16(fgcolor, _mm_srli_epi16(_mm_mullo_epi16(material, lit), 8));
+		fgcolor = _mm_min_epi16(fgcolor, _mm_set1_epi16(255));
+		return fgcolor;
+	}
+
+	template<typename ShadeModeT>
+	FORCEINLINE __m128i VECTORCALL Shade32(__m128i fgcolor, __m128i mlight, unsigned int ifgcolor0, unsigned int ifgcolor1, int desaturate, __m128i inv_desaturate, __m128i shade_fade, __m128i shade_light, const PolyLight *lights, int num_lights, __m128 worldpos, __m128 worldnormal)
+	{
+		__m128i material = fgcolor;
 		if (ShadeModeT::Mode == (int)ShadeMode::Simple)
 		{
 			fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, mlight), 8);
@@ -168,7 +223,8 @@ namespace TriScreenDrawerModes
 			fgcolor = _mm_srli_epi16(_mm_add_epi16(shade_fade, fgcolor), 8);
 			fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, shade_light), 8);
 		}
-		return fgcolor;
+
+		return AddLights(material, fgcolor, lights, num_lights, worldpos, worldnormal);
 	}
 
 	template<typename BlendT>
@@ -333,6 +389,11 @@ private:
 
 		int fuzzpos = (ScreenTriangle::FuzzStart + destX * 123 + destY) % FUZZTABLE;
 
+		auto lights = args->uniforms->Lights();
+		auto num_lights = args->uniforms->NumLights();
+		__m128 worldpos = _mm_setzero_ps();
+		__m128 worldnormal = _mm_setzero_ps();
+
 		// Calculate gradients
 		const ShadedTriVertex &v1 = *args->v1;
 		ScreenTriangleStepVariables gradientX = args->gradientX;
@@ -341,9 +402,15 @@ private:
 		blockPosY.W = v1.w + gradientX.W * (destX - v1.x) + gradientY.W * (destY - v1.y);
 		blockPosY.U = v1.u * v1.w + gradientX.U * (destX - v1.x) + gradientY.U * (destY - v1.y);
 		blockPosY.V = v1.v * v1.w + gradientX.V * (destX - v1.x) + gradientY.V * (destY - v1.y);
+		blockPosY.WorldX = v1.worldX * v1.w + gradientX.WorldX * (destX - v1.x) + gradientY.WorldX * (destY - v1.y);
+		blockPosY.WorldY = v1.worldY * v1.w + gradientX.WorldY * (destX - v1.x) + gradientY.WorldY * (destY - v1.y);
+		blockPosY.WorldZ = v1.worldZ * v1.w + gradientX.WorldZ * (destX - v1.x) + gradientY.WorldZ * (destY - v1.y);
 		gradientX.W *= 8.0f;
 		gradientX.U *= 8.0f;
 		gradientX.V *= 8.0f;
+		gradientX.WorldX *= 8.0f;
+		gradientX.WorldY *= 8.0f;
+		gradientX.WorldZ *= 8.0f;
 
 		// Output
 		uint32_t * RESTRICT destOrg = (uint32_t*)args->dest;
@@ -404,10 +471,16 @@ private:
 				fixed_t lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT);
 				lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask);
 
+				__m128 mrcpW = _mm_set1_ps(1.0f / blockPosY.W);
+				worldpos = _mm_mul_ps(_mm_loadu_ps(&blockPosY.WorldX), mrcpW);
+
 				ScreenTriangleStepVariables blockPosX = blockPosY;
 				blockPosX.W += gradientX.W;
 				blockPosX.U += gradientX.U;
 				blockPosX.V += gradientX.V;
+				blockPosX.WorldX += gradientX.WorldX;
+				blockPosX.WorldY += gradientX.WorldY;
+				blockPosX.WorldZ += gradientX.WorldZ;
 
 				rcpW = 0x01000000 / blockPosX.W;
 				int32_t nextU = (int32_t)(blockPosX.U * rcpW);
@@ -462,7 +535,7 @@ private:
 
 					// Shade and blend
 					__m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128());
-					fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light);
+					fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light, lights, num_lights, worldpos, worldnormal);
 					__m128i outcolor = Blend32<BlendT>(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], ifgshade[0], ifgshade[1], srcalpha, destalpha);
 
 					// Store result
@@ -472,6 +545,9 @@ private:
 				blockPosY.W += gradientY.W;
 				blockPosY.U += gradientY.U;
 				blockPosY.V += gradientY.V;
+				blockPosY.WorldX += gradientY.WorldX;
+				blockPosY.WorldY += gradientY.WorldY;
+				blockPosY.WorldZ += gradientY.WorldZ;
 
 				dest += pitch;
 			}
@@ -488,10 +564,16 @@ private:
 				fixed_t lightpos = FRACUNIT - (fixed_t)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT);
 				lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask);
 
+				__m128 mrcpW = _mm_set1_ps(1.0f / blockPosY.W);
+				worldpos = _mm_mul_ps(_mm_loadu_ps(&blockPosY.WorldX), mrcpW);
+
 				ScreenTriangleStepVariables blockPosX = blockPosY;
 				blockPosX.W += gradientX.W;
 				blockPosX.U += gradientX.U;
 				blockPosX.V += gradientX.V;
+				blockPosX.WorldX += gradientX.WorldX;
+				blockPosX.WorldY += gradientX.WorldY;
+				blockPosX.WorldZ += gradientX.WorldZ;
 
 				rcpW = 0x01000000 / blockPosX.W;
 				int32_t nextU = (int32_t)(blockPosX.U * rcpW);
@@ -551,7 +633,7 @@ private:
 
 					// Shade and blend
 					__m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128());
-					fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light);
+					fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light, lights, num_lights, worldpos, worldnormal);
 					__m128i outcolor = Blend32<BlendT>(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], ifgshade[0], ifgshade[1], srcalpha, destalpha);
 
 					// Store result
@@ -565,6 +647,9 @@ private:
 				blockPosY.W += gradientY.W;
 				blockPosY.U += gradientY.U;
 				blockPosY.V += gradientY.V;
+				blockPosY.WorldX += gradientY.WorldX;
+				blockPosY.WorldY += gradientY.WorldY;
+				blockPosY.WorldZ += gradientY.WorldZ;
 
 				dest += pitch;
 			}
@@ -579,10 +664,16 @@ private:
 				fixed_t lightpos = FRACUNIT - (fixed_t)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT);
 				lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask);
 
+				__m128 mrcpW = _mm_set1_ps(1.0f / blockPosY.W);
+				worldpos = _mm_mul_ps(_mm_loadu_ps(&blockPosY.WorldX), mrcpW);
+
 				ScreenTriangleStepVariables blockPosX = blockPosY;
 				blockPosX.W += gradientX.W;
 				blockPosX.U += gradientX.U;
 				blockPosX.V += gradientX.V;
+				blockPosX.WorldX += gradientX.WorldX;
+				blockPosX.WorldY += gradientX.WorldY;
+				blockPosX.WorldZ += gradientX.WorldZ;
 
 				rcpW = 0x01000000 / blockPosX.W;
 				int32_t nextU = (int32_t)(blockPosX.U * rcpW);
@@ -642,7 +733,7 @@ private:
 
 					// Shade and blend
 					__m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128());
-					fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light);
+					fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light, lights, num_lights, worldpos, worldnormal);
 					__m128i outcolor = Blend32<BlendT>(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], ifgshade[0], ifgshade[1], srcalpha, destalpha);
 
 					// Store result
@@ -656,6 +747,9 @@ private:
 				blockPosY.W += gradientY.W;
 				blockPosY.U += gradientY.U;
 				blockPosY.V += gradientY.V;
+				blockPosY.WorldX += gradientY.WorldX;
+				blockPosY.WorldY += gradientY.WorldY;
+				blockPosY.WorldZ += gradientY.WorldZ;
 
 				dest += pitch;
 			}
@@ -798,7 +892,7 @@ private:
 
 				// Shade and blend
 				__m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128());
-				fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light);
+				fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light, nullptr, 0, _mm_setzero_ps(), _mm_setzero_ps());
 				__m128i outcolor = Blend32<BlendT>(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], ifgshade[0], ifgshade[1], srcalpha, destalpha);
 
 				// Store result
@@ -826,7 +920,7 @@ private:
 
 				// Shade and blend
 				__m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128());
-				fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light);
+				fgcolor = Shade32<ShadeModeT>(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade_lit, shade_light, nullptr, 0, _mm_setzero_ps(), _mm_setzero_ps());
 				__m128i outcolor = Blend32<BlendT>(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], ifgshade[0], ifgshade[1], srcalpha, destalpha);
 
 				// Store result
diff --git a/src/polyrenderer/drawers/poly_triangle.cpp b/src/polyrenderer/drawers/poly_triangle.cpp
index 2448e25447..beb2834987 100644
--- a/src/polyrenderer/drawers/poly_triangle.cpp
+++ b/src/polyrenderer/drawers/poly_triangle.cpp
@@ -155,6 +155,9 @@ ShadedTriVertex PolyTriangleDrawer::shade_vertex(const PolyDrawArgs &drawargs, c
 	sv.w = position.W;
 	sv.u = v.u;
 	sv.v = v.v;
+	sv.worldX = v.x;
+	sv.worldY = v.y;
+	sv.worldZ = v.z;
 
 	// Calculate gl_ClipDistance[i]
 	for (int i = 0; i < 3; i++)
@@ -446,6 +449,9 @@ int PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, ShadedTriVertex *
 			v.w += verts[w].w * weight;
 			v.u += verts[w].u * weight;
 			v.v += verts[w].v * weight;
+			v.worldX += verts[w].worldX * weight;
+			v.worldY += verts[w].worldY * weight;
+			v.worldZ += verts[w].worldZ * weight;
 		}
 	}
 	return inputverts;
diff --git a/src/polyrenderer/drawers/screen_triangle.h b/src/polyrenderer/drawers/screen_triangle.h
index d51c8b63e0..a76dc4b466 100644
--- a/src/polyrenderer/drawers/screen_triangle.h
+++ b/src/polyrenderer/drawers/screen_triangle.h
@@ -46,11 +46,13 @@ struct ShadedTriVertex
 	float x, y, z, w;
 	float u, v;
 	float clipDistance[3];
+	float worldX, worldY, worldZ;
 };
 
 struct ScreenTriangleStepVariables
 {
 	float W, U, V;
+	float WorldX, WorldY, WorldZ, Padding; // Padding so it can be loaded directly into a XMM register
 };
 
 struct TriDrawTriangleArgs
@@ -79,11 +81,19 @@ struct TriDrawTriangleArgs
 			return false;
 
 		gradientX.W = FindGradientX(bottomX, 1.0f, 1.0f, 1.0f);
-		gradientY.W = FindGradientY(bottomY, 1.0f, 1.0f, 1.0f);
 		gradientX.U = FindGradientX(bottomX, v1->u, v2->u, v3->u);
-		gradientY.U = FindGradientY(bottomY, v1->u, v2->u, v3->u);
 		gradientX.V = FindGradientX(bottomX, v1->v, v2->v, v3->v);
+		gradientX.WorldX = FindGradientX(bottomX, v1->worldX, v2->worldX, v3->worldX);
+		gradientX.WorldY = FindGradientX(bottomX, v1->worldY, v2->worldY, v3->worldY);
+		gradientX.WorldZ = FindGradientX(bottomX, v1->worldZ, v2->worldZ, v3->worldZ);
+
+		gradientY.W = FindGradientY(bottomY, 1.0f, 1.0f, 1.0f);
+		gradientY.U = FindGradientY(bottomY, v1->u, v2->u, v3->u);
 		gradientY.V = FindGradientY(bottomY, v1->v, v2->v, v3->v);
+		gradientY.WorldX = FindGradientY(bottomY, v1->worldX, v2->worldX, v3->worldX);
+		gradientY.WorldY = FindGradientY(bottomY, v1->worldY, v2->worldY, v3->worldY);
+		gradientY.WorldZ = FindGradientY(bottomY, v1->worldZ, v2->worldZ, v3->worldZ);
+
 		return true;
 	}
 
diff --git a/src/polyrenderer/scene/poly_plane.cpp b/src/polyrenderer/scene/poly_plane.cpp
index 668053a01d..766d4260b8 100644
--- a/src/polyrenderer/scene/poly_plane.cpp
+++ b/src/polyrenderer/scene/poly_plane.cpp
@@ -32,6 +32,7 @@
 #include "polyrenderer/scene/poly_light.h"
 #include "polyrenderer/poly_renderthread.h"
 #include "p_lnspec.h"
+#include "a_dynlight.h"
 
 EXTERN_CVAR(Int, r_3dfloors)
 
@@ -253,6 +254,7 @@ void RenderPolyPlane::Render(PolyRenderThread *thread, const TriMatrix &worldToC
 
 	if (!isSky)
 	{
+		SetDynLights(thread, args, sub);
 		args.SetTexture(tex);
 		args.SetStyle(TriBlendMode::TextureOpaque);
 		args.DrawArray(thread, vertices, sub->numlines, PolyDrawMode::TriangleFan);
@@ -277,6 +279,64 @@ void RenderPolyPlane::Render(PolyRenderThread *thread, const TriMatrix &worldToC
 	}
 }
 
+void RenderPolyPlane::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub)
+{
+	FLightNode *light_list = sub->lighthead;
+
+	auto cameraLight = PolyCameraLight::Instance();
+	if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr))
+	{
+		args.SetLights(nullptr, 0); // [SP] Don't draw dynlights if invul/lightamp active
+		return;
+	}
+
+	// Calculate max lights that can touch the wall so we can allocate memory for the list
+	int max_lights = 0;
+	FLightNode *cur_node = light_list;
+	while (cur_node)
+	{
+		if (!(cur_node->lightsource->flags2&MF2_DORMANT))
+			max_lights++;
+		cur_node = cur_node->nextLight;
+	}
+
+	if (max_lights == 0)
+	{
+		args.SetLights(nullptr, 0);
+		return;
+	}
+
+	int dc_num_lights = 0;
+	PolyLight *dc_lights = thread->FrameMemory->AllocMemory<PolyLight>(max_lights);
+
+	// Setup lights
+	cur_node = light_list;
+	while (cur_node)
+	{
+		if (!(cur_node->lightsource->flags2&MF2_DORMANT))
+		{
+			//bool is_point_light = (cur_node->lightsource->lightflags & LF_ATTENUATE) != 0;
+
+			// To do: cull lights not touching subsector
+
+			uint32_t red = cur_node->lightsource->GetRed();
+			uint32_t green = cur_node->lightsource->GetGreen();
+			uint32_t blue = cur_node->lightsource->GetBlue();
+
+			auto &light = dc_lights[dc_num_lights++];
+			light.x = (float)cur_node->lightsource->X();
+			light.y = (float)cur_node->lightsource->Y();
+			light.z = (float)cur_node->lightsource->Z();
+			light.radius = 256.0f / cur_node->lightsource->GetRadius();
+			light.color = (red << 16) | (green << 8) | blue;
+		}
+
+		cur_node = cur_node->nextLight;
+	}
+
+	args.SetLights(dc_lights, dc_num_lights);
+}
+
 void RenderPolyPlane::RenderSkyWalls(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub, sector_t *frontsector, FSectorPortal *portal, PolyDrawSectorPortal *polyportal, bool ceiling, double skyHeight, const PolyPlaneUVTransform &transform)
 {
 	for (uint32_t i = 0; i < sub->numlines; i++)
diff --git a/src/polyrenderer/scene/poly_plane.h b/src/polyrenderer/scene/poly_plane.h
index 918640369c..2293f4f633 100644
--- a/src/polyrenderer/scene/poly_plane.h
+++ b/src/polyrenderer/scene/poly_plane.h
@@ -62,6 +62,7 @@ public:
 private:
 	void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, subsector_t *sub, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals);
 	void RenderSkyWalls(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub, sector_t *frontsector, FSectorPortal *portal, PolyDrawSectorPortal *polyportal, bool ceiling, double skyHeight, const PolyPlaneUVTransform &transform);
+	void SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub);
 };
 
 class Render3DFloorPlane
diff --git a/src/polyrenderer/scene/poly_wall.cpp b/src/polyrenderer/scene/poly_wall.cpp
index 57e24da37d..c90052b228 100644
--- a/src/polyrenderer/scene/poly_wall.cpp
+++ b/src/polyrenderer/scene/poly_wall.cpp
@@ -35,6 +35,7 @@
 #include "polyrenderer/scene/poly_light.h"
 #include "polyrenderer/poly_renderthread.h"
 #include "g_levellocals.h"
+#include "a_dynlight.h"
 
 EXTERN_CVAR(Bool, r_drawmirrors)
 EXTERN_CVAR(Bool, r_fogboundary)
@@ -326,6 +327,8 @@ void RenderPolyWall::Render(PolyRenderThread *thread, const TriMatrix &worldToCl
 		args.SetTexture(Texture);
 	args.SetClipPlane(0, clipPlane);
 
+	SetDynLights(thread, args);
+
 	if (FogBoundary)
 	{
 		args.SetStyle(TriBlendMode::FogBoundary);
@@ -365,6 +368,64 @@ void RenderPolyWall::Render(PolyRenderThread *thread, const TriMatrix &worldToCl
 	RenderPolyDecal::RenderWallDecals(thread, worldToClip, clipPlane, LineSeg, StencilValue);
 }
 
+void RenderPolyWall::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args)
+{
+	FLightNode *light_list = (LineSeg && LineSeg->sidedef) ? LineSeg->sidedef->lighthead : nullptr;
+
+	auto cameraLight = PolyCameraLight::Instance();
+	if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr))
+	{
+		args.SetLights(nullptr, 0); // [SP] Don't draw dynlights if invul/lightamp active
+		return;
+	}
+
+	// Calculate max lights that can touch the wall so we can allocate memory for the list
+	int max_lights = 0;
+	FLightNode *cur_node = light_list;
+	while (cur_node)
+	{
+		if (!(cur_node->lightsource->flags2&MF2_DORMANT))
+			max_lights++;
+		cur_node = cur_node->nextLight;
+	}
+
+	if (max_lights == 0)
+	{
+		args.SetLights(nullptr, 0);
+		return;
+	}
+
+	int dc_num_lights = 0;
+	PolyLight *dc_lights = thread->FrameMemory->AllocMemory<PolyLight>(max_lights);
+
+	// Setup lights
+	cur_node = light_list;
+	while (cur_node)
+	{
+		if (!(cur_node->lightsource->flags2&MF2_DORMANT))
+		{
+			//bool is_point_light = (cur_node->lightsource->lightflags & LF_ATTENUATE) != 0;
+
+			// To do: cull lights not touching wall
+
+			uint32_t red = cur_node->lightsource->GetRed();
+			uint32_t green = cur_node->lightsource->GetGreen();
+			uint32_t blue = cur_node->lightsource->GetBlue();
+
+			auto &light = dc_lights[dc_num_lights++];
+			light.x = (float)cur_node->lightsource->X();
+			light.y = (float)cur_node->lightsource->Y();
+			light.z = (float)cur_node->lightsource->Z();
+			light.radius = 256.0f / cur_node->lightsource->GetRadius();
+			light.color = (red << 16) | (green << 8) | blue;
+		}
+
+		cur_node = cur_node->nextLight;
+	}
+
+	args.SetLights(dc_lights, dc_num_lights);
+}
+
 void RenderPolyWall::DrawStripes(PolyRenderThread *thread, PolyDrawArgs &args, TriVertex *vertices)
 {
 	const auto &lightlist = Line->frontsector->e->XFloor.lightlist;
diff --git a/src/polyrenderer/scene/poly_wall.h b/src/polyrenderer/scene/poly_wall.h
index 40b6d55090..6cb7aae2ff 100644
--- a/src/polyrenderer/scene/poly_wall.h
+++ b/src/polyrenderer/scene/poly_wall.h
@@ -68,6 +68,8 @@ private:
 	int GetLightLevel();
 	void DrawStripes(PolyRenderThread *thread, PolyDrawArgs &args, TriVertex *vertices);
 
+	void SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args);
+
 	static bool IsFogBoundary(sector_t *front, sector_t *back);
 	static FTexture *GetTexture(const line_t *Line, const side_t *Side, side_t::ETexpart texpart);
 };