fix(placement): character and car stand on the ground, not above it

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.
This commit is contained in:
zwitschi committed 2026-11-09 15:59:52 +01:00
1 parent b50afa4bcd
commit a17e982561
47 files changed
+124 -62

No files matched your search

+17 -2
View File
@@ -67,6 +67,16 @@ def classify(obj):
return "body"
def min_z(objects):
from mathutils import Vector
lowest = None
for obj in objects:
for corner in obj.bound_box:
z = (obj.matrix_world @ Vector(corner)).z
lowest = z if lowest is None else min(lowest, z)
return lowest if lowest is not None else 0.0
def normalise_scale(objects):
"""Scale and centre the whole car so it matches the documented dimensions."""
xs, ys, zs = [], [], []
@@ -89,7 +99,9 @@ def normalise_scale(objects):
obj.location = tuple(l * factor for l in obj.location)
bpy.context.view_layer.update()
# Drop the car onto Z 0 and centre it on the origin in X/Y.
# Drop the car onto Z 0 and centre it on the origin in X/Y. Ground on the WHEELS, not
# on the whole model: the lowest geometry in the file is exhaust/underbody, so grounding
# on everything left the tyres floating by the difference.
xs, ys, zs = [], [], []
for obj in objects:
for corner in obj.bound_box:
@@ -97,8 +109,11 @@ def normalise_scale(objects):
xs.append(world.x)
ys.append(world.y)
zs.append(world.z)
wheels = [o for o in objects if "rubber" in [m.name.lower() for m in o.data.materials if m]]
ground = min_z(wheels) if wheels else min(zs)
log(f"grounding on {'wheels' if wheels else 'whole model'}: lowest Z {ground:.3f} m")
offset = __import__("mathutils").Vector(
(-(min(xs) + max(xs)) / 2.0, -(min(ys) + max(ys)) / 2.0, -min(zs)))
(-(min(xs) + max(xs)) / 2.0, -(min(ys) + max(ys)) / 2.0, -ground))
for obj in objects:
obj.location = tuple(l + o for l, o in zip(obj.location, offset))
bpy.context.view_layer.update()