#include "legopathboundary.h" #include "decomp.h" #include "geom/legoorientededge.h" #include "legoanimpresenter.h" #include "legopathactor.h" #include "legopathstruct.h" #include 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; }