363 lines
11 KiB
C
363 lines
11 KiB
C
#include "../shim.h"
|
|
#include "surface_collision.h"
|
|
#include "../include/surface_terrains.h"
|
|
#include "../../load_surfaces.h"
|
|
|
|
/**
|
|
* Iterate through the list of ceilings and find the first ceiling over a given point.
|
|
*/
|
|
static struct SM64SurfaceCollisionData *find_ceil_from_list( s32 x, s32 y, s32 z, f32 *pheight) {
|
|
register struct SM64SurfaceCollisionData *surf;
|
|
register s32 x1, z1, x2, z2, x3, z3;
|
|
struct SM64SurfaceCollisionData *ceil = NULL;
|
|
|
|
ceil = NULL;
|
|
|
|
uint32_t groupCount = loaded_surface_iter_group_count();
|
|
for( int i = 0; i < groupCount; ++i ) {
|
|
uint32_t surfCount = loaded_surface_iter_group_size( i );
|
|
for( int j = 0; j < surfCount; ++j ) {
|
|
surf = loaded_surface_iter_get_at_index( i, j );
|
|
|
|
// libsm64: Weed out surfaces whose triangles are actually line segs. TODO do this at surface load time
|
|
if( !surf->isValid ) continue;
|
|
|
|
// Do the check normally done in add_surface_to_cell
|
|
if( surf->normal.y >= -0.01f ) continue;
|
|
|
|
x1 = surf->vertex1[0];
|
|
z1 = surf->vertex1[2];
|
|
z2 = surf->vertex2[2];
|
|
x2 = surf->vertex2[0];
|
|
|
|
// Checking if point is in bounds of the triangle laterally.
|
|
if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) > 0) {
|
|
continue;
|
|
}
|
|
|
|
// Slight optimization by checking these later.
|
|
x3 = surf->vertex3[0];
|
|
z3 = surf->vertex3[2];
|
|
if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) > 0) {
|
|
continue;
|
|
}
|
|
if ((z3 - z) * (x1 - x3) - (x3 - x) * (z1 - z3) > 0) {
|
|
continue;
|
|
}
|
|
|
|
{
|
|
f32 nx = surf->normal.x;
|
|
f32 ny = surf->normal.y;
|
|
f32 nz = surf->normal.z;
|
|
f32 oo = surf->originOffset;
|
|
f32 height;
|
|
|
|
// If a wall, ignore it. Likely a remnant, should never occur.
|
|
if (ny == 0.0f) {
|
|
continue;
|
|
}
|
|
|
|
// Find the ceil height at the specific point.
|
|
height = -(x * nx + nz * z + oo) / ny;
|
|
|
|
// Checks for ceiling interaction with a 78 unit buffer.
|
|
//! (Exposed Ceilings) Because any point above a ceiling counts
|
|
// as interacting with a ceiling, ceilings far below can cause
|
|
// "invisible walls" that are really just exposed ceilings.
|
|
if (y - (height - -78.0f) > 0.0f) {
|
|
continue;
|
|
}
|
|
|
|
if( height < *pheight )
|
|
{
|
|
*pheight = height;
|
|
ceil = surf;
|
|
}
|
|
}
|
|
}}
|
|
return ceil;
|
|
}
|
|
|
|
/**
|
|
* Iterate through the list of floors and find the first floor under a given point.
|
|
*/
|
|
static struct SM64SurfaceCollisionData *find_floor_from_list( s32 x, s32 y, s32 z, f32 *pheight) {
|
|
register struct SM64SurfaceCollisionData *surf;
|
|
register s32 x1, z1, x2, z2, x3, z3;
|
|
f32 nx, ny, nz;
|
|
f32 oo;
|
|
f32 height;
|
|
struct SM64SurfaceCollisionData *floor = NULL;
|
|
|
|
uint32_t groupCount = loaded_surface_iter_group_count();
|
|
for( int i = 0; i < groupCount; ++i ) {
|
|
uint32_t surfCount = loaded_surface_iter_group_size( i );
|
|
for( int j = 0; j < surfCount; ++j ) {
|
|
surf = loaded_surface_iter_get_at_index( i, j );
|
|
|
|
// libsm64: Weed out surfaces whose triangles are actually line segs. TODO do this at surface load time
|
|
if( !surf->isValid ) continue;
|
|
|
|
// Do the check normally done in add_surface_to_cell
|
|
if( surf->normal.y <= 0.01f ) continue;
|
|
|
|
x1 = surf->vertex1[0];
|
|
z1 = surf->vertex1[2];
|
|
x2 = surf->vertex2[0];
|
|
z2 = surf->vertex2[2];
|
|
|
|
// Check that the point is within the triangle bounds.
|
|
if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) < 0) {
|
|
continue;
|
|
}
|
|
|
|
// To slightly save on computation time, set this later.
|
|
x3 = surf->vertex3[0];
|
|
z3 = surf->vertex3[2];
|
|
|
|
if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) < 0) {
|
|
continue;
|
|
}
|
|
if ((z3 - z) * (x1 - x3) - (x3 - x) * (z1 - z3) < 0) {
|
|
continue;
|
|
}
|
|
|
|
nx = surf->normal.x;
|
|
ny = surf->normal.y;
|
|
nz = surf->normal.z;
|
|
oo = surf->originOffset;
|
|
|
|
// If a wall, ignore it. Likely a remnant, should never occur.
|
|
if (ny == 0.0f) {
|
|
continue;
|
|
}
|
|
|
|
// Find the height of the floor at a given location.
|
|
height = -(x * nx + nz * z + oo) / ny;
|
|
// Checks for floor interaction with a 78 unit buffer.
|
|
if (y - (height + -78.0f) < 0.0f) {
|
|
continue;
|
|
}
|
|
|
|
if( height > *pheight )
|
|
{
|
|
*pheight = height;
|
|
floor = surf;
|
|
}
|
|
}}
|
|
return floor;
|
|
}
|
|
|
|
static s32 find_wall_collisions_from_list( struct SM64WallCollisionData *data) {
|
|
register struct SM64SurfaceCollisionData *surf;
|
|
register f32 offset;
|
|
register f32 radius = data->radius;
|
|
register f32 x = data->x;
|
|
register f32 y = data->y + data->offsetY;
|
|
register f32 z = data->z;
|
|
register f32 px, pz;
|
|
register f32 w1, w2, w3;
|
|
register f32 y1, y2, y3;
|
|
s32 numCols = 0;
|
|
|
|
// Max collision radius = 200
|
|
if (radius > 200.0f) {
|
|
radius = 200.0f;
|
|
}
|
|
|
|
uint32_t groupCount = loaded_surface_iter_group_count();
|
|
for( int i = 0; i < groupCount; ++i ) {
|
|
uint32_t surfCount = loaded_surface_iter_group_size( i );
|
|
for( int j = 0; j < surfCount; ++j ) {
|
|
surf = loaded_surface_iter_get_at_index( i, j );
|
|
|
|
// libsm64: Weed out surfaces whose triangles are actually line segs. TODO do this at surface load time
|
|
if( !surf->isValid ) continue;
|
|
|
|
// Do the check normally done in add_surface_to_cell
|
|
if( surf->normal.y < -0.01f || surf->normal.y > 0.01f ) continue;
|
|
|
|
if( surf->normal.x < -0.707f || surf->normal.x > 0.707f ) {
|
|
surf->flags |= SURFACE_FLAG_X_PROJECTION;
|
|
}
|
|
|
|
// Exclude a large number of walls immediately to optimize.
|
|
if (y < surf->lowerY || y > surf->upperY) {
|
|
continue;
|
|
}
|
|
|
|
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 {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
//! (Wall Overlaps) Because this doesn't update the x and z local variables,
|
|
// multiple walls can push mario more than is required.
|
|
data->x += surf->normal.x * (radius - offset);
|
|
data->z += surf->normal.z * (radius - offset);
|
|
|
|
//! (Unreferenced Walls) Since this only returns the first four walls,
|
|
// this can lead to wall interaction being missed. Typically unreferenced walls
|
|
// come from only using one wall, however.
|
|
if (data->numWalls < 4) {
|
|
data->walls[data->numWalls++] = surf;
|
|
}
|
|
|
|
numCols++;
|
|
}}
|
|
|
|
return numCols;
|
|
}
|
|
|
|
s32 f32_find_wall_collision(f32 *xPtr, f32 *yPtr, f32 *zPtr, f32 offsetY, f32 radius)
|
|
{
|
|
struct SM64WallCollisionData collision;
|
|
s32 numCollisions = 0;
|
|
|
|
collision.offsetY = offsetY;
|
|
collision.radius = radius;
|
|
|
|
collision.x = *xPtr;
|
|
collision.y = *yPtr;
|
|
collision.z = *zPtr;
|
|
|
|
collision.numWalls = 0;
|
|
|
|
numCollisions = find_wall_collisions(&collision);
|
|
|
|
*xPtr = collision.x;
|
|
*yPtr = collision.y;
|
|
*zPtr = collision.z;
|
|
|
|
return numCollisions;
|
|
}
|
|
|
|
s32 find_wall_collisions(struct SM64WallCollisionData *colData)
|
|
{
|
|
s32 numCollisions = 0;
|
|
colData->numWalls = 0;
|
|
|
|
// libsm64: Don't care about level boundaries with 32-bit ints for vertex positions
|
|
// s16 x = colData->x;
|
|
// s16 z = colData->z;
|
|
// if (x <= -LEVEL_BOUNDARY_MAX || x >= LEVEL_BOUNDARY_MAX) {
|
|
// return numCollisions;
|
|
// }
|
|
// if (z <= -LEVEL_BOUNDARY_MAX || z >= LEVEL_BOUNDARY_MAX) {
|
|
// return numCollisions;
|
|
// }
|
|
|
|
numCollisions += find_wall_collisions_from_list(colData);
|
|
return numCollisions;
|
|
}
|
|
|
|
f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct SM64SurfaceCollisionData **pceil)
|
|
{
|
|
f32 height = CELL_HEIGHT_LIMIT;
|
|
*pceil = find_ceil_from_list( posX, posY, posZ, &height );
|
|
return height;
|
|
}
|
|
|
|
struct SM64FloorCollisionData sFloorGeo;
|
|
|
|
f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct SM64FloorCollisionData **floorGeo)
|
|
{
|
|
struct SM64SurfaceCollisionData *floor;
|
|
f32 floorHeight = find_floor(xPos, yPos, zPos, &floor);
|
|
|
|
*floorGeo = NULL;
|
|
|
|
if (floor != NULL) {
|
|
sFloorGeo.normalX = floor->normal.x;
|
|
sFloorGeo.normalY = floor->normal.y;
|
|
sFloorGeo.normalZ = floor->normal.z;
|
|
sFloorGeo.originOffset = floor->originOffset;
|
|
|
|
*floorGeo = &sFloorGeo;
|
|
}
|
|
return floorHeight;
|
|
}
|
|
|
|
f32 find_floor_height(f32 x, f32 y, f32 z)
|
|
{
|
|
f32 height = FLOOR_LOWER_LIMIT;
|
|
find_floor_from_list( x, y, z, &height );
|
|
return height;
|
|
}
|
|
|
|
f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct SM64SurfaceCollisionData **pfloor)
|
|
{
|
|
f32 height = FLOOR_LOWER_LIMIT;
|
|
*pfloor = find_floor_from_list( xPos, yPos, zPos, &height );
|
|
return height;
|
|
}
|
|
|
|
f32 find_water_level(f32 x, f32 z)
|
|
{
|
|
return -10000.0f;
|
|
}
|
|
|
|
f32 find_poison_gas_level(f32 x, f32 z)
|
|
{
|
|
return -10000.0f;
|
|
}
|