diff --git a/game_patch/CMakeLists.txt b/game_patch/CMakeLists.txt index bebf86ba..c4e5568f 100644 --- a/game_patch/CMakeLists.txt +++ b/game_patch/CMakeLists.txt @@ -226,6 +226,7 @@ set(SRCS object/monitor.cpp object/particle.cpp object/obj_light.cpp + object/mesh_collision.cpp object/alpine_mesh.cpp object/alpine_corona.cpp object/alpine_corona.h diff --git a/game_patch/object/mesh_collision.cpp b/game_patch/object/mesh_collision.cpp new file mode 100644 index 00000000..bd4d3819 --- /dev/null +++ b/game_patch/object/mesh_collision.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include "../rf/vmesh.h" +#include "../rf/physics.h" +#include "../rf/object.h" + +// Mesh collision in the RF engine uses per-triangle intersection tests (vmesh_collide) +// rather than BSP-based solid collision (GSolid::collide). This makes it fundamentally +// less robust: triangle soup has gaps at edges/seams, no concept of a closed solid +// volume, and limited multi-hit resolution. These patches improve mesh collision +// reliability to reduce "falling through" and "getting stuck" on mesh surfaces. + +// Small margin added to the collision sphere radius when testing physics collision +// against mesh triangles. This closes gaps at triangle edges/seams by making the +// effective collision volume slightly larger, preventing the sphere from slipping +// between adjacent triangles. The margin is small enough to not noticeably affect +// gameplay feel. +static constexpr float mesh_radius_margin = 0.02f; + +// Small amount subtracted from hit_time after mesh collision detection. This ensures +// the object stops slightly before the mesh surface, providing clearance that prevents +// the object from getting embedded in the mesh on the next frame. This reduces the +// oscillation/"stuck" behavior that occurs when conflicting normals from adjacent +// triangles push the player back and forth. +static constexpr float mesh_hit_time_safety = 0.005f; + +// Hook vmesh_collide (0x005031F0) at its call sites in the physics collision paths. +// This inflates the collision radius for the duration of the mesh test only, making +// the swept sphere slightly larger and closing edge gaps between mesh triangles. +// +// Call sites: +// 0x00499BD2 - collide_spheres_mesh: ground contact and stick-to-ground tests +// 0x0049B332 - collide_object_object_mesh: object-pair mesh collision +// +// Other vmesh_collide callers (weapon raycasts, line-of-sight, glare occlusion) +// are intentionally not hooked as they need precise triangle-level accuracy. +CallHook +mesh_physics_collide_radius_hook{ + { + 0x00499BD2, // in collide_spheres_mesh + 0x0049B332, // in collide_object_object_mesh + }, + [](rf::VMesh* vmesh, rf::VMeshCollisionInput* in, rf::VMeshCollisionOutput* out, bool clear) { + float original_radius = in->radius; + in->radius += mesh_radius_margin; + bool result = mesh_physics_collide_radius_hook.call_target(vmesh, in, out, clear); + in->radius = original_radius; + return result; + }, +}; + +// Hook collide_object_object_mesh (0x0049AFE0) to apply a safety margin to the +// collision hit_time. When a mesh collision is detected, the object is stopped at +// (hit_time - margin) instead of exactly at hit_time. This small clearance prevents +// the object from being positioned right on the mesh surface where floating-point +// imprecision and conflicting triangle normals can cause it to get stuck or embedded. +FunHook collide_object_object_mesh_hook{ + 0x0049AFE0, + [](rf::Object* sphere_obj, rf::Object* mesh_obj) { + bool result = collide_object_object_mesh_hook.call_target(sphere_obj, mesh_obj); + if (result) { + float& hit_time = sphere_obj->p_data.collide_out.hit_time; + if (hit_time > mesh_hit_time_safety) { + hit_time -= mesh_hit_time_safety; + } + } + return result; + }, +}; + +void mesh_collision_apply_patches() +{ + mesh_physics_collide_radius_hook.install(); + collide_object_object_mesh_hook.install(); +} diff --git a/game_patch/object/object.cpp b/game_patch/object/object.cpp index f44faeca..795835f5 100644 --- a/game_patch/object/object.cpp +++ b/game_patch/object/object.cpp @@ -524,4 +524,5 @@ void object_do_patch() mover_do_patch(); particle_do_patch(); obj_light_apply_patch(); + mesh_collision_apply_patches(); } diff --git a/game_patch/object/object_private.h b/game_patch/object/object_private.h index badc9abd..a0a57f6e 100644 --- a/game_patch/object/object_private.h +++ b/game_patch/object/object_private.h @@ -13,3 +13,4 @@ void entity_do_patch(); void item_do_patch(); void particle_do_patch(); void obj_light_apply_patch(); +void mesh_collision_apply_patches();