gameplay: walkable demo map, reset and pause
Tools/ue_build_demo_map.py builds /Game/Hollywood/DemoMap: sun, sky, the hero facade on the building line, six site meshes imported with collision, and a player start on the north sidewalk. Tools/ue_import_site.py imports the site FBXs; terrain, road and sidewalks use complex-as-simple so a simple hull cannot ramp over the kerbs. Art/blender/site/export_site_fbx.py exports one FBX per object so each can carry its own collision. Reset (R) and pause (Escape) join the character and the input context: reset restores the spawn transform and clears momentum, pause is a real game pause that releases the cursor. DefaultInput.ini captures the mouse on launch. Three placement defects found by measuring the imported actors rather than trusting the plan: the spawn was on the wrong sidewalk because the FBX flip inverts north, a mirror I added to the exporter double-corrected an already correct sidewalk, and the street meshes only carry part of the north walk - the rest is terrain.
This commit is contained in:
1 parent
c9278378ee
commit
cf392a02f7
30 files changed
+519
-9
No files matched your search
@@ -0,0 +1,93 @@
|
||||
"""Export the site meshes from the assembled site .blend, one FBX per object.
|
||||
|
||||
blender -b --factory-startup Art/blender/site/site-2026-11-06.blend \
|
||||
--python Art/blender/site/export_site_fbx.py -- --out /tmp/gigline/site
|
||||
|
||||
Why one file per object: terrain, road, sidewalks and kerbs want different
|
||||
collision settings in Unreal, and a single combined mesh cannot carry that.
|
||||
|
||||
Uses the same named export preset `ue_metric_v1` as the scale test, so the metre
|
||||
to centimetre mapping is identical to the one already verified.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
MESHES = ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North",
|
||||
"Kerb_South", "Kerb_North")
|
||||
|
||||
|
||||
def main():
|
||||
argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--out", default="/tmp/gigline/site")
|
||||
args = ap.parse_args(argv)
|
||||
os.makedirs(args.out, exist_ok=True)
|
||||
|
||||
print(f"[site] scene objects: {len(bpy.data.objects)}")
|
||||
found = []
|
||||
for name in MESHES:
|
||||
obj = bpy.data.objects.get(name)
|
||||
if obj is None or obj.type != "MESH":
|
||||
print(f"[site] MISSING {name}")
|
||||
continue
|
||||
found.append(name)
|
||||
|
||||
for name in found:
|
||||
# export only the object under test: select it, export, deselect
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
obj = bpy.data.objects[name]
|
||||
obj.select_set(True)
|
||||
# Sidewalks were generated with their north/south offsets inverted relative
|
||||
# to the road and kerbs: in Unreal the north sidewalk landed at the south
|
||||
# kerb. Mirror this one object across Y so the export matches the source
|
||||
# frame instead of rotating the actor in the map.
|
||||
mirrored = False # the south sidewalk is correct in the .blend; see street_mesh
|
||||
if mirrored:
|
||||
obj.scale.y *= -1.0
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
# Object mode has no mesh context; flip winding through the mesh data.
|
||||
me = obj.data
|
||||
for poly in me.polygons:
|
||||
verts = list(poly.vertices)
|
||||
verts.reverse()
|
||||
poly.vertices = verts
|
||||
me.update()
|
||||
_ = bpy.context.view_layer
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
path = os.path.join(args.out, name + ".fbx")
|
||||
bpy.ops.export_scene.fbx(
|
||||
filepath=path,
|
||||
use_selection=True,
|
||||
object_types={"MESH"},
|
||||
apply_unit_scale=True,
|
||||
apply_scale_options="FBX_SCALE_NONE",
|
||||
axis_forward="-Y",
|
||||
axis_up="Z",
|
||||
use_space_transform=True,
|
||||
use_mesh_modifiers=True,
|
||||
mesh_smooth_type="OFF",
|
||||
path_mode="AUTO",
|
||||
bake_space_transform=False,
|
||||
)
|
||||
if mirrored:
|
||||
# The flip is an export correction, not a change to the source: undo it.
|
||||
obj.scale.y *= -1.0
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
for poly in obj.data.polygons:
|
||||
verts = list(poly.vertices)
|
||||
verts.reverse()
|
||||
poly.vertices = verts
|
||||
obj.data.update()
|
||||
zs = [(obj.matrix_world @ v.co).z for v in obj.data.vertices]
|
||||
print(f"[site] {name:18} verts {len(obj.data.vertices):6} z {min(zs):+.2f}..{max(zs):+.2f} m "
|
||||
f"-> {path}")
|
||||
|
||||
print(f"[site] exported {len(found)} of {len(MESHES)} meshes")
|
||||
|
||||
|
||||
main()
|
||||
Reference in new issue
Block a user