Move raycasts back to math_util

This commit is contained in:
Fazana
2021-09-30 10:40:37 +01:00
parent be79ded91e
commit 4448273b14
4 changed files with 163 additions and 164 deletions

View File

@@ -1116,3 +1116,165 @@ s32 anim_spline_poll(Vec3f result) {
return hasEnded;
}
/**************************************************
* 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 > -NEAR_ZERO) && (a < NEAR_ZERO)) 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)) && (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] > -NEAR_ONE) && (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] < NEAR_ONE) && (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) {
s32 cellZ, cellX, cellPrevX, cellPrevZ;
f32 fCellZ, fCellX;
Vec3f normalized_dir;
f32 step;
s32 i;
// Set that no surface has been hit
*hit_surface = NULL;
vec3_sum(hit_pos, orig, dir);
// Get normalized direction
f32 dir_length = vec3_mag(dir);
f32 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;
}
f32 dx = dir[0] / step / CELL_SIZE;
f32 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);
}
}
}

View File

@@ -530,5 +530,6 @@ void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c);
void anim_spline_init(Vec4s *keyFrames);
s32 anim_spline_poll(Vec3f result);
void mtxf_rot_trans_mul(Vec3s rot, Vec3f trans, Mat4 dest, Mat4 src);
void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos, s32 flags);
#endif // MATH_UTIL_H

View File

@@ -832,166 +832,3 @@ 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 > -NEAR_ZERO) && (a < NEAR_ZERO)) 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)) && (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] > -NEAR_ONE) && (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] < NEAR_ONE) && (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) {
s32 cellZ, cellX, cellPrevX, cellPrevZ;
f32 fCellZ, fCellX;
Vec3f normalized_dir;
f32 step;
s32 i;
// Set that no surface has been hit
*hit_surface = NULL;
vec3_sum(hit_pos, orig, dir);
// Get normalized direction
f32 dir_length = vec3_mag(dir);
f32 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;
}
f32 dx = dir[0] / step / CELL_SIZE;
f32 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);
}
}
}

View File

@@ -46,6 +46,5 @@ 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);
void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos, s32 flags);
#endif // SURFACE_COLLISION_H