From 435e8c74faa584127ba4addbd07157e2ee3a11cf Mon Sep 17 00:00:00 2001 From: Fazana <52551480+FazanaJ@users.noreply.github.com> Date: Tue, 24 Aug 2021 13:53:04 +0100 Subject: [PATCH] Puppycam raycasts moved to engine segment Also no longer need puppycam enabled to use. --- src/engine/math_util.c | 233 +++++++++++++++++++++++++++++++++++++++++ src/engine/math_util.h | 8 ++ src/game/camera.h | 1 - src/game/puppycam2.c | 230 ---------------------------------------- src/game/puppycam2.h | 12 --- 5 files changed, 241 insertions(+), 243 deletions(-) diff --git a/src/engine/math_util.c b/src/engine/math_util.c index 491d6d33..a16c29f0 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -6,9 +6,14 @@ #include "surface_collision.h" #include "extended_bounds.h" #include "trig_tables.inc.c" +#include "surface_load.h" +#include "game/puppyprint.h" #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; @@ -874,3 +879,231 @@ s32 anim_spline_poll(Vec3f result) { return hasEnded; } + +/// Multiply vector 'dest' by a +void *vec3f_mul(Vec3f dest, f32 a) +{ + dest[0] *= a; + dest[1] *= a; + dest[2] *= a; + return dest; //! warning: function returns address of local variable +} + +/// Get length of vector 'a' +f32 vec3f_length(Vec3f a) +{ + return sqrtf(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); +} + +/// Get dot product of vectors 'a' and 'b' +f32 vec3f_dot(Vec3f a, Vec3f b) +{ + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +/// Make 'dest' the difference of vectors a and b. +void *vec3f_dif(Vec3f dest, Vec3f a, Vec3f b) { + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; + dest[2] = a[2] - b[2]; + return dest; //! warning: function returns address of local variable +} + +//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 + norm[0] = 0; + norm[1] = surface->normal.y; + norm[2] = 0; + vec3f_mul(norm,RAY_OFFSET); + + vec3s_to_vec3f(v0, surface->vertex1); + vec3s_to_vec3f(v1, surface->vertex2); + vec3s_to_vec3f(v2, surface->vertex3); + + vec3f_add(v0, norm); + vec3f_add(v1, norm); + vec3f_add(v2, norm); + + vec3f_dif(e1, v1, v0); + vec3f_dif(e2, v2, v0); + + vec3f_cross(h, dir, e2); + + // Check if we're perpendicular from the surface + a = vec3f_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; + + vec3f_dif(s, orig, v0); + u = f * vec3f_dot(s, h); + if (u < 0.0f || u > 1.0f) + return FALSE; + + vec3f_cross(q, s, e1); + v = f * vec3f_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 * vec3f_dot(e2, q); + if (*length <= 0.00001 || *length > dir_length) + return FALSE; + + // Successful contact + vec3f_copy(add_dir, dir); + vec3f_mul(add_dir, *length); + vec3f_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; + #ifdef PUPPYPRINT + 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; + } + } + } + #ifdef PUPPYPRINT + collisionTime[perfIteration] += osGetTime()-first; + #endif +} + +void find_surface_on_ray_cell(s16 cellX, s16 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; + vec3f_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 9c10baf4..5cfac85b 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -32,9 +32,16 @@ extern f32 gCosineTable[]; #define min(a, b) ((a) <= (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#define ABS(x) ((x) > 0.f ? (x) : -(x)) #define sqr(x) ((x) * (x)) +#define RAYCAST_FIND_FLOOR (0x1) +#define RAYCAST_FIND_WALL (0x2) +#define RAYCAST_FIND_CEIL (0x4) +#define RAYCAST_FIND_WATER (0x8) +#define RAYCAST_FIND_ALL (0xFFFFFFFF) + void *vec3f_copy(Vec3f dest, Vec3f src); void *vec3f_set(Vec3f dest, f32 x, f32 y, f32 z); void *vec3f_add(Vec3f dest, Vec3f a); @@ -73,5 +80,6 @@ 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); #endif // MATH_UTIL_H diff --git a/src/game/camera.h b/src/game/camera.h index fad76a35..aac94161 100644 --- a/src/game/camera.h +++ b/src/game/camera.h @@ -17,7 +17,6 @@ * @see camera.c */ -#define ABS(x) ((x) > 0.f ? (x) : -(x)) #define ABS2(x) ((x) >= 0.f ? (x) : -(x)) /** diff --git a/src/game/puppycam2.c b/src/game/puppycam2.c index 4722be02..397a4e46 100644 --- a/src/game/puppycam2.c +++ b/src/game/puppycam2.c @@ -27,8 +27,6 @@ #ifdef PUPPYCAM -#define OFFSET 30.0f -#define STEPS 4 #define DECELERATION 0.66f #define DEADZONE 20 #define SCRIPT_MEMORY_POOL 0x1000 @@ -901,234 +899,6 @@ void puppycam_terrain_angle(void) gPuppyCam.terrainPitch = approach_f32_asymptotic(gPuppyCam.terrainPitch, gPuppyCam.intendedTerrainPitch, adjustSpeed); } -/// Multiply vector 'dest' by a -void *vec3f_mul(Vec3f dest, f32 a) -{ - dest[0] *= a; - dest[1] *= a; - dest[2] *= a; - return dest; //! warning: function returns address of local variable -} - -/// Get length of vector 'a' -f32 vec3f_length(Vec3f a) -{ - return sqrtf(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); -} - -/// Get dot product of vectors 'a' and 'b' -f32 vec3f_dot(Vec3f a, Vec3f b) -{ - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -} - -/// Make 'dest' the difference of vectors a and b. -void *vec3f_dif(Vec3f dest, Vec3f a, Vec3f b) { - dest[0] = a[0] - b[0]; - dest[1] = a[1] - b[1]; - dest[2] = a[2] - b[2]; - return dest; //! warning: function returns address of local variable -} - -//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 - norm[0] = 0; - norm[1] = surface->normal.y; - norm[2] = 0; - vec3f_mul(norm,OFFSET); - - vec3s_to_vec3f(v0, surface->vertex1); - vec3s_to_vec3f(v1, surface->vertex2); - vec3s_to_vec3f(v2, surface->vertex3); - - vec3f_add(v0, norm); - vec3f_add(v1, norm); - vec3f_add(v2, norm); - - vec3f_dif(e1, v1, v0); - vec3f_dif(e2, v2, v0); - - vec3f_cross(h, dir, e2); - - // Check if we're perpendicular from the surface - a = vec3f_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; - - vec3f_dif(s, orig, v0); - u = f * vec3f_dot(s, h); - if (u < 0.0f || u > 1.0f) - return FALSE; - - vec3f_cross(q, s, e1); - v = f * vec3f_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 * vec3f_dot(e2, q); - if (*length <= 0.00001 || *length > dir_length) - return FALSE; - - // Successful contact - vec3f_copy(add_dir, dir); - vec3f_mul(add_dir, *length); - vec3f_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; - #ifdef PUPPYPRINT - 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; - } - } - } - #ifdef PUPPYPRINT - collisionTime[perfIteration] += osGetTime()-first; - #endif -} - -void find_surface_on_ray_cell(s16 cellX, s16 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; - vec3f_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 = STEPS*ABS(dir[0]) / CELL_SIZE; - else - step = 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); - } - } -} - const struct sPuppyAngles puppyAnglesNull = { {PUPPY_NULL, PUPPY_NULL, PUPPY_NULL}, diff --git a/src/game/puppycam2.h b/src/game/puppycam2.h index 48b8fc36..7f8b0e9b 100644 --- a/src/game/puppycam2.h +++ b/src/game/puppycam2.h @@ -25,13 +25,6 @@ #define PUPPYCAM_MODE3_ZOOMED_OUT 0x4 #define PUPPYCAM_MODE3_ENTER_FIRST_PERSON 0x8 -#define RAYCAST_FIND_FLOOR (0x1) -#define RAYCAST_FIND_WALL (0x2) -#define RAYCAST_FIND_CEIL (0x4) -#define RAYCAST_FIND_WATER (0x8) -#define RAYCAST_FIND_ALL (0xFFFFFFFF) - - #include "include/command_macros_base.h" #define PUPPYVOLUME(x, y, z, length, height, width, yaw, functionptr, anglesptr, addflags, removeflags, flagpersistance, room, shape) \ @@ -43,10 +36,6 @@ CMD_W(removeflags), \ CMD_BBH(flagpersistance, shape, room) -//Some macros for the sake of basic human sanity. -#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) -#define ABS(x) ((x) > 0.f ? (x) : -(x)) - struct gPuppyOptions { s16 analogue; @@ -174,7 +163,6 @@ extern void puppycam_boot(void); extern void puppycam_init(void); extern void puppycam_loop(void); extern void puppycam_shake(s16 x, s16 y, s16 z); -extern void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos, s32 flags); extern f32 approach_f32_asymptotic(f32 current, f32 target, f32 multiplier); extern void puppycam_default_config(void); extern s16 LENCOS(s16 length, s16 direction);