1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2024-12-02 07:46:01 +00:00
oot/src/code/z_bgcheck.c
engineer124 0ce0313342
Minor Misc Cleanup 3 (#1502)
* more misc cleanup

* remove z64 conflicts

* undo ISVDbg
2023-07-05 18:36:19 -04:00

4568 lines
166 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "global.h"
#include "terminal.h"
u16 DynaSSNodeList_GetNextNodeIdx(DynaSSNodeList* nodeList);
void BgCheck_GetStaticLookupIndicesFromPos(CollisionContext* colCtx, Vec3f* pos, Vec3i* sector);
s32 BgCheck_PosInStaticBoundingBox(CollisionContext* colCtx, Vec3f* pos);
s32 BgCheck_CheckLineImpl(CollisionContext* colCtx, u16 xpFlags1, u16 xpFlags2, Vec3f* posA, Vec3f* posB,
Vec3f* posResult, CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 chkDist,
u32 bccFlags);
void SSNodeList_Initialize(SSNodeList* this);
void SSNodeList_Alloc(PlayState* play, SSNodeList* this, s32 tblMax, s32 numPolys);
u16 SSNodeList_GetNextNodeIdx(SSNodeList* this);
void DynaPoly_Init(PlayState* play, DynaCollisionContext* dyna);
void DynaPoly_Alloc(PlayState* play, DynaCollisionContext* dyna);
f32 BgCheck_RaycastDownDyna(DynaRaycastDown* dynaRaycastDown);
s32 BgCheck_SphVsDynaWall(CollisionContext* colCtx, u16 xpFlags, f32* outX, f32* outZ, Vec3f* pos, f32 radius,
CollisionPoly** outPoly, s32* outBgId, Actor* actor);
s32 BgCheck_CheckDynaCeiling(CollisionContext* colCtx, u16 xpFlags, f32* outY, Vec3f* pos, f32 chkDist,
CollisionPoly** outPoly, s32* outBgId, Actor* actor);
s32 BgCheck_CheckLineAgainstDyna(CollisionContext* colCtx, u16 xpFlags, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
CollisionPoly** outPoly, f32* distSq, s32* outBgId, Actor* actor, f32 chkDist,
s32 bccFlags);
s32 BgCheck_SphVsFirstDynaPoly(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, s32* outBgId,
Vec3f* center, f32 radius, Actor* actor, u16 bciFlags);
void BgCheck_ResetPolyCheckTbl(SSNodeList* nodeList, s32 numPolys);
#define SS_NULL 0xFFFF
// bccFlags
#define BGCHECK_CHECK_WALL (1 << 0)
#define BGCHECK_CHECK_FLOOR (1 << 1)
#define BGCHECK_CHECK_CEILING (1 << 2)
#define BGCHECK_CHECK_ONE_FACE (1 << 3)
#define BGCHECK_CHECK_DYNA (1 << 4)
#define BGCHECK_CHECK_ALL \
(BGCHECK_CHECK_WALL | BGCHECK_CHECK_FLOOR | BGCHECK_CHECK_CEILING | BGCHECK_CHECK_ONE_FACE | BGCHECK_CHECK_DYNA)
// bciFlags
#define BGCHECK_IGNORE_NONE 0
#define BGCHECK_IGNORE_CEILING (1 << 0)
#define BGCHECK_IGNORE_WALL (1 << 1)
#define BGCHECK_IGNORE_FLOOR (1 << 2)
// poly exclusion flags (xpFlags)
#define COLPOLY_IGNORE_NONE 0
#define COLPOLY_IGNORE_CAMERA (1 << 0)
#define COLPOLY_IGNORE_ENTITY (1 << 1)
#define COLPOLY_IGNORE_PROJECTILES (1 << 2)
// raycast down flags (downChkFlags)
#define BGCHECK_RAYCAST_DOWN_CHECK_CEILINGS (1 << 0)
#define BGCHECK_RAYCAST_DOWN_CHECK_WALLS (1 << 1)
#define BGCHECK_RAYCAST_DOWN_CHECK_FLOORS (1 << 2)
#define BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE (1 << 3) // stops checking dyna walls on finding first candidate result
#define BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY (1 << 4) // skips walls and ceilings with normal.y < 0
// raycast down groundChk flag. When enabled, search range is limited to floors and walls with a normal.y >= 0
#define BGCHECK_GROUND_CHECK_ON (1 << 0)
s32 D_80119D90[WALL_TYPE_MAX] = {
0, // WALL_TYPE_0
WALL_FLAG_0, // WALL_TYPE_1
WALL_FLAG_0 | WALL_FLAG_1, // WALL_TYPE_2
WALL_FLAG_0 | WALL_FLAG_2, // WALL_TYPE_3
WALL_FLAG_3, // WALL_TYPE_4
WALL_FLAG_CRAWLSPACE_1, // WALL_TYPE_5
WALL_FLAG_CRAWLSPACE_2, // WALL_TYPE_6
WALL_FLAG_6, // WALL_TYPE_7
};
u16 sSurfaceMaterialToSfxOffset[SURFACE_MATERIAL_MAX] = {
SURFACE_SFX_OFFSET_DIRT, // SURFACE_MATERIAL_DIRT
SURFACE_SFX_OFFSET_SAND, // SURFACE_MATERIAL_SAND
SURFACE_SFX_OFFSET_STONE, // SURFACE_MATERIAL_STONE
SURFACE_SFX_OFFSET_JABU, // SURFACE_MATERIAL_JABU
SURFACE_SFX_OFFSET_WATER_SHALLOW, // SURFACE_MATERIAL_WATER_SHALLOW
SURFACE_SFX_OFFSET_WATER_DEEP, // SURFACE_MATERIAL_WATER_DEEP
SURFACE_SFX_OFFSET_TALL_GRASS, // SURFACE_MATERIAL_TALL_GRASS
SURFACE_SFX_OFFSET_LAVA, // SURFACE_MATERIAL_LAVA
SURFACE_SFX_OFFSET_GRASS, // SURFACE_MATERIAL_GRASS
SURFACE_SFX_OFFSET_BRIDGE, // SURFACE_MATERIAL_BRIDGE
SURFACE_SFX_OFFSET_WOOD, // SURFACE_MATERIAL_WOOD
SURFACE_SFX_OFFSET_DIRT, // SURFACE_MATERIAL_DIRT_SOFT
SURFACE_SFX_OFFSET_ICE, // SURFACE_MATERIAL_ICE
SURFACE_SFX_OFFSET_CARPET, // SURFACE_MATERIAL_CARPET
};
/**
* original name: T_BGCheck_PosErrorCheck
*/
s32 BgCheck_PosErrorCheck(Vec3f* pos, char* file, s32 line) {
if (pos->x >= BGCHECK_XYZ_ABSMAX || pos->x <= -BGCHECK_XYZ_ABSMAX || pos->y >= BGCHECK_XYZ_ABSMAX ||
pos->y <= -BGCHECK_XYZ_ABSMAX || pos->z >= BGCHECK_XYZ_ABSMAX || pos->z <= -BGCHECK_XYZ_ABSMAX) {
osSyncPrintf(VT_FGCOL(RED));
// "Position is invalid."
osSyncPrintf("T_BGCheck_PosErrorCheck():位置が妥当ではありません。pos (%f,%f,%f) file:%s line:%d\n", pos->x,
pos->y, pos->z, file, line);
osSyncPrintf(VT_RST);
return true;
}
return false;
}
/**
* Set SSNode
*/
void SSNode_SetValue(SSNode* node, s16* polyId, u16 next) {
node->polyId = *polyId;
node->next = next;
}
/**
* Set SSList to SS_NULL
*/
void SSList_SetNull(SSList* ssList) {
ssList->head = SS_NULL;
}
/**
* Insert `polyId` at the start of the static `ssList` list
*/
void SSNodeList_SetSSListHead(SSNodeList* nodeList, SSList* ssList, s16* polyId) {
u16 newNodeId = SSNodeList_GetNextNodeIdx(nodeList);
SSNode_SetValue(&nodeList->tbl[newNodeId], polyId, ssList->head);
ssList->head = newNodeId;
}
/**
* Insert `polyId` at the start of the dyna `ssList` list
*/
void DynaSSNodeList_SetSSListHead(DynaSSNodeList* nodeList, SSList* ssList, s16* polyId) {
u16 newNodeId = DynaSSNodeList_GetNextNodeIdx(nodeList);
ASSERT(newNodeId != SS_NULL, "new_node != SS_NULL", "../z_bgcheck.c", 1776);
SSNode_SetValue(&nodeList->tbl[newNodeId], polyId, ssList->head);
ssList->head = newNodeId;
}
/**
* Initialize DynaSSNodeList
*/
void DynaSSNodeList_Initialize(PlayState* play, DynaSSNodeList* nodeList) {
nodeList->tbl = NULL;
nodeList->count = 0;
}
/**
* Initialize DynaSSNodeList tbl
*/
void DynaSSNodeList_Alloc(PlayState* play, DynaSSNodeList* nodeList, s32 max) {
nodeList->tbl = THA_AllocTailAlign(&play->state.tha, max * sizeof(SSNode), ALIGNOF_MASK(SSNode));
ASSERT(nodeList->tbl != NULL, "psst->tbl != NULL", "../z_bgcheck.c", 1811);
nodeList->max = max;
nodeList->count = 0;
}
/**
* Reset DynaSSNodeList count
*/
void DynaSSNodeList_ResetCount(DynaSSNodeList* nodeList) {
nodeList->count = 0;
}
/**
* Get next available node index in DynaSSNodeList
* returns SS_NULL if list is full
*/
u16 DynaSSNodeList_GetNextNodeIdx(DynaSSNodeList* nodeList) {
u16 idx = nodeList->count++;
if (nodeList->max <= idx) {
return SS_NULL;
}
return idx;
}
/**
* original name: T_BGCheck_Vec3sToVec3f
*/
void BgCheck_Vec3sToVec3f(Vec3s* src, Vec3f* dst) {
dst->x = src->x;
dst->y = src->y;
dst->z = src->z;
}
/**
* original name: T_BGCheck_Vec3fToVec3s
*/
void BgCheck_Vec3fToVec3s(Vec3s* dst, Vec3f* src) {
dst->x = src->x;
dst->y = src->y;
dst->z = src->z;
}
/**
* Get CollisionPoly's lowest y point
*/
s16 CollisionPoly_GetMinY(CollisionPoly* poly, Vec3s* vtxList) {
s32 a;
s32 b;
s32 c;
s16 min;
//! @bug Due to rounding errors, some polys with a slight slope have a y normal of 1.0f/-1.0f. As such, this
//! optimization returns the wrong minimum y for a subset of these polys.
if (poly->normal.y == COLPOLY_SNORMAL(1.0f) || poly->normal.y == COLPOLY_SNORMAL(-1.0f)) {
return vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)].y;
}
a = COLPOLY_VTX_INDEX(poly->flags_vIA);
b = COLPOLY_VTX_INDEX(poly->flags_vIB);
c = poly->vIC;
min = vtxList[a].y;
if (min > vtxList[b].y) {
min = vtxList[b].y;
}
if (min < vtxList[c].y) {
return min;
}
return vtxList[c].y;
}
/**
* CollisionPoly get unit normal
*/
void CollisionPoly_GetNormalF(CollisionPoly* poly, f32* nx, f32* ny, f32* nz) {
*nx = COLPOLY_GET_NORMAL(poly->normal.x);
*ny = COLPOLY_GET_NORMAL(poly->normal.y);
*nz = COLPOLY_GET_NORMAL(poly->normal.z);
}
/**
* Compute transform matrix mapping +y (up) to the collision poly's normal
*/
void func_80038A28(CollisionPoly* poly, f32 tx, f32 ty, f32 tz, MtxF* dest) {
f32 nx;
f32 ny;
f32 nz;
s32 pad;
f32 xx;
f32 zz;
f32 yz;
f32 xxInv;
f32 zzInv;
if (poly == NULL) {
return;
}
CollisionPoly_GetNormalF(poly, &nx, &ny, &nz);
xx = sqrtf(1.0f - SQ(nx));
if (!IS_ZERO(xx)) {
xxInv = 1.0f / xx;
zz = ny * xxInv;
yz = -(nz * xxInv);
} else {
zz = sqrtf(1.0f - SQ(ny));
if (1) {}
if (!IS_ZERO(zz)) {
zzInv = 1.0f / zz;
yz = nx * zzInv;
xx = -(nz * zzInv);
} else {
yz = 0.0f;
xx = 0.0f;
}
}
dest->xx = xx;
dest->yx = -nx * zz;
dest->zx = nx * yz;
dest->xy = nx;
dest->yy = ny;
dest->zy = nz;
dest->yz = yz;
dest->zz = zz;
dest->wx = 0.0f;
dest->wy = 0.0f;
dest->xz = 0.0f;
dest->wz = 0.0f;
dest->xw = tx;
dest->yw = ty;
dest->zw = tz;
dest->ww = 1.0f;
}
/**
* Calculate point distance from plane along normal
*/
f32 CollisionPoly_GetPointDistanceFromPlane(CollisionPoly* poly, Vec3f* point) {
return (poly->normal.x * point->x + poly->normal.y * point->y + poly->normal.z * point->z) * COLPOLY_NORMAL_FRAC +
poly->dist;
}
/**
* Get Poly Vertices
*/
void CollisionPoly_GetVertices(CollisionPoly* poly, Vec3s* vtxList, Vec3f* dest) {
BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)], &dest[0]);
BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)], &dest[1]);
BgCheck_Vec3sToVec3f(&vtxList[poly->vIC], &dest[2]);
}
/**
* Get vertices by bgId
* original name: T_Polygon_GetVertex_bg_ai
*/
void CollisionPoly_GetVerticesByBgId(CollisionPoly* poly, s32 bgId, CollisionContext* colCtx, Vec3f* dest) {
Vec3s* vtxList;
if (poly == NULL || bgId > BG_ACTOR_MAX || dest == NULL) {
osSyncPrintf(VT_COL(RED, WHITE));
// "Argument not appropriate. Processing terminated."
osSyncPrintf("T_Polygon_GetVertex_bg_ai(): Error %d %d %d 引数が適切ではありません。処理を終了します。\n",
poly == NULL, bgId > BG_ACTOR_MAX, dest == NULL);
osSyncPrintf(VT_RST);
if (dest != NULL) {
//! @bug: dest[2] x and y are not set to 0
dest[0].x = dest[0].y = dest[0].z = dest[1].x = dest[1].y = dest[1].z = dest[2].z = 0.0f;
}
} else {
if (bgId == BGCHECK_SCENE) {
vtxList = colCtx->colHeader->vtxList;
} else {
vtxList = colCtx->dyna.vtxList;
}
CollisionPoly_GetVertices(poly, vtxList, dest);
}
}
/**
* Checks if point (`x`,`z`) is within `chkDist` of `poly`, computing `yIntersect` if true
* Determinant max 300.0f
*/
s32 CollisionPoly_CheckYIntersectApprox1(CollisionPoly* poly, Vec3s* vtxList, f32 x, f32 z, f32* yIntersect,
f32 chkDist) {
static Vec3f polyVerts[3];
f32 nx;
f32 ny;
f32 nz;
Vec3s* vA;
Vec3s* vB;
Vec3s* vC;
vA = &vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)];
Math_Vec3s_ToVec3f(&polyVerts[0], vA);
vB = &vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)];
Math_Vec3s_ToVec3f(&polyVerts[1], vB);
vC = &vtxList[poly->vIC];
Math_Vec3s_ToVec3f(&polyVerts[2], vC);
nx = COLPOLY_GET_NORMAL(poly->normal.x);
ny = COLPOLY_GET_NORMAL(poly->normal.y);
nz = COLPOLY_GET_NORMAL(poly->normal.z);
return Math3D_TriChkPointParaYIntersectDist(&polyVerts[0], &polyVerts[1], &polyVerts[2], nx, ny, nz, poly->dist, z,
x, yIntersect, chkDist);
}
/**
* Checks if point (`x`,`z`) is within `chkDist` of `poly`, computing `yIntersect` if true
* Determinant max 0.0f (checks if on or within poly)
*/
s32 CollisionPoly_CheckYIntersect(CollisionPoly* poly, Vec3s* vtxList, f32 x, f32 z, f32* yIntersect, f32 chkDist) {
static Vec3f polyVerts[3];
f32 nx;
f32 ny;
f32 nz;
CollisionPoly_GetVertices(poly, vtxList, polyVerts);
CollisionPoly_GetNormalF(poly, &nx, &ny, &nz);
return Math3D_TriChkPointParaYIntersectInsideTri(&polyVerts[0], &polyVerts[1], &polyVerts[2], nx, ny, nz,
poly->dist, z, x, yIntersect, chkDist);
}
/**
* Checks if point (`x`,`z`) is within 1.0f of `poly`, computing `yIntersect` if true
* Determinant max 300.0f
*/
s32 CollisionPoly_CheckYIntersectApprox2(CollisionPoly* poly, Vec3s* vtxList, f32 x, f32 z, f32* yIntersect) {
return CollisionPoly_CheckYIntersectApprox1(poly, vtxList, x, z, yIntersect, 1.0f);
}
/**
* Checks if point (`y`,`z`) is within 1.0f of `poly`, computing `xIntersect` if true
* Determinant max 300.0f
*/
s32 CollisionPoly_CheckXIntersectApprox(CollisionPoly* poly, Vec3s* vtxList, f32 y, f32 z, f32* xIntersect) {
static Vec3f polyVerts[3];
f32 nx;
f32 ny;
f32 nz;
CollisionPoly_GetVertices(poly, vtxList, polyVerts);
CollisionPoly_GetNormalF(poly, &nx, &ny, &nz);
return Math3D_TriChkPointParaXIntersect(&polyVerts[0], &polyVerts[1], &polyVerts[2], nx, ny, nz, poly->dist, y, z,
xIntersect);
}
/**
* Checks if point (`x`,`y`) is within 1.0f of `poly`, computing `zIntersect` if true
* Determinant max 300.0f
*/
s32 CollisionPoly_CheckZIntersectApprox(CollisionPoly* poly, Vec3s* vtxList, f32 x, f32 y, f32* zIntersect) {
static Vec3f polyVerts[3];
f32 nx;
f32 ny;
f32 nz;
CollisionPoly_GetVertices(poly, vtxList, polyVerts);
CollisionPoly_GetNormalF(poly, &nx, &ny, &nz);
return Math3D_TriChkPointParaZIntersect(&polyVerts[0], &polyVerts[1], &polyVerts[2], nx, ny, nz, poly->dist, x, y,
zIntersect);
}
/**
* Test if travelling from `posA` to `posB` intersects `poly`
* returns true if an intersection occurs, else false
* returns `planeIntersect`, which is the point at which the line from `posA` to `posB` crosses `poly`'s plane
* if `chkOneFace` is true, return false (no intersection) when going through the poly from A to B is done in the
* normal's direction
*/
s32 CollisionPoly_LineVsPoly(CollisionPoly* poly, Vec3s* vtxList, Vec3f* posA, Vec3f* posB, Vec3f* planeIntersect,
s32 chkOneFace, f32 chkDist) {
static Vec3f polyVerts[3];
static Plane plane;
f32 planeDistA;
f32 planeDistB;
f32 planeDistDelta;
plane.originDist = poly->dist;
planeDistA =
(poly->normal.x * posA->x + poly->normal.y * posA->y + poly->normal.z * posA->z) * COLPOLY_NORMAL_FRAC +
plane.originDist;
planeDistB =
(poly->normal.x * posB->x + poly->normal.y * posB->y + poly->normal.z * posB->z) * COLPOLY_NORMAL_FRAC +
plane.originDist;
planeDistDelta = planeDistA - planeDistB;
if ((planeDistA >= 0.0f && planeDistB >= 0.0f) || (planeDistA < 0.0f && planeDistB < 0.0f) ||
(chkOneFace && planeDistA < 0.0f && planeDistB > 0.0f) || IS_ZERO(planeDistDelta)) {
return false;
}
CollisionPoly_GetNormalF(poly, &plane.normal.x, &plane.normal.y, &plane.normal.z);
CollisionPoly_GetVertices(poly, vtxList, polyVerts);
Math3D_LineSplitRatio(posA, posB, planeDistA / planeDistDelta, planeIntersect);
if ((fabsf(plane.normal.x) > 0.5f &&
Math3D_TriChkPointParaXDist(&polyVerts[0], &polyVerts[1], &polyVerts[2], &plane, planeIntersect->y,
planeIntersect->z, chkDist)) ||
(fabsf(plane.normal.y) > 0.5f &&
Math3D_TriChkPointParaYDist(&polyVerts[0], &polyVerts[1], &polyVerts[2], &plane, planeIntersect->z,
planeIntersect->x, chkDist)) ||
(fabsf(plane.normal.z) > 0.5f &&
Math3D_TriChkLineSegParaZDist(&polyVerts[0], &polyVerts[1], &polyVerts[2], &plane, planeIntersect->x,
planeIntersect->y, chkDist))) {
return true;
}
return false;
}
/**
* Tests if sphere `center` `radius` intersects `poly`
*/
s32 CollisionPoly_SphVsPoly(CollisionPoly* poly, Vec3s* vtxList, Vec3f* center, f32 radius) {
static Sphere16 sphere;
static TriNorm tri;
Vec3f intersect;
CollisionPoly_GetVertices(poly, vtxList, tri.vtx);
CollisionPoly_GetNormalF(poly, &tri.plane.normal.x, &tri.plane.normal.y, &tri.plane.normal.z);
tri.plane.originDist = poly->dist;
sphere.center.x = center->x;
sphere.center.y = center->y;
sphere.center.z = center->z;
sphere.radius = radius;
return Math3D_TriVsSphIntersect(&sphere, &tri, &intersect);
}
/**
* Add poly to StaticLookup table
* Table is sorted by poly's smallest y vertex component
* `ssList` is the list to append a new poly to
* `polyList` is the CollisionPoly lookup list
* `vtxList` is the vertex lookup list
* `polyId` is the index of the poly in polyList to insert into the lookup table
*/
void StaticLookup_AddPolyToSSList(CollisionContext* colCtx, SSList* ssList, CollisionPoly* polyList, Vec3s* vtxList,
s16 polyId) {
SSNode* curNode;
SSNode* nextNode;
s32 polyYMin;
u16 newNodeId;
s16 curPolyId;
// if list is null
if (ssList->head == SS_NULL) {
SSNodeList_SetSSListHead(&colCtx->polyNodes, ssList, &polyId);
return;
}
polyYMin = CollisionPoly_GetMinY(&polyList[polyId], vtxList);
curNode = &colCtx->polyNodes.tbl[ssList->head];
curPolyId = curNode->polyId;
// if the poly being inserted has a lower y than the first poly
if (polyYMin < vtxList[COLPOLY_VTX_INDEX(polyList[curPolyId].flags_vIA)].y &&
polyYMin < vtxList[COLPOLY_VTX_INDEX(polyList[curPolyId].flags_vIB)].y &&
polyYMin < vtxList[polyList[curPolyId].vIC].y) {
SSNodeList_SetSSListHead(&colCtx->polyNodes, ssList, &polyId);
return;
}
while (true) {
// if at the end of the list
if (curNode->next == SS_NULL) {
newNodeId = SSNodeList_GetNextNodeIdx(&colCtx->polyNodes);
SSNode_SetValue(&colCtx->polyNodes.tbl[newNodeId], &polyId, SS_NULL);
curNode->next = newNodeId;
return;
}
nextNode = &colCtx->polyNodes.tbl[curNode->next];
curPolyId = nextNode->polyId;
// if the poly being inserted is lower than the next poly
if (polyYMin < vtxList[COLPOLY_VTX_INDEX(polyList[curPolyId].flags_vIA)].y &&
polyYMin < vtxList[COLPOLY_VTX_INDEX(polyList[curPolyId].flags_vIB)].y &&
polyYMin < vtxList[polyList[curPolyId].vIC].y) {
newNodeId = SSNodeList_GetNextNodeIdx(&colCtx->polyNodes);
SSNode_SetValue(&colCtx->polyNodes.tbl[newNodeId], &polyId, curNode->next);
curNode->next = newNodeId;
return;
}
curNode = nextNode;
}
}
/**
* Add CollisionPoly to StaticLookup list
*/
void StaticLookup_AddPoly(StaticLookup* lookup, CollisionContext* colCtx, CollisionPoly* polyList, Vec3s* vtxList,
s16 index) {
if (polyList[index].normal.y > COLPOLY_SNORMAL(0.5f)) {
StaticLookup_AddPolyToSSList(colCtx, &lookup->floor, polyList, vtxList, index);
} else if (polyList[index].normal.y < COLPOLY_SNORMAL(-0.8f)) {
StaticLookup_AddPolyToSSList(colCtx, &lookup->ceiling, polyList, vtxList, index);
} else {
StaticLookup_AddPolyToSSList(colCtx, &lookup->wall, polyList, vtxList, index);
}
}
/**
* Locates the closest static poly directly underneath `pos`, starting at list `ssList`
* returns yIntersect of the closest poly, or `yIntersectMin`
* stores the pointer of the closest poly to `outPoly`
* if BGCHECK_GROUND_CHECK_ON is set, ignore polys with a normal.y < 0 (from vertical walls to ceilings)
*/
f32 BgCheck_RaycastDownStaticList(CollisionContext* colCtx, u16 xpFlags, SSList* ssList, CollisionPoly** outPoly,
Vec3f* pos, f32 yIntersectMin, f32 chkDist, s32 groundChk) {
SSNode* curNode;
s32 polyId;
f32 result;
f32 yIntersect;
result = yIntersectMin;
if (ssList->head == SS_NULL) {
return result;
}
curNode = &colCtx->polyNodes.tbl[ssList->head];
while (true) {
polyId = curNode->polyId;
if (COLPOLY_VIA_FLAG_TEST(colCtx->colHeader->polyList[polyId].flags_vIA, xpFlags) ||
((groundChk & BGCHECK_GROUND_CHECK_ON) && colCtx->colHeader->polyList[polyId].normal.y < 0)) {
if (curNode->next == SS_NULL) {
break;
}
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
if (pos->y < colCtx->colHeader->vtxList[COLPOLY_VTX_INDEX(colCtx->colHeader->polyList[polyId].flags_vIA)].y &&
pos->y < colCtx->colHeader->vtxList[COLPOLY_VTX_INDEX(colCtx->colHeader->polyList[polyId].flags_vIB)].y &&
pos->y < colCtx->colHeader->vtxList[colCtx->colHeader->polyList[polyId].vIC].y) {
break;
}
if (CollisionPoly_CheckYIntersect(&colCtx->colHeader->polyList[polyId], colCtx->colHeader->vtxList, pos->x,
pos->z, &yIntersect, chkDist) == true) {
// if poly is closer to pos without going over
if (yIntersect < pos->y && result < yIntersect) {
result = yIntersect;
*outPoly = &colCtx->colHeader->polyList[polyId];
}
}
if (curNode->next == SS_NULL) {
break;
}
curNode = &colCtx->polyNodes.tbl[curNode->next];
}
return result;
}
/**
* Locates the closest static poly directly underneath `pos` within `lookup`.
* returns yIntersect of the closest poly, or `yIntersectMin`
* stores the pointer of the closest poly to `outPoly`
*/
f32 BgCheck_RaycastDownStatic(StaticLookup* lookup, CollisionContext* colCtx, u16 xpFlags, CollisionPoly** poly,
Vec3f* pos, u32 downChkFlags, f32 chkDist, f32 yIntersectMin) {
f32 yIntersect = yIntersectMin;
s32 groundChk;
if (downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_FLOORS) {
yIntersect = BgCheck_RaycastDownStaticList(colCtx, xpFlags, &lookup->floor, poly, pos, yIntersect, chkDist, 0);
}
if ((downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_WALLS) || (downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE)) {
groundChk = 0;
if (downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY) {
groundChk |= BGCHECK_GROUND_CHECK_ON;
}
yIntersect =
BgCheck_RaycastDownStaticList(colCtx, xpFlags, &lookup->wall, poly, pos, yIntersect, chkDist, groundChk);
}
if (downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_CEILINGS) {
groundChk = 0;
if (downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY) {
groundChk |= BGCHECK_GROUND_CHECK_ON;
}
yIntersect =
BgCheck_RaycastDownStaticList(colCtx, xpFlags, &lookup->ceiling, poly, pos, yIntersect, chkDist, groundChk);
}
return yIntersect;
}
/**
* Compute wall displacement on `posX` and `posZ`
* sets `wallPolyPtr` to `poly` if `wallPolyPtr` is NULL or doesn't have `surfaceData[0] & 0x08000000` set
* returns true if `wallPolyPtr` was changed
* `invXZlength` is 1 / sqrt( sq(poly.normal.x) + sq(poly.normal.z) )
*/
s32 BgCheck_ComputeWallDisplacement(CollisionContext* colCtx, CollisionPoly* poly, f32* posX, f32* posZ, f32 nx, f32 ny,
f32 nz, f32 invXZlength, f32 planeDist, f32 radius, CollisionPoly** wallPolyPtr) {
CollisionPoly* wallPoly;
u32 surfaceData;
u32 hasFlag27;
f32 displacement = (radius - planeDist) * invXZlength;
*posX += displacement * nx;
*posZ += displacement * nz;
wallPoly = *wallPolyPtr;
if (wallPoly == NULL) {
*wallPolyPtr = poly;
return true;
}
surfaceData = colCtx->colHeader->surfaceTypeList[wallPoly->type].data[1];
hasFlag27 = surfaceData & 0x08000000 ? 1 : 0;
if (!hasFlag27) {
*wallPolyPtr = poly;
return true;
}
return false;
}
/**
* Performs collision detection on static poly walls within `lookup` on sphere `pos`, `radius`
* returns true if a collision was detected
* `outX` `outZ` return the displaced x,z coordinates,
* `outPoly` returns the pointer to the nearest poly collided with, or NULL
*/
s32 BgCheck_SphVsStaticWall(StaticLookup* lookup, CollisionContext* colCtx, u16 xpFlags, f32* outX, f32* outZ,
Vec3f* pos, f32 radius, CollisionPoly** outPoly) {
Vec3f resultPos;
f32 temp_f2;
f32 temp_f2_2;
f32 planeDist;
f32 intersect;
s32 result;
CollisionPoly* curPoly;
CollisionPoly* polyList;
SSNode* curNode;
f32 invNormalXZ;
f32 zTemp;
f32 xTemp;
s32 polyId;
f32 normalXZ;
f32 nx;
f32 ny;
f32 nz;
f32 temp_f16;
Vec3s* vtxList;
u16 pad;
f32 zMin;
f32 zMax;
f32 xMin;
f32 xMax;
result = false;
if (lookup->wall.head == SS_NULL) {
return result;
}
resultPos = *pos;
polyList = colCtx->colHeader->polyList;
vtxList = colCtx->colHeader->vtxList;
curNode = &colCtx->polyNodes.tbl[lookup->wall.head];
while (true) {
polyId = curNode->polyId;
curPoly = &polyList[polyId];
if (pos->y < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].y &&
pos->y < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].y && pos->y < vtxList[curPoly->vIC].y) {
break;
}
nx = COLPOLY_GET_NORMAL(curPoly->normal.x);
ny = COLPOLY_GET_NORMAL(curPoly->normal.y);
nz = COLPOLY_GET_NORMAL(curPoly->normal.z);
normalXZ = sqrtf(SQ(nx) + SQ(nz));
planeDist = Math3D_DistPlaneToPos(nx, ny, nz, curPoly->dist, &resultPos);
if (radius < fabsf(planeDist) || COLPOLY_VIA_FLAG_TEST(curPoly->flags_vIA, xpFlags)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
ASSERT(!IS_ZERO(normalXZ), "!IS_ZERO(ac_size)", "../z_bgcheck.c", 2854);
invNormalXZ = 1.0f / normalXZ;
temp_f16 = fabsf(nz) * invNormalXZ;
if (temp_f16 < 0.4f) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
// compute curPoly zMin/zMax
zTemp = vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].z;
zMax = zMin = zTemp;
zTemp = vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].z;
if (zTemp < zMin) {
zMin = zTemp;
} else if (zMax < zTemp) {
zMax = zTemp;
}
zTemp = vtxList[curPoly->vIC].z;
if (zTemp < zMin) {
zMin = zTemp;
} else if (zTemp > zMax) {
zMax = zTemp;
}
zMin -= radius;
zMax += radius;
if (resultPos.z < zMin || resultPos.z > zMax) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
if (CollisionPoly_CheckZIntersectApprox(curPoly, vtxList, resultPos.x, pos->y, &intersect)) {
if (fabsf(intersect - resultPos.z) <= radius / temp_f16) {
if ((intersect - resultPos.z) * nz <= 4.0f) {
BgCheck_ComputeWallDisplacement(colCtx, curPoly, &resultPos.x, &resultPos.z, nx, ny, nz,
invNormalXZ, planeDist, radius, outPoly);
result = true;
}
}
}
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
}
}
curNode = &colCtx->polyNodes.tbl[lookup->wall.head];
while (true) {
polyId = curNode->polyId;
curPoly = &polyList[polyId];
if (pos->y < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].y &&
pos->y < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].y && pos->y < vtxList[curPoly->vIC].y) {
break;
}
nx = COLPOLY_GET_NORMAL(curPoly->normal.x);
ny = COLPOLY_GET_NORMAL(curPoly->normal.y);
nz = COLPOLY_GET_NORMAL(curPoly->normal.z);
normalXZ = sqrtf(SQ(nx) + SQ(nz));
planeDist = Math3D_DistPlaneToPos(nx, ny, nz, curPoly->dist, &resultPos);
if (radius < fabsf(planeDist) || COLPOLY_VIA_FLAG_TEST(curPoly->flags_vIA, xpFlags)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
ASSERT(!IS_ZERO(normalXZ), "!IS_ZERO(ac_size)", "../z_bgcheck.c", 2964);
invNormalXZ = 1.0f / normalXZ;
temp_f16 = fabsf(nx) * invNormalXZ;
if (temp_f16 < 0.4f) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
// compute curPoly xMin/xMax
xTemp = vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].x;
xMax = xMin = xTemp;
xTemp = vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].x;
if (xTemp < xMin) {
xMin = xTemp;
} else if (xMax < xTemp) {
xMax = xTemp;
}
xTemp = vtxList[curPoly->vIC].x;
if (xTemp < xMin) {
xMin = xTemp;
} else if (xMax < xTemp) {
xMax = xTemp;
}
xMin -= radius;
xMax += radius;
if (resultPos.x < xMin || xMax < resultPos.x) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
if (CollisionPoly_CheckXIntersectApprox(curPoly, vtxList, pos->y, resultPos.z, &intersect)) {
if (fabsf(intersect - resultPos.x) <= radius / temp_f16) {
if ((intersect - resultPos.x) * nx <= 4.0f) {
BgCheck_ComputeWallDisplacement(colCtx, curPoly, &resultPos.x, &resultPos.z, nx, ny, nz,
invNormalXZ, planeDist, radius, outPoly);
result = true;
}
}
}
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
*outX = resultPos.x;
*outZ = resultPos.z;
return result;
}
/**
* Tests for collision with a static poly ceiling
* returns true if a collision occurs, else false
* `outPoly` returns the poly collided with
* `outY` returns the y coordinate needed to not collide with `outPoly`
*/
s32 BgCheck_CheckStaticCeiling(StaticLookup* lookup, u16 xpFlags, CollisionContext* colCtx, f32* outY, Vec3f* pos,
f32 checkHeight, CollisionPoly** outPoly) {
s32 result = false;
u16 nextId;
CollisionPoly* curPoly;
CollisionPoly* polyList;
f32 ceilingY;
Vec3s* vtxList;
SSNode* curNode;
s32 curPolyId;
if (lookup->ceiling.head == SS_NULL) {
return false;
}
curNode = &colCtx->polyNodes.tbl[lookup->ceiling.head];
polyList = colCtx->colHeader->polyList;
vtxList = colCtx->colHeader->vtxList;
*outY = pos->y;
while (true) {
curPolyId = curNode->polyId;
if (COLPOLY_VIA_FLAG_TEST(colCtx->colHeader->polyList[curPolyId].flags_vIA, xpFlags)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
curPoly = &polyList[curPolyId];
if (CollisionPoly_CheckYIntersectApprox2(curPoly, vtxList, pos->x, pos->z, &ceilingY)) {
f32 intersectDist = ceilingY - *outY;
f32 ny = COLPOLY_GET_NORMAL(curPoly->normal.y);
if (intersectDist > 0.0f && intersectDist < checkHeight && intersectDist * ny <= 0) {
*outY = ceilingY - checkHeight;
*outPoly = curPoly;
result = true;
}
}
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
return result;
}
/**
* Tests if line `posA` to `posB` intersects with a static poly in list `ssList`. Uses polyCheckTbl
* returns true if such a poly exists, else false
* `outPoly` returns the pointer of the poly intersected
* `posB` and `outPos` returns the point of intersection with `outPoly`
* `outDistSq` returns the squared distance from `posA` to the point of intersect
*/
s32 BgCheck_CheckLineAgainstSSList(SSList* ssList, CollisionContext* colCtx, u16 xpFlags1, u16 xpFlags2, Vec3f* posA,
Vec3f* posB, Vec3f* outPos, CollisionPoly** outPoly, f32* outDistSq, f32 chkDist,
s32 bccFlags) {
SSNode* curNode;
u8* checkedPoly;
Vec3f polyIntersect;
CollisionPoly* polyList;
CollisionPoly* curPoly;
s32 result;
f32 minY;
f32 distSq;
s16 polyId;
result = false;
polyList = colCtx->colHeader->polyList;
if (ssList->head == SS_NULL) {
return result;
}
curNode = &colCtx->polyNodes.tbl[ssList->head];
while (true) {
polyId = curNode->polyId;
checkedPoly = &colCtx->polyNodes.polyCheckTbl[polyId];
if (*checkedPoly == true || COLPOLY_VIA_FLAG_TEST(polyList[polyId].flags_vIA, xpFlags1) ||
!(xpFlags2 == 0 || COLPOLY_VIA_FLAG_TEST(polyList[polyId].flags_vIA, xpFlags2))) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &colCtx->polyNodes.tbl[curNode->next];
continue;
}
}
*checkedPoly = true;
curPoly = &polyList[polyId];
minY = CollisionPoly_GetMinY(curPoly, colCtx->colHeader->vtxList);
if (posA->y < minY && posB->y < minY) {
break;
}
if (CollisionPoly_LineVsPoly(curPoly, colCtx->colHeader->vtxList, posA, posB, &polyIntersect,
(bccFlags & BGCHECK_CHECK_ONE_FACE) != 0, chkDist)) {
distSq = Math3D_Vec3fDistSq(posA, &polyIntersect);
if (distSq < *outDistSq) {
*outDistSq = distSq;
*outPos = polyIntersect;
*posB = polyIntersect;
*outPoly = curPoly;
result = true;
}
}
if (curNode->next == SS_NULL) {
break;
}
curNode = &colCtx->polyNodes.tbl[curNode->next];
}
return result;
}
/**
* Tests if line `posA` to `posB` intersects with a static poly in `lookup`. Uses polyCheckTbl
* returns true if such a poly exists, else false
* `outPoly` returns the pointer of the poly intersected
* `posB` and `outPos` returns the point of intersection with `outPoly`
* `outDistSq` returns the squared distance from `posA` to the point of intersect
*/
s32 BgCheck_CheckLineInSubdivision(StaticLookup* lookup, CollisionContext* colCtx, u16 xpFlags1, u16 xpFlags2,
Vec3f* posA, Vec3f* posB, Vec3f* outPos, CollisionPoly** outPoly, f32 chkDist,
f32* outDistSq, u32 bccFlags) {
s32 result = false;
if ((bccFlags & BGCHECK_CHECK_FLOOR) && lookup->floor.head != SS_NULL) {
if (BgCheck_CheckLineAgainstSSList(&lookup->floor, colCtx, xpFlags1, xpFlags2, posA, posB, outPos, outPoly,
outDistSq, chkDist, bccFlags)) {
result = true;
}
}
if ((bccFlags & BGCHECK_CHECK_WALL) && lookup->wall.head != SS_NULL) {
if (BgCheck_CheckLineAgainstSSList(&lookup->wall, colCtx, xpFlags1, xpFlags2, posA, posB, outPos, outPoly,
outDistSq, chkDist, bccFlags)) {
result = true;
}
}
if ((bccFlags & BGCHECK_CHECK_CEILING) && lookup->ceiling.head != SS_NULL) {
if (BgCheck_CheckLineAgainstSSList(&lookup->ceiling, colCtx, xpFlags1, xpFlags2, posA, posB, outPos, outPoly,
outDistSq, chkDist, bccFlags)) {
result = true;
}
}
return result;
}
/**
* Get first static poly intersecting sphere `center` `radius` from list `node`
* returns true if any poly intersects the sphere, else returns false
* `outPoly` returns the pointer of the first poly found that intersects
*/
s32 BgCheck_SphVsFirstStaticPolyList(SSNode* node, u16 xpFlags, CollisionContext* colCtx, Vec3f* center, f32 radius,
CollisionPoly** outPoly) {
CollisionPoly* polyList = colCtx->colHeader->polyList;
Vec3s* vtxList = colCtx->colHeader->vtxList;
CollisionPoly* curPoly;
u16 nextId;
s16 curPolyId;
while (true) {
curPolyId = node->polyId;
curPoly = &polyList[curPolyId];
if (COLPOLY_VIA_FLAG_TEST(colCtx->colHeader->polyList[curPolyId].flags_vIA, xpFlags)) {
if (node->next == SS_NULL) {
break;
} else {
node = &colCtx->polyNodes.tbl[node->next];
continue;
}
}
if (center->y + radius < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].y &&
center->y + radius < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].y &&
center->y + radius < vtxList[curPoly->vIC].y) {
break;
}
if (CollisionPoly_SphVsPoly(curPoly, vtxList, center, radius)) {
*outPoly = curPoly;
return true;
}
if (node->next == SS_NULL) {
break;
}
node = &colCtx->polyNodes.tbl[node->next];
}
return false;
}
/**
* Get first static poly intersecting sphere `center` `radius` within `lookup`
* returns true if any poly intersects the sphere, else false
* `outPoly` returns the first poly found that intersects
*/
s32 BgCheck_SphVsFirstStaticPoly(StaticLookup* lookup, u16 xpFlags, CollisionContext* colCtx, Vec3f* center, f32 radius,
CollisionPoly** outPoly, u16 bciFlags) {
if (lookup->floor.head != SS_NULL && !(bciFlags & BGCHECK_IGNORE_FLOOR) &&
BgCheck_SphVsFirstStaticPolyList(&colCtx->polyNodes.tbl[lookup->floor.head], xpFlags, colCtx, center, radius,
outPoly)) {
return true;
}
if (lookup->wall.head != SS_NULL && !(bciFlags & BGCHECK_IGNORE_WALL) &&
BgCheck_SphVsFirstStaticPolyList(&colCtx->polyNodes.tbl[lookup->wall.head], xpFlags, colCtx, center, radius,
outPoly)) {
return true;
}
if (lookup->ceiling.head != SS_NULL && !(bciFlags & BGCHECK_IGNORE_CEILING) &&
BgCheck_SphVsFirstStaticPolyList(&colCtx->polyNodes.tbl[lookup->ceiling.head], xpFlags, colCtx, center, radius,
outPoly)) {
return true;
}
return false;
}
/**
* Get StaticLookup from `pos`
* Does not return NULL
*/
StaticLookup* BgCheck_GetNearestStaticLookup(CollisionContext* colCtx, StaticLookup* lookupTbl, Vec3f* pos) {
Vec3i sector;
s32 subdivAmountX;
BgCheck_GetStaticLookupIndicesFromPos(colCtx, pos, &sector);
subdivAmountX = colCtx->subdivAmount.x;
return (sector.z * subdivAmountX) * colCtx->subdivAmount.y + lookupTbl + sector.x + sector.y * subdivAmountX;
}
/**
* Get StaticLookup from `pos`
* Returns NULL if just outside the mesh bounding box
*/
StaticLookup* BgCheck_GetStaticLookup(CollisionContext* colCtx, StaticLookup* lookupTbl, Vec3f* pos) {
Vec3i sector;
s32 subdivAmountX;
if (!BgCheck_PosInStaticBoundingBox(colCtx, pos)) {
return NULL;
}
BgCheck_GetStaticLookupIndicesFromPos(colCtx, pos, &sector);
subdivAmountX = colCtx->subdivAmount.x;
return (sector.z * subdivAmountX) * colCtx->subdivAmount.y + lookupTbl + sector.x + sector.y * subdivAmountX;
}
/**
* Get StaticLookup subdivision indices from `pos`
* `sector` returns the subdivision x,y,z indices containing or is nearest to `pos`
*/
void BgCheck_GetStaticLookupIndicesFromPos(CollisionContext* colCtx, Vec3f* pos, Vec3i* sector) {
sector->x = (pos->x - colCtx->minBounds.x) * colCtx->subdivLengthInv.x;
sector->y = (pos->y - colCtx->minBounds.y) * colCtx->subdivLengthInv.y;
sector->z = (pos->z - colCtx->minBounds.z) * colCtx->subdivLengthInv.z;
if (sector->x < 0) {
sector->x = 0;
} else if (sector->x >= colCtx->subdivAmount.x) {
sector->x = colCtx->subdivAmount.x - 1;
}
if (sector->y < 0) {
sector->y = 0;
} else if (sector->y >= colCtx->subdivAmount.y) {
sector->y = colCtx->subdivAmount.y - 1;
}
if (sector->z < 0) {
sector->z = 0;
} else if (sector->z >= colCtx->subdivAmount.z) {
sector->z = colCtx->subdivAmount.z - 1;
}
}
/**
* Get negative bias subdivision indices
* decrements indices if `pos` is within BGCHECK_SUBDIV_OVERLAP units of the negative subdivision boundary
* `sx`, `sy`, `sz` returns the subdivision x, y, z indices
*/
void BgCheck_GetSubdivisionMinBounds(CollisionContext* colCtx, Vec3f* pos, s32* sx, s32* sy, s32* sz) {
f32 dx = pos->x - colCtx->minBounds.x;
f32 dy = pos->y - colCtx->minBounds.y;
f32 dz = pos->z - colCtx->minBounds.z;
*sx = dx * colCtx->subdivLengthInv.x;
*sy = dy * colCtx->subdivLengthInv.y;
*sz = dz * colCtx->subdivLengthInv.z;
if (((s32)dx % (s32)colCtx->subdivLength.x < BGCHECK_SUBDIV_OVERLAP) && (*sx > 0)) {
*sx -= 1;
}
if (((s32)dy % (s32)colCtx->subdivLength.y < BGCHECK_SUBDIV_OVERLAP) && (*sy > 0)) {
*sy -= 1;
}
if (((s32)dz % (s32)colCtx->subdivLength.z < BGCHECK_SUBDIV_OVERLAP) && (*sz > 0)) {
*sz -= 1;
}
}
/**
* Get positive bias subdivision indices
* increments indices if `pos` is within BGCHECK_SUBDIV_OVERLAP units of the positive subdivision boundary
* `sx`, `sy`, `sz` returns the subdivision x, y, z indices
*/
void BgCheck_GetSubdivisionMaxBounds(CollisionContext* colCtx, Vec3f* pos, s32* sx, s32* sy, s32* sz) {
f32 dx = pos->x - colCtx->minBounds.x;
f32 dy = pos->y - colCtx->minBounds.y;
f32 dz = pos->z - colCtx->minBounds.z;
*sx = dx * colCtx->subdivLengthInv.x;
*sy = dy * colCtx->subdivLengthInv.y;
*sz = dz * colCtx->subdivLengthInv.z;
if (((s32)colCtx->subdivLength.x - BGCHECK_SUBDIV_OVERLAP < (s32)dx % (s32)colCtx->subdivLength.x) &&
(*sx < colCtx->subdivAmount.x - 1)) {
*sx += 1;
}
if (((s32)colCtx->subdivLength.y - BGCHECK_SUBDIV_OVERLAP < (s32)dy % (s32)colCtx->subdivLength.y) &&
(*sy < colCtx->subdivAmount.y - 1)) {
*sy += 1;
}
if (((s32)colCtx->subdivLength.z - BGCHECK_SUBDIV_OVERLAP < (s32)dz % (s32)colCtx->subdivLength.z) &&
(*sz < colCtx->subdivAmount.z - 1)) {
*sz += 1;
}
}
/**
* Calculate the subdivision index bounding box for CollisionPoly `polyId`
* `subdivMinX`, `subdivMinY`, `subdivMinZ` returns the minimum subdivision x, y, z indices
* `subdivMaxX`, `subdivMaxY`, `subdivMaxZ` returns the maximum subdivision x, y, z indices
*/
void BgCheck_GetPolySubdivisionBounds(CollisionContext* colCtx, Vec3s* vtxList, CollisionPoly* polyList,
s32* subdivMinX, s32* subdivMinY, s32* subdivMinZ, s32* subdivMaxX,
s32* subdivMaxY, s32* subdivMaxZ, s16 polyId) {
u16* vtxDataTemp;
Vec3f minVtx;
Vec3f maxVtx;
f32 x;
f32 y;
f32 z;
Vec3s* vtx;
s16 vtxId = COLPOLY_VTX_INDEX(polyList[polyId].vtxData[0]);
Math_Vec3s_ToVec3f(&maxVtx, &vtxList[vtxId]);
Math_Vec3f_Copy(&minVtx, &maxVtx);
for (vtxDataTemp = polyList[polyId].vtxData + 1; vtxDataTemp < polyList[polyId].vtxData + 3; vtxDataTemp++) {
vtxId = COLPOLY_VTX_INDEX(*vtxDataTemp);
vtx = &vtxList[vtxId];
x = vtx->x;
y = vtx->y;
z = vtx->z;
if (minVtx.x > x) {
minVtx.x = x;
} else if (maxVtx.x < x) {
maxVtx.x = x;
}
if (minVtx.y > y) {
minVtx.y = y;
} else if (maxVtx.y < y) {
maxVtx.y = y;
}
if (minVtx.z > z) {
minVtx.z = z;
} else if (maxVtx.z < z) {
maxVtx.z = z;
}
}
BgCheck_GetSubdivisionMinBounds(colCtx, &minVtx, subdivMinX, subdivMinY, subdivMinZ);
BgCheck_GetSubdivisionMaxBounds(colCtx, &maxVtx, subdivMaxX, subdivMaxY, subdivMaxZ);
}
/**
* Test if poly `polyList`[`polyId`] intersects cube `min` `max`
* returns true if the poly intersects the cube, else false
*/
s32 BgCheck_PolyIntersectsSubdivision(Vec3f* min, Vec3f* max, CollisionPoly* polyList, Vec3s* vtxList, s16 polyId) {
f32 intersect;
Vec3f va2;
Vec3f vb2;
Vec3f vc2;
CollisionPoly* poly;
f32 nx;
f32 ny;
f32 nz;
f32 dist;
Vec3f va;
Vec3f vb;
Vec3f vc;
s32 flags[3];
flags[0] = flags[1] = 0;
poly = &polyList[polyId];
BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)], &va);
flags[0] = Math3D_PointRelativeToCubeFaces(&va, min, max);
if (flags[0] == 0) {
return true;
}
BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)], &vb);
flags[1] = Math3D_PointRelativeToCubeFaces(&vb, min, max);
if (flags[1] == 0) {
return true;
}
BgCheck_Vec3sToVec3f(&vtxList[poly->vIC], &vc);
flags[2] = Math3D_PointRelativeToCubeFaces(&vc, min, max);
if (flags[2] == 0) {
return true;
}
if (flags[0] & flags[1] & flags[2]) {
return false;
}
flags[0] |= Math3D_PointRelativeToCubeEdges(&va, min, max) << 8;
flags[1] |= Math3D_PointRelativeToCubeEdges(&vb, min, max) << 8;
flags[2] |= Math3D_PointRelativeToCubeEdges(&vc, min, max) << 8;
if (flags[0] & flags[1] & flags[2]) {
return false;
}
flags[0] |= Math3D_PointRelativeToCubeVertices(&va, min, max) << 0x18;
flags[1] |= Math3D_PointRelativeToCubeVertices(&vb, min, max) << 0x18;
flags[2] |= Math3D_PointRelativeToCubeVertices(&vc, min, max) << 0x18;
if (flags[0] & flags[1] & flags[2]) {
return false;
}
CollisionPoly_GetNormalF(poly, &nx, &ny, &nz);
dist = poly->dist;
if (Math3D_TriChkLineSegParaYIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->z, min->x, &intersect, min->y,
max->y) ||
Math3D_TriChkLineSegParaYIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->z, min->x, &intersect, min->y,
max->y) ||
Math3D_TriChkLineSegParaYIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->z, max->x, &intersect, min->y,
max->y) ||
Math3D_TriChkLineSegParaYIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->z, max->x, &intersect, min->y,
max->y)) {
return true;
}
if (Math3D_TriChkLineSegParaZIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->x, min->y, &intersect, min->z,
max->z) ||
Math3D_TriChkLineSegParaZIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->x, max->y, &intersect, min->z,
max->z) ||
Math3D_TriChkLineSegParaZIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->x, min->y, &intersect, min->z,
max->z) ||
Math3D_TriChkLineSegParaZIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->x, max->y, &intersect, min->z,
max->z)) {
return true;
}
if (Math3D_TriChkLineSegParaXIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->y, min->z, &intersect, min->x,
max->x) ||
Math3D_TriChkLineSegParaXIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->y, max->z, &intersect, min->x,
max->x) ||
Math3D_TriChkLineSegParaXIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->y, min->z, &intersect, min->x,
max->x) ||
Math3D_TriChkLineSegParaXIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->y, max->z, &intersect, min->x,
max->x)) {
return true;
}
BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)], &va2);
BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)], &vb2);
BgCheck_Vec3sToVec3f(&vtxList[poly->vIC], &vc2);
if (Math3D_LineVsCube(min, max, &va2, &vb2) || Math3D_LineVsCube(min, max, &vb2, &vc2) ||
Math3D_LineVsCube(min, max, &vc2, &va2)) {
return true;
}
return false;
}
/**
* Initialize StaticLookup Table
* returns size of table, in bytes
*/
u32 BgCheck_InitializeStaticLookup(CollisionContext* colCtx, PlayState* play, StaticLookup* lookupTbl) {
Vec3s* vtxList;
CollisionPoly* polyList;
s32 polyMax;
s32 polyIdx;
s32 sx;
s32 sy;
s32 sz;
// subdivMin indices
s32 sxMin;
s32 syMin;
s32 szMin;
// subdivMax indices
s32 sxMax;
s32 syMax;
s32 szMax;
// subdiv min/max bounds for adding a poly
Vec3f curSubdivMin;
Vec3f curSubdivMax;
CollisionHeader* colHeader = colCtx->colHeader;
StaticLookup* lookupTblXY;
StaticLookup* lookupTblX;
StaticLookup* lookup;
s32 subdivAmountXY;
f32 subdivLengthX;
f32 subdivLengthY;
f32 subdivLengthZ;
for (lookupTblXY = lookupTbl;
lookupTblXY < (colCtx->subdivAmount.x * colCtx->subdivAmount.y * colCtx->subdivAmount.z + lookupTbl);
lookupTblXY++) {
lookupTblXY->floor.head = SS_NULL;
lookupTblXY->wall.head = SS_NULL;
lookupTblXY->ceiling.head = SS_NULL;
}
polyMax = colHeader->numPolygons;
vtxList = colHeader->vtxList;
polyList = colHeader->polyList;
subdivAmountXY = colCtx->subdivAmount.x * colCtx->subdivAmount.y;
subdivLengthX = colCtx->subdivLength.x + (2 * BGCHECK_SUBDIV_OVERLAP);
subdivLengthY = colCtx->subdivLength.y + (2 * BGCHECK_SUBDIV_OVERLAP);
subdivLengthZ = colCtx->subdivLength.z + (2 * BGCHECK_SUBDIV_OVERLAP);
for (polyIdx = 0; polyIdx < polyMax; polyIdx++) {
BgCheck_GetPolySubdivisionBounds(colCtx, vtxList, polyList, &sxMin, &syMin, &szMin, &sxMax, &syMax, &szMax,
polyIdx);
lookupTblXY = szMin * subdivAmountXY + lookupTbl;
curSubdivMin.z = (colCtx->subdivLength.z * szMin + colCtx->minBounds.z) - BGCHECK_SUBDIV_OVERLAP;
curSubdivMax.z = curSubdivMin.z + subdivLengthZ;
for (sz = szMin; sz < szMax + 1; sz++) {
lookupTblX = (colCtx->subdivAmount.x * syMin) + lookupTblXY;
curSubdivMin.y = (colCtx->subdivLength.y * syMin + colCtx->minBounds.y) - BGCHECK_SUBDIV_OVERLAP;
curSubdivMax.y = curSubdivMin.y + subdivLengthY;
for (sy = syMin; sy < syMax + 1; sy++) {
lookup = sxMin + lookupTblX;
curSubdivMin.x = (colCtx->subdivLength.x * sxMin + colCtx->minBounds.x) - BGCHECK_SUBDIV_OVERLAP;
curSubdivMax.x = curSubdivMin.x + subdivLengthX;
for (sx = sxMin; sx < sxMax + 1; sx++) {
if (BgCheck_PolyIntersectsSubdivision(&curSubdivMin, &curSubdivMax, polyList, vtxList, polyIdx)) {
StaticLookup_AddPoly(lookup, colCtx, polyList, vtxList, polyIdx);
}
curSubdivMin.x += colCtx->subdivLength.x;
curSubdivMax.x += colCtx->subdivLength.x;
lookup++;
}
curSubdivMin.y += colCtx->subdivLength.y;
curSubdivMax.y += colCtx->subdivLength.y;
lookupTblX += colCtx->subdivAmount.x;
}
curSubdivMin.z += colCtx->subdivLength.z;
curSubdivMax.z += colCtx->subdivLength.z;
lookupTblXY += subdivAmountXY;
}
}
return colCtx->polyNodes.count * sizeof(SSNode);
}
/**
* Is current scene a SPOT scene
*/
s32 BgCheck_IsSpotScene(PlayState* play) {
static s16 spotScenes[] = {
SCENE_HYRULE_FIELD, SCENE_KAKARIKO_VILLAGE, SCENE_GRAVEYARD, SCENE_ZORAS_RIVER,
SCENE_KOKIRI_FOREST, SCENE_SACRED_FOREST_MEADOW, SCENE_LAKE_HYLIA, SCENE_ZORAS_DOMAIN,
SCENE_ZORAS_FOUNTAIN, SCENE_GERUDO_VALLEY, SCENE_LOST_WOODS, SCENE_DESERT_COLOSSUS,
SCENE_GERUDOS_FORTRESS, SCENE_HAUNTED_WASTELAND, SCENE_HYRULE_CASTLE, SCENE_DEATH_MOUNTAIN_TRAIL,
SCENE_DEATH_MOUNTAIN_CRATER, SCENE_GORON_CITY, SCENE_LON_LON_RANCH,
};
s16* i;
for (i = spotScenes; i < spotScenes + ARRAY_COUNT(spotScenes); i++) {
if (play->sceneId == *i) {
return true;
}
}
return false;
}
typedef struct {
s16 sceneId;
u32 memSize;
} BgCheckSceneMemEntry;
/**
* Get custom scene memSize
*/
s32 BgCheck_TryGetCustomMemsize(s32 sceneId, u32* memSize) {
static BgCheckSceneMemEntry sceneMemList[] = {
{ SCENE_HYRULE_FIELD, 0xB798 }, { SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR, 0x78C8 },
{ SCENE_GANON_BOSS, 0x70C8 }, { SCENE_SPIRIT_TEMPLE_BOSS, 0xACC8 },
{ SCENE_CHAMBER_OF_THE_SAGES, 0x70C8 }, { SCENE_SPIRIT_TEMPLE, 0x16CC8 },
{ SCENE_FIRE_TEMPLE, 0x198C8 }, { SCENE_GANONDORF_BOSS, 0x84C8 },
};
s32 i;
for (i = 0; i < ARRAY_COUNT(sceneMemList); i++) {
if (sceneId == sceneMemList[i].sceneId) {
*memSize = sceneMemList[i].memSize;
return true;
}
}
return false;
}
/**
* Compute subdivLength for scene mesh lookup, for a single dimension
*/
void BgCheck_SetSubdivisionDimension(f32 min, s32 subdivAmount, f32* max, f32* subdivLength, f32* subdivLengthInv) {
f32 length = (*max - min);
*subdivLength = (s32)(length / subdivAmount) + 1;
*subdivLength = CLAMP_MIN(*subdivLength, BGCHECK_SUBDIV_MIN);
*subdivLengthInv = 1.0f / *subdivLength;
*max = *subdivLength * subdivAmount + min;
}
typedef struct {
s16 sceneId;
Vec3s subdivAmount;
s32 nodeListMax; // if -1, dynamically compute max nodes
} BgCheckSceneSubdivisionEntry;
/**
* Allocate CollisionContext
*/
void BgCheck_Allocate(CollisionContext* colCtx, PlayState* play, CollisionHeader* colHeader) {
static BgCheckSceneSubdivisionEntry sceneSubdivisionList[] = {
{ SCENE_SHADOW_TEMPLE, { 23, 7, 14 }, -1 },
{ SCENE_FOREST_TEMPLE, { 38, 1, 38 }, -1 },
};
u32 tblMax;
u32 memSize;
u32 lookupTblMemSize;
SSNodeList* nodeList;
s32 useCustomSubdivisions;
u32 customMemSize;
s32 customNodeListMax;
s32 i;
colCtx->colHeader = colHeader;
customNodeListMax = -1;
// "/*---------------- BGCheck Buffer Memory Size -------------*/\n"
osSyncPrintf("/*---------------- BGCheck バッファーメモリサイズ -------------*/\n");
if ((R_SCENE_CAM_TYPE == SCENE_CAM_TYPE_FIXED_SHOP_VIEWPOINT) ||
(R_SCENE_CAM_TYPE == SCENE_CAM_TYPE_FIXED_TOGGLE_VIEWPOINT) || (R_SCENE_CAM_TYPE == SCENE_CAM_TYPE_FIXED) ||
(R_SCENE_CAM_TYPE == SCENE_CAM_TYPE_FIXED_MARKET)) {
if (play->sceneId == SCENE_STABLE) {
// "/* BGCheck LonLon Size %dbyte */\n"
osSyncPrintf("/* BGCheck LonLonサイズ %dbyte */\n", 0x3520);
colCtx->memSize = 0x3520;
} else {
// "/* BGCheck Mini Size %dbyte */\n"
osSyncPrintf("/* BGCheck ミニサイズ %dbyte */\n", 0x4E20);
colCtx->memSize = 0x4E20;
}
colCtx->dyna.polyNodesMax = 500;
colCtx->dyna.polyListMax = 256;
colCtx->dyna.vtxListMax = 256;
colCtx->subdivAmount.x = 2;
colCtx->subdivAmount.y = 2;
colCtx->subdivAmount.z = 2;
} else if (BgCheck_IsSpotScene(play) == true) {
colCtx->memSize = 0xF000;
// "/* BGCheck Spot Size %dbyte */\n"
osSyncPrintf("/* BGCheck Spot用サイズ %dbyte */\n", 0xF000);
colCtx->dyna.polyNodesMax = 1000;
colCtx->dyna.polyListMax = 512;
colCtx->dyna.vtxListMax = 512;
colCtx->subdivAmount.x = 16;
colCtx->subdivAmount.y = 4;
colCtx->subdivAmount.z = 16;
} else {
if (BgCheck_TryGetCustomMemsize(play->sceneId, &customMemSize)) {
colCtx->memSize = customMemSize;
} else {
colCtx->memSize = 0x1CC00;
}
// "/* BGCheck Normal Size %dbyte */\n"
osSyncPrintf("/* BGCheck ノーマルサイズ %dbyte */\n", colCtx->memSize);
colCtx->dyna.polyNodesMax = 1000;
colCtx->dyna.polyListMax = 512;
colCtx->dyna.vtxListMax = 512;
useCustomSubdivisions = false;
for (i = 0; i < ARRAY_COUNT(sceneSubdivisionList); i++) {
if (play->sceneId == sceneSubdivisionList[i].sceneId) {
colCtx->subdivAmount.x = sceneSubdivisionList[i].subdivAmount.x;
colCtx->subdivAmount.y = sceneSubdivisionList[i].subdivAmount.y;
colCtx->subdivAmount.z = sceneSubdivisionList[i].subdivAmount.z;
useCustomSubdivisions = true;
customNodeListMax = sceneSubdivisionList[i].nodeListMax;
}
}
if (!useCustomSubdivisions) {
colCtx->subdivAmount.x = 16;
colCtx->subdivAmount.y = 4;
colCtx->subdivAmount.z = 16;
}
}
colCtx->lookupTbl = THA_AllocTailAlign(&play->state.tha,
colCtx->subdivAmount.x * sizeof(StaticLookup) * colCtx->subdivAmount.y *
colCtx->subdivAmount.z,
ALIGNOF_MASK(StaticLookup));
if (colCtx->lookupTbl == NULL) {
LogUtils_HungupThread("../z_bgcheck.c", 4176);
}
colCtx->minBounds.x = colCtx->colHeader->minBounds.x;
colCtx->minBounds.y = colCtx->colHeader->minBounds.y;
colCtx->minBounds.z = colCtx->colHeader->minBounds.z;
colCtx->maxBounds.x = colCtx->colHeader->maxBounds.x;
colCtx->maxBounds.y = colCtx->colHeader->maxBounds.y;
colCtx->maxBounds.z = colCtx->colHeader->maxBounds.z;
BgCheck_SetSubdivisionDimension(colCtx->minBounds.x, colCtx->subdivAmount.x, &colCtx->maxBounds.x,
&colCtx->subdivLength.x, &colCtx->subdivLengthInv.x);
BgCheck_SetSubdivisionDimension(colCtx->minBounds.y, colCtx->subdivAmount.y, &colCtx->maxBounds.y,
&colCtx->subdivLength.y, &colCtx->subdivLengthInv.y);
BgCheck_SetSubdivisionDimension(colCtx->minBounds.z, colCtx->subdivAmount.z, &colCtx->maxBounds.z,
&colCtx->subdivLength.z, &colCtx->subdivLengthInv.z);
memSize = colCtx->subdivAmount.x * sizeof(StaticLookup) * colCtx->subdivAmount.y * colCtx->subdivAmount.z +
colCtx->colHeader->numPolygons * sizeof(u8) + colCtx->dyna.polyNodesMax * sizeof(SSNode) +
colCtx->dyna.polyListMax * sizeof(CollisionPoly) + colCtx->dyna.vtxListMax * sizeof(Vec3s) +
sizeof(CollisionContext);
if (customNodeListMax > 0) {
// tblMax is set without checking if customNodeListMax will result in a memory overflow
// this is a non-issue as long as sceneSubdivisionList.nodeListMax is -1
tblMax = customNodeListMax;
} else {
if (colCtx->memSize < memSize) {
LogUtils_HungupThread("../z_bgcheck.c", 4230);
}
tblMax = (colCtx->memSize - memSize) / sizeof(SSNode);
}
SSNodeList_Initialize(&colCtx->polyNodes);
SSNodeList_Alloc(play, &colCtx->polyNodes, tblMax, colCtx->colHeader->numPolygons);
lookupTblMemSize = BgCheck_InitializeStaticLookup(colCtx, play, colCtx->lookupTbl);
osSyncPrintf(VT_FGCOL(GREEN));
osSyncPrintf("/*---結局 BG使用サイズ %dbyte---*/\n", memSize + lookupTblMemSize);
osSyncPrintf(VT_RST);
DynaPoly_Init(play, &colCtx->dyna);
DynaPoly_Alloc(play, &colCtx->dyna);
}
/**
* Get CollisionHeader
* original name: T_BGCheck_getBGDataInfo
*/
CollisionHeader* BgCheck_GetCollisionHeader(CollisionContext* colCtx, s32 bgId) {
if (bgId == BGCHECK_SCENE) {
return colCtx->colHeader;
}
if (bgId < 0 || bgId > BG_ACTOR_MAX) {
return NULL;
}
if (!(colCtx->dyna.bgActorFlags[bgId] & BGACTOR_IN_USE)) {
osSyncPrintf(VT_COL(YELLOW, BLACK));
osSyncPrintf("T_BGCheck_getBGDataInfo():そのbg_actor_indexは使われておりません。index=%d\n");
osSyncPrintf(VT_RST);
return NULL;
}
return colCtx->dyna.bgActors[bgId].colHeader;
}
/**
* Test if pos is near collision boundaries
*/
s32 BgCheck_PosInStaticBoundingBox(CollisionContext* colCtx, Vec3f* pos) {
if (pos->x < (colCtx->minBounds.x - BGCHECK_SUBDIV_OVERLAP) ||
(colCtx->maxBounds.x + BGCHECK_SUBDIV_OVERLAP) < pos->x ||
pos->y < (colCtx->minBounds.y - BGCHECK_SUBDIV_OVERLAP) ||
(colCtx->maxBounds.y + BGCHECK_SUBDIV_OVERLAP) < pos->y ||
pos->z < (colCtx->minBounds.z - BGCHECK_SUBDIV_OVERLAP) ||
(colCtx->maxBounds.z + BGCHECK_SUBDIV_OVERLAP) < pos->z) {
return false;
}
return true;
}
/**
* Raycast Downward
* If `actor` != null, bgcheck will be skipped for that actor
* returns the yIntersect of the nearest poly found directly below `pos`, or BGCHECK_Y_MIN if no floor detected
* returns the poly found in `outPoly`, and the bgId of the entity in `outBgId`
*/
f32 BgCheck_RaycastDownImpl(PlayState* play, CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly,
s32* outBgId, Vec3f* pos, Actor* actor, u32 downChkFlags, f32 chkDist) {
f32 yIntersectDyna;
s32* temp_a0;
StaticLookup* lookupTbl;
Vec3f checkPos;
StaticLookup* lookup;
DynaRaycastDown dynaRaycastDown;
f32 yIntersect;
*outBgId = BGCHECK_SCENE;
*outPoly = NULL;
lookupTbl = colCtx->lookupTbl;
yIntersect = BGCHECK_Y_MIN;
checkPos = *pos;
while (true) {
if (checkPos.y < colCtx->minBounds.y) {
break;
}
if (BgCheck_PosErrorCheck(&checkPos, "../z_bgcheck.c", 4410)) {
if (actor != NULL) {
osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id);
}
}
lookup = BgCheck_GetStaticLookup(colCtx, lookupTbl, &checkPos);
if (lookup == NULL) {
checkPos.y -= colCtx->subdivLength.y;
continue;
}
yIntersect =
BgCheck_RaycastDownStatic(lookup, colCtx, xpFlags, outPoly, pos, downChkFlags, chkDist, BGCHECK_Y_MIN);
if (yIntersect > BGCHECK_Y_MIN) {
break;
}
checkPos.y -= colCtx->subdivLength.y;
}
dynaRaycastDown.colCtx = colCtx;
dynaRaycastDown.xpFlags = xpFlags;
dynaRaycastDown.yIntersect = yIntersect;
dynaRaycastDown.pos = pos;
dynaRaycastDown.actor = actor;
dynaRaycastDown.downChkFlags = downChkFlags;
dynaRaycastDown.chkDist = chkDist;
dynaRaycastDown.play = play;
dynaRaycastDown.resultPoly = outPoly;
dynaRaycastDown.bgId = outBgId;
yIntersectDyna = BgCheck_RaycastDownDyna(&dynaRaycastDown);
if (yIntersect < yIntersectDyna) {
yIntersect = yIntersectDyna;
}
if (yIntersect != BGCHECK_Y_MIN && SurfaceType_IsSoft(colCtx, *outPoly, *outBgId)) {
yIntersect -= 1.0f;
}
return yIntersect;
}
/**
* Public raycast downward, ground check (UNUSED)
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_CameraRaycastDown1(CollisionContext* colCtx, CollisionPoly** outGroundPoly, Vec3f* pos) {
s32 bgId;
return BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_CAMERA, outGroundPoly, &bgId, pos, NULL,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS |
BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY,
1.0f);
}
/**
* Public raycast downward, ground check
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_EntityRaycastDown1(CollisionContext* colCtx, CollisionPoly** outGroundPoly, Vec3f* pos) {
s32 bgId;
return BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outGroundPoly, &bgId, pos, NULL,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS |
BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY,
1.0f);
}
/**
* Public raycast downward, ground check
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_EntityRaycastDown2(PlayState* play, CollisionContext* colCtx, CollisionPoly** outGroundPoly, Vec3f* pos) {
s32 bgId;
return BgCheck_RaycastDownImpl(play, colCtx, COLPOLY_IGNORE_ENTITY, outGroundPoly, &bgId, pos, NULL,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS |
BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY,
1.0f);
}
/**
* Public raycast downward, ground check
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_EntityRaycastDown3(CollisionContext* colCtx, CollisionPoly** outGroundPoly, s32* bgId, Vec3f* pos) {
return BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outGroundPoly, bgId, pos, NULL,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS |
BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY,
1.0f);
}
/**
* Public raycast downward, ground check
* If `actor` != null, bgcheck will be skipped for that actor
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_EntityRaycastDown4(CollisionContext* colCtx, CollisionPoly** outGroundPoly, s32* bgId, Actor* actor,
Vec3f* pos) {
return BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outGroundPoly, bgId, pos, actor,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS |
BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY,
1.0f);
}
/**
* Public raycast downward, ground check
* If `actor` != null, bgcheck will be skipped for that actor
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_EntityRaycastDown5(PlayState* play, CollisionContext* colCtx, CollisionPoly** outGroundPoly, s32* bgId,
Actor* actor, Vec3f* pos) {
return BgCheck_RaycastDownImpl(play, colCtx, COLPOLY_IGNORE_ENTITY, outGroundPoly, bgId, pos, actor,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS |
BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY,
1.0f);
}
/**
* Public raycast downward, ground check
* If `actor` != null, bgcheck will be skipped for that actor
* `chkDist` is the distance beyond the poly's boundary where the check will still pass
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_EntityRaycastDown6(CollisionContext* colCtx, CollisionPoly** outGroundPoly, s32* bgId, Actor* actor,
Vec3f* pos, f32 chkDist) {
return BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outGroundPoly, bgId, pos, actor,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS |
BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY,
chkDist);
}
/**
* Public raycast downward, floor and exhaustive wall check (UNUSED)
* If `actor` != null, bgcheck will be skipped for that actor
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_EntityRaycastDown7(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor, Vec3f* pos) {
return BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, actor,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS, 1.0f);
}
/**
* Public raycast downward, ground check
* `outGroundPoly` returns original value if no poly detected
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_AnyRaycastDown1(CollisionContext* colCtx, CollisionPoly* outGroundPoly, Vec3f* pos) {
CollisionPoly* checkResultPoly;
f32 result;
s32 bgId;
result = BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_NONE, &checkResultPoly, &bgId, pos, NULL,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS |
BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY,
1.0f);
if (checkResultPoly != NULL) {
*outGroundPoly = *checkResultPoly;
}
return result;
}
/**
* Public raycast downward, ground check (UNUSED)
* `outGroundPoly` returns original value if no poly detected
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_AnyRaycastDown2(CollisionContext* colCtx, CollisionPoly* outGroundPoly, s32* bgId, Vec3f* pos) {
CollisionPoly* checkResultPoly;
f32 result = BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_NONE, &checkResultPoly, bgId, pos, NULL,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS |
BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY,
1.0f);
if (checkResultPoly != NULL) {
*outGroundPoly = *checkResultPoly;
}
return result;
}
/**
* Public raycast downward, floor and exhaustive wall check
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_CameraRaycastDown2(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Vec3f* pos) {
return BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_CAMERA, outPoly, bgId, pos, NULL,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS, 1.0f);
}
/**
* Public raycast downward, exhaustive wall check (UNUSED)
* If `actor` != null, bgcheck will be skipped for that actor
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_EntityRaycastDownWalls(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor,
Vec3f* pos) {
return BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, actor,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS, 1.0f);
}
/**
* Public raycast downward, floor and exhaustive wall check (UNUSED)
* returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected
*/
f32 BgCheck_EntityRaycastDown9(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Vec3f* pos) {
return BgCheck_RaycastDownImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, NULL,
BGCHECK_RAYCAST_DOWN_CHECK_WALLS | BGCHECK_RAYCAST_DOWN_CHECK_FLOORS, 1.0f);
}
/**
* Tests if moving from `posPrev` to `posNext` will collide with a "wall"
* `radius` is used to form a sphere for collision detection purposes
* `checkHeight` is the positive height above posNext to perform certain checks
* returns true if a collision is detected, else false
* `outPoly` returns the closest poly detected, while `outBgId` returns the poly owner
*/
s32 BgCheck_CheckWallImpl(CollisionContext* colCtx, u16 xpFlags, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev,
f32 radius, CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 checkHeight, u8 argA) {
StaticLookup* lookupTbl;
f32 temp_f0;
s32 result;
CollisionPoly* poly;
f32 dx, dy, dz; // change between posPrev to posNext
Vec3f sphCenter;
s32 dynaPolyCollision;
Vec3f posIntersect;
s32 bgId;
f32 temp_f0_2;
f32 f32temp;
f32 nx2, nz2;
Vec3f checkLineNext;
Vec3f checkLinePrev;
f32 n2XZDist;
f32 n3XZDist;
f32 nx3, nz3;
s32 bccFlags;
Vec3f posIntersect2;
s32 bgId2;
f32 nx, ny, nz; // unit normal of polygon
result = false;
*outBgId = BGCHECK_SCENE;
*outPoly = NULL;
lookupTbl = colCtx->lookupTbl;
*posResult = *posNext;
dx = posNext->x - posPrev->x;
dy = posNext->y - posPrev->y;
dz = posNext->z - posPrev->z;
if (BgCheck_PosErrorCheck(posNext, "../z_bgcheck.c", 4831) == true ||
BgCheck_PosErrorCheck(posPrev, "../z_bgcheck.c", 4832) == true) {
if (actor != NULL) {
osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id);
}
}
// if there's movement on the xz plane, and argA flag is 0,
if ((dx != 0.0f || dz != 0.0f) && (argA & 1) == 0) {
if ((checkHeight + dy) < 5.0f) {
//! @bug checkHeight is not applied to posPrev/posNext
result = BgCheck_CheckLineImpl(colCtx, xpFlags, COLPOLY_IGNORE_NONE, posPrev, posNext, &posIntersect, &poly,
&bgId, actor, 1.0f, BGCHECK_CHECK_ALL & ~BGCHECK_CHECK_CEILING);
if (result) {
ny = COLPOLY_GET_NORMAL(poly->normal.y);
// if poly is floor, push result underneath the floor
if (ny > 0.5f) {
posResult->x = posIntersect.x;
if (checkHeight > 1.0f) {
posResult->y = posIntersect.y - 1.0f;
} else {
posResult->y = posIntersect.y - checkHeight;
}
posResult->z = posIntersect.z;
}
// poly is wall
else {
nx = COLPOLY_GET_NORMAL(poly->normal.x);
nz = COLPOLY_GET_NORMAL(poly->normal.z);
posResult->x = radius * nx + posIntersect.x;
posResult->y = radius * ny + posIntersect.y;
posResult->z = radius * nz + posIntersect.z;
}
*outPoly = poly;
*outBgId = bgId;
}
} else {
// if the radius is less than the distance travelled on the xz plane, also test for floor collisions
bccFlags = SQ(radius) < (SQ(dx) + SQ(dz))
? (BGCHECK_CHECK_ALL & ~BGCHECK_CHECK_CEILING)
: (BGCHECK_CHECK_ALL & ~BGCHECK_CHECK_FLOOR & ~BGCHECK_CHECK_CEILING);
// perform a straight line test to see if a line at posNext.y + checkHeight from posPrev.xz to posNext.xz
// passes through any wall and possibly floor polys
checkLineNext = *posNext;
checkLineNext.y += checkHeight;
checkLinePrev = *posPrev;
checkLinePrev.y = checkLineNext.y;
result = BgCheck_CheckLineImpl(colCtx, xpFlags, COLPOLY_IGNORE_NONE, &checkLinePrev, &checkLineNext,
&posIntersect, &poly, &bgId, actor, 1.0f, bccFlags);
if (result) {
nx2 = COLPOLY_GET_NORMAL(poly->normal.x);
nz2 = COLPOLY_GET_NORMAL(poly->normal.z);
n2XZDist = sqrtf(SQ(nx2) + SQ(nz2));
// if poly is not a "flat" floor or "flat" ceiling
if (!IS_ZERO(n2XZDist)) {
// normalize nx,nz and multiply each by the radius to go back to the other side of the wall
f32temp = 1.0f / n2XZDist;
temp_f0 = radius * f32temp;
posResult->x = temp_f0 * nx2 + posIntersect.x;
posResult->z = temp_f0 * nz2 + posIntersect.z;
*outPoly = poly;
*outBgId = bgId;
result = true;
}
}
}
}
sphCenter = *posResult;
dynaPolyCollision = false;
sphCenter.y += checkHeight;
// test if sphere (sphCenter, radius) collides with a dynamic wall, displacing the x/z coordinates
if (BgCheck_SphVsDynaWall(colCtx, xpFlags, &posResult->x, &posResult->z, &sphCenter, radius, outPoly, outBgId,
actor)) {
result = true;
dynaPolyCollision = true;
sphCenter = *posResult;
sphCenter.y += checkHeight;
}
// test if sphere (sphCenter, radius) collides with a static wall, displacing the x/z coordinates
if (BgCheck_PosInStaticBoundingBox(colCtx, posNext) == true &&
// possible bug? if the sphere's radius is smaller than the distance to a subdivision boundary, some static
// polys will be missed
BgCheck_SphVsStaticWall(BgCheck_GetNearestStaticLookup(colCtx, lookupTbl, posResult), colCtx, xpFlags,
&posResult->x, &posResult->z, &sphCenter, radius, outPoly)) {
*outBgId = BGCHECK_SCENE;
result = true;
}
// if a collision with a dyna poly was detected
if (dynaPolyCollision == true || *outBgId != BGCHECK_SCENE) {
if (BgCheck_CheckLineImpl(colCtx, xpFlags, COLPOLY_IGNORE_NONE, posPrev, posResult, &posIntersect2, &poly,
&bgId2, actor, 1.0f, BGCHECK_CHECK_ONE_FACE | BGCHECK_CHECK_WALL)) {
nx3 = COLPOLY_GET_NORMAL(poly->normal.x);
nz3 = COLPOLY_GET_NORMAL(poly->normal.z);
n3XZDist = sqrtf(SQ(nx3) + SQ(nz3));
// if poly is not a "flat" floor or "flat" ceiling
if (!IS_ZERO(n3XZDist)) {
// normalize nx,nz and multiply each by the radius to go back to the other side of the wall
f32temp = 1.0f / n3XZDist;
temp_f0_2 = radius * f32temp;
posResult->x = temp_f0_2 * nx3 + posIntersect2.x;
posResult->z = temp_f0_2 * nz3 + posIntersect2.z;
*outPoly = poly;
*outBgId = bgId2;
result = true;
}
}
}
return result;
}
/**
* Public. Tests if moving from `posPrev` to `posNext` will collide with a "wall"
* `radius` is used to form a sphere for collision detection purposes
* `checkHeight` is the positive height above posNext to perform certain checks
* returns true if a collision is detected, else false
* `outPoly` returns the closest poly detected
*/
s32 BgCheck_EntitySphVsWall1(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius,
CollisionPoly** outPoly, f32 checkHeight) {
s32 bgId;
return BgCheck_CheckWallImpl(colCtx, COLPOLY_IGNORE_ENTITY, posResult, posNext, posPrev, radius, outPoly, &bgId,
NULL, checkHeight, 0);
}
/**
* Public. Tests if moving from `posPrev` to `posNext` will collide with a "wall"
* `radius` is used to form a sphere for collision detection purposes
* `checkHeight` is the positive height above posNext to perform certain checks
* returns true if a collision is detected, else false
* `outPoly` returns the closest poly detected, while `outBgId` returns the poly owner
*/
s32 BgCheck_EntitySphVsWall2(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius,
CollisionPoly** outPoly, s32* outBgId, f32 checkHeight) {
return BgCheck_CheckWallImpl(colCtx, COLPOLY_IGNORE_ENTITY, posResult, posNext, posPrev, radius, outPoly, outBgId,
NULL, checkHeight, 0);
}
/**
* Public. Tests if moving from `posPrev` to `posNext` will collide with a "wall"
* `radius` is used to form a sphere for collision detection purposes
* `checkHeight` is the positive height above posNext to perform certain checks
* `actor` is the actor performing the check, allowing it to be skipped
* returns true if a collision is detected, else false
* `outPoly` returns the closest poly detected, while `outBgId` returns the poly owner
*/
s32 BgCheck_EntitySphVsWall3(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius,
CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 checkHeight) {
return BgCheck_CheckWallImpl(colCtx, COLPOLY_IGNORE_ENTITY, posResult, posNext, posPrev, radius, outPoly, outBgId,
actor, checkHeight, 0);
}
/***
* Public. Tests if moving from `posPrev` to `posNext` will collide with a "wall"
* Skips a check that occurs only when moving on the xz plane
* `radius` is used to form a sphere for collision detection purposes
* `checkHeight` is the positive height above posNext to perform certain checks
* `actor` is the actor performing the check, allowing it to be skipped
* returns true if a collision is detected, else false
* `outPoly` returns the closest poly detected, while `outBgId` returns the poly owner
*/
s32 BgCheck_EntitySphVsWall4(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius,
CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 checkHeight) {
return BgCheck_CheckWallImpl(colCtx, COLPOLY_IGNORE_ENTITY, posResult, posNext, posPrev, radius, outPoly, outBgId,
actor, checkHeight, 1);
}
/***
* Tests for collision with a ceiling poly
* `checkHeight` should be a positive value
* returns true if a collision occurs, else false
* `outPoly` returns the poly collided with, while `outBgId` returns the owner of the poly
* `outY` returns the y coordinate of pos needed to not collide with `outPoly`
*/
s32 BgCheck_CheckCeilingImpl(CollisionContext* colCtx, u16 xpFlags, f32* outY, Vec3f* pos, f32 checkHeight,
CollisionPoly** outPoly, s32* outBgId, Actor* actor) {
StaticLookup* lookupTbl;
StaticLookup* lookup;
s32 result;
Vec3f posTemp;
f32 tempY;
*outBgId = BGCHECK_SCENE;
*outY = pos->y;
if (BgCheck_PosErrorCheck(pos, "../z_bgcheck.c", 5206) == true) {
if (actor != NULL) {
osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id);
}
}
lookupTbl = colCtx->lookupTbl;
if (!BgCheck_PosInStaticBoundingBox(colCtx, pos)) {
return false;
}
lookup = BgCheck_GetNearestStaticLookup(colCtx, lookupTbl, pos);
result = BgCheck_CheckStaticCeiling(lookup, xpFlags, colCtx, outY, pos, checkHeight, outPoly);
posTemp = *pos;
posTemp.y = *outY;
tempY = *outY;
if (BgCheck_CheckDynaCeiling(colCtx, xpFlags, &tempY, &posTemp, checkHeight, outPoly, outBgId, actor)) {
*outY = tempY;
result = true;
}
return result;
}
/**
* Tests for collision with any ceiling poly
* `checkHeight` must be a positive value
* returns true if a collision occurs, else false
* `outY` returns the displaced y coordinate needed to not collide with the poly
*/
s32 BgCheck_AnyCheckCeiling(CollisionContext* colCtx, f32* outY, Vec3f* pos, f32 checkHeight) {
CollisionPoly* poly;
s32 bgId;
return BgCheck_CheckCeilingImpl(colCtx, COLPOLY_IGNORE_NONE, outY, pos, checkHeight, &poly, &bgId, NULL);
}
/**
* Tests for collision with any entity solid ceiling poly
* `checkHeight` must be a positive value
* returns true if a collision occurs, else false
* `outY` returns the displaced y coordinate needed to not collide with the poly
*/
s32 BgCheck_EntityCheckCeiling(CollisionContext* colCtx, f32* outY, Vec3f* pos, f32 checkHeight,
CollisionPoly** outPoly, s32* outBgId, Actor* actor) {
return BgCheck_CheckCeilingImpl(colCtx, COLPOLY_IGNORE_ENTITY, outY, pos, checkHeight, outPoly, outBgId, actor);
}
/**
* Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
* `posB`? `posResult` returns the point of intersection
* `outPoly` returns the pointer to the intersected poly, while `outBgId` returns the entity the poly belongs to
*/
s32 BgCheck_CheckLineImpl(CollisionContext* colCtx, u16 xpFlags1, u16 xpFlags2, Vec3f* posA, Vec3f* posB,
Vec3f* posResult, CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 chkDist,
u32 bccFlags) {
StaticLookup* lookupTbl = colCtx->lookupTbl;
StaticLookup* iLookup;
s32 subdivMin[3];
s32 subdivMax[3];
s32 i;
s32 result;
f32 distSq;
Vec3f posBTemp = *posB;
Vec3f sectorMin;
Vec3f sectorMax;
s32 k;
StaticLookup* lookup;
s32 j;
StaticLookup* jLookup;
s32 temp_lo;
*outBgId = BGCHECK_SCENE;
if (BgCheck_PosErrorCheck(posA, "../z_bgcheck.c", 5334) == true ||
BgCheck_PosErrorCheck(posB, "../z_bgcheck.c", 5335) == true) {
if (actor != NULL) {
osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id);
} else {
osSyncPrintf("pself_actor == NULLで犯人不明\n");
}
}
BgCheck_ResetPolyCheckTbl(&colCtx->polyNodes, colCtx->colHeader->numPolygons);
BgCheck_GetStaticLookupIndicesFromPos(colCtx, posA, (Vec3i*)&subdivMin);
BgCheck_GetStaticLookupIndicesFromPos(colCtx, &posBTemp, (Vec3i*)&subdivMax);
*posResult = *posB;
result = false;
distSq = 1.0e38f;
*outPoly = NULL;
if (subdivMin[0] != subdivMax[0] || subdivMin[1] != subdivMax[1] || subdivMin[2] != subdivMax[2]) {
for (i = 0; i < 3; i++) {
if (subdivMax[i] < subdivMin[i]) {
j = subdivMax[i];
subdivMax[i] = subdivMin[i];
subdivMin[i] = j;
}
}
temp_lo = colCtx->subdivAmount.x * colCtx->subdivAmount.y;
iLookup = lookupTbl + subdivMin[2] * temp_lo;
sectorMin.z = subdivMin[2] * colCtx->subdivLength.z + colCtx->minBounds.z;
sectorMax.z = colCtx->subdivLength.z + sectorMin.z;
for (i = subdivMin[2]; i < subdivMax[2] + 1; i++) {
jLookup = iLookup + subdivMin[1] * colCtx->subdivAmount.x;
sectorMin.y = subdivMin[1] * colCtx->subdivLength.y + colCtx->minBounds.y;
sectorMax.y = colCtx->subdivLength.y + sectorMin.y;
for (j = subdivMin[1]; j < subdivMax[1] + 1; j++) {
lookup = jLookup + subdivMin[0];
sectorMin.x = subdivMin[0] * colCtx->subdivLength.x + colCtx->minBounds.x;
sectorMax.x = colCtx->subdivLength.x + sectorMin.x;
for (k = subdivMin[0]; k < subdivMax[0] + 1; k++) {
if (Math3D_LineVsCube(&sectorMin, &sectorMax, posA, &posBTemp) == true &&
BgCheck_CheckLineInSubdivision(lookup, colCtx, xpFlags1, xpFlags2, posA, &posBTemp, posResult,
outPoly, chkDist, &distSq, bccFlags)) {
result = true;
}
lookup++;
sectorMin.x += colCtx->subdivLength.x;
sectorMax.x += colCtx->subdivLength.x;
}
jLookup += colCtx->subdivAmount.x;
sectorMin.y += colCtx->subdivLength.y;
sectorMax.y += colCtx->subdivLength.y;
}
iLookup += temp_lo;
sectorMin.z += colCtx->subdivLength.z;
sectorMax.z += colCtx->subdivLength.z;
}
} else if (!BgCheck_PosInStaticBoundingBox(colCtx, posA)) {
return false;
} else {
result =
BgCheck_CheckLineInSubdivision(BgCheck_GetNearestStaticLookup(colCtx, lookupTbl, posA), colCtx, xpFlags1,
xpFlags2, posA, &posBTemp, posResult, outPoly, chkDist, &distSq, bccFlags);
if (result == true) {
distSq = Math3D_Vec3fDistSq(posResult, posA);
}
}
if ((bccFlags & BGCHECK_CHECK_DYNA) &&
BgCheck_CheckLineAgainstDyna(colCtx, xpFlags1, posA, &posBTemp, posResult, outPoly, &distSq, outBgId, actor,
chkDist, bccFlags)) {
result = true;
}
return result;
}
/**
* Get bccFlags
*/
u32 BgCheck_GetBccFlags(s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32 chkDyna) {
u32 result = 0;
if (chkWall) {
result = BGCHECK_CHECK_WALL;
}
if (chkFloor) {
result |= BGCHECK_CHECK_FLOOR;
}
if (chkCeil) {
result |= BGCHECK_CHECK_CEILING;
}
if (chkOneFace) {
result |= BGCHECK_CHECK_ONE_FACE;
}
if (chkDyna) {
result |= BGCHECK_CHECK_DYNA;
}
return result;
}
/**
* Public. Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
*/
s32 BgCheck_CameraLineTest1(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace,
s32* bgId) {
return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_CAMERA, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly,
bgId, NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true));
}
/**
* Public. Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
*/
s32 BgCheck_CameraLineTest2(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace,
s32* bgId) {
return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_NONE, COLPOLY_IGNORE_CAMERA, posA, posB, posResult, outPoly,
bgId, NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true));
}
/**
* Public. Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
*/
s32 BgCheck_EntityLineTest1(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace,
s32* bgId) {
return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_ENTITY, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly,
bgId, NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true));
}
/**
* Public. Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
*/
s32 BgCheck_EntityLineTest2(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId,
Actor* actor) {
return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_ENTITY, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly,
bgId, actor, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true));
}
/**
* Public. Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
*/
s32 BgCheck_EntityLineTest3(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId,
Actor* actor, f32 chkDist) {
return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_ENTITY, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly,
bgId, actor, chkDist,
BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true));
}
/**
* Public. Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
*/
s32 BgCheck_ProjectileLineTest(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace,
s32* bgId) {
return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_PROJECTILES, COLPOLY_IGNORE_NONE, posA, posB, posResult,
outPoly, bgId, NULL, 1.0f,
BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true));
}
/**
* Public. Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
*/
s32 BgCheck_AnyLineTest1(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, CollisionPoly** outPoly,
s32 chkOneFace) {
return BgCheck_AnyLineTest2(colCtx, posA, posB, posResult, outPoly, true, true, true, chkOneFace);
}
/**
* Public. Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
*/
s32 BgCheck_AnyLineTest2(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, CollisionPoly** outPoly,
s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace) {
s32 bgId;
return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_NONE, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly,
&bgId, NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true));
}
/**
* Public. Tests if a line from `posA` to `posB` intersects with a poly
* returns true if it does, else false
*/
s32 BgCheck_AnyLineTest3(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, CollisionPoly** outPoly,
s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId) {
return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_NONE, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly, bgId,
NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true));
}
/**
* Get first poly intersecting sphere `center` `radius`
* ignores `actor` dyna poly
* returns true if any poly intersects the sphere, else false
* `outPoly` returns the pointer of the first poly found that intersects
* `outBgId` returns the bgId of the entity that owns `outPoly`
*/
s32 BgCheck_SphVsFirstPolyImpl(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, s32* outBgId,
Vec3f* center, f32 radius, Actor* actor, u16 bciFlags) {
StaticLookup* lookup;
*outBgId = BGCHECK_SCENE;
if (BgCheck_PosErrorCheck(center, "../z_bgcheck.c", 5852) == true) {
if (actor != NULL) {
osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id);
}
}
lookup = BgCheck_GetStaticLookup(colCtx, colCtx->lookupTbl, center);
if (lookup == NULL) {
return false;
} else if (BgCheck_SphVsFirstStaticPoly(lookup, xpFlags, colCtx, center, radius, outPoly, bciFlags) ||
BgCheck_SphVsFirstDynaPoly(colCtx, xpFlags, outPoly, outBgId, center, radius, actor, bciFlags)) {
return true;
}
return false;
}
/**
* Public get first poly intersecting sphere `center` `radius`
*/
s32 BgCheck_SphVsFirstPoly(CollisionContext* colCtx, Vec3f* center, f32 radius) {
CollisionPoly* poly;
s32 bgId;
return BgCheck_SphVsFirstPolyImpl(colCtx, COLPOLY_IGNORE_NONE, &poly, &bgId, center, radius, NULL,
BGCHECK_IGNORE_NONE);
}
/**
* Public get first wall poly intersecting sphere `center` `radius`
*/
s32 BgCheck_SphVsFirstWall(CollisionContext* colCtx, Vec3f* center, f32 radius) {
CollisionPoly* poly;
s32 bgId;
return BgCheck_SphVsFirstPolyImpl(colCtx, COLPOLY_IGNORE_NONE, &poly, &bgId, center, radius, NULL,
BGCHECK_IGNORE_FLOOR | BGCHECK_IGNORE_CEILING);
}
/**
* Init SSNodeList
*/
void SSNodeList_Initialize(SSNodeList* this) {
this->max = 0;
this->count = 0;
this->tbl = NULL;
this->polyCheckTbl = NULL;
}
/**
* Allocate SSNodeList
* tblMax is the number of SSNode records to allocate
* numPolys is the number of polygons defined within the CollisionHeader
*/
void SSNodeList_Alloc(PlayState* play, SSNodeList* this, s32 tblMax, s32 numPolys) {
this->max = tblMax;
this->count = 0;
this->tbl = THA_AllocTailAlign(&play->state.tha, tblMax * sizeof(SSNode), ALIGNOF_MASK(SSNode));
ASSERT(this->tbl != NULL, "this->short_slist_node_tbl != NULL", "../z_bgcheck.c", 5975);
this->polyCheckTbl = GameState_Alloc(&play->state, numPolys, "../z_bgcheck.c", 5979);
ASSERT(this->polyCheckTbl != NULL, "this->polygon_check != NULL", "../z_bgcheck.c", 5981);
}
/**
* Get next SSNodeList SSNode
*/
SSNode* SSNodeList_GetNextNode(SSNodeList* this) {
SSNode* result = &this->tbl[this->count];
this->count++;
ASSERT(this->count < this->max, "this->short_slist_node_last_index < this->short_slist_node_size", "../z_bgcheck.c",
5998);
if (!(this->count < this->max)) {
return NULL;
}
return result;
}
/**
* Get next SSNodeList SSNode index
*/
u16 SSNodeList_GetNextNodeIdx(SSNodeList* this) {
u16 new_index = this->count++;
ASSERT(new_index < this->max, "new_index < this->short_slist_node_size", "../z_bgcheck.c", 6021);
return new_index;
}
/**
* Initialize ScaleRotPos
*/
void ScaleRotPos_Initialize(ScaleRotPos* srp) {
srp->scale.x = srp->scale.y = srp->scale.z = 1.0f;
srp->pos.x = srp->pos.y = srp->pos.z = 0.0f;
srp->rot.x = srp->rot.y = srp->rot.z = 0;
}
/**
* Set ScaleRotPos
*/
void ScaleRotPos_SetValue(ScaleRotPos* srp, Vec3f* scale, Vec3s* rot, Vec3f* pos) {
srp->scale = *scale;
srp->rot = *rot;
srp->pos = *pos;
}
/**
* ScaleRotPos equality test
*/
s32 ScaleRotPos_Equals(ScaleRotPos* a, ScaleRotPos* b) {
if (a->scale.x != b->scale.x || a->scale.y != b->scale.y || a->scale.z != b->scale.z || a->rot.x != b->rot.x ||
a->rot.y != b->rot.y || a->rot.z != b->rot.z || a->pos.x != b->pos.x || a->pos.y != b->pos.y ||
a->pos.z != b->pos.z) {
return false;
}
return true;
}
/**
* Reset DynaLookup lists
*/
void DynaLookup_ResetLists(DynaLookup* dynaLookup) {
SSList_SetNull(&dynaLookup->ceiling);
SSList_SetNull(&dynaLookup->wall);
SSList_SetNull(&dynaLookup->floor);
}
/**
* Reset DynaLookup
*/
void DynaLookup_Reset(DynaLookup* dynaLookup) {
dynaLookup->polyStartIndex = 0;
DynaLookup_ResetLists(dynaLookup);
}
/**
* Reset vtxStartIndex
*/
void DynaLookup_ResetVtxStartIndex(u16* vtxStartIndex) {
*vtxStartIndex = 0;
}
/**
* Initialize BgActor
*/
void BgActor_Initialize(PlayState* play, BgActor* bgActor) {
bgActor->actor = NULL;
bgActor->colHeader = NULL;
ScaleRotPos_Initialize(&bgActor->prevTransform);
ScaleRotPos_Initialize(&bgActor->curTransform);
DynaLookup_Reset(&bgActor->dynaLookup);
DynaLookup_ResetVtxStartIndex(&bgActor->vtxStartIndex);
bgActor->boundingSphere.center.x = bgActor->boundingSphere.center.y = bgActor->boundingSphere.center.z = 0;
bgActor->boundingSphere.radius = 0;
}
/**
* setActor internal
*/
void BgActor_SetActor(BgActor* bgActor, Actor* actor, CollisionHeader* colHeader) {
bgActor->actor = actor;
bgActor->colHeader = colHeader;
bgActor->prevTransform.scale = actor->scale;
bgActor->prevTransform.rot = actor->shape.rot;
bgActor->prevTransform.rot.x--;
bgActor->prevTransform.pos = actor->world.pos;
bgActor->curTransform.scale = actor->scale;
bgActor->curTransform.rot = actor->shape.rot;
bgActor->curTransform.pos = actor->world.pos;
}
/**
* Test if the BgActor transform is the same
*/
s32 BgActor_IsTransformUnchanged(BgActor* bgActor) {
return ScaleRotPos_Equals(&bgActor->prevTransform, &bgActor->curTransform);
}
/**
* NULL polyList
*/
void DynaPoly_NullPolyList(CollisionPoly** polyList) {
*polyList = NULL;
}
/**
* Allocate dyna.polyList
*/
void DynaPoly_AllocPolyList(PlayState* play, CollisionPoly** polyList, s32 numPolys) {
*polyList = THA_AllocTailAlign(&play->state.tha, numPolys * sizeof(CollisionPoly), ALIGNOF_MASK(CollisionPoly));
ASSERT(*polyList != NULL, "ptbl->pbuf != NULL", "../z_bgcheck.c", 6247);
}
/**
* NULL vtxList
*/
void DynaPoly_NullVtxList(Vec3s** vtxList) {
*vtxList = NULL;
}
/**
* Allocate dyna.vtxList
*/
void DynaPoly_AllocVtxList(PlayState* play, Vec3s** vtxList, s32 numVtx) {
*vtxList = THA_AllocTailAlign(&play->state.tha, numVtx * sizeof(Vec3s), ALIGNOF_MASK(Vec3s));
ASSERT(*vtxList != NULL, "ptbl->pbuf != NULL", "../z_bgcheck.c", 6277);
}
/**
* Update BgActor's prevTransform
*/
void DynaPoly_SetBgActorPrevTransform(PlayState* play, BgActor* bgActor) {
bgActor->prevTransform = bgActor->curTransform;
}
/**
* Is BgActor Id
*/
s32 DynaPoly_IsBgIdBgActor(s32 bgId) {
if (bgId < 0 || bgId >= BG_ACTOR_MAX) {
return false;
}
return true;
}
/**
* Init DynaCollisionContext
*/
void DynaPoly_Init(PlayState* play, DynaCollisionContext* dyna) {
dyna->bitFlag = DYNAPOLY_INVALIDATE_LOOKUP;
DynaPoly_NullPolyList(&dyna->polyList);
DynaPoly_NullVtxList(&dyna->vtxList);
DynaSSNodeList_Initialize(play, &dyna->polyNodes);
}
/**
* Set DynaCollisionContext
*/
void DynaPoly_Alloc(PlayState* play, DynaCollisionContext* dyna) {
s32 i;
for (i = 0; i < BG_ACTOR_MAX; i++) {
BgActor_Initialize(play, &dyna->bgActors[i]);
dyna->bgActorFlags[i] = 0;
}
DynaPoly_NullPolyList(&dyna->polyList);
DynaPoly_AllocPolyList(play, &dyna->polyList, dyna->polyListMax);
DynaPoly_NullVtxList(&dyna->vtxList);
DynaPoly_AllocVtxList(play, &dyna->vtxList, dyna->vtxListMax);
DynaSSNodeList_Initialize(play, &dyna->polyNodes);
DynaSSNodeList_Alloc(play, &dyna->polyNodes, dyna->polyNodesMax);
}
/**
* Set BgActor
* original name: DynaPolyInfo_setActor
*/
s32 DynaPoly_SetBgActor(PlayState* play, DynaCollisionContext* dyna, Actor* actor, CollisionHeader* colHeader) {
s32 bgId;
s32 foundSlot = false;
for (bgId = 0; bgId < BG_ACTOR_MAX; bgId++) {
if (!(dyna->bgActorFlags[bgId] & BGACTOR_IN_USE)) {
dyna->bgActorFlags[bgId] |= BGACTOR_IN_USE;
foundSlot = true;
break;
}
}
if (!foundSlot) {
osSyncPrintf(VT_FGCOL(RED));
osSyncPrintf("DynaPolyInfo_setActor():ダイナミックポリゴン 空きインデックスはありません\n");
osSyncPrintf(VT_RST);
return BG_ACTOR_MAX;
}
BgActor_SetActor(&dyna->bgActors[bgId], actor, colHeader);
dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP;
dyna->bgActorFlags[bgId] &= ~BGACTOR_1;
osSyncPrintf(VT_FGCOL(GREEN));
osSyncPrintf("DynaPolyInfo_setActor():index %d\n", bgId);
osSyncPrintf(VT_RST);
return bgId;
}
/**
* Gets the actor assigned to `bgId`
* possible orginal name: DynaPolyInfo_getActor
*/
DynaPolyActor* DynaPoly_GetActor(CollisionContext* colCtx, s32 bgId) {
if (!DynaPoly_IsBgIdBgActor(bgId) || !(colCtx->dyna.bgActorFlags[bgId] & BGACTOR_IN_USE) ||
colCtx->dyna.bgActorFlags[bgId] & BGACTOR_1) {
return NULL;
}
return (DynaPolyActor*)colCtx->dyna.bgActors[bgId].actor;
}
void DynaPoly_DisableCollision(PlayState* play, DynaCollisionContext* dyna, s32 bgId) {
if (DynaPoly_IsBgIdBgActor(bgId)) {
dyna->bgActorFlags[bgId] |= BGACTOR_COLLISION_DISABLED;
dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP;
}
}
void DynaPoly_EnableCollision(PlayState* play, DynaCollisionContext* dyna, s32 bgId) {
if (DynaPoly_IsBgIdBgActor(bgId)) {
dyna->bgActorFlags[bgId] &= ~BGACTOR_COLLISION_DISABLED;
dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP;
}
}
void DynaPoly_DisableCeilingCollision(PlayState* play, DynaCollisionContext* dyna, s32 bgId) {
if (DynaPoly_IsBgIdBgActor(bgId)) {
dyna->bgActorFlags[bgId] |= BGACTOR_CEILING_COLLISION_DISABLED;
dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP;
}
}
void DynaPoly_EnableCeilingCollision(PlayState* play, DynaCollisionContext* dyna, s32 bgId) {
if (DynaPoly_IsBgIdBgActor(bgId)) {
dyna->bgActorFlags[bgId] &= ~BGACTOR_CEILING_COLLISION_DISABLED;
dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP;
}
}
/**
* original name: DynaPolyInfo_delReserve
*/
void DynaPoly_DeleteBgActor(PlayState* play, DynaCollisionContext* dyna, s32 bgId) {
DynaPolyActor* actor;
osSyncPrintf(VT_FGCOL(GREEN));
osSyncPrintf("DynaPolyInfo_delReserve():index %d\n", bgId);
osSyncPrintf(VT_RST);
if (!DynaPoly_IsBgIdBgActor(bgId)) {
if (bgId == -1) {
osSyncPrintf(VT_FGCOL(GREEN));
// "The index that should have been deleted(? ) was(== -1), processing aborted."
osSyncPrintf(
"DynaPolyInfo_delReserve():削除されているはずの(?)\nインデックス(== -1)のため,処理を中止します。\n");
osSyncPrintf(VT_RST);
return;
} else {
osSyncPrintf(VT_FGCOL(RED));
// "Unable to deallocate index / index unallocated, processing aborted."
osSyncPrintf("DynaPolyInfo_delReserve():"
"確保していない出来なかったインデックスの解放のため、処理を中止します。index == %d\n",
bgId);
osSyncPrintf(VT_RST);
return;
}
}
actor = DynaPoly_GetActor(&play->colCtx, bgId);
if (actor != NULL) {
actor->bgId = BGACTOR_NEG_ONE;
dyna->bgActors[bgId].actor = NULL;
dyna->bgActorFlags[bgId] |= BGACTOR_1;
}
}
void DynaPoly_InvalidateLookup(PlayState* play, DynaCollisionContext* dyna) {
dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP;
}
/**
* original name: DynaPolyInfo_expandSRT
*/
void DynaPoly_AddBgActorToLookup(PlayState* play, DynaCollisionContext* dyna, s32 bgId, s32* vtxStartIndex,
s32* polyStartIndex) {
MtxF mtx;
Actor* actor;
s32 pad;
s32 pad2;
f32 numVtxInverse;
s32 i;
Vec3f pos;
Sphere16* sphere;
Vec3s* dVtxList;
Vec3s* point;
Vec3f newCenterPoint;
f32 newRadiusSq;
CollisionHeader* pbgdata;
Vec3f newVtx;
Vec3f vtxA;
Vec3f vtxB;
Vec3f vtxC;
Vec3f newNormal;
pbgdata = dyna->bgActors[bgId].colHeader;
sphere = &dyna->bgActors[bgId].boundingSphere;
actor = dyna->bgActors[bgId].actor;
dyna->bgActors[bgId].dynaLookup.polyStartIndex = *polyStartIndex;
dyna->bgActors[bgId].vtxStartIndex = *vtxStartIndex;
pos = actor->world.pos;
pos.y += actor->shape.yOffset * actor->scale.y;
ScaleRotPos_SetValue(&dyna->bgActors[bgId].curTransform, &actor->scale, &actor->shape.rot, &pos);
if (dyna->bgActorFlags[bgId] & BGACTOR_COLLISION_DISABLED) {
return;
}
if (!(dyna->polyListMax >= *polyStartIndex + pbgdata->numPolygons)) {
osSyncPrintf(VT_FGCOL(RED));
// "do not use if %d exceeds %d"
osSyncPrintf("DynaPolyInfo_expandSRT():polygon over %dが%dを越えるとダメ\n",
*polyStartIndex + pbgdata->numPolygons, dyna->polyListMax);
}
if (!(dyna->vtxListMax >= *vtxStartIndex + pbgdata->numVertices)) {
osSyncPrintf(VT_FGCOL(RED));
// "do not use if %d exceeds %d"
osSyncPrintf("DynaPolyInfo_expandSRT():vertex over %dが%dを越えるとダメ\n",
*vtxStartIndex + pbgdata->numVertices, dyna->vtxListMax);
}
ASSERT(dyna->polyListMax >= *polyStartIndex + pbgdata->numPolygons,
"pdyna_poly_info->poly_num >= *pstart_poly_index + pbgdata->poly_num", "../z_bgcheck.c", 6687);
ASSERT(dyna->vtxListMax >= *vtxStartIndex + pbgdata->numVertices,
"pdyna_poly_info->vert_num >= *pstart_vert_index + pbgdata->vtx_num", "../z_bgcheck.c", 6688);
if (!(dyna->bitFlag & DYNAPOLY_INVALIDATE_LOOKUP) &&
(BgActor_IsTransformUnchanged(&dyna->bgActors[bgId]) == true)) {
s32 pi;
for (pi = *polyStartIndex; pi < *polyStartIndex + pbgdata->numPolygons; pi++) {
CollisionPoly* poly = &dyna->polyList[pi];
s16 normalY = poly->normal.y;
if (normalY > COLPOLY_SNORMAL(0.5f)) {
s16 polyIndex = pi;
DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.floor, &polyIndex);
} else if (normalY < COLPOLY_SNORMAL(-0.8f)) {
if (!(dyna->bgActorFlags[bgId] & BGACTOR_CEILING_COLLISION_DISABLED)) {
s16 polyIndex = pi;
DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.ceiling,
&polyIndex);
}
} else {
s16 polyIndex = pi;
DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.wall, &polyIndex);
}
}
*polyStartIndex += pbgdata->numPolygons;
*vtxStartIndex += pbgdata->numVertices;
} else {
SkinMatrix_SetTranslateRotateYXZScale(
&mtx, dyna->bgActors[bgId].curTransform.scale.x, dyna->bgActors[bgId].curTransform.scale.y,
dyna->bgActors[bgId].curTransform.scale.z, dyna->bgActors[bgId].curTransform.rot.x,
dyna->bgActors[bgId].curTransform.rot.y, dyna->bgActors[bgId].curTransform.rot.z,
dyna->bgActors[bgId].curTransform.pos.x, dyna->bgActors[bgId].curTransform.pos.y,
dyna->bgActors[bgId].curTransform.pos.z);
numVtxInverse = 1.0f / pbgdata->numVertices;
newCenterPoint.x = newCenterPoint.y = newCenterPoint.z = 0.0f;
for (i = 0; i < pbgdata->numVertices; i++) {
Vec3f vtx;
Vec3f vtxT; // Vtx after mtx transform
Math_Vec3s_ToVec3f(&vtx, &pbgdata->vtxList[i]);
SkinMatrix_Vec3fMtxFMultXYZ(&mtx, &vtx, &vtxT);
BgCheck_Vec3fToVec3s(&dyna->vtxList[*vtxStartIndex + i], &vtxT);
if (i == 0) {
dyna->bgActors[bgId].minY = dyna->bgActors[bgId].maxY = vtxT.y;
} else if (vtxT.y < dyna->bgActors[bgId].minY) {
dyna->bgActors[bgId].minY = vtxT.y;
} else if (dyna->bgActors[bgId].maxY < vtxT.y) {
dyna->bgActors[bgId].maxY = vtxT.y;
}
newCenterPoint.x += vtxT.x;
newCenterPoint.y += vtxT.y;
newCenterPoint.z += vtxT.z;
}
newCenterPoint.x *= numVtxInverse;
newCenterPoint.y *= numVtxInverse;
newCenterPoint.z *= numVtxInverse;
sphere->center.x = newCenterPoint.x;
sphere->center.y = newCenterPoint.y;
sphere->center.z = newCenterPoint.z;
newRadiusSq = -SQ(10.0f);
for (i = 0; i < pbgdata->numVertices; i++) {
f32 radiusSq;
newVtx.x = dyna->vtxList[*vtxStartIndex + i].x;
newVtx.y = dyna->vtxList[*vtxStartIndex + i].y;
newVtx.z = dyna->vtxList[*vtxStartIndex + i].z;
radiusSq = Math3D_Vec3fDistSq(&newVtx, &newCenterPoint);
if (newRadiusSq < radiusSq) {
newRadiusSq = radiusSq;
}
}
sphere->radius = sqrtf(newRadiusSq) * 1.1f;
for (i = 0; i < pbgdata->numPolygons; i++) {
CollisionPoly* newPoly = &dyna->polyList[*polyStartIndex + i];
f32 newNormMagnitude;
*newPoly = pbgdata->polyList[i];
// Yeah, this is all kinds of fake, but my God, it matches.
newPoly->flags_vIA =
(COLPOLY_VTX_INDEX(newPoly->flags_vIA) + *vtxStartIndex) | ((*newPoly).flags_vIA & 0xE000);
newPoly->flags_vIB =
(COLPOLY_VTX_INDEX(newPoly->flags_vIB) + *vtxStartIndex) | ((*newPoly).flags_vIB & 0xE000);
newPoly->vIC = *vtxStartIndex + newPoly->vIC;
dVtxList = dyna->vtxList;
vtxA.x = dVtxList[(u32)COLPOLY_VTX_INDEX(newPoly->flags_vIA)].x;
vtxA.y = dVtxList[(u32)COLPOLY_VTX_INDEX(newPoly->flags_vIA)].y;
vtxA.z = dVtxList[(u32)COLPOLY_VTX_INDEX(newPoly->flags_vIA)].z;
vtxB.x = dVtxList[(u32)COLPOLY_VTX_INDEX(newPoly->flags_vIB)].x;
vtxB.y = dVtxList[(u32)COLPOLY_VTX_INDEX(newPoly->flags_vIB)].y;
vtxB.z = dVtxList[(u32)COLPOLY_VTX_INDEX(newPoly->flags_vIB)].z;
vtxC.x = dVtxList[newPoly->vIC].x;
vtxC.y = dVtxList[newPoly->vIC].y;
vtxC.z = dVtxList[newPoly->vIC].z;
Math3D_SurfaceNorm(&vtxA, &vtxB, &vtxC, &newNormal);
newNormMagnitude = Math3D_Vec3fMagnitude(&newNormal);
if (!IS_ZERO(newNormMagnitude)) {
newNormal.x *= (1.0f / newNormMagnitude);
newNormal.y *= (1.0f / newNormMagnitude);
newNormal.z *= (1.0f / newNormMagnitude);
newPoly->normal.x = COLPOLY_SNORMAL(newNormal.x);
newPoly->normal.y = COLPOLY_SNORMAL(newNormal.y);
newPoly->normal.z = COLPOLY_SNORMAL(newNormal.z);
}
newPoly->dist = -DOTXYZ(newNormal, dVtxList[(u32)COLPOLY_VTX_INDEX(newPoly->flags_vIA)]);
if (newNormal.y > 0.5f) {
s16 polyId = *polyStartIndex + i;
DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.floor, &polyId);
} else if (newNormal.y < -0.8f) {
s16 polyId = *polyStartIndex + i;
DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.ceiling, &polyId);
} else {
s16 polyId = *polyStartIndex + i;
DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.wall, &polyId);
}
}
*polyStartIndex += pbgdata->numPolygons;
*vtxStartIndex += pbgdata->numVertices;
}
}
void DynaPoly_UnsetAllInteractFlags(PlayState* play, DynaCollisionContext* dyna, Actor* actor) {
DynaPolyActor* dynaActor;
s32 i;
for (i = 0; i < BG_ACTOR_MAX; i++) {
if (dyna->bgActorFlags[i] & BGACTOR_IN_USE) {
dynaActor = DynaPoly_GetActor(&play->colCtx, i);
if (dynaActor != NULL && &dynaActor->actor == actor) {
DynaPolyActor_UnsetAllInteractFlags((DynaPolyActor*)actor);
break;
}
}
}
}
/**
* Original name: "DynaPolyInfo_setup"
*/
void DynaPoly_UpdateContext(PlayState* play, DynaCollisionContext* dyna) {
DynaPolyActor* actor;
s32 vtxStartIndex;
s32 polyStartIndex;
s32 i;
DynaSSNodeList_ResetCount(&dyna->polyNodes);
for (i = 0; i < BG_ACTOR_MAX; i++) {
DynaLookup_ResetLists(&dyna->bgActors[i].dynaLookup);
}
for (i = 0; i < BG_ACTOR_MAX; i++) {
if (dyna->bgActorFlags[i] & BGACTOR_1) {
// Initialize BgActor
osSyncPrintf(VT_FGCOL(GREEN));
osSyncPrintf("DynaPolyInfo_setup():削除 index=%d\n", i);
osSyncPrintf(VT_RST);
dyna->bgActorFlags[i] = 0;
BgActor_Initialize(play, &dyna->bgActors[i]);
dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP;
}
if (dyna->bgActors[i].actor != NULL && dyna->bgActors[i].actor->update == NULL) {
// Delete BgActor
osSyncPrintf(VT_FGCOL(GREEN));
osSyncPrintf("DynaPolyInfo_setup():削除 index=%d\n", i);
osSyncPrintf(VT_RST);
actor = DynaPoly_GetActor(&play->colCtx, i);
if (actor == NULL) {
return;
}
actor->bgId = BGACTOR_NEG_ONE;
dyna->bgActorFlags[i] = 0;
BgActor_Initialize(play, &dyna->bgActors[i]);
dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP;
}
}
vtxStartIndex = 0;
polyStartIndex = 0;
for (i = 0; i < BG_ACTOR_MAX; i++) {
if (dyna->bgActorFlags[i] & BGACTOR_IN_USE) {
DynaPoly_AddBgActorToLookup(play, dyna, i, &vtxStartIndex, &polyStartIndex);
}
}
dyna->bitFlag &= ~DYNAPOLY_INVALIDATE_LOOKUP;
}
/**
* Update all BgActor's previous ScaleRotPos
*/
void DynaPoly_UpdateBgActorTransforms(PlayState* play, DynaCollisionContext* dyna) {
s32 i;
for (i = 0; i < BG_ACTOR_MAX; i++) {
if (dyna->bgActorFlags[i] & BGACTOR_IN_USE) {
DynaPoly_SetBgActorPrevTransform(play, &dyna->bgActors[i]);
}
}
}
#define DYNA_RAYCAST_FLOORS 1
#define DYNA_RAYCAST_WALLS 2
#define DYNA_RAYCAST_CEILINGS 4
/**
* Performs a downward raycast check on a list of floor, wall, or ceiling dyna polys
* `listType` specifies the poly list type (e.g. DYNA_RAYCAST_FLOORS)
*/
f32 BgCheck_RaycastDownDynaList(DynaRaycastDown* dynaRaycastDown, u32 listType) {
CollisionPoly* polyList;
SSNode* curNode;
f32 result;
f32 yIntersect;
s16 id;
result = dynaRaycastDown->yIntersect;
if (dynaRaycastDown->ssList->head == SS_NULL) {
return result;
}
polyList = dynaRaycastDown->dyna->polyList;
curNode = &dynaRaycastDown->dyna->polyNodes.tbl[dynaRaycastDown->ssList->head];
while (true) {
id = curNode->polyId;
if (COLPOLY_VIA_FLAG_TEST(polyList[id].flags_vIA, dynaRaycastDown->xpFlags)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dynaRaycastDown->dyna->polyNodes.tbl[curNode->next];
continue;
}
}
if ((listType & (DYNA_RAYCAST_WALLS | DYNA_RAYCAST_CEILINGS)) &&
(dynaRaycastDown->downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_GROUND_ONLY) &&
COLPOLY_GET_NORMAL(polyList[id].normal.y) < 0.0f) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dynaRaycastDown->dyna->polyNodes.tbl[curNode->next];
continue;
}
}
if (CollisionPoly_CheckYIntersectApprox1(&polyList[id], dynaRaycastDown->dyna->vtxList, dynaRaycastDown->pos->x,
dynaRaycastDown->pos->z, &yIntersect,
dynaRaycastDown->chkDist) == true &&
yIntersect < dynaRaycastDown->pos->y && result < yIntersect) {
result = yIntersect;
*dynaRaycastDown->resultPoly = &dynaRaycastDown->dyna->polyList[id];
}
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dynaRaycastDown->dyna->polyNodes.tbl[curNode->next];
continue;
}
}
return result;
}
/**
* Performs a downward raycast check on dyna polys
* returns the yIntersect of the poly found, or BGCHECK_Y_MIN if no poly is found
*/
f32 BgCheck_RaycastDownDyna(DynaRaycastDown* dynaRaycastDown) {
s32 i;
f32 result;
f32 intersect2;
s32 i2;
s32 pauseState;
DynaPolyActor* dynaActor;
s32 pad;
Vec3f polyVtx[3];
Vec3f polyNorm;
u32 polyIndex;
CollisionPoly* polyMin;
MtxF srpMtx;
f32 magnitude;
Vec3s* vtxList;
f32 polyDist;
Vec3f vtx;
f32 intersect;
ScaleRotPos* curTransform;
CollisionPoly* poly;
result = BGCHECK_Y_MIN;
*dynaRaycastDown->bgId = BGCHECK_SCENE;
for (i = 0; i < BG_ACTOR_MAX; i++) {
if (!(dynaRaycastDown->colCtx->dyna.bgActorFlags[i] & BGACTOR_IN_USE)) {
continue;
}
if (dynaRaycastDown->actor == dynaRaycastDown->colCtx->dyna.bgActors[i].actor ||
dynaRaycastDown->pos->y < dynaRaycastDown->colCtx->dyna.bgActors[i].minY ||
!Math3D_XZInSphere(&dynaRaycastDown->colCtx->dyna.bgActors[i].boundingSphere, dynaRaycastDown->pos->x,
dynaRaycastDown->pos->z)) {
continue;
}
dynaRaycastDown->dyna = &dynaRaycastDown->colCtx->dyna;
if (dynaRaycastDown->downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_FLOORS) {
dynaRaycastDown->ssList = &dynaRaycastDown->colCtx->dyna.bgActors[i].dynaLookup.floor;
intersect2 = BgCheck_RaycastDownDynaList(dynaRaycastDown, DYNA_RAYCAST_FLOORS);
if (dynaRaycastDown->yIntersect < intersect2) {
dynaRaycastDown->yIntersect = intersect2;
*dynaRaycastDown->bgId = i;
result = intersect2;
}
}
if ((dynaRaycastDown->downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_WALLS) ||
(*dynaRaycastDown->resultPoly == NULL &&
(dynaRaycastDown->downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_WALLS_SIMPLE))) {
dynaRaycastDown->ssList = &dynaRaycastDown->colCtx->dyna.bgActors[i].dynaLookup.wall;
intersect2 = BgCheck_RaycastDownDynaList(dynaRaycastDown, DYNA_RAYCAST_WALLS);
if (dynaRaycastDown->yIntersect < intersect2) {
dynaRaycastDown->yIntersect = intersect2;
*dynaRaycastDown->bgId = i;
result = intersect2;
}
}
if (dynaRaycastDown->downChkFlags & BGCHECK_RAYCAST_DOWN_CHECK_CEILINGS) {
dynaRaycastDown->ssList = &dynaRaycastDown->colCtx->dyna.bgActors[i].dynaLookup.ceiling;
intersect2 = BgCheck_RaycastDownDynaList(dynaRaycastDown, DYNA_RAYCAST_CEILINGS);
if (dynaRaycastDown->yIntersect < intersect2) {
dynaRaycastDown->yIntersect = intersect2;
*dynaRaycastDown->bgId = i;
result = intersect2;
}
}
}
dynaActor = DynaPoly_GetActor(dynaRaycastDown->colCtx, *dynaRaycastDown->bgId);
if ((result != BGCHECK_Y_MIN) && (dynaActor != NULL) && (dynaRaycastDown->play != NULL)) {
pauseState = dynaRaycastDown->play->pauseCtx.state != 0;
if (pauseState == 0) {
pauseState = dynaRaycastDown->play->pauseCtx.debugState != 0;
}
if (!pauseState && (dynaRaycastDown->colCtx->dyna.bgActorFlags[*dynaRaycastDown->bgId] & BGACTOR_1)) {
curTransform = &dynaRaycastDown->dyna->bgActors[*dynaRaycastDown->bgId].curTransform;
polyMin =
&dynaRaycastDown->dyna
->polyList[dynaRaycastDown->dyna->bgActors[*dynaRaycastDown->bgId].dynaLookup.polyStartIndex];
polyIndex = *dynaRaycastDown->resultPoly - polyMin;
poly = &dynaRaycastDown->dyna->bgActors[*dynaRaycastDown->bgId].colHeader->polyList[polyIndex];
SkinMatrix_SetTranslateRotateYXZScale(&srpMtx, curTransform->scale.x, curTransform->scale.y,
curTransform->scale.z, curTransform->rot.x, curTransform->rot.y,
curTransform->rot.z, curTransform->pos.x, curTransform->pos.y,
curTransform->pos.z);
vtxList = dynaRaycastDown->dyna->bgActors[*dynaRaycastDown->bgId].colHeader->vtxList;
for (i2 = 0; i2 < 3; i2++) {
Math_Vec3s_ToVec3f(&vtx, &vtxList[COLPOLY_VTX_INDEX(poly->vtxData[i2])]);
SkinMatrix_Vec3fMtxFMultXYZ(&srpMtx, &vtx, &polyVtx[i2]);
}
Math3D_SurfaceNorm(&polyVtx[0], &polyVtx[1], &polyVtx[2], &polyNorm);
magnitude = Math3D_Vec3fMagnitude(&polyNorm);
if (!IS_ZERO(magnitude)) {
polyNorm.x *= 1.0f / magnitude;
polyNorm.y *= 1.0f / magnitude;
polyNorm.z *= 1.0f / magnitude;
polyDist = -DOTXYZ(polyNorm, polyVtx[0]);
if (Math3D_TriChkPointParaYIntersectInsideTri(
&polyVtx[0], &polyVtx[1], &polyVtx[2], polyNorm.x, polyNorm.y, polyNorm.z, polyDist,
dynaRaycastDown->pos->z, dynaRaycastDown->pos->x, &intersect, dynaRaycastDown->chkDist)) {
if (fabsf(intersect - result) < 1.0f) {
result = intersect;
}
}
}
}
}
return result;
}
/**
* Performs collision detection on a BgActor's wall polys on sphere `pos`, `radius`
* returns true if a collision was detected
* `outX` `outZ` return the displaced x,z coordinates
* `outPoly` returns the pointer to the nearest poly collided with, or NULL
* `outBgId` returns `bgId` if the poly SurfaceType's wall damage flag is not set, else ?
*/
s32 BgCheck_SphVsDynaWallInBgActor(CollisionContext* colCtx, u16 xpFlags, DynaCollisionContext* dyna, SSList* ssList,
f32* outX, f32* outZ, CollisionPoly** outPoly, s32* outBgId, Vec3f* pos, f32 radius,
s32 bgId) {
f32 temp;
f32 intersect;
s32 result = false;
CollisionPoly* poly;
SSNode* curNode;
f32 nx;
f32 ny;
f32 nz;
Vec3f resultPos;
s16 polyId;
f32 zTemp;
f32 xTemp;
f32 normalXZ;
f32 invNormalXZ;
f32 planeDist;
f32 temp_f18;
f32 zIntersectDist;
f32 xIntersectDist;
f32 zMin;
f32 zMax;
f32 xMin;
f32 xMax;
if (ssList->head == SS_NULL) {
return result;
}
resultPos = *pos;
curNode = &dyna->polyNodes.tbl[ssList->head];
while (true) {
polyId = curNode->polyId;
poly = &dyna->polyList[polyId];
CollisionPoly_GetNormalF(poly, &nx, &ny, &nz);
normalXZ = sqrtf(SQ(nx) + SQ(nz));
ASSERT(!IS_ZERO(normalXZ), "!IS_ZERO(ac_size)", "../z_bgcheck.c", 7382);
planeDist = Math3D_DistPlaneToPos(nx, ny, nz, poly->dist, &resultPos);
if (radius < fabsf(planeDist) || COLPOLY_VIA_FLAG_TEST(poly->flags_vIA, xpFlags)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
invNormalXZ = 1.0f / normalXZ;
temp_f18 = fabsf(nz) * invNormalXZ;
if (temp_f18 < 0.4f) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
// compute poly zMin/zMax
zTemp = dyna->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)].z;
zMax = zMin = zTemp;
zTemp = dyna->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)].z;
if (zTemp < zMin) {
zMin = zTemp;
} else if (zTemp > zMax) {
zMax = zTemp;
}
zTemp = dyna->vtxList[poly->vIC].z;
if (zTemp < zMin) {
zMin = zTemp;
} else if (zMax < zTemp) {
zMax = zTemp;
}
zMin -= radius;
zMax += radius;
if (resultPos.z < zMin || zMax < resultPos.z) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
if (CollisionPoly_CheckZIntersectApprox(poly, dyna->vtxList, resultPos.x, pos->y, &intersect)) {
if (fabsf(intersect - resultPos.z) <= radius / temp_f18) {
if ((intersect - resultPos.z) * nz <= 4.0f) {
if (BgCheck_ComputeWallDisplacement(colCtx, poly, &resultPos.x, &resultPos.z, nx, ny, nz,
invNormalXZ, planeDist, radius, outPoly)) {
*outBgId = bgId;
}
result = true;
}
}
}
if (curNode->next == SS_NULL) {
break;
}
curNode = &dyna->polyNodes.tbl[curNode->next];
}
curNode = &dyna->polyNodes.tbl[ssList->head];
while (true) {
polyId = curNode->polyId;
poly = &dyna->polyList[polyId];
CollisionPoly_GetNormalF(poly, &nx, &ny, &nz);
normalXZ = sqrtf(SQ(nx) + SQ(nz));
ASSERT(!IS_ZERO(normalXZ), "!IS_ZERO(ac_size)", "../z_bgcheck.c", 7489);
planeDist = Math3D_DistPlaneToPos(nx, ny, nz, poly->dist, &resultPos);
if (radius < fabsf(planeDist) || COLPOLY_VIA_FLAG_TEST(poly->flags_vIA, xpFlags)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
invNormalXZ = 1.0f / normalXZ;
temp_f18 = fabsf(nx) * invNormalXZ;
if (temp_f18 < 0.4f) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
// compute poly xMin/xMax
xTemp = dyna->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)].x;
xMax = xMin = xTemp;
xTemp = dyna->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)].x;
if (xTemp < xMin) {
xMin = xTemp;
} else if (xMax < xTemp) {
xMax = xTemp;
}
xTemp = dyna->vtxList[poly->vIC].x;
if (xTemp < xMin) {
xMin = xTemp;
} else if (xMax < xTemp) {
xMax = xTemp;
}
xMin -= radius;
xMax += radius;
if (resultPos.x < xMin || xMax < resultPos.x) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
if (CollisionPoly_CheckXIntersectApprox(poly, dyna->vtxList, pos->y, resultPos.z, &intersect)) {
xIntersectDist = intersect - resultPos.x;
if (fabsf(xIntersectDist) <= radius / temp_f18) {
if (xIntersectDist * nx <= 4.0f) {
if (BgCheck_ComputeWallDisplacement(colCtx, poly, &resultPos.x, &resultPos.z, nx, ny, nz,
invNormalXZ, planeDist, radius, outPoly)) {
*outBgId = bgId;
}
result = true;
}
}
}
if (curNode->next == SS_NULL) {
break;
}
curNode = &dyna->polyNodes.tbl[curNode->next];
}
*outX = resultPos.x;
*outZ = resultPos.z;
return result;
}
/**
* Performs collision detection on all dyna poly walls using sphere `pos`, `radius`
* returns true if a collision was detected
* `outX` `outZ` return the displaced x,z coordinates
* `outPoly` returns the pointer to the nearest poly collided with, or NULL
* `outBgId` returns the index of the BgActor that owns `outPoly`
* If `actor` is not NULL, an BgActor bound to that actor will be ignored
*/
s32 BgCheck_SphVsDynaWall(CollisionContext* colCtx, u16 xpFlags, f32* outX, f32* outZ, Vec3f* pos, f32 radius,
CollisionPoly** outPoly, s32* outBgId, Actor* actor) {
Vec3f resultPos;
s32 result;
f32 r;
f32 dz;
f32 dx;
BgActor* bgActor;
s32 i;
result = false;
resultPos = *pos;
for (i = 0; i < BG_ACTOR_MAX; i++) {
if (!(colCtx->dyna.bgActorFlags[i] & BGACTOR_IN_USE)) {
continue;
}
if ((colCtx->dyna.bgActors + i)->actor == actor) {
continue;
}
bgActor = &colCtx->dyna.bgActors[i];
if (bgActor->minY > resultPos.y || bgActor->maxY < resultPos.y) {
continue;
}
bgActor->boundingSphere.radius += (s16)radius;
r = bgActor->boundingSphere.radius;
dx = bgActor->boundingSphere.center.x - resultPos.x;
dz = bgActor->boundingSphere.center.z - resultPos.z;
if (SQ(r) < (SQ(dx) + SQ(dz)) || (!Math3D_XYInSphere(&bgActor->boundingSphere, resultPos.x, resultPos.y) &&
!Math3D_YZInSphere(&bgActor->boundingSphere, resultPos.y, resultPos.z))) {
bgActor->boundingSphere.radius -= (s16)radius;
continue;
}
bgActor->boundingSphere.radius -= (s16)radius;
if (BgCheck_SphVsDynaWallInBgActor(colCtx, xpFlags, &colCtx->dyna,
&(colCtx->dyna.bgActors + i)->dynaLookup.wall, outX, outZ, outPoly, outBgId,
&resultPos, radius, i)) {
resultPos.x = *outX;
resultPos.z = *outZ;
result = true;
}
}
return result;
}
/**
* Tests for collision with a dyna poly ceiling, starting at `ssList`
* returns true if a collision occurs, else false
* `outPoly` returns the poly collided with
* `outY` returns the y coordinate needed to not collide with `outPoly`
*/
s32 BgCheck_CheckDynaCeilingList(CollisionContext* colCtx, u16 xpFlags, DynaCollisionContext* dyna, SSList* ssList,
f32* outY, Vec3f* pos, f32 checkHeight, CollisionPoly** outPoly) {
s16 polyId;
SSNode* curNode;
CollisionPoly* poly;
Vec3f testPos;
f32 ceilingY;
f32 sign;
f32 nx;
f32 ny;
f32 nz;
s32 result = false;
f32 intersectDist;
u16 padding;
if (ssList->head == SS_NULL) {
return false;
}
curNode = &dyna->polyNodes.tbl[ssList->head];
testPos = *pos;
while (true) {
polyId = curNode->polyId;
poly = &dyna->polyList[polyId];
if (COLPOLY_VIA_FLAG_TEST(poly->flags_vIA, xpFlags)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
CollisionPoly_GetNormalF(poly, &nx, &ny, &nz);
if (checkHeight < Math3D_UDistPlaneToPos(nx, ny, nz, poly->dist, &testPos)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
if (CollisionPoly_CheckYIntersectApprox2(poly, dyna->vtxList, testPos.x, testPos.z, &ceilingY)) {
intersectDist = ceilingY - testPos.y;
if (testPos.y < ceilingY && intersectDist < checkHeight && intersectDist * ny <= 0.0f) {
sign = (0.0f <= ny) ? 1.0f : -1.0f;
testPos.y = (sign * checkHeight) + ceilingY;
result = true;
*outPoly = poly;
}
}
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
*outY = testPos.y;
return result;
}
/**
* Tests collision with a dyna poly ceiling
* returns true if a collision occurs, else false
* `outPoly` returns the poly collided with, while `outBgId` returns the id of the BgActor that owns the poly
* `outY` returns the y coordinate needed to not collide with `outPoly`, or `pos`.y + `chkDist` if no collision occurs
*/
s32 BgCheck_CheckDynaCeiling(CollisionContext* colCtx, u16 xpFlags, f32* outY, Vec3f* pos, f32 chkDist,
CollisionPoly** outPoly, s32* outBgId, Actor* actor) {
s32 i = 0;
s32 result = false;
f32 resultY;
f32 tempY = chkDist + pos->y;
BgActor* bgActor;
CollisionPoly* poly;
resultY = tempY;
for (i = 0; i < BG_ACTOR_MAX; i++) {
if (!(colCtx->dyna.bgActorFlags[i] & BGACTOR_IN_USE)) {
continue;
}
if (actor == colCtx->dyna.bgActors[i].actor) {
continue;
}
if (!Math3D_XZInSphere(&colCtx->dyna.bgActors[i].boundingSphere, pos->x, pos->z)) {
continue;
}
if (BgCheck_CheckDynaCeilingList(colCtx, xpFlags, &colCtx->dyna, &colCtx->dyna.bgActors[i].dynaLookup.ceiling,
&tempY, pos, chkDist, &poly) == true &&
tempY < resultY) {
resultY = tempY;
*outPoly = poly;
*outBgId = i;
result = true;
}
}
*outY = resultY;
return result;
}
/**
* Tests if DynaLineTest intersects with a poly
* returns true if a poly was intersected, else false
*/
s32 BgCheck_CheckLineAgainstBgActorSSList(DynaLineTest* dynaLineTest) {
f32 distSq;
s32 result;
CollisionPoly* curPoly;
SSNode* curNode;
Vec3f polyIntersect;
s16 polyId;
if (dynaLineTest->ssList->head == SS_NULL) {
return false;
}
curNode = &dynaLineTest->dyna->polyNodes.tbl[dynaLineTest->ssList->head];
result = false;
while (true) {
polyId = curNode->polyId;
curPoly = &dynaLineTest->dyna->polyList[polyId];
if (COLPOLY_VIA_FLAG_TEST(curPoly->flags_vIA, dynaLineTest->xpFlags)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dynaLineTest->dyna->polyNodes.tbl[curNode->next];
}
} else {
if (CollisionPoly_LineVsPoly(curPoly, dynaLineTest->dyna->vtxList, dynaLineTest->posA, dynaLineTest->posB,
&polyIntersect, dynaLineTest->chkOneFace, dynaLineTest->chkDist)) {
distSq = Math3D_Vec3fDistSq(dynaLineTest->posA, &polyIntersect);
if (distSq < *dynaLineTest->distSq) {
*dynaLineTest->distSq = distSq;
*dynaLineTest->posResult = polyIntersect;
*dynaLineTest->posB = polyIntersect;
*dynaLineTest->resultPoly = curPoly;
result = true;
}
}
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dynaLineTest->dyna->polyNodes.tbl[curNode->next];
}
}
}
return result;
}
/**
* Tests if line `posA` `posB` intersects with a dyna poly within BgActor `bgId`
* `distSq` is the maximum squared distance to check for a collision
* returns true if an intersection occurred, else false
* `posB`? and `posResult` return the point of intersection
* `outPoly` returns the poly intersected
* `distSq` returns the squared distance of the intersection
*/
s32 BgCheck_CheckLineAgainstBgActor(CollisionContext* colCtx, u16 xpFlags, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
CollisionPoly** outPoly, f32* distSq, s32 bgId, f32 chkDist, s32 bccFlags) {
s32 result = false;
DynaLineTest dynaLineTest;
dynaLineTest.colCtx = colCtx;
dynaLineTest.xpFlags = xpFlags;
dynaLineTest.dyna = &colCtx->dyna;
dynaLineTest.posA = posA;
dynaLineTest.posB = posB;
dynaLineTest.posResult = posResult;
dynaLineTest.resultPoly = outPoly;
dynaLineTest.chkOneFace = (bccFlags & BGCHECK_CHECK_ONE_FACE) != 0;
dynaLineTest.distSq = distSq;
dynaLineTest.chkDist = chkDist;
dynaLineTest.ssList = &colCtx->dyna.bgActors[bgId].dynaLookup.wall;
if (bccFlags & BGCHECK_CHECK_WALL) {
if (BgCheck_CheckLineAgainstBgActorSSList(&dynaLineTest)) {
result = true;
}
}
dynaLineTest.ssList = &colCtx->dyna.bgActors[bgId].dynaLookup.floor;
if (bccFlags & BGCHECK_CHECK_FLOOR) {
if (BgCheck_CheckLineAgainstBgActorSSList(&dynaLineTest)) {
result = true;
}
}
dynaLineTest.ssList = &colCtx->dyna.bgActors[bgId].dynaLookup.ceiling;
if (bccFlags & BGCHECK_CHECK_CEILING) {
if (BgCheck_CheckLineAgainstBgActorSSList(&dynaLineTest)) {
result = true;
}
}
return result;
}
/**
* Tests if line from `posA` to `posB` passes through a dyna poly.
* returns true if so, otherwise false
* `outPoly` returns the pointer of the poly intersected.
* `outBgId` returns the BgActor index of the poly
*/
s32 BgCheck_CheckLineAgainstDyna(CollisionContext* colCtx, u16 xpFlags, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
CollisionPoly** outPoly, f32* distSq, s32* outBgId, Actor* actor, f32 chkDist,
s32 bccFlags) {
s32 pad;
s32 i;
s32 result = false;
Linef line;
f32 ay;
f32 by;
for (i = 0; i < BG_ACTOR_MAX; i++) {
if (colCtx->dyna.bgActorFlags[i] & BGACTOR_IN_USE) {
if (actor != colCtx->dyna.bgActors[i].actor) {
ay = posA->y;
by = posB->y;
if (!(ay < colCtx->dyna.bgActors[i].minY) || !(by < colCtx->dyna.bgActors[i].minY)) {
if (!(colCtx->dyna.bgActors[i].maxY < ay) || !(colCtx->dyna.bgActors[i].maxY < by)) {
line.a = *posA;
line.b = *posB;
if (Math3D_LineVsSph(&colCtx->dyna.bgActors[i].boundingSphere, &line) != 0) {
if (BgCheck_CheckLineAgainstBgActor(colCtx, xpFlags, posA, posB, posResult, outPoly, distSq,
i, chkDist, bccFlags) == true) {
*outBgId = i;
result = true;
}
}
}
}
}
}
}
return result;
}
/**
* Get first dyna poly intersecting sphere `center` `radius` from list `ssList`
* returns true if any poly intersects the sphere, else returns false
* `outPoly` returns the pointer of the first poly found that intersects
*/
s32 BgCheck_SphVsFirstDynaPolyList(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, Vec3f* center,
f32 radius, SSList* ssList) {
CollisionPoly* curPoly;
DynaCollisionContext* dyna;
SSNode* curNode;
s32 curPolyId;
if (ssList->head == SS_NULL) {
return false;
}
dyna = &colCtx->dyna;
curNode = &dyna->polyNodes.tbl[ssList->head];
while (true) {
curPolyId = curNode->polyId;
curPoly = &dyna->polyList[curPolyId];
if (COLPOLY_VIA_FLAG_TEST(curPoly->flags_vIA, xpFlags)) {
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
if (CollisionPoly_SphVsPoly(curPoly, dyna->vtxList, center, radius)) {
*outPoly = curPoly;
return true;
}
if (curNode->next == SS_NULL) {
break;
} else {
curNode = &dyna->polyNodes.tbl[curNode->next];
continue;
}
}
return false;
}
/**
* Get first dyna poly intersecting sphere `center` `radius` from BgActor `bgId`
* returns true if any poly intersects the sphere, else false
* `outPoly` returns the pointer of the first poly found that intersects
*/
s32 BgCheck_SphVsFirstDynaPolyInBgActor(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, Vec3f* center,
f32 radius, s32 bgId, u16 bciFlags) {
if ((bciFlags & BGCHECK_IGNORE_CEILING) == 0) {
if (BgCheck_SphVsFirstDynaPolyList(colCtx, xpFlags, outPoly, center, radius,
&colCtx->dyna.bgActors[bgId].dynaLookup.ceiling)) {
return true;
}
}
if ((bciFlags & BGCHECK_IGNORE_WALL) == 0) {
if (BgCheck_SphVsFirstDynaPolyList(colCtx, xpFlags, outPoly, center, radius,
&colCtx->dyna.bgActors[bgId].dynaLookup.wall)) {
return true;
}
}
if ((bciFlags & BGCHECK_IGNORE_FLOOR) == 0) {
if (BgCheck_SphVsFirstDynaPolyList(colCtx, xpFlags, outPoly, center, radius,
&colCtx->dyna.bgActors[bgId].dynaLookup.floor)) {
return true;
}
}
return false;
}
/**
* Gets first dyna poly intersecting sphere `center` `radius`
* returns true if poly detected, else false
* `outPoly` returns the first intersecting poly, while `outBgId` returns the BgActor index of that poly
*/
s32 BgCheck_SphVsFirstDynaPoly(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, s32* outBgId,
Vec3f* center, f32 radius, Actor* actor, u16 bciFlags) {
s32 i = 0;
Sphere16 testSphere;
for (i = 0; i < BG_ACTOR_MAX; i++) {
if (!(colCtx->dyna.bgActorFlags[i] & BGACTOR_IN_USE)) {
continue;
}
if (colCtx->dyna.bgActors[i].actor == actor) {
continue;
}
testSphere.center.x = center->x;
testSphere.center.y = center->y;
testSphere.center.z = center->z;
testSphere.radius = radius;
if (!Math3D_SphVsSph(&testSphere, &colCtx->dyna.bgActors[i].boundingSphere)) {
continue;
}
if (BgCheck_SphVsFirstDynaPolyInBgActor(colCtx, xpFlags, outPoly, center, radius, i, bciFlags)) {
return true;
}
}
return false;
}
/**
* SEGMENTED_TO_VIRTUAL CollisionHeader members
*/
void CollisionHeader_SegmentedToVirtual(CollisionHeader* colHeader) {
colHeader->vtxList = SEGMENTED_TO_VIRTUAL(colHeader->vtxList);
colHeader->polyList = SEGMENTED_TO_VIRTUAL(colHeader->polyList);
colHeader->surfaceTypeList = SEGMENTED_TO_VIRTUAL(colHeader->surfaceTypeList);
colHeader->bgCamList = SEGMENTED_TO_VIRTUAL(colHeader->bgCamList);
colHeader->waterBoxes = SEGMENTED_TO_VIRTUAL(colHeader->waterBoxes);
}
/**
* Convert CollisionHeader Segmented to Virtual addressing
*/
void CollisionHeader_GetVirtual(void* colHeader, CollisionHeader** dest) {
*dest = SEGMENTED_TO_VIRTUAL(colHeader);
CollisionHeader_SegmentedToVirtual(*dest);
}
/**
* SEGMENT_TO_VIRTUAL all active BgActor CollisionHeaders
*/
void func_800418D0(CollisionContext* colCtx, PlayState* play) {
DynaCollisionContext* dyna = &colCtx->dyna;
s32 i;
u16 flag;
for (i = 0; i < BG_ACTOR_MAX; i++) {
flag = dyna->bgActorFlags[i];
if ((flag & BGACTOR_IN_USE) && !(flag & BGACTOR_1)) {
Actor_SetObjectDependency(play, dyna->bgActors[i].actor);
CollisionHeader_SegmentedToVirtual(dyna->bgActors[i].colHeader);
}
}
}
/**
* Reset SSNodeList polyCheckTbl
*/
void BgCheck_ResetPolyCheckTbl(SSNodeList* nodeList, s32 numPolys) {
u8* t;
for (t = nodeList->polyCheckTbl; t < nodeList->polyCheckTbl + numPolys; t++) {
*t = 0;
}
}
/**
* Get SurfaceType property set
*/
u32 SurfaceType_GetData(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId, s32 dataIdx) {
CollisionHeader* colHeader;
SurfaceType* surfaceTypes;
colHeader = BgCheck_GetCollisionHeader(colCtx, bgId);
if (colHeader == NULL || poly == NULL) {
return 0;
}
surfaceTypes = colHeader->surfaceTypeList;
if (surfaceTypes == SEGMENTED_TO_VIRTUAL(NULL)) {
return 0;
}
return surfaceTypes[poly->type].data[dataIdx];
}
u32 SurfaceType_GetBgCamIndex(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 0) & 0xFF;
}
/**
* BgCam get setting of bgCam
*/
u16 BgCheck_GetBgCamSettingImpl(CollisionContext* colCtx, u32 bgCamIndex, s32 bgId) {
u16 camSetting;
CollisionHeader* colHeader;
BgCamInfo* bgCamList;
colHeader = BgCheck_GetCollisionHeader(colCtx, bgId);
if (colHeader == NULL) {
return CAM_SET_NONE;
}
bgCamList = colHeader->bgCamList;
camSetting = bgCamList[bgCamIndex].setting;
return camSetting;
}
/**
* BgCam Get the camera setting of bgCam
*/
u16 BgCheck_GetBgCamSetting(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId);
BgCamInfo* bgCamList;
SurfaceType* surfaceTypes;
if (colHeader == NULL) {
return CAM_SET_NONE;
}
bgCamList = colHeader->bgCamList;
if (bgCamList == SEGMENTED_TO_VIRTUAL(NULL)) {
return CAM_SET_NONE;
}
surfaceTypes = colHeader->surfaceTypeList;
if (surfaceTypes == SEGMENTED_TO_VIRTUAL(NULL)) {
return CAM_SET_NONE;
}
return BgCheck_GetBgCamSettingImpl(colCtx, SurfaceType_GetBgCamIndex(colCtx, poly, bgId), bgId);
}
/**
* BgCam Get the total count of Vec3s data from bgCamFuncData
*/
u16 BgCheck_GetBgCamCountImpl(CollisionContext* colCtx, u32 bgCamIndex, s32 bgId) {
CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId);
BgCamInfo* bgCamList;
if (colHeader == NULL) {
return 0;
}
bgCamList = colHeader->bgCamList;
if (bgCamList == SEGMENTED_TO_VIRTUAL(NULL)) {
return 0;
}
return bgCamList[bgCamIndex].count;
}
/**
* BgCam Get the total count of Vec3s data from bgCamFuncData
*/
u16 BgCheck_GetBgCamCount(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId);
BgCamInfo* bgCamList;
SurfaceType* surfaceTypes;
if (colHeader == NULL) {
return 0;
}
bgCamList = colHeader->bgCamList;
if (bgCamList == SEGMENTED_TO_VIRTUAL(NULL)) {
return 0;
}
surfaceTypes = colHeader->surfaceTypeList;
if (surfaceTypes == SEGMENTED_TO_VIRTUAL(NULL)) {
return 0;
}
return BgCheck_GetBgCamCountImpl(colCtx, SurfaceType_GetBgCamIndex(colCtx, poly, bgId), bgId);
}
/**
* BgCam Get Vec3s data from bgCamFuncData
*/
Vec3s* BgCheck_GetBgCamFuncDataImpl(CollisionContext* colCtx, s32 bgCamIndex, s32 bgId) {
CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId);
BgCamInfo* bgCamList;
if (colHeader == NULL) {
return NULL;
}
bgCamList = colHeader->bgCamList;
if (bgCamList == SEGMENTED_TO_VIRTUAL(NULL)) {
return NULL;
}
return (Vec3s*)SEGMENTED_TO_VIRTUAL(bgCamList[bgCamIndex].bgCamFuncData);
}
/**
* BgCam Get Vec3s data from bgCamFuncData
*/
Vec3s* BgCheck_GetBgCamFuncData(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId);
BgCamInfo* bgCamList;
SurfaceType* surfaceTypes;
if (colHeader == NULL) {
return NULL;
}
bgCamList = colHeader->bgCamList;
if (bgCamList == SEGMENTED_TO_VIRTUAL(NULL)) {
return NULL;
}
surfaceTypes = colHeader->surfaceTypeList;
if (surfaceTypes == SEGMENTED_TO_VIRTUAL(NULL)) {
return NULL;
}
return BgCheck_GetBgCamFuncDataImpl(colCtx, SurfaceType_GetBgCamIndex(colCtx, poly, bgId), bgId);
}
u32 SurfaceType_GetExitIndex(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 8 & 0x1F;
}
u32 SurfaceType_GetFloorType(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 13 & 0x1F;
}
/**
* SurfaceType Get ? Property (& 0x001C 0000)
*/
u32 func_80041D70(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 18 & 7;
}
u32 SurfaceType_GetWallType(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 21 & 0x1F;
}
s32 SurfaceType_GetWallFlags(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return D_80119D90[SurfaceType_GetWallType(colCtx, poly, bgId)];
}
s32 SurfaceType_CheckWallFlag0(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return (SurfaceType_GetWallFlags(colCtx, poly, bgId) & WALL_FLAG_0) ? true : false;
}
s32 SurfaceType_CheckWallFlag1(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return (SurfaceType_GetWallFlags(colCtx, poly, bgId) & WALL_FLAG_1) ? true : false;
}
s32 SurfaceType_CheckWallFlag2(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return (SurfaceType_GetWallFlags(colCtx, poly, bgId) & WALL_FLAG_2) ? true : false;
}
u32 SurfaceType_GetFloorProperty2(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 26 & 0xF;
}
u32 SurfaceType_GetFloorProperty(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 26 & 0xF;
}
u32 SurfaceType_IsSoft(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 30 & 1;
}
u32 SurfaceType_IsHorseBlocked(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 31 & 1;
}
u32 SurfaceType_GetMaterial(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 1) & 0xF;
}
u16 SurfaceType_GetSfxOffset(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
s32 surfaceMaterial = SurfaceType_GetMaterial(colCtx, poly, bgId);
if ((surfaceMaterial < 0) || (surfaceMaterial >= ARRAY_COUNT(sSurfaceMaterialToSfxOffset))) {
return SURFACE_SFX_OFFSET_DIRT;
}
return sSurfaceMaterialToSfxOffset[surfaceMaterial];
}
u32 SurfaceType_GetFloorEffect(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 4 & 3;
}
u32 SurfaceType_GetLightSetting(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 6 & 0x1F;
}
u32 SurfaceType_GetEcho(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 11 & 0x3F;
}
u32 SurfaceType_CanHookshot(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 17 & 1;
}
/**
* CollisionPoly is ignored by entities
* Returns true if poly is ignored by entities, else false
*/
s32 SurfaceType_IsIgnoredByEntities(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
u32 flags;
if (BgCheck_GetCollisionHeader(colCtx, bgId) == NULL) {
return true;
}
flags = poly->flags_vIA & 0x4000;
return !!flags;
}
/**
* CollisionPoly is ignored by projectiles
* Returns true if poly is ignored by projectiles, else false
*/
s32 SurfaceType_IsIgnoredByProjectiles(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
u32 flags;
if (BgCheck_GetCollisionHeader(colCtx, bgId) == NULL) {
return true;
}
flags = poly->flags_vIA & 0x8000;
return !!flags;
}
/**
* Checks if poly is a floor conveyor
*
* A conveyor surface is enabled with non-zero speed.
* When enabled, the conveyor will exhibit two types of behaviour depending on the return value:
*
* If true, then it is a floor conveyor and will push player only while being stood on
* If false, then it is a water conveyor and will push player only while in water
*/
s32 SurfaceType_IsFloorConveyor(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
u32 flags;
if (BgCheck_GetCollisionHeader(colCtx, bgId) == NULL) {
return true;
}
flags = poly->flags_vIB & 0x2000;
return !!flags;
}
u32 SurfaceType_GetConveyorSpeed(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 18 & 7;
}
/**
* returns a value between 0-63, representing 360 / 64 degrees of rotation
*/
u32 SurfaceType_GetConveyorDirection(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 21 & 0x3F;
}
u32 func_80042108(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) {
return (SurfaceType_GetData(colCtx, poly, bgId, 1) & 0x08000000) ? 1 : 0;
}
/**
* Zora's Domain WaterBox in King Zora's Room
*/
WaterBox sZorasDomainWaterBox = { -348, 877, -1746, 553, 780, 0x2104 };
/**
* WaterBox's effective bounding box
*/
f32 sZorasDomainWaterBoxMinX = -348.0f;
f32 sZorasDomainWaterBoxMinY = 777.0f;
f32 sZorasDomainWaterBoxMinZ = -1746.0f;
f32 sZorasDomainWaterBoxMaxX = 205.0f;
f32 sZorasDomainWaterBoxMaxY = 977.0f;
f32 sZorasDomainWaterBoxMaxZ = -967.0f;
/**
* Public. Get the water surface at point (`x`, `ySurface`, `z`). `ySurface` doubles as position y input
* returns true if point is within the xz boundaries of an active water box, else false
* `ySurface` returns the water box's surface, while `outWaterBox` returns a pointer to the WaterBox
*/
s32 WaterBox_GetSurface1(PlayState* play, CollisionContext* colCtx, f32 x, f32 z, f32* ySurface,
WaterBox** outWaterBox) {
if (play->sceneId == SCENE_ZORAS_DOMAIN) {
if (sZorasDomainWaterBoxMinX < x && x < sZorasDomainWaterBoxMaxX && sZorasDomainWaterBoxMinY < *ySurface &&
*ySurface < sZorasDomainWaterBoxMaxY && sZorasDomainWaterBoxMinZ < z && z < sZorasDomainWaterBoxMaxZ) {
*outWaterBox = &sZorasDomainWaterBox;
*ySurface = sZorasDomainWaterBox.ySurface;
return true;
}
}
return WaterBox_GetSurfaceImpl(play, colCtx, x, z, ySurface, outWaterBox);
}
/**
* Internal. Get the water surface at point (`x`, `ySurface`, `z`). `ySurface` doubles as position y input
* returns true if point is within the xz boundaries of an active water box, else false
* `ySurface` returns the water box's surface, while `outWaterBox` returns a pointer to the WaterBox
*/
s32 WaterBox_GetSurfaceImpl(PlayState* play, CollisionContext* colCtx, f32 x, f32 z, f32* ySurface,
WaterBox** outWaterBox) {
CollisionHeader* colHeader = colCtx->colHeader;
s32 room;
WaterBox* waterBox;
if (colHeader->numWaterBoxes == 0 || colHeader->waterBoxes == SEGMENTED_TO_VIRTUAL(NULL)) {
return false;
}
for (waterBox = colHeader->waterBoxes; waterBox < colHeader->waterBoxes + colHeader->numWaterBoxes; waterBox++) {
room = WATERBOX_ROOM(waterBox->properties);
if (room == play->roomCtx.curRoom.num || room == WATERBOX_ROOM_ALL) {
if (!(waterBox->properties & WATERBOX_FLAG_19)) {
if (waterBox->xMin < x && x < waterBox->xMin + waterBox->xLength) {
if (waterBox->zMin < z && z < waterBox->zMin + waterBox->zLength) {
*outWaterBox = waterBox;
*ySurface = waterBox->ySurface;
return true;
}
}
}
}
}
return false;
}
/**
* Gets the first active WaterBox at `pos` with WATERBOX_FLAG_19 not set
* `surfaceChkDist` is the absolute y distance from the water surface to check
* returns the index of the waterbox found, or -1 if no waterbox is found
* `outWaterBox` returns the pointer to the waterbox found, or NULL if none is found
*/
s32 WaterBox_GetSurface2(PlayState* play, CollisionContext* colCtx, Vec3f* pos, f32 surfaceChkDist,
WaterBox** outWaterBox) {
CollisionHeader* colHeader = colCtx->colHeader;
s32 room;
s32 i;
WaterBox* waterBox;
WaterBox* waterBoxList = colHeader->waterBoxes; // unused, needed for matching
if (colHeader->numWaterBoxes == 0 || colHeader->waterBoxes == SEGMENTED_TO_VIRTUAL(NULL)) {
*outWaterBox = NULL;
return -1;
}
for (i = 0; i < colHeader->numWaterBoxes; i++) {
waterBox = &colHeader->waterBoxes[i];
room = WATERBOX_ROOM(waterBox->properties);
if (room == play->roomCtx.curRoom.num || room == WATERBOX_ROOM_ALL) {
if (!(waterBox->properties & WATERBOX_FLAG_19)) {
if (waterBox->xMin < pos->x && pos->x < waterBox->xMin + waterBox->xLength) {
if (waterBox->zMin < pos->z && pos->z < waterBox->zMin + waterBox->zLength) {
if (pos->y - surfaceChkDist < waterBox->ySurface &&
waterBox->ySurface < pos->y + surfaceChkDist) {
*outWaterBox = waterBox;
return i;
}
}
}
}
}
}
*outWaterBox = NULL;
return -1;
}
/**
* WaterBox get BgCam index
*/
u32 WaterBox_GetBgCamIndex(CollisionContext* colCtx, WaterBox* waterBox) {
u32 bgCamIndex = WATERBOX_BGCAM_INDEX(waterBox->properties);
return bgCamIndex;
}
/**
* WaterBox get BgCam setting
*/
u16 WaterBox_GetBgCamSetting(CollisionContext* colCtx, WaterBox* waterBox) {
s32 bgCamIndex = WaterBox_GetBgCamIndex(colCtx, waterBox);
BgCamInfo* bgCamList = colCtx->colHeader->bgCamList;
if (bgCamList == SEGMENTED_TO_VIRTUAL(NULL)) {
return CAM_SET_NONE;
}
return colCtx->colHeader->bgCamList[bgCamIndex].setting;
}
/**
* WaterBox get lighting settings
*/
u32 WaterBox_GetLightIndex(CollisionContext* colCtx, WaterBox* waterBox) {
u32 lightIndex = WATERBOX_LIGHT_INDEX(waterBox->properties);
return lightIndex;
}
/**
* Get the water surface at point (`x`, `ySurface`, `z`). `ySurface` doubles as position y input
* same as WaterBox_GetSurfaceImpl, but tests if WATERBOX_FLAG_19 is set
* returns true if point is within the xz boundaries of an active water box, else false
* `ySurface` returns the water box's surface, while `outWaterBox` returns a pointer to the WaterBox
*/
s32 func_800425B0(PlayState* play, CollisionContext* colCtx, f32 x, f32 z, f32* ySurface, WaterBox** outWaterBox) {
CollisionHeader* colHeader = colCtx->colHeader;
s32 room;
WaterBox* waterBox;
if (colHeader->numWaterBoxes == 0 || colHeader->waterBoxes == SEGMENTED_TO_VIRTUAL(NULL)) {
return false;
}
for (waterBox = colHeader->waterBoxes; waterBox < colHeader->waterBoxes + colHeader->numWaterBoxes; waterBox++) {
room = WATERBOX_ROOM(waterBox->properties);
if ((room == play->roomCtx.curRoom.num) || (room == WATERBOX_ROOM_ALL)) {
if (waterBox->properties & WATERBOX_FLAG_19) {
if (waterBox->xMin < x && x < (waterBox->xMin + waterBox->xLength)) {
if (waterBox->zMin < z && z < (waterBox->zMin + waterBox->zLength)) {
*outWaterBox = waterBox;
*ySurface = waterBox->ySurface;
return true;
}
}
}
}
}
return false;
}
/**
* Gets the `closestPoint` to `point` on the line formed from the intesection of planes `polyA` and `polyB`
* returns true if the `closestPoint` exists, else returns false
*/
s32 func_80042708(CollisionPoly* polyA, CollisionPoly* polyB, Vec3f* point, Vec3f* closestPoint) {
f32 n1X;
f32 n1Y;
f32 n1Z;
f32 n2X;
f32 n2Y;
f32 n2Z;
CollisionPoly_GetNormalF(polyA, &n1X, &n1Y, &n1Z);
CollisionPoly_GetNormalF(polyB, &n2X, &n2Y, &n2Z);
return Math3D_PlaneVsPlaneVsLineClosestPoint(n1X, n1Y, n1Z, polyA->dist, n2X, n2Y, n2Z, polyB->dist, point,
closestPoint);
}
/**
* Get the `closestPoint` to line (`pointA`, `pointB`) formed from the intersection of planes `polyA` and `polyB`
* returns true if the `closestPoint` exists, else returns false
*/
s32 func_800427B4(CollisionPoly* polyA, CollisionPoly* polyB, Vec3f* pointA, Vec3f* pointB, Vec3f* closestPoint) {
f32 n1X;
f32 n1Y;
f32 n1Z;
f32 n2X;
f32 n2Y;
f32 n2Z;
s32 result;
CollisionPoly_GetNormalF(polyA, &n1X, &n1Y, &n1Z);
CollisionPoly_GetNormalF(polyB, &n2X, &n2Y, &n2Z);
result = Math3D_PlaneVsLineSegClosestPoint(n1X, n1Y, n1Z, polyA->dist, n2X, n2Y, n2Z, polyB->dist, pointA, pointB,
closestPoint);
return result;
}
/**
* Draw a list of dyna polys, specified by `ssList`
*/
void BgCheck_DrawDynaPolyList(PlayState* play, CollisionContext* colCtx, DynaCollisionContext* dyna, SSList* ssList,
u8 r, u8 g, u8 b) {
s16 curPolyId;
CollisionPoly* poly;
SSNode* curNode;
Vec3f vA;
Vec3f vB;
Vec3f vC;
f32 nx;
f32 ny;
f32 nz;
if (ssList->head != SS_NULL) {
curNode = &dyna->polyNodes.tbl[ssList->head];
while (true) {
curPolyId = curNode->polyId;
poly = &dyna->polyList[curPolyId];
BgCheck_Vec3sToVec3f(COLPOLY_VTX_INDEX(poly->flags_vIA) + dyna->vtxList, &vA);
BgCheck_Vec3sToVec3f(COLPOLY_VTX_INDEX(poly->flags_vIB) + dyna->vtxList, &vB);
BgCheck_Vec3sToVec3f((s32)(poly->vIC) + dyna->vtxList, &vC);
if (AREG(26)) {
nx = COLPOLY_GET_NORMAL(poly->normal.x);
ny = COLPOLY_GET_NORMAL(poly->normal.y);
nz = COLPOLY_GET_NORMAL(poly->normal.z);
vA.x += AREG(26) * nx;
vA.y += AREG(26) * ny;
vA.z += AREG(26) * nz;
vB.x += AREG(26) * nx;
vB.y += AREG(26) * ny;
vB.z += AREG(26) * nz;
vC.x += AREG(26) * nx;
vC.y += AREG(26) * ny;
vC.z += AREG(26) * nz;
}
Collider_DrawPoly(play->state.gfxCtx, &vA, &vB, &vC, r, g, b);
if (curNode->next == SS_NULL) {
break;
}
curNode = &dyna->polyNodes.tbl[curNode->next];
}
}
}
/**
* Draw a BgActor's dyna polys
* `bgId` is the BgActor index that should be drawn
*/
void BgCheck_DrawBgActor(PlayState* play, CollisionContext* colCtx, s32 bgId) {
if (AREG(21)) {
BgCheck_DrawDynaPolyList(play, colCtx, &colCtx->dyna, &colCtx->dyna.bgActors[bgId].dynaLookup.ceiling, 255, 0,
0);
}
if (AREG(22)) {
BgCheck_DrawDynaPolyList(play, colCtx, &colCtx->dyna, &colCtx->dyna.bgActors[bgId].dynaLookup.wall, 0, 255, 0);
}
if (AREG(23)) {
BgCheck_DrawDynaPolyList(play, colCtx, &colCtx->dyna, &colCtx->dyna.bgActors[bgId].dynaLookup.floor, 0, 0, 255);
}
}
/**
* Draw all dyna polys
*/
void BgCheck_DrawDynaCollision(PlayState* play, CollisionContext* colCtx) {
s32 bgId;
for (bgId = 0; bgId < BG_ACTOR_MAX; bgId++) {
if (!(colCtx->dyna.bgActorFlags[bgId] & BGACTOR_IN_USE)) {
continue;
}
BgCheck_DrawBgActor(play, colCtx, bgId);
}
}
/**
* Draw a static poly
*/
void BgCheck_DrawStaticPoly(PlayState* play, CollisionContext* colCtx, CollisionPoly* poly, u8 r, u8 g, u8 b) {
Vec3f vA;
Vec3f vB;
Vec3f vC;
f32 nx;
f32 ny;
f32 nz;
BgCheck_Vec3sToVec3f(COLPOLY_VTX_INDEX(poly->flags_vIA) + colCtx->colHeader->vtxList, &vA);
BgCheck_Vec3sToVec3f(COLPOLY_VTX_INDEX(poly->flags_vIB) + colCtx->colHeader->vtxList, &vB);
BgCheck_Vec3sToVec3f(poly->vIC + colCtx->colHeader->vtxList, &vC);
if (AREG(26) != 0) {
nx = COLPOLY_GET_NORMAL(poly->normal.x);
ny = COLPOLY_GET_NORMAL(poly->normal.y);
nz = COLPOLY_GET_NORMAL(poly->normal.z);
vA.x += AREG(26) * nx;
vA.y += AREG(26) * ny;
vA.z += AREG(26) * nz;
vB.x += AREG(26) * nx;
vB.y += AREG(26) * ny;
vB.z += AREG(26) * nz;
vC.x += AREG(26) * nx;
vC.y += AREG(26) * ny;
vC.z += AREG(26) * nz;
}
Collider_DrawPoly(play->state.gfxCtx, &vA, &vB, &vC, r, g, b);
}
/**
* Draw a list of static polys, specified by `ssList`
*/
void BgCheck_DrawStaticPolyList(PlayState* play, CollisionContext* colCtx, SSList* ssList, u8 r, u8 g, u8 b) {
SSNode* curNode;
CollisionPoly* polyList = colCtx->colHeader->polyList;
s16 curPolyId;
if (ssList->head != SS_NULL) {
curNode = &colCtx->polyNodes.tbl[ssList->head];
while (true) {
curPolyId = curNode->polyId;
BgCheck_DrawStaticPoly(play, colCtx, &polyList[curPolyId], r, g, b);
if (curNode->next == SS_NULL) {
break;
}
curNode = &colCtx->polyNodes.tbl[curNode->next];
}
}
}
/**
* Draw scene collision
*/
void BgCheck_DrawStaticCollision(PlayState* play, CollisionContext* colCtx) {
Player* player = GET_PLAYER(play);
StaticLookup* lookup = BgCheck_GetNearestStaticLookup(colCtx, colCtx->lookupTbl, &player->actor.world.pos);
if (AREG(23) != 0) {
BgCheck_DrawStaticPolyList(play, colCtx, &lookup->floor, 0, 0, 255);
}
if (AREG(22) != 0) {
BgCheck_DrawStaticPolyList(play, colCtx, &lookup->wall, 0, 255, 0);
}
if (AREG(21) != 0) {
BgCheck_DrawStaticPolyList(play, colCtx, &lookup->ceiling, 255, 0, 0);
}
}