A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
KartSub.cc
1#include "KartSub.hh"
2
3#include "game/kart/KartAction.hh"
4#include "game/kart/KartBody.hh"
5#include "game/kart/KartCollide.hh"
6#include "game/kart/KartMove.hh"
7#include "game/kart/KartObject.hh"
8#include "game/kart/KartPullPath.hh"
9#include "game/kart/KartState.hh"
10#include "game/kart/KartSuspensionPhysics.hh"
11
12#include "game/field/BoxColManager.hh"
13#include "game/field/CollisionDirector.hh"
14
15#include "game/system/RaceConfig.hh"
16#include "game/system/RaceManager.hh"
17
18#include <egg/math/Math.hh>
19
20namespace Kart {
21
22KartSub::KartSub() = default;
23
25KartSub::~KartSub() {
26 delete m_collide;
27 delete m_state;
28 delete m_move;
29 delete m_action;
30}
31
33void KartSub::createSubsystems(bool isBike, const KartParam::Stats &stats) {
34 m_move = isBike ? new KartMoveBike : new KartMove;
35 m_action = new KartAction;
36 m_move->createSubsystems(stats);
37 m_state = new KartState;
38 m_collide = new KartCollide;
39}
40
44 pointers.collide = m_collide;
45 pointers.state = m_state;
46 pointers.move = m_move;
47 pointers.action = m_action;
48}
49
51void KartSub::init() {
52 resetPhysics();
53 body()->reset();
54 m_state->init();
55 move()->setTurnParams();
56 action()->init();
57 m_collide->init();
58}
59
61void KartSub::initAABB(KartAccessor &accessor, KartObject *object) {
62 f32 radius = 25.0f + collide()->boundingRadius();
63 f32 hardSpeedLimit = move()->hardSpeedLimit();
64
65 accessor.boxColUnit = Field::BoxColManager::Instance()->insertDriver(radius, hardSpeedLimit,
66 &pos(), true, object);
67}
68
70void KartSub::initPhysicsValues() {
71 physics()->updatePose();
72 collide()->resetHitboxes();
73}
74
76void KartSub::resetPhysics() {
77 physics()->reset();
78 physics()->updatePose();
79 collide()->resetHitboxes();
80
81 for (u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) {
82 suspensionPhysics(wheelIdx)->reset();
83 }
84 for (u16 tireIdx = 0; tireIdx < tireCount(); ++tireIdx) {
85 tirePhysics(tireIdx)->reset();
86 }
87 m_move->setKartSpeedLimit();
88
89 resizeAABB(1.0f);
90
92 m_someScale = 1.0f;
93 m_maxSuspOvertravel.setZero();
94 m_minSuspOvertravel.setZero();
95}
96
103 auto &status = KartObjectProxy::status();
104
105 if (status.onBit(eStatus::CannonStart)) {
106 physics()->hitboxGroup()->reset();
107 for (size_t i = 0; i < tireCount(); ++i) {
108 tirePhysics(i)->hitboxGroup()->reset();
109 }
110 move()->enterCannon();
111 }
112
113 state()->calc();
114
115 if (status.onBit(eStatus::TriggerRespawn)) {
116 setInertiaScale(EGG::Vector3f(1.0f, 1.0f, 1.0f));
117 resetPhysics();
118 state()->reset();
119 move()->setTurnParams();
120 move()->calcRespawnStart();
121 }
122
123 physics()->setPos(dynamics()->pos());
124 physics()->setVelocity(dynamics()->velocity());
125 dynamics()->setGravity(-1.3f);
126 dynamics()->setAngVel0YFactor(0.9f);
127
128 state()->calcInput();
129 move()->calc();
130 action()->calc();
131 collide()->pullPath().calc();
132
133 if (status.onBit(eStatus::SkipWheelCalc)) {
134 for (size_t tireIdx = 0; tireIdx < tireCount(); ++tireIdx) {
135 tirePhysics(tireIdx)->setLastPos(pos());
136 }
137 return;
138 }
139
140 tryEndHWG();
141
142 dynamics()->setTop(move()->up());
143
144 // Pertains to startslides / leaning in stage 0 and 1
145 const auto *raceManager = System::RaceManager::Instance();
146 if (!raceManager->isStageReached(System::RaceManager::Stage::Race)) {
147 dynamics()->setIntVel(EGG::Vector3f::zero);
148
149 EGG::Vector3f killExtVel = dynamics()->extVel();
150 if (isBike()) {
151 killExtVel = killExtVel.rej(move()->smoothedUp());
152 } else {
153 killExtVel.x = 0.0f;
154 killExtVel.z = 0.0f;
155 }
156
157 dynamics()->setExtVel(killExtVel);
158 }
159
160 f32 maxSpeed = move()->hardSpeedLimit();
161 physics()->calc(DT, maxSpeed, scale(), status.offBit(eStatus::TouchingGround));
162
163 move()->calcRejectRoad();
164
165 if (status.offBit(eStatus::InCannon)) {
166 collide()->calcHitboxes();
167 collisionGroup()->setHitboxScale(move()->totalScale());
168 }
169}
170
177 constexpr s16 SIDE_COLLISION_TIME = 5;
178
179 state()->resetEjection();
180
181 m_movingWaterCollisionCount = 0;
182 m_movingObjCollisionCount = 0;
183 m_floorCollisionCount = 0;
184 m_objVel.setZero();
185 m_maxSuspOvertravel.setZero();
186 m_minSuspOvertravel.setZero();
187
188 // The flag is really 0x1f, but we only care about objects.
189 Field::BoxColFlag flags;
190 flags.setBit(Field::eBoxColFlag::Drivable, Field::eBoxColFlag::Object);
191 boxColUnit()->search(flags);
192
193 collide()->calcObjectCollision();
194 dynamics()->setPos(pos() + collide()->tangentOff());
195
196 auto &status = KartObjectProxy::status();
197
198 if (status.onBit(eStatus::SomethingWallCollision)) {
199 const EGG::Vector3f &softWallSpeed = state()->softWallSpeed();
200 f32 speedFactor = 5.0f;
201 EGG::Vector3f effectiveSpeed;
202
203 if (status.onBit(eStatus::HWG)) {
204 speedFactor = 10.0f;
205 effectiveSpeed = softWallSpeed;
206 } else {
207 effectiveSpeed = softWallSpeed.perpInPlane(move()->smoothedUp(), true);
208 f32 speedDotUp = softWallSpeed.dot(move()->smoothedUp());
209 if (speedDotUp < 0.0f) {
210 speedFactor += -speedDotUp * 10.0f;
211 }
212 }
213
214 effectiveSpeed *= speedFactor * scale().y;
215 setPos(pos() + effectiveSpeed);
216 collide()->setMovement(collide()->movement() + effectiveSpeed);
217 }
218
219 auto &colData = collisionData();
220 if (colData.bWallAtLeftCloser || colData.bWallAtRightCloser || m_sideCollisionTimer > 0) {
221 EGG::Vector3f right = dynamics()->mainRot().rotateVector(EGG::Vector3f::ex);
222
223 if (colData.bWallAtLeftCloser || colData.bWallAtRightCloser) {
224 f32 sign = colData.bWallAtRightCloser ? 1.0f : -1.0f;
225 m_colPerpendicularity = sign * colData.colPerpendicularity;
226 m_sideCollisionTimer = SIDE_COLLISION_TIME;
227 }
228
229 EGG::Vector3f colPerpBounceDir = 2.0f * m_colPerpendicularity * scale().x * right;
230 colPerpBounceDir.y = 0.0f;
231 setPos(pos() + colPerpBounceDir);
232 collide()->setMovement(collide()->movement() + colPerpBounceDir);
233 }
234
236
237 body()->calcSinkDepth();
238
239 Field::CollisionDirector::Instance()->checkCourseColNarrScLocal(250.0f, pos(),
241
242 if (status.offBit(eStatus::InCannon)) {
243 if (status.offBit(eStatus::ZipperStick)) {
244 collide()->findCollision();
245 body()->calcTargetSinkDepth();
246
247 if (colData.bWall || colData.bWall3) {
248 collide()->setMovement(collide()->movement() + colData.movement);
249 }
250 } else {
251 colData.reset();
252 }
253
254 collide()->calcFloorEffect();
255 collide()->calcFloorMomentRate();
256
257 if (colData.bFloor) {
258 // Update floor count
259 addFloor(colData, false);
260 }
261 }
262
263 EGG::Vector3f forward = fullRot().rotateVector(EGG::Vector3f::ez);
264 m_someScale = std::max(scale().y, param()->stats().shrinkScale);
265
266 const EGG::Vector3f gravity(0.0f, -1.3f, 0.0f);
267 f32 speedFactor = 1.0f;
268 f32 handlingFactor = 0.0f;
269 for (u16 i = 0; i < suspCount(); ++i) {
270 const EGG::Matrix34f wheelMatrix = body()->wheelMatrix(i);
271 suspensionPhysics(i)->calcCollision(DT, gravity, wheelMatrix);
272
273 const CollisionData &colData = tirePhysics(i)->hitboxGroup()->collisionData();
274
275 speedFactor = std::min(speedFactor, colData.speedFactor);
276
277 if (colData.bFloor) {
278 handlingFactor += colData.rotFactor;
279 addFloor(colData, false);
280 }
281 }
282
283 if (status.offBit(eStatus::SkipWheelCalc)) {
284 EGG::Vector3f vehicleCompensation = m_maxSuspOvertravel + m_minSuspOvertravel;
285 dynamics()->setPos(dynamics()->pos() + vehicleCompensation);
286
287 if (!collisionData().bFloor) {
288 EGG::Vector3f relPos = EGG::Vector3f::zero;
289 EGG::Vector3f vel = EGG::Vector3f::zero;
290 EGG::Vector3f floorNrm = EGG::Vector3f::zero;
291 u32 count = 0;
292
293 for (u16 wheelIdx = 0; wheelIdx < tireCount(); ++wheelIdx) {
294 const WheelPhysics *wheelPhysics = tirePhysics(wheelIdx);
295 if (wheelPhysics->_74() == 0.0f) {
296 continue;
297 }
298
299 const CollisionData &colData = wheelPhysics->hitboxGroup()->collisionData();
300 relPos += colData.relPos;
301 vel += colData.vel;
302 floorNrm += colData.floorNrm;
303 ++count;
304 }
305
306 if (count > 0) {
307 f32 scalar = (1.0f / static_cast<f32>(count));
308 floorNrm.normalise();
309
310 collide()->setFloorColInfo(collisionData(), relPos * scalar, vel * scalar,
311 floorNrm);
312
313 collide()->FUN_80572F4C();
314 }
315 }
316
317 for (u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) {
318 suspensionPhysics(wheelIdx)->calcSuspension(forward, vehicleCompensation);
319 }
320
321 move()->calcHopPhysics();
322 }
323
324 if (status.onBit(eStatus::CollidingOffroad)) {
325 const auto &stats = param()->stats();
326 speedFactor = stats.kclSpeed[3];
327 handlingFactor = stats.kclRot[3];
328 }
329
330 move()->setKCLWheelSpeedFactor(speedFactor);
331 move()->setKCLWheelRotFactor(handlingFactor);
332
333 move()->setFloorCollisionCount(m_floorCollisionCount);
334
335 calcMovingObj();
336 calcMovingWater();
337
338 physics()->updatePose();
339
340 collide()->resetHitboxes();
341
342 // calcRotation() is only ever used for gfx rendering, so skip
343}
344
346void KartSub::resizeAABB(f32 radiusScale) {
347 f32 radius = radiusScale * collisionGroup()->boundingRadius();
348 boxColUnit()->resize(radius + 25.0f, move()->hardSpeedLimit());
349}
350
352void KartSub::addFloor(const CollisionData &colData, bool) {
353 ++m_floorCollisionCount;
354
355 if (colData.bHasRoadVel) {
356 ++m_movingObjCollisionCount;
357 m_objVel += colData.roadVelocity;
358 }
359
360 auto &status = KartObjectProxy::status();
361 if (colData.bMovingWaterMomentum || colData.bMovingWaterDecaySpeed) {
362 ++m_movingWaterCollisionCount;
363
364 status.changeBit(colData.bMovingWaterDecaySpeed, eStatus::MovingWaterDecaySpeed);
365 status.changeBit(colData.bMovingWaterDisableAccel, eStatus::DisableAcceleration);
366 status.changeBit(colData.bMovingWaterVertical, eStatus::MovingWaterVertical);
367 } else {
368 status.resetBit(eStatus::MovingWaterDecaySpeed, eStatus::DisableAcceleration,
369 eStatus::MovingWaterVertical);
370 }
371
372 status.changeBit(colData.bMovingWaterStickyRoad, eStatus::MovingWaterStickyRoad);
373}
374
376void KartSub::updateSuspOvertravel(const EGG::Vector3f &suspOvertravel) {
377 m_maxSuspOvertravel = m_maxSuspOvertravel.minimize(suspOvertravel);
378 m_minSuspOvertravel = m_minSuspOvertravel.maximize(suspOvertravel);
379}
380
382void KartSub::tryEndHWG() {
383 auto &status = KartObjectProxy::status();
384
385 if (status.onBit(eStatus::SoftWallDrift)) {
386 if (EGG::Mathf::abs(move()->speed()) > 15.0f ||
388 status.resetBit(eStatus::SoftWallDrift);
389 } else if (status.onBit(eStatus::TouchingGround)) {
390 if (EGG::Mathf::abs(componentXAxis().dot(EGG::Vector3f::ey)) > 0.8f) {
391 status.resetBit(eStatus::SoftWallDrift);
392 }
393 }
394 }
395
396 if (status.onBit(eStatus::HWG) && status.offBit(eStatus::SomethingWallCollision)) {
399 status.resetBit(eStatus::HWG);
400 }
401 }
402
403 if (status.offBit(eStatus::InAction)) {
404 dynamics()->setForceUpright(status.offBit(eStatus::SoftWallDrift));
405 }
406}
407
409void KartSub::calcMovingObj() {
410 if (m_movingObjCollisionCount == 0) {
411 f32 scalar = state()->airtime() < 20 ? 1.0f : 0.9f;
412 physics()->composeDecayingMovingObjVel(0.7f, scalar, m_floorCollisionCount != 0);
413 } else {
414 m_objVel *= 1.0f / static_cast<f32>(m_floorCollisionCount);
415 physics()->composeMovingObjVel(m_objVel, 0.2f);
416 }
417}
418
420void KartSub::calcMovingWater() {
421 constexpr f32 DECAY_FLOOR_SCALAR = 0.7f;
422 constexpr f32 DECAY_AIR_SCALAR = 0.5f;
423 constexpr f32 DECAY_KC_AIR_SCALAR = 0.3f;
424
425 auto &status = KartObjectProxy::status();
426 const auto &pullPath = collide()->pullPath();
427
428 if (m_movingWaterCollisionCount > 0) {
429 f32 ratio = static_cast<f32>(m_movingWaterCollisionCount) /
430 static_cast<f32>(m_floorCollisionCount);
431 EGG::Vector3f dir = pullPath.pullDirection().perpInPlane(move()->smoothedUp(), true);
432
433 if (status.offBit(eStatus::MovingWaterDecaySpeed)) {
434 EGG::Vector3f vel = pullPath.pullSpeed() * dir * ratio;
435 physics()->composeMovingRoadVel(vel, 0.2f);
436 } else {
437 EGG::Vector3f vel = pullPath.pullSpeed() * dir;
438 physics()->shiftDecayMovingRoadVel(vel, pullPath.maxPullSpeed());
439 }
440 } else {
441 f32 airScalar = status.onBit(eStatus::MovingWaterStickyRoad) ? DECAY_AIR_SCALAR : 1.0f;
442 if (System::RaceConfig::Instance()->raceScenario().course == Course::Koopa_Cape &&
443 status.onBit(eStatus::MovingWaterDecaySpeed)) {
444 airScalar = DECAY_KC_AIR_SCALAR;
445 }
446
447 physics()->decayMovingRoadVel(DECAY_FLOOR_SCALAR, airScalar, m_floorCollisionCount > 0);
448 }
449
450 if (status.onBit(eStatus::MovingWaterStickyRoad)) {
451 EGG::Vector3f dir = pullPath.pullDirection().perpInPlane(move()->smoothedUp(), true);
452 dir.y *= 50.0f;
453 const EGG::Vector3f &vel = dynamics()->movingRoadVel();
454 dynamics()->setMovingRoadVel(EGG::Vector3f(vel.x, dir.y, vel.z));
455 }
456}
457
458} // namespace Kart
#define KCL_TYPE_VEHICLE_INTERACTABLE
0xEFFFBDFF
A 3 x 4 matrix.
Definition Matrix.hh:8
constexpr TBitFlagExt< N, E > & changeBit(bool on, Es... es)
Changes the state of the corresponding bits for the provided enum values.
Definition BitFlag.hh:367
constexpr bool offBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are off.
Definition BitFlag.hh:412
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
Definition BitFlag.hh:379
constexpr TBitFlagExt< N, E > & resetBit(Es... es)
Resets the corresponding bits for the provided enum values.
Definition BitFlag.hh:355
virtual EGG::Matrix34f wheelMatrix(u16)
Computes a matrix to represent wheel rotation. For Karts, this is wheel-agnostic.
Definition KartBody.cc:16
void calcHitboxes()
On each frame, calculates the positions for each hitbox.
void calc()
Each frame, calculates the kart's movement.
Definition KartMove.cc:287
void updatePose()
Constructs a transformation matrix from rotation and position.
void calc(f32 dt, f32 maxSpeed, const EGG::Vector3f &scale, bool air)
Computes trick rotation and calls to KartDynamics::calc().
void calc()
Every frame, resets the input state and saves collision-related bit flags.
Definition KartState.cc:115
void calcInput()
Each frame, read input and save related bit flags. Also handles start boosts.
Definition KartState.cc:66
void resetEjection()
Resets certain bitfields pertaining to ejections (reject road, half pipe zippers, etc....
Definition KartState.cc:411
void calcPass0()
The first phase of physics computations on each frame.
Definition KartSub.cc:102
static constexpr f32 DT
Delta time.
Definition KartSub.hh:54
void copyPointers(KartAccessor &pointers)
Called during static construction of KartObject to synchronize the pointers.
Definition KartSub.cc:43
f32 m_colPerpendicularity
Dot product between floor and colliding wall normals.
Definition KartSub.hh:51
void calcPass1()
The second phase of physics computations on each frame.Handles the second-half of physics calculation...
Definition KartSub.cc:176
s16 m_sideCollisionTimer
Number of frames to apply movement from wall collision.
Definition KartSub.hh:50
void calcSuspension(const EGG::Vector3f &forward, const EGG::Vector3f &vehicleMovement)
Calculates linear force and rotation from the kart's suspension.
Manages wheel physics and collision checks.
Pertains to kart-related functionality.
@ HWG
Set when "Horizontal Wall Glitch" is active.
@ ZipperStick
Set while mid-air and still influenced by the zipper.
@ AllWheelsCollision
Set when all wheels are touching floor collision.
@ AirtimeOver20
Set after 20 frames of airtime, resets on landing.
@ Wall3Collision
Set when colliding with wall KCL COL_TYPE_WALL_2.
@ TouchingGround
Set when any part of the vehicle is colliding with floor KCL.
@ WallCollision
Set if we are colliding with a wall.
Vector3f rotateVector(const Vector3f &vec) const
Rotates a vector based on the quat.
Definition Quat.cc:55
constexpr TBitFlag< T, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
Definition BitFlag.hh:62
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
Vector3f perpInPlane(const EGG::Vector3f &rhs, bool normalise) const
Calculates the orthogonal vector, based on the plane defined by this vector and rhs.
Definition Vector.cc:114
Vector3f rej(const Vector3f &rhs) const
The rejection of this vector onto rhs.
Definition Vector.hh:210
Vector3f maximize(const Vector3f &rhs) const
Returns a vector whose elements are the max of the elements of both vectors.
Definition Vector.cc:83
Vector3f minimize(const Vector3f &rhs) const
Returns a vector whose elements are the min of the elements of both vectors.
Definition Vector.cc:95
Information about the current collision and its properties.
bool bFloor
Set if colliding with KCL which satisfies KCL_TYPE_FLOOR.
Shared between classes who inherit KartObjectProxy so they can access one another.