A collection of things a C caller cannot learn from the headers, gathered while writing the new test suites. Each one cost real time to discover, and each is one comment away from being free.
The caller must integrate gravity
CharacterVirtual_Update(dt, gravity, allocator) does not apply gravity to the character's velocity. Jolt's own header is explicit:
it's your own responsibility to apply gravity to the character velocity
@param inGravity ... only used when the character is standing on top of another object to apply downward force
A character with zero velocity does not move, whatever is passed for gravity. A test asserted otherwise, failed on all three platforms, and the wrapper was right. The documented pattern is read velocity, add gravity * dt, write it back, then Update.
This is the one most likely to cost somebody an afternoon: the symptom is a floating character with no error anywhere.
Preconditions that assert inside Jolt
| call |
precondition |
CharacterVirtual_SetInnerBodyShape |
requires a character created with an innerBodyShape |
RagdollSettings_GetConstraintIndexForBodyIndex |
requires CalculateBodyIndexToConstraintIndex() first; CreateRagdoll does not call it |
TrackedVehicleController_SetDriverInput / SetLeftRatio / SetRightRatio |
upstream asserts ratio != 0.0f, forwarded unguarded |
WheeledVehicleControllerSettings_SetGearRatio |
zero-fills gaps, but the constructor asserts forward ratios > 0 and reverse < 0, so a sparse write asserts far from the cause |
Body_GetWorldSpaceSurfaceNormal |
needs the empty SubShapeID sentinel, for which the C API exports no constant |
Units and signs that the names do not suggest
- Suspension
MinLength is the fully raised pose and MaxLength the drooped one; natural length is max + preload.
- A wheel's two friction curves use different X units: longitudinal is a dimensionless slip ratio in
[0, 0.2], lateral a slip angle in degrees in [0, 20].
limitedSlipRatio must be > 1, with FLT_MAX meaning open.
CastCylinder_Create takes a convex radius fraction; CastSphere_Create takes a radius.
- Every angle is radians.
Null fallbacks indistinguishable from real answers
CharacterVirtual_GetUp(NULL) returns (0,0,0) but Character_GetUp(NULL) returns (0,1,0).
GetGroundState(NULL) returns IN_AIR.
VehicleTransmissionSettings_GetMode(NULL) returns AUTO, which is 0.
Wheel_GetContactSubShapeID(NULL) returns 0, a valid sub-shape ID; the real empty sentinel is 0xFFFFFFFF.
Behaviours that look like bugs and are not
Recorded so nobody "fixes" them during a bump:
Ragdoll::GetPose is not the inverse of SetPose: it returns body 0's world position as the root offset and zeroes joint matrix 0's translation. Get then Set is a fixed point; Set then Get is not.
SkeletalAnimation::GetDuration() reads only animated joint 0's last keyframe. Other joints do not contribute, and Sample's looping wrap uses that value.
VehicleEngine::SetCurrentRPM clamps to [minRPM, maxRPM], so it is not plain assignment.
Wheel::GetContactPosition/Normal/Longitudinal/Lateral assert HasContact() upstream; the wrapper substitutes zeros.
Also
vehicle.cpp:196 reads sp.mFrequency unconditionally. Upstream keeps mFrequency and mStiffness in a union, so this is correct today — but 5.6.0 adds ESpringMode::MassNormalizedStiffnessAndDamping, and if that arrives with the union split the getter returns the wrong field for two of three modes and still compiles. test_vehicle_extra.c pins both ESpringMode and EMotorState as append-only, since both are converted with a bare cast.
A collection of things a C caller cannot learn from the headers, gathered while writing the new test suites. Each one cost real time to discover, and each is one comment away from being free.
The caller must integrate gravity
CharacterVirtual_Update(dt, gravity, allocator)does not apply gravity to the character's velocity. Jolt's own header is explicit:A character with zero velocity does not move, whatever is passed for gravity. A test asserted otherwise, failed on all three platforms, and the wrapper was right. The documented pattern is read velocity, add
gravity * dt, write it back, thenUpdate.This is the one most likely to cost somebody an afternoon: the symptom is a floating character with no error anywhere.
Preconditions that assert inside Jolt
CharacterVirtual_SetInnerBodyShapeinnerBodyShapeRagdollSettings_GetConstraintIndexForBodyIndexCalculateBodyIndexToConstraintIndex()first;CreateRagdolldoes not call itTrackedVehicleController_SetDriverInput/SetLeftRatio/SetRightRatioratio != 0.0f, forwarded unguardedWheeledVehicleControllerSettings_SetGearRatio> 0and reverse< 0, so a sparse write asserts far from the causeBody_GetWorldSpaceSurfaceNormalSubShapeIDsentinel, for which the C API exports no constantUnits and signs that the names do not suggest
MinLengthis the fully raised pose andMaxLengththe drooped one; natural length is max + preload.[0, 0.2], lateral a slip angle in degrees in[0, 20].limitedSlipRatiomust be> 1, withFLT_MAXmeaning open.CastCylinder_Createtakes a convex radius fraction;CastSphere_Createtakes a radius.Null fallbacks indistinguishable from real answers
CharacterVirtual_GetUp(NULL)returns(0,0,0)butCharacter_GetUp(NULL)returns(0,1,0).GetGroundState(NULL)returnsIN_AIR.VehicleTransmissionSettings_GetMode(NULL)returnsAUTO, which is 0.Wheel_GetContactSubShapeID(NULL)returns 0, a valid sub-shape ID; the real empty sentinel is0xFFFFFFFF.Behaviours that look like bugs and are not
Recorded so nobody "fixes" them during a bump:
Ragdoll::GetPoseis not the inverse ofSetPose: it returns body 0's world position as the root offset and zeroes joint matrix 0's translation.GetthenSetis a fixed point;SetthenGetis not.SkeletalAnimation::GetDuration()reads only animated joint 0's last keyframe. Other joints do not contribute, andSample's looping wrap uses that value.VehicleEngine::SetCurrentRPMclamps to[minRPM, maxRPM], so it is not plain assignment.Wheel::GetContactPosition/Normal/Longitudinal/LateralassertHasContact()upstream; the wrapper substitutes zeros.Also
vehicle.cpp:196readssp.mFrequencyunconditionally. Upstream keepsmFrequencyandmStiffnessin a union, so this is correct today — but 5.6.0 addsESpringMode::MassNormalizedStiffnessAndDamping, and if that arrives with the union split the getter returns the wrong field for two of three modes and still compiles.test_vehicle_extra.cpins bothESpringModeandEMotorStateas append-only, since both are converted with a bare cast.