The physics asset body was missing PhysicsType. BuildPhysicsAsset created the
USkeletalBodySetup with a box but never set Body->PhysicsType, leaving it
PhysType_Default ('follow owner'). The base AWheeledVehiclePawn sets
Mesh->BodyInstance.bSimulatePhysics=false (the Chaos async solver drives it), so
'follow owner' meant the chassis never simulated - the vehicle sat frozen at
spawn (simulating=0) and -drivetest reported speed=0 travelled=0.
Set Body->PhysicsType = EPhysicsType::PhysType_Simulated. Result: simulating=1,
settled at Z 77 cm, and the drive test moves the car (36.3 m). It drives backwards
(-96.5 km/h) - the next bug is the reverse direction, not the physics setup.
The windows material was metallic 1.0 with roughness 0.0, which renders as a black void -
indistinguishable from a cabin with no glass at all. It is now tinted translucent: opacity
0.85, roughness 0.08, dark blue-grey base. The windscreen and side glass read as glass.
Opacity 0.45 was tried first and was worse than either extreme: the glass became invisible
and the greenhouse looked open again. The number is recorded in the tool's log line so the
next person does not repeat it.
The vision judge still returns verdict=no for this frame, disputing that the glazing is dark
and the wheels visible. The image shows both. It is an advisory helper - AGENTS.md says it
never replaces the human verdict - so the disagreement is written into
Screenshots/README.md rather than being resolved by iterating against the model.
Root cause was in the template, not the import path: 'car paint' is a legacy mix-shader
graph. The FBX exporter cannot map that node tree, so SM_Vehicle_Body imported holding
WorldGridMaterial - the engine's checker fallback - and rendered black while Blender showed
red.
Two changes:
- Tools/vehicle_from_template.py converts legacy graphs to a Principled BSDF before export
and picks the SATURATED colour from the mix nodes; a 'brightest wins' rule chose the 0.8
grey diffuse beside the 0.49 red and produced a grey car.
- Tools/ue_vehicle_materials.py creates M_Vehicle_HatchPaint, and the staged body panel
gets it assigned at build time, because a static mesh slot cannot be repointed from
Python after import.
Judge on the new capture: verdict=yes confidence=0.9 - 'a red hatch car with a red body and
dark wheels parked on a road'. The comparison table in Screenshots/README.md now shows both
columns agreeing on colour.
Still open and recorded: the glazing does not read - the greenhouse looks open, so the glass
system is mispositioned or drawn behind the body.
Merged COMPARISON.md into the review README as a generated section between
<!-- blender-comparison:begin/end --> markers, so there is one place to look and one place
to sign off. Tools/build_comparison_md.py rewrites only that block - the hand-written
sections around it are untouched - and formats the file with prettier afterwards, because a
generated table is never byte-identical to prettier's output and the repo gates on
--check.
Run it after every capture or Blender render; it is the routine that keeps the page current
with new images rather than a one-off export.
Both floats had different causes and both were measured.
Character: the player start sat at Z 600 while the capsule settles at Z 561 on this
pavement - the QA self-test has been reporting that settle height all along - so the soles
hung 39 cm in the air. The start now uses the measured 561.
Car: the template's lowest geometry is exhaust and underbody, so grounding the model on its
whole bounding box left the tyres 4 cm above the origin. It is grounded on its WHEELS now.
The staged height is 342 rather than the traced road height of 366, because at 367 the
render still showed a gap: the trace and the visible road mesh disagree by about 25 cm at
that spot, and the number is recorded as empirical rather than derived.
Also adds a foot-contact log to the character at BeginPlay (actor Z, ground, capsule half,
mesh bottom, gap) so the next float is a measurement rather than a look.
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.
The vehicle floated 3.7 m above the street because the physics asset had a body per bone
and the four wheel bodies intersected the chassis body, pushing the whole thing up. It is
now a single chassis body on the root bone (452 x 205 x 40 cm) - the same shape Unreal's
vehicle template uses, because Chaos simulates the wheels itself and they must not be
separate rigid bodies.
Settle check, same spawn point:
before: settled at Z 494 cm
after: settled at Z 119 cm
Added -drivetest, which holds full throttle for 3 s and reports speed and distance. It
fails, and the failure names its own cause: BeginPlay logs '0 wheel objects after register'
while the component reports 4 wheel setups, so no wheels exist to receive torque. Recorded
in TODO.md with the next step - check the skeleton bone names against the setups, then give
the front and rear pairs their own wheel subclasses instead of the base class.
The physics asset could not be made from Python (PhysicsAssetFactory exposes no mesh
property), so a small editor-only C++ helper calls FPhysicsAssetUtils::CreateFromSkeletalMesh
- the same code path as the editor's Create Physics Asset action. It produces
PA_Vehicle_Sedan with 6 bodies and assigns it to SK_Vehicle_Sedan.
AGigLineVehicle is a Chaos wheeled pawn that configures its four wheels from the modelled
dimensions (radius 33 cm, wheelbase 278, track 156) and is staged in DemoMap. A run logs:
spawned at (-12950, -900, 120), 4 wheel setups
settled at Z 494 cm, simulating=1, speed=0 cm/s
That last line is the defect and it is not hidden: the vehicle rests 3.7 m above the road,
so the generated physics bodies do not match the mesh. Recorded in TODO.md with the next
step - inspect the bodies against the mesh, then either fix the creation params or author
two boxes and four wheel spheres explicitly.
Chaos drives a skeletal mesh, so the static system split was not drivable on its own. The
builder now also rigs the vehicle: a root bone plus one bone per wheel, every wheel skinned
to its own bone, exported with the character preset. SK_Vehicle_Sedan imports with
SK_Vehicle_Sedan_Skeleton (508 triangles).
Blocked, and recorded as blocked rather than worked around: Chaos needs a PhysicsAsset, and
unreal.PhysicsAssetFactory in 5.8 exposes no mesh property - setting skeletal_mesh fails
with 'Failed to find property'. Bodies cannot be generated from the mesh in a commandlet,
so the asset has to be authored once in the editor or built in C++ with FPhysicsAssetUtils.
Also fixed the build order: the skeletal join destroys the per-system objects, so the
report and LODs read them before the join - the first attempt died with 'StructRNA of type
Object has been removed'.
Follows the vehicle-artist object split, which the first build violated: body, glass,
front and rear lights, mirrors and a single wheel mesh are now separate objects and
separate assets in /Game/Hollywood/Vehicle. A single merged mesh across all systems is
what the skill forbids for a game vehicle, and it also meant glass could not be made
transparent or lights emissive independently.
LODs decimate rather than duplicate: LOD1 38 triangles, LOD2 14, against 132 for LOD0.
The first pass copied the shell unchanged and produced 84-triangle 'LODs' that saved
nothing.
Imports verified with bounds: body 4.76 x 1.76 x 1.28 m, wheel 0.22 x 0.66 x 0.66 m,
which match the modelled dimensions.
Open, recorded: LOD group wiring needs StaticMeshEditorSubsystem.import_lod, which is
unavailable in a headless commandlet, so the LOD meshes sit as their own assets for now.