Cast enough rays to fit the resolution of your tile map. Based on ray intersection information you can determine whether or not a tile is visible or not.
You will need these:
tan(theta)=opposite/adjacent
cos(theta)=adjacent/hypotenuse
sin(theta)=opposite/hypotenuse
tan(a)=x/y
cos(a)=y/h
sin(a)=x/h
so:
You can step through your map using tan() instead of cos/sin. On a 100x100 tile grid, cos and sin method will force you to search 100x100 intersections. Not good.
Here is the basis for tangent ray casting.
tan(angle)=x/y
Well you know that x is your horizontal cell or tile size.
You also know that y is your vertical cell or tile size.
So you split the ray cast into two parts: Cast on X or horizontal, and cast on Y or vertical.
Y cast
tan(angle)=x/y
tan(angle)*y=x
Code:
float stepsizex=tanf(angle)*(float)cellheight;
X cast
tan(angle)=x/y
tan(angle)/x=y
Code:
float stepsizey=tanf(angle)/(float)cellwidth;
or
Code:
float oneOverCellWidth=1.0f/cellwidth;
float stepsizey=tanf(angle)*oneOverCellWidth;
To figure out what angle is in the equation
HFOV=Horizontal Field Of View
Code:
float LeftRayAngle=PlayerHeading-(HFOV*0.5f);
if (LeftRayAngle<=0.0f) LeftRayAngle+=360.0f;
float RightRayAngle=LeftRayAngle+HFOV;
if (RightRayAngle>=360.0f) RightRayAngle-=360.0f;
Now you need to decide on the resolution of your raycast. If you wish to cast out a lot of rays, your cast will be more precise, but will take longer. For a tile map you shouldn't need a huge resolution. I'd say 90 rays would suffice.
So:
Code:
....
float AngleStep=NumberOfRays/360.0f;
or
Code:
float oneOver360=1.0f/360.0f;
float AngleStep=NumberOfRays*oneOver360;
Now just create two loops, one to raycast on x and one on y. If TileMap(x,y) is an opaque object, exit the loop, increment the ray angle, and continue. If not, continue stepping in ray direction until opaque tile is hit. You could render an entire scene this way. Start out with a black screen and render. Darken the correct vertices of the quad that is the last one visible and you will have a 'fog of war' Command and Conquer type effect.
Instead of doing this using anglestep you could use linear interpolation.
Code:
float Lerp(float v1,float v2,float interpamount)
{
return (v1+interpamount*(v2-v1));
}
float LerpASM(float v1,float v2,float interpamount)
{
float result=0.0f;
asm {
;load v2 into st(0)
fld [v2]
;subtract v1 from st(0)
fsub [v1]
;multiply st(0) by interpamount
fmul [interpamount]
;add v1 to st(0)
fadd [v1]
;pop st(0) off stack into result
;only necessary to get along with function prototypes
fstp [result]
}
return result;
}
Now instead of using stepangle you can use an interpolation amount to interpolate from one ray angle to the next or from LeftRayAngle to RightRayAngle. Since the HFOV is usually 60 degrees, and interpamount of 0.5f would be roughly halfway or 30 degrees.
Note that in the last function I had to specifcally return a value even though the result is left in st(0). I had to do this because the compiler sees the float return type and even though my code does in fact leave the correct value in st(0), the compiler has no way of knowing that. If this function was coded in pure asm, the prototype would still be float LerpASM(), but I would not have to return anything since the correct value is left in st(0) and the compiler would pick that up automatically. For more information consult an assembly language book.