Hi guys i have a Ray Casting program my friend sent me, now my c++ isnt great ive looked online for help but still cant do it. I have all the maths figured out i just cant put that maths in the code, if there is anyone on here that is capable of filling the code for me, it would mean the world to me :)
here is the RayCast function -:
void view::RayCast( float fRayStartX, float fRayStartY, unsigned int uViewingAngle )
{
float fDX, fDY; // General purpose change in X and Y variables.
int nGridX = 0; // The map array position of the next square intersecting the ray
int nGridY = 0; // The map array position of the next square intersecting the ray
int nTextIndex = 0; // The index into the relevant column of the texture map
// Set the column angle to the angle of first ray with respect to the view.
unsigned int nColumnAngle = m_nViewColAngStart;
// Loop through all columns of pixels in viewport:
for (int nColumn = 0; nColumn < m_nViewWidth; nColumn++)
{
// ***************************************************************************************
// STEP 1: Use the starting position of the ray (fRayStartX, fRayStartY), the viewing
// angle (uViewingAngle) and the column angle for this pixel column (nColumnAngle) to
// calculate the end position (fRayEndX, fRayEndY) of a ray 1024 units long.
// ***************************************************************************************
// Avoid the situation where the column angle is 0
if (nColumnAngle == 0) { nColumnAngle++; }
// Calculate the angle of ray in with respect to the world
// Ensure the angle is in the range of 0-4096
while (nColumnAngle >=0 && nColumnAngle <= 4096)
{
int RayAngle = nColumnAngle + uViewingAngle;
}
// Look up the sin and cos of the ray angle in the relevant arrays
// Rotate endpoint of a ray 1024 units long by the viewing angle:
// Create variables to hold the ray's current position (fRayX, fRayY) and initialise them
// to the start position.
float fRayX = fRayStartX;
float fRayY = fRayStartY;
// ***************************************************************************************
// STEP 2: Using the endpoint of the ray (fRayEndX, fRayEndY) calculate the change in
// x and y (fDRayX and fDRayY) along the length of the ray. Then use these values to calculate
// the slope (fSlope) of the ray (which is also m in the line equation y = mx + c).
// ***************************************************************************************
// Find difference in x,y coordinates along ray:
// Make sure fDRayX is never exactly 0 otherwise you could get divide by zero errors
// Calculate fSlope and make sure that is never exactly 0 either
// Cast ray from grid line to grid line until the loop breaks out
while( true )
{
// ***********************************************************************************
// STEP 3: Calculate the co-ordinates of the intersection points between the ray and
// the next map grid line in both the x and y axis: (fXCrossX, fXCrossY) for the x axis
// and (fYCrossX, fYCrossY) for the y axis.
// ***********************************************************************************
float fXCrossX, fXCrossY; // Intersection point between the ray and the next grid line in the x axis
// The sign of fDRayX tells you the direction of the ray in the x axis which will help you
// to calculate fXCrossX. Once you have fXCrossX you can calculate fXCrossY based on the
// fact that the change in x position (fXCrossX - fRayX) and the slope (fSlope) allows
// you to calculate the change in the y position (fXCrossY - fRayY) for the movement to
// the next x gridline. i.e. dy = m * dx
// Check sign of fDRayX and calculate fXCrossX
// Calculate fXCrossY
float fYCrossX, fYCrossY; // Intersection point between the ray and the next grid line in the y axis
// The sign of fDRayY tells you the direction of the ray in the y axis which will help you
// to calculate fYCrossX. Once you have fYCrossX you can calculate fYCrossY based on the
// fact that the change in y position (fXCrossY - fRayY) and the slope (fSlope) allows
// you to calculate the change in the x position (fYCrossY - fRayX) for the movement to
// the next y gridline. i.e. dx = dy / m
// Check sign of fDRayY and calculate fXCrossX
// Calculate fXCrossX
// ***********************************************************************************
// STEP 4: Use Pythagoras to calculate the relative distance to each of the grid line
// intersection points(dXDistSq and dYDistSq) No need to use square roots here!
// Double precision is necessary though.
// ***********************************************************************************
// Calculate dXDistSq
double dXDistSq = 0.0;
// Calculate dYDistSq
double dYDistSq = 0.0;
// ***********************************************************************************
// STEP 5: Depending on which grid line is closer, update the ray position and
// calcuate the map grid position (nGridX, nGridY). Check this map position to for an
// obstruction and break out of the while loop if there is.
// ***********************************************************************************
// If x grid line is closer...
if (dXDistSq < dYDistSq) {
// Calculate maze grid coordinates of square nGridX and nGridY
// Move current ray position to ray intersection point
// Is there a maze cube here? If so, stop looping:
if ( m_pViewWorld->m_pWorldTextures[nGridX][nGridY] != NULL)
break;
} else { // If y grid line is closer:
// Calculate maze grid coordinates of square nGridX and nGridY
// Move current ray position to ray intersection point
// Is there a maze cube here? If so, stop looping:
if ( m_pViewWorld->m_pWorldTextures[nGridX][nGridY] != NULL)
break;
ok the first step is to Calculate the position of an arbitrary "end point" 1024 units along the length of the ray.
To calculate (fRayEndX, fRayEndY) you need:
Ray Angle (θ)
Ray Length (1024)
Ray Start (fRayStartX, fRayStartY)
so ray angle = column angle + viewing angle.
I need to calculate the endpoint which is done in these steps-:
A ray of length 1024 rotated by θ
sin θ = O / H (oposite divided by hypotenuse)
cos θ = A / H (Adjacent divided by hypotenuse)
Then add fRayStart
Note:
Current ray position is (fRayX, fRayY)
This is the one that changes, not fRayStart
Calculate the changes in X and Y along the length of the ray (dX and dY) and use these to calculate the slope of the line.
Slope
dy / dx.
Slope is equal to m in
y = mx + c
Make sure dx is not zero
Make sure slope is not zero
Step 3
Calculate the co-ordinates of the intersection points between the ray and the next map grid lines in both the x and y axis.
fXCrossX and fYCrossY
Next gridline depends on the direction of the ray (dX and dY)
Dividing by grid size, discardingremainder and multiplying backup by grid size provides co-ordsof current grid line.
+128 for next
-1 for previous (i.e. "other side" of current gridline).
fXCrossY
dy = m * dx
but:
dy = (fXCrossY - fRayY)
dx = (fXCrossX - fRayX)
substitute and rearrange:
fXCrossY = fRayY + m * (fXCrossX - fRayX)
fYCrossX
dx = dy / m
substitute and rearrange:
fYCrossX = fRayX + (fYCrossY - fRayY) / m
for step 4
Calculate the relative distance to each of the grid line intersection points to decide which is the closest.
dXDistSq and dYDistSq
The square on the hypotenuse is equal to the sum of the squares on the other two sides.
No need to sqrt
for step 5
Depending on which grid line is closer, update the ray position and calculate the map grid position (nGridX, nGridY).
nGridX and nGridY
Dividing fXCrossX, fXCrossY by grid size (discarding remainder)provides map grid position.
Move fRayX, fRayY to closestgridline crossing position.
Grid test and break done for you
Next column on column loop
Thats the maths behind the function which took me ages to work out but my c++ knowledge is shit and just need someone to help transform the maths into code
Well firstly why don't you create a simple vector class instead of naming variables "____X, ____Y". That isn't math, if you had the actual math, writing the code would be easy as it has a somewhat similar syntax "1 + 1 = x" (math), "x = 1 + 1;" (code). There are also some other things you need to worry about, such as precision.
To ensure the angle is in the range of 0-4096 i put the follwing code,
while (nColumnAngle >=0 && nColumnAngle <= 4096)
{
int RayAngle = nColumnAngle + uViewingAngle;
}
so i've worked out the ray angle
the next step is to // Look up the sin and cos of the ray angle in the relevant arrays but what does that mean?
vilimil thanks for that, i've had a go for step 2-
// ***************************************************************************************
// STEP 2: Using the endpoint of the ray (fRayEndX, fRayEndY) calculate the change in
// x and y (fDRayX and fDRayY) along the length of the ray. Then use these values to calculate
// the slope (fSlope) of the ray (which is also m in the line equation y = mx + c).
// ***************************************************************************************
// Find difference in x,y coordinates along ray:
float fDRayX = fRayEndX - fRayX;
float fDRayY = fRayEndY - fRayY;
// Make sure fDRayX is never exactly 0 otherwise you could get divide by zero errors
// Calculate fSlope and make sure that is never exactly 0 either
while ( fDRayX != 0)
{
float fSlope = fDRayY / fDRayX;
/*float c = fRayX - m*fRayY;*/
Another complication for you is that you have to be careful comparing floating point numbers. Floating point numbers often cannot be represented exactly, so often you get:
"FloatValue = DesiredVlaue - very small value."
This why the comparisons often don't work.
The answer is to make use of built in constants like EPSILON and DBL_EPSILON found in the maths header file.
Google it & Read up about it.
Edit: Changeing the type to double just means there will be less numbers that give a problem, but there is still a problem - ie the above snippet still won't work.
With the epsilon, wouldn't it be easier to use the DBL_EPSILON value directly? Only need to #include the maths header.
It also saves the function calls to pow.
1 2 3 4 5
#include <math>
short sign(double x)
{
return x > DBL_EPSILON ? 1 : x < -DBL_EPSILON ? -1 : 0;
}
Not trying to split hairs, just an idea :)
If you are going to make comparisons with numbers other than zero, you need to scale up DBL_EPSILON to suit the value you are comparing. The idea is to use DBL_EPSILON to calculate the "distance" between representable numbers. So if you want to check a number for equality with 1000.0 say, you need to use 1000 * DBL_EPSILON as the "distance". Similar thing for less than and greater than.
The other way is to take the absolute value of (x - 1000.0) and compare that with the "distance" value - in this case 1000 * DBL_EPSILON.
viliml 's code does <, ==, and > all in one go, which is great.
Umm, SVD400, did you do the third part by now?
BTW I can't find EPSILON or DBL_EPSILON anywhere!
BTBTW'sW
The other way is to take the absolute value of (x - 1000.0) and compare that with the "distance" value - in this case 1000 * DBL_EPSILON.
You mean take abs(x-1000.0) and compare it with the epsilon?
BTBTBTW'sW'sW I belive that the epsilon macros do actually call pow, eg #define EPSILON pow(2.0f, 24.0f)
You mean take abs(x-1000.0) and compare it with the epsilon?
No. Epsilon is the distance between representable numbers when the number is 1.0 That is 1.0 + EPSILON is the next representable number for a float. Obviously use DBL_EPSILON for doubles.
So if your number is 1000, you need to scale up the DBL_EPSILON by 1000 because that is the distance between representable doubles on that part of the number line. The distance varies because of the exponent, with large negative exponents the distance is very small, with large positive exponents the distance is large.
I couldn't find EPSILON in the math header either. It is probably inlcuded from some other file. I am pretty sure if you include <math> you will be able to use it.
If the EPSILON macro is defined that way - that is fine. However I thought the one of the purposes of defined constant was so that one didn't have keep calling functions to work them out - calc once use multiple times.
Will the code be easier to read if you just use the DBL_EPSILON? You will always need to scale it up anyway.
Edit:
Just curious - what does BTBTBTW'sW'sW mean ? :D
I don't have the <math> header, and it's not in <cmath> or <math.h>!
And, BTBTBTW'sW'sW means by the BTBTW'sW's way, and BTBTW'sW is by the BTW's way. I made it up just for fun xD
Oh and in the <limits> header, you can use numeric_limits<double>::epsilon(), as it's the C++ way. Macros are C.
BTW: even the epsilon isn't really the smalles reprezentable number. It's the smallest representable NORMAL number. The real SMALLEST reprezentable number is more than 4.5*google3 times smaller than the smalles normal number.