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