#include "Debug/DescentShowcaseRig.h" #include "Vehicles/KazahTender.h" #include "TheDescent.h" #include "DescentUnits.h" #include "Components/ChildActorComponent.h" #include "Components/PointLightComponent.h" #include "Components/StaticMeshComponent.h" #include "Engine/StaticMesh.h" #include "Engine/World.h" #include "EngineUtils.h" #include "Materials/MaterialInstanceDynamic.h" #include "UObject/ConstructorHelpers.h" namespace { // Base brightness of the three fill lights, in the game's unitless light units (DESIGN 7.5: no inverse-square falloff, exponent 3). // Since the integration pass the rig has no fog or exposure of its own: ADescentEnvironment lights the shot, these only keep the // shadowed sides readable, and the Tender's own headlamp is the one of DESIGN 7.5. constexpr float ShowcaseFillFront = 2.4f; constexpr float ShowcaseFillRear = 1.4f; constexpr float ShowcaseFillTop = 1.7f; } ADescentShowcaseRig::ADescentShowcaseRig() { PrimaryActorTick.bCanEverTick = false; USceneComponent* Root = CreateDefaultSubobject(TEXT("Root")); SetRootComponent(Root); TenderSlot = CreateDefaultSubobject(TEXT("TenderSlot")); TenderSlot->SetupAttachment(Root); TenderSlot->SetChildActorClass(AKazahTender::StaticClass()); auto MakeFill = [this, Root](const TCHAR* Name, const FVector& OffsetM, const FLinearColor& Color, float Intensity) { UPointLightComponent* L = CreateDefaultSubobject(Name); L->SetupAttachment(Root); L->SetMobility(EComponentMobility::Movable); L->bUseInverseSquaredFalloff = false; L->SetLightFalloffExponent(3.f); L->SetIntensity(Intensity); L->SetLightColor(Color); L->SetAttenuationRadius(Descent::MetresToUU(40.0)); L->SetSourceRadius(40.f); // soft highlight on the metal instead of a pin-prick L->SetCastShadows(false); // fill light: no shadow cost, no hard edges L->SetRelativeLocation(Descent::MetresToUU(OffsetM)); return L; }; FillFront = MakeFill(TEXT("FillFront"), FVector(9.0, 6.0, 5.0), FLinearColor(0.55f, 0.75f, 1.0f), ShowcaseFillFront); FillRear = MakeFill(TEXT("FillRear"), FVector(-9.0, -5.0, -1.5), FLinearColor(0.15f, 0.85f, 0.8f), ShowcaseFillRear); FillTop = MakeFill(TEXT("FillTop"), FVector(0.5, -4.0, 7.5), FLinearColor(0.45f, 0.7f, 0.85f), ShowcaseFillTop); // A dim seabed 4 m under the keel, so the headlight has something to hit and the vehicle has a scale reference. Seabed = CreateDefaultSubobject(TEXT("Seabed")); Seabed->SetupAttachment(Root); Seabed->SetCollisionEnabled(ECollisionEnabled::NoCollision); Seabed->SetMobility(EComponentMobility::Movable); static ConstructorHelpers::FObjectFinder PlaneFinder(TEXT("/Engine/BasicShapes/Plane.Plane")); if (PlaneFinder.Succeeded()) { Seabed->SetStaticMesh(PlaneFinder.Object); } Seabed->SetRelativeLocation(FVector(0.f, 0.f, -Descent::MetresToUU(5.0))); Seabed->SetRelativeScale3D(FVector(400.f, 400.f, 1.f)); // the basic plane is 1 m square: this is 400 m x 400 m Seabed->SetCastShadow(false); } void ADescentShowcaseRig::OnConstruction(const FTransform& Transform) { Super::OnConstruction(Transform); // Tint the seabed with a dynamic instance of the engine's plain shape material. if (Seabed && Seabed->GetNumMaterials() > 0 && !Cast(Seabed->GetMaterial(0))) { if (UMaterialInstanceDynamic* MID = Seabed->CreateAndSetMaterialInstanceDynamic(0)) { MID->SetVectorParameterValue(TEXT("Color"), FLinearColor(0.012f, 0.02f, 0.022f)); } } } void ADescentShowcaseRig::SetFillScale(float Scale) { FillScale = Scale; // Base candela values are the ones set in the constructor; scale relative to them. if (FillFront) { FillFront->SetIntensity(ShowcaseFillFront * Scale); } if (FillRear) { FillRear->SetIntensity(ShowcaseFillRear * Scale); } if (FillTop) { FillTop->SetIntensity(ShowcaseFillTop * Scale); } } void ADescentShowcaseRig::SetOwnEnvironment(bool) { // Kept so older scenarios still compile: the rig has no environment of its own any more (ADescentEnvironment is the only authority). } AKazahTender* ADescentShowcaseRig::GetTender() const { return TenderSlot ? Cast(TenderSlot->GetChildActor()) : nullptr; } ADescentShowcaseRig* ADescentShowcaseRig::FindOrSpawn(UWorld* World, const FVector& LocationM) { if (!World) { return nullptr; } for (TActorIterator It(World); It; ++It) { return *It; } FActorSpawnParameters Params; Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; return World->SpawnActor(Descent::MetresToUU(LocationM), FRotator::ZeroRotator, Params); }