A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectHeyho.cc
1#include "ObjectHeyho.hh"
2
3#include "game/field/CollisionDirector.hh"
4#include "game/field/RailManager.hh"
5
6namespace Field {
7
9ObjectHeyho::ObjectHeyho(const System::MapdataGeoObj &params)
10 : ObjectCollidable(params), StateManager(this), m_color(params.setting(1)) {
11 const auto *rail = RailManager::Instance()->rail(params.pathId());
12 ASSERT(rail);
13 const auto &railPts = rail->points();
14
15 // The two endpoints are candidates for the highest Y coordinate in the route
16 // The midpoint's Y coordinate should be the lowest in the route
17 m_apex = std::max(railPts.front().pos.y, railPts.back().pos.y);
18 m_midpoint = railPts[railPts.size() / 2].pos;
19
20 // The object should speed up as we approach the low point and slow down as we leave it
21 // We form an acceleration constant so multiplying with (pos - center) gives v^2
22 // This way, we can inversely correlate height difference with speed
23 ASSERT(m_apex - m_midpoint.y > std::numeric_limits<f32>::epsilon());
24 f32 maxVel = static_cast<f32>(static_cast<s16>(params.setting(0)));
25 m_maxVelSq = maxVel * maxVel;
26 m_accel = m_maxVelSq / (m_apex - m_midpoint.y);
27}
28
30ObjectHeyho::~ObjectHeyho() = default;
31
33void ObjectHeyho::init() {
34 ASSERT(m_railInterpolator);
35 m_railInterpolator->init(0.0f, m_railInterpolator->pointCount() / 2);
36 m_pos = m_railInterpolator->curPos();
37 m_flags.setBit(eFlags::Position);
38 m_currentVel = EGG::Mathf::sqrt(
39 m_maxVelSq - m_accel * (m_railInterpolator->curPos().y - m_midpoint.y));
40
41 m_transformOffset = m_railInterpolator->curTangentDir() * m_currentVel;
42 m_floorCollision = false;
43 m_up = EGG::Vector3f::ey;
44 m_forward = EGG::Vector3f::ez;
45 m_freeFall = false;
46 m_spinFrame = 0;
47
48 changeAnimation(Animation::Move);
49}
50
52void ObjectHeyho::calc() {
53 calcStateTransition();
54 calcMotion();
55 StateManager::calc();
56 calcInterp();
57}
58
60void ObjectHeyho::loadAnims() {
61 std::array<const char *, 4> names = {{
62 "body_color",
63 "move",
64 "jump",
65 "jump_ed",
66 }};
67
68 std::array<Render::AnmType, 4> types = {{
69 Render::AnmType::Pat,
70 Render::AnmType::Chr,
71 Render::AnmType::Chr,
72 Render::AnmType::Chr,
73 }};
74
75 linkAnims(names, types);
76}
77
79void ObjectHeyho::calcCollisionTransform() {
80 auto *objCol = collision();
81 if (!objCol) {
82 return;
83 }
84
86 tm.makeT(EGG::Vector3f(0.0f, 100.0f, 0.0f));
87 calcTransform();
88 EGG::Matrix34f m = m_transform.multiplyTo(tm);
89 objCol->transform(m, m_scale, m_transformOffset);
90}
91
93void ObjectHeyho::calcMove() {
94 m_forward = m_railInterpolator->nextPoint().pos - m_railInterpolator->curPoint().pos;
95 m_floorCollision = false;
96
97 CollisionInfo info;
98
99 if (CollisionDirector::Instance()->checkSphereFull(COLLISION_RADIUS, m_pos + COLLISION_OFFSET,
100 EGG::Vector3f::inf, KCL_TYPE_FLOOR, &info, nullptr, 0)) {
101 m_floorCollision = true;
102 if (info.floorDist > -std::numeric_limits<f32>::min()) {
103 m_floorNrm = info.floorNrm;
104 }
105
106 // Not m_pos += info.tangentOff - m_floorNrm * 60.0f
107 // The former requires m_pos to be the last addition to occur
108 // TODO: 100.0f - 10.0f - 30.0f? Why is this the case?
109 m_pos = m_pos + info.tangentOff - m_floorNrm * 60.0f;
110 m_flags.setBit(eFlags::Position);
111
112 if (m_currentAnim == Animation::Jumped) {
113 const auto *anim = m_drawMdl->anmMgr()->activeAnim(Render::AnmType::Chr);
114 if (anim->frame() >= anim->frameCount()) {
115 changeAnimation(Animation::Move);
116 }
117 }
118 }
119}
120
122void ObjectHeyho::calcJump() {
123 constexpr s16 SPIN_DELAY_FRAMES = 5;
124 constexpr s16 SPIN_RATE = 12; // degrees per frame
125 constexpr s16 SPIN_DEGREES = 720;
126
127 m_floorCollision = false;
128
129 CollisionInfo info;
130 Field::KCLTypeMask flags = KCL_NONE;
131
132 if (CollisionDirector::Instance()->checkSphereFull(COLLISION_RADIUS, m_pos + COLLISION_OFFSET,
133 EGG::Vector3f::inf, KCL_TYPE_VEHICLE_COLLIDEABLE, &info, &flags, 0)) {
134 m_floorCollision = true;
135 if (info.floorDist > -std::numeric_limits<f32>::min()) {
136 m_floorNrm = info.floorNrm;
137 }
138
139 // Not m_pos += info.tangentOff - m_floorNrm * 60.0f
140 // The former requires m_pos to be the last addition to occur
141 // TODO: 100.0f - 10.0f - 30.0f? Why is this the case?
142 m_pos.x = m_pos.x + info.tangentOff.x - m_floorNrm.x * 60.0f;
143 m_pos.z = m_pos.z + info.tangentOff.z - m_floorNrm.z * 60.0f;
144 m_flags.setBit(eFlags::Position);
145
146 if (!(flags & KCL_TYPE_BIT(COL_TYPE_INVISIBLE_WALL)) && m_currentAnim != Animation::Move) {
147 m_forward = m_railInterpolator->nextPoint().pos - m_railInterpolator->curPoint().pos;
148 if (m_currentAnim == Animation::Jump) {
149 m_currentAnim = Animation::Jumped;
150 }
151 } else {
152 m_floorCollision = false;
153 const auto &curPos = m_railInterpolator->curPoint().pos;
154 const auto &nextPos = m_railInterpolator->nextPoint().pos;
155
156 m_forward = (nextPos.y > curPos.y ? nextPos : curPos) - m_midpoint;
157
158 // Red shy guys do a spin. Yes, this is based on color, and no, it's not an animation
159 // We couldn't possibly use even one of the six unused settings for this
160 if (m_color == 0) {
161 s16 frame = m_spinFrame - SPIN_DELAY_FRAMES;
162 if (frame >= 0 && frame <= SPIN_DEGREES / SPIN_RATE) {
164 m.setAxisRotation(static_cast<f32>(frame) *
165 (static_cast<f32>(-SPIN_RATE) * DEG2RAD),
166 m_up);
167 m.setBase(3, EGG::Vector3f::zero);
168 m_forward = m.ps_multVector(m_forward);
169 }
170 }
171
172 ++m_spinFrame;
173 }
174 }
175
176 if (m_railInterpolator->segmentT() > 0.6f && m_currentAnim == Animation::Move) {
177 changeAnimation(Animation::Jump);
178 }
179}
180
182void ObjectHeyho::calcStateTransition() {
183 if (m_railInterpolator->curPoint().setting[1] == 0 ||
184 m_railInterpolator->nextPoint().setting[1] == 0) {
185 if (m_currentStateId != 0) {
186 m_currentStateId = 0;
187 }
188 } else {
189 if (m_currentStateId != 1) {
190 m_currentStateId = 1;
191 }
192 }
193}
194
196void ObjectHeyho::calcMotion() {
197 if (!m_freeFall) {
198 f32 sqVel = m_maxVelSq - m_accel * (m_pos.y - m_midpoint.y);
199 if (sqVel <= 0.0f) {
200 sqVel = 0.001f;
201 }
202 m_currentVel = EGG::Mathf::sqrt(sqVel);
203 m_railInterpolator->setCurrVel(m_currentVel);
204
205 if (m_railInterpolator->calc() == RailInterpolator::Status::ChangingDirection) {
206 // We have an edgecase where the endpoint heights aren't the same
207 // While the current velocity reaches 0 at the apex, it's higher on the other side
208 // To counter this, the object goes off the rail and into free fall until we land
209 m_launchVel = m_currentVel;
210 if (m_currentVel > 1.0f) {
211 m_freeFall = true;
212 }
213 }
214
215 if (m_currentStateId == 0 && !m_floorCollision) {
216 const auto &curPos = m_railInterpolator->curPos();
217 m_pos.x = curPos.x;
218 m_pos.z = curPos.z;
219 m_flags.setBit(eFlags::Position);
220 } else {
221 m_pos = m_railInterpolator->curPos();
222 m_flags.setBit(eFlags::Position);
223 }
224 } else {
225 m_currentVel -= 1.0f;
226 m_pos.y = m_currentVel + m_pos.y;
227 m_flags.setBit(eFlags::Position);
228 }
229
230 // m_currentVel < 0.0f => either we're in free fall or we just snapped to the rail
231 // If we would land back on the rail on the next frame, just let it snap to the rail instead
232 if (m_currentVel < 0.0f &&
233 EGG::Mathf::abs(m_pos.y - m_railInterpolator->curPos().y) <= -m_currentVel) {
234 m_currentVel = m_launchVel;
235 m_freeFall = false;
236 }
237}
238
240void ObjectHeyho::calcInterp() {
241 m_up = Interpolate(0.2f, m_up, m_floorNrm);
242 m_up.normalise2();
243 m_forward.normalise2();
244 setMatrixTangentTo(m_up, m_forward);
245}
246
247} // namespace Field
@ COL_TYPE_INVISIBLE_WALL
Solid wall that is (typically) invisible.
#define KCL_TYPE_BIT(x)
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
#define KCL_TYPE_VEHICLE_COLLIDEABLE
0xEAF8BDFF
A 3 x 4 matrix.
Definition Matrix.hh:8
Matrix34f multiplyTo(const Matrix34f &rhs) const
Multiplies two matrices.
Definition Matrix.cc:189
void setBase(size_t col, const Vector3f &base)
Sets one column of a matrix.
Definition Matrix.cc:181
void setAxisRotation(f32 angle, const Vector3f &axis)
Rotates the matrix about an axis.
Definition Matrix.cc:167
Vector3f ps_multVector(const Vector3f &vec) const
Paired-singles impl. of multVector.
Definition Matrix.cc:224
Pertains to collision.
A 3D float vector.
Definition Vector.hh:88