From ecdd8208dbc5c28ca4eb33cd90708fed28cce168 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Tue, 10 Nov 2026 19:27:36 +0100 Subject: [PATCH] fix(vehicle): author wheel axles horizontally; record reverse is not the axle Wheel axles were vertical in Unreal (the FBX armature conversion rolled them 90 deg about X). Fix: axis_forward:X in the character preset and a Blender-Z wheel tail so the axle lands on Unreal Y. This is correct (reference pose FL roll 180 / FR roll 0) but does not fix the reverse: -drivetest still -2737 cm/s, and FinalRatio sign is a no-op. The remaining cause is the Chaos wheel spin-axis convention or engine torque sign. --- MEMORY.md | 13 +++++++++++++ Tools/blender/fbx_export_preset.py | 1 + Tools/blender/vehicle_build.py | 6 ++++-- Tools/unreal/ue_vehicle_import.py | 5 +++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/MEMORY.md b/MEMORY.md index bda8850..27e56f9 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -1092,3 +1092,16 @@ Historical task context from 2026-11-09/10; later vehicle findings above may sup fix** — rotate each wheel bone's reference pose −90° about X in `ue_vehicle_import.py` (or a commandlet) so the axle lands on Y. Verify with the `get_ref_bone_pose` probe (axle should read ±Y) before re-running `-drivetest`. + +## 2026-11-10 — Vehicle reverse: not the axle, not the gear ratio + +- The reverse direction is **not** the axle orientation and **not** the final-drive ratio: + flipping the wheel-bone axle to horizontal (Unreal ±Y, via `axis_forward: X` + a Blender-Z + tail) changed the reference pose (FL roll 180, FR roll 0) but the `-drivetest` still reads + `speed=-2737 cm/s`. `FinalRatio = -3.5` also changed nothing (it is a magnitude; the gear + direction is `ForwardGearRatios` vs `ReverseGearRatios`). +- So the wheels spin backward for positive torque regardless of axle direction, which points + at the **wheel spin axis convention in the Chaos solver** (the `FSimpleWheel` angular + velocity axis) or the engine torque sign, not the bone authoring. The horizontal-axle fix + is kept (it is correct even if insufficient): `axis_forward: X` in the character preset and + a Blender-Z wheel tail (`r - copysign(0.25, y)`) so the axle lands on Unreal Y. diff --git a/Tools/blender/fbx_export_preset.py b/Tools/blender/fbx_export_preset.py index fc2f459..6cc201c 100644 --- a/Tools/blender/fbx_export_preset.py +++ b/Tools/blender/fbx_export_preset.py @@ -60,6 +60,7 @@ _METRIC = { _CHARACTER = dict( _METRIC, object_types={"ARMATURE", "MESH"}, + axis_forward="X", add_leaf_bones=False, path_mode="COPY", embed_textures=False, diff --git a/Tools/blender/vehicle_build.py b/Tools/blender/vehicle_build.py index 5a54388..aa2493c 100644 --- a/Tools/blender/vehicle_build.py +++ b/Tools/blender/vehicle_build.py @@ -199,8 +199,10 @@ def build_skeletal(joined, wheels, out_fbx, exp_dir): for name, (x, y) in wheel_positions.items(): bone = edit_bones.new(name) bone.head = (x, y, r) - # Point the bone along the axle: the wheel spins about its own Y. - bone.tail = (x, y - math.copysign(0.25, y), r) + # Point the bone along the axle in BLENDER Z: the Unreal FBX import maps (x,y,z)-> + # (x,-z,-y), so a tail along Z lands on Unreal Y (horizontal axle). Opposite sides + # get opposite signs (left down, right up) so the axles point outward. + bone.tail = (x, y, r - math.copysign(0.25, y)) bone.parent = root bpy.ops.object.mode_set(mode="OBJECT") diff --git a/Tools/unreal/ue_vehicle_import.py b/Tools/unreal/ue_vehicle_import.py index 2ddfe1c..582dd9c 100644 --- a/Tools/unreal/ue_vehicle_import.py +++ b/Tools/unreal/ue_vehicle_import.py @@ -119,6 +119,11 @@ def main(): ui.import_animations = False ui.import_materials = True ui.skeletal_mesh_import_data.import_uniform_scale = 1.0 + # The FBX is already authored in the Unreal frame (nose +X, axle along Y); the + # importer's default axis conversion otherwise rolls the armature 90 deg about X and + # the wheel axles land on Z, which drives the Chaos vehicle backward. + ui.skeletal_mesh_import_data.import_rotation = unreal.Rotator(0.0, 0.0, 0.0) + ui.skeletal_mesh_import_data.import_translation = unreal.Vector(0.0, 0.0, 0.0) task.options = ui unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) skel = unreal.EditorAssetLibrary.load_asset(f"{DEST}/SK_Vehicle_Sedan")