# Difference between revisions of "FOV using recursive shadowcasting - improved"

Code and improvement for recursive shadowcasting - Henri Hakl

1. Introduction

This text expands the article by Bj????rn Bergstr????m called "FOV using recursive shadowcasting". The article in question is excellent and describes one of the best visibility determination algorithms for roguelike environments.

The reader is encouraged to read Bj????rn's article, as this text assumes that the reader knows the method described. This text offers a subtle improvement to the algorithm as well as pseudocode. The improvement is primarily one that allows a more elegant representation, it does not influence the efficiency of the algorithm.

To outline, Bj????rn describes an improvement to Gordon Lipford's shadowcasting article "Computing LOS for Large Areas" by making use of recursive computation. Shadowcasting divides the Field of View (FOV) into eight sections and traces the rows or columns in the sections according to simple laws that ensure that a minimum of cells need to be investigated: Cells determined to be in shadow are skipped and cells are generally only visited once (with possible exception of cells on the boundaries of sections).

Bj????rn proceeds to explain finer mechanics in considerable detail and with well drawn ASCII art. He omits to offer code in the article, but on-site resources offer an implementation that should suffice for many.

2. Improvement

Each recursion in the algorithm described by Bj????rn loops through rows/columns until a break condition is met (row/column ends with obstruction). This works because any regions closer to the starting point are already finished, and areas further away are (if appropriate) handled by recursive calls to the shadowcasting code.

For the sake of elegance this breaking condition can be handled differently: Instead of looping if the break-condition isn't met, it facilitates code readability to simply create a recursive call to the shadowcaster. The change does not influence things on an efficiency level, however it does clean up the code somewhat.

Partial pseudocode, describes one octant:

``` Scan(depth, startslope, endslope)

init y
init x

while current_slope has not reached endslope do
if (x,y) within visual range then
if (x,y) blocked and prior not blocked then
Scan(depth + 1, startslope, new_endslope)
if (x,y) not blocked and prior blocked then
new_startslope
set (x,y) visible
progress (x,y)

regress (x,y)

if depth < visual range and (x,y) not blocked
Scan(depth + 1, startslope, endslope)
end
```

The following code excerpt comes from my own implementation in Turbo Pascal:

```  mv -> global describing view distance
mw -> mv * mv

Octants are divided as follows:

\ 1 | 2 /
8         3
--       --
7         4
/ 6 | 5 \

function GetSlopeStd(x1, y1, x2, y2 : single) : single;
begin
GetSlopeStd := (x1 - x2) / (y1 - y2);
end;

function GetSlopeInv(x1, y1, x2, y2 : single) : single;
begin
GetSlopeInv := (y1 - y2) / (x1 - x2);
end;

function GetVisDistance(x1, y1, x2, y2 : integer) : integer;
begin
GetVisDistance := (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
end;

procedure RecursiveVisibility(e : PCreature; oct, depth : integer; slopeA, slopeB : single);
var
x, y : integer;
begin
case oct of
1 : begin
y := e^.y - depth;                                                { initialize y }
x := round(e^.x - slopeA * depth);                                { initialize z }
while GetSlopeStd(x, y, e^.x, e^.y) >= slopeB do begin            { while in octant }
if GetVisDistance(x, y, e^.x, e^.y) <= mw then begin            { if within max visual range }
if WorldSurface[x, y].entity^.obstruct then begin             { if obstruction }
if not WorldSurface[x - 1, y].entity^.obstruct then begin   { if no prior obstruction }
RecursiveVisibility(e, 1, depth + 1, slopeA, GetSlopeStd(x - 0.5, y + 0.5, e^.x, e^.y));
end;                                                        { ^create recursive scan }
end else begin                                                { no obstruction }
if WorldSurface[x - 1, y].entity^.obstruct then begin       { if prior obstruction }
slopeA := GetSlopeStd(x - 0.5, y - 0.5, e^.x, e^.y);      { adjust slope for later recursion }
end;
end;
WorldSurface[x, y].visibility := 3;                           { set block visible }
end;
inc(x);
end;
dec(x)
end;
2 : begin
y := e^.y - depth;                                                { initialize y }
x := round(e^.x - slopeA * depth);                                { initialize z }
while GetSlopeStd(x, y, e^.x, e^.y) <= slopeB do begin            { while in octant }
if GetVisDistance(x, y, e^.x, e^.y) <= mw then begin            { if within max visual range }
if WorldSurface[x, y].entity^.obstruct then begin             { if obstruction }
if not WorldSurface[x + 1, y].entity^.obstruct then begin   { if no prior obstruction }
RecursiveVisibility(e, 2, depth + 1, slopeA, GetSlopeStd(x + 0.5, y + 0.5, e^.x, e^.y));
end;                                                        { ^create recursive scan }
end else begin                                                { no obstruction }
if WorldSurface[x + 1, y].entity^.obstruct then begin       { if prior obstruction }
slopeA := GetSlopeStd(x + 0.5, y - 0.5, e^.x, e^.y);      { adjust slope for later recursion }
end;
end;
WorldSurface[x, y].visibility := 3;                           { set block visible }
end;
dec(x);
end;
inc(x)
end;
3 : begin
x := e^.x + depth;                                                { initialize y }
y := round(e^.y + slopeA * depth);                                { initialize z }
while GetSlopeInv(x, y, e^.x, e^.y) <= slopeB do begin            { while in octant }
if GetVisDistance(x, y, e^.x, e^.y) <= mw then begin            { if within max visual range }
if WorldSurface[x, y].entity^.obstruct then begin             { if obstruction }
if not WorldSurface[x, y - 1].entity^.obstruct then begin   { if no prior obstruction }
RecursiveVisibility(e, 3, depth + 1, slopeA, GetSlopeInv(x - 0.5, y - 0.5, e^.x, e^.y));
end;                                                        { ^create recursive scan }
end else begin                                                { no obstruction }
if WorldSurface[x, y - 1].entity^.obstruct then begin       { if prior obstruction }
slopeA := GetSlopeInv(x + 0.5, y - 0.5, e^.x, e^.y);      { adjust slope for later recursion }
end;
end;
WorldSurface[x, y].visibility := 3;                           { set block visible }
end;
inc(y);
end;
dec(y)
end;
4 : begin
x := e^.x + depth;                                                { initialize y }
y := round(e^.y + slopeA * depth);                                { initialize z }
while GetSlopeInv(x, y, e^.x, e^.y) >= slopeB do begin            { while in octant }
if GetVisDistance(x, y, e^.x, e^.y) <= mw then begin            { if within max visual range }
if WorldSurface[x, y].entity^.obstruct then begin             { if obstruction }
if not WorldSurface[x, y + 1].entity^.obstruct then begin   { if no prior obstruction }
RecursiveVisibility(e, 4, depth + 1, slopeA, GetSlopeInv(x - 0.5, y + 0.5, e^.x, e^.y));
end;                                                        { ^create recursive scan }
end else begin                                                { no obstruction }
if WorldSurface[x, y + 1].entity^.obstruct then begin       { if prior obstruction }
slopeA := GetSlopeInv(x + 0.5, y + 0.5, e^.x, e^.y);      { adjust slope for later recursion }
end;
end;
WorldSurface[x, y].visibility := 3;                           { set block visible }
end;
dec(y);
end;
inc(y)
end;
5 : begin
y := e^.y + depth;                                                { initialize y }
x := round(e^.x + slopeA * depth);                                { initialize z }
while GetSlopeStd(x, y, e^.x, e^.y) >= slopeB do begin            { while in octant }
if GetVisDistance(x, y, e^.x, e^.y) <= mw then begin            { if within max visual range }
if WorldSurface[x, y].entity^.obstruct then begin             { if obstruction }
if not WorldSurface[x + 1, y].entity^.obstruct then begin   { if no prior obstruction }
RecursiveVisibility(e, 5, depth + 1, slopeA, GetSlopeStd(x + 0.5, y - 0.5, e^.x, e^.y));
end;                                                        { ^create recursive scan }
end else begin                                                { no obstruction }
if WorldSurface[x + 1, y].entity^.obstruct then begin       { if prior obstruction }
slopeA := GetSlopeStd(x + 0.5, y + 0.5, e^.x, e^.y);      { adjust slope for later recursion }
end;
end;
WorldSurface[x, y].visibility := 3;                           { set block visible }
end;
dec(x);
end;
inc(x)
end;
6 : begin
y := e^.y + depth;                                                { initialize y }
x := round(e^.x + slopeA * depth);                                { initialize z }
while GetSlopeStd(x, y, e^.x, e^.y) <= slopeB do begin            { while in octant }
if GetVisDistance(x, y, e^.x, e^.y) <= mw then begin            { if within max visual range }
if WorldSurface[x, y].entity^.obstruct then begin             { if obstruction }
if not WorldSurface[x - 1, y].entity^.obstruct then begin   { if no prior obstruction }
RecursiveVisibility(e, 6, depth + 1, slopeA, GetSlopeStd(x - 0.5, y - 0.5, e^.x, e^.y));
end;                                                        { ^create recursive scan }
end else begin                                                { no obstruction }
if WorldSurface[x - 1, y].entity^.obstruct then begin       { if prior obstruction }
slopeA := GetSlopeStd(x - 0.5, y + 0.5, e^.x, e^.y);      { adjust slope for later recursion }
end;
end;
WorldSurface[x, y].visibility := 3;                           { set block visible }
end;
inc(x);
end;
dec(x)
end;
7 : begin
x := e^.x - depth;                                                { initialize y }
y := round(e^.y - slopeA * depth);                                { initialize z }
while GetSlopeInv(x, y, e^.x, e^.y) <= slopeB do begin            { while in octant }
if GetVisDistance(x, y, e^.x, e^.y) <= mw then begin            { if within max visual range }
if WorldSurface[x, y].entity^.obstruct then begin             { if obstruction }
if not WorldSurface[x, y + 1].entity^.obstruct then begin   { if no prior obstruction }
RecursiveVisibility(e, 7, depth + 1, slopeA, GetSlopeInv(x + 0.5, y + 0.5, e^.x, e^.y));
end;                                                        { ^create recursive scan }
end else begin                                                { no obstruction }
if WorldSurface[x, y + 1].entity^.obstruct then begin       { if prior obstruction }
slopeA := GetSlopeInv(x - 0.5, y + 0.5, e^.x, e^.y);      { adjust slope for later recursion }
end;
end;
WorldSurface[x, y].visibility := 3;                           { set block visible }
end;
dec(y);
end;
inc(y)
end;
8 : begin
x := e^.x - depth;                                                { initialize y }
y := round(e^.y - slopeA * depth);                                { initialize z }
while GetSlopeInv(x, y, e^.x, e^.y) >= slopeB do begin            { while in octant }
if GetVisDistance(x, y, e^.x, e^.y) <= mw then begin            { if within max visual range }
if WorldSurface[x, y].entity^.obstruct then begin             { if obstruction }
if not WorldSurface[x, y - 1].entity^.obstruct then begin   { if no prior obstruction }
RecursiveVisibility(e, 8, depth + 1, slopeA, GetSlopeInv(x + 0.5, y - 0.5, e^.x, e^.y));
end;                                                        { ^create recursive scan }
end else begin                                                { no obstruction }
if WorldSurface[x, y - 1].entity^.obstruct then begin       { if prior obstruction }
slopeA := GetSlopeInv(x - 0.5, y - 0.5, e^.x, e^.y);      { adjust slope for later recursion }
end;
end;
WorldSurface[x, y].visibility := 3;                           { set block visible }
end;
inc(y);
end;
dec(y)
end;
end;

if (depth < mv) and not WorldSurface[x, y].entity^.obstruct then begin   { break/continue }
RecursiveVisibility(e, oct, depth + 1, slopeA, slopeB);
end;
end;
```