Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Vehicle/TrackedVehicleController.cpp
9912 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Physics/Vehicle/TrackedVehicleController.h>
8
#include <Jolt/Physics/PhysicsSystem.h>
9
#include <Jolt/ObjectStream/TypeDeclarations.h>
10
#include <Jolt/Core/StreamIn.h>
11
#include <Jolt/Core/StreamOut.h>
12
#ifdef JPH_DEBUG_RENDERER
13
#include <Jolt/Renderer/DebugRenderer.h>
14
#endif // JPH_DEBUG_RENDERER
15
16
JPH_NAMESPACE_BEGIN
17
18
JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TrackedVehicleControllerSettings)
19
{
20
JPH_ADD_BASE_CLASS(TrackedVehicleControllerSettings, VehicleControllerSettings)
21
22
JPH_ADD_ATTRIBUTE(TrackedVehicleControllerSettings, mEngine)
23
JPH_ADD_ATTRIBUTE(TrackedVehicleControllerSettings, mTransmission)
24
JPH_ADD_ATTRIBUTE(TrackedVehicleControllerSettings, mTracks)
25
}
26
27
JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheelSettingsTV)
28
{
29
JPH_ADD_ATTRIBUTE(WheelSettingsTV, mLongitudinalFriction)
30
JPH_ADD_ATTRIBUTE(WheelSettingsTV, mLateralFriction)
31
}
32
33
void WheelSettingsTV::SaveBinaryState(StreamOut &inStream) const
34
{
35
inStream.Write(mLongitudinalFriction);
36
inStream.Write(mLateralFriction);
37
}
38
39
void WheelSettingsTV::RestoreBinaryState(StreamIn &inStream)
40
{
41
inStream.Read(mLongitudinalFriction);
42
inStream.Read(mLateralFriction);
43
}
44
45
WheelTV::WheelTV(const WheelSettingsTV &inSettings) :
46
Wheel(inSettings)
47
{
48
}
49
50
void WheelTV::CalculateAngularVelocity(const VehicleConstraint &inConstraint)
51
{
52
const WheelSettingsTV *settings = GetSettings();
53
const Wheels &wheels = inConstraint.GetWheels();
54
const VehicleTrack &track = static_cast<const TrackedVehicleController *>(inConstraint.GetController())->GetTracks()[mTrackIndex];
55
56
// Calculate angular velocity of this wheel
57
mAngularVelocity = track.mAngularVelocity * wheels[track.mDrivenWheel]->GetSettings()->mRadius / settings->mRadius;
58
}
59
60
void WheelTV::Update(uint inWheelIndex, float inDeltaTime, const VehicleConstraint &inConstraint)
61
{
62
CalculateAngularVelocity(inConstraint);
63
64
// Update rotation of wheel
65
mAngle = fmod(mAngle + mAngularVelocity * inDeltaTime, 2.0f * JPH_PI);
66
67
// Reset brake impulse, will be set during post collision again
68
mBrakeImpulse = 0.0f;
69
70
if (mContactBody != nullptr)
71
{
72
// Friction at the point of this wheel between track and floor
73
const WheelSettingsTV *settings = GetSettings();
74
VehicleConstraint::CombineFunction combine_friction = inConstraint.GetCombineFriction();
75
mCombinedLongitudinalFriction = settings->mLongitudinalFriction;
76
mCombinedLateralFriction = settings->mLateralFriction;
77
combine_friction(inWheelIndex, mCombinedLongitudinalFriction, mCombinedLateralFriction, *mContactBody, mContactSubShapeID);
78
}
79
else
80
{
81
// No collision
82
mCombinedLongitudinalFriction = mCombinedLateralFriction = 0.0f;
83
}
84
}
85
86
VehicleController *TrackedVehicleControllerSettings::ConstructController(VehicleConstraint &inConstraint) const
87
{
88
return new TrackedVehicleController(*this, inConstraint);
89
}
90
91
TrackedVehicleControllerSettings::TrackedVehicleControllerSettings()
92
{
93
// Numbers guestimated from: https://en.wikipedia.org/wiki/M1_Abrams
94
mEngine.mMinRPM = 500.0f;
95
mEngine.mMaxRPM = 4000.0f;
96
mEngine.mMaxTorque = 500.0f; // Note actual torque for M1 is around 5000 but we need a reduced mass in order to keep the simulation sane
97
98
mTransmission.mShiftDownRPM = 1000.0f;
99
mTransmission.mShiftUpRPM = 3500.0f;
100
mTransmission.mGearRatios = { 4.0f, 3.0f, 2.0f, 1.0f };
101
mTransmission.mReverseGearRatios = { -4.0f, -3.0f };
102
}
103
104
void TrackedVehicleControllerSettings::SaveBinaryState(StreamOut &inStream) const
105
{
106
mEngine.SaveBinaryState(inStream);
107
108
mTransmission.SaveBinaryState(inStream);
109
110
for (const VehicleTrackSettings &t : mTracks)
111
t.SaveBinaryState(inStream);
112
}
113
114
void TrackedVehicleControllerSettings::RestoreBinaryState(StreamIn &inStream)
115
{
116
mEngine.RestoreBinaryState(inStream);
117
118
mTransmission.RestoreBinaryState(inStream);
119
120
for (VehicleTrackSettings &t : mTracks)
121
t.RestoreBinaryState(inStream);
122
}
123
124
TrackedVehicleController::TrackedVehicleController(const TrackedVehicleControllerSettings &inSettings, VehicleConstraint &inConstraint) :
125
VehicleController(inConstraint)
126
{
127
// Copy engine settings
128
static_cast<VehicleEngineSettings &>(mEngine) = inSettings.mEngine;
129
JPH_ASSERT(inSettings.mEngine.mMinRPM >= 0.0f);
130
JPH_ASSERT(inSettings.mEngine.mMinRPM <= inSettings.mEngine.mMaxRPM);
131
mEngine.SetCurrentRPM(mEngine.mMinRPM);
132
133
// Copy transmission settings
134
static_cast<VehicleTransmissionSettings &>(mTransmission) = inSettings.mTransmission;
135
#ifdef JPH_ENABLE_ASSERTS
136
for (float r : inSettings.mTransmission.mGearRatios)
137
JPH_ASSERT(r > 0.0f);
138
for (float r : inSettings.mTransmission.mReverseGearRatios)
139
JPH_ASSERT(r < 0.0f);
140
#endif // JPH_ENABLE_ASSERTS
141
JPH_ASSERT(inSettings.mTransmission.mSwitchTime >= 0.0f);
142
JPH_ASSERT(inSettings.mTransmission.mShiftDownRPM > 0.0f);
143
JPH_ASSERT(inSettings.mTransmission.mMode != ETransmissionMode::Auto || inSettings.mTransmission.mShiftUpRPM < inSettings.mEngine.mMaxRPM);
144
JPH_ASSERT(inSettings.mTransmission.mShiftUpRPM > inSettings.mTransmission.mShiftDownRPM);
145
146
// Copy track settings
147
for (uint i = 0; i < std::size(mTracks); ++i)
148
{
149
const VehicleTrackSettings &d = inSettings.mTracks[i];
150
static_cast<VehicleTrackSettings &>(mTracks[i]) = d;
151
JPH_ASSERT(d.mInertia >= 0.0f);
152
JPH_ASSERT(d.mAngularDamping >= 0.0f);
153
JPH_ASSERT(d.mMaxBrakeTorque >= 0.0f);
154
JPH_ASSERT(d.mDifferentialRatio > 0.0f);
155
}
156
}
157
158
bool TrackedVehicleController::AllowSleep() const
159
{
160
return mForwardInput == 0.0f // No user input
161
&& mTransmission.AllowSleep() // Transmission is not shifting
162
&& mEngine.AllowSleep(); // Engine is idling
163
}
164
165
void TrackedVehicleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem)
166
{
167
Wheels &wheels = mConstraint.GetWheels();
168
169
// Fill in track index
170
for (size_t t = 0; t < std::size(mTracks); ++t)
171
for (uint w : mTracks[t].mWheels)
172
static_cast<WheelTV *>(wheels[w])->mTrackIndex = (uint)t;
173
174
// Angular damping: dw/dt = -c * w
175
// Solution: w(t) = w(0) * e^(-c * t) or w2 = w1 * e^(-c * dt)
176
// Taylor expansion of e^(-c * dt) = 1 - c * dt + ...
177
// Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough
178
for (VehicleTrack &t : mTracks)
179
t.mAngularVelocity *= max(0.0f, 1.0f - t.mAngularDamping * inDeltaTime);
180
}
181
182
void TrackedVehicleController::SyncLeftRightTracks()
183
{
184
// Apply left to right ratio according to track inertias
185
VehicleTrack &tl = mTracks[(int)ETrackSide::Left];
186
VehicleTrack &tr = mTracks[(int)ETrackSide::Right];
187
188
if (mLeftRatio * mRightRatio > 0.0f)
189
{
190
// Solve: (tl.mAngularVelocity + dl) / (tr.mAngularVelocity + dr) = mLeftRatio / mRightRatio and dl * tr.mInertia = -dr * tl.mInertia, where dl/dr are the delta angular velocities for left and right tracks
191
float impulse = (mLeftRatio * tr.mAngularVelocity - mRightRatio * tl.mAngularVelocity) / (mLeftRatio * tr.mInertia + mRightRatio * tl.mInertia);
192
tl.mAngularVelocity += impulse * tl.mInertia;
193
tr.mAngularVelocity -= impulse * tr.mInertia;
194
}
195
else
196
{
197
// Solve: (tl.mAngularVelocity + dl) / (tr.mAngularVelocity + dr) = mLeftRatio / mRightRatio and dl * tr.mInertia = dr * tl.mInertia, where dl/dr are the delta angular velocities for left and right tracks
198
float impulse = (mLeftRatio * tr.mAngularVelocity - mRightRatio * tl.mAngularVelocity) / (mRightRatio * tl.mInertia - mLeftRatio * tr.mInertia);
199
tl.mAngularVelocity += impulse * tl.mInertia;
200
tr.mAngularVelocity += impulse * tr.mInertia;
201
}
202
}
203
204
void TrackedVehicleController::PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem)
205
{
206
JPH_PROFILE_FUNCTION();
207
208
Wheels &wheels = mConstraint.GetWheels();
209
210
// Update wheel angle, do this before applying torque to the wheels (as friction will slow them down again)
211
for (uint wheel_index = 0, num_wheels = (uint)wheels.size(); wheel_index < num_wheels; ++wheel_index)
212
{
213
WheelTV *w = static_cast<WheelTV *>(wheels[wheel_index]);
214
w->Update(wheel_index, inDeltaTime, mConstraint);
215
}
216
217
// First calculate engine speed based on speed of all wheels
218
bool can_engine_apply_torque = false;
219
if (mTransmission.GetCurrentGear() != 0 && mTransmission.GetClutchFriction() > 1.0e-3f)
220
{
221
float transmission_ratio = mTransmission.GetCurrentRatio();
222
bool forward = transmission_ratio >= 0.0f;
223
float fastest_wheel_speed = forward? -FLT_MAX : FLT_MAX;
224
for (const VehicleTrack &t : mTracks)
225
{
226
if (forward)
227
fastest_wheel_speed = max(fastest_wheel_speed, t.mAngularVelocity * t.mDifferentialRatio);
228
else
229
fastest_wheel_speed = min(fastest_wheel_speed, t.mAngularVelocity * t.mDifferentialRatio);
230
for (uint w : t.mWheels)
231
if (wheels[w]->HasContact())
232
{
233
can_engine_apply_torque = true;
234
break;
235
}
236
}
237
238
// Update RPM only if the tracks are connected to the engine
239
if (fastest_wheel_speed > -FLT_MAX && fastest_wheel_speed < FLT_MAX)
240
mEngine.SetCurrentRPM(fastest_wheel_speed * mTransmission.GetCurrentRatio() * VehicleEngine::cAngularVelocityToRPM);
241
}
242
else
243
{
244
// Update engine with damping
245
mEngine.ApplyDamping(inDeltaTime);
246
247
// In auto transmission mode, don't accelerate the engine when switching gears
248
float forward_input = mTransmission.mMode == ETransmissionMode::Manual? abs(mForwardInput) : 0.0f;
249
250
// Engine not connected to wheels, update RPM based on engine inertia alone
251
mEngine.ApplyTorque(mEngine.GetTorque(forward_input), inDeltaTime);
252
}
253
254
// Update transmission
255
// Note: only allow switching gears up when the tracks are rolling in the same direction
256
mTransmission.Update(inDeltaTime, mEngine.GetCurrentRPM(), mForwardInput, mLeftRatio * mRightRatio > 0.0f && can_engine_apply_torque);
257
258
// Calculate the amount of torque the transmission gives to the differentials
259
float transmission_ratio = mTransmission.GetCurrentRatio();
260
float transmission_torque = mTransmission.GetClutchFriction() * transmission_ratio * mEngine.GetTorque(abs(mForwardInput));
261
if (transmission_torque != 0.0f)
262
{
263
// Apply the transmission torque to the wheels
264
for (uint i = 0; i < std::size(mTracks); ++i)
265
{
266
VehicleTrack &t = mTracks[i];
267
268
// Get wheel rotation ratio for this track
269
float ratio = i == 0? mLeftRatio : mRightRatio;
270
271
// Calculate the max angular velocity of the driven wheel of the track given current engine RPM
272
// Note this adds 0.1% slop to avoid numerical accuracy issues
273
float track_max_angular_velocity = mEngine.GetCurrentRPM() / (transmission_ratio * t.mDifferentialRatio * ratio * VehicleEngine::cAngularVelocityToRPM) * 1.001f;
274
275
// Calculate torque on the driven wheel
276
float differential_torque = t.mDifferentialRatio * ratio * transmission_torque;
277
278
// Apply torque to driven wheel
279
if (t.mAngularVelocity * track_max_angular_velocity < 0.0f || abs(t.mAngularVelocity) < abs(track_max_angular_velocity))
280
t.mAngularVelocity += differential_torque * inDeltaTime / t.mInertia;
281
}
282
}
283
284
// Ensure that we have the correct ratio between the two tracks
285
SyncLeftRightTracks();
286
287
// Braking
288
for (VehicleTrack &t : mTracks)
289
{
290
// Calculate brake torque
291
float brake_torque = mBrakeInput * t.mMaxBrakeTorque;
292
if (brake_torque > 0.0f)
293
{
294
// Calculate how much torque is needed to stop the track from rotating in this time step
295
float brake_torque_to_lock_track = abs(t.mAngularVelocity) * t.mInertia / inDeltaTime;
296
if (brake_torque > brake_torque_to_lock_track)
297
{
298
// Wheels are locked
299
t.mAngularVelocity = 0.0f;
300
brake_torque -= brake_torque_to_lock_track;
301
}
302
else
303
{
304
// Slow down the track
305
t.mAngularVelocity -= Sign(t.mAngularVelocity) * brake_torque * inDeltaTime / t.mInertia;
306
}
307
}
308
309
if (brake_torque > 0.0f)
310
{
311
// Sum the radius of all wheels touching the floor
312
float total_radius = 0.0f;
313
for (uint wheel_index : t.mWheels)
314
{
315
const WheelTV *w = static_cast<WheelTV *>(wheels[wheel_index]);
316
317
if (w->HasContact())
318
total_radius += w->GetSettings()->mRadius;
319
}
320
321
if (total_radius > 0.0f)
322
{
323
brake_torque /= total_radius;
324
for (uint wheel_index : t.mWheels)
325
{
326
WheelTV *w = static_cast<WheelTV *>(wheels[wheel_index]);
327
if (w->HasContact())
328
{
329
// Impulse: p = F * dt = Torque / Wheel_Radius * dt, Torque = Total_Torque * Wheel_Radius / Summed_Radius => p = Total_Torque * dt / Summed_Radius
330
w->mBrakeImpulse = brake_torque * inDeltaTime;
331
}
332
}
333
}
334
}
335
}
336
337
// Update wheel angular velocity based on that of the track
338
for (Wheel *w_base : wheels)
339
{
340
WheelTV *w = static_cast<WheelTV *>(w_base);
341
w->CalculateAngularVelocity(mConstraint);
342
}
343
}
344
345
bool TrackedVehicleController::SolveLongitudinalAndLateralConstraints(float inDeltaTime)
346
{
347
bool impulse = false;
348
349
for (Wheel *w_base : mConstraint.GetWheels())
350
if (w_base->HasContact())
351
{
352
WheelTV *w = static_cast<WheelTV *>(w_base);
353
const WheelSettingsTV *settings = w->GetSettings();
354
VehicleTrack &track = mTracks[w->mTrackIndex];
355
356
// Calculate max impulse that we can apply on the ground
357
float max_longitudinal_friction_impulse = w->mCombinedLongitudinalFriction * w->GetSuspensionLambda();
358
359
// Calculate relative velocity between wheel contact point and floor in longitudinal direction
360
Vec3 relative_velocity = mConstraint.GetVehicleBody()->GetPointVelocity(w->GetContactPosition()) - w->GetContactPointVelocity();
361
float relative_longitudinal_velocity = relative_velocity.Dot(w->GetContactLongitudinal());
362
363
// Calculate brake force to apply
364
float min_longitudinal_impulse, max_longitudinal_impulse;
365
if (w->mBrakeImpulse != 0.0f)
366
{
367
// Limit brake force by max tire friction
368
float brake_impulse = min(w->mBrakeImpulse, max_longitudinal_friction_impulse);
369
370
// Check which direction the brakes should be applied (we don't want to apply an impulse that would accelerate the vehicle)
371
if (relative_longitudinal_velocity >= 0.0f)
372
{
373
min_longitudinal_impulse = -brake_impulse;
374
max_longitudinal_impulse = 0.0f;
375
}
376
else
377
{
378
min_longitudinal_impulse = 0.0f;
379
max_longitudinal_impulse = brake_impulse;
380
}
381
382
// Longitudinal impulse, note that we assume that once the wheels are locked that the brakes have more than enough torque to keep the wheels locked so we exclude any rotation deltas
383
impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse);
384
}
385
else
386
{
387
// Assume we want to apply an angular impulse that makes the delta velocity between track and ground zero in one time step, calculate the amount of linear impulse needed to do that
388
float desired_angular_velocity = relative_longitudinal_velocity / settings->mRadius;
389
float linear_impulse = (track.mAngularVelocity - desired_angular_velocity) * track.mInertia / settings->mRadius;
390
391
// Limit the impulse by max track friction
392
float prev_lambda = w->GetLongitudinalLambda();
393
min_longitudinal_impulse = max_longitudinal_impulse = Clamp(prev_lambda + linear_impulse, -max_longitudinal_friction_impulse, max_longitudinal_friction_impulse);
394
395
// Longitudinal impulse
396
impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse);
397
398
// Update the angular velocity of the track according to the lambda that was applied
399
track.mAngularVelocity -= (w->GetLongitudinalLambda() - prev_lambda) * settings->mRadius / track.mInertia;
400
SyncLeftRightTracks();
401
}
402
}
403
404
for (Wheel *w_base : mConstraint.GetWheels())
405
if (w_base->HasContact())
406
{
407
WheelTV *w = static_cast<WheelTV *>(w_base);
408
409
// Update angular velocity of wheel for the next iteration
410
w->CalculateAngularVelocity(mConstraint);
411
412
// Lateral friction
413
float max_lateral_friction_impulse = w->mCombinedLateralFriction * w->GetSuspensionLambda();
414
impulse |= w->SolveLateralConstraintPart(mConstraint, -max_lateral_friction_impulse, max_lateral_friction_impulse);
415
}
416
417
return impulse;
418
}
419
420
#ifdef JPH_DEBUG_RENDERER
421
422
void TrackedVehicleController::Draw(DebugRenderer *inRenderer) const
423
{
424
float constraint_size = mConstraint.GetDrawConstraintSize();
425
426
// Draw RPM
427
Body *body = mConstraint.GetVehicleBody();
428
Vec3 rpm_meter_up = body->GetRotation() * mConstraint.GetLocalUp();
429
RVec3 rpm_meter_pos = body->GetPosition() + body->GetRotation() * mRPMMeterPosition;
430
Vec3 rpm_meter_fwd = body->GetRotation() * mConstraint.GetLocalForward();
431
mEngine.DrawRPM(inRenderer, rpm_meter_pos, rpm_meter_fwd, rpm_meter_up, mRPMMeterSize, mTransmission.mShiftDownRPM, mTransmission.mShiftUpRPM);
432
433
// Draw current vehicle state
434
String status = StringFormat("Forward: %.1f, LRatio: %.1f, RRatio: %.1f, Brake: %.1f\n"
435
"Gear: %d, Clutch: %.1f, EngineRPM: %.0f, V: %.1f km/h",
436
(double)mForwardInput, (double)mLeftRatio, (double)mRightRatio, (double)mBrakeInput,
437
mTransmission.GetCurrentGear(), (double)mTransmission.GetClutchFriction(), (double)mEngine.GetCurrentRPM(), (double)body->GetLinearVelocity().Length() * 3.6);
438
inRenderer->DrawText3D(body->GetPosition(), status, Color::sWhite, constraint_size);
439
440
for (const VehicleTrack &t : mTracks)
441
{
442
const WheelTV *w = static_cast<const WheelTV *>(mConstraint.GetWheels()[t.mDrivenWheel]);
443
const WheelSettings *settings = w->GetSettings();
444
445
// Calculate where the suspension attaches to the body in world space
446
RVec3 ws_position = body->GetCenterOfMassPosition() + body->GetRotation() * (settings->mPosition - body->GetShape()->GetCenterOfMass());
447
448
DebugRenderer::sInstance->DrawText3D(ws_position, StringFormat("W: %.1f", (double)t.mAngularVelocity), Color::sWhite, constraint_size);
449
}
450
451
RMat44 body_transform = body->GetWorldTransform();
452
453
for (const Wheel *w_base : mConstraint.GetWheels())
454
{
455
const WheelTV *w = static_cast<const WheelTV *>(w_base);
456
const WheelSettings *settings = w->GetSettings();
457
458
// Calculate where the suspension attaches to the body in world space
459
RVec3 ws_position = body_transform * settings->mPosition;
460
Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection);
461
462
// Draw suspension
463
RVec3 min_suspension_pos = ws_position + ws_direction * settings->mSuspensionMinLength;
464
RVec3 max_suspension_pos = ws_position + ws_direction * settings->mSuspensionMaxLength;
465
inRenderer->DrawLine(ws_position, min_suspension_pos, Color::sRed);
466
inRenderer->DrawLine(min_suspension_pos, max_suspension_pos, Color::sGreen);
467
468
// Draw current length
469
RVec3 wheel_pos = ws_position + ws_direction * w->GetSuspensionLength();
470
inRenderer->DrawMarker(wheel_pos, w->GetSuspensionLength() < settings->mSuspensionMinLength? Color::sRed : Color::sGreen, constraint_size);
471
472
// Draw wheel basis
473
Vec3 wheel_forward, wheel_up, wheel_right;
474
mConstraint.GetWheelLocalBasis(w, wheel_forward, wheel_up, wheel_right);
475
wheel_forward = body_transform.Multiply3x3(wheel_forward);
476
wheel_up = body_transform.Multiply3x3(wheel_up);
477
wheel_right = body_transform.Multiply3x3(wheel_right);
478
Vec3 steering_axis = body_transform.Multiply3x3(settings->mSteeringAxis);
479
inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_forward, Color::sRed);
480
inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_up, Color::sGreen);
481
inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_right, Color::sBlue);
482
inRenderer->DrawLine(wheel_pos, wheel_pos + steering_axis, Color::sYellow);
483
484
// Draw wheel
485
RMat44 wheel_transform(Vec4(wheel_up, 0.0f), Vec4(wheel_right, 0.0f), Vec4(wheel_forward, 0.0f), wheel_pos);
486
wheel_transform.SetRotation(wheel_transform.GetRotation() * Mat44::sRotationY(-w->GetRotationAngle()));
487
inRenderer->DrawCylinder(wheel_transform, settings->mWidth * 0.5f, settings->mRadius, w->GetSuspensionLength() <= settings->mSuspensionMinLength? Color::sRed : Color::sGreen, DebugRenderer::ECastShadow::Off, DebugRenderer::EDrawMode::Wireframe);
488
489
if (w->HasContact())
490
{
491
// Draw contact
492
inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactNormal(), Color::sYellow);
493
inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLongitudinal(), Color::sRed);
494
inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLateral(), Color::sBlue);
495
496
DebugRenderer::sInstance->DrawText3D(w->GetContactPosition(), StringFormat("S: %.2f", (double)w->GetSuspensionLength()), Color::sWhite, constraint_size);
497
}
498
}
499
}
500
501
#endif // JPH_DEBUG_RENDERER
502
503
void TrackedVehicleController::SaveState(StateRecorder &inStream) const
504
{
505
inStream.Write(mForwardInput);
506
inStream.Write(mLeftRatio);
507
inStream.Write(mRightRatio);
508
inStream.Write(mBrakeInput);
509
510
mEngine.SaveState(inStream);
511
mTransmission.SaveState(inStream);
512
513
for (const VehicleTrack &t : mTracks)
514
t.SaveState(inStream);
515
}
516
517
void TrackedVehicleController::RestoreState(StateRecorder &inStream)
518
{
519
inStream.Read(mForwardInput);
520
inStream.Read(mLeftRatio);
521
inStream.Read(mRightRatio);
522
inStream.Read(mBrakeInput);
523
524
mEngine.RestoreState(inStream);
525
mTransmission.RestoreState(inStream);
526
527
for (VehicleTrack &t : mTracks)
528
t.RestoreState(inStream);
529
}
530
531
JPH_NAMESPACE_END
532
533