mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-12-08 00:52:41 +00:00
Fix collision bugs setting now allows for non-axis-aligned walls to be correct
This commit is contained in:
parent
3e46cc1161
commit
7b7e2245aa
8 changed files with 267 additions and 80 deletions
|
|
@ -134,8 +134,15 @@ void *vec3f_cross(Vec3f dest, Vec3f a, Vec3f b) {
|
|||
|
||||
/// Scale vector 'dest' so it has length 1
|
||||
void *vec3f_normalize(Vec3f dest) {
|
||||
//! Possible division by zero
|
||||
f32 invsqrt = 1.0f / sqrtf(dest[0] * dest[0] + dest[1] * dest[1] + dest[2] * dest[2]);
|
||||
f32 div = sqrtf(dest[0] * dest[0] + dest[1] * dest[1] + dest[2] * dest[2]);
|
||||
if (div == 0) {
|
||||
dest[0] = 0;
|
||||
dest[1] = 0;
|
||||
dest[2] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
f32 invsqrt = 1.0f / div;
|
||||
|
||||
dest[0] *= invsqrt;
|
||||
dest[1] *= invsqrt;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,89 @@
|
|||
#include "game/game_init.h"
|
||||
#include "pc/utils/misc.h"
|
||||
#include "pc/network/network.h"
|
||||
#include "src/game/debug.h" // DO NOT COMMIT
|
||||
|
||||
Vec3f gFindWallDirection = { 0 };
|
||||
u8 gFindWallDirectionActive = false;
|
||||
|
||||
#define CLAMP(_val, _min, _max) MAX(MIN((_val), _max), _min)
|
||||
|
||||
static void closest_point_to_triangle(struct Surface* surf, Vec3f src, Vec3f out) {
|
||||
Vec3f v1; vec3s_to_vec3f(v1, surf->vertex1);
|
||||
Vec3f v2; vec3s_to_vec3f(v2, surf->vertex2);
|
||||
Vec3f v3; vec3s_to_vec3f(v3, surf->vertex3);
|
||||
|
||||
Vec3f edge0; vec3f_dif(edge0, v2, v1);
|
||||
Vec3f edge1; vec3f_dif(edge1, v3, v1);
|
||||
Vec3f v0; vec3f_dif(v0, v1, src);
|
||||
|
||||
f32 a = vec3f_dot(edge0, edge0);
|
||||
f32 b = vec3f_dot(edge0, edge1);
|
||||
f32 c = vec3f_dot(edge1, edge1);
|
||||
f32 d = vec3f_dot(edge0, v0);
|
||||
f32 e = vec3f_dot(edge1, v0);
|
||||
|
||||
f32 det = (a * c) - (b * b);
|
||||
f32 s = (b * e) - (c * d);
|
||||
f32 t = (b * d) - (a * e);
|
||||
|
||||
if ((s + t) < det) {
|
||||
if (s < 0) {
|
||||
if (t < 0) {
|
||||
if (d < 0) {
|
||||
s = CLAMP(-d/a, 0, 1);
|
||||
t = 0;
|
||||
} else {
|
||||
s = 0;
|
||||
t = CLAMP(-e/c, 0, 1);
|
||||
}
|
||||
} else {
|
||||
s = 0;
|
||||
t = CLAMP(-e/c, 0, 1);
|
||||
}
|
||||
} else if (t < 0) {
|
||||
s = CLAMP(-d/a, 0, 1);
|
||||
t = 0;
|
||||
} else {
|
||||
f32 invDet = 1 / det;
|
||||
s *= invDet;
|
||||
t *= invDet;
|
||||
}
|
||||
} else {
|
||||
if (s < 0) {
|
||||
f32 tmp0 = (b + d);
|
||||
f32 tmp1 = (c + e);
|
||||
if (tmp1 > tmp0) {
|
||||
f32 numer = tmp1 - tmp0;
|
||||
f32 denom = a-2*b+c;
|
||||
s = CLAMP(numer/denom, 0, 1);
|
||||
t = (1 - s);
|
||||
} else {
|
||||
t = CLAMP(-e/c, 0, 1);
|
||||
s = 0;
|
||||
}
|
||||
} else if (t < 0.f) {
|
||||
if ((a + d) > (b + e)) {
|
||||
f32 numer = c+e-b-d;
|
||||
f32 denom = a-2*b+c;
|
||||
s = CLAMP(numer/denom, 0, 1);
|
||||
t = (1 - s);
|
||||
} else {
|
||||
s = CLAMP(-e/c, 0, 1);
|
||||
t = 0;
|
||||
}
|
||||
} else {
|
||||
f32 numer = c+e-b-d;
|
||||
f32 denom = a-2*b+c;
|
||||
s = CLAMP(numer/denom, 0, 1);
|
||||
t = 1 - s;
|
||||
}
|
||||
}
|
||||
|
||||
out[0] = v1[0] + s * edge0[0] + t * edge1[0];
|
||||
out[1] = v1[1] + s * edge0[1] + t * edge1[1];
|
||||
out[2] = v1[2] + s * edge0[2] + t * edge1[2];
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* WALLS *
|
||||
|
|
@ -33,6 +116,9 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode,
|
|||
register f32 y1, y2, y3;
|
||||
s32 numCols = 0;
|
||||
|
||||
Vec3f cPos = { 0 };
|
||||
Vec3f cNorm = { 0 };
|
||||
|
||||
// Max collision radius = 200
|
||||
if (radius > 200.0f) {
|
||||
radius = 200.0f;
|
||||
|
|
@ -48,66 +134,106 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode,
|
|||
continue;
|
||||
}
|
||||
|
||||
offset = surf->normal.x * x + surf->normal.y * y + surf->normal.z * z + surf->originOffset;
|
||||
if (gServerSettings.fixCollisionBugs) {
|
||||
// Check AABB to exclude walls before doing expensive triangle check
|
||||
f32 minX = MIN(MIN(surf->vertex1[0], surf->vertex2[0]), surf->vertex3[0]) - radius;
|
||||
f32 minZ = MIN(MIN(surf->vertex1[2], surf->vertex2[2]), surf->vertex3[2]) - radius;
|
||||
f32 maxX = MAX(MAX(surf->vertex1[0], surf->vertex2[0]), surf->vertex3[0]) + radius;
|
||||
f32 maxZ = MAX(MAX(surf->vertex1[2], surf->vertex2[2]), surf->vertex3[2]) + radius;
|
||||
if (x < minX || x > maxX) { continue; }
|
||||
if (z < minZ || z > maxZ) { continue; }
|
||||
|
||||
if (offset < -radius || offset > radius) {
|
||||
continue;
|
||||
}
|
||||
|
||||
px = x;
|
||||
pz = z;
|
||||
|
||||
//! (Quantum Tunneling) Due to issues with the vertices walls choose and
|
||||
// the fact they are floating point, certain floating point positions
|
||||
// along the seam of two walls may collide with neither wall or both walls.
|
||||
if (surf->flags & SURFACE_FLAG_X_PROJECTION) {
|
||||
w1 = -surf->vertex1[2]; w2 = -surf->vertex2[2]; w3 = -surf->vertex3[2];
|
||||
y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1];
|
||||
|
||||
if (surf->normal.x > 0.0f) {
|
||||
if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) < 0.0f) {
|
||||
// Exclude triangles from wrong movement side
|
||||
Vec3f norm = { surf->normal.x, surf->normal.y, surf->normal.z };
|
||||
if (gFindWallDirectionActive) {
|
||||
if (vec3f_dot(norm, gFindWallDirection) > 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w1 = surf->vertex1[0]; w2 = surf->vertex2[0]; w3 = surf->vertex3[0];
|
||||
y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1];
|
||||
|
||||
if (surf->normal.z > 0.0f) {
|
||||
if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) > 0.0f) {
|
||||
continue;
|
||||
// Find closest point to triangle
|
||||
Vec3f src = { x, y, z };
|
||||
closest_point_to_triangle(surf, src, cPos);
|
||||
|
||||
// Exclude triangles where y isn't inside of it
|
||||
if (fabs(cPos[1] - y) > 1) { continue; }
|
||||
|
||||
// Figure out normal
|
||||
f32 dX = src[0] - cPos[0];
|
||||
f32 dZ = src[2] - cPos[2];
|
||||
f32 dist = sqrtf(dX * dX + dZ * dZ);
|
||||
if (dist > radius) { continue; }
|
||||
|
||||
cNorm[0] = dX / dist;
|
||||
cNorm[1] = 0;
|
||||
cNorm[2] = dZ / dist;
|
||||
|
||||
// Exclude triangles that are colliding from the wrong side
|
||||
if (!gFindWallDirectionActive && vec3f_dot(norm, cNorm) < 0) { continue; }
|
||||
|
||||
} else {
|
||||
|
||||
offset = surf->normal.x * x + surf->normal.y * y + surf->normal.z * z + surf->originOffset;
|
||||
|
||||
if (offset < -radius || offset > radius) {
|
||||
continue;
|
||||
}
|
||||
|
||||
px = x;
|
||||
pz = z;
|
||||
|
||||
//! (Quantum Tunneling) Due to issues with the vertices walls choose and
|
||||
// the fact they are floating point, certain floating point positions
|
||||
// along the seam of two walls may collide with neither wall or both walls.
|
||||
if (surf->flags & SURFACE_FLAG_X_PROJECTION) {
|
||||
w1 = -surf->vertex1[2]; w2 = -surf->vertex2[2]; w3 = -surf->vertex3[2];
|
||||
y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1];
|
||||
|
||||
if (surf->normal.x > 0.0f) {
|
||||
if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) < 0.0f) {
|
||||
continue;
|
||||
w1 = surf->vertex1[0]; w2 = surf->vertex2[0]; w3 = surf->vertex3[0];
|
||||
y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1];
|
||||
|
||||
if (surf->normal.z > 0.0f) {
|
||||
if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) > 0.0f) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -147,11 +273,17 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode,
|
|||
//! (Wall Overlaps) Because this doesn't update the x and z local variables,
|
||||
// multiple walls can push mario more than is required.
|
||||
// <Fixed when gServerSettings.fixCollisionBugs != 0>
|
||||
data->x += surf->normal.x * (radius - offset);
|
||||
data->z += surf->normal.z * (radius - offset);
|
||||
if (gServerSettings.fixCollisionBugs) {
|
||||
data->x = cPos[0] + cNorm[0] * radius;
|
||||
data->z = cPos[2] + cNorm[2] * radius;
|
||||
x = data->x;
|
||||
z = data->z;
|
||||
data->normalAddition[0] += cNorm[0];
|
||||
data->normalAddition[2] += cNorm[2];
|
||||
data->normalCount++;
|
||||
} else {
|
||||
data->x += surf->normal.x * (radius - offset);
|
||||
data->z += surf->normal.z * (radius - offset);
|
||||
}
|
||||
|
||||
//! (Unreferenced Walls) Since this only returns the first four walls,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ struct WallCollisionData
|
|||
/*0x14*/ s16 unused;
|
||||
/*0x16*/ s16 numWalls;
|
||||
/*0x18*/ struct Surface *walls[4];
|
||||
/*????*/ Vec3f normalAddition;
|
||||
/*????*/ u8 normalCount;
|
||||
};
|
||||
|
||||
struct FloorGeometry
|
||||
|
|
@ -34,6 +36,9 @@ struct FloorGeometry
|
|||
f32 originOffset;
|
||||
};
|
||||
|
||||
extern Vec3f gFindWallDirection;
|
||||
extern u8 gFindWallDirectionActive;
|
||||
|
||||
s32 f32_find_wall_collision(f32 *xPtr, f32 *yPtr, f32 *zPtr, f32 offsetY, f32 radius);
|
||||
s32 find_wall_collisions(struct WallCollisionData *colData);
|
||||
f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "debug.h"
|
||||
#include "engine/behavior_script.h"
|
||||
#include "engine/surface_collision.h"
|
||||
#include "game/level_update.h"
|
||||
#include "game_init.h"
|
||||
#include "main.h"
|
||||
#include "object_constants.h"
|
||||
|
|
@ -568,3 +569,15 @@ void debug_enemy_unknown(s16 *enemyArr) {
|
|||
enemyArr[6] = gDebugInfo[DEBUG_PAGE_ENEMYINFO][3];
|
||||
enemyArr[7] = gDebugInfo[DEBUG_PAGE_ENEMYINFO][4];
|
||||
}
|
||||
|
||||
void debug_position(f32 x, f32 y, f32 z, bool red) {
|
||||
struct Object* player = gMarioStates[0].marioObj;
|
||||
if (player == NULL) { return; }
|
||||
struct Object* obj = spawn_object(player, red ? MODEL_RED_COIN : MODEL_YELLOW_COIN, bhvSparkle);
|
||||
if (obj != NULL) {
|
||||
obj_scale(obj, 0.25f);
|
||||
obj->oPosX = x;
|
||||
obj->oPosY = y;
|
||||
obj->oPosZ = z;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,4 +25,6 @@ void try_print_debug_mario_object_info(void);
|
|||
void try_do_mario_debug_object_spawn(void);
|
||||
void try_print_debug_mario_level_info(void);
|
||||
|
||||
void debug_position(f32 x, f32 y, f32 z, bool red);
|
||||
|
||||
#endif // DEBUG_H
|
||||
|
|
|
|||
|
|
@ -623,9 +623,9 @@ f32 vec3f_find_ceil(Vec3f pos, f32 height, struct Surface **ceil) {
|
|||
// Prevent exposed ceilings
|
||||
f32 vec3f_mario_ceil(Vec3f pos, f32 height, struct Surface **ceil) {
|
||||
if (gServerSettings.fixCollisionBugs) {
|
||||
height = MAX(height, pos[1]);
|
||||
height = MAX(height + 80.0f, pos[1] - 2);
|
||||
}
|
||||
return vec3f_find_ceil(pos, height, ceil);
|
||||
return find_ceil(pos[0], height, pos[2], ceil);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2273,17 +2273,15 @@ void mario_update_wall(struct MarioState* m, struct WallCollisionData* wcd) {
|
|||
? wcd->walls[wcd->numWalls - 1]
|
||||
: NULL;
|
||||
|
||||
vec3f_set(m->wallNormal, 0, 0, 0);
|
||||
for (u8 i = 0; i < wcd->numWalls; i++) {
|
||||
if (!gServerSettings.fixCollisionBugs) {
|
||||
i = (wcd->numWalls - 1);
|
||||
}
|
||||
struct Surface* wall = wcd->walls[i];
|
||||
Vec3f normal = { wall->normal.x, wall->normal.y, wall->normal.z };
|
||||
vec3f_add(m->wallNormal, normal);
|
||||
}
|
||||
|
||||
if (m->wall) {
|
||||
vec3f_normalize(m->wallNormal);
|
||||
if (gServerSettings.fixCollisionBugs && wcd->normalCount > 0) {
|
||||
vec3f_set(m->wallNormal,
|
||||
wcd->normalAddition[0] / wcd->normalCount,
|
||||
wcd->normalAddition[1] / wcd->normalCount,
|
||||
wcd->normalAddition[2] / wcd->normalCount);
|
||||
} else if (m->wall) {
|
||||
vec3f_set(m->wallNormal,
|
||||
m->wall->normal.x,
|
||||
m->wall->normal.y,
|
||||
m->wall->normal.z);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -334,11 +334,24 @@ s32 perform_ground_step(struct MarioState *m) {
|
|||
smlua_call_event_hooks_mario_param(HOOK_BEFORE_PHYS_STEP, m);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
intendedPos[0] = m->pos[0] + m->floor->normal.y * (m->vel[0] / 4.0f);
|
||||
intendedPos[2] = m->pos[2] + m->floor->normal.y * (m->vel[2] / 4.0f);
|
||||
intendedPos[1] = m->pos[1];
|
||||
Vec3f step = {
|
||||
m->floor->normal.y * (m->vel[0] / 4.0f),
|
||||
0,
|
||||
m->floor->normal.y * (m->vel[2] / 4.0f),
|
||||
};
|
||||
|
||||
intendedPos[0] = m->pos[0] + step[0];
|
||||
intendedPos[1] = m->pos[1];
|
||||
intendedPos[2] = m->pos[2] + step[2];
|
||||
|
||||
vec3f_normalize(step);
|
||||
|
||||
vec3f_copy(gFindWallDirection, step);
|
||||
|
||||
gFindWallDirectionActive = true;
|
||||
stepResult = perform_ground_quarter_step(m, intendedPos);
|
||||
gFindWallDirectionActive = false;
|
||||
|
||||
if (stepResult == GROUND_STEP_LEFT_GROUND || stepResult == GROUND_STEP_HIT_WALL_STOP_QSTEPS) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -665,11 +678,22 @@ s32 perform_air_step(struct MarioState *m, u32 stepArg) {
|
|||
m->wall = NULL;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
intendedPos[0] = m->pos[0] + m->vel[0] / 4.0f;
|
||||
intendedPos[1] = m->pos[1] + m->vel[1] / 4.0f;
|
||||
intendedPos[2] = m->pos[2] + m->vel[2] / 4.0f;
|
||||
Vec3f step = {
|
||||
m->vel[0] / 4.0f,
|
||||
m->vel[1] / 4.0f,
|
||||
m->vel[2] / 4.0f,
|
||||
};
|
||||
|
||||
intendedPos[0] = m->pos[0] + step[0];
|
||||
intendedPos[1] = m->pos[1] + step[1];
|
||||
intendedPos[2] = m->pos[2] + step[2];
|
||||
|
||||
vec3f_normalize(step);
|
||||
vec3f_copy(gFindWallDirection, step);
|
||||
|
||||
gFindWallDirectionActive = true;
|
||||
quarterStepResult = perform_air_quarter_step(m, intendedPos, stepArg);
|
||||
gFindWallDirectionActive = false;
|
||||
|
||||
//! On one qf, hit OOB/ceil/wall to store the 2 return value, and continue
|
||||
// getting 0s until your last qf. Graze a wall on your last qf, and it will
|
||||
|
|
|
|||
|
|
@ -170,9 +170,15 @@ s8 obj_find_wall(f32 objNewX, f32 objY, f32 objNewZ, f32 objVelX, f32 objVelZ) {
|
|||
o->oPosY = hitbox.y;
|
||||
o->oPosZ = hitbox.z;
|
||||
|
||||
wall_nX = hitbox.walls[0]->normal.x;
|
||||
wall_nY = hitbox.walls[0]->normal.y;
|
||||
wall_nZ = hitbox.walls[0]->normal.z;
|
||||
if (gServerSettings.fixCollisionBugs && hitbox.normalCount > 0) {
|
||||
wall_nX = hitbox.normalAddition[0] / hitbox.normalCount;
|
||||
wall_nY = hitbox.normalAddition[1] / hitbox.normalCount;
|
||||
wall_nZ = hitbox.normalAddition[2] / hitbox.normalCount;
|
||||
} else {
|
||||
wall_nX = hitbox.walls[0]->normal.x;
|
||||
wall_nY = hitbox.walls[0]->normal.y;
|
||||
wall_nZ = hitbox.walls[0]->normal.z;
|
||||
}
|
||||
|
||||
objVelXCopy = objVelX;
|
||||
objVelZCopy = objVelZ;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue