A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
KartPullPath.cc
1#include "KartPullPath.hh"
2
3#include "game/system/CourseMap.hh"
4
5#include <egg/geom/Plane.hh>
6
7namespace Kart {
8
10KartPullPathTracker::KartPullPathTracker(KartPullPath *handle, Type type)
11 : m_type(type), m_currentIdx(0), m_pointInfo(nullptr), m_handle(handle) {}
12
14KartPullPathTracker::~KartPullPathTracker() = default;
15
16void KartPullPathTracker::calc() {
17 ASSERT(m_pointInfo);
18
19 if (m_type == Type::Global) {
20 calcTrackerGlobal();
21 } else if (m_type == Type::Regional) {
22 calcTrackerRegional();
23 }
24}
25
27void KartPullPathTracker::calcTrackerGlobal() {
28 s16 idx;
29 EGG::Vector3f point, dir;
30 if (search(SearchDirection::Current, idx, point, dir)) {
31 m_handle->changePoint(idx, getDistance(point, dir));
32 }
33
34 if (static_cast<size_t>(++m_currentIdx) >= m_pointInfo->pointCount() - 1) {
35 m_currentIdx = 0;
36 }
37}
38
40void KartPullPathTracker::calcTrackerRegional() {
41 if (m_handle->incomingIdx() < 0) {
42 return;
43 }
44
45 s16 idx;
46 EGG::Vector3f point, dir;
47
48 if (search(SearchDirection::Current, idx, point, dir)) {
49 m_handle->changePoint(idx, getDistance(point, dir));
50 return;
51 }
52
53 m_handle->resetDistance();
54 if (search(SearchDirection::Next, idx, point, dir)) {
55 m_handle->changePoint(idx, getDistance(point, dir));
56 return;
57 }
58
59 if (search(SearchDirection::Previous, idx, point, dir)) {
60 m_handle->changePoint(idx, getDistance(point, dir));
61 }
62}
63
68f32 KartPullPathTracker::getDistance(const EGG::Vector3f &point, const EGG::Vector3f &dir) const {
69 EGG::Vector3f diff = pos() - point;
70 f32 dist = diff.length();
71 f32 x = EGG::Mathf::abs(diff.dot(dir));
72 return EGG::Mathf::sqrt(dist * dist - x * x);
73}
74
76bool KartPullPathTracker::search(SearchDirection searchDirection, s16 &idx, EGG::Vector3f &point,
77 EGG::Vector3f &dir) const {
78 ASSERT(m_pointInfo);
79
80 s16 p, c, n;
81 switch (searchDirection) {
82 case SearchDirection::Current:
83 idx = m_currentIdx;
84 p = -1;
85 c = 0;
86 n = 1;
87 break;
88 case SearchDirection::Next:
89 idx = m_currentIdx + 1;
90 p = 0;
91 c = 1;
92 n = 2;
93 break;
94 case SearchDirection::Previous:
95 idx = m_currentIdx - 1;
96 p = -2;
97 c = -1;
98 n = 0;
99 break;
100 default:
101 PANIC("Invalid search direction!");
102 break;
103 }
104
105 const auto &points = m_pointInfo->points();
106
107 // There always needs to be a current point and a next point to get the direction
108 if (m_currentIdx + c < 0 || static_cast<size_t>(m_currentIdx + c) >= points.size()) {
109 return false;
110 }
111
112 if (m_currentIdx + n < 0 || static_cast<size_t>(m_currentIdx + n) >= points.size()) {
113 return false;
114 }
115
116 const auto &currentPos = points[m_currentIdx + c].pos;
117 const auto &nextPos = points[m_currentIdx + n].pos;
118
119 EGG::Vector3f back = currentPos - nextPos;
120 back.normalise();
121 EGG::Vector3f front = -back;
122
123 if (m_currentIdx + p >= 0 && static_cast<size_t>(m_currentIdx + p) < points.size()) {
124 const auto &prevPos = points[m_currentIdx + p].pos;
125 back = prevPos - currentPos;
126 back.normalise();
127 }
128
129 EGG::Plane3f backPlane = EGG::Plane3f(currentPos, back);
130 EGG::Plane3f frontPlane = EGG::Plane3f(nextPos, front);
131
132 if (backPlane.testPoint(pos()) && frontPlane.testPoint(pos())) {
133 point = currentPos;
134 dir = front;
135 return true;
136 }
137
138 return false;
139}
140
142KartPullPath::KartPullPath()
143 : m_globalTracker(this, KartPullPathTracker::Type::Global),
144 m_regionalTracker(this, KartPullPathTracker::Type::Regional) {
145 init();
146}
147
149KartPullPath::~KartPullPath() = default;
150
152void KartPullPath::init() {
153 reset();
154 m_areaId = -1;
155 m_roadSpeedDecay = 1.0f;
156}
157
161 m_distance = -1.0f;
162 m_currentIdx = -1;
163 m_incomingIdx = -1;
164 m_pointInfo = nullptr;
165 m_pullDirection = EGG::Vector3f::zero;
166 m_pullSpeed = 0.0f;
167 m_maxPullSpeed = 0.0f;
168}
169
171void KartPullPath::calc() {
172 if (!calcArea()) {
173 return;
174 }
175
176 ASSERT(m_pointInfo);
177 if (m_pointInfo->pointCount() > 2) {
178 calcTrackers();
179 }
180}
181
183void KartPullPath::changePoint(s16 idx, f32 distance) {
184 if ((m_distance >= 0.0f || distance >= 3000.0f) && distance >= m_distance) {
185 return;
186 }
187
188 m_incomingIdx = idx;
189 m_distance = distance;
190 m_regionalTracker.setCurrentIdx(idx);
191 calcPointChange();
192}
193
195bool KartPullPath::calcArea() {
196 auto *courseMap = System::CourseMap::Instance();
197
198 s16 prevAreaId = m_areaId;
199 m_areaId = System::CourseMap::Instance()->getCurrentAreaID(m_areaId, pos(),
200 System::MapdataAreaBase::Type::MovingRoad);
201
202 if (m_areaId >= 0) {
203 if (prevAreaId < 0 || prevAreaId != m_areaId) {
204 reset();
205 auto *area = courseMap->getArea(m_areaId);
206 ASSERT(area);
207 auto *pointInfo = area->getPointInfo();
208 ASSERT(pointInfo);
209 m_pointInfo = pointInfo;
210 if (pointInfo->pointCount() > 2) {
211 setTrackerPointInfo(pointInfo);
212 } else {
213 m_incomingIdx = 0;
214 calcPointChange();
215 }
216 m_roadSpeedDecay = 0.9f + 0.001f * static_cast<f32>(area->param(0));
217 m_maxPullSpeed = static_cast<f32>(area->param(1));
218 }
219 } else {
220 m_areaId = -1;
221 m_currentIdx = -1;
222 m_incomingIdx = -1;
223 m_pullDirection = EGG::Vector3f::zero;
224 }
225
226 return m_areaId >= 0;
227}
228
230void KartPullPath::calcPointChange() {
231 if (m_currentIdx == m_incomingIdx) {
232 return;
233 }
234
235 const auto &points = m_pointInfo->points();
236
237 const auto &currentPos = points[m_incomingIdx].pos;
238 const auto &nextPos = points[m_incomingIdx + 1].pos;
239 m_pullSpeed = static_cast<f32>(points[m_incomingIdx].setting[0]);
240 u16 pullInfluence = points[m_incomingIdx + 1].setting[1];
241
242 m_pullDirection = nextPos - currentPos;
243 m_pullDirection.normalise();
244
245 // If the pull influence is 0, the pull direction moves forward, so do nothing
246 // If the pull influence is 1, the pull direction moves left
247 if (pullInfluence == 1) {
248 m_pullDirection = getPullUnitNormal() * -1.0f;
249 }
250
251 // If the pull influence is 2, the pull direction moves right
252 else if (pullInfluence == 2) {
253 m_pullDirection = getPullUnitNormal();
254 }
255
256 m_currentIdx = m_incomingIdx;
257}
258
260void KartPullPath::calcTrackers() {
261 m_globalTracker.calc();
262 m_regionalTracker.calc();
263}
264
268 // Dot product of two unit vectors being 1.0f => angle between them is 0
269 // They're the same vector, just return zero
270 if (EGG::Vector3f::ey.dot(m_pullDirection) == 1.0f) {
271 return EGG::Vector3f::zero;
272 }
273
274 EGG::Vector3f nrm = EGG::Vector3f::ey.cross(m_pullDirection);
275 nrm.normalise();
276 return nrm;
277}
278
280void KartPullPath::setTrackerPointInfo(System::MapdataPointInfo *info) {
281 m_globalTracker.setPointInfo(info);
282 m_regionalTracker.setPointInfo(info);
283}
284
285} // namespace Kart
EGG::Vector3f getPullUnitNormal() const
Pertains to kart-related functionality.
Represents a plane in 3D space, expressed with n.dot(x) = d for point x on the plane.
Definition Plane.hh:10
A 3D float vector.
Definition Vector.hh:88
f32 normalise()
Normalizes the vector and returns the original length.
Definition Vector.cc:52
f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
Definition Vector.hh:187
f32 length() const
The square root of the vector's dot product.
Definition Vector.hh:192