gameplay: designated street crossing — zebra markings, corridor cut, up-facing street

- street_mesh.py: continental zebra markings (0.5 m bars, 0.5 m gaps, 3 m
  band) at all three signalised crossings, triangles not quads (FBX SDK
  refuses to triangulate the crown-twisted quads), stripes +6 cm above the
  DEM so they stay above the road surface; winding flipped so every street
  surface faces up (road/sidewalks/markings were 100% down-facing and
  invisible in engine once the terrain stopped covering them).
- terrain_from_dem.py: cut a 10.7 m corridor around the Hollywood Blvd
  centreline so road, kerbs and sidewalks are not buried under the terrain;
  sidewalks themselves cover the transition band, walk-check stays at 0 holes.
- Blender builders save with a writability guard + fatal failure: *.blend
  is LFS-lockable and checks out read-only; a failed save previously left
  downstream steps building from stale files.
- ue_import_site.py: Crossing_Markings imports with no collision (paint on
  the road); ue_build_demo_map.py stages it and chmods the lockable .umap
  before saving (save_map silently returned False on read-only maps).
- ue_build_review_map.py: demo-crossing camera moved to the real crossing
  (s=130.1 m -> UE -24291,-1340) and fixed the ensure_writable path (was
  missing the .umap suffix, so the chmod never fired).
This commit is contained in:
zwitschi committed 2026-11-07 15:46:45 +01:00
1 parent 985dfcbac7
commit bc2e421d33
20 files changed
+133 -36

No files matched your search

+42 -1
View File
@@ -53,6 +53,9 @@ def parse_args():
ap.add_argument("--ymax", type=float, default=110.0)
ap.add_argument("--osm", default=None,
help="OSM extract; building outlines are cut out so rooftops never become terrain")
ap.add_argument("--corridor", type=float, default=10.7,
help="half-width in m of the Hollywood Blvd corridor to cut out so the "
"street meshes (road, kerbs, sidewalks) are not buried under the terrain")
return ap.parse_args(argv)
@@ -110,6 +113,30 @@ def pts_flat(ring):
return ring
def centreline_polyline(osm_path):
"""Hollywood Blvd centreline as a local-frame polyline (Highland -> Orange)."""
nodes, ways = oc.load(osm_path)
line, _, _, _, _ = oc.block_extent(
nodes, ways, "Hollywood Boulevard", "North Orange Drive", "North Highland Avenue")
utm = project_to_utm(line, find_proj())
return [(e - ORIGIN_E, n - ORIGIN_N) for e, n in utm]
def dist_to_polyline(x, y, pts):
best = float("inf")
for (ax, ay), (bx, by) in zip(pts, pts[1:]):
dx, dy = bx - ax, by - ay
l2 = dx * dx + dy * dy
if l2 == 0.0:
continue
t = max(0.0, min(1.0, ((x - ax) * dx + (y - ay) * dy) / l2))
px, py = ax + t * dx, ay + t * dy
d = (x - px) ** 2 + (y - py) ** 2
if d < best:
best = d
return best ** 0.5
def inside(x, y, ring):
"""Ray casting point-in-polygon."""
hit = False
@@ -146,9 +173,12 @@ def main():
f"X {args.xmin}..{args.xmax}, Y {args.ymin}..{args.ymax}")
polys = []
corridor = None
if args.osm:
polys = building_polygons(args.osm)
print(f"masking {len(polys)} building footprints")
corridor = centreline_polyline(args.osm)
print(f"cutting {args.corridor} m corridor around the Hollywood Blvd centreline")
verts = []
ok = []
@@ -168,6 +198,10 @@ def main():
verts.append((x, y, 0.0))
ok.append(False)
continue
if corridor is not None and dist_to_polyline(x, y, corridor) <= args.corridor:
verts.append((x, y, 0.0))
ok.append(False)
continue
z = values[ri * width + ci]
if nodata is not None and z == nodata:
verts.append((x, y, 0.0))
@@ -212,7 +246,14 @@ def main():
out = os.path.abspath(args.out)
os.makedirs(os.path.dirname(out), exist_ok=True)
bpy.ops.wm.save_as_mainfile(filepath=out)
# *.blend is LFS-lockable, so a fresh checkout marks it read-only. Make it
# writable again or the save fails and downstream steps build from a stale
# file — which is worse than failing here.
if os.path.exists(out) and not os.access(out, os.W_OK):
os.chmod(out, 0o644)
result = bpy.ops.wm.save_as_mainfile(filepath=out)
if result != {"FINISHED"}:
raise RuntimeError(f"could not save {out}: {result}")
print(f"saved {out}")