A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectTruckWagon.cc
1#include "ObjectTruckWagon.hh"
2
3#include "game/field/CollisionDirector.hh"
4#include "game/field/Rail.hh"
5#include "game/field/RailManager.hh"
6
7#include "game/kart/KartCollide.hh"
8#include "game/kart/KartObject.hh"
9
10namespace Kinoko::Field {
11
13ObjectTruckWagonCart::ObjectTruckWagonCart(const System::MapdataGeoObj &params)
14 : ObjectCollidable(params), StateManager(this, STATE_ENTRIES), m_active(true),
15 m_vel(EGG::Vector3f::zero), m_lastVel(EGG::Vector3f::zero), m_up(EGG::Vector3f::zero),
16 m_tangent(EGG::Vector3f::zero), m_pitch(0.0f) {}
17
19ObjectTruckWagonCart::~ObjectTruckWagonCart() = default;
20
22void ObjectTruckWagonCart::calc() {
23 if (!m_active) {
24 return;
25 }
26
27 switch (m_railInterpolator->calc()) {
28 case RailInterpolator::Status::SegmentEnd:
29 if (m_currentStateId != 1) {
30 u16 setting = m_railInterpolator->curPoint().setting[1];
31 if (setting <= 2) {
32 m_nextStateId = setting;
33 }
34 }
35 break;
36 case RailInterpolator::Status::ChangingDirection:
37 deactivate();
38 break;
39 default:
40 break;
41 }
42
43 m_vel.x = m_railInterpolator->currVel() * m_railInterpolator->curTangentDir().x;
44 m_vel.z = m_railInterpolator->currVel() * m_railInterpolator->curTangentDir().z;
45
46 StateManager::calc();
47
48 calcTransform();
49}
50
52void ObjectTruckWagonCart::calcCollisionTransform() {
53 auto *col = collision();
54 if (!col || !m_active) {
55 return;
56 }
57
58 f32 yOffset = m_currentStateId == 1 ? 500.0f : 100.0f;
59 EGG::Matrix34f mat = EGG::Matrix34f::zero;
60 mat.makeT(EGG::Vector3f(0.0f, yOffset, 0.0f));
61
62 calcTransform();
63 col->transform(transform().multiplyTo(mat), scale(), m_vel);
64}
65
67Kart::Reaction ObjectTruckWagonCart::onCollision(Kart::KartObject *kartObj,
68 Kart::Reaction reactionOnKart, Kart::Reaction /*reactionOnObj*/,
69 EGG::Vector3f & /*hitDepth*/) {
70 return kartObj->speedRatioCapped() < 0.5f ? Kart::Reaction::WallAllSpeed : reactionOnKart;
71}
72
74void ObjectTruckWagonCart::calcState0() {
75 constexpr f32 RADIUS = 50.0f;
76 constexpr f32 GRAVITY = 2.0f;
77 constexpr f32 INITIAL_FALL_OFFSET = 15.0f;
78
79 const EGG::Vector3f &railPos = m_railInterpolator->curPos();
80 if (m_railInterpolator->curPointIdx() != 3) {
81 setPos(railPos);
82
83 m_up = Interpolate(0.1f, m_up,
84 m_railInterpolator->floorNrm(m_railInterpolator->nextPointIdx()));
85 m_tangent = Interpolate(0.1f, m_tangent, m_railInterpolator->curTangentDir());
86 m_up.normalise2();
87 m_tangent.normalise2();
88
89 setMatrixTangentTo(m_up, m_tangent);
90
91 return;
92 }
93
94 setPos(EGG::Vector3f(railPos.x, pos().y - INITIAL_FALL_OFFSET, railPos.z));
95
96 CollisionInfo info;
97 EGG::Vector3f colPos = pos() + EGG::Vector3f(0.0f, RADIUS, 0.0f);
98
99 EGG::Vector3f floorNrm = m_up;
100 EGG::Vector3f tangent = m_tangent;
101
102 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS, colPos, EGG::Vector3f::inf,
103 KCL_TYPE_FLOOR, &info, nullptr, 0);
104
105 if (hasCol) {
106 m_vel.y = 0.0f;
107 addPos(info.tangentOff);
108
109 if (info.floorDist > -std::numeric_limits<f32>::min()) {
110 floorNrm = info.floorNrm;
111 }
112
113 tangent = m_railInterpolator->curTangentDir();
114 } else {
115 m_vel.y -= GRAVITY;
116 setPos(EGG::Vector3f(pos().x, m_vel.y + pos().y, pos().z));
117 }
118
119 m_up = Interpolate(0.1f, m_up, floorNrm);
120 m_tangent = Interpolate(0.1f, m_tangent, tangent);
121 m_up.normalise2();
122 m_tangent.normalise2();
123
124 setMatrixTangentTo(m_up, m_tangent);
125}
126
128void ObjectTruckWagonCart::calcState1() {
129 constexpr EGG::Vector3f INITIAL_OFFSET = EGG::Vector3f(0.0f, 710.0f, 0.0f);
130
131 // Controls how strongly the cart tries to restore to neutral (damped harmonic oscillator)
132 constexpr f32 SPRING_STIFFNESS = 3.9f;
133 constexpr f32 PITCH_INERTIA = 1300.0f;
134 constexpr f32 ANG_VEL_DECAY = 0.998f;
135
136 EGG::Vector2f lastVelXZ = EGG::Vector2f(m_lastVel.x, m_lastVel.z);
137 EGG::Vector2f velXZ = EGG::Vector2f(m_vel.x, m_vel.z);
138 f32 velMagDiff = EGG::Mathf::sqrt((velXZ - lastVelXZ).dot());
139 velMagDiff = velXZ.cross(lastVelXZ) > 0.0f ? -velMagDiff : velMagDiff;
140 f32 cos = velMagDiff * EGG::Mathf::CosFIdx(RAD2FIDX * m_pitch);
141 f32 sin = EGG::Mathf::SinFIdx(RAD2FIDX * m_pitch);
142 m_angVel = (m_angVel + (cos + -SPRING_STIFFNESS * sin) / PITCH_INERTIA) * ANG_VEL_DECAY;
143 m_pitch += m_angVel;
144
145 EGG::Vector3f tanXZ = m_railInterpolator->curTangentDir();
146 tanXZ.y = 0.0f;
147 tanXZ.normalise2();
148 EGG::Matrix34f mat;
149 mat.setAxisRotation(m_pitch, tanXZ);
150 mat.setBase(3, EGG::Vector3f::zero);
151
152 m_up = Interpolate(0.1f, m_up, mat.ps_multVector(EGG::Vector3f::ey));
153 m_tangent = Interpolate(0.1f, m_tangent, tanXZ);
154 m_tangent.y = 0.0f;
155
156 m_up.normalise2();
157 m_tangent.normalise2();
158 EGG::Vector3f cross = m_up.cross(m_tangent);
159 cross.normalise2();
160
161 mat.setBase(0, cross);
162 mat.setBase(1, m_up);
163 mat.setBase(2, m_tangent);
164 mat.setBase(3, EGG::Vector3f::zero);
165
166 EGG::Matrix34f transMat = mat;
167 transMat.setBase(3, pos());
168 setTransform(transMat);
169
170 setPos(m_railInterpolator->curPos() + INITIAL_OFFSET - mat.ps_multVector(INITIAL_OFFSET));
171 m_lastVel = m_vel;
172}
173
175void ObjectTruckWagonCart::reset(u32 idx) {
176 m_railInterpolator->init(0.0f, idx);
177 m_railInterpolator->setPerPointVelocities(true);
178
179 setPos(m_railInterpolator->curPos());
180 m_speed = m_railInterpolator->speed();
181 m_vel = m_railInterpolator->curTangentDir() * m_speed;
182 m_lastVel.setZero();
183
184 if (m_currentStateId != 1) {
185 u16 setting = m_railInterpolator->curPoint().setting[1];
186 if (setting <= 2) {
187 m_nextStateId = setting;
188 }
189 }
190
191 m_up = EGG::Vector3f::ey;
192 m_tangent = EGG::Vector3f::ez;
193 m_pitch = 0.0f;
194 m_angVel = 0.0f;
195}
196
198ObjectTruckWagon::ObjectTruckWagon(const System::MapdataGeoObj &params)
199 : ObjectCollidable(params), m_spawn2Frame(static_cast<s32>(params.setting(1))),
200 m_cycleDuration(static_cast<s32>(params.setting(2))) {
201 constexpr u32 CART_COUNT = 12;
202
203 // For now, we don't care about low LOD minecarts since they don't have collision.
204 if (params.setting(3) > 0) {
205 return;
206 }
207
208 m_carts = owning_span<ObjectTruckWagonCart *>(CART_COUNT);
209
210 for (auto *&cart : m_carts) {
211 cart = EGG::egg_new<ObjectTruckWagonCart>(params);
212 cart->load();
213 }
214
215 auto *rail = RailManager::Instance()->rail(params.pathId());
216 ASSERT(rail);
217
218 if (rail->pointCount() > 40) {
219 rail->checkSphereFull();
220 }
221}
222
224ObjectTruckWagon::~ObjectTruckWagon() = default;
225
227void ObjectTruckWagon::init() {
228 // If this spawner is for low LOD carts, then we need to skip init since the span is empty
229 if (m_carts.empty()) {
230 return;
231 }
232
233 for (auto *&cart : m_carts) {
234 if (cart->isActive()) {
235 cart->deactivate();
236 }
237 }
238
239 u16 ptCount = m_carts[0]->railInterpolator()->pointCount();
240 u32 cartCount = m_carts.size();
241 u32 halfCount = m_carts.size() / 2;
242
243 for (u32 i = halfCount; i < cartCount; ++i) {
244 auto *&cart = m_carts[i];
245 cart->reset(ptCount / halfCount * (cartCount - i));
246 cart->setActive(true);
247 cart->loadAABB(0.0f);
248 }
249
250 m_cycleFrame = 0;
251 m_curCartIdx = 0;
252}
253
255void ObjectTruckWagon::calc() {
256 if (m_carts.empty()) {
257 return;
258 }
259
260 if (m_cycleFrame == m_spawn2Frame || m_cycleFrame == m_spawn2Frame + m_cycleDuration) {
261 m_carts[m_curCartIdx]->activate();
262 m_curCartIdx = (m_curCartIdx + 1) % m_carts.size();
263 }
264
265 m_cycleFrame = m_cycleFrame % (m_spawn2Frame + m_cycleDuration) + 1;
266}
267
268} // namespace Kinoko::Field
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
Base class that represents different "states" for an object.
Pertains to collision.