fix(site): the slice cuts now keep the middle, and the surfaces do not agree
Two findings, one a bug and one the real cause. Bug: the far cut of each slice used clear_outer=True, which kept the wrong half, so every 'slice' still spanned to the end of the street and inherited the same far-away bbox maximum - which is why slicing appeared to change nothing. Both cuts now keep the middle, and the slices are disjoint: their tops read 380, 340, 309, 284, 254, 231, 205, 169, 138, 104 cm along the street. Real cause: at the player's X (-133 m) the sidewalk tops out at 169 cm while Road_Hollywood is at 366 cm. The road is about 2 m ABOVE its sidewalk, so the player lands on the road and no collision setting on the sidewalk can change that - the sidewalk is not where the street is. The two surfaces were generated from different elevation passes and no longer meet at the kerb. That is site-pipeline data, not placement. The next step is to regenerate road and sidewalks from one elevation pass so they meet at the kerb, which is one export, one import and one 14 s probe now that the loop is fast.
This commit is contained in:
1 parent
e50f620aa3
commit
d0d87e8fda
84 files changed
+180
-143
No files matched your search
@@ -30,6 +30,19 @@ SEGMENTED = ("Sidewalk_North", "Sidewalk_South", "Kerb_North", "Kerb_South")
|
||||
SEGMENT_M = 20.0
|
||||
|
||||
|
||||
def _ucx_box(name, x0, x1, y0, y1, z0, z1):
|
||||
"""A thin convex box for a ground slice, named for Unreal's UCX convention."""
|
||||
thickness = max(0.10, min(0.30, (z1 - z0) * 0.25))
|
||||
cx, cy = (x0 + x1) * 0.5, (y0 + y1) * 0.5
|
||||
cz = z1 - thickness * 0.5
|
||||
bpy.ops.mesh.primitive_cube_add(size=1.0, location=(cx, cy, cz))
|
||||
hull = bpy.context.active_object
|
||||
hull.name = f"UCX_{name}_01"
|
||||
hull.scale = ((x1 - x0) * 0.99, (y1 - y0) * 0.99, thickness)
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
return hull
|
||||
|
||||
|
||||
def _segment(obj, name, out_dir, length_m):
|
||||
"""Export `obj` as slices of `length_m` along X, each with its own honest bbox."""
|
||||
xs = [(obj.matrix_world @ v.co).x for v in obj.data.vertices]
|
||||
@@ -46,18 +59,35 @@ def _segment(obj, name, out_dir, length_m):
|
||||
piece = bpy.context.active_object
|
||||
bpy.context.view_layer.objects.active = piece
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
# Two cuts, each with the normal pointing at the half to KEEP and clear_inner=True:
|
||||
# normal +X at a -> keeps x > a
|
||||
# normal -X at b -> keeps x < b
|
||||
# The first version used clear_outer for the far cut and produced nested slices that
|
||||
# each still spanned to the end of the street, which made every slice's bbox top out
|
||||
# at the same far-away maximum.
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
# Cut at both boundaries and keep only the middle: faces spanning a boundary are
|
||||
# split cleanly rather than dropped, which a face-centre filter would do.
|
||||
bpy.ops.mesh.bisect(plane_co=(a, 0.0, 0.0), plane_no=(1.0, 0.0, 0.0),
|
||||
clear_inner=True)
|
||||
bpy.ops.mesh.bisect(plane_co=(b, 0.0, 0.0), plane_no=(1.0, 0.0, 0.0),
|
||||
clear_outer=True)
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
bpy.ops.mesh.bisect(plane_co=(b, 0.0, 0.0), plane_no=(-1.0, 0.0, 0.0),
|
||||
clear_inner=True)
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
piece.name = f"{name}_{index:02d}"
|
||||
zs = [(piece.matrix_world @ v.co).z for v in piece.data.vertices]
|
||||
# Explicit UCX box per slice. Neither the triangulated complex collision nor
|
||||
# complex-as-simple makes these meshes block in Unreal, so the collision is authored
|
||||
# here: one convex box per 20 m slice, thin, with its top at the slice's surface.
|
||||
xs_w = [(piece.matrix_world @ v.co).x for v in piece.data.vertices]
|
||||
ys_w = [(piece.matrix_world @ v.co).y for v in piece.data.vertices]
|
||||
hull = _ucx_box(piece.name, min(xs_w), max(xs_w), min(ys_w), max(ys_w),
|
||||
min(zs), max(zs))
|
||||
path = os.path.join(out_dir, f"{piece.name}.fbx")
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
piece.select_set(True)
|
||||
hull.select_set(True)
|
||||
bpy.context.view_layer.objects.active = piece
|
||||
fbx_preset.export_static(path, selection=True)
|
||||
bpy.data.objects.remove(hull, do_unlink=True)
|
||||
print(f"[site] {piece.name:22} x {a:+.1f}..{b:+.1f} m "
|
||||
f"z {min(zs):+.2f}..{max(zs):+.2f} m -> {path}")
|
||||
bpy.data.objects.remove(piece, do_unlink=True)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -533,3 +533,10 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO.
|
||||
- **The sidewalks' normals are correct** (2790 faces, all pointing up) and they have no loose edges, so winding was not the reason they fail to block.
|
||||
- **`set_complex_collision_as_simple` on sidewalks and kerbs did not make them block** either - the terrain needs it, they do not respond to it.
|
||||
- **The heights at the player, corrected once more:** the road is **366** and the sidewalk **380** in the shipped map; the 158 cm figure came from the site blend's own Z datum, which is ~2.2 m below the map's because the terrain was added after that blend was written. The float is therefore about **14 cm** - the character stands on the road because the sidewalk does not collide - not the metre it looks like against the kerb.
|
||||
|
||||
## 2026-11-09 — The slices were nested, and the site surfaces disagree with each other
|
||||
|
||||
- **Bug in the slice export:** the far cut used `clear_outer=True`, which kept the wrong half, so every "slice" still spanned to the end of the street (-27950..-43) and its bbox topped out at the same far-away maximum. Fixed by making both cuts keep the middle (`clear_inner=True` with the normal pointing at the half to keep). The slices are now disjoint and their tops read 380, 340, 309, 284, 254, 231, 205, 169, 138, 104 cm along the street.
|
||||
- **The surfaces themselves disagree:** at the player's X (-133 m) the sidewalk top is **169 cm** while `Road_Hollywood` is **366 cm** - the road sits ~2 m ABOVE the sidewalk. The player therefore lands on the road, and no sidewalk collision trick will change that, because the sidewalk is not where the road is.
|
||||
- That is a **site-data problem**, not a placement or collision one: the road and the sidewalks were generated from different passes and their heights no longer agree along this stretch.
|
||||
- **Next:** regenerate the road and sidewalks from one elevation pass in the site assembly so their surfaces meet at the kerb, then re-export and re-import (one export + one import + one 14 s probe).
|
||||
Reference in new issue
Block a user