nuttx-apps/graphics/traveler/src/trv_raycast.c

1406 lines
49 KiB
C

/****************************************************************************
* apps/graphics/traveler/src/trv_raycast.c
* This file contains the low-level ray casting logic
*
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included files
****************************************************************************/
#include "trv_types.h"
#include "trv_world.h"
#include "trv_doors.h"
#include "trv_plane.h"
#include "trv_bitmaps.h"
#include "trv_trigtbl.h"
#include "trv_rayrend.h"
#include "trv_rayprune.h"
#include "trv_raycast.h"
/****************************************************************************
* Compilation switches
****************************************************************************/
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* The following switch enables view correction logic. */
#define ENABLE_VIEW_CORRECTION 1
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void trv_ray_xcaster14(FAR struct trv_raycast_s *result);
static void trv_ray_xcaster23(FAR struct trv_raycast_s *result);
static void trv_ray_ycaster12(FAR struct trv_raycast_s *result);
static void trv_ray_ycaster34(FAR struct trv_raycast_s *result);
static void trv_ray_zcasteru(FAR struct trv_raycast_s *result);
static void trv_ray_zcasterl(FAR struct trv_raycast_s *result);
/****************************************************************************
* Private Data
****************************************************************************/
/* The following are the tangent and the cotangent of the pitch angle
* adjusted for the viewing yaw angle so that the view is correct for the
* "fish eye" effect which results from the projection of the polar ray cast
* onto the flat display
*/
static int32_t g_adj_tanpitch;
static int32_t g_adj_cotpitch;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: trv_ray_xcaster14
*
* Description:
* This function casts a ray along the Y-Axis looking at points of
* intersection on the X-Axis. If a block is defined at this intersection
* then a "hit" is found and the distance to this hit is determined.
*
* If we are in the "right" half of the view, then the X ray cast will
* proceed in a positive along the X axis and that all possible X-axis
* intersections will occur to at the "left" of a cell.
*
* NOTE: The X-Ray caster must run first because it initializes a
* data structure needed by both the Y and Z ray casters.
*
****************************************************************************/
static void trv_ray_xcaster14(FAR struct trv_raycast_s *result)
{
struct trv_rect_list_s *list; /* Points to the current X plane rectangle */
struct trv_rect_data_s *rect; /* Points to the rectangle data */
trv_coord_t relx; /* Relative position of the X plane */
trv_coord_t absy; /* Absolute Y position at relx given yaw */
trv_coord_t absz; /* Absolute Z position at relx given pitch */
trv_coord_t lastrelx1 = -1; /* Last relative X position processed */
trv_coord_t lastrelx2 = -1; /* Last relative X position processed */
int32_t dydx; /* Rate of change of Y wrt X (double) */
int32_t dzdx; /* Rate of change of Z wrt X (double) */
/* At a viewing angle of 270 degrees, no intersections with the g_ray_xplanes
* are possible!
*/
if (g_camera.yaw == ANGLE_270)
{
return;
}
/* Pre-calculate the rate of change of Y and Z with respect to X */
/* The tangent is equal to the rate of change of Y with respect to the
* X-axis. The tangent is stored at double the "normal" scaling.
*/
dydx = TAN(g_camera.yaw);
/* Determine the rate of change of the Z with respect to X. The tangent is
* "double" precision; the secant is "double" precision. dzdx will be
* retained as "double" precision.
*/
dzdx = qTOd(g_adj_tanpitch * ABS(g_sec_table[g_camera.yaw]));
/* Look at every rectangle lying in the X plane */
/* This logic should be improved at some point so that non-visible planes
* are "pruned" from the list prior to ray casting!
*/
for (list = g_ray_xplane.head; list; list = list->flink)
{
rect = &list->d;
/* Search for a rectangle which lies "beyond" the current camera
* position
*/
if (rect->plane > g_camera.x)
{
/* get the X distance to the plane */
relx = rect->plane - g_camera.x;
#if 0
/* g_ray_xplane is an ordered list, if we have already hit something
* closer, then we can abort the casting now.
*/
if (relx > result->xdist)
{
return;
}
#endif
/* Calculate the Y position at this relative X position. We can skip
* this step if we are processing another rectangle at the same relx
* distance.
*/
if (relx != lastrelx1)
{
int32_t deltay; /* Scale == "triple" */
/* The dydx is stored at double the"normal" scaling -- so
* deltay is "triple" precision
*/
deltay = dydx * ((int32_t) relx);
absy = tTOs(deltay) + g_camera.y; /* back to "single" */
lastrelx1 = relx;
}
/* Check if this Y position intersects the rectangle */
if (absy >= rect->hstart && absy <= rect->hend)
{
/* The Y position lies in the rectangle. Now, calculate the
* theZ position at this relative X position. We can skip
* this step if we are processing another rectangle at the
* same relx distance.
*/
if (relx != lastrelx2)
{
int32_t deltaz; /* Scale == TRIPLE */
/* The dzdx is stored at double the"normal" scaling -- so
* deltaz is "triple" precision
*/
deltaz = dzdx * ((int32_t) relx);
absz = tTOs(deltaz) + g_camera.z; /* Back to single */
lastrelx2 = relx;
}
/* Check if this Z position intersects the rectangle */
if (absz >= rect->vstart && absz <= rect->vend)
{
/* We've got a potential hit, let's see what it is */
/* Check if we just hit an ordinary opaque wall */
if (IS_NORMAL(rect))
{
/* Yes..Save the parameters associated with the normal
* wall hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(FRONT_HIT, X_HIT);
result->xpos = absy;
result->ypos = absz;
result->xdist = relx;
result->ydist = ABS(absy - g_camera.y);
result->zdist = ABS(absz - g_camera.z);
/* Terminate X casting */
return;
}
else if (IS_DOOR(rect))
{
/* Check if the door is in motion. */
if (!IS_MOVING_DOOR(rect))
{
/* Save the parameters associated with the normal
* door hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(FRONT_HIT, X_HIT);
result->xpos = absy;
result->ypos = absz;
result->xdist = relx;
result->ydist = ABS(absy - g_camera.y);
result->zdist = ABS(absz - g_camera.z);
/* Terminate X casting */
return;
}
/* The door is in motion, the Z-position to see if we can
* see under the door
*/
else if (absz > g_opendoor.zbottom)
{
/* Save the parameters associated with the moving
* door hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(FRONT_HIT, X_HIT);
result->xpos = absy;
result->ypos = absz - g_opendoor.zdist;
result->xdist = relx;
result->ydist = ABS(absy - g_camera.y);
result->zdist = ABS(absz - g_camera.z);
/* Terminate X casting */
return;
}
}
/* Otherwise, it must be a transparent wall. We'll need to
* make our decision based upon the pixel that we hit
*/
/* Check if the pixel at this location is visible */
else if (GET_FRONT_PIXEL(rect, absy, absz) != INVISIBLE_PIXEL)
{
/* Its visible, save the parameters associated with the
* transparent wall hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(FRONT_HIT, X_HIT);
result->xpos = absy;
result->ypos = absz;
result->xdist = relx;
result->ydist = ABS(absy - g_camera.y);
result->zdist = ABS(absz - g_camera.z);
/* Terminate X casting */
return;
}
}
}
}
}
}
/****************************************************************************
* Name: trv_ray_xcaster23
*
* Description:
* This function casts a ray along the Y-Axis looking at points of
* intersection on the X-Axis. If a block is defined at this intersection
* then a "hit" is found and the distance to this hit is determined.
*
* If we are in the "left" half of the view, then the X ray cast will
* proceed in a negative along the X axis and that all possible X-axis
* intersections will occur on the "right" of a cell.
*
* NOTE: The X-Ray caster must run first because it initializes a
* data structure needed by both the Y and Z ray casters.
*
****************************************************************************/
static void trv_ray_xcaster23(FAR struct trv_raycast_s *result)
{
struct trv_rect_list_s *list; /* Points to the current X plane rectangle */
struct trv_rect_data_s *rect; /* Points to the rectangle data */
trv_coord_t relx; /* Relative position of the X plane */
trv_coord_t absy; /* Absolute Y position at relx given yaw */
trv_coord_t absz; /* Absolute Z position at relx given pitch */
trv_coord_t lastrelx1 = -1; /* Last relative X position processed */
trv_coord_t lastrelx2 = -1; /* Last relative X position processed */
int32_t dydx; /* Rate of change of Y wrt X (double) */
int32_t dzdx; /* Rate of change of Z wrt X (double) */
/* At a view angle of 90 degrees, no intersections with the g_ray_xplanes are
* possible!
*/
if (g_camera.yaw == ANGLE_90)
{
return;
}
/* Pre-calculate the rate of change of Y and Z with respect to X */
/* The negative tangent is equal to the rate of change of Y with respect
* to the X-axis. The tangent is stored at double the "normal" scaling.
*/
dydx = -TAN(g_camera.yaw);
/* Determine the rate of change of the Z with respect to X. dydx is
* "double" precision; the secant is "double" precision. dzdx will be
* retained as "double" precision.
*/
dzdx = qTOd(g_adj_tanpitch * ABS(g_sec_table[g_camera.yaw]));
/* Look at every rectangle lying in the X plane */
/* This logic should be improved at some point so that non-visible planes
* are "pruned" from the list prior to ray casting!
*/
for (list = g_ray_xplane.tail; list; list = list->blink)
{
rect = &list->d;
/* Search for a rectangle which lies "before" the current camera
* position
*/
if (rect->plane < g_camera.x)
{
/* get the X distance to the plane */
relx = g_camera.x - rect->plane;
#if 0
/* g_ray_xplane is an ordered list, if we have already hit something
* closer, then we can abort the casting now.
*/
if (relx > result->xdist)
{
return;
}
#endif
/* Calculate the Y position at this relative X position. We can skip
* this step if we are processing another rectangle at the same relx
* distance.
*/
if (relx != lastrelx1)
{
int32_t deltay; /* Scale == "triple" */
/* The dydx is stored at double the"normal" scaling -- so deltay
* is "triple" precision
*/
deltay = dydx * ((int32_t) relx);
absy = tTOs(deltay) + g_camera.y; /* back to "single" */
lastrelx1 = relx;
}
/* Check if this Y position intersects the rectangle */
if (absy >= rect->hstart && absy <= rect->hend)
{
/* The Y position lies in the rectangle. Now, calculate the
* Z position at this relative X position. We can skip this
* step if we are processing another rectangle at the same
* relx distance.
*/
if (relx != lastrelx2)
{
int32_t deltaz; /* Scale == TRIPLE */
/* The dzdx is stored at double the"normal" scaling -- so
* deltaz is "triple" precision
*/
deltaz = dzdx * ((int32_t) relx);
absz = tTOs(deltaz) + g_camera.z; /* Back to single */
lastrelx2 = relx;
}
/* Check if this Z position intersects the rectangle */
if (absz >= rect->vstart && absz <= rect->vend)
{
/* We've got a potential hit, let's see what it is */
/* Check if we just hit an ordinary opaque wall */
if (IS_NORMAL(rect))
{
/* Yes..Save the parameters associated with the normal
* wall hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(BACK_HIT, X_HIT);
result->xpos = absy;
result->ypos = absz;
result->xdist = relx;
result->ydist = ABS(absy - g_camera.y);
result->zdist = ABS(absz - g_camera.z);
/* Terminate X casting */
return;
}
else if (IS_DOOR(rect))
{
/* Check if the door is in motion. */
if (!IS_MOVING_DOOR(rect))
{
/* Save the parameters associated with the normal
* door hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(BACK_HIT, X_HIT);
result->xpos = absy;
result->ypos = absz;
result->xdist = relx;
result->ydist = ABS(absy - g_camera.y);
result->zdist = ABS(absz - g_camera.z);
/* Terminate X casting */
return;
}
/* The door is in motion, the Z-position to see if we can
* see under the door
*/
else if (absz > g_opendoor.zbottom)
{
/* Save the parameters associated with the moving
* door hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(BACK_HIT, X_HIT);
result->xpos = absy;
result->ypos = absz - g_opendoor.zdist;
result->xdist = relx;
result->ydist = ABS(absy - g_camera.y);
result->zdist = ABS(absz - g_camera.z);
/* Terminate X casting */
return;
}
}
/* Otherwise, it must be a transparent wall. We'll need to
* make our decision based upon the pixel that we hit
*/
/* Check if the pixel at this location is visible */
else if (GET_BACK_PIXEL(rect, absy, absz) != INVISIBLE_PIXEL)
{
/* Its visible, save the parameters associated with the
* transparent wall hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(BACK_HIT, X_HIT);
result->xpos = absy;
result->ypos = absz;
result->xdist = relx;
result->ydist = ABS(absy - g_camera.y);
result->zdist = ABS(absz - g_camera.z);
/* Terminate X casting */
return;
}
}
}
}
}
}
/****************************************************************************
* Name: trv_ray_ycaster12
*
* Description:
* This function casts a ray along the X-Axis looking at points of
* intersection on the Y-Axis. If a block is defined at this intersection
* then a "hit" is found and the distance to this hit is determined.
*
* If we are in the "forward" half of the view, then the Y ray cast will
* proceed in a positive along the Y axis and that all possible Y-axis
* intersections will occur on the "front" of a cell.
*
* NOTE: The X-Ray is assumed to have been performed first!
*
****************************************************************************/
static void trv_ray_ycaster12(FAR struct trv_raycast_s *result)
{
struct trv_rect_list_s *list; /* Points to the current P plane rectangle */
struct trv_rect_data_s *rect; /* Points to the rectangle data */
trv_coord_t rely; /* Relative position of the Y plane */
trv_coord_t absx; /* Absolute X position at rely given yaw */
trv_coord_t absz; /* Absolute Z position at rely given pitch */
trv_coord_t lastrely1 = -1; /* Last relative Y position processed */
trv_coord_t lastrely2 = -1; /* Last relative Y position processed */
int32_t dxdy; /* Rate of change of X wrt Y (double) */
int32_t dzdy; /* Rate of change of Z wrt Y (double) */
/* At a viewing angle of 0 degrees, no intersections with the g_ray_yplane is
* possible!
*/
if (g_camera.yaw == ANGLE_0)
{
return;
}
/* Pre-calculate the rate of change of X and Z with respect to Y */
/* The inverted tangent is equal to the rate of change of X with respect to
* the Y-axis. The cotangent is stored at double the the "normal" scaling.
*/
dxdy = g_cot_table(g_camera.yaw);
/* Determine the rate of change of the Z with respect to Y. The tangent
* is "double" precision; the cosecant is "double" precision. dzdy will
* be retained as "double" precision.
*/
dzdy = qTOd(g_adj_tanpitch * ABS(g_csc_table[g_camera.yaw]));
/* Look at every rectangle lying in a Y plane */
/* This logic should be improved at some point so that non-visible planes
* are "pruned" from the list prior to ray casting!
*/
for (list = g_ray_yplane.head; list; list = list->flink)
{
rect = &list->d;
/* Search for a rectangle which lies "beyond" the current camera
* position
*/
if (rect->plane > g_camera.y)
{
/* get the Y distance to the plane */
rely = rect->plane - g_camera.y;
/* g_ray_yplane is an ordered list, if we have already hit something
* closer, then we can abort the casting now.
*/
if (rely > result->ydist)
{
return;
}
/* Calculate the Y position at this relative X position. We can skip
* this step if we are processing another rectangle at the same relx
* distance.
*/
if (rely != lastrely1)
{
int32_t deltax; /* Scale == "triple" */
/* The dxdy is stored at double the"normal" scaling -- so deltax
* is "triple" precision
*/
deltax = dxdy * ((int32_t) rely);
absx = tTOs(deltax) + g_camera.x; /* back to "single" */
lastrely1 = rely;
}
/* Check if this X position intersects the rectangle */
if (absx >= rect->hstart && absx <= rect->hend)
{
/* The X position lies in the rectangle. Now, calculate the the
* Z position at this relative X position. We can skip this step
* if we are processing another rectangle at the same relx
* distance.
*/
if (rely != lastrely2)
{
int32_t deltaz; /* Scale == TRIPLE */
/* The dzdy is stored at double the"normal" scaling -- so
* deltaz is "triple" precision
*/
deltaz = dzdy * ((int32_t) rely);
absz = tTOs(deltaz) + g_camera.z; /* Back to single */
lastrely2 = rely;
}
/* Check if this Z position intersects the rectangle */
if (absz >= rect->vstart && absz <= rect->vend)
{
/* We've got a potential hit, let's see what it is */
/* Check if we just hit an ordinary opaque wall */
if (IS_NORMAL(rect))
{
/* Yes..Save the parameters associated with the normal
* wall hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(FRONT_HIT, Y_HIT);
result->xpos = absx;
result->ypos = absz;
result->xdist = ABS(absx - g_camera.x);
result->ydist = rely;
result->zdist = ABS(absz - g_camera.z);
/* Terminate Y casting */
return;
}
else if (IS_DOOR(rect))
{
/* Check if the door is in motion. */
if (!IS_MOVING_DOOR(rect))
{
/* Save the parameters associated with the normal
* door hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(FRONT_HIT, Y_HIT);
result->xpos = absx;
result->ypos = absz;
result->xdist = ABS(absx - g_camera.x);
result->ydist = rely;
result->zdist = ABS(absz - g_camera.z);
/* Terminate Y casting */
return;
}
/* The door is in motion, the Z-position to see if we can
* see under the door
*/
else if (absz > g_opendoor.zbottom)
{
/* Save the parameters associated with the moving
* door hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(FRONT_HIT, Y_HIT);
result->xpos = absx;
result->ypos = absz - g_opendoor.zdist;
result->xdist = ABS(absx - g_camera.x);
result->ydist = rely;
result->zdist = ABS(absz - g_camera.z);
/* Terminate Y casting */
return;
}
}
/* Otherwise, it must be a transparent wall. We'll need to
* make our decision based upon the pixel that we hit
*/
/* Check if the pixel at this location is visible */
else if (GET_FRONT_PIXEL(rect, absx, absz) != INVISIBLE_PIXEL)
{
/* Its visible, save the parameters associated with the
* transparent wall hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(FRONT_HIT, Y_HIT);
result->xpos = absx;
result->ypos = absz;
result->xdist = ABS(absx - g_camera.x);
result->ydist = rely;
result->zdist = ABS(absz - g_camera.z);
/* Terminate Y casting */
return;
}
}
}
}
}
}
/****************************************************************************
* Name: trv_ray_ycaster34
*
* Description:
* This function casts a ray along the X-Axis looking at points of
* intersection on the Y-Axis. If a block is defined at this intersection
* then a "hit" is found and the distance to this hit is determined.
*
* If we are in the "back" half of the view, then the Y ray cast will
* proceed in a negative along the Y axis and that all possible Y-axis
* intersections will occur on the "back" of a cell.
*
* NOTE: The X-Ray is assumed to have been performed first!
*
****************************************************************************/
static void trv_ray_ycaster34(FAR struct trv_raycast_s *result)
{
struct trv_rect_list_s *list; /* Points to the current P plane rectangle */
struct trv_rect_data_s *rect; /* Points to the rectangle data */
trv_coord_t rely; /* Relative position of the Y plane */
trv_coord_t absx; /* Absolute X position at rely given yaw */
trv_coord_t absz; /* Absolute Z position at rely given pitch */
trv_coord_t lastrely1 = -1; /* Last relative Y position processed */
trv_coord_t lastrely2 = -1; /* Last relative Y position processed */
int32_t dxdy; /* Rate of change of X wrt Y (double) */
int32_t dzdy; /* Rate of change of Z wrt Y (double) */
/* At a viewing angle of 180 degrees, no intersections with the line x = bXi
* are possible!
*/
if (g_camera.yaw == ANGLE_180)
{
return;
}
/* Pre-calculate the rate of change of X and Z with respect to Y */
/* The negative inverted tangent is equal to the rate of change of X with
* respect to the Y-axis. The cotangent is stored at double the the
* "normal" scaling.
*/
dxdy = -g_cot_table(g_camera.yaw - ANGLE_180);
/* Determine the rate of change of the Z with respect to Y. The tangent
* is "double" precision; the cosecant is "double" precision. dzdy will
* be retained as "double" precision.
*/
dzdy = qTOd(g_adj_tanpitch * ABS(g_csc_table[g_camera.yaw]));
/* Look at every rectangle lying in a Y plane */
/* This logic should be improved at some point so that non-visible planes
* are "pruned" from the list prior to ray casting!
*/
for (list = g_ray_yplane.tail; list; list = list->blink)
{
rect = &list->d;
/* Search for a rectangle which lies "before" the current camera
* position
*/
if (rect->plane < g_camera.y)
{
/* get the Y distance to the plane */
rely = g_camera.y - rect->plane;
/* g_ray_yplane is an ordered list, if we have already hit something
* closer, then we can abort the casting now.
*/
if (rely > result->ydist)
{
return;
}
/* Calculate the Y position at this relative X position. We can skip
* this step if we are processing another rectangle at the same relx
* distance.
*/
if (rely != lastrely1)
{
int32_t deltax; /* Scale == "triple" */
/* The dxdy is stored at double the"normal" scaling -- so deltax
* is "triple" precision
*/
deltax = dxdy * ((int32_t) rely);
absx = tTOs(deltax) + g_camera.x; /* back to "single" */
lastrely1 = rely;
}
/* Check if this X position intersects the rectangle */
if (absx >= rect->hstart && absx <= rect->hend)
{
/* The X position lies in the rectangle. Now, calculate the
* Z position at this relative X position. We can skip this
* step if we are processing another rectangle at the same
* relx distance.
*/
if (rely != lastrely2)
{
int32_t deltaz; /* Scale == TRIPLE */
/* The dzdy is stored at double the"normal" scaling -- so
* deltaz is "triple" precision
*/
deltaz = dzdy * ((int32_t) rely);
absz = tTOs(deltaz) + g_camera.z; /* Back to single */
lastrely2 = rely;
}
/* Check if this Z position intersects the rectangle */
if (absz >= rect->vstart && absz <= rect->vend)
{
/* We've got a potential hit, let's see what it is */
/* Check if we just hit an ordinary opaque wall */
if (IS_NORMAL(rect))
{
/* Yes..Save the parameters associated with the normal
* wall hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(BACK_HIT, Y_HIT);
result->xpos = absx;
result->ypos = absz;
result->xdist = ABS(absx - g_camera.x);
result->ydist = rely;
result->zdist = ABS(absz - g_camera.z);
/* Terminate Y casting */
return;
}
else if (IS_DOOR(rect))
{
/* Check if the door is in motion. */
if (!IS_MOVING_DOOR(rect))
{
/* Save the parameters associated with the normal
* door hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(BACK_HIT, Y_HIT);
result->xpos = absx;
result->ypos = absz;
result->xdist = ABS(absx - g_camera.x);
result->ydist = rely;
result->zdist = ABS(absz - g_camera.z);
/* Terminate Y casting */
return;
}
/* The door is in motion, the Z-position to see if we can
* see under the door
*/
else if (absz > g_opendoor.zbottom)
{
/* Save the parameters associated with the moving
* door hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(BACK_HIT, Y_HIT);
result->xpos = absx;
result->ypos = absz - g_opendoor.zdist;
result->xdist = ABS(absx - g_camera.x);
result->ydist = rely;
result->zdist = ABS(absz - g_camera.z);
/* Terminate Y casting */
return;
}
}
/* Otherwise, it must be a transparent wall. We'll need to
* make our decision based upon the pixel that we hit
*/
/* Check if the pixel at this location is visible */
else if (GET_BACK_PIXEL(rect, absx, absz) != INVISIBLE_PIXEL)
{
/* Its visible, save the parameters associated with the
* transparent wall hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(BACK_HIT, Y_HIT);
result->xpos = absx;
result->ypos = absz;
result->xdist = ABS(absx - g_camera.x);
result->ydist = rely;
result->zdist = ABS(absz - g_camera.z);
/* Terminate Y casting */
return;
}
}
}
}
}
}
/****************************************************************************
* Name: trv_ray_zcasteru
*
* Description:
* This function casts a ray along the rotated Y-Axis looking at points of
* intersection on the Z-Axis. If a block is defined at this intersection
* then a "hit" is found and the distance to this hit is determined.
*
* If we are in the "upper" half of the view, then the Z ray cast will
* proceed along the positive Z axis and that all possible Z-axis
* intersections will occur on the "bottom" of a cell.
*
* NOTE: It is assumed that both the X and Y ray casters have already
* ran!
****************************************************************************/
static void trv_ray_zcasteru(FAR struct trv_raycast_s *result)
{
struct trv_rect_list_s *list; /* Points to the current Z plane rectangle */
struct trv_rect_data_s *rect; /* Points to the rectangle data */
trv_coord_t relz; /* Relative position of the Z plane */
trv_coord_t absx; /* Absolute X position at relz given yaw */
trv_coord_t absy; /* Absolute Y position at relz given yaw */
trv_coord_t lastrelz1 = -1; /* Last relative Z position processed */
trv_coord_t lastrelz2 = -1; /* Last relative Z position processed */
int32_t dxdz; /* Rate of change of X wrt Z (double) */
int32_t dydz; /* Rate of change of Y wrt Z (double) */
/* At a viewing angle of 0 degrees, no intersections with the g_ray_zplanes are
* possible!
*/
if (g_camera.pitch == ANGLE_0)
{
return;
}
/* Pre-calculate the rate of change of X and Y with respect to Z */
/* Calculate the rate of change of X with respect to the Z-axis. The
* cotangent is stored at double the "normal" scaling and the cosine is
* also at double scaling. dxdz will be also be stored at double
* precision.
*/
dxdz = qTOd(g_adj_cotpitch * ((int32_t) g_cos_table[g_camera.yaw]));
/* Calculate the rate of change of Y with respect to the Z-axis. The
* cotangent is stored at double the "normal" scaling and the sine is also
* at double scaling. dxdz will be also be stored at double precision.
*/
dydz = qTOd(g_adj_cotpitch * ((int32_t) g_sin_table[g_camera.yaw]));
/* Look at every rectangle lying in the Z plane */
/* This logic should be improved at some point so that non-visible planes
* are "pruned" from the list prior to ray casting!
*/
for (list = g_ray_zplane.head; list; list = list->flink)
{
rect = &list->d;
/* Search for a rectangle which lies "beyond" the current camera
* position
*/
if (rect->plane > g_camera.z)
{
/* get the Z distance to the plane */
relz = rect->plane - g_camera.z;
/* g_ray_zplane is an ordered list, if we have already hit something
* closer, then we can abort the casting now.
*/
if (relz > result->zdist)
{
return;
}
/* Calculate the X position at this relative Z position. We can skip
* this step if we are processing another rectangle at the same relx
* distance.
*/
if (relz != lastrelz1)
{
int32_t deltax; /* Scale == "triple" */
/* The dxdz is stored at double the"normal" scaling -- so deltax
* is "triple" precision
*/
deltax = dxdz * ((int32_t) relz);
absx = tTOs(deltax) + g_camera.x; /* back to "single" */
lastrelz1 = relz;
}
/* Check if this X position intersects the rectangle */
if (absx >= rect->hstart && absx <= rect->hend)
{
/* The X position lies in the rectangle. Now, calculate the the
* Y position at this relative Z position. We can skip this step
* if we are processing another rectangle at the same relx
* distance.
*/
if (relz != lastrelz2)
{
int32_t deltay; /* Scale == TRIPLE */
/* The dydz is stored at double the"normal" scaling -- so
* deltay is "triple" precision
*/
deltay = dydz * ((int32_t) relz);
absy = tTOs(deltay) + g_camera.y; /* back to "single" */
lastrelz2 = relz;
}
/* Check if this Y position intersects the rectangle */
if (absy >= rect->vstart && absy <= rect->vend)
{
/* We've got a hit, ..Save the parameters associated with the
* ceiling hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(BACK_HIT, Z_HIT);
result->xpos = absx;
result->ypos = absy;
result->xdist = ABS(absx - g_camera.x);
result->ydist = ABS(absy - g_camera.y);
result->zdist = relz;
/* Terminate Z casting */
return;
}
}
}
}
}
/****************************************************************************
* Name: trv_ray_zcasterl
*
* Description:
* This function casts a ray along the rotated Y-Axis looking at points of
* intersection on the Z-Axis. If a block is defined at this intersection
* then a "hit" is found and the distance to this hit is determined.
*
* If we are in the "lower" half of the view, then the Z ray cast will
* proceed along the negative Z axis and that all possible Z-axis
* intersections will occur on the "top" of a cell.
*
* NOTE: It is assumed that both the X and Y ray casters have already
* ran!
*
****************************************************************************/
static void trv_ray_zcasterl(FAR struct trv_raycast_s *result)
{
struct trv_rect_list_s *list; /* Points to the current Z plane rectangle */
struct trv_rect_data_s *rect; /* Points to the rectangle data */
trv_coord_t relz; /* Relative position of the Z plane */
trv_coord_t absx; /* Absolute X position at relz given yaw */
trv_coord_t absy; /* Absolute Y position at relz given yaw */
trv_coord_t lastrelz1 = -1; /* Last relative Z position processed */
trv_coord_t lastrelz2 = -1; /* Last relative Z position processed */
int32_t dxdz; /* Rate of change of X wrt Z (double) */
int32_t dydz; /* Rate of change of Y wrt Z (double) */
/* At a viewing angle of 0 degrees, no intersections with the g_ray_zplanes are
* possible!
*/
if (g_camera.pitch == ANGLE_0)
{
return;
}
/* Pre-calculate the rate of change of X and Y with respect to Z */
/* Calculate the rate of change of X with respect to the Z-axis. The
* cotangent is stored at double the "normal" scaling and the cosine is
* also at double scaling. dxdz will be also be stored at double
* precision.
*/
dxdz = qTOd(g_adj_cotpitch * ((int32_t) g_cos_table[g_camera.yaw]));
/* Calculate the rate of change of Y with respect to the Z-axis. The
* cotangent is stored at double the "normal" scaling and the sine is
* also at double scaling. dxdz will be also be stored at double
* precision.
*/
dydz = qTOd(g_adj_cotpitch * ((int32_t) g_sin_table[g_camera.yaw]));
/* Look at every rectangle lying in the Z plane */
/* This logic should be improved at some point so that non-visible planes
* are "pruned" from the list prior to ray casting!
*/
for (list = g_ray_zplane.tail; list; list = list->blink)
{
rect = &list->d;
/* Search for a rectangle which lies "before" the current camera
* position
*/
if (rect->plane < g_camera.z)
{
/* get the Z distance to the plane */
relz = g_camera.z - rect->plane;
/* g_ray_zplane is an ordered list, if we have already hit something
* closer, then we can abort the casting now.
*/
if (relz > result->zdist)
{
return;
}
/* Calculate the X position at this relative Z position. We can skip
* this step if we are processing another rectangle at the same relx
* distance.
*/
if (relz != lastrelz1)
{
int32_t deltax; /* Scale == "triple" */
/* The dxdz is stored at double the"normal" scaling -- so deltax
* is "triple" precision
*/
deltax = dxdz * ((int32_t) relz);
absx = tTOs(deltax) + g_camera.x; /* back to "single" */
lastrelz1 = relz;
}
/* Check if this X position intersects the rectangle */
if (absx >= rect->hstart && absx <= rect->hend)
{
/* The X position lies in the rectangle. Now, calculate the the
* Y position at this relative Z position. We can skip this step
* if we are processing another rectangle at the same relx
* distance.
*/
if (relz != lastrelz2)
{
int32_t deltay; /* Scale == TRIPLE */
/* The dydz is stored at double the"normal" scaling -- so
* deltay is "triple" precision
*/
deltay = dydz * ((int32_t) relz);
absy = tTOs(deltay) + g_camera.y; /* back to "single" */
lastrelz2 = relz;
}
/* Check if this Y position intersects the rectangle */
if (absy >= rect->vstart && absy <= rect->vend)
{
/* We've got a hit, ..Save the parameters associated with the
* floor hit
*/
result->rect = rect;
result->type = MK_HIT_TYPE(FRONT_HIT, Z_HIT);
result->xpos = absx;
result->ypos = absy;
result->xdist = ABS(absx - g_camera.x);
result->ydist = ABS(absy - g_camera.y);
result->zdist = relz;
/* Terminate Z casting */
return;
}
}
}
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: trv_raycast
*
* Description:
* This function performs a single ray cast. It decomposes the cast by
* quadrants so that simpler casting algorithms can be used. It also
* enforces the order of casting: X first, then Y, and finally Z.
*
****************************************************************************/
void trv_raycast(int16_t pitch, int16_t yaw, int16_t screenyaw,
FAR struct trv_raycast_s *result)
{
/* Set the camera pitch and yaw angles for this cast */
g_camera.pitch = pitch;
g_camera.yaw = yaw;
/* Initialize the result structure, assuming that there will be no hit */
result->rect = NULL;
result->type = NO_HIT;
result->xpos = 0;
result->ypos = 0;
result->xdist = TRV_INFINITY;
result->ydist = TRV_INFINITY;
result->zdist = TRV_INFINITY;
/* Prepare for X and Y ray casts. These casts will need the adjusted tangent
* of the pitch angle in order to correct for "fish eye" distortion. This
* correction consists of multiplying by the cosine of the relative screen
* yaw position. The tangent is double precision, the cosine is double
* precision, the result will be retained as double precision.
*/
screenyaw = ABS(screenyaw);
#if ENABLE_VIEW_CORRECTION
g_adj_tanpitch = qTOd(TAN(pitch) * ((int32_t) g_cos_table[screenyaw]));
#else
g_adj_tanpitch = TAN(pitch);
#endif
/* Perform X & Y raycasting based on the quadrant of the yaw angle */
if (g_camera.yaw < ANGLE_90)
{
trv_ray_xcaster14(result);
trv_ray_ycaster12(result);
}
else if (g_camera.yaw < ANGLE_180)
{
trv_ray_xcaster23(result);
trv_ray_ycaster12(result);
}
else if (g_camera.yaw < ANGLE_270)
{
trv_ray_xcaster23(result);
trv_ray_ycaster34(result);
}
else
{
trv_ray_xcaster14(result);
trv_ray_ycaster34(result);
}
/* Perform Z ray casting based upon if we are looking up or down */
if (g_camera.pitch < ANGLE_90)
{
/* Get the adjusted cotangent of the pitch angle which is used to correct
* for the "fish eye" distortion. This correction consists of
* multiplying by the inverted cosine of the relative screen yaw
* position. The cotangent is double precision, the secant is double
* precision, the result will be retained as double precision.
*/
#if ENABLE_VIEW_CORRECTION
g_adj_cotpitch = qTOd(g_cot_table(pitch) * g_sec_table[screenyaw]);
#else
g_adj_cotpitch = g_cot_table(pitch);
#endif
trv_ray_zcasteru(result);
}
else
{
/* Get the adjusted cotangent of the pitch angle which is used to correct
* for the "fish eye" distortion. This correction consists of
* multiplying by the inverted cosine of the relative screen yaw
* position. The cotangent is double precision, the secant is double
* precision, the result will be retained as double precision.
*/
#if ENABLE_VIEW_CORRECTION
g_adj_cotpitch =
qTOd(g_cot_table(ANGLE_360 - pitch) * g_sec_table[screenyaw]);
#else
g_adj_cotpitch = g_cot_table(ANGLE_360 - pitch);
#endif
trv_ray_zcasterl(result);
}
}