Files
UnrealEngineUWP/Engine/Source/Editor/Cascade/Private/CascadePreviewViewportClient.cpp
Marc Audy a7c9001a94 Merging //UE5/Release-Engine-Staging to Main (//UE5/Main) @ 14075166
#rb
#rnx

[CL 14075271 by Marc Audy in ue5-main branch]
2020-08-11 01:36:57 -04:00

836 lines
29 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CascadePreviewViewportClient.h"
#include "EngineGlobals.h"
#include "UObject/Package.h"
#include "Engine/Engine.h"
#include "Components/StaticMeshComponent.h"
#include "Settings/LevelEditorViewportSettings.h"
#include "Serialization/ArchiveCountMem.h"
#include "Preferences/CascadeOptions.h"
#include "CanvasItem.h"
#include "Engine/Canvas.h"
#include "ParticleHelper.h"
#include "Engine/StaticMesh.h"
#include "ImageUtils.h"
#include "SEditorViewport.h"
#include "Components/VectorFieldComponent.h"
#include "CascadeParticleSystemComponent.h"
#include "Cascade.h"
#include "SCascadePreviewViewport.h"
#include "Particles/Spawn/ParticleModuleSpawn.h"
#include "Particles/TypeData/ParticleModuleTypeDataGpu.h"
#include "Particles/VectorField/ParticleModuleVectorFieldLocal.h"
#include "Particles/ParticleLODLevel.h"
#include "Particles/ParticleModuleRequired.h"
#include "Components/LineBatchComponent.h"
#include "Physics/PhysicsInterfaceCore.h"
#define LOCTEXT_NAMESPACE "CascadeViewportClient"
FCascadeEdPreviewViewportClient::FCascadeEdPreviewViewportClient(TWeakPtr<FCascade> InCascade, const TSharedRef<SCascadePreviewViewport>& InCascadeViewport)
: FEditorViewportClient(nullptr, nullptr, StaticCastSharedRef<SEditorViewport>(InCascadeViewport))
, CascadePtr(InCascade)
, CascadePreviewScene(FPreviewScene::ConstructionValues()
.SetLightRotation(FRotator(-45.f, 180.f, 0.f))
.SetSkyBrightness(0.25f)
.SetLightBrightness(1.f))
, VectorFieldHitproxyInfo(0)
, LightRotSpeed(0.22f)
{
PreviewScene = &CascadePreviewScene;
check(CascadePtr.IsValid() && EditorViewportWidget.IsValid());
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
UCascadeParticleSystemComponent* ParticleSystemComponent = CascadePtr.Pin()->GetParticleSystemComponent();
UVectorFieldComponent* LocalVectorFieldPreviewComponent = CascadePtr.Pin()->GetLocalVectorFieldComponent();
UCascadeOptions* EditorOptions = CascadePtr.Pin()->GetEditorOptions();
check(EditorOptions);
// Create ParticleSystemComponent to use for preview.
ParticleSystemComponent->CascadePreviewViewportPtr = this;
ParticleSystemComponent->CastShadow = true;
CascadePreviewScene.AddComponent(ParticleSystemComponent, FTransform::Identity);
ParticleSystemComponent->SetFlags(RF_Transactional);
// Create a component for previewing local vector fields.
LocalVectorFieldPreviewComponent->bPreviewVectorField = true;
LocalVectorFieldPreviewComponent->SetVisibility(false);
CascadePreviewScene.AddComponent(LocalVectorFieldPreviewComponent,FTransform::Identity);
// Use game defaults to hide emitter sprite etc., but we want to still show the Axis widget in the corner...
// todo: seems this cold be done cleaner
EngineShowFlags = FEngineShowFlags(ESFIM_Game);
EngineShowFlags.Game = 0;
EngineShowFlags.SetSnap(0);
SetViewMode(VMI_Lit);
EngineShowFlags.DisableAdvancedFeatures();
EngineShowFlags.SetCompositeEditorPrimitives(true);
EngineShowFlags.SeparateTranslucency = true;
OverrideNearClipPlane(1.0f);
SetViewLocation( FVector(-200.f, 0.f, 0.f) );
SetViewRotation( FRotator::ZeroRotator );
PreviewAngle = FRotator::ZeroRotator;
PreviewDistance = 0.f;
bCaptureScreenShot = false;
BackgroundColor = FColor::Black;
WidgetAxis = EAxisList::None;
WidgetMM = WMM_Translate;
bManipulatingVectorField = false;
DrawFlags = ParticleCounts | ParticleSystemCompleted;
bUsingOrbitCamera = true;
WireSphereRadius = 150.0f;
FColor GridColorAxis(0, 0, 80);
FColor GridColorMajor(0, 0, 72);
FColor GridColorMinor(0, 0, 64);
GridColorAxis = CascadePtr.Pin()->GetEditorOptions()->GridColor_Hi;
GridColorMajor = CascadePtr.Pin()->GetEditorOptions()->GridColor_Low;
GridColorMinor = CascadePtr.Pin()->GetEditorOptions()->GridColor_Low;
DrawHelper.bDrawGrid = CascadePtr.Pin()->GetEditorOptions()->bShowGrid;
DrawHelper.GridColorAxis = GridColorAxis;
DrawHelper.GridColorMajor = GridColorMajor;
DrawHelper.GridColorMinor = GridColorMinor;
DrawHelper.bDrawKillZ = false;
DrawHelper.bDrawWorldBox = false;
DrawHelper.bDrawPivot = false;
DrawHelper.PerspectiveGridSize = CascadePtr.Pin()->GetEditorOptions()->GridPerspectiveSize;
DrawHelper.DepthPriorityGroup = SDPG_World;
if (DrawHelper.bDrawGrid)
{
EngineShowFlags.SetGrid(true);
}
if (EditorOptions->FloorMesh == TEXT(""))
{
if (ParticleSystem != NULL)
{
EditorOptions->FloorMesh = ParticleSystem->FloorMesh;
EditorOptions->FloorScale = ParticleSystem->FloorScale;
EditorOptions->FloorScale3D = ParticleSystem->FloorScale3D;
}
else
{
EditorOptions->FloorMesh = FString::Printf(TEXT("/Engine/EditorMeshes/AnimTreeEd_PreviewFloor.AnimTreeEd_PreviewFloor"));
EditorOptions->FloorScale = 1.0f;
EditorOptions->FloorScale3D = FVector(1.0f, 1.0f, 1.0f);
}
EditorOptions->bShowFloor = false;
}
UStaticMesh* Mesh = NULL;
FloorComponent = NULL;
if (ParticleSystem)
{
Mesh = (UStaticMesh*)StaticLoadObject(UStaticMesh::StaticClass(),NULL,
*(ParticleSystem->FloorMesh),NULL,LOAD_None,NULL);
}
if ((Mesh == NULL) && (EditorOptions->FloorMesh != TEXT("")))
{
Mesh = (UStaticMesh*)StaticLoadObject(UStaticMesh::StaticClass(),NULL,
*(EditorOptions->FloorMesh),NULL,LOAD_None,NULL);
}
if (Mesh == NULL)
{
// Safety catch...
EditorOptions->FloorMesh = FString::Printf(TEXT("/Engine/EditorMeshes/AnimTreeEd_PreviewFloor.AnimTreeEd_PreviewFloor"));
Mesh = (UStaticMesh*)StaticLoadObject(UStaticMesh::StaticClass(),NULL,
*(EditorOptions->FloorMesh),NULL,LOAD_None,NULL);
}
if (Mesh)
{
FloorComponent = NewObject<UStaticMeshComponent>(GetTransientPackage(), TEXT("FloorComponent"));
check(FloorComponent);
FloorComponent->SetStaticMesh(Mesh);
FloorComponent->DepthPriorityGroup = SDPG_World;
// Hide it for now...
FloorComponent->SetVisibility(EditorOptions->bShowFloor);
if (ParticleSystem)
{
FloorComponent->SetRelativeLocation_Direct(ParticleSystem->FloorPosition);
FloorComponent->SetRelativeRotation_Direct(ParticleSystem->FloorRotation);
FloorComponent->SetRelativeScale3D(ParticleSystem->FloorScale3D);
}
else
{
FloorComponent->SetRelativeLocation_Direct(EditorOptions->FloorPosition);
FloorComponent->SetRelativeRotation_Direct(EditorOptions->FloorRotation);
FloorComponent->SetRelativeScale3D(EditorOptions->FloorScale3D);
}
FPhysScene* PhysScene = new FPhysScene();
CascadePreviewScene.GetWorld()->SetPhysicsScene(PhysScene);
CascadePreviewScene.AddComponent(FloorComponent,FTransform::Identity);
}
}
void FCascadeEdPreviewViewportClient::AddReferencedObjects( FReferenceCollector& Collector )
{
FEditorViewportClient::AddReferencedObjects(Collector);
CascadePreviewScene.AddReferencedObjects(Collector);
}
bool FCascadeEdPreviewViewportClient::CanCycleWidgetMode() const
{
// @todo Cascade: Handled manually for now
return false;
}
FCascadeEdPreviewViewportClient::~FCascadeEdPreviewViewportClient()
{
}
void FCascadeEdPreviewViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas)
{
if (!CascadePtr.IsValid())
{
return;
}
Canvas->Clear( GetPreviewBackgroundColor());
// Clear out the lines from the previous frame
CascadePreviewScene.ClearLineBatcher();
ULineBatchComponent* LineBatcher = CascadePreviewScene.GetLineBatcher();
CascadePreviewScene.RemoveComponent(LineBatcher);
const FVector XAxis(1,0,0);
const FVector YAxis(0,1,0);
const FVector ZAxis(0,0,1);
if (GetDrawElement(OriginAxis))
{
FMatrix ArrowMatrix = FMatrix(XAxis, YAxis, ZAxis, FVector::ZeroVector);
LineBatcher->DrawDirectionalArrow(ArrowMatrix, FColorList::Red, 10.f, 1.0f, SDPG_World);
ArrowMatrix = FMatrix(YAxis, ZAxis, XAxis, FVector::ZeroVector);
LineBatcher->DrawDirectionalArrow(ArrowMatrix, FColorList::Green, 10.f, 1.0f, SDPG_World);
ArrowMatrix = FMatrix(ZAxis, XAxis, YAxis, FVector::ZeroVector);
LineBatcher->DrawDirectionalArrow(ArrowMatrix, FColorList::Blue, 10.f, 1.0f, SDPG_World);
}
if (GetDrawElement(WireSphere))
{
FVector Base(0.f);
FColor WireColor = FColor::Red;
const int32 NumRings = 16;
const float RotatorMultiplier = 360.f / NumRings;
const int32 NumSides = 32;
for (int32 i = 0; i < NumRings; i++)
{
FVector RotXAxis;
FVector RotYAxis;
FVector RotZAxis;
FRotationMatrix RotMatrix(FRotator(i * RotatorMultiplier, 0, 0));
RotXAxis = RotMatrix.TransformPosition(XAxis);
RotYAxis = RotMatrix.TransformPosition(YAxis);
RotZAxis = RotMatrix.TransformPosition(ZAxis);
LineBatcher->DrawCircle(Base, RotXAxis, RotYAxis, WireColor, WireSphereRadius, NumSides, SDPG_World);
LineBatcher->DrawCircle(Base, RotXAxis, RotZAxis, WireColor, WireSphereRadius, NumSides, SDPG_World);
LineBatcher->DrawCircle(Base, RotYAxis, RotZAxis, WireColor, WireSphereRadius, NumSides, SDPG_World);
RotMatrix = FRotationMatrix(FRotator(0, i * RotatorMultiplier, 0));
RotXAxis = RotMatrix.TransformPosition(XAxis);
RotYAxis = RotMatrix.TransformPosition(YAxis);
RotZAxis = RotMatrix.TransformPosition(ZAxis);
LineBatcher->DrawCircle(Base, RotXAxis, RotYAxis, WireColor, WireSphereRadius, NumSides, SDPG_World);
LineBatcher->DrawCircle(Base, RotXAxis, RotZAxis, WireColor, WireSphereRadius, NumSides, SDPG_World);
LineBatcher->DrawCircle(Base, RotYAxis, RotZAxis, WireColor, WireSphereRadius, NumSides, SDPG_World);
}
}
FEngineShowFlags SavedEngineShowFlags = EngineShowFlags;
if (GetDrawElement(Bounds))
{
EngineShowFlags.SetBounds(true);
EngineShowFlags.Game = 1;
}
EngineShowFlags.SetVectorFields(GetDrawElement(VectorFields));
CascadePreviewScene.AddComponent(LineBatcher,FTransform::Identity);
FVector2D ScaledViewportSize = FVector2D(InViewport->GetSizeXY()) / Canvas->GetDPIScale();
FEditorViewportClient::Draw(InViewport, Canvas);
EngineShowFlags = SavedEngineShowFlags;
FCanvasTextItem TextItem( FVector2D::ZeroVector, FText::GetEmpty(), GEngine->GetTinyFont(), FLinearColor::White );
if (GetDrawElement(ParticleCounts) || GetDrawElement(ParticleTimes) || GetDrawElement(ParticleEvents) || GetDrawElement(ParticleMemory) || GetDrawElement(ParticleSystemCompleted))
{
// 'Up' from the lower left...
FString strOutput;
const int32 XPosition = ScaledViewportSize.X - 5;
int32 YPosition = ScaledViewportSize.Y - (GetDrawElement(ParticleMemory) ? 15 : 5);
UParticleSystemComponent* PartComp = CascadePtr.Pin()->GetParticleSystemComponent();
int32 iWidth, iHeight;
if (PartComp->EmitterInstances.Num())
{
for (int32 i = 0; i < PartComp->EmitterInstances.Num(); i++)
{
FParticleEmitterInstance* Instance = PartComp->EmitterInstances[i];
if (!Instance || !Instance->SpriteTemplate)
{
continue;
}
UParticleLODLevel* LODLevel = Instance->SpriteTemplate->GetCurrentLODLevel(Instance);
if (!LODLevel)
{
continue;
}
if (GetDrawElement(EmitterTickTimes))
{
FString StatLine = Instance->SpriteTemplate->EmitterName.ToString() + " tick: " + FString::Printf(TEXT("%.3f ms"), Instance->LastTickDurationMs);
Canvas->DrawShadowedString(0, i*16+25, *StatLine, GEngine->GetTinyFont(), FLinearColor::Gray);
}
strOutput = TEXT("");
if (Instance->SpriteTemplate->EmitterRenderMode != ERM_None)
{
UParticleLODLevel* HighLODLevel = Instance->SpriteTemplate->GetLODLevel(0);
if (GetDrawElement(ParticleCounts))
{
strOutput += FString::Printf(TEXT("%4d/%4d"),
Instance->ActiveParticles, HighLODLevel->PeakActiveParticles);
}
if (GetDrawElement(ParticleTimes))
{
if (GetDrawElement(ParticleCounts))
{
strOutput += TEXT("/");
}
strOutput += FString::Printf(TEXT("%8.4f/%8.4f"),
Instance->EmitterTime, Instance->SecondsSinceCreation);
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (GetDrawElement(ParticleEvents))
{
if (GetDrawElement(ParticleCounts) || GetDrawElement(ParticleTimes))
{
strOutput += TEXT("/");
}
strOutput += FString::Printf(TEXT("Evts: %4d/%4d"), Instance->EventCount, Instance->MaxEventCount);
}
#endif //#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
UCanvas::ClippedStrLen(GEngine->GetTinyFont(), 1.0f, 1.0f, iWidth, iHeight, *strOutput);
TextItem.SetColor( Instance->SpriteTemplate->EmitterEditorColor );
TextItem.Text = FText::FromString( strOutput );
Canvas->DrawItem( TextItem, XPosition - iWidth, YPosition - iHeight );
YPosition -= iHeight - 2;
}
}
if (GetDrawElement(ParticleMemory))
{
YPosition = ScaledViewportSize.Y - 5;
FString MemoryOutput = FString::Printf(TEXT("Template: %.0f KByte / Instance: %.0f KByte"),
(float)ParticleSystemRootSize / 1024.0f + (float)ParticleModuleMemSize / 1024.0f,
(float)PSysCompRootSize / 1024.0f + (float)PSysCompResourceSize / 1024.0f);
UCanvas::ClippedStrLen(GEngine->GetTinyFont(), 1.0f, 1.0f, iWidth, iHeight, *MemoryOutput);
TextItem.SetColor( FLinearColor::White );
TextItem.Text = FText::FromString( MemoryOutput );
Canvas->DrawItem( TextItem, XPosition - iWidth, YPosition - iHeight );
}
}
else
{
for (int32 i = 0; i < PartComp->Template->Emitters.Num(); i++)
{
strOutput = TEXT("");
UParticleEmitter* Emitter = PartComp->Template->Emitters[i];
UParticleLODLevel* LODLevel = Emitter->GetLODLevel(0);
if (LODLevel && LODLevel->bEnabled && (Emitter->EmitterRenderMode != ERM_None))
{
if (GetDrawElement(ParticleCounts))
{
strOutput += FString::Printf(TEXT("%4d/%4d"),
0, LODLevel->PeakActiveParticles);
}
if (GetDrawElement(ParticleTimes))
{
if (GetDrawElement(ParticleCounts))
{
strOutput += TEXT("/");
}
strOutput += FString::Printf(TEXT("%8.4f/%8.4f"), 0.f, 0.f);
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (GetDrawElement(ParticleEvents))
{
if (GetDrawElement(ParticleCounts) || GetDrawElement(ParticleTimes))
{
strOutput += TEXT("/");
}
strOutput += FString::Printf(TEXT("Evts: %4d/%4d"), 0, 0);
}
#endif //#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
UCanvas::ClippedStrLen(GEngine->GetTinyFont(), 1.0f, 1.0f, iWidth, iHeight, *strOutput);
TextItem.SetColor( Emitter->EmitterEditorColor );
TextItem.Text = FText::FromString( strOutput );
Canvas->DrawItem( TextItem, XPosition - iWidth, YPosition - iHeight );
YPosition -= iHeight - 2;
}
}
if (GetDrawElement(ParticleMemory))
{
YPosition = ScaledViewportSize.Y - 5;
FString MemoryOutput = FString::Printf(TEXT("Template: %.0f KByte / Instance: %.0f KByte"),
(float)ParticleSystemRootSize / 1024.0f + (float)ParticleModuleMemSize / 1024.0f,
(float)PSysCompRootSize / 1024.0f + (float)PSysCompResourceSize / 1024.0f);
UCanvas::ClippedStrLen(GEngine->GetTinyFont(), 1.0f, 1.0f, iWidth, iHeight, *MemoryOutput);
TextItem.SetColor( FLinearColor::White );
TextItem.Text = FText::FromString( MemoryOutput );
Canvas->DrawItem( TextItem, XPosition - iWidth, YPosition - iHeight );
}
}
if (GetDrawElement(ParticleSystemCompleted))
{
if (PartComp->HasCompleted())
{
TextItem.SetColor(FLinearColor::White);
TextItem.Text = LOCTEXT("SystemCompleted", "Completed");
TextItem.bCentreX = true;
TextItem.bCentreY = true;
Canvas->DrawItem(TextItem, ScaledViewportSize.X * 0.5f, ScaledViewportSize.Y - 10);
TextItem.bCentreX = false;
TextItem.bCentreY = false;
}
}
}
float MiscMessageY = ScaledViewportSize.Y - 75.0f;
float MisMessageYSep = 15;
//Display a warning message in the preview window if the system has no fixed bounding-box and contains a GPU emitter.
if(CascadePtr.Pin()->GetParticleSystem()->bUseFixedRelativeBoundingBox == false)
{
UParticleSystemComponent* PartComp = CascadePtr.Pin()->GetParticleSystemComponent();
if (PartComp->EmitterInstances.Num())
{
//We iterate over the emitter instances to find any that contain a GPU Sprite TypeData module. If found, we draw the warning message.
for (int32 i = 0; i < PartComp->EmitterInstances.Num(); i++)
{
FParticleEmitterInstance* Instance = PartComp->EmitterInstances[i];
if(!Instance || !Instance->SpriteTemplate)
{
continue;
}
UParticleLODLevel* LODLevel = Instance->SpriteTemplate->GetCurrentLODLevel(Instance);
if (!LODLevel || !LODLevel->TypeDataModule)
{
continue;
}
const bool bIsAGPUEmitter = LODLevel->TypeDataModule->IsA(UParticleModuleTypeDataGpu::StaticClass());
if(bIsAGPUEmitter)
{
const int32 XPosition = 5;
const int32 YPosition = MiscMessageY;
MiscMessageY -= MisMessageYSep;
FString strOutput = NSLOCTEXT("Cascade", "NoFixedBounds_Warning", "WARNING: This particle system has no fixed bounding box and contains a GPU emitter.").ToString();
TextItem.SetColor( FLinearColor::White );
TextItem.Text = FText::FromString( strOutput );
Canvas->DrawItem( TextItem, XPosition, YPosition );
break;
}
}
}
}
int32 DetailMode = CascadePtr.Pin()->GetDetailMode();
if (DetailMode != DM_High)
{
FString DetailModeOutput = FString::Printf(TEXT("DETAIL MODE: %s"), (DetailMode == DM_Medium)? TEXT("MEDIUM"): TEXT("LOW"));
TextItem.SetColor( FLinearColor::Red );
TextItem.Text = FText::FromString( DetailModeOutput );
Canvas->DrawItem(TextItem, 5.0f, MiscMessageY);
MiscMessageY -= MisMessageYSep;
}
if (GEngine->bEnableEditorPSysRealtimeLOD)
{
TextItem.SetColor( FLinearColor(0.25f, 0.25f, 1.0f) );
TextItem.Text = LOCTEXT("LODPREVIEWMODEENABLED","LOD PREVIEW MODE ENABLED");
Canvas->DrawItem(TextItem, 5.0f, MiscMessageY);
MiscMessageY -= MisMessageYSep;
}
EParticleSignificanceLevel ReqSignificance = CascadePtr.Pin()->GetRequiredSignificance();
if (ReqSignificance != EParticleSignificanceLevel::Low)
{
FString ReqSigOutput = FString::Printf(TEXT("REQUIRED SIGNIFICANCE: %s"), (ReqSignificance == EParticleSignificanceLevel::Medium) ? TEXT("MEDIUM") : ((ReqSignificance == EParticleSignificanceLevel::High) ? TEXT("HIGH") : TEXT("CRITICAL")));
TextItem.SetColor(FLinearColor::Red);
TextItem.Text = FText::FromString(ReqSigOutput);
Canvas->DrawItem(TextItem, 5.0f, MiscMessageY);
MiscMessageY -= MisMessageYSep;
}
if(UParticleSystem* PSys = CascadePtr.Pin()->GetParticleSystem())
{
if (!PSys->CanTickInAnyThread())
{
//Place a warning on screen if this system cannot tick async.
TextItem.SetColor(FLinearColor::Red);
TextItem.Text = FText::FromString(TEXT("NO ASYNC TICK"));
Canvas->DrawItem(TextItem, 5.0f, MiscMessageY);
MiscMessageY -= MisMessageYSep;
}
}
if (bCaptureScreenShot)
{
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
int32 SrcWidth = InViewport->GetSizeXY().X;
int32 SrcHeight = InViewport->GetSizeXY().Y;
// Read the contents of the viewport into an array.
TArray<FColor> OrigBitmap;
if (InViewport->ReadPixels(OrigBitmap))
{
check(OrigBitmap.Num() == SrcWidth * SrcHeight);
// Resize image to enforce max size.
TArray<FColor> ScaledBitmap;
int32 ScaledWidth = 512;
int32 ScaledHeight = 512;
FImageUtils::CropAndScaleImage(SrcWidth, SrcHeight, ScaledWidth, ScaledHeight, OrigBitmap, ScaledBitmap);
// Compress.
FCreateTexture2DParameters Params;
Params.bDeferCompression = true;
ParticleSystem->ThumbnailImage = FImageUtils::CreateTexture2D(ScaledWidth, ScaledHeight, ScaledBitmap, ParticleSystem, TEXT("ThumbnailTexture"), RF_NoFlags, Params);
ParticleSystem->ThumbnailImageOutOfDate = false;
ParticleSystem->MarkPackageDirty();
}
bCaptureScreenShot = false;
}
}
void FCascadeEdPreviewViewportClient::Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI)
{
DrawHelper.Draw(View, PDI);
// If a local vector field module is selected, draw a widget so that the user can move the vector field around.
UParticleModuleVectorFieldLocal* VectorFieldModule = Cast<UParticleModuleVectorFieldLocal>(CascadePtr.Pin()->GetSelectedModule());
if (VectorFieldModule)
{
const FVector WidgetOrigin = VectorFieldModule->RelativeTranslation;
const FRotator WidgetRotation = (WidgetMM == WMM_Translate) ? FRotator::ZeroRotator : VectorFieldModule->RelativeRotation;
const FTransform WidgetTransform(
WidgetRotation,
WidgetOrigin,
FVector(1.0f,1.0f,1.0f));
FUnrealEdUtils::DrawWidget(View, PDI, WidgetTransform.ToMatrixWithScale(), VectorFieldHitproxyInfo, 0, WidgetAxis, WidgetMM);
}
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
UCascadeParticleSystemComponent* ParticleSystemComponent = CascadePtr.Pin()->GetParticleSystemComponent();
// Can now iterate over the modules on this system...
for (int32 i = 0; i < ParticleSystem->Emitters.Num(); i++)
{
UParticleEmitter* Emitter = ParticleSystem->Emitters[i];
if (Emitter == NULL)
{
continue;
}
// Emitters may have a set number of loops.
// After which, the system will kill them off
if (i < ParticleSystemComponent->EmitterInstances.Num())
{
FParticleEmitterInstance* EmitterInst = ParticleSystemComponent->EmitterInstances[i];
if (EmitterInst && EmitterInst->SpriteTemplate)
{
check(EmitterInst->SpriteTemplate == Emitter);
UParticleLODLevel* LODLevel = Emitter->GetCurrentLODLevel(EmitterInst);
for(int32 j=0; j< LODLevel->Modules.Num(); j++)
{
UParticleModule* Module = LODLevel->Modules[j];
if (Module && Module->bSupported3DDrawMode && Module->b3DDrawMode)
{
Module->Render3DPreview(EmitterInst, View, PDI);
}
}
}
}
}
// Draw the preview scene light visualization
DrawPreviewLightVisualization(View, PDI);
}
bool FCascadeEdPreviewViewportClient::InputKey(FViewport* InViewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed, bool Gamepad)
{
//Update cursor
UpdateAndApplyCursorVisibility();
bool bHandled = false;
const int32 HitX = InViewport->GetMouseX();
const int32 HitY = InViewport->GetMouseY();
if(Key == EKeys::LeftMouseButton)
{
if (Event == IE_Pressed)
{
InViewport->InvalidateHitProxy();
HHitProxy* HitResult = InViewport->GetHitProxy(HitX,HitY);
if (HitResult && HitResult->IsA(HWidgetUtilProxy::StaticGetType()))
{
HWidgetUtilProxy* WidgetProxy = (HWidgetUtilProxy*)HitResult;
if (WidgetProxy->Info1 == VectorFieldHitproxyInfo)
{
bManipulatingVectorField = true;
}
WidgetAxis = WidgetProxy->Axis;
// Calculate the scree-space directions for this drag.
FSceneViewFamilyContext ViewFamily( FSceneViewFamily::ConstructionValues( InViewport, GetScene(), EngineShowFlags ));
FSceneView* View = CalcSceneView(&ViewFamily);
WidgetProxy->CalcVectors(View, FViewportClick(View, this, Key, Event, HitX, HitY), LocalManipulateDir, WorldManipulateDir, DragX, DragY);
bHandled = true;
}
}
else if (Event == IE_Released)
{
if (bManipulatingVectorField)
{
WidgetAxis = EAxisList::None;
bManipulatingVectorField = false;
bHandled = true;
}
}
}
else if (Key == EKeys::SpaceBar && Event == IE_Pressed)
{
if (CascadePtr.Pin()->GetSelectedModule() && CascadePtr.Pin()->GetSelectedModule()->IsA(UParticleModuleVectorFieldLocal::StaticClass()))
{
bHandled = true;
WidgetMM = (EWidgetMovementMode)((WidgetMM+1) % WMM_MAX);
}
}
if( !bHandled )
{
bHandled = FEditorViewportClient::InputKey(InViewport,ControllerId,Key,Event,AmountDepressed,Gamepad);
}
return bHandled;
}
// Tweakable speeds for manipulating the widget.
//static TAutoConsoleVariable<float> CVarCascadeDragSpeed(TEXT("CascadeDragSpeed"),1.0f,TEXT("Cascade drag speed."));
static auto CVarCascadeDragSpeed = TAutoConsoleVariable<float>(TEXT("CascadeDragSpeed"),1.0f,TEXT("Cascade drag speed."));
static TAutoConsoleVariable<float> CVarCascadeRotateSpeed(TEXT("CascadeRotateSpeed"),0.005f,TEXT("Cascade drag speed."));
static TAutoConsoleVariable<float> CVarCascadeScaleSpeed(TEXT("CascadeScaleSpeed"),1.0f,TEXT("Cascade scale speed."));
bool FCascadeEdPreviewViewportClient::InputAxis(FViewport* InViewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad)
{
bool bHandled = false;
if (bManipulatingVectorField)
{
UParticleModuleVectorFieldLocal* VectorFieldModule = Cast<UParticleModuleVectorFieldLocal>(CascadePtr.Pin()->GetSelectedModule());
if (VectorFieldModule)
{
const float MoveX = ((Key == EKeys::MouseX) ? Delta : 0.0f) * DragX;
const float MoveY = ((Key == EKeys::MouseY) ? Delta : 0.0f) * DragY;
const float MoveAmount = MoveX + MoveY;
VectorFieldModule->PreEditChange(NULL);
if (WidgetMM == WMM_Translate)
{
VectorFieldModule->RelativeTranslation += (LocalManipulateDir * MoveAmount * CVarCascadeDragSpeed.GetValueOnGameThread());
}
else if (WidgetMM == WMM_Rotate)
{
const FQuat CurrentRotation = VectorFieldModule->RelativeRotation.Quaternion();
const FQuat DeltaRotation(LocalManipulateDir, -MoveAmount * CVarCascadeRotateSpeed.GetValueOnGameThread());
const FQuat NewRotation = CurrentRotation * DeltaRotation;
VectorFieldModule->RelativeRotation = FRotator(NewRotation);
}
else if (WidgetMM == WMM_Scale)
{
VectorFieldModule->RelativeScale3D += (LocalManipulateDir * MoveAmount * CVarCascadeScaleSpeed.GetValueOnGameThread());
}
VectorFieldModule->PostEditChange();
}
bHandled = true;
}
else
{
bHandled = FEditorViewportClient::InputAxis(InViewport,ControllerId,Key,Delta,DeltaTime,NumSamples,bGamepad);
}
if (!IsRealtime() && !FMath::IsNearlyZero(Delta))
{
InViewport->Invalidate();
}
return bHandled;
}
void FCascadeEdPreviewViewportClient::SetPreviewCamera(const FRotator& NewPreviewAngle, float NewPreviewDistance)
{
PreviewAngle = NewPreviewAngle;
PreviewDistance = NewPreviewDistance;
SetViewLocation( PreviewAngle.Vector() * -PreviewDistance );
SetViewRotation( PreviewAngle );
Viewport->Invalidate();
}
void FCascadeEdPreviewViewportClient::UpdateMemoryInformation()
{
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
UCascadeParticleSystemComponent* ParticleSystemComponent = CascadePtr.Pin()->GetParticleSystemComponent();
if (ParticleSystem != NULL)
{
FArchiveCountMem MemCount(ParticleSystem);
ParticleSystemRootSize = MemCount.GetMax();
ParticleModuleMemSize = 0;
TMap<UParticleModule*,bool> ModuleList;
for (int32 EmitterIdx = 0; EmitterIdx < ParticleSystem->Emitters.Num(); EmitterIdx++)
{
UParticleEmitter* Emitter = ParticleSystem->Emitters[EmitterIdx];
if (Emitter != NULL)
{
for (int32 LODIdx = 0; LODIdx < Emitter->LODLevels.Num(); LODIdx++)
{
UParticleLODLevel* LODLevel = Emitter->LODLevels[LODIdx];
if (LODLevel != NULL)
{
ModuleList.Add(LODLevel->RequiredModule, true);
ModuleList.Add(LODLevel->SpawnModule, true);
for (int32 ModuleIdx = 0; ModuleIdx < LODLevel->Modules.Num(); ModuleIdx++)
{
ModuleList.Add(LODLevel->Modules[ModuleIdx], true);
}
}
}
}
}
for (TMap<UParticleModule*,bool>::TIterator ModuleIt(ModuleList); ModuleIt; ++ModuleIt)
{
UParticleModule* Module = ModuleIt.Key();
FArchiveCountMem ModuleCount(Module);
ParticleModuleMemSize += ModuleCount.GetMax();
}
}
if (ParticleSystemComponent != NULL)
{
FArchiveCountMem ComponentMemCount(ParticleSystemComponent);
PSysCompRootSize = ComponentMemCount.GetMax();
PSysCompResourceSize = ParticleSystemComponent->GetResourceSizeBytes(EResourceSizeMode::Exclusive);
}
}
void FCascadeEdPreviewViewportClient::CreateThumbnail()
{
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
ParticleSystem->ThumbnailAngle = PreviewAngle;
ParticleSystem->ThumbnailDistance = PreviewDistance;
ParticleSystem->PreviewComponent = NULL;
bCaptureScreenShot = true;
}
FSceneInterface* FCascadeEdPreviewViewportClient::GetScene() const
{
return CascadePreviewScene.GetScene();
}
FLinearColor FCascadeEdPreviewViewportClient::GetBackgroundColor() const
{
return GetPreviewBackgroundColor();
}
bool FCascadeEdPreviewViewportClient::ShouldOrbitCamera() const
{
if (GetDefault<ULevelEditorViewportSettings>()->bUseUE3OrbitControls)
{
// this editor orbits always if ue3 orbit controls are enabled
return true;
}
return FEditorViewportClient::ShouldOrbitCamera();
}
FPreviewScene& FCascadeEdPreviewViewportClient::GetPreviewScene()
{
return CascadePreviewScene;
}
bool FCascadeEdPreviewViewportClient::GetDrawElement(EDrawElements Element) const
{
return (DrawFlags & Element) != 0;
}
void FCascadeEdPreviewViewportClient::ToggleDrawElement(EDrawElements Element)
{
DrawFlags = DrawFlags ^ Element;
}
FColor FCascadeEdPreviewViewportClient::GetPreviewBackgroundColor() const
{
if (CascadePtr.IsValid() && CascadePtr.Pin()->GetParticleSystem())
{
return CascadePtr.Pin()->GetParticleSystem()->BackgroundColor;
}
return BackgroundColor;
}
UStaticMeshComponent* FCascadeEdPreviewViewportClient::GetFloorComponent()
{
return FloorComponent;
}
FEditorCommonDrawHelper& FCascadeEdPreviewViewportClient::GetDrawHelper()
{
return DrawHelper;
}
float& FCascadeEdPreviewViewportClient::GetWireSphereRadius()
{
return WireSphereRadius;
}
#undef LOCTEXT_NAMESPACE