Originally Posted by 3run
- transform into clip-space
- then transform from clip-space to normalized device coordinate space (NDC space)
- and then transform from this [-1, 1] (NDC) space to window-relative coordinates!

The very same I said wink

Originally Posted by 3run
does vecLightViewPos[i].w contain lightrange?


I thought yes but no and it should frown
I can only name it a bug because it is forcing to include full world space light vectors to get the light ranges while the W component of vecLightViewPos is empty. brff...

Here goes an example of the concepts I tryed to explain but with no bugs grin
Code
#include <acknex.h>
#include <default.c>

MATERIAL *mtlProjectedLights =
{
	effect = "
		float4x4 matProj;
		const float4 vecViewPort;
		
		float iLights;
		float4 vecLightPos[8];
		float4 vecLightViewPos[8];
		float4 vecLightDir[8];
		float4 vecLightColor[8];
		
		texture TargetMap;
		
		sampler postTex = sampler_state
		{
			Texture = <TargetMap>;
			MinFilter = Point; // No need of linear interpolation on pixel to pixel pp effects. 
		};
		
		float4 PS (
			in float2 inTex : TEXCOORD0, 
			in float2 inPos : VPOS) : COLOR0
		{
			float4 color0 = float4(0, 0, 0, 1.0f);
			
			for(float i = 0; i < iLights; i += 1.0f)
			{
				if(vecLightDir[i].w > 0) // Avoid spotlights
					continue;
				
				float4 clipSpacePos = mul(float4(vecLightViewPos[i].xyz, 1.0f), matProj);
				float2 ndcSpacePos = clipSpacePos.xy / clipSpacePos.w;
				float2 windowSpacePos = float2(1.0f + ndcSpacePos.x, 1.0f - ndcSpacePos.y) * vecViewPort.xy * 0.5f;
				
				float4 clipSpaceOutterPoint = mul(float4(vecLightViewPos[i].x - vecLightPos[i].w, vecLightViewPos[i].yz, 1.0f), matProj);
				float ndcSpaceOutterPoint = clipSpaceOutterPoint.x / clipSpaceOutterPoint.w;
				
				float windowSpaceRadius = (ndcSpacePos.x - ndcSpaceOutterPoint) * vecViewPort.x * 0.5f;
				color0.rgb += vecLightColor[i].rgb * saturate((1.0f - length(inPos - windowSpacePos) / windowSpaceRadius));
			}
			
			float3 albedo = tex2D(postTex, inTex).rgb;
			color0.rgb = albedo * saturate(color0.rgb);
			
			return color0;
		}
		
		technique
		{
			pass one
			{
				ZWriteEnable = False;
				AlphaBlendEnable = False;
				PixelShader = compile ps_3_0 PS();
			}
		}	
	";
}

VIEW *camProjectedLights =
{
	material = mtlProjectedLights;
	flags = PROCESS_TARGET | CHILD | SHOW;
}

void main () {
	video_mode = 10;
	sun_light = 0;
	wait(3);
	level_load("level.wmb");
	camera->arc = 100;
	camera->stage = camProjectedLights;
	
	while(!key_esc)
		wait(1);
	
	sys_exit(NULL);
}

#define light_blue skill1
#define light_green skill2
#define light_red skill3
#define light_range skill4
//uses: light_blue, light_green, light_red, light_range
action actLight () {
	my->blue = my->light_blue;
	my->green = my->light_green;
	my->red = my->light_red;
	my->lightrange = my->light_range;
	
	vec_set(&my->skill20, &my->x);
	my->skill23 = random(360);
	
	my->flags |= UNLIT | LIGHT | BRIGHT;
	
	while(1)
	{
		my->lightrange = my->light_range + random(10); // flicker
		my->x = my->skill20 + fsin(my->skill23 + total_ticks * 4, 80);
		my->y = my->skill21 + fcos(my->skill23 + total_ticks * 4, 80);
		wait(1);
	}
}


Salud!