From aa2ebba8dc0e21d5d17e6dbb78beb0b10cf0034d Mon Sep 17 00:00:00 2001 From: Arceveti <73617174+Arceveti@users.noreply.github.com> Date: Sun, 26 Sep 2021 10:32:42 -0700 Subject: [PATCH] Move raycasting functions to surface_collision --- src/engine/math_util.c | 182 -------------------------------- src/engine/math_util.h | 1 - src/engine/surface_collision.c | 186 +++++++++++++++++++++++++++++++++ src/engine/surface_collision.h | 1 + 4 files changed, 187 insertions(+), 183 deletions(-) diff --git a/src/engine/math_util.c b/src/engine/math_util.c index 722f7ebe..d2a3b0c9 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -12,9 +12,6 @@ #include "config.h" -#define RAY_OFFSET 30.0f /*How many units to extrapolate surfaces when testing for a raycast*/ -#define RAY_STEPS 4 /*How many steps to do when casting rays, default to quartersteps.*/ - // Variables for a spline curve animation (used for the flight path in the grand star cutscene) Vec4s *gSplineKeyframe; float gSplineKeyframeFraction; @@ -818,182 +815,3 @@ f32 vec3f_dot(Vec3f a, Vec3f b) { void vec3f_dif(Vec3f dest, Vec3f a, Vec3f b) { vec3_diff(dest, a, b); } - -// Raycasting -s32 ray_surface_intersect(Vec3f orig, Vec3f dir, f32 dir_length, struct Surface *surface, Vec3f hit_pos, f32 *length) { - Vec3f v0, v1, v2, e1, e2, h, s, q; - f32 a, f, u, v; - Vec3f add_dir; - Vec3f norm; - - //Ignore certain surface types. - if (surface->type == SURFACE_INTANGIBLE || surface->flags & SURFACE_FLAG_NO_CAM_COLLISION) - return FALSE; - - // Get surface normal and some other stuff - vec3_set(norm, 0, surface->normal.y, 0); - vec3_mul_val(norm, RAY_OFFSET); - - vec3_copy(v0, surface->vertex1); - vec3_copy(v1, surface->vertex2); - vec3_copy(v2, surface->vertex3); - - vec3_add(v0, norm); - vec3_add(v1, norm); - vec3_add(v2, norm); - - vec3_diff(e1, v1, v0); - vec3_diff(e2, v2, v0); - - vec3_cross(h, dir, e2); - - // Check if we're perpendicular from the surface - a = vec3_dot(e1, h); - if (a > -0.00001f && a < 0.00001f) { - return FALSE; - } - // Check if we're making contact with the surface - f = 1.0f / a; - - vec3_diff(s, orig, v0); - u = f * vec3_dot(s, h); - if (u < 0.0f || u > 1.0f) { - return FALSE; - } - vec3_cross(q, s, e1); - v = f * vec3_dot(dir, q); - if (v < 0.0f || u + v > 1.0f) { - return FALSE; - } - // Get the length between our origin and the surface contact point - *length = f * vec3_dot(e2, q); - if (*length <= 0.00001 || *length > dir_length) { - return FALSE; - } - // Successful contact - vec3f_copy(add_dir, dir); - vec3_mul_val(add_dir, *length); - vec3_sum(hit_pos, orig, add_dir); - return TRUE; -} - -void find_surface_on_ray_list(struct SurfaceNode *list, Vec3f orig, Vec3f dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length) { - s32 hit; - f32 length; - Vec3f chk_hit_pos; - f32 top, bottom; - #if PUPPYPRINT_DEBUG - OSTime first = osGetTime(); - #endif - - // Get upper and lower bounds of ray - if (dir[1] >= 0.0f) { - top = orig[1] + dir[1] * dir_length; - bottom = orig[1]; - } else { - top = orig[1]; - bottom = orig[1] + dir[1] * dir_length; - } - - // Iterate through every surface of the list - for (; list != NULL; list = list->next) { - // Reject surface if out of vertical bounds - if (list->surface->lowerY > top || list->surface->upperY < bottom) { - continue; - } - // Check intersection between the ray and this surface - if ((hit = ray_surface_intersect(orig, dir, dir_length, list->surface, chk_hit_pos, &length)) != 0) { - if (length <= *max_length) { - *hit_surface = list->surface; - vec3f_copy(hit_pos, chk_hit_pos); - *max_length = length; - } - } - } - #if PUPPYPRINT_DEBUG - collisionTime[perfIteration] += osGetTime()-first; - #endif -} - -void find_surface_on_ray_cell(s32 cellX, s32 cellZ, Vec3f orig, Vec3f normalized_dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length, s32 flags) { - // Skip if OOB - if (cellX >= 0 && cellX <= (NUM_CELLS - 1) && cellZ >= 0 && cellZ <= (NUM_CELLS - 1)) { - // Iterate through each surface in this partition - if (normalized_dir[1] > -0.99999f && flags & RAYCAST_FIND_CEIL) { - find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - } - if (normalized_dir[1] < 0.99999f && flags & RAYCAST_FIND_FLOOR) { - find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - } - if (flags & RAYCAST_FIND_WALL) { - find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - } - if (flags & RAYCAST_FIND_WATER) { - find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WATER].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WATER].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - } - } -} - -void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos, s32 flags) { - f32 max_length; - s32 cellZ, cellX, cellPrevX, cellPrevZ; - f32 fCellZ, fCellX; - f32 dir_length; - Vec3f normalized_dir; - f32 step, dx, dz; - s32 i; - - // Set that no surface has been hit - *hit_surface = NULL; - vec3_sum(hit_pos, orig, dir); - - // Get normalized direction - dir_length = vec3f_length(dir); - max_length = dir_length; - vec3f_copy(normalized_dir, dir); - vec3f_normalize(normalized_dir); - - // Get our cell coordinate - fCellX = (orig[0] + LEVEL_BOUNDARY_MAX) / CELL_SIZE; - fCellZ = (orig[2] + LEVEL_BOUNDARY_MAX) / CELL_SIZE; - cellX = fCellX; - cellZ = fCellZ; - cellPrevX = cellX; - cellPrevZ = cellZ; - - // Don't do DDA if straight down - if (normalized_dir[1] >= 0.99999f || normalized_dir[1] <= -0.99999f) { - find_surface_on_ray_cell(cellX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length, flags); - return; - } - - // Get cells we cross using DDA - if (ABS(dir[0]) >= ABS(dir[2])) - step = RAY_STEPS * ABS(dir[0]) / CELL_SIZE; - else - step = RAY_STEPS * ABS(dir[2]) / CELL_SIZE; - - dx = dir[0] / step / CELL_SIZE; - dz = dir[2] / step / CELL_SIZE; - - for (i = 0; i < step && *hit_surface == NULL; i++) { - find_surface_on_ray_cell(cellX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length, flags); - - // Move cell coordinate - fCellX += dx; - fCellZ += dz; - cellPrevX = cellX; - cellPrevZ = cellZ; - cellX = fCellX; - cellZ = fCellZ; - - if ((cellPrevX != cellX) && (cellPrevZ != cellZ)) { - find_surface_on_ray_cell(cellX, cellPrevZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length, flags); - find_surface_on_ray_cell(cellPrevX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length, flags); - } - } -} diff --git a/src/engine/math_util.h b/src/engine/math_util.h index faf6a6d9..8c776a55 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -381,4 +381,3 @@ f32 atan2f(f32 a, f32 b); void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c); void anim_spline_init(Vec4s *keyFrames); s32 anim_spline_poll(Vec3f result); -extern void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos, s32 flags); diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index 9a23100b..c71baf00 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -933,3 +933,189 @@ s32 unused_resolve_floor_or_ceil_collisions(s32 checkCeil, f32 *px, f32 *py, f32 return 0; } + +/************************************************** + * RAYCASTING * + **************************************************/ + +#define RAY_OFFSET 30.0f /*How many units to extrapolate surfaces when testing for a raycast*/ +#define RAY_STEPS 4 /*How many steps to do when casting rays, default to quartersteps.*/ + +s32 ray_surface_intersect(Vec3f orig, Vec3f dir, f32 dir_length, struct Surface *surface, Vec3f hit_pos, f32 *length) { + Vec3f v0, v1, v2, e1, e2, h, s, q; + f32 a, f, u, v; + Vec3f add_dir; + Vec3f norm; + + //Ignore certain surface types. + if (surface->type == SURFACE_INTANGIBLE || surface->flags & SURFACE_FLAG_NO_CAM_COLLISION) + return FALSE; + + // Get surface normal and some other stuff + vec3_set(norm, 0, surface->normal.y, 0); + vec3_mul_val(norm, RAY_OFFSET); + + vec3_copy(v0, surface->vertex1); + vec3_copy(v1, surface->vertex2); + vec3_copy(v2, surface->vertex3); + + vec3_add(v0, norm); + vec3_add(v1, norm); + vec3_add(v2, norm); + + vec3_diff(e1, v1, v0); + vec3_diff(e2, v2, v0); + + vec3_cross(h, dir, e2); + + // Check if we're perpendicular from the surface + a = vec3_dot(e1, h); + if (a > -0.00001f && a < 0.00001f) { + return FALSE; + } + // Check if we're making contact with the surface + f = 1.0f / a; + + vec3_diff(s, orig, v0); + u = f * vec3_dot(s, h); + if (u < 0.0f || u > 1.0f) { + return FALSE; + } + vec3_cross(q, s, e1); + v = f * vec3_dot(dir, q); + if (v < 0.0f || u + v > 1.0f) { + return FALSE; + } + // Get the length between our origin and the surface contact point + *length = f * vec3_dot(e2, q); + if (*length <= 0.00001 || *length > dir_length) { + return FALSE; + } + // Successful contact + vec3f_copy(add_dir, dir); + vec3_mul_val(add_dir, *length); + vec3_sum(hit_pos, orig, add_dir); + return TRUE; +} + +void find_surface_on_ray_list(struct SurfaceNode *list, Vec3f orig, Vec3f dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length) { + s32 hit; + f32 length; + Vec3f chk_hit_pos; + f32 top, bottom; + #if PUPPYPRINT_DEBUG + OSTime first = osGetTime(); + #endif + + // Get upper and lower bounds of ray + if (dir[1] >= 0.0f) { + top = orig[1] + dir[1] * dir_length; + bottom = orig[1]; + } else { + top = orig[1]; + bottom = orig[1] + dir[1] * dir_length; + } + + // Iterate through every surface of the list + for (; list != NULL; list = list->next) { + // Reject surface if out of vertical bounds + if (list->surface->lowerY > top || list->surface->upperY < bottom) { + continue; + } + // Check intersection between the ray and this surface + if ((hit = ray_surface_intersect(orig, dir, dir_length, list->surface, chk_hit_pos, &length)) != 0) { + if (length <= *max_length) { + *hit_surface = list->surface; + vec3f_copy(hit_pos, chk_hit_pos); + *max_length = length; + } + } + } + #if PUPPYPRINT_DEBUG + collisionTime[perfIteration] += osGetTime()-first; + #endif +} + +void find_surface_on_ray_cell(s32 cellX, s32 cellZ, Vec3f orig, Vec3f normalized_dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length, s32 flags) { + // Skip if OOB + if (cellX >= 0 && cellX <= (NUM_CELLS - 1) && cellZ >= 0 && cellZ <= (NUM_CELLS - 1)) { + // Iterate through each surface in this partition + if (normalized_dir[1] > -0.99999f && flags & RAYCAST_FIND_CEIL) { + find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + } + if (normalized_dir[1] < 0.99999f && flags & RAYCAST_FIND_FLOOR) { + find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + } + if (flags & RAYCAST_FIND_WALL) { + find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + } + if (flags & RAYCAST_FIND_WATER) { + find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WATER].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WATER].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + } + } +} + +void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos, s32 flags) { + f32 max_length; + s32 cellZ, cellX, cellPrevX, cellPrevZ; + f32 fCellZ, fCellX; + f32 dir_length; + Vec3f normalized_dir; + f32 step, dx, dz; + s32 i; + + // Set that no surface has been hit + *hit_surface = NULL; + vec3_sum(hit_pos, orig, dir); + + // Get normalized direction + dir_length = vec3_mag(dir); + max_length = dir_length; + vec3f_copy(normalized_dir, dir); + vec3f_normalize(normalized_dir); + + // Get our cell coordinate + fCellX = (orig[0] + LEVEL_BOUNDARY_MAX) / CELL_SIZE; + fCellZ = (orig[2] + LEVEL_BOUNDARY_MAX) / CELL_SIZE; + cellX = fCellX; + cellZ = fCellZ; + cellPrevX = cellX; + cellPrevZ = cellZ; + + // Don't do DDA if straight down + if (normalized_dir[1] >= 0.99999f || normalized_dir[1] <= -0.99999f) { + find_surface_on_ray_cell(cellX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length, flags); + return; + } + + // Get cells we cross using DDA + if (ABS(dir[0]) >= ABS(dir[2])) + step = RAY_STEPS * ABS(dir[0]) / CELL_SIZE; + else + step = RAY_STEPS * ABS(dir[2]) / CELL_SIZE; + + dx = dir[0] / step / CELL_SIZE; + dz = dir[2] / step / CELL_SIZE; + + for (i = 0; i < step && *hit_surface == NULL; i++) { + find_surface_on_ray_cell(cellX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length, flags); + + // Move cell coordinate + fCellX += dx; + fCellZ += dz; + cellPrevX = cellX; + cellPrevZ = cellZ; + cellX = fCellX; + cellZ = fCellZ; + + if ((cellPrevX != cellX) && (cellPrevZ != cellZ)) { + find_surface_on_ray_cell(cellX, cellPrevZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length, flags); + find_surface_on_ray_cell(cellPrevX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length, flags); + } + } +} + diff --git a/src/engine/surface_collision.h b/src/engine/surface_collision.h index 6cfe6ce5..7adf2544 100644 --- a/src/engine/surface_collision.h +++ b/src/engine/surface_collision.h @@ -38,3 +38,4 @@ s32 find_water_level_and_floor(s32 x, s32 z, struct Surface **pfloor); s32 find_water_level(s32 x, s32 z); s32 find_poison_gas_level(s32 x, s32 z); void debug_surface_list_info(f32 xPos, f32 zPos); +extern void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos, s32 flags);