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 Kinoko::Field {
6
8HanachanChainManager::HanachanChainManager(const std::span<const f32> &linkDistances) {
9 size_t count = linkDistances.size() + 1;
10
11 m_links = owning_span<Field::SphereLink>(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() = default;
29
31void HanachanChainManager::calc() {
32 for (u32 i = 0; i < 2; ++i) {
33 for (auto &link : m_links) {
34 link.calc();
35 }
36
37 for (auto &link : m_links) {
38 link.calcPos();
39 }
40 }
41
42 for (size_t i = 0; i < m_links.size() - 1; ++i) {
43 EGG::Vector3f delta = m_links[i].pos() - m_links[i + 1].pos();
44 f32 len = m_links[i].linkLen();
45
46 if (delta.squaredLength() - len * len > 0.0f) {
47 calcConstraints();
48 break;
49 }
50 }
51
52 for (auto &link : m_links) {
53 link.checkCollision();
54 }
55}
56
58ObjectHanachanHead::ObjectHanachanHead(const char *name, const EGG::Vector3f &pos,
59 const EGG::Vector3f &rot, const EGG::Vector3f &scale)
60 : ObjectHanachanPart(name, pos, rot, scale), m_lastPos(EGG::Vector3f::zero) {}
61
63ObjectHanachanHead::~ObjectHanachanHead() = default;
64
66void ObjectHanachanHead::calcCollisionTransform() {
67 calcTransform();
68
69 EGG::Matrix34f trans = transform();
70 EGG::Vector3f scaledUp = trans.base(1) * 330.0f * scale().y;
71 EGG::Vector3f scaledForward = trans.base(2) * 100.0f * scale().y;
72 trans.setBase(3, pos() + scaledUp + scaledForward);
73 EGG::Vector3f speed = pos() - m_lastPos;
74
75 m_collision->transform(trans, scale(), speed);
76 m_lastPos = pos();
77}
78
79ObjectHanachanBody::ObjectHanachanBody(const System::MapdataGeoObj &params, const char *mdlName)
80 : ObjectHanachanPart(params), m_mdlName(mdlName), m_lastSegment(false),
81 m_lastPos(EGG::Vector3f::zero) {}
82
83ObjectHanachanBody::ObjectHanachanBody(const char *name, const EGG::Vector3f &pos,
84 const EGG::Vector3f &rot, const EGG::Vector3f &scale, const char *mdlName)
85 : ObjectHanachanPart(name, pos, rot, scale), m_mdlName(mdlName), m_lastSegment(false),
86 m_lastPos(EGG::Vector3f::zero) {}
87
89ObjectHanachanBody::~ObjectHanachanBody() = default;
90
92void ObjectHanachanBody::calcCollisionTransform() {
93 if (!m_lastSegment) {
94 ObjectCollidable::calcCollisionTransform();
95 return;
96 }
97
98 EGG::Matrix34f trans = transform();
99 trans.setBase(3, pos() + trans.base(2) * 80.0f * scale().y);
100 EGG::Vector3f posDelta = pos() - m_lastPos;
101
102 m_collision->transform(trans, scale(), posDelta);
103 m_lastPos = pos();
104}
105
107ObjectHanachan::ObjectHanachan(const System::MapdataGeoObj &params)
108 : ObjectCollidable(params), StateManager(this, STATE_ENTRIES), m_chain(BODY_PART_DISTANCES),
109 m_movingVel(static_cast<f32>(static_cast<s16>(params.setting(0)))) {
110 constexpr f32 SCALE = 3.0f;
111 constexpr EGG::Vector3f SCALE_VEC = EGG::Vector3f(SCALE, SCALE, SCALE);
112
113 auto *&head = headPart();
114 head = EGG::egg_new<ObjectHanachanHead>("BossHanachanHead", EGG::Vector3f::zero,
115 EGG::Vector3f::ez, EGG::Vector3f::ez);
116 head->setScale(SCALE_VEC);
117
118 const auto &mdlNames = ObjectHanachanBody::MDL_NAMES;
119 size_t mdlNameCount = ObjectHanachanBody::MDL_NAMES.size();
120 auto parts = bodyParts();
121 for (size_t i = 0; i < parts.size(); ++i) {
122 auto *&part = parts[i];
123 part = EGG::egg_new<ObjectHanachanBody>(params, mdlNames[i % mdlNameCount]);
124 part->setScale(SCALE_VEC);
125 }
126
127 head->load();
128
129 const auto &flowTable = ObjectDirector::Instance()->flowTable();
130 for (size_t i = 0; i < parts.size(); ++i) {
131 auto *&part = parts[i];
132 part->load();
133
134 const auto *collisionSet = flowTable.set(flowTable.slot(part->id()));
135 f32 radius = SCALE * static_cast<f32>(parse<s16>(collisionSet->params.sphere.radius));
136 part->resize(radius, 0.0f);
137 }
138
139 m_partDisplacement[0] = 0.0f;
140 for (size_t i = 1; i < m_partDisplacement.size(); ++i) {
141 m_partDisplacement[i] = m_partDisplacement[i - 1] + BODY_PART_DISTANCES[i - 1];
142 }
143}
144
146ObjectHanachan::~ObjectHanachan() = default;
147
149void ObjectHanachan::init() {
150 m_railInterpolator->init(0.0f, 0);
151 m_railInterpolator->setCurrVel(m_movingVel);
152
153 initBody();
154
155 m_still = false;
156 m_stillAngVel = 15.0f;
157 m_leftMisalignFrame = 0;
158 m_right = EGG::Vector3f::ez;
159 m_railAlignment = RailAlignment::Unknown;
160 m_prevRailAlignment = RailAlignment::Unknown;
161
162 reinterpret_cast<ObjectHanachanBody *>(m_parts.back())->m_lastSegment = true;
163
164 initChain();
165}
166
168void ObjectHanachan::initWalk() {
169 setMovingVel();
170 m_stillAngVel = 15.0f;
171 m_still = false;
172 m_leftMisalignFrame = 0;
173}
174
176void ObjectHanachan::calcWalk() {
177 if (m_still) {
178 m_railInterpolator->setCurrVel(0.0f);
179 m_nextStateId = 1;
180 }
181
182 m_prevRailAlignment = m_railAlignment;
183 m_railAlignment = calcRailAlignment();
184
185 clearChain();
186 calcRailAlignmentMotion();
187 m_chain.calc();
188
189 m_stillAngVel = 15.0f;
190}
191
193void ObjectHanachan::calcWait() {
194 if (shouldStartMoving()) {
195 m_nextStateId = 0;
196 }
197
198 clearChain();
199
200 if (m_stillAngVel >= 0.0f) {
201 m_stillAngVel -= 0.25f;
202 } else {
203 m_stillAngVel += 0.25f;
204 }
205
206 if (EGG::Mathf::abs(m_stillAngVel) <= 1.0f) {
207 m_stillAngVel = 0.0f;
208 }
209
210 calcSlowMotion();
211
212 m_chain.calc();
213}
214
216void ObjectHanachan::onSegmentEnd() {
217 u16 setting = m_railInterpolator->curPoint().setting[0];
218 if (setting != 0) {
219 m_still = true;
220 m_stillDuration = setting;
221 }
222}
223
225void ObjectHanachan::calcRail() {
226 m_right = m_railInterpolator->curTangentDir();
227
228 if (m_railInterpolator->calc() == RailInterpolator::Status::SegmentEnd) {
229 onSegmentEnd();
230 }
231}
232
234void ObjectHanachan::calcBody() {
235 auto *&head = headPart();
236 head->calcTransform();
237 EGG::Vector3f forward = head->transform().base(2);
238 EGG::Vector3f dir = Interpolate(0.1f, forward, m_railInterpolator->curTangentDir());
239 head->calcTransformFromUpAndTangent(m_chain.pos(0), m_chain.up(0), dir);
240
241 auto parts = bodyParts();
242 for (size_t i = 0; i < parts.size(); ++i) {
243 const EGG::Vector3f &pos = m_chain.pos(i + 1);
244 dir = m_chain.pos(i) - pos;
245 dir.normalise2();
246 parts[i]->calcTransformFromUpAndTangent(pos, m_chain.up(i + 1), dir);
247 }
248}
249
251void ObjectHanachan::initBody() {
252 headPart()->setPos(m_railInterpolator->curPos());
253
254 const EGG::Vector3f &curTanDir = m_railInterpolator->curTangentDir();
255 for (size_t i = 1; i < m_parts.size(); ++i) {
256 m_parts[i]->setPos(m_parts[i - 1]->pos() - curTanDir * BODY_PART_DISTANCES[i - 1]);
257 m_parts[i]->setMatrixTangentTo(EGG::Vector3f::ey, curTanDir);
258 }
259}
260
262void ObjectHanachan::initChain() {
263 m_chain.init();
264
265 for (size_t i = 0; i < m_parts.size(); ++i) {
266 m_chain.setPos(i, m_parts[i]->pos());
267 }
268}
269
271void ObjectHanachan::clearChain() {
272 m_chain.setPos(0, m_railInterpolator->curPos());
273 m_chain.setVel(0, EGG::Vector3f::zero);
274 m_chain.addSpringForce(0, EGG::Vector3f::ey * SphereLink::Gravity());
275}
276
281 constexpr u16 MISALIGNMENT_CORRECTION_DURATION = 80;
282
283 bool becameUnaligned = m_prevRailAlignment == RailAlignment::Aligned &&
284 m_railAlignment != RailAlignment::Aligned;
285
286 if (becameUnaligned && m_railAlignment == RailAlignment::MisalignedLeft) {
287 m_leftMisalignFrame = m_currentFrame;
288 }
289
290 if (m_leftMisalignFrame != 0 && m_currentFrame >= m_leftMisalignFrame &&
291 m_currentFrame <
292 static_cast<u32>(m_leftMisalignFrame + MISALIGNMENT_CORRECTION_DURATION)) {
293 calcFastMotion(m_currentFrame - m_leftMisalignFrame);
294 } else {
295 calcSlowMotion();
296 }
297}
298
300void ObjectHanachan::calcLateralMotion(f32 amplitude, f32 period, f32 wavelength, s16 frame) {
301 f32 velAmplitude = F_TAU * amplitude / period;
302
303 auto parts = bodyParts();
304 for (size_t i = 0; i < parts.size(); ++i) {
305 auto *&part = parts[i];
306 f32 phase =
307 F_TAU * (static_cast<f32>(frame) / period - m_partDisplacement[i + 1] / wavelength);
308 part->calcTransform();
309 EGG::Vector3f right = RotateXZByYaw(HALF_PI, part->transform().base(2));
310 EGG::Vector3f posOffset = right * (amplitude * EGG::Mathf::SinFIdx(RAD2FIDX * phase));
311 m_chain.setPos(i + 1, m_parts[i + 1]->pos() + posOffset);
312 m_chain.setVel(i + 1, right * (velAmplitude * EGG::Mathf::CosFIdx(RAD2FIDX * phase)));
313 }
314}
315
317ObjectHanachan::RailAlignment ObjectHanachan::calcRailAlignment() const {
318 constexpr f32 EPSILON = 0.9995f;
319
320 EGG::Vector3f curTanDir = m_railInterpolator->curTangentDir();
321 EGG::Vector2f railTan = EGG::Vector2f(curTanDir.x, curTanDir.z);
322 EGG::Vector2f right = EGG::Vector2f(m_right.x, m_right.z);
323 railTan.normalise2();
324 right.normalise2();
325
326 if (railTan.dot(right) >= EPSILON) {
327 return RailAlignment::Aligned;
328 }
329
330 return right.cross(railTan) < 0.0f ? RailAlignment::MisalignedLeft :
331 RailAlignment::MisalignedRight;
332}
333
334} // namespace Kinoko::Field
u16 m_leftMisalignFrame
Frame at which the wiggler became left-misaligned from the rail.
void calcRailAlignmentMotion()
If the wiggler has moved out of alignment of the rail, then applies a lateral adjustment to the wiggl...
RailAlignment m_railAlignment
Captures when the wiggle takes sharp turns.
Pertains to collision.
A 3D float vector.
Definition Vector.hh:107