Advertisement
Guest User

raycaster.c

a guest
Apr 9th, 2020
207
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 9.05 KB | None | 0 0
  1. #include <ultra64.h>
  2.  
  3. #include "sm64.h"
  4. #include "engine/surface_collision.h"
  5. #include "engine/surface_load.h"
  6. #include "area.h"
  7. #include "engine/math_util.h"
  8. #include "raycaster.h"
  9. #include "randomizer.h"
  10.  
  11. f32 gWallCheckRaycasterSearchDist = WALL_CHECK_RAYCASTER_DEFAULT_SEARCH_DIST;
  12. u8 gWallCheckNumRaySteps = 0;
  13.  
  14. Vec3f sRaycastDirections[WALL_CHECK_RAYCASTER_NUM_RAYS_TO_CAST];
  15. u8 sRaycasterInitialized = FALSE;
  16.  
  17. static struct Surface *return_first_wall_collision(struct SurfaceNode *surfaceNode, struct WallCollisionData *data)
  18. {
  19.     register f32 offset;
  20.     register f32 radius = data->radius;
  21.     struct Surface *surf;
  22.     register f32 x = data->x;
  23.     register f32 y = data->y;
  24.     register f32 z = data->z;
  25.     register f32 px, pz;
  26.     register f32 w1, w2, w3;
  27.     register f32 y1, y2, y3;
  28.  
  29.     while (surfaceNode != NULL)
  30.     {
  31.         surf = surfaceNode->surface;
  32.         surfaceNode = surfaceNode->next;
  33.  
  34.         if (y < surf->lowerY || y > surf->upperY)
  35.             continue;
  36.  
  37.         offset = surf->normal.x * x + surf->normal.y * y + surf->normal.z * z + surf->originOffset;
  38.  
  39.         if (offset < -radius || offset > radius)
  40.             continue;
  41.  
  42.         px = x;
  43.         pz = z;
  44.  
  45.         if (surf->flags & SURFACE_FLAG_X_PROJECTION)
  46.         {
  47.             w1 = -surf->vertex1[2];
  48.             w2 = -surf->vertex2[2];
  49.             w3 = -surf->vertex3[2];
  50.             y1 = surf->vertex1[1];
  51.             y2 = surf->vertex2[1];
  52.             y3 = surf->vertex3[1];
  53.  
  54.             if (surf->normal.x > 0.0f)
  55.             {
  56.                 if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) > 0.0f) continue;
  57.                 if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) > 0.0f) continue;
  58.                 if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) > 0.0f) continue;
  59.             }
  60.             else
  61.             {
  62.                 if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) < 0.0f) continue;
  63.                 if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) < 0.0f) continue;
  64.                 if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) < 0.0f) continue;
  65.             }
  66.         }
  67.         else
  68.         {
  69.             w1 = surf->vertex1[0];
  70.             w2 = surf->vertex2[0];
  71.             w3 = surf->vertex3[0];
  72.             y1 = surf->vertex1[1];
  73.             y2 = surf->vertex2[1];
  74.             y3 = surf->vertex3[1];
  75.  
  76.             if (surf->normal.z > 0.0f)
  77.             {
  78.                 if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) > 0.0f) continue;
  79.                 if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) > 0.0f) continue;
  80.                 if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) > 0.0f) continue;
  81.             }
  82.             else
  83.             {
  84.                 if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) < 0.0f) continue;
  85.                 if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) < 0.0f) continue;
  86.                 if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) < 0.0f) continue;
  87.             }
  88.         }
  89.  
  90.         if (surf->type == SURFACE_CAMERA_BOUNDARY)
  91.             continue;
  92.        
  93.         return surf;
  94.     }
  95.  
  96.     return NULL;
  97. }
  98.  
  99. static struct Surface *spawn_find_wall_collisions(struct WallCollisionData *colData)
  100. {
  101.     s16 cellX, cellZ;
  102.     s16 x = colData->x;
  103.     s16 z = colData->z;
  104.     struct Surface *surf;
  105.  
  106.     // World (level) consists of a 16x16 grid. Find where the collision is on
  107.     // the grid (round toward -inf)
  108.     cellX = ((x + 0x2000) / 0x400) & 0x0F;
  109.     cellZ = ((z + 0x2000) / 0x400) & 0x0F;
  110.  
  111.     surf = return_first_wall_collision(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, colData);
  112.    
  113.     if (surf == NULL)
  114.     {
  115.         surf = return_first_wall_collision(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, colData);
  116.     }
  117.    
  118.     return surf;
  119. }
  120.  
  121. void vec3s_resolve_wall_collisions(Vec3s pos, f32 radius)
  122. {
  123.     Vec3f pos2;
  124.    
  125.     vec3s_to_vec3f(pos2, pos);
  126.     f32_find_wall_collision(&pos2[0], &pos2[1], &pos2[2], 0.0f, radius);
  127.     vec3f_to_vec3s(pos, pos2);
  128. }
  129.  
  130. static u8 vec3f_is_in_ceiling_or_oob(Vec3f pos)
  131. {
  132.     struct Surface *floor, *ceil;
  133.     f32 floorHeight, ceilHeight;
  134.    
  135.     floorHeight = find_floor(pos[0], pos[1], pos[2], &floor);
  136.     ceilHeight = find_ceil(pos[0], floorHeight + 80, pos[2], &ceil);
  137.    
  138.     return ceilHeight < (pos[1] + 100) || floor == NULL;
  139. }
  140.  
  141. static void init_raycaster(void)
  142. {
  143.     u32 ray;
  144.     u32 angleBetweenRays = 0x10000 / WALL_CHECK_RAYCASTER_NUM_RAYS_TO_CAST;
  145.    
  146.     // Initialize gWallCheckNumRaySteps (gWallCheckRaycasterSearchDist is not a const so we have to do it programmatically)
  147.     if (gWallCheckNumRaySteps == 0) {
  148.         gWallCheckNumRaySteps = gWallCheckRaycasterSearchDist / WALL_CHECK_RAYCASTER_STEP_SIZE;
  149.     }
  150.  
  151.     // Fill sRaycastDirections
  152.     for (ray = 0; ray < WALL_CHECK_RAYCASTER_NUM_RAYS_TO_CAST; ray++) {
  153.         sRaycastDirections[ray][0] = WALL_CHECK_RAYCASTER_STEP_SIZE * coss(ray * angleBetweenRays);
  154.         sRaycastDirections[ray][1] = 0.0f;
  155.         sRaycastDirections[ray][2] = WALL_CHECK_RAYCASTER_STEP_SIZE * sins(ray * angleBetweenRays);
  156.     }
  157.    
  158.     sRaycasterInitialized = TRUE;
  159. }
  160.  
  161. void update_raycaster_params(void)
  162. {
  163.     if (gCurrLevelNum < 4 || sLevelParams[gCurrLevelNum - 4] == NULL) {
  164.         return;
  165.     }
  166.    
  167.     gWallCheckRaycasterSearchDist = (*sLevelParams[gCurrLevelNum - 4])[gCurrAreaIndex - 1].wallCheckRaycasterSearchDist;
  168.     gWallCheckNumRaySteps = gWallCheckRaycasterSearchDist / WALL_CHECK_RAYCASTER_STEP_SIZE;
  169. }
  170.  
  171. u8 is_safe_near_walls(Vec3s pos, u8 killOnOob)
  172. {
  173.     /*
  174.         1. cast WALL_CHECK_RAYCASTER_NUM_RAYS_TO_CAST rays out evenly from pos until they hit ceiling, oob, or a wall
  175.         2. discard the ones that hit ceiling or oob
  176.         3. for each ray, take the dot product of the ray/direction vector with the wall's normal
  177.         4. if there is a positive dot product, return false
  178.         5. return true
  179.     */
  180.    
  181.     register s8 i, j;
  182.    
  183.     register f32 rayX;
  184.     register f32 rayY = pos[1] + 15;
  185.     register f32 rayZ;
  186.    
  187.     register f32 directionX;
  188.     register f32 directionZ;
  189.    
  190.     struct Surface *lowFloor, *highFloor, *ceil;
  191.     register f32 floorHeight, ceilHeight, upperFloorHeight;
  192.    
  193.     struct WallCollisionData wallCollision;
  194.     struct Surface *rayWall = NULL;
  195.    
  196.     register f32 normX, normZ;
  197.    
  198.     // Initialize raycaster if necessary
  199.     if (sRaycasterInitialized == FALSE)
  200.         init_raycaster();
  201.    
  202.     wallCollision.x = pos[0],
  203.     wallCollision.z = pos[2],
  204.    
  205.     // Rays only move in X or Z directions, since walls are vertical, so we dont need to update this
  206.     wallCollision.y = rayY,
  207.    
  208.     // Thickness of the walls
  209.     wallCollision.radius = 75.0f;
  210.    
  211.     // If the point is already in a wall, it is not safe
  212.     if (spawn_find_wall_collisions(&wallCollision) != NULL)
  213.         return FALSE;
  214.  
  215.     // Raycast
  216.     for (i = -1; ++i < WALL_CHECK_RAYCASTER_NUM_RAYS_TO_CAST;) {
  217.         // Create ray
  218.         rayX = pos[0],
  219.         rayZ = pos[2];
  220.        
  221.         // Create direction vector
  222.         directionX = sRaycastDirections[i][0],
  223.         directionZ = sRaycastDirections[i][2];
  224.        
  225.         // Check if ray is in a wall yet, if not, advance it until it hits the limit
  226.         // If it ends up in a ceiling or OoB, disregard the ray
  227.         // Then check the wall's normal to determine if the ray collided with the back of it
  228.         for (j = -1; ++j < gWallCheckNumRaySteps;) {
  229.             rayX += directionX;
  230.             rayZ += directionZ;
  231.            
  232.             floorHeight = find_floor(rayX, rayY, rayZ, &lowFloor);
  233.            
  234.             // Check if the ray is in a ceiling
  235.             ceilHeight = find_ceil(rayX, floorHeight + 80, rayZ, &ceil);
  236.             if (ceilHeight < (rayY + 100)) {
  237.                 break;
  238.             }
  239.            
  240.             // See if the ray passed under a floor
  241.             upperFloorHeight = find_floor(rayX, ceilHeight - 80, rayZ, &highFloor);
  242.             if (upperFloorHeight > rayY && (upperFloorHeight - rayY) < 750) {
  243.                 break;
  244.             }
  245.            
  246.             // Check if the ray is in OoB
  247.             if (lowFloor == NULL) {
  248.                 if (killOnOob) {
  249.                     return FALSE;
  250.                 }
  251.                
  252.                 break;
  253.             }
  254.            
  255.             // Create collision parameters
  256.             wallCollision.x = rayX,
  257.             wallCollision.z = rayZ;
  258.            
  259.             // Check for walls
  260.             if ((rayWall = spawn_find_wall_collisions(&wallCollision)) != NULL)
  261.                 break;
  262.         }
  263.        
  264.         // Check if the ray collided with the back of the wall
  265.         if (rayWall == NULL)
  266.             continue;
  267.        
  268.         normX = rayWall->normal.x,
  269.         normZ = rayWall->normal.z;
  270.        
  271.         // Take dot product; positive means the ray is aligned with the wall normal,
  272.         // and is therefore behind it.
  273.         if (((rayX - pos[0]) * normX + (rayZ - pos[2]) * normZ) > 0)
  274.             return FALSE;
  275.     }
  276.    
  277.     return TRUE;
  278. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement