0.1.4 materials pass plus the camera fix levelling exposed. Materials (Tools/ue_build_demo_map.py): - ensure_material() gains optional masonry coursing (world-space course height, joint band, joint tint) and roughness variation, so facades read as coursed stone/stucco instead of one flat specular plane. - WorldPosition is in centimetres: course heights in metres are scaled by 100 (the first attempt divided by 0.55 and the coursing was invisible). - connect_material_expressions returns False rather than raising, so every link now goes through a link() helper that logs failures; UE has no MaterialExpressionLess, so the joint mask is Frac -> Divide -> OneMinus -> Clamp. - New materials: M_FacadeStoneShadow, M_FacadeGlass (specular 0.62 ~ IOR 1.5), M_FacadeBanner. - Material assignment is now per SLOT keyed on the imported slot material name. It was slot-0 only, which on the hero is the banner, so stone, shadow stone and glazing still wore flat Blender materials. Hero now 4/4 slots. Cameras (GigLineShotActor + ue_build_review_map): - Optional per-shot ShotFieldOfView; the shot tuples take a 4th element. - Elevations use a 42-55 deg lens from across the street: with the cameras levelled (0.1.2 follow-up) the building was small in frame, and tilting to fill it is what breaks the verticals. hero-facade-front sky 39% -> 20%, demo-spawn-view 23% -> 16%. Screenshots re-rendered for the whole set.
101 lines
2.7 KiB
C++
101 lines
2.7 KiB
C++
#include "GigLineShotActor.h"
|
|
|
|
#include "Camera/CameraActor.h"
|
|
#include "Camera/CameraComponent.h"
|
|
#include "Engine/World.h"
|
|
#include "GameFramework/PlayerController.h"
|
|
#include "UnrealClient.h"
|
|
|
|
AGigLineShotActor::AGigLineShotActor()
|
|
{
|
|
PrimaryActorTick.bCanEverTick = true;
|
|
PrimaryActorTick.bStartWithTickEnabled = true;
|
|
}
|
|
|
|
void AGigLineShotActor::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
NextShot();
|
|
}
|
|
|
|
void AGigLineShotActor::Tick(float DeltaSeconds)
|
|
{
|
|
Super::Tick(DeltaSeconds);
|
|
|
|
// QA mode: the -qaselftest run owns the process (it walks, resets, pauses,
|
|
// transects, then quits on its own). Skip the screenshot rounds so the game
|
|
// does not exit mid-test via bQuitWhenDone.
|
|
if (FParse::Param(FCommandLine::Get(), TEXT("qaselftest")))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bWaitingForSettle)
|
|
{
|
|
SettleCountdown -= DeltaSeconds;
|
|
if (SettleCountdown <= 0.f)
|
|
{
|
|
bWaitingForSettle = false;
|
|
// Use the stable custom name only (no list-index prefix): inserting or
|
|
// reordering cameras must not renumber files and orphan the old ones.
|
|
const FString Name = CurrentShot < ShotNames.Num()
|
|
? ShotNames[CurrentShot]
|
|
: FString::FromInt(CurrentShot);
|
|
const FString FileName = FString::Printf(TEXT("review-%s"), *Name);
|
|
// Resolution is the game window's; the runner passes -ResX/-ResY.
|
|
FScreenshotRequest::RequestScreenshot(FileName, /*bInShowUI=*/true, false);
|
|
bWaitingForScreenshot = true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (bWaitingForScreenshot && !FScreenshotRequest::IsScreenshotRequested())
|
|
{
|
|
bWaitingForScreenshot = false;
|
|
UE_LOG(LogTemp, Log, TEXT("GigLineShotActor: captured %s"), *FScreenshotRequest::GetFilename());
|
|
++CurrentShot;
|
|
NextShot();
|
|
}
|
|
}
|
|
|
|
void AGigLineShotActor::NextShot()
|
|
{
|
|
if (!PlayerController)
|
|
{
|
|
PlayerController = GetWorld()->GetFirstPlayerController();
|
|
}
|
|
|
|
if (CurrentShot >= ShotLocations.Num() || !PlayerController)
|
|
{
|
|
UE_LOG(LogTemp, Log, TEXT("GigLineShotActor: done (%d shots)"), CurrentShot);
|
|
if (bQuitWhenDone)
|
|
{
|
|
PlayerController->ConsoleCommand(TEXT("quit"));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (CurrentCamera)
|
|
{
|
|
CurrentCamera->Destroy();
|
|
}
|
|
|
|
FActorSpawnParameters Params;
|
|
Params.ObjectFlags |= RF_Transient;
|
|
CurrentCamera = GetWorld()->SpawnActor<ACameraActor>(
|
|
ACameraActor::StaticClass(), ShotLocations[CurrentShot], ShotRotations[CurrentShot], Params);
|
|
if (ShotFieldOfView.IsValidIndex(CurrentShot) && ShotFieldOfView[CurrentShot] > 0.0f)
|
|
{
|
|
if (UCameraComponent* Cam = CurrentCamera->GetCameraComponent())
|
|
{
|
|
// Long lens for elevations: fills the frame from across the street
|
|
// without pitching the camera, so verticals stay parallel.
|
|
Cam->SetFieldOfView(ShotFieldOfView[CurrentShot]);
|
|
}
|
|
}
|
|
PlayerController->SetViewTarget(CurrentCamera);
|
|
|
|
SettleCountdown = SettleSeconds;
|
|
bWaitingForSettle = true;
|
|
}
|