mirror of
https://gitlab.com/ryandwyer/perfect-dark
synced 2026-08-13 03:59:41 -04:00
289 lines
7.0 KiB
C
289 lines
7.0 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "game/atan2f.h"
|
|
#include "game/padhalllv.h"
|
|
#include "bss.h"
|
|
#include "lib/lib_17ce0.h"
|
|
#include "lib/mtx.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
u8 var8005ef20 = 254;
|
|
|
|
u8 var8009a4e0[456][2];
|
|
|
|
void portal_get_centre(s32 portalnum, struct coord *avg)
|
|
{
|
|
struct portalvertices *pvertices = (struct portalvertices *)((uintptr_t)g_BgPortals + g_BgPortals[portalnum].verticesoffset);
|
|
f32 f0;
|
|
s32 i;
|
|
|
|
avg->x = pvertices->vertices[0].x;
|
|
avg->y = pvertices->vertices[0].y;
|
|
avg->z = pvertices->vertices[0].z;
|
|
|
|
f0 = 1.0f / pvertices->count;
|
|
|
|
for (i = 1; i < pvertices->count; i++) {
|
|
avg->x += pvertices->vertices[i].x;
|
|
avg->y += pvertices->vertices[i].y;
|
|
avg->z += pvertices->vertices[i].z;
|
|
}
|
|
|
|
avg->x *= f0;
|
|
avg->y *= f0;
|
|
avg->z *= f0;
|
|
}
|
|
|
|
#if VERSION < VERSION_NTSC_1_0
|
|
bool portal00018e34nb(s32 portalnum)
|
|
{
|
|
return g_BgPortals[portalnum].flags & PORTALFLAG_02;
|
|
}
|
|
|
|
void portal00018e54nb(s32 portalnum)
|
|
{
|
|
g_BgPortals[portalnum].flags |= PORTALFLAG_02;
|
|
}
|
|
|
|
void portal00018e74nb(s32 portalnum)
|
|
{
|
|
g_BgPortals[portalnum].flags &= (~PORTALFLAG_02 & 0xff);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Add roomnum to the rooms list, provided it's not already there and there's
|
|
* space available at the end of the list.
|
|
*
|
|
* The list is assumed to have 16 slots, with the last being reserved for the
|
|
* -1 terminator.
|
|
*/
|
|
void portal_append_room(RoomNum *rooms, RoomNum roomnum)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < 16 && rooms[i] != -1; i++) {
|
|
if (rooms[i] == roomnum) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (i < 15) {
|
|
rooms[i] = roomnum;
|
|
rooms[i + 1] = -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determine whether a line from pos1 to pos2 intersects the portal,
|
|
* and if so then in which direction.
|
|
*
|
|
* Return one of:
|
|
* PORTALINTERSECTION_NONE
|
|
* PORTALINTERSECTION_BEHINDTOFRONT
|
|
* PORTALINTERSECTION_FRONTTOBEHIND
|
|
*
|
|
* The portal's normal vector is the front.
|
|
*/
|
|
s32 portal_calculate_intersection(s32 portalnum, struct coord *pos1, struct coord *pos2)
|
|
{
|
|
s32 i;
|
|
struct coord sp60;
|
|
struct portalvertices *pvertices;
|
|
struct coord *curr;
|
|
struct coord *next;
|
|
struct coord sp48;
|
|
u8 lastside;
|
|
f32 sp40[1];
|
|
struct coord sp34;
|
|
f32 value1;
|
|
f32 value2;
|
|
f32 tmp;
|
|
u32 stack;
|
|
|
|
lastside = 0;
|
|
pvertices = (struct portalvertices *)((uintptr_t)g_BgPortals + g_BgPortals[portalnum].verticesoffset);
|
|
value1 = pos1->f[0] * (g_PortalMetrics + portalnum)->normal.f[0] + pos1->f[1] * (g_PortalMetrics + portalnum)->normal.f[1] + pos1->f[2] * (g_PortalMetrics + portalnum)->normal.f[2];
|
|
value2 = pos2->f[0] * (g_PortalMetrics + portalnum)->normal.f[0] + pos2->f[1] * (g_PortalMetrics + portalnum)->normal.f[1] + pos2->f[2] * (g_PortalMetrics + portalnum)->normal.f[2];
|
|
|
|
if (value1 < (g_PortalMetrics + portalnum)->min) {
|
|
if (value2 < (g_PortalMetrics + portalnum)->min) {
|
|
return PORTALINTERSECTION_NONE;
|
|
}
|
|
} else if ((g_PortalMetrics + portalnum)->max < value1 && (g_PortalMetrics + portalnum)->max < value2) {
|
|
return PORTALINTERSECTION_NONE;
|
|
}
|
|
|
|
sp60.f[0] = pos2->f[0] - pos1->f[0];
|
|
sp60.f[1] = pos2->f[1] - pos1->f[1];
|
|
sp60.f[2] = pos2->f[2] - pos1->f[2];
|
|
|
|
var8007fcb4 = (value1 + value2) * 0.5f - (g_PortalMetrics + portalnum)->min;
|
|
|
|
curr = &pvertices->vertices[0];
|
|
next = &pvertices->vertices[1];
|
|
|
|
for (i = 0; i < pvertices->count; i++) {
|
|
if (i + 1 == pvertices->count) {
|
|
next = &pvertices->vertices[0];
|
|
}
|
|
|
|
sp48.f[0] = next->f[0] - curr->f[0];
|
|
sp48.f[1] = next->f[1] - curr->f[1];
|
|
sp48.f[2] = next->f[2] - curr->f[2];
|
|
|
|
sp34.f[0] = sp48.f[1] * sp60.f[2] - sp48.f[2] * sp60.f[1];
|
|
sp34.f[1] = sp48.f[2] * sp60.f[0] - sp48.f[0] * sp60.f[2];
|
|
sp34.f[2] = sp48.f[0] * sp60.f[1] - sp48.f[1] * sp60.f[0];
|
|
|
|
tmp = sp34.f[0] * sp34.f[0] + sp34.f[1] * sp34.f[1] + sp34.f[2] * sp34.f[2];
|
|
|
|
if (tmp == 0.0f) {
|
|
return PORTALINTERSECTION_NONE;
|
|
}
|
|
|
|
sp40[0] = sp34.f[0] * curr->f[0] + sp34.f[1] * curr->f[1] + sp34.f[2] * curr->f[2];
|
|
tmp = sp34.f[0] * pos1->f[0] + sp34.f[1] * pos1->f[1] + sp34.f[2] * pos1->f[2];
|
|
|
|
if (tmp < sp40[0]) {
|
|
if (lastside == 2) {
|
|
return PORTALINTERSECTION_NONE;
|
|
}
|
|
|
|
lastside = 1;
|
|
} else {
|
|
if (lastside == 1) {
|
|
return PORTALINTERSECTION_NONE;
|
|
}
|
|
|
|
lastside = 2;
|
|
}
|
|
|
|
curr++;
|
|
next++;
|
|
}
|
|
|
|
return (value1 < (g_PortalMetrics + portalnum)->min)
|
|
? PORTALINTERSECTION_BEHINDTOFRONT
|
|
: PORTALINTERSECTION_FRONTTOBEHIND;
|
|
}
|
|
|
|
/**
|
|
* Given frompos, fromrooms and a topos,
|
|
* use portals to figure out the new rooms list.
|
|
*
|
|
* The final rooms are written to results1,
|
|
* which should only ever have 0 or 1 room in it.
|
|
*
|
|
* The caller may optionally pass results2 and its length,
|
|
* which will be populated with all traversed rooms.
|
|
*
|
|
* This is overengineered. It allows a room to have multiple
|
|
* *overlapping* portals. This is dumb and never happens.
|
|
*/
|
|
void portal_find_rooms(struct coord *frompos, struct coord *topos, RoomNum *fromrooms, RoomNum *finalrooms, RoomNum *intersecting, s32 maxintersecting)
|
|
{
|
|
s32 i;
|
|
s32 j;
|
|
s32 roomnum;
|
|
s32 numportals;
|
|
s16 *portalnums;
|
|
RoomNum srcrooms[16];
|
|
RoomNum foundrooms[16];
|
|
RoomNum allrooms[16];
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
srcrooms[i] = fromrooms[i];
|
|
allrooms[i] = fromrooms[i];
|
|
|
|
if (fromrooms[i] == -1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
var8005ef20++;
|
|
|
|
if (var8005ef20 == 255) {
|
|
for (i = 0; i < g_BgNumPortalCameraCacheItems; i++) {
|
|
var8009a4e0[i][0] = 0xff;
|
|
}
|
|
|
|
var8005ef20 = 0;
|
|
}
|
|
|
|
// Iterate outwards
|
|
do {
|
|
foundrooms[0] = -1;
|
|
|
|
// Iterate source rooms
|
|
for (j = 0; (roomnum = srcrooms[j]) != -1 && j < 16; j++) {
|
|
numportals = g_Rooms[roomnum].numportals;
|
|
portalnums = &g_RoomPortals[g_Rooms[roomnum].roomportallistoffset];
|
|
|
|
// Iterate this source room's portals
|
|
for (i = 0; i < numportals; i++) {
|
|
s32 portalnum = *portalnums;
|
|
u8 *s1 = var8009a4e0[portalnum];
|
|
|
|
if (s1[0] != var8005ef20) {
|
|
s1[0] = var8005ef20;
|
|
s1[1] = portal_calculate_intersection(portalnum, frompos, topos);
|
|
}
|
|
|
|
if (s1[1] != PORTALINTERSECTION_NONE) {
|
|
if (s1[1] == PORTALINTERSECTION_BEHINDTOFRONT) {
|
|
if (roomnum == g_BgPortals[portalnum].roomnum1) {
|
|
portal_append_room(foundrooms, g_BgPortals[portalnum].roomnum2);
|
|
portal_append_room(allrooms, g_BgPortals[portalnum].roomnum2);
|
|
s1[1] = PORTALINTERSECTION_NONE;
|
|
}
|
|
}
|
|
|
|
if (s1[1] == PORTALINTERSECTION_FRONTTOBEHIND) {
|
|
if (roomnum == g_BgPortals[portalnum].roomnum2) {
|
|
portal_append_room(foundrooms, g_BgPortals[portalnum].roomnum1);
|
|
portal_append_room(allrooms, g_BgPortals[portalnum].roomnum1);
|
|
s1[1] = PORTALINTERSECTION_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
portalnums++;
|
|
}
|
|
}
|
|
|
|
if (foundrooms[0] == -1) {
|
|
break;
|
|
}
|
|
|
|
// Replace srcrooms with the newly found rooms so the newly
|
|
// found rooms are used as the source on the next iteration.
|
|
for (i = 0; i < 16; i++) {
|
|
srcrooms[i] = foundrooms[i];
|
|
|
|
if (foundrooms[i] == -1) {
|
|
break;
|
|
}
|
|
}
|
|
} while (foundrooms[0] != -1);
|
|
|
|
for (i = 0; i < 7 && srcrooms[i] != -1; i++) {
|
|
finalrooms[i] = srcrooms[i];
|
|
}
|
|
|
|
finalrooms[i] = -1;
|
|
|
|
if (intersecting != NULL) {
|
|
for (i = 0; i < maxintersecting; i++) {
|
|
intersecting[i] = allrooms[i];
|
|
|
|
if (allrooms[i] == -1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
intersecting[i] = -1;
|
|
}
|
|
}
|