Improve bettercam's collision detection and avoidance

The precision of ray casting was too low previously, causing the
collision checks to skip right past a wall sometimes. There was also
nothing to prevent the camera from getting too close to a wall
horizontally or vertically.
This commit is contained in:
MysterD 2020-09-19 18:59:58 -07:00
parent bbdc3180cd
commit 4b37648487
2 changed files with 62 additions and 5 deletions

View file

@ -977,11 +977,14 @@ void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Ve
return;
}
// increase collision checking precision (normally 1)
f32 precision = 2;
// Get cells we cross using DDA
if (absx(dir[0]) >= absx(dir[2]))
step = absx(dir[0]) / CELL_SIZE;
step = precision * absx(dir[0]) / CELL_SIZE;
else
step = absx(dir[2]) / CELL_SIZE;
step = precision * absx(dir[2]) / CELL_SIZE;
dx = dir[0] / step / CELL_SIZE;
dz = dir[2] / step / CELL_SIZE;

View file

@ -16,6 +16,9 @@
#include <stdio.h>
#endif
#define NEW_CAM_BOUNDING_BOX_RAYS 4
#define NEW_CAM_BOUNDING_BOX_HRADIUS 250
#define NEW_CAM_BOUNDING_BOX_VRADIUS 50
/**
Quick explanation of the camera modes
@ -473,6 +476,47 @@ static void newcam_update_values(void) {
}
}
static void newcam_bounding_box(void) {
Vec3f camdirs[NEW_CAM_BOUNDING_BOX_RAYS] = { 0 };
Vec3f raypos[NEW_CAM_BOUNDING_BOX_RAYS] = { 0 };
s16 antiYaw = newcam_yaw - 0x4000;
// sideways ray 1
camdirs[0][0] = coss(antiYaw) * NEW_CAM_BOUNDING_BOX_HRADIUS;
camdirs[0][2] = sins(antiYaw) * NEW_CAM_BOUNDING_BOX_HRADIUS;
// sideways ray 2
camdirs[1][0] = -coss(antiYaw) * NEW_CAM_BOUNDING_BOX_HRADIUS;
camdirs[1][2] = -sins(antiYaw) * NEW_CAM_BOUNDING_BOX_HRADIUS;
// vertical rays
camdirs[2][1] = -NEW_CAM_BOUNDING_BOX_VRADIUS;
camdirs[3][1] = NEW_CAM_BOUNDING_BOX_VRADIUS;
for (int i = 0; i < NEW_CAM_BOUNDING_BOX_RAYS; i++) {
struct Surface* surf;
Vec3f offset = { 0 };
Vec3f startpos = { 0 };
vec3f_copy(startpos, newcam_pos);
vec3f_add(startpos, offset);
find_surface_on_ray(startpos, camdirs[i], &surf, raypos[i]);
if (!surf) {
vec3f_copy(raypos[i], startpos);
vec3f_add(raypos[i], camdirs[i]);
}
}
Vec3f avg = { 0 };
for (int i = 0; i < NEW_CAM_BOUNDING_BOX_RAYS; i++) {
vec3f_add(avg, raypos[i]);
}
vec3f_mul(avg, 1.0f / ((f32)NEW_CAM_BOUNDING_BOX_RAYS));
vec3f_copy(newcam_pos, avg);
}
static void newcam_collision(void) {
struct Surface *surf;
Vec3f camdir;
@ -486,8 +530,16 @@ static void newcam_collision(void) {
newcam_coldist = sqrtf((newcam_pos_target[0] - hitpos[0]) * (newcam_pos_target[0] - hitpos[0]) + (newcam_pos_target[1] - hitpos[1]) * (newcam_pos_target[1] - hitpos[1]) + (newcam_pos_target[2] - hitpos[2]) * (newcam_pos_target[2] - hitpos[2]));
if (surf) {
// offset the hit pos by the hit normal
Vec3f offset = { 0 };
offset[0] = surf->normal.x;
offset[1] = surf->normal.y;
offset[2] = surf->normal.z;
vec3f_mul(offset, 5.0f);
vec3f_add(hitpos, offset);
newcam_pos[0] = hitpos[0];
newcam_pos[1] = approach_f32(hitpos[1],newcam_pos[1],25,-25);
newcam_pos[1] = hitpos[1];
newcam_pos[2] = hitpos[2];
newcam_pan_x = 0;
newcam_pan_z = 0;
@ -541,8 +593,10 @@ static void newcam_position_cam(void) {
if (newcam_modeflags & NC_FLAG_FOCUSZ)
newcam_lookat[2] = newcam_pos_target[2]-newcam_pan_z;
if (newcam_modeflags & NC_FLAG_COLLISION)
newcam_collision();
if (newcam_modeflags & NC_FLAG_COLLISION) {
newcam_collision();
newcam_bounding_box();
}
}