isle-portable/LEGO1/lego/legoomni/src/paths/legopathboundary.cpp
Christian Semmler a3122cd209
Some checks are pending
CI / clang-format (push) Waiting to run
CI / ${{ matrix.name }} (false, --toolchain /usr/local/vitasdk/share/vita.toolchain.cmake, false, false, Ninja, Vita, ubuntu-latest, true, true) (push) Waiting to run
CI / ${{ matrix.name }} (false, -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.26100.0, false, false, Visual Studio 17 2022, true, Xbox One, windows-latest, amd64, false, true) (push) Waiting to run
CI / ${{ matrix.name }} (false, -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/3DS.cmake, false, devkitpro/devkitarm:latest, false, Ninja, true, Nintendo 3DS, ubuntu-latest, true) (push) Waiting to run
CI / ${{ matrix.name }} (false, -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake, false, devkitpro/devkita64:latest, false, Ninja, Nintendo Switch, true, ubuntu-latest, true) (push) Waiting to run
CI / ${{ matrix.name }} (false, emcmake, false, false, true, Ninja, Emscripten, ubuntu-latest, true) (push) Waiting to run
CI / ${{ matrix.name }} (false, false, false, Ninja, true, MSVC (arm64), windows-latest, amd64_arm64, false) (push) Waiting to run
CI / ${{ matrix.name }} (false, false, true, Ninja, true, MSVC (x86), windows-latest, amd64_x86, false) (push) Waiting to run
CI / ${{ matrix.name }} (false, true, false, Ninja, true, MSVC (x64), windows-latest, amd64, false) (push) Waiting to run
CI / ${{ matrix.name }} (false, true, true, false, Ninja, true, MSVC (x64 Debug), windows-latest, amd64, false) (push) Waiting to run
CI / ${{ matrix.name }} (true, false, -DCMAKE_SYSTEM_NAME=iOS, false, false, Xcode, true, iOS, macos-15, true) (push) Waiting to run
CI / ${{ matrix.name }} (true, false, false, Ninja, true, mingw-w64-i686, mingw32, msys2 mingw32, windows-latest, msys2 {0}, true) (push) Waiting to run
CI / ${{ matrix.name }} (true, false, false, false, Ninja, Android, ubuntu-latest, true) (push) Waiting to run
CI / ${{ matrix.name }} (true, false, true, false, Ninja, macOS, macos-latest, true) (push) Waiting to run
CI / ${{ matrix.name }} (true, true, false, Ninja, true, mingw-w64-x86_64, mingw64, msys2 mingw64, windows-latest, msys2 {0}, true) (push) Waiting to run
CI / ${{ matrix.name }} (true, true, true, false, Ninja, true, Linux (Debug), ubuntu-latest, true) (push) Waiting to run
CI / ${{ matrix.name }} (true, true, true, false, Ninja, true, Linux, ubuntu-latest, true) (push) Waiting to run
CI / Flatpak (${{ matrix.arch }}) (aarch64, ubuntu-22.04-arm) (push) Waiting to run
CI / Flatpak (${{ matrix.arch }}) (x86_64, ubuntu-latest) (push) Waiting to run
CI / C++ (push) Waiting to run
CI / Release (push) Blocked by required conditions
Docker / Publish web port (push) Waiting to run
Merge remote-tracking branch 'isle/master'
2026-01-30 17:06:10 -08:00

381 lines
9.4 KiB
C++

#include "legopathboundary.h"
#include "decomp.h"
#include "geom/legoorientededge.h"
#include "legoanimpresenter.h"
#include "legopathactor.h"
#include "legopathstruct.h"
#include <SDL3/SDL_log.h>
DECOMP_SIZE_ASSERT(LegoPathBoundary, 0x74)
// FUNCTION: LEGO1 0x10056a70
// FUNCTION: BETA10 0x100b1360
LegoPathBoundary::LegoPathBoundary()
{
}
// FUNCTION: LEGO1 0x10057260
// FUNCTION: BETA10 0x100b140d
LegoPathBoundary::~LegoPathBoundary()
{
for (LegoPathActorSet::iterator it = m_actors.begin(); !(it == m_actors.end()); it++) {
(*it)->SetBoundary(NULL);
}
m_actors.erase(m_actors.begin(), m_actors.end());
}
// FUNCTION: LEGO1 0x100573f0
// FUNCTION: BETA10 0x100b1536
MxResult LegoPathBoundary::AddActor(LegoPathActor* p_actor)
{
m_actors.insert(p_actor);
p_actor->SetBoundary(this);
return SUCCESS;
}
// FUNCTION: LEGO1 0x100574a0
// FUNCTION: BETA10 0x100b156f
MxResult LegoPathBoundary::RemoveActor(LegoPathActor* p_actor)
{
m_actors.erase(p_actor);
return SUCCESS;
}
// FUNCTION: LEGO1 0x100575b0
// FUNCTION: BETA10 0x100b1598
void LegoPathBoundary::CheckAndCallPathTriggers(Vector3& p_from, Vector3& p_to, LegoPathActor* p_actor)
{
Vector3* ccwV = NULL;
if (m_numTriggers > 0 && m_direction != NULL) {
ccwV = m_edges[0]->CCWVertex(*this);
Mx3DPointFloat v;
v = p_from;
v -= *ccwV;
float dot1 = v.Dot(v, *m_direction);
v = p_to;
v -= *ccwV;
float dot2 = v.Dot(v, *m_direction);
if (dot2 > dot1) {
for (MxS32 i = 0; i < m_numTriggers; i++) {
LegoPathStruct* s = m_pathTrigger[i].m_pathStruct;
if (m_pathTrigger[i].m_triggerLength >= dot1 && m_pathTrigger[i].m_triggerLength < dot2) {
s->HandleTrigger(p_actor, TRUE, m_pathTrigger[i].m_data);
}
}
}
else if (dot2 < dot1) {
for (MxS32 i = 0; i < m_numTriggers; i++) {
LegoPathStruct* s = m_pathTrigger[i].m_pathStruct;
if (m_pathTrigger[i].m_triggerLength >= dot2 && m_pathTrigger[i].m_triggerLength < dot1) {
s->HandleTrigger(p_actor, FALSE, m_pathTrigger[i].m_data);
}
}
}
}
}
// FUNCTION: LEGO1 0x10057720
// FUNCTION: BETA10 0x100b17ef
void LegoPathBoundary::SwitchBoundary(
LegoPathActor* p_actor,
LegoPathBoundary*& p_boundary,
LegoOrientedEdge*& p_edge,
float& p_scale
)
{
LegoOrientedEdge* e = p_edge;
if (p_edge->BETA_100b53b0(*p_boundary)) {
LegoPathBoundary* newBoundary = (LegoPathBoundary*) p_edge->OtherFace(p_boundary);
if (newBoundary == NULL) {
newBoundary = p_boundary;
}
MxS32 availableEdgeCount = 0;
MxU8 userNavFlag;
if (e->BETA_1004a830(*newBoundary, 1)) {
userNavFlag = p_actor->GetUserNavFlag();
}
else {
userNavFlag = TRUE;
}
do {
p_edge = (LegoOrientedEdge*) p_edge->GetCounterclockwiseEdge(*newBoundary);
LegoPathBoundary* otherBoundary = (LegoPathBoundary*) p_edge->OtherFace(newBoundary);
if (p_edge->GetMask0x03() && (userNavFlag || p_edge->BETA_1004a830(*otherBoundary, 1))) {
availableEdgeCount++;
}
} while (p_edge != e);
MxBool countCounterclockwise = TRUE;
MxS32 selectedEdgeIndex = availableEdgeCount - 1;
if (availableEdgeCount <= 1) {
selectedEdgeIndex = 0;
}
else if (availableEdgeCount == 2) {
selectedEdgeIndex = 1;
}
else {
p_actor->GetWalkingBehavior(countCounterclockwise, selectedEdgeIndex);
}
while (selectedEdgeIndex > 0) {
if (countCounterclockwise) {
p_edge = (LegoOrientedEdge*) p_edge->GetCounterclockwiseEdge(*newBoundary);
}
else {
p_edge = (LegoOrientedEdge*) p_edge->GetClockwiseEdge(*newBoundary);
}
LegoPathBoundary* otherBoundary = (LegoPathBoundary*) p_edge->OtherFace(newBoundary);
if (p_edge->GetMask0x03() && (userNavFlag || p_edge->BETA_1004a830(*otherBoundary, 1))) {
selectedEdgeIndex--;
}
}
if (p_edge == e) {
p_edge = (LegoOrientedEdge*) p_edge->GetCounterclockwiseEdge(*newBoundary);
p_edge = (LegoOrientedEdge*) p_edge->GetCounterclockwiseEdge(*newBoundary);
}
if (p_boundary != newBoundary) {
p_boundary->RemoveActor(p_actor);
p_boundary = newBoundary;
p_boundary->AddActor(p_actor);
}
else {
p_scale = 1.0 - p_scale;
}
}
else {
do {
p_edge = (LegoOrientedEdge*) p_edge->GetCounterclockwiseEdge(*p_boundary);
if (p_edge->GetMask0x03()) {
break;
}
} while (p_edge != e);
if (p_edge == e) {
p_edge = (LegoOrientedEdge*) p_edge->GetCounterclockwiseEdge(*p_boundary);
p_edge = (LegoOrientedEdge*) p_edge->GetCounterclockwiseEdge(*p_boundary);
}
p_scale = 1.0 - p_scale;
}
}
// FUNCTION: LEGO1 0x10057950
// FUNCTION: BETA10 0x100b1adc
MxU32 LegoPathBoundary::Intersect(
float p_scale,
Vector3& p_oldPos,
Vector3& p_newPos,
Vector3& p_intersectionPoint,
LegoOrientedEdge*& p_edge
)
{
LegoOrientedEdge* e = NULL;
float minHitDistance;
MxU32 normalizedCalculated = 0;
float len = 0.0f;
Mx3DPointFloat direction;
for (MxS32 i = 0; i < m_numEdges; i++) {
LegoOrientedEdge* edge = (LegoOrientedEdge*) m_edges[i];
if (p_newPos.Dot(m_edgeNormals[i], p_newPos) + m_edgeNormals[i][3] <= -1e-07) {
if (normalizedCalculated == FALSE) {
normalizedCalculated = TRUE;
direction = p_newPos;
direction -= p_oldPos;
len = direction.LenSquared();
if (len <= 0.0f) {
return 0;
}
len = sqrt(len);
direction /= len;
}
float dot = direction.Dot(direction, m_edgeNormals[i]);
if (dot != 0.0f) {
float hitDistance = (-m_edgeNormals[i][3] - p_oldPos.Dot(p_oldPos, m_edgeNormals[i])) / dot;
if (hitDistance >= -0.001 && hitDistance <= len && (e == NULL || hitDistance < minHitDistance)) {
e = edge;
minHitDistance = hitDistance;
}
}
}
}
if (e != NULL) {
if (minHitDistance < 0.0f) {
minHitDistance = 0.0f;
}
Mx3DPointFloat startToPosition;
Mx3DPointFloat edgeNormal;
Vector3* start = e->CWVertex(*this);
p_intersectionPoint = direction;
p_intersectionPoint *= minHitDistance;
p_intersectionPoint += p_oldPos;
startToPosition = p_newPos;
startToPosition -= *start;
e->GetFaceNormal(*this, edgeNormal);
float projection = startToPosition.Dot(startToPosition, edgeNormal);
LegoOrientedEdge* candidateEdge = NULL;
if (projection < 0.0f) {
Mx3DPointFloat candidateEdgeNormal;
for (LegoOrientedEdge* cwEdge = (LegoOrientedEdge*) e->GetClockwiseEdge(*this); e != cwEdge;
cwEdge = (LegoOrientedEdge*) cwEdge->GetClockwiseEdge(*this)) {
cwEdge->GetFaceNormal(*this, candidateEdgeNormal);
if (candidateEdgeNormal.Dot(candidateEdgeNormal, edgeNormal) <= 0.9) {
break;
}
Vector3* candidateStart = cwEdge->CWVertex(*this);
Mx3DPointFloat candidateToIntersection(p_intersectionPoint);
candidateToIntersection -= *candidateStart;
float candidateProjection = candidateToIntersection.Dot(candidateToIntersection, candidateEdgeNormal);
if (candidateProjection > projection && candidateProjection < cwEdge->m_length) {
candidateEdge = cwEdge;
projection = candidateProjection;
edgeNormal = candidateEdgeNormal;
start = candidateStart;
}
}
}
else {
if (e->m_length < projection) {
Mx3DPointFloat candidateEdgeNormal;
for (LegoOrientedEdge* ccwEdge = (LegoOrientedEdge*) e->GetCounterclockwiseEdge(*this); e != ccwEdge;
ccwEdge = (LegoOrientedEdge*) ccwEdge->GetCounterclockwiseEdge(*this)) {
ccwEdge->GetFaceNormal(*this, candidateEdgeNormal);
if (candidateEdgeNormal.Dot(candidateEdgeNormal, edgeNormal) <= 0.9) {
break;
}
Vector3* candidateStart = ccwEdge->CWVertex(*this);
Mx3DPointFloat candidateToIntersection(p_intersectionPoint);
candidateToIntersection -= *candidateStart;
float candidateProjection =
candidateToIntersection.Dot(candidateToIntersection, candidateEdgeNormal);
if (candidateProjection < projection && candidateProjection >= 0.0f) {
candidateEdge = ccwEdge;
projection = candidateProjection;
edgeNormal = candidateEdgeNormal;
start = candidateStart;
}
}
}
}
if (candidateEdge != NULL) {
e = candidateEdge;
}
if (projection <= 0.0f) {
if (!e->GetMask0x03()) {
p_edge = (LegoOrientedEdge*) e->GetClockwiseEdge(*this);
}
else {
p_edge = e;
}
p_intersectionPoint = *start;
return 2;
}
else if (projection > 0.0f && e->m_length > projection) {
p_intersectionPoint = edgeNormal;
p_intersectionPoint *= projection;
p_intersectionPoint += *start;
p_edge = e;
return 1;
}
else {
p_intersectionPoint = *e->CCWVertex(*this);
if (!e->GetMask0x03()) {
p_edge = (LegoOrientedEdge*) e->GetCounterclockwiseEdge(*this);
}
else {
p_edge = e;
}
return 2;
}
}
return 0;
}
// FUNCTION: LEGO1 0x10057fe0
// FUNCTION: BETA10 0x100b2220
MxU32 LegoPathBoundary::AddPresenterIfInRange(LegoAnimPresenter* p_presenter)
{
Mx3DPointFloat centerDistance;
centerDistance = m_centerPoint;
centerDistance -= p_presenter->m_centerPoint;
float len = centerDistance.LenSquared();
float radiusSquared = p_presenter->m_boundingRadius + m_boundingRadius;
if (len > 0.001 && len > radiusSquared * radiusSquared) {
return 0;
}
m_presenters.insert(p_presenter);
return 1;
}
// FUNCTION: LEGO1 0x100586e0
// FUNCTION: BETA10 0x100b22d1
MxU32 LegoPathBoundary::RemovePresenter(LegoAnimPresenter* p_presenter)
{
if (p_presenter != NULL) {
if (m_presenters.find(p_presenter) != m_presenters.end()) {
m_presenters.erase(p_presenter);
return 1;
}
}
else {
for (LegoAnimPresenterSet::iterator it = m_presenters.begin(); it != m_presenters.end(); it++) {
(*it)->SetCurrentWorld(NULL);
}
}
return 0;
}