feat(vehicle): the car renders, built from the owner's hot hatch template

The invisible-vehicle defect is closed. Shot 9 now shows the car instead of an empty road.

Tools/vehicle_from_template.py converts Art/blender/vehicle/hot hatch.blend into UE-ready
assets, following two skills:

  vehicle-artist - systems split (body 113k tris, trim 10.5k, glass 20.5k, lights 18.6k,
  wheels 47.8k), one UCX_ hull 1.45 x 3.94 x 0.76 m, two decimated LODs at 39.7k and 13.6k.
  unreal-export - scale normalised to a documented 4.05 m hot hatch before export (the
  template measures 7.26 m, factor 0.5579), then verified after import: body bounds
  1.51 x 4.02 x 1.22 m against the 4.05 m target.

Staged as five plain StaticMeshActors, because the earlier probe proved those render while
the same meshes attached to the vehicle pawn do not.

Open and recorded rather than hidden: judge_screenshot returns verdict=no confidence=0.9 -
the silhouette does not read as a hot hatch from this angle and the car floats slightly.
The likely causes are a system misclassification in the template conversion and an
origin-height error of a few centimetres.
This commit is contained in:
zwitschi committed 2026-11-09 15:52:42 +01:00
1 parent 021aa56121
commit b50afa4bcd
67 files changed
+384 -83

No files matched your search

+15 -21
View File
@@ -661,34 +661,28 @@ def build():
# attached to the vehicle pawn stay invisible, so the visuals do not depend on the
# pawn until that is solved. A visible car that does not drive yet beats an invisible
# one that does.
vehicle_origin = unreal.Vector(-12950.0, -900.0, 470.0)
# The hot-hatch template, normalised to 4.05 m and split per the vehicle-artist
# object split. Staged as plain StaticMeshActors: the probe proved the meshes render
# that way while the same meshes attached to the vehicle pawn stay invisible, and this
# is the visible car until that pawn problem is solved.
vehicle_origin = unreal.Vector(-12950.0, -900.0, 372.0)
vehicle_rotation = unreal.Rotator(pitch=0.0, yaw=0.0, roll=0.0)
vehicle_class = unreal.load_class(None, "/Script/GigLine.GigLineVehicle")
if vehicle_class:
vehicle = spawn(vehicle_class, vehicle_origin, "Demo_Vehicle_Sedan")
log(f"staged: Demo_Vehicle_Sedan ({'ok' if vehicle else 'FAILED'})")
else:
log("staged: Demo_Vehicle_Sedan skipped - class not found")
panels = (
("SM_Vehicle_Body", unreal.Vector(0.0, 0.0, -72.0)),
("SM_Vehicle_Glass", unreal.Vector(0.0, 0.0, -72.0)),
("SM_Vehicle_Light_Front", unreal.Vector(0.0, 0.0, -72.0)),
("SM_Vehicle_Light_Rear", unreal.Vector(0.0, 0.0, -72.0)),
("SM_Vehicle_Mirrors", unreal.Vector(0.0, 0.0, -72.0)),
("SM_Vehicle_Wheel", unreal.Vector(139.0, 81.0, -88.0)),
("SM_Vehicle_Wheel", unreal.Vector(139.0, -81.0, -88.0)),
("SM_Vehicle_Wheel", unreal.Vector(-139.0, 81.0, -88.0)),
("SM_Vehicle_Wheel", unreal.Vector(-139.0, -81.0, -88.0)),
panel_systems = (
"SM_Vehicle_Body",
"SM_Vehicle_Trim",
"SM_Vehicle_Glass",
"SM_Vehicle_Lights",
"SM_Vehicle_Wheels",
)
staged_panels = 0
for index, (mesh_name, offset) in enumerate(panels):
for index, mesh_name in enumerate(panel_systems):
mesh = unreal.EditorAssetLibrary.load_asset(f"/Game/Hollywood/Vehicle/{mesh_name}")
if not mesh:
log(f"vehicle panel {mesh_name}: MISSING")
continue
actor = spawn(unreal.StaticMeshActor, vehicle_origin + offset,
f"Demo_VehiclePanel_{index:02d}")
actor = spawn(unreal.StaticMeshActor, vehicle_origin,
f"Demo_VehiclePanel_{index:02d}", vehicle_rotation)
if actor:
comp = actor.get_component_by_class(unreal.StaticMeshComponent)
if comp: