diff --git a/Game/GigLine/Content/Hollywood/DemoMap.umap b/Game/GigLine/Content/Hollywood/DemoMap.umap index 125570e..9565dee 100644 --- a/Game/GigLine/Content/Hollywood/DemoMap.umap +++ b/Game/GigLine/Content/Hollywood/DemoMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c4090fdb335a9a2b3fe7a893e47fee03d76d166b0575fdf3f37221704f70ba9 -size 55060 +oid sha256:4182a28144b21d629e09e38ad2300e4537a9c5fa3ad5ed4ba2f91457f9551a99 +size 53223 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset b/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset index 1cd0afe..786ce6a 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a80b14b851004748983d1409c12d2f505600078134113cab2f53f48f5b48339 +oid sha256:3984f701d136b0931c56a87109f862ed16132ff210cc348cdbbc122f35d98478 size 7876 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset b/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset index 955f0ab..f526b3a 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f62893f52fdde9f16c7716d3eedfce3e90dad531c0679a014ce3f99895d3f9d5 +oid sha256:3a9537f05120a0cb5fc0bacac1fc44847e983ba757866316cb5e9a10fd142e53 size 7881 diff --git a/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset b/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset index 9a12b00..5382da3 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb8aabbf86e9649f2d57bd8687d49f7c871e2853037eae325bdcb9c52512abd6 +oid sha256:89fc0b99258c68c458dfc13e32d2ac8dd1e0977891d5992c38d123657f3a78d0 size 7896 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset b/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset index d68fc4d..b5edd8a 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:adea68105cb7a3987e64a342d5a0aefe7dcd025c5aded71cdad6e535ad919f2d +oid sha256:a143de1f26d5fe2c053571b25e5ead359cfba9ed12b09b9e703fe269fda09161 size 7871 diff --git a/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset b/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset index 1a1eff9..daaddd6 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bf845145364d552eac6aee056cdb3c5c522661c662c5bc27a9d46982cba7fc8 +oid sha256:7d61219af92c368d63d810a880cd479568edd55ec1e13d3a0921a15b5f0bd029 size 7901 diff --git a/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset b/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset index 703aa07..2e27ef9 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08984db88f47a5760d87fc46e60581ec5917a994443f7e9be3dedd97aa1c3c58 +oid sha256:f09f4c1cb9c3d6dc98223d3046cab45453693794ea7089203f442ac4162c9fc6 size 7886 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset b/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset index cb5efd9..5f5c400 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec26dc022aa25a6c39a904f16a56de9c54b9b13b81f50489db23353f7725986a +oid sha256:6ad2b4e24f36480b4089a509186d2cbbf8f03e50d9a6f50342420285acbed791 size 7871 diff --git a/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset b/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset index 4f38add..3d200fd 100644 --- a/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c01d12a342abd31999ada2f3cc98defdff53bb831ed8f39dd6523654b2bc8d4 -size 335530 +oid sha256:2a1e69c853b4d19fd9e445b29d89dd6feb69057f17de54edfdec3d784b3ba779 +size 335528 diff --git a/Game/GigLine/Source/GigLine/GigLineAssetTools.cpp b/Game/GigLine/Source/GigLine/GigLineAssetTools.cpp new file mode 100644 index 0000000..481a8dc --- /dev/null +++ b/Game/GigLine/Source/GigLine/GigLineAssetTools.cpp @@ -0,0 +1,65 @@ +#include "GigLineAssetTools.h" + +#include "Engine/StaticMesh.h" +#include "PhysicsEngine/BodySetup.h" +#include "StaticMeshResources.h" + +void UGigLineAssetTools::SetComplexCollisionAsSimple(UStaticMesh* Mesh, bool bEnable) +{ + if (!Mesh) + { + return; + } + + UBodySetup* BodySetup = Mesh->GetBodySetup(); + if (!BodySetup) + { + return; + } + + BodySetup->CollisionTraceFlag = bEnable ? CTF_UseComplexAsSimple : CTF_UseSimpleAndComplex; + BodySetup->InvalidatePhysicsData(); + BodySetup->CreatePhysicsMeshes(); + Mesh->MarkPackageDirty(); + Mesh->PostEditChange(); +} + +void UGigLineAssetTools::ClearSimpleCollision(UStaticMesh* Mesh) +{ + if (!Mesh) + { + return; + } + + UBodySetup* BodySetup = Mesh->GetBodySetup(); + if (!BodySetup) + { + return; + } + + BodySetup->AggGeom.EmptyElements(); + BodySetup->InvalidatePhysicsData(); + BodySetup->CreatePhysicsMeshes(); + Mesh->MarkPackageDirty(); + Mesh->PostEditChange(); +} + +FString UGigLineAssetTools::GetCollisionComplexity(UStaticMesh* Mesh) +{ + if (!Mesh) + { + return TEXT("no mesh"); + } + + const UBodySetup* BodySetup = Mesh->GetBodySetup(); + if (!BodySetup) + { + return TEXT("no body setup"); + } + + const int32 Flag = static_cast(BodySetup->CollisionTraceFlag.GetValue()); + if (Flag == CTF_UseComplexAsSimple) { return TEXT("UseComplexAsSimple"); } + if (Flag == CTF_UseSimpleAsComplex) { return TEXT("UseSimpleAsComplex"); } + if (Flag == CTF_UseSimpleAndComplex) { return TEXT("UseSimpleAndComplex"); } + return TEXT("ProjectDefault"); +} diff --git a/Game/GigLine/Source/GigLine/GigLineAssetTools.h b/Game/GigLine/Source/GigLine/GigLineAssetTools.h new file mode 100644 index 0000000..cf48766 --- /dev/null +++ b/Game/GigLine/Source/GigLine/GigLineAssetTools.h @@ -0,0 +1,34 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "GigLineAssetTools.generated.h" + +class UStaticMesh; + +/** + * Small editor-side helpers for the headless asset pipeline scripts. + * + * Exposed to Python so tools can fix up imported meshes that the Interchange + * importer gives unusable default collision. The terrain is the motivating + * case: without complex-as-simple it gets a bounding-box proxy, so the whole + * street sits under an invisible 8 m box and the player walks on the box lid. + */ +UCLASS() +class GIGLINE_API UGigLineAssetTools : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + /** Make collision trace against the mesh triangles instead of a proxy hull. */ + UFUNCTION(BlueprintCallable, Category = "GigLine|Assets") + static void SetComplexCollisionAsSimple(UStaticMesh* Mesh, bool bEnable = true); + + /** Drop any generated simple collision (hulls/boxes) from the mesh. */ + UFUNCTION(BlueprintCallable, Category = "GigLine|Assets") + static void ClearSimpleCollision(UStaticMesh* Mesh); + + /** Report the current collision complexity: "SimpleAndComplex", "Simple", ... */ + UFUNCTION(BlueprintCallable, Category = "GigLine|Assets") + static FString GetCollisionComplexity(UStaticMesh* Mesh); +}; diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp index a7fca8d..9a2482c 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp @@ -134,7 +134,7 @@ void AGigLineCharacter::BeginPlay() { if (APlayerController* PC = Cast(GetController())) { - PC->ConsoleCommand(TEXT("HighResShot 1280 720")); + PC->ConsoleCommand(TEXT("HighResShot 1280x720")); } }, 1.5f, false); @@ -146,15 +146,26 @@ void AGigLineCharacter::BeginPlay() { UCharacterMovementComponent* M = GetCharacterMovement(); UE_LOG(LogTemp, Log, - TEXT("GigLine SIM: t=%.2f Z=%.0f grav=%.1f fall=%d local=%d role=%d mode=%d ctrl=%s"), + TEXT("GigLine SIM: t=%.2f XYZ=(%.0f,%.0f,%.0f) grav=%.1f fall=%d local=%d role=%d mode=%d ctrl=%s"), GetWorld() ? GetWorld()->GetTimeSeconds() : -1.f, - GetActorLocation().Z, + GetActorLocation().X, GetActorLocation().Y, GetActorLocation().Z, M ? M->GetGravityZ() : 0.f, (M && M->IsFalling()) ? 1 : 0, IsLocallyControlled() ? 1 : 0, (int)GetLocalRole(), M ? (int)M->MovementMode : -1, GetController() ? *GetController()->GetName() : TEXT("NONE")); + if (UWorld* W = GetWorld()) + { + const FVector L = GetActorLocation(); + FHitResult Hit; + FCollisionQueryParams Q(TEXT("GigLineGroundProbe"), false, this); + const bool bHit = W->LineTraceSingleByChannel(Hit, + FVector(L.X, L.Y, 3000.f), FVector(L.X, L.Y, -3000.f), ECC_Visibility, Q); + UE_LOG(LogTemp, Log, TEXT("GigLine GROUND: top=%.0f actor=%s"), + bHit ? Hit.ImpactPoint.Z : -9999.f, + bHit && Hit.GetActor() ? *Hit.GetActor()->GetActorLabel() : TEXT("none")); + } }, 1.0f, true); } diff --git a/MEMORY.md b/MEMORY.md index 23c2bb5..5ebe9ac 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -6,6 +6,16 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO. --- +## 2026-11-08 — The fall-through was an 8 m invisible box (QA branch) + +- **Symptom:** player "spawned above ground but never fell", sat at Z 909, `fall=0`, `mode=1` (Walking), no fall-through, view looked plausible so it was mistaken for working. +- **Cause:** `Terrain_DEM` had only a **bounding-box simple collision proxy** (Interchange default). A line trace down at *every* Y through the block hit `Demo_Terrain_DEM` at exactly **819** — the mesh bbox max Z. The whole street was under an invisible lid; the pawn stood on the box, 8 m above the road. +- **Proof:** a debug `LineTraceSingleByChannel` profile at Y −1912…+1447 reported `top=819 actor=Demo_Terrain_DEM` for the entire street. After the fix it reports `Sidewalk_North 395`, `Road_Hollywood 366`, `Sidewalk_South 317`. +- **Fix:** force complex-as-simple collision on the terrain. Python **cannot** set it (`StaticMesh`/`BodySetup` expose no such property; `dir()` shows only `lod_for_collision`). Added `UGigLineAssetTools::SetComplexCollisionAsSimple` in the C++ module (`BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple`, UE 5.8 no longer has `bUseComplexAsSimpleCollision`). +- **Second gotcha:** the flag lives on the **asset**, not the map. Setting it in a builder session and saving only the `.umap` loses it — `EditorAssetLibrary.save_loaded_asset()` is required, otherwise the next run reloads the bbox proxy. +- **Third gotcha:** `HighResShot 1280 720` means **multiplier 1280** (UE warns "the high resolution screenshot multiplier is too large for your system"). The correct form is `HighResShot 1280x720`. +- Resulting spawn: `(-13336, -1150, 600)` — inside the carved corridor, lands on the road at Z 456 (feet 368 on road top 366). + ## 2026-11-07 — Publication audit - MPFB 2.0.18's installed `blender_manifest.toml` identifies its code as GPL-3.0-or-later; its versioned `LICENSE.md` explicitly puts bundled mesh, texture, and rig assets under CC0 1.0. The earlier CC0 claim for the add-on code was wrong. diff --git a/Tools/ue_build_demo_map.py b/Tools/ue_build_demo_map.py index 2c897b8..c7d907a 100644 --- a/Tools/ue_build_demo_map.py +++ b/Tools/ue_build_demo_map.py @@ -301,6 +301,19 @@ def build(): mesh = unreal.EditorAssetLibrary.load_asset(f"/Game/Hollywood/Site/{name}") if mesh is None: continue + # The imported terrain carries a bounding-box simple collision proxy, + # which puts an invisible lid ~8 m over the whole block: the player + # walks on that box instead of the street. Force trace against the real + # triangles so the carved street corridor is actually open. + if "Terrain" in name: + asset = unreal.EditorAssetLibrary.load_asset(f"/Game/Hollywood/Site/{name}") + if asset: + unreal.GigLineAssetTools.set_complex_collision_as_simple(asset, True) + # The flag lives on the asset, not the map: persist it or the + # next session reloads the bbox proxy and the street is buried. + saved_asset = unreal.EditorAssetLibrary.save_loaded_asset(asset) + log(f"terrain collision: {unreal.GigLineAssetTools.get_collision_complexity(asset)} " + f"(saved asset: {saved_asset})") actor = spawn(unreal.StaticMeshActor, unreal.Vector(0, 0, 0), f"Demo_{name}") if actor: comp = actor.static_mesh_component @@ -372,14 +385,17 @@ def build(): # sidewalk is at Y -1912..-1053 and the SOUTH sidewalk at Y +718..+1447. # The hero facade is on the SOUTH side at Y +1307. Stand on the north # sidewalk and face south (+Y, yaw 90) across the road at it. - # Z: the terrain here is ~Z 99-138. The capsule half-height is 87.5 cm, so - # feet must start ABOVE the surface or the point is embedded and UE rejects - # the PlayerStart ("NO PLAYERSTART with positive rating"), dropping the pawn - # at the world origin to fall through. Cap feet ~80 cm above grade: centre - # ~138+80+87 = 305, so Z 300 lets it drop cleanly and gravity settles it. - spawn(unreal.PlayerStart, unreal.Vector(-13336.0, -1700.0, 300.0), "Demo_PlayerStart", + # CRITICAL: the terrain is only carved inside the street corridor, and + # terrain_from_dem.py --corridor 10.7 is a HALF-width around the road + # centreline (Y ~= -167), so the cut band is Y -1237..+902. A PlayerStart at + # Y -1700 sits OUTSIDE it, under ~8 m of uncut terrain: the capsule spawns + # embedded, depenetration pops it to Z ~909 (terrain top 819 + half-height + # 87.5) and it never touches the sidewalk. Y -1150 is on the north sidewalk + # (Y -1912..-1053) AND inside the cut, so the pavement is the ground. + # Z 600 is clear air; the capsule drops onto the pavement and settles. + spawn(unreal.PlayerStart, unreal.Vector(-13336.0, -1150.0, 600.0), "Demo_PlayerStart", unreal.Rotator(pitch=0.0, yaw=90.0, roll=0.0)) - log("staged: Demo_PlayerStart on the north sidewalk (cap above grade), facing south (+Y)") + log("staged: Demo_PlayerStart on the cut north sidewalk, facing south (+Y)") # A lockable LFS .umap checks out read-only, which makes save_map silently # return False — chmod u+w first or the staged actors never persist.