UDK doesn't support custom/deferred shadow mapping fully (Real time shading is slow as shit and a hack job), so to get any sort of LOS system at all I embedded custom LOSVolumes in the maps that would define an area to project the view frustum from. Then in the Canvas / HUD system I would project the Volumes location into the 2D space and draw polygons to define where the shadow is. This had the unfortunate side effect of being drawn after the geometry and thus not allowing proper depth rendering (cutting off players heads if they jump too high before I raised the walls, ect ect). It would have been decently good if the canvas element in UDK allowed for opacity in the textures but sadly it does not.
In UE4 it would be far simpler because you can just use a custom shader to manage a LOS/Fog of war system using deferred shadow mapping to project the real time shadows.
Also for the record, UE4 is fantastic.
I've pasted the LOS triangle loading function below, the code base was messy as hell but mostly functional. I didn't see a point in optimizing it as I dropped the project not that long after I had a working system. If you want to look at it more in depth the NoXPlayerHud.uc file in the source has the drawing loop and that function in it.
function DrawLOS(Canvas Canvas1)
{
local NoXPawn myPawn;
local NoXPlayerController myController;
local LOSVolume LOSVol;
local Vector temVec;
local Vector PawnLocation;
//local Vector2D PawnLoc;
local Vector One,Two,Three,Four,Five,Six,Seven,Eight;
local Vector OneLoc,TwoLoc,ThreeLoc,FourLoc,FiveLoc,SixLoc,SevenLoc,EightLoc;
local int CheckX,CheckX2;
local int CheckY,CheckY2;
local Vector screenLoc;
local Vector outLoc;
local Vector hitLoc;
local Vector hitNormal;
local Vector StartTrace;
local Vector EndTrace;
local Vector2D ScreenSize;
//local Canvas Canvas1;
if( !bDrawTrueSight )
return;
myController = NoXPlayerController(PlayerOwner);
myPawn = NoXPawn(myController.Pawn);
PawnLocation = myPawn.Location;
PawnLocation.Z = 0;
PawnScreenLoc = Canvas1.Project(PawnLocation);
ScreenSize.X = self.SizeX;
ScreenSize.Y = self.SizeY;
Canvas.Deproject(ScreenSize,screenLoc,outLoc);
StartTrace = (IsometricCamera(myController.PlayerCamera)).ViewTarget.POV.Location;
EndTrace = StartTrace + outLoc * 5000;
Trace(hitLoc, hitNormal, EndTrace, StartTrace, false);
CheckX = hitLoc.X;//myPawn.Location.X//+400; //500
CheckY = hitLoc.Y;//myPawn.Location.Y//+400;//500
ScreenSize.X = 0;
ScreenSize.Y = 0;
Canvas.Deproject(ScreenSize,screenLoc,outLoc);
EndTrace = StartTrace + outLoc * 5000;
Trace(hitLoc, hitNormal, EndTrace, StartTrace, false);
CheckX2 = hitLoc.X;//myPawn.Location.X//-300;//400
CheckY2 = hitLoc.Y;//myPawn.Location.Y//-300;//400
foreach AllActors(class'LOSVolume', LOSVol)
{
temVec.X = LOSVol.BrushComponent.Bounds.Origin.X + LOSVol.BrushComponent.Bounds.BoxExtent.X;
temVec.Y = LOSVol.BrushComponent.Bounds.Origin.Y + LOSVol.BrushComponent.Bounds.BoxExtent.Y;
temVec.Z = LOSVol.BrushComponent.Bounds.Origin.Z + LOSVol.BrushComponent.Bounds.BoxExtent.Z;
//Two = Canvas1.Project(temVec);
TwoLoc = temVec;
temVec.X = LOSVol.BrushComponent.Bounds.Origin.X - LOSVol.BrushComponent.Bounds.BoxExtent.X;
temVec.Y = LOSVol.BrushComponent.Bounds.Origin.Y + LOSVol.BrushComponent.Bounds.BoxExtent.Y;
temVec.Z = LOSVol.BrushComponent.Bounds.Origin.Z + LOSVol.BrushComponent.Bounds.BoxExtent.Z;
//One = Canvas1.Project(temVec);
OneLoc = temVec;
temVec.X = LOSVol.BrushComponent.Bounds.Origin.X - LOSVol.BrushComponent.Bounds.BoxExtent.X;
temVec.Y = LOSVol.BrushComponent.Bounds.Origin.Y - LOSVol.BrushComponent.Bounds.BoxExtent.Y;
temVec.Z = LOSVol.BrushComponent.Bounds.Origin.Z + LOSVol.BrushComponent.Bounds.BoxExtent.Z;
//Three = Canvas1.Project(temVec);
ThreeLoc = temVec;
temVec.X = LOSVol.BrushComponent.Bounds.Origin.X + LOSVol.BrushComponent.Bounds.BoxExtent.X;
temVec.Y = LOSVol.BrushComponent.Bounds.Origin.Y - LOSVol.BrushComponent.Bounds.BoxExtent.Y;
temVec.Z = LOSVol.BrushComponent.Bounds.Origin.Z + LOSVol.BrushComponent.Bounds.BoxExtent.Z;
//Four = Canvas1.Project(temVec);
FourLoc = temVec;
temVec.X = LOSVol.BrushComponent.Bounds.Origin.X - LOSVol.BrushComponent.Bounds.BoxExtent.X;
temVec.Y = LOSVol.BrushComponent.Bounds.Origin.Y + LOSVol.BrushComponent.Bounds.BoxExtent.Y;
temVec.Z = 0;//LOSVol.BrushComponent.Bounds.Origin.Z - LOSVol.BrushComponent.Bounds.BoxExtent.Z;
//Six = Canvas1.Project(temVec);
SixLoc = temVec;
temVec.X = LOSVol.BrushComponent.Bounds.Origin.X - LOSVol.BrushComponent.Bounds.BoxExtent.X;
temVec.Y = LOSVol.BrushComponent.Bounds.Origin.Y - LOSVol.BrushComponent.Bounds.BoxExtent.Y;
temVec.Z = 0;//LOSVol.BrushComponent.Bounds.Origin.Z - LOSVol.BrushComponent.Bounds.BoxExtent.Z;
//Five = Canvas1.Project(temVec);
FiveLoc = temVec;
temVec.X = LOSVol.BrushComponent.Bounds.Origin.X + LOSVol.BrushComponent.Bounds.BoxExtent.X;
temVec.Y = LOSVol.BrushComponent.Bounds.Origin.Y + LOSVol.BrushComponent.Bounds.BoxExtent.Y;
temVec.Z = 0;//LOSVol.BrushComponent.Bounds.Origin.Z - LOSVol.BrushComponent.Bounds.BoxExtent.Z;
//Seven = Canvas1.Project(temVec);
SevenLoc = temVec;
temVec.X = LOSVol.BrushComponent.Bounds.Origin.X + LOSVol.BrushComponent.Bounds.BoxExtent.X;
temVec.Y = LOSVol.BrushComponent.Bounds.Origin.Y - LOSVol.BrushComponent.Bounds.BoxExtent.Y;
temVec.Z = 0;//LOSVol.BrushComponent.Bounds.Origin.Z - LOSVol.BrushComponent.Bounds.BoxExtent.Z;
//Eight = Canvas1.Project(temVec);
EightLoc = temVec;
if((
(LOSVol.BrushComponent.Bounds.Origin.X > CheckX || LOSVol.BrushComponent.Bounds.Origin.X < CheckX2) &&
(LOSVol.BrushComponent.Bounds.Origin.Y > CheckY || LOSVol.BrushComponent.Bounds.Origin.Y < CheckY2) &&
(FourLoc.X > CheckX || FourLoc.X < CheckX2) &&
(FourLoc.Y > CheckY || FourLoc.Y < CheckY2) &&
(TwoLoc.X > CheckX || TwoLoc.X < CheckX2) &&
(TwoLoc.Y > CheckY || TwoLoc.Y < CheckY2) &&
(SixLoc.X > CheckX || SixLoc.X < CheckX2) &&
(SixLoc.Y > CheckY || SixLoc.Y < CheckY2) &&
(FiveLoc.X > CheckX || FiveLoc.X < CheckX2) &&
(FiveLoc.Y > CheckY || FiveLoc.Y < CheckY2)
))
continue;
One = Canvas1.Project(OneLoc);
Two = Canvas1.Project(TwoLoc);
Three = Canvas1.Project(ThreeLoc);
Four = Canvas1.Project(FourLoc);
Five = Canvas1.Project(FiveLoc);
Six = Canvas1.Project(SixLoc);
Seven = Canvas1.Project(SevenLoc);
Eight = Canvas1.Project(EightLoc);
//MatCanvas1.DrawColor = MakeColor(255,0,0,255);
//MatCanvas1.SetPos(MatCanvas1.SizeX/2,MatCanvas1.SizeY/2);
//MatCanvas1.DrawBox(4,4);
/*Canvas1.DrawColor = MakeColor(255,255,255,255);
Canvas1.SetPos(One.X,One.Y);
Canvas1.DrawText("One");
//Canvas1.DrawColor = MakeColor(255,0,0,255);
Canvas1.SetPos(Six.X,Six.Y);
Canvas1.DrawText("Six");
//Canvas1.DrawColor = MakeColor(255,0,0,255);
Canvas1.SetPos(Eight.X,Eight.Y);
Canvas1.DrawText("Eight");*/
if( LOSVol.bIsWindow )
AddWashTriangle(One,Two,Three,Four,true);
else if( LOSVol.bIsDoor )
AddWashTriangle(One,Two,Three,Four,false,true);
else
AddWashTriangle(One,Two,Three,Four,false);
if( LOSVol.bIsWindow )
{
if((
((LOSVol.BrushComponent.Bounds.Origin.X-LOSVol.BrushComponent.Bounds.BoxExtent.X < myPawn.Location.X+80 &&
LOSVol.BrushComponent.Bounds.Origin.X+LOSVol.BrushComponent.Bounds.BoxExtent.X > myPawn.Location.X-80) &&
LOSVol.BrushComponent.Bounds.Origin.Y-LOSVol.BrushComponent.Bounds.BoxExtent.Y < myPawn.Location.Y+80 &&
LOSVol.BrushComponent.Bounds.Origin.Y+LOSVol.BrushComponent.Bounds.BoxExtent.Y > myPawn.Location.Y-80)
))
continue;
}
if( PawnLocation.Y <= SixLoc.Y /*&& PawnLocation.X <= EightLoMatCanvas1.X*/ || PawnLocation.Y/*-25*/ <= SevenLoc.Y /*&& PawnLocation.X >= FiveLoMatCanvas1.X*/)
{
// Side 2
AddViewTriangle(Four, Three, Eight, Five);
//AddViewTriangle(One, Two, Six, Seven);
}
if( PawnLocation.Y >= SevenLoc.Y /*&& PawnLocation.X <= SevenLoMatCanvas1.X*/ || PawnLocation.Y >= SixLoc.Y /*&& PawnLocation.X >= SixLoMatCanvas1.X*/)
{
// Side 1
//TriangleList.Remove(0,TriangleList.Length);
AddViewTriangle(One, Two, Six, Seven);
}
if( PawnLocation.X >= FiveLoc.X /*&& PawnLocation.Y >= EightLoMatCanvas1.X*/ || PawnLocation.X >= SixLoc.Y/* && PawnLocation.Y <= SevenLoMatCanvas1.X*/)
{
// Side 3
//TriangleList.Remove(0,TriangleList.Length);
AddViewTriangle(Four, Two, Eight, Seven);
//AddViewTriangle(Three, One, Five, Six);
}
if( PawnLocation.X <= FiveLoc.X /*&& PawnLocation.Y >= FiveLoMatCanvas1.X*/ || PawnLocation.X <= SixLoc.Y /*&& PawnLocation.Y <= SixLoMatCanvas1.X*/)
{
// Side 4
//TriangleList.Remove(0,TriangleList.Length);
AddViewTriangle(Three, One, Five, Six);
//AddViewTriangle(Four, Two, Eight, Seven);
}
// Now need to run the wall ends
// Wall only visual aspects
/* if( !LOSVol.bDisableAllCorners )
{
if( LOSVol.bEnableCorner3 && (PawnLocation.Y >= FiveLoc.Y && PawnLocation.X <= FiveLoc.X || PawnLocation.Y <= FiveLoc.Y && PawnLocation.X >= FiveLoc.X))
{
// Corner 3
AddViewTriangle(Five, Three, Five, Five);
}
if( LOSVol.bEnableCorner1 && (PawnLocation.Y <= SixLoc.Y && PawnLocation.X <= SixLoc.X || PawnLocation.Y >= SixLoc.Y && PawnLocation.X >= SixLoc.X))
{
// Corner 1
AddViewTriangle(Six, One, Six, Six);
}
if( LOSVol.bEnableCorner2 && (PawnLocation.Y >= SevenLoc.Y && PawnLocation.X <= SevenLoc.X ))
{
// Corner 2
AddViewTriangle(Seven, Two, Seven, Two );
}
if( LOSVol.bEnableCorner2 && (PawnLocation.Y <= SevenLoc.Y && PawnLocation.X >= SevenLoc.X))
{
// Corner 2
AddViewTriangle(Seven, Two, Seven, Seven);
}
if( LOSVol.bEnableCorner4 && (PawnLocation.Y <= EightLoc.Y && PawnLocation.X <= EightLoc.X ))
{
// Corner 4
AddViewTriangle(Eight, Four, Eight, Eight);
}
}*/
}
}