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/KartState.hh"
9#include "game/kart/KartSuspensionPhysics.hh"
10
11#include "game/field/BoxColManager.hh"
12#include "game/field/CollisionDirector.hh"
13
14#include "game/system/RaceManager.hh"
15
16#include <egg/math/Math.hh>
17
18namespace Kart {
19
20KartSub::KartSub() = default;
21
23KartSub::~KartSub() {
24 delete m_collide;
25 delete m_state;
26 delete m_move;
27 delete m_action;
28}
29
31void KartSub::createSubsystems(bool isBike) {
32 m_move = isBike ? new KartMoveBike : new KartMove;
33 m_action = new KartAction;
34 m_move->createSubsystems();
35 m_state = new KartState;
36 m_collide = new KartCollide;
37}
38
42 pointers.collide = m_collide;
43 pointers.state = m_state;
44 pointers.move = m_move;
45 pointers.action = m_action;
46}
47
49void KartSub::init() {
50 resetPhysics();
51 body()->reset();
52 m_state->init();
53 move()->setTurnParams();
54 action()->init();
55 m_collide->init();
56}
57
59void KartSub::initAABB(KartAccessor &accessor, KartObject *object) {
60 f32 radius = 25.0f + collide()->boundingRadius();
61 f32 hardSpeedLimit = move()->hardSpeedLimit();
62
63 accessor.boxColUnit = Field::BoxColManager::Instance()->insertDriver(radius, hardSpeedLimit,
64 &pos(), true, object);
65}
66
68void KartSub::initPhysicsValues() {
69 physics()->updatePose();
70 collide()->resetHitboxes();
71}
72
74void KartSub::resetPhysics() {
75 physics()->reset();
76 physics()->updatePose();
77 collide()->resetHitboxes();
78
79 for (u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) {
80 suspensionPhysics(wheelIdx)->reset();
81 }
82 for (u16 tireIdx = 0; tireIdx < tireCount(); ++tireIdx) {
83 tirePhysics(tireIdx)->reset();
84 }
85 m_move->setKartSpeedLimit();
86
87 resizeAABB(1.0f);
88
90 m_someScale = 1.0f;
91 m_maxSuspOvertravel.setZero();
92 m_minSuspOvertravel.setZero();
93}
94
101 if (state()->isCannonStart()) {
102 physics()->hitboxGroup()->reset();
103 for (size_t i = 0; i < tireCount(); ++i) {
104 tirePhysics(i)->hitboxGroup()->reset();
105 }
106 move()->enterCannon();
107 }
108
109 state()->calc();
110
111 if (state()->isTriggerRespawn()) {
112 setInertiaScale(EGG::Vector3f(1.0f, 1.0f, 1.0f));
113 resetPhysics();
114 state()->reset();
115 move()->setTurnParams();
116 move()->calcRespawnStart();
117 }
118
119 physics()->setPos(dynamics()->pos());
120 physics()->setVelocity(dynamics()->velocity());
121 dynamics()->setGravity(-1.3f);
122 dynamics()->setAngVel0YFactor(0.9f);
123
124 state()->calcInput();
125 move()->calc();
126 action()->calc();
127
128 if (state()->isSkipWheelCalc()) {
129 for (size_t tireIdx = 0; tireIdx < tireCount(); ++tireIdx) {
130 tirePhysics(tireIdx)->setLastPos(pos());
131 }
132 return;
133 }
134
135 tryEndHWG();
136
137 dynamics()->setTop(move()->up());
138
139 // Pertains to startslides / leaning in stage 0 and 1
140 const auto *raceManager = System::RaceManager::Instance();
141 if (!raceManager->isStageReached(System::RaceManager::Stage::Race)) {
142 dynamics()->setIntVel(EGG::Vector3f::zero);
143
144 EGG::Vector3f killExtVel = dynamics()->extVel();
145 if (isBike()) {
146 killExtVel = killExtVel.rej(move()->smoothedUp());
147 } else {
148 killExtVel.x = 0.0f;
149 killExtVel.z = 0.0f;
150 }
151
152 dynamics()->setExtVel(killExtVel);
153 }
154
155 f32 maxSpeed = move()->hardSpeedLimit();
156 physics()->calc(DT, maxSpeed, scale(), !state()->isTouchingGround());
157
158 move()->calcRejectRoad();
159
160 if (!state()->isInCannon()) {
161 collide()->calcHitboxes();
162 collisionGroup()->setHitboxScale(move()->totalScale());
163 }
164}
165
172 constexpr s16 SIDE_COLLISION_TIME = 5;
173
174 state()->resetEjection();
175
176 m_movingObjCollisionCount = 0;
177 m_floorCollisionCount = 0;
178 m_objVel.setZero();
179 m_maxSuspOvertravel.setZero();
180 m_minSuspOvertravel.setZero();
181
182 // The flag is really 0x1f, but we only care about objects.
183 Field::BoxColFlag flags;
184 flags.setBit(Field::eBoxColFlag::Drivable, Field::eBoxColFlag::Object);
185 boxColUnit()->search(flags);
186
187 collide()->calcObjectCollision();
188 dynamics()->setPos(pos() + collide()->tangentOff());
189
190 if (state()->isSomethingWallCollision()) {
191 const EGG::Vector3f &softWallSpeed = state()->softWallSpeed();
192 f32 speedFactor = 5.0f;
193 EGG::Vector3f effectiveSpeed;
194
195 if (state()->isHWG()) {
196 speedFactor = 10.0f;
197 effectiveSpeed = softWallSpeed;
198 } else {
199 effectiveSpeed = softWallSpeed.perpInPlane(move()->smoothedUp(), true);
200 f32 speedDotUp = softWallSpeed.dot(move()->smoothedUp());
201 if (speedDotUp < 0.0f) {
202 speedFactor += -speedDotUp * 10.0f;
203 }
204 }
205
206 effectiveSpeed *= speedFactor * scale().y;
207 setPos(pos() + effectiveSpeed);
208 collide()->setMovement(collide()->movement() + effectiveSpeed);
209 }
210
211 auto &colData = collisionData();
212 if (colData.bWallAtLeftCloser || colData.bWallAtRightCloser || m_sideCollisionTimer > 0) {
213 EGG::Vector3f right = dynamics()->mainRot().rotateVector(EGG::Vector3f::ex);
214
215 if (colData.bWallAtLeftCloser || colData.bWallAtRightCloser) {
216 f32 sign = colData.bWallAtRightCloser ? 1.0f : -1.0f;
217 m_colPerpendicularity = sign * colData.colPerpendicularity;
218 m_sideCollisionTimer = SIDE_COLLISION_TIME;
219 }
220
221 EGG::Vector3f colPerpBounceDir = 2.0f * m_colPerpendicularity * scale().x * right;
222 colPerpBounceDir.y = 0.0f;
223 setPos(pos() + colPerpBounceDir);
224 collide()->setMovement(collide()->movement() + colPerpBounceDir);
225 }
226
228
229 body()->calcSinkDepth();
230
231 Field::CollisionDirector::Instance()->checkCourseColNarrScLocal(250.0f, pos(),
233
234 if (!state()->isInCannon()) {
235 if (!state()->isZipperStick()) {
236 collide()->findCollision();
237 body()->calcTargetSinkDepth();
238
239 if (colData.bWall || colData.bWall3) {
240 collide()->setMovement(collide()->movement() + colData.movement);
241 }
242 } else {
243 colData.reset();
244 }
245
246 collide()->calcFloorEffect();
247 collide()->calcFloorMomentRate();
248
249 if (colData.bFloor) {
250 // Update floor count
251 addFloor(colData, false);
252 }
253 }
254
255 EGG::Vector3f forward = fullRot().rotateVector(EGG::Vector3f::ez);
256 m_someScale = scale().y;
257
258 const EGG::Vector3f gravity(0.0f, -1.3f, 0.0f);
259 f32 speedFactor = 1.0f;
260 f32 handlingFactor = 0.0f;
261 for (u16 i = 0; i < suspCount(); ++i) {
262 const EGG::Matrix34f wheelMatrix = body()->wheelMatrix(i);
263 suspensionPhysics(i)->calcCollision(DT, gravity, wheelMatrix);
264
265 const CollisionData &colData = tirePhysics(i)->hitboxGroup()->collisionData();
266
267 speedFactor = std::min(speedFactor, colData.speedFactor);
268
269 if (colData.bFloor) {
270 handlingFactor += colData.rotFactor;
271 addFloor(colData, false);
272 }
273 }
274
275 if (!state()->isSkipWheelCalc()) {
276 EGG::Vector3f vehicleCompensation = m_maxSuspOvertravel + m_minSuspOvertravel;
277 dynamics()->setPos(dynamics()->pos() + vehicleCompensation);
278
279 if (!collisionData().bFloor) {
280 EGG::Vector3f relPos = EGG::Vector3f::zero;
281 EGG::Vector3f vel = EGG::Vector3f::zero;
282 EGG::Vector3f floorNrm = EGG::Vector3f::zero;
283 u32 count = 0;
284
285 for (u16 wheelIdx = 0; wheelIdx < tireCount(); ++wheelIdx) {
286 const WheelPhysics *wheelPhysics = tirePhysics(wheelIdx);
287 if (wheelPhysics->_74() == 0.0f) {
288 continue;
289 }
290
291 const CollisionData &colData = wheelPhysics->hitboxGroup()->collisionData();
292 relPos += colData.relPos;
293 vel += colData.vel;
294 floorNrm += colData.floorNrm;
295 ++count;
296 }
297
298 if (count > 0) {
299 f32 scalar = (1.0f / static_cast<f32>(count));
300 floorNrm.normalise();
301
302 collide()->setFloorColInfo(collisionData(), relPos * scalar, vel * scalar,
303 floorNrm);
304
305 collide()->FUN_80572F4C();
306 }
307 }
308
309 for (u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) {
310 suspensionPhysics(wheelIdx)->calcSuspension(forward, vehicleCompensation);
311 }
312
313 move()->calcHopPhysics();
314 }
315
316 move()->setKCLWheelSpeedFactor(speedFactor);
317 move()->setKCLWheelRotFactor(handlingFactor);
318
319 move()->setFloorCollisionCount(m_floorCollisionCount);
320
321 calcMovingObj();
322
323 physics()->updatePose();
324
325 collide()->resetHitboxes();
326
327 // calcRotation() is only ever used for gfx rendering, so skip
328}
329
331void KartSub::resizeAABB(f32 radiusScale) {
332 f32 radius = radiusScale * collisionGroup()->boundingRadius();
333 boxColUnit()->resize(radius + 25.0f, move()->hardSpeedLimit());
334}
335
337void KartSub::addFloor(const CollisionData &colData, bool) {
338 ++m_floorCollisionCount;
339
340 if (colData.bHasRoadVel) {
341 ++m_movingObjCollisionCount;
342 m_objVel += colData.roadVelocity;
343 }
344}
345
347void KartSub::updateSuspOvertravel(const EGG::Vector3f &suspOvertravel) {
348 m_maxSuspOvertravel = m_maxSuspOvertravel.minimize(suspOvertravel);
349 m_minSuspOvertravel = m_minSuspOvertravel.maximize(suspOvertravel);
350}
351
353void KartSub::tryEndHWG() {
354 if (state()->isSoftWallDrift()) {
355 if (EGG::Mathf::abs(move()->speed()) > 15.0f || state()->isAirtimeOver20() ||
356 state()->isAllWheelsCollision()) {
357 state()->setSoftWallDrift(false);
358 } else if (state()->isTouchingGround()) {
359 if (EGG::Mathf::abs(componentXAxis().dot(EGG::Vector3f::ey)) > 0.8f) {
360 state()->setSoftWallDrift(false);
361 }
362 }
363 }
364
365 if (state()->isHWG() && !state()->isSomethingWallCollision()) {
366 if (!state()->isWallCollision() || state()->isAllWheelsCollision()) {
367 state()->setHWG(false);
368 }
369 }
370
371 if (!state()->isInAction()) {
372 dynamics()->setForceUpright(!state()->isSoftWallDrift());
373 }
374}
375
377void KartSub::calcMovingObj() {
378 if (m_movingObjCollisionCount == 0) {
379 f32 scalar = state()->airtime() < 20 ? 1.0f : 0.9f;
380 physics()->composeDecayingMovingObjVel(0.7f, scalar, m_floorCollisionCount != 0);
381 } else {
382 m_objVel *= 1.0f / static_cast<f32>(m_floorCollisionCount);
383 physics()->composeMovingObjVel(m_objVel, 0.2f);
384 }
385}
386
387} // namespace Kart
#define KCL_TYPE_VEHICLE_INTERACTABLE
0xEFFFBDFF
A 3 x 4 matrix.
Definition Matrix.hh:8
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:272
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:117
void calcInput()
Each frame, read input and save related bit flags. Also handles start boosts.
Definition KartState.cc:70
void resetEjection()
Resets certain bitfields pertaining to ejections (reject road, half pipe zippers, etc....
Definition KartState.cc:420
void calcPass0()
The first phase of physics computations on each frame.
Definition KartSub.cc:100
static constexpr f32 DT
Delta time.
Definition KartSub.hh:52
void copyPointers(KartAccessor &pointers)
Called during static construction of KartObject to synchronize the pointers.
Definition KartSub.cc:41
f32 m_colPerpendicularity
Dot product between floor and colliding wall normals.
Definition KartSub.hh:49
void calcPass1()
The second phase of physics computations on each frame.Handles the second-half of physics calculation...
Definition KartSub.cc:171
s16 m_sideCollisionTimer
Number of frames to apply movement from wall collision.
Definition KartSub.hh:48
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.
Vector3f rotateVector(const Vector3f &vec) const
Rotates a vector based on the quat.
Definition Quat.cc:50
constexpr TBitFlag< T, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
Definition BitFlag.hh:57
A 3D float vector.
Definition Vector.hh:83
f32 normalise()
Normalizes the vector and returns the original length.
Definition Vector.cc:44
f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
Definition Vector.hh:182
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:96
Vector3f rej(const Vector3f &rhs) const
The rejection of this vector onto rhs.
Definition Vector.hh:199
Vector3f maximize(const Vector3f &rhs) const
Returns a vector whose elements are the max of the elements of both vectors.
Definition Vector.cc:65
Vector3f minimize(const Vector3f &rhs) const
Returns a vector whose elements are the min of the elements of both vectors.
Definition Vector.cc:77
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.