A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectHanachan.cc
1#include "ObjectHanachan.hh"
2
3#include "game/field/ObjectDirector.hh"
4
5namespace Field {
6
8HanachanChainManager::HanachanChainManager(const std::span<const f32> &linkDistances) {
9 size_t count = linkDistances.size() + 1;
10
11 m_links = std::span(new SphereLink[count], count);
12 m_links[0].initLinkLen(linkDistances[0]);
13
14 for (size_t i = 1; i < count - 1; ++i) {
15 m_links[i].initLinkLen(linkDistances[i]);
16 m_links[i - 1].setNext(&m_links[i]);
17 m_links[i].setPrev(&m_links[i - 1]);
18 }
19
20 auto &last = m_links.back();
21 auto &secondToLast = m_links[count - 2];
22 last.initLinkLen(0.0f);
23 secondToLast.setNext(&last);
24 last.setPrev(&secondToLast);
25}
26
28HanachanChainManager::~HanachanChainManager() {
29 delete[] m_links.data();
30}
31
33void HanachanChainManager::calc() {
34 for (u32 i = 0; i < 2; ++i) {
35 for (auto &link : m_links) {
36 link.calc();
37 }
38
39 for (auto &link : m_links) {
40 link.calcPos();
41 }
42 }
43
44 for (size_t i = 0; i < m_links.size() - 1; ++i) {
45 EGG::Vector3f delta = m_links[i].pos() - m_links[i + 1].pos();
46 f32 len = m_links[i].linkLen();
47
48 if (delta.squaredLength() - len * len > 0.0f) {
49 calcConstraints();
50 break;
51 }
52 }
53
54 for (auto &link : m_links) {
55 link.checkCollision();
56 }
57}
58
60void ObjectHanachanPart::calcTransformFromUpAndTangent(const EGG::Vector3f &pos,
61 const EGG::Vector3f &up, const EGG::Vector3f &tangent) {
63 SetRotTangentHorizontal(mat, up, tangent);
64 m_flags.setBit(eFlags::Matrix);
65 m_transform = mat;
66 m_transform.setBase(3, pos);
67 m_pos = pos;
68}
69
71ObjectHanachanHead::ObjectHanachanHead(const char *name, const EGG::Vector3f &pos,
72 const EGG::Vector3f &rot, const EGG::Vector3f &scale)
73 : ObjectHanachanPart(name, pos, rot, scale), m_lastPos(EGG::Vector3f::zero) {}
74
76ObjectHanachanHead::~ObjectHanachanHead() = default;
77
79void ObjectHanachanHead::calcCollisionTransform() {
80 calcTransform();
81
82 EGG::Matrix34f trans = m_transform;
83 EGG::Vector3f scaledUp = trans.base(1) * 330.0f * m_scale.y;
84 EGG::Vector3f scaledForward = trans.base(2) * 100.0f * m_scale.y;
85 trans.setBase(3, m_pos + scaledUp + scaledForward);
86 EGG::Vector3f speed = m_pos - m_lastPos;
87
88 m_collision->transform(trans, m_scale, speed);
89 m_lastPos = m_pos;
90}
91
92ObjectHanachanBody::ObjectHanachanBody(const System::MapdataGeoObj &params, const char *mdlName)
93 : ObjectHanachanPart(params), m_mdlName(mdlName), m_lastSegment(false),
94 m_lastPos(EGG::Vector3f::zero) {}
95
96ObjectHanachanBody::ObjectHanachanBody(const char *name, const EGG::Vector3f &pos,
97 const EGG::Vector3f &rot, const EGG::Vector3f &scale, const char *mdlName)
98 : ObjectHanachanPart(name, pos, rot, scale), m_mdlName(mdlName), m_lastSegment(false),
99 m_lastPos(EGG::Vector3f::zero) {}
100
102ObjectHanachanBody::~ObjectHanachanBody() = default;
103
105void ObjectHanachanBody::calcCollisionTransform() {
106 if (!m_lastSegment) {
107 ObjectCollidable::calcCollisionTransform();
108 return;
109 }
110
111 EGG::Matrix34f trans = m_transform;
112 trans.setBase(3, m_pos + trans.base(2) * 80.0f * m_scale.y);
113 EGG::Vector3f posDelta = m_pos - m_lastPos;
114
115 m_collision->transform(trans, m_scale, posDelta);
116 m_lastPos = m_pos;
117}
118
120ObjectHanachan::ObjectHanachan(const System::MapdataGeoObj &params)
121 : ObjectCollidable(params), StateManager(this, STATE_ENTRIES), m_chain(BODY_PART_DISTANCES),
122 m_movingVel(static_cast<f32>(static_cast<s16>(params.setting(0)))) {
123 constexpr f32 SCALE = 3.0f;
124 constexpr EGG::Vector3f SCALE_VEC = EGG::Vector3f(SCALE, SCALE, SCALE);
125
126 auto *&head = headPart();
127 head = new ObjectHanachanHead("BossHanachanHead", EGG::Vector3f::zero, EGG::Vector3f::ez,
128 EGG::Vector3f::ez);
129 head->setScale(SCALE_VEC);
130
131 const auto &mdlNames = ObjectHanachanBody::MDL_NAMES;
132 size_t mdlNameCount = ObjectHanachanBody::MDL_NAMES.size();
133 auto parts = bodyParts();
134 for (size_t i = 0; i < parts.size(); ++i) {
135 auto *&part = parts[i];
136 part = new ObjectHanachanBody(params, mdlNames[i % mdlNameCount]);
137 part->setScale(SCALE_VEC);
138 }
139
140 head->load();
141
142 const auto &flowTable = ObjectDirector::Instance()->flowTable();
143 for (size_t i = 0; i < parts.size(); ++i) {
144 auto *&part = parts[i];
145 part->load();
146
147 const auto *collisionSet = flowTable.set(flowTable.slot(part->id()));
148 f32 radius = SCALE * static_cast<f32>(parse<s16>(collisionSet->params.sphere.radius));
149 part->resize(radius, 0.0f);
150 }
151
152 m_partDisplacement[0] = 0.0f;
153 for (size_t i = 1; i < m_partDisplacement.size(); ++i) {
154 m_partDisplacement[i] = m_partDisplacement[i - 1] + BODY_PART_DISTANCES[i - 1];
155 }
156}
157
159ObjectHanachan::~ObjectHanachan() = default;
160
162void ObjectHanachan::init() {
163 m_railInterpolator->init(0.0f, 0);
164 m_railInterpolator->setCurrVel(m_movingVel);
165
166 initBody();
167
168 m_still = false;
169 m_stillAngVel = 15.0f;
170 m_leftMisalignFrame = 0;
171 m_right = EGG::Vector3f::ez;
172 m_railAlignment = RailAlignment::Unknown;
173 m_prevRailAlignment = RailAlignment::Unknown;
174
175 reinterpret_cast<ObjectHanachanBody *>(m_parts.back())->m_lastSegment = true;
176
177 initChain();
178}
179
181void ObjectHanachan::initWalk() {
182 setMovingVel();
183 m_stillAngVel = 15.0f;
184 m_still = false;
185 m_leftMisalignFrame = 0;
186}
187
189void ObjectHanachan::calcWalk() {
190 if (m_still) {
191 m_railInterpolator->setCurrVel(0.0f);
192 m_nextStateId = 1;
193 }
194
195 m_prevRailAlignment = m_railAlignment;
196 m_railAlignment = calcRailAlignment();
197
198 clearChain();
199 calcRailAlignmentMotion();
200 m_chain.calc();
201
202 m_stillAngVel = 15.0f;
203}
204
206void ObjectHanachan::calcWait() {
207 if (shouldStartMoving()) {
208 m_nextStateId = 0;
209 }
210
211 clearChain();
212
213 if (m_stillAngVel >= 0.0f) {
214 m_stillAngVel -= 0.25f;
215 } else {
216 m_stillAngVel += 0.25f;
217 }
218
219 if (EGG::Mathf::abs(m_stillAngVel) <= 1.0f) {
220 m_stillAngVel = 0.0f;
221 }
222
223 calcSlowMotion();
224
225 m_chain.calc();
226}
227
229void ObjectHanachan::onSegmentEnd() {
230 u16 setting = m_railInterpolator->curPoint().setting[0];
231 if (setting != 0) {
232 m_still = true;
233 m_stillDuration = setting;
234 }
235}
236
238void ObjectHanachan::calcRail() {
239 m_right = m_railInterpolator->curTangentDir();
240
241 if (m_railInterpolator->calc() == RailInterpolator::Status::SegmentEnd) {
242 onSegmentEnd();
243 }
244}
245
247void ObjectHanachan::calcBody() {
248 auto *&head = headPart();
249 head->calcTransform();
250 EGG::Vector3f forward = head->m_transform.base(2);
251 EGG::Vector3f dir = Interpolate(0.1f, forward, m_railInterpolator->curTangentDir());
252 head->calcTransformFromUpAndTangent(m_chain.pos(0), m_chain.up(0), dir);
253
254 auto parts = bodyParts();
255 for (size_t i = 0; i < parts.size(); ++i) {
256 const EGG::Vector3f &pos = m_chain.pos(i + 1);
257 dir = m_chain.pos(i) - pos;
258 dir.normalise2();
259 parts[i]->calcTransformFromUpAndTangent(pos, m_chain.up(i + 1), dir);
260 }
261}
262
264void ObjectHanachan::initBody() {
265 headPart()->setPos(m_railInterpolator->curPos());
266
267 const EGG::Vector3f &curTanDir = m_railInterpolator->curTangentDir();
268 for (size_t i = 1; i < m_parts.size(); ++i) {
269 m_parts[i]->setPos(m_parts[i - 1]->pos() - curTanDir * BODY_PART_DISTANCES[i - 1]);
270 m_parts[i]->setMatrixTangentTo(EGG::Vector3f::ey, curTanDir);
271 }
272}
273
275void ObjectHanachan::initChain() {
276 m_chain.init();
277
278 for (size_t i = 0; i < m_parts.size(); ++i) {
279 m_chain.setPos(i, m_parts[i]->pos());
280 }
281}
282
284void ObjectHanachan::clearChain() {
285 m_chain.setPos(0, m_railInterpolator->curPos());
286 m_chain.setVel(0, EGG::Vector3f::zero);
287 m_chain.addSpringForce(0, EGG::Vector3f::ey * SphereLink::Gravity());
288}
289
293void ObjectHanachan::calcRailAlignmentMotion() {
294 constexpr u16 MISALIGNMENT_CORRECTION_DURATION = 80;
295
296 bool becameUnaligned = m_prevRailAlignment == RailAlignment::Aligned &&
297 m_railAlignment != RailAlignment::Aligned;
298
299 if (becameUnaligned && m_railAlignment == RailAlignment::MisalignedLeft) {
300 m_leftMisalignFrame = m_currentFrame;
301 }
302
303 if (m_leftMisalignFrame != 0 && m_currentFrame >= m_leftMisalignFrame &&
304 m_currentFrame <
305 static_cast<u32>(m_leftMisalignFrame + MISALIGNMENT_CORRECTION_DURATION)) {
306 calcFastMotion(m_currentFrame - m_leftMisalignFrame);
307 } else {
308 calcSlowMotion();
309 }
310}
311
313void ObjectHanachan::calcLateralMotion(f32 amplitude, f32 period, f32 wavelength, s16 frame) {
314 f32 velAmplitude = F_TAU * amplitude / period;
315
316 auto parts = bodyParts();
317 for (size_t i = 0; i < parts.size(); ++i) {
318 auto *&part = parts[i];
319 f32 phase =
320 F_TAU * (static_cast<f32>(frame) / period - m_partDisplacement[i + 1] / wavelength);
321 part->calcTransform();
322 EGG::Vector3f right = RotateXZByYaw(HALF_PI, part->m_transform.base(2));
323 EGG::Vector3f posOffset = right * (amplitude * EGG::Mathf::SinFIdx(RAD2FIDX * phase));
324 m_chain.setPos(i + 1, m_parts[i + 1]->pos() + posOffset);
325 m_chain.setVel(i + 1, right * (velAmplitude * EGG::Mathf::CosFIdx(RAD2FIDX * phase)));
326 }
327}
328
330ObjectHanachan::RailAlignment ObjectHanachan::calcRailAlignment() const {
331 constexpr f32 EPSILON = 0.9995f;
332
333 EGG::Vector3f curTanDir = m_railInterpolator->curTangentDir();
334 EGG::Vector2f railTan = EGG::Vector2f(curTanDir.x, curTanDir.z);
335 EGG::Vector2f right = EGG::Vector2f(m_right.x, m_right.z);
336 railTan.normalise2();
337 right.normalise2();
338
339 if (railTan.dot(right) >= EPSILON) {
340 return RailAlignment::Aligned;
341 }
342
343 return right.cross(railTan) < 0.0f ? RailAlignment::MisalignedLeft :
344 RailAlignment::MisalignedRight;
345}
346
347} // namespace Field
A 3 x 4 matrix.
Definition Matrix.hh:8
void setBase(size_t col, const Vector3f &base)
Sets one column of a matrix.
Definition Matrix.cc:194
Vector3f base(size_t col) const
Get a particular column from a matrix.
Definition Matrix.hh:72
EGG core library.
Definition Archive.cc:6
Pertains to collision.
constexpr TBitFlag< T, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
Definition BitFlag.hh:62
A 2D float vector.
Definition Vector.hh:12
A 3D float vector.
Definition Vector.hh:88
f32 squaredLength() const
The dot product between the vector and itself.
Definition Vector.hh:182