Files
UnrealEngine/Engine/Plugins/PCG/Source/PCGEditor/Private/PCGVolumeFactory.cpp
2025-05-18 13:04:45 +08:00

123 lines
3.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PCGVolumeFactory.h"
#include "PCGComponent.h"
#include "PCGEngineSettings.h"
#include "PCGGraph.h"
#include "PCGSubsystem.h"
#include "PCGVolume.h"
#include "Elements/Framework/TypedElementRegistry.h"
#include "Elements/Interfaces/TypedElementObjectInterface.h"
#include "Subsystems/PlacementSubsystem.h"
#define LOCTEXT_NAMESPACE "PCGVolumeFactory"
UPCGVolumeFactory::UPCGVolumeFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
DisplayName = LOCTEXT("PCGVolumeDisplayName", "PCG Volume");
NewActorClass = APCGVolume::StaticClass();
bUseSurfaceOrientation = false;
}
bool UPCGVolumeFactory::CanCreateActorFrom(const FAssetData& AssetData, FText& OutErrorMsg)
{
if (UActorFactory::CanCreateActorFrom(AssetData, OutErrorMsg))
{
return true;
}
if ( AssetData.IsValid() && !AssetData.IsInstanceOf<UPCGGraphInterface>())
{
OutErrorMsg = LOCTEXT("NoPCGGraph", "A valid PCG graph asset must be specified.");
return false;
}
return true;
}
bool UPCGVolumeFactory::PreSpawnActor(UObject* Asset, FTransform& InOutLocation)
{
if (Super::PreSpawnActor(Asset, InOutLocation))
{
if (const UPCGEngineSettings* Settings = GetDefault<UPCGEngineSettings>())
{
InOutLocation.SetScale3D(Settings->VolumeScale);
}
return true;
}
return false;
}
void UPCGVolumeFactory::PostPlaceAsset(TArrayView<const FTypedElementHandle> InElementHandles, const FAssetPlacementInfo& InPlacementInfo, const FPlacementOptions& InPlacementOptions)
{
Super::PostPlaceAsset(InElementHandles, InPlacementInfo, InPlacementOptions);
// Preview elements are created while dragging an asset, but before dropping.
if (InPlacementOptions.bIsCreatingPreviewElements)
{
return;
}
for (const FTypedElementHandle& PlacedElement : InElementHandles)
{
TTypedElement<ITypedElementObjectInterface> ObjectInterface = UTypedElementRegistry::GetInstance()->GetElement<ITypedElementObjectInterface>(PlacedElement);
AActor* NewActor = ObjectInterface ? ObjectInterface.GetObjectAs<AActor>() : nullptr;
if (!NewActor)
{
continue;
}
UPCGGraphInterface* PCGGraph = Cast<UPCGGraphInterface>(InPlacementInfo.AssetToPlace.GetAsset());
const UPCGEngineSettings* Settings = GetDefault<UPCGEngineSettings>();
if (!PCGGraph || !Settings)
{
continue;
}
const APCGVolume* PCGVolume = CastChecked<APCGVolume>(NewActor);
UPCGComponent* PCGComponent = CastChecked<UPCGComponent>(PCGVolume->GetComponentByClass(UPCGComponent::StaticClass()));
PCGComponent->SetGraph(PCGGraph);
if (Settings->bGenerateOnDrop)
{
if (UPCGSubsystem* Subsystem = PCGComponent->GetSubsystem())
{
TWeakObjectPtr<UPCGComponent> ComponentPtr(PCGComponent);
// Schedule a task to generate this component.
// We cannot generate the component right away because after PostSpawnActor is called, all
// actor components are unregistered and re-registered, which cancels component generation
Subsystem->ScheduleGeneric([ComponentPtr]()
{
if (UPCGComponent* Component = ComponentPtr.Get())
{
// If the component is not valid anymore, just early out.
if (!IsValid(Component))
{
return true;
}
Component->Generate();
}
return true;
}, PCGComponent, /*TaskDependencies=*/{});
}
}
}
}
UObject* UPCGVolumeFactory::GetAssetFromActorInstance(AActor* ActorInstance)
{
const APCGVolume* PCGVolume = CastChecked<APCGVolume>(ActorInstance);
const UPCGComponent* PCGComponent = PCGVolume->GetComponentByClass<UPCGComponent>();
return PCGComponent ? PCGComponent->GetGraph() : nullptr;
}
#undef LOCTEXT_NAMESPACE