Files
UnrealEngineUWP/Engine/Source/Editor/Cascade/Private/CascadeEmitterCanvasClient.cpp
Chris Wood 8a112d6140 PushMenu() now respects QueryPopupMethod(). All menus now support reusing windows.
UE-14641 - Fix PushMenu() to use QueryPopupMethod()

Pretty big refactor
Adds IMenu as way to identify menus. Replaces referring to menus as SWindows.
Lots of uses of PushMenu() fixed up to match new API

#codereview Nick.Atamas

[CL 2579277 by Chris Wood in Main branch]
2015-06-05 20:19:33 -04:00

2215 lines
70 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "CascadeModule.h"
#include "Cascade.h"
#include "IDistCurveEditor.h"
#include "CascadeActions.h"
#include "CascadeEmitterHitProxies.h"
#include "SCascadeEmitterCanvas.h"
#include "CascadeEmitterCanvasClient.h"
#include "Runtime/Engine/Public/Slate/SceneViewport.h"
#include "Particles/Material/ParticleModuleMeshMaterial.h"
#include "Particles/Spawn/ParticleModuleSpawn.h"
#include "Particles/TypeData/ParticleModuleTypeDataMesh.h"
#include "Particles/ParticleLODLevel.h"
#include "Particles/ParticleModule.h"
#include "Particles/ParticleModuleRequired.h"
#include "Particles/ParticleSpriteEmitter.h"
#include "Particles/ParticleSystem.h"
#include "SColorPicker.h"
#include "CanvasTypes.h"
#include "Engine/Font.h"
#include "Engine/StaticMesh.h"
FCascadeEmitterCanvasClient::FCascadeEmitterCanvasClient(TWeakPtr<FCascade> InCascade, TWeakPtr<SCascadeEmitterCanvas> InCascadeViewport)
: FEditorViewportClient(nullptr)
, CascadePtr(InCascade)
, CascadeViewportPtr(InCascadeViewport)
, EmitterWidth(180)
, EmitterCollapsedWidth(18)
, EmitterHeadHeight(60)
, EmitterThumbBorder(5)
, ModuleHeight(40)
, EmptyBackgroundColor(112, 112, 112)
, EmitterBackgroundColor(130, 130, 130)
, EmitterSelectedColor(255, 130, 30)
, EmitterUnselectedColor(180, 180, 180)
, RenderModeSelected(255,200,0)
, RenderModeUnselected(112, 112, 112)
, Module3DDrawModeEnabledColor(255, 200, 0)
, Module3DDrawModeDisabledColor(112,112,112)
, RequiredModuleOffset(1)
, SpawnModuleOffset(2)
, ModulesOffset(3)
, bInitializedModuleEntries(false)
{
check(CascadePtr.IsValid() && CascadeViewportPtr.IsValid());
UCascadeOptions* EditorOptions = CascadePtr.Pin()->GetEditorOptions();
if (EditorOptions->bUseSlimCascadeDraw == true)
{
ModuleHeight = FMath::Max<int32>(EditorOptions->SlimCascadeDrawHeight, 20);
}
else
{
EditorOptions->bCenterCascadeModuleText = false;
}
// This window will be 2D/canvas only, so set the viewport type to None
ViewportType = LVT_None;
CanvasDimensions = FIntPoint(0,0);
CurrentMoveMode = MoveMode_None;
MouseHoldOffset = FIntPoint(0,0);
MousePressPosition = FIntPoint(0,0);
bMouseDragging = false;
bMouseDown = false;
#if defined(_CASCADE_ENABLE_MODULE_DUMP_)
bDrawDraggedModule = EditorOptions->bShowModuleDump;
#else //#if defined(_CASCADE_ENABLE_MODULE_DUMP_)
bDrawDraggedModule = false;
#endif //#if defined(_CASCADE_ENABLE_MODULE_DUMP_)
DraggedModule = NULL;
Origin2D = FIntPoint(0,0);
ResetDragModIndex = INDEX_NONE;
EmptyBackgroundColor = EditorOptions->Empty_Background;
EmptyBackgroundColor.A = 255;
EmitterBackgroundColor = EditorOptions->Emitter_Background;
EmitterBackgroundColor.A = 255;
EmitterSelectedColor = EditorOptions->Emitter_Unselected;
EmitterSelectedColor.A = 255;
EmitterUnselectedColor = EditorOptions->Emitter_Selected;
EmitterUnselectedColor.A = 255;
FColor ColorOptionsUnselected[EPMT_MAX] = { EditorOptions->ModuleColor_General_Unselected,
EditorOptions->ModuleColor_TypeData_Unselected,
EditorOptions->ModuleColor_Beam_Unselected,
EditorOptions->ModuleColor_Trail_Unselected,
EditorOptions->ModuleColor_Spawn_Unselected,
EditorOptions->ModuleColor_Required_Unselected,
EditorOptions->ModuleColor_Event_Unselected,
EditorOptions->ModuleColor_Light_Unselected
};
FColor ColorOptionsSelected[EPMT_MAX] = { EditorOptions->ModuleColor_General_Selected,
EditorOptions->ModuleColor_TypeData_Selected,
EditorOptions->ModuleColor_Beam_Selected,
EditorOptions->ModuleColor_Trail_Selected,
EditorOptions->ModuleColor_Spawn_Selected,
EditorOptions->ModuleColor_Required_Selected,
EditorOptions->ModuleColor_Event_Selected,
EditorOptions->ModuleColor_Light_Selected
};
for (int32 i = 0; i < EPMT_MAX; ++i)
{
ModuleColors[i][Selection_Unselected] = ColorOptionsUnselected[i];
ModuleColors[i][Selection_Unselected].A = 255;
ModuleColors[i][Selection_Selected] = ColorOptionsSelected[i];
ModuleColors[i][Selection_Selected].A = 255;
}
IconTex[Icon_RenderNormal] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_Normal.CASC_Normal"), NULL, LOAD_None, NULL);
check(IconTex[Icon_RenderNormal]);
IconTex[Icon_RenderCross] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_Cross.CASC_Cross"), NULL, LOAD_None, NULL);
check(IconTex[Icon_RenderCross]);
IconTex[Icon_RenderPoint] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_Point.CASC_Point"), NULL, LOAD_None, NULL);
check(IconTex[Icon_RenderPoint]);
IconTex[Icon_RenderNone] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_None.CASC_None"), NULL, LOAD_None, NULL);
check(IconTex[Icon_RenderNone]);
IconTex[Icon_RenderLights] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_Lights.CASC_Lights"), NULL, LOAD_None, NULL);
check(IconTex[Icon_RenderLights]);
IconTex[Icon_CurveEdit] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_CurveEd.CASC_CurveEd"), NULL, LOAD_None, NULL);
check(IconTex[Icon_RenderLights]);
IconTex[Icon_3DDrawEnabled] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_ModuleEnable.CASC_ModuleEnable"), NULL, LOAD_None, NULL);
check(IconTex[Icon_3DDrawEnabled]);
IconTex[Icon_3DDrawDisabled] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_ModuleDisable.CASC_ModuleDisable"), NULL, LOAD_None, NULL);
check(IconTex[Icon_3DDrawDisabled]);
IconTex[Icon_ModuleEnabled] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_ModuleEnable.CASC_ModuleEnable"), NULL, LOAD_None, NULL);
check(IconTex[Icon_ModuleEnabled]);
IconTex[Icon_ModuleDisabled] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_ModuleDisable.CASC_ModuleDisable"), NULL, LOAD_None, NULL);
check(IconTex[Icon_ModuleDisabled]);
IconTex[Icon_SoloEnabled] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_Solo_On.CASC_Solo_On"), NULL, LOAD_None, NULL);
check(IconTex[Icon_SoloEnabled]);
IconTex[Icon_SoloDisabled] = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_Solo_Off.CASC_Solo_Off"), NULL, LOAD_None, NULL);
check(IconTex[Icon_SoloDisabled]);
TexModuleDisabledBackground = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("/Engine/EditorMaterials/Cascade/CASC_DisabledModule.CASC_DisabledModule"), NULL, LOAD_None, NULL);
check(TexModuleDisabledBackground);
}
FCascadeEmitterCanvasClient::~FCascadeEmitterCanvasClient()
{
}
void FCascadeEmitterCanvasClient::Draw(FViewport* Viewport, FCanvas* Canvas)
{
if (!CascadePtr.IsValid())
{
return;
}
UpdateScrollBars();
FVector2D ScrollBarPos = GetViewportScrollBarPositions();
Origin2D.X = -ScrollBarPos.X;
Origin2D.Y = -ScrollBarPos.Y;
NumRejectedModulesDrawn = 0;
ModuleErrorStrings.Reset();
Canvas->PushAbsoluteTransform(FTranslationMatrix(FVector(Origin2D.X,Origin2D.Y,0)));
// Clear the background to gray and set the 2D draw origin for the viewport
if (Canvas->IsHitTesting() == false)
{
Canvas->Clear( EmptyBackgroundColor);
}
else
{
Canvas->Clear(FLinearColor(1.0f,1.0f,1.0f,1.0f));
}
int32 ViewX = Viewport->GetSizeXY().X;
int32 ViewY = Viewport->GetSizeXY().Y;
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
int32 EmitterOffset = 0;
for(int32 i=0; i<ParticleSystem->Emitters.Num(); i++)
{
UParticleEmitter* Emitter = ParticleSystem->Emitters[i];
if (Emitter)
{
DrawEmitter(i, EmitterOffset, Emitter, Viewport, Canvas);
}
// Move X position on to next emitter.
if (Emitter && Emitter->bCollapsed)
{
EmitterOffset += EmitterCollapsedWidth;
}
else
{
EmitterOffset += EmitterWidth;
}
// Draw vertical line after last column
Canvas->DrawTile(EmitterOffset - 1, 0, 1, ViewY - Origin2D.Y, 0.f, 0.f, 0.f, 0.f, FLinearColor::Black);
}
// Draw line under emitter headers
Canvas->DrawTile(0, EmitterHeadHeight-1, ViewX - Origin2D.X, 1, 0.f, 0.f, 0.f, 0.f, FLinearColor::Black);
// Draw the module dump, if it is enabled
if (bDrawDraggedModule)
DrawModuleDump(Viewport, Canvas);
// When dragging a module.
if ((CurrentMoveMode != MoveMode_None) && bMouseDragging)
{
if (DraggedModule)
DrawDraggedModule(DraggedModule, Viewport, Canvas);
}
Canvas->PopTransform();
// Draw module errors and warnings.
{
UFont* ErrorFont = GEngine->GetSmallFont();
const int32 LineHeight = FMath::TruncToInt( ErrorFont->GetMaxCharHeight() );
int32 DrawY = ViewY - 2 - LineHeight;
for (int32 i = 0; i < ModuleErrorStrings.Num(); ++i)
{
Canvas->DrawShadowedString(
2,
DrawY,
*ModuleErrorStrings[i],
ErrorFont,
FLinearColor::Red
);
DrawY -= LineHeight;
}
if (NumRejectedModulesDrawn != 0)
{
Canvas->DrawShadowedText(
2,
DrawY,
NSLOCTEXT("UnrealEd", "InvalidModules", "An emitter has modules that are incompatible with its type data."),
ErrorFont,
FLinearColor::Red
);
}
}
}
bool FCascadeEmitterCanvasClient::InputKey(FViewport* Viewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed, bool Gamepad)
{
bool bHandled = false;
bool bLODIsValid = true;
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
bool bCtrlDown = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl);
bool bShiftDown = Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift);
bool bAltDown = Viewport->KeyState(EKeys::LeftAlt) || Viewport->KeyState(EKeys::RightAlt);
int32 HitX = Viewport->GetMouseX();
int32 HitY = Viewport->GetMouseY();
FIntPoint MousePos = FIntPoint(HitX, HitY);
if (Key == EKeys::LeftMouseButton || Key == EKeys::RightMouseButton)
{
bHandled = true;
if (Event == IE_Pressed)
{
if (Key == EKeys::LeftMouseButton)
{
MousePressPosition = MousePos;
bMouseDown = true;
}
HHitProxy* HitResult = Viewport->GetHitProxy(HitX,HitY);
// Short-term, performing a quick-out
bool bHandledHitProxy = true;
if (HitResult)
{
if (HitResult->IsA(HCascadeEdEmitterProxy::StaticGetType()))
{
UParticleEmitter* Emitter = ((HCascadeEdEmitterProxy*)HitResult)->Emitter;
CascadePtr.Pin()->SetSelectedEmitter(Emitter);
if (Key == EKeys::RightMouseButton)
{
OpenEmitterMenu();
}
}
else if (HitResult->IsA(HCascadeEdEmitterEnableProxy::StaticGetType()))
{
if (bLODIsValid && (ParticleSystem != NULL))
{
CascadePtr.Pin()->ToggleEnableOnSelectedEmitter(((HCascadeEdDrawModeButtonProxy*)HitResult)->Emitter);
}
}
else if (HitResult->IsA(HCascadeEdDrawModeButtonProxy::StaticGetType()))
{
if (bLODIsValid)
{
UParticleEmitter* Emitter = ((HCascadeEdDrawModeButtonProxy*)HitResult)->Emitter;
EEmitterRenderMode DrawMode = (EEmitterRenderMode)((HCascadeEdDrawModeButtonProxy*)HitResult)->DrawMode;
switch (DrawMode)
{
case ERM_Normal: DrawMode = ERM_Point; break;
case ERM_Point: DrawMode = ERM_Cross; break;
case ERM_Cross: DrawMode = ERM_LightsOnly; break;
case ERM_LightsOnly: DrawMode = ERM_None; break;
case ERM_None: DrawMode = ERM_Normal; break;
}
CascadePtr.Pin()->SetSelectedEmitter(Emitter);
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
if (LODLevel && LODLevel->IsModuleEditable(LODLevel->RequiredModule))
{
Emitter->EmitterRenderMode = DrawMode;
}
}
}
else if (HitResult->IsA(HCascadeEdSoloButtonProxy::StaticGetType()))
{
if (bLODIsValid)
{
UParticleEmitter* Emitter = ((HCascadeEdSoloButtonProxy*)HitResult)->Emitter;
bool bIsSoloing = CascadePtr.Pin()->GetParticleSystem()->ToggleSoloing(Emitter);
CascadePtr.Pin()->SetIsSoloing(bIsSoloing);
CascadePtr.Pin()->SetSelectedEmitter(Emitter);
}
}
else if (HitResult->IsA(HCascadeEdColorButtonProxy::StaticGetType()))
{
if (bLODIsValid)
{
UParticleEmitter* Emitter = ((HCascadeEdModuleProxy*)HitResult)->Emitter;
UParticleModule* Module = ((HCascadeEdModuleProxy*)HitResult)->Module;
if (Module || Emitter)
{
TArray<FColor*> FColorArray;
if (Module)
{
FColorArray.Add(&Module->ModuleEditorColor);
}
else
{
check(Emitter);
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
if ( LODLevel )
{
FColorArray.Add(&Emitter->EmitterEditorColor);
}
}
if ( FColorArray.Num() > 0 )
{
// Let go of the mouse lock...
Viewport->LockMouseToViewport(false);
Viewport->CaptureMouse(false);
FColorPickerArgs PickerArgs;
PickerArgs.DisplayGamma = TAttribute<float>::Create( TAttribute<float>::FGetter::CreateUObject(GEngine, &UEngine::GetDisplayGamma) );
PickerArgs.ColorArray = &FColorArray;
OpenColorPicker(PickerArgs);
}
}
}
}
else if (HitResult->IsA(HCascadeEdModuleProxy::StaticGetType()))
{
UParticleEmitter* Emitter = ((HCascadeEdModuleProxy*)HitResult)->Emitter;
UParticleModule* Module = ((HCascadeEdModuleProxy*)HitResult)->Module;
CascadePtr.Pin()->SetSelectedModule(Emitter, Module);
if (Key == EKeys::RightMouseButton)
{
if (bMouseDragging)// && (CurrentMoveMode != CMMM_None))
{
// Don't allow menu pop-up while moving modules...
}
else
{
OpenModuleMenu();
}
}
else
{
check(CascadePtr.Pin()->GetSelectedModule());
// We are starting to drag this module. Look at keys to see if we are moving/instancing
if (bCtrlDown || bAltDown)
{
CascadePtr.Pin()->SetCopyModule(Emitter, Module);
CurrentMoveMode = MoveMode_Copy;
}
else if (bShiftDown)
{
CurrentMoveMode = MoveMode_Instance;
}
else
{
CurrentMoveMode = MoveMode_Move;
}
// Figure out and save the offset from mouse location to top-left of selected module.
FIntPoint ModuleTopLeft = FindModuleTopLeft(Emitter, Module, Viewport);
MouseHoldOffset = ModuleTopLeft - MousePressPosition;
}
}
else if (HitResult->IsA(HCascadeEdGraphButton::StaticGetType()))
{
if (bLODIsValid)
{
UParticleEmitter* Emitter = ((HCascadeEdModuleProxy*)HitResult)->Emitter;
UParticleModule* Module = ((HCascadeEdModuleProxy*)HitResult)->Module;
if (Module)
{
CascadePtr.Pin()->SetSelectedModule(Emitter, Module);
}
else
{
CascadePtr.Pin()->SetSelectedEmitter(Emitter);
}
TArray<const FCurveEdEntry*> CurveEntries;
const bool bNewCurve = CascadePtr.Pin()->AddSelectedToGraph(CurveEntries);
if ( !bNewCurve )
{
CascadePtr.Pin()->ShowDesiredCurvesOnly(CurveEntries);
}
}
}
else if (HitResult->IsA(HCascadeEd3DDrawModeButtonProxy::StaticGetType()))
{
if (bLODIsValid)
{
UParticleModule* Module = ((HCascadeEdModuleProxy*)HitResult)->Module;
check(Module);
Module->b3DDrawMode = !Module->b3DDrawMode;
}
}
else if (HitResult->IsA(HCascadeEd3DDrawModeOptionsButtonProxy::StaticGetType()))
{
if (bLODIsValid)
{
UParticleModule* Module = ((HCascadeEdModuleProxy*)HitResult)->Module;
check(Module);
// Pop up an options dialog??
FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "Prompt_7", "3DDrawMode Options Menu!"));
}
}
else if (HitResult->IsA(HCascadeEdEnableButtonProxy::StaticGetType()))
{
if (bLODIsValid)
{
UParticleEmitter* Emitter = ((HCascadeEdModuleProxy*)HitResult)->Emitter;
check(Emitter);
UParticleModule* Module = ((HCascadeEdModuleProxy*)HitResult)->Module;
check(Module);
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
if (LODLevel && LODLevel->IsModuleEditable(Module))
{
Module->bEnabled = !Module->bEnabled;
Module->PostEditChange();
}
}
}
else
{
bHandledHitProxy = false;
}
}
else
{
bHandledHitProxy = false;
}
if (bHandledHitProxy == false)
{
CascadePtr.Pin()->SetSelectedModule(NULL, NULL);
if (Key == EKeys::RightMouseButton)
OpenBackgroundMenu();
}
}
else if (Event == IE_Released)
{
// If we were dragging a module, find where the mouse currently is, and move module there
if ((CurrentMoveMode != MoveMode_None) && bMouseDragging)
{
TArray<UParticleModule*>& ModuleDumpList = CascadePtr.Pin()->GetDraggedModuleList();
if (DraggedModule)
{
// Find where to move module to.
UParticleEmitter* TargetEmitter = NULL;
int32 TargetIndex = INDEX_NONE;
FindDesiredModulePosition(MousePos, TargetEmitter, TargetIndex);
if (TargetEmitter && (TargetEmitter->bCollapsed == true))
{
TargetEmitter = NULL;
}
if (!TargetEmitter || TargetIndex == INDEX_NONE)
{
// If the target is the DumpModules area, add it to the list of dump modules
if (bDrawDraggedModule)
{
ModuleDumpList.Add(DraggedModule);
DraggedModule = NULL;
}
else if (CurrentMoveMode == MoveMode_Move)
{
// If target is invalid and we were moving it, put it back where it came from.
if ((ResetDragModIndex != INDEX_NONE) && CascadePtr.Pin()->GetSelectedEmitter())
{
CascadePtr.Pin()->InsertModule(DraggedModule, CascadePtr.Pin()->GetSelectedEmitter(), ResetDragModIndex, true);
CascadePtr.Pin()->GetSelectedEmitter()->UpdateModuleLists();
RemoveFromDraggedList(DraggedModule);
}
else
{
ModuleDumpList.Add(DraggedModule);
}
}
}
else
{
// Add dragged module in new location.
if ((CurrentMoveMode == MoveMode_Move) || (CurrentMoveMode == MoveMode_Instance) || (CurrentMoveMode == MoveMode_Copy))
{
if (CurrentMoveMode == MoveMode_Copy)
{
CascadePtr.Pin()->CopyModuleToEmitter(DraggedModule, TargetEmitter, ParticleSystem, TargetIndex);
TargetEmitter->UpdateModuleLists();
RemoveFromDraggedList(DraggedModule);
}
else
{
if (CascadePtr.Pin()->InsertModule(DraggedModule, TargetEmitter, TargetIndex, true))
{
TargetEmitter->UpdateModuleLists();
}
else
{
CascadePtr.Pin()->InsertModule(DraggedModule, CascadePtr.Pin()->GetSelectedEmitter(), ResetDragModIndex, true);
CascadePtr.Pin()->GetSelectedEmitter()->UpdateModuleLists();
}
RemoveFromDraggedList(DraggedModule);
}
CascadePtr.Pin()->OnRestartInLevel();
}
}
}
}
bMouseDown = false;
bMouseDragging = false;
CurrentMoveMode = MoveMode_None;
DraggedModule = NULL;
Viewport->Invalidate();
}
else if (Event == IE_DoubleClick)
{
if (Key == EKeys::LeftMouseButton)
{
HHitProxy* HitResult = Viewport->GetHitProxy(HitX,HitY);
if (HitResult)
{
if (HitResult->IsA(HCascadeEdEmitterProxy::StaticGetType()))
{
UParticleEmitter* Emitter = ((HCascadeEdEmitterProxy*)HitResult)->Emitter;
if (Emitter)
{
Emitter->bCollapsed = !Emitter->bCollapsed;
if (Emitter->bCollapsed == true)
{
CascadePtr.Pin()->SetSelectedModule(NULL);
}
Viewport->Invalidate();
}
}
}
}
}
}
if (Event == IE_Pressed)
{
if (bMouseDragging && (CurrentMoveMode != MoveMode_None))
{
// Don't allow deleting while moving modules...
bHandled = true;
}
else
{
if ( Key == EKeys::Platform_Delete )
{
if (CascadePtr.Pin()->GetSelectedModule())
{
CascadePtr.Pin()->OnDeleteModule(true);
}
else
{
CascadePtr.Pin()->OnDeleteEmitter();
}
bHandled = true;
}
else if (Key == EKeys::Left)
{
CascadePtr.Pin()->MoveSelectedEmitter(-1);
bHandled = true;
}
else if (Key == EKeys::Right)
{
CascadePtr.Pin()->MoveSelectedEmitter(1);
bHandled = true;
}
else if ((Key == EKeys::Z) && bCtrlDown)
{
CascadePtr.Pin()->OnUndo();
bHandled = true;
}
else if ((Key == EKeys::Y) && bCtrlDown)
{
CascadePtr.Pin()->OnRedo();
bHandled = true;
}
else if (Key == EKeys::PageDown)
{
CascadePtr.Pin()->OnJumpToLowerLOD();
bHandled = true;
}
else if (Key == EKeys::PageUp)
{
CascadePtr.Pin()->OnJumpToHigherLOD();
bHandled = true;
}
}
}
// Handle viewport screenshot.
bHandled |= InputTakeScreenshot(Viewport, Key, Event);
return bHandled;
}
void FCascadeEmitterCanvasClient::CapturedMouseMove(FViewport* Viewport, int32 X, int32 Y)
{
// Update bMouseDragging.
if (bMouseDown && !bMouseDragging)
{
UParticleEmitter* SelectedEmitter = CascadePtr.Pin()->GetSelectedEmitter();
UParticleModule* SelectedModule = CascadePtr.Pin()->GetSelectedModule();
int32 SelectedModuleIndex = CascadePtr.Pin()->GetSelectedModuleIndex();
// See how far mouse has moved since we pressed button.
FIntPoint TotalMouseMove = FIntPoint(X,Y) - MousePressPosition;
int32 MoveThresh = CascadePtr.Pin()->GetEditorOptions() ? CascadePtr.Pin()->GetEditorOptions()->Cascade_MouseMoveThreshold : 4;
MoveThresh = FMath::Max<int32>(4,MoveThresh);
if (TotalMouseMove.Size() > MoveThresh)
{
if ((SelectedModuleIndex == INDEX_REQUIREDMODULE) ||
(SelectedModuleIndex == INDEX_SPAWNMODULE))
{
// Only allow dragging of these if they are being copied/shared...
if ((Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl)) ||
(Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift)))
{
bMouseDragging = true;
}
}
else
{
bMouseDragging = true;
}
}
if (SelectedEmitter)
{
int32 CurrentLODIndex = CascadePtr.Pin()->GetCurrentlySelectedLODLevelIndex();
if (CurrentLODIndex != 0)
{
MousePressPosition = FIntPoint(X,Y);
bMouseDragging = false;
}
}
// If we are moving a module, here is where we remove it from its emitter.
// Should not be able to change the CurrentMoveMode unless a module is selected.
if (bMouseDragging && (CurrentMoveMode != MoveMode_None))
{
if (SelectedModule)
{
DraggedModule = SelectedModule;
if (SelectedEmitter)
{
// DraggedModules
if (DraggedModules.Num() == 0)
{
// We are pulling from an emitter...
DraggedModules.InsertUninitialized(0, SelectedEmitter->LODLevels.Num());
}
for (int32 LODIndex = 0; LODIndex < SelectedEmitter->LODLevels.Num(); LODIndex++)
{
UParticleLODLevel* LODLevel = SelectedEmitter->LODLevels[LODIndex];
if (LODLevel)
{
if (SelectedModuleIndex >= 0)
{
DraggedModules[LODIndex] = LODLevel->Modules[SelectedModuleIndex];
}
else
{
if (SelectedModuleIndex == INDEX_TYPEDATAMODULE)
{
DraggedModules[LODIndex] = LODLevel->TypeDataModule;
}
else if (SelectedModuleIndex == INDEX_REQUIREDMODULE)
{
DraggedModules[LODIndex] = LODLevel->RequiredModule;
}
else if (SelectedModuleIndex == INDEX_SPAWNMODULE)
{
DraggedModules[LODIndex] = LODLevel->SpawnModule;
}
}
}
}
}
if (CurrentMoveMode == MoveMode_Move)
{
// Remeber where to put this module back to if we abort the move.
ResetDragModIndex = INDEX_NONE;
if (SelectedEmitter)
{
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel();
if (LODLevel)
{
for (int32 i=0; i < LODLevel->Modules.Num(); i++)
{
if (LODLevel->Modules[i] == SelectedModule)
{
ResetDragModIndex = i;
}
}
}
if (ResetDragModIndex == INDEX_NONE)
{
if (SelectedModule->IsA(UParticleModuleTypeDataBase::StaticClass()))
{
ResetDragModIndex = INDEX_TYPEDATAMODULE;
}
else if (SelectedModule->IsA(UParticleModuleRequired::StaticClass()))
{
ResetDragModIndex = INDEX_REQUIREDMODULE;
}
else if (SelectedModule->IsA(UParticleModuleSpawn::StaticClass()))
{
ResetDragModIndex = INDEX_SPAWNMODULE;
}
}
check(ResetDragModIndex != INDEX_NONE);
if ((ResetDragModIndex != INDEX_SPAWNMODULE) &&
(ResetDragModIndex != INDEX_REQUIREDMODULE))
{
CascadePtr.Pin()->OnDeleteModule(false);
}
}
else
{
// Remove the module from the dump
RemoveFromDraggedList(SelectedModule);
}
}
}
}
}
// If dragging a module around, update each frame.
if (bMouseDragging && CurrentMoveMode != MoveMode_None)
{
Viewport->Invalidate();
}
}
float FCascadeEmitterCanvasClient::GetViewportVerticalScrollBarRatio() const
{
float WidgetHeight = 1.0f;
if (CascadeViewportPtr.Pin()->GetVerticalScrollBar().IsValid())
{
WidgetHeight = CascadeViewportPtr.Pin()->GetViewport()->GetSizeXY().Y;
}
return WidgetHeight / CanvasDimensions.Y;
}
float FCascadeEmitterCanvasClient::GetViewportHorizontalScrollBarRatio() const
{
float WidgetWidth = 1.0f;
if (CascadeViewportPtr.Pin()->GetHorizontalScrollBar().IsValid())
{
WidgetWidth = CascadeViewportPtr.Pin()->GetViewport()->GetSizeXY().X;
}
return WidgetWidth / CanvasDimensions.X;
}
UParticleModule* FCascadeEmitterCanvasClient::GetDraggedModule()
{
return DraggedModule;
}
TArray<UParticleModule*>& FCascadeEmitterCanvasClient::GetDraggedModules()
{
return DraggedModules;
}
void FCascadeEmitterCanvasClient::UpdateScrollBars()
{
CanvasDimensions.Y = 0;
CanvasDimensions.X = 0;
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
for (int32 i = 0; i < ParticleSystem->Emitters.Num(); ++i)
{
int32 Height = 0;
UParticleEmitter* Emitter = ParticleSystem->Emitters[i];
if (Emitter)
{
if (Emitter->bCollapsed)
{
CanvasDimensions.X += EmitterCollapsedWidth;
}
else
{
CanvasDimensions.X += EmitterWidth;
}
Height = EmitterHeadHeight + ModulesOffset * ModuleHeight;
UParticleEmitter* SaveSelectedEmitter = CascadePtr.Pin()->GetSelectedEmitter();
CascadePtr.Pin()->SetSelectedEmitter(Emitter, true);
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel();
if (LODLevel)
{
Height += LODLevel->Modules.Num() * ModuleHeight;
}
CascadePtr.Pin()->SetSelectedEmitter(SaveSelectedEmitter, true);
if (Height > CanvasDimensions.Y)
{
CanvasDimensions.Y = Height;
}
}
}
CanvasDimensions.X += EmitterWidth; // Extra padding so the user can open a context menu in the "background" area
if (CascadeViewportPtr.Pin()->GetVerticalScrollBar().IsValid() && CascadeViewportPtr.Pin()->GetHorizontalScrollBar().IsValid())
{
float VRatio = GetViewportVerticalScrollBarRatio();
float HRatio = GetViewportHorizontalScrollBarRatio();
float VDistFromBottom = CascadeViewportPtr.Pin()->GetVerticalScrollBar()->DistanceFromBottom();
float HDistFromBottom = CascadeViewportPtr.Pin()->GetHorizontalScrollBar()->DistanceFromBottom();
if (VRatio < 1.0f)
{
if (VDistFromBottom < 1.0f)
{
CascadeViewportPtr.Pin()->GetVerticalScrollBar()->SetState(FMath::Clamp(1.0f - VRatio - VDistFromBottom, 0.0f, 1.0f), VRatio);
}
else
{
CascadeViewportPtr.Pin()->GetVerticalScrollBar()->SetState(0.0f, VRatio);
}
}
if (HRatio < 1.0f)
{
if (HDistFromBottom < 1.0f)
{
CascadeViewportPtr.Pin()->GetHorizontalScrollBar()->SetState(FMath::Clamp(1.0f - HRatio - HDistFromBottom, 0.0f, 1.0f), HRatio);
}
else
{
CascadeViewportPtr.Pin()->GetHorizontalScrollBar()->SetState(0.0f, HRatio);
}
}
}
}
void FCascadeEmitterCanvasClient::ChangeViewportScrollBarPosition(EScrollDirection Direction)
{
if (CascadeViewportPtr.Pin()->GetVerticalScrollBar().IsValid())
{
float Ratio = GetViewportVerticalScrollBarRatio();
float DistFromBottom = CascadeViewportPtr.Pin()->GetVerticalScrollBar()->DistanceFromBottom();
float OneMinusRatio = 1.0f - Ratio;
float Diff = 0.1f * OneMinusRatio;
if (Direction == Scroll_Down)
{
Diff *= -1.0f;
}
CascadeViewportPtr.Pin()->GetVerticalScrollBar()->SetState(FMath::Clamp(OneMinusRatio - DistFromBottom + Diff, 0.0f, OneMinusRatio), Ratio);
CascadeViewportPtr.Pin()->RefreshViewport();
}
}
FVector2D FCascadeEmitterCanvasClient::GetViewportScrollBarPositions() const
{
FVector2D Positions = FVector2D::ZeroVector;
if (CascadeViewportPtr.Pin()->GetVerticalScrollBar().IsValid() && CascadeViewportPtr.Pin()->GetHorizontalScrollBar().IsValid())
{
uint32 Width = CanvasDimensions.X;
uint32 Height = CanvasDimensions.Y;
float VRatio = GetViewportVerticalScrollBarRatio();
float HRatio = GetViewportHorizontalScrollBarRatio();
float VDistFromBottom = CascadeViewportPtr.Pin()->GetVerticalScrollBar()->DistanceFromBottom();
float HDistFromBottom = CascadeViewportPtr.Pin()->GetHorizontalScrollBar()->DistanceFromBottom();
if ((CascadeViewportPtr.Pin()->GetVerticalScrollBar()->GetVisibility() == EVisibility::Visible) && VDistFromBottom < 1.0f)
{
Positions.Y = FMath::Clamp(1.0f - VRatio - VDistFromBottom, 0.0f, 1.0f) * Height;
}
else
{
Positions.Y = 0.0f;
}
if ((CascadeViewportPtr.Pin()->GetHorizontalScrollBar()->GetVisibility() == EVisibility::Visible) && HDistFromBottom < 1.0f)
{
Positions.X = FMath::Clamp(1.0f - HRatio - HDistFromBottom, 0.0f, 1.0f) * Width;
}
else
{
Positions.X = 0.0f;
}
}
return Positions;
}
void FCascadeEmitterCanvasClient::DrawEmitter(int32 Index, int32 XPos, UParticleEmitter* Emitter, FViewport* Viewport, FCanvas* Canvas)
{
int32 ViewY = Viewport->GetSizeXY().Y;
if (Emitter && (Emitter->bCollapsed == false))
{
// Draw background block
// Draw header block
DrawHeaderBlock(Index, XPos, Emitter, Viewport, Canvas);
// Draw the type data module
DrawTypeDataBlock(XPos, Emitter, Viewport, Canvas);
// Draw the required module
DrawRequiredBlock(XPos, Emitter, Viewport, Canvas);
// Draw the spawn module
DrawSpawnBlock(XPos, Emitter, Viewport, Canvas);
// Draw each module - skipping the 'required' modules!
int32 YPos = EmitterHeadHeight + ModulesOffset * ModuleHeight;
int32 j;
UParticleEmitter* SaveSelectedEmitter = CascadePtr.Pin()->GetSelectedEmitter();
// Now, draw the remaining modules
CascadePtr.Pin()->SetSelectedEmitter(Emitter, true);
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel();
if (LODLevel)
{
for(j = 0; j < LODLevel->Modules.Num(); j++)
{
UParticleModule* Module = LODLevel->Modules[j];
check(Module);
if (!(Module->IsA(UParticleModuleTypeDataBase::StaticClass())))
{
DrawModule(XPos, YPos, Emitter, Module, Viewport, Canvas);
// Update Y position for next module.
YPos += ModuleHeight;
}
}
}
CascadePtr.Pin()->SetSelectedEmitter(SaveSelectedEmitter, true);
}
else
{
// Draw header block
DrawCollapsedHeaderBlock(Index, XPos, Emitter, Viewport, Canvas);
}
}
void FCascadeEmitterCanvasClient::DrawHeaderBlock(int32 Index, int32 XPos, UParticleEmitter* Emitter, FViewport* Viewport,FCanvas* Canvas)
{
int32 ViewY = Viewport->GetSizeXY().Y;
FColor HeadColor = (Emitter == CascadePtr.Pin()->GetSelectedEmitter()) ? EmitterSelectedColor : EmitterUnselectedColor;
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
if (LODLevel == NULL)
{
return;
}
if (Canvas->IsHitTesting())
Canvas->SetHitProxy(new HCascadeEdEmitterProxy(Emitter));
// If the module is shared w/ higher LOD levels, then mark it as such...
if (LODLevel->bEnabled == true)
{
Canvas->DrawTile(XPos, 0, EmitterWidth, EmitterHeadHeight, 0.f, 0.f, 1.f, 1.f, HeadColor);
}
else
{
Canvas->DrawTile(XPos, 0, EmitterWidth, EmitterHeadHeight, 0.f, 0.f, 1.f, 1.f, HeadColor, TexModuleDisabledBackground->Resource);
}
UParticleSpriteEmitter* SpriteEmitter = Cast<UParticleSpriteEmitter>(Emitter);
if (!Canvas->IsHitTesting())
{
if (SpriteEmitter)
{
FString TempString;
TempString = SpriteEmitter->GetEmitterName().ToString();
Canvas->DrawShadowedString(XPos + 10, 5, *TempString, GEngine->GetSmallFont(), FLinearColor::White);
int32 ThumbSize = EmitterHeadHeight - 2*EmitterThumbBorder;
FIntPoint ThumbPos(XPos + EmitterWidth - ThumbSize - EmitterThumbBorder, EmitterThumbBorder);
ThumbPos.X += Origin2D.X;
ThumbPos.Y += Origin2D.Y;
UParticleLODLevel* HighestLODLevel = Emitter->LODLevels[0];
TempString = FString::Printf(TEXT("%4d"), HighestLODLevel->PeakActiveParticles);
Canvas->DrawShadowedString(XPos + 90, 25, *TempString, GEngine->GetSmallFont(), FLinearColor::White);
if (!Canvas->IsHitTesting())
{
// Draw sprite material thumbnail.
check(LODLevel->RequiredModule);
UMaterialInterface* MaterialInterface = LODLevel->RequiredModule->Material;
UParticleModuleTypeDataMesh* MeshTD = Cast<UParticleModuleTypeDataMesh>(LODLevel->TypeDataModule);
if (MeshTD)
{
UStaticMesh* Mesh = MeshTD->Mesh;
if (Mesh)
{
// See if there is a mesh material
if (MeshTD->bOverrideMaterial == false)
{
MaterialInterface = Mesh->GetMaterial(0);
}
// See if there is a mesh material module...
for (int32 ModIndex = 0; ModIndex < LODLevel->Modules.Num(); ModIndex++)
{
UParticleModuleMeshMaterial* MeshMatMod = Cast<UParticleModuleMeshMaterial>(LODLevel->Modules[ModIndex]);
if (MeshMatMod && MeshMatMod->bEnabled)
{
for (int32 MatIndex = 0; MatIndex < MeshMatMod->MeshMaterials.Num(); MatIndex++)
{
if (MeshMatMod->MeshMaterials[MatIndex])
{
MaterialInterface = MeshMatMod->MeshMaterials[MatIndex];
break;
}
}
}
}
}
}
if (MaterialInterface)
{
// Get the rendering info for this object
FThumbnailRenderingInfo* RenderInfo =
GUnrealEd->GetThumbnailManager()->GetRenderingInfo(MaterialInterface);
// If there is an object configured to handle it, draw the thumbnail
if (RenderInfo != NULL && RenderInfo->Renderer != NULL)
{
RenderInfo->Renderer->Draw(MaterialInterface, ThumbPos.X, ThumbPos.Y, ThumbSize, ThumbSize, Viewport, Canvas);
}
}
else
{
Canvas->DrawTile(ThumbPos.X - Origin2D.X, ThumbPos.Y - Origin2D.Y, ThumbSize, ThumbSize, 0.f, 0.f, 1.f, 1.f, FLinearColor::Black);
}
}
}
}
// Draw column background
Canvas->DrawTile(XPos, EmitterHeadHeight, EmitterWidth, ViewY - EmitterHeadHeight - Origin2D.Y, 0.f, 0.f, 1.f, 1.f, EmitterBackgroundColor);
if (Canvas->IsHitTesting())
Canvas->SetHitProxy(NULL);
// Draw enable/disable button
FTexture* EnabledIconTxtr = NULL;
if (LODLevel->bEnabled == true)
{
EnabledIconTxtr = GetIconTexture(Icon_ModuleEnabled);
}
else
{
EnabledIconTxtr = GetIconTexture(Icon_ModuleDisabled);
}
check(EnabledIconTxtr);
if (Canvas->IsHitTesting())
Canvas->SetHitProxy(new HCascadeEdEmitterEnableProxy(Emitter));
Canvas->DrawTile(XPos + 12, 26, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, EnabledIconTxtr);
if (Canvas->IsHitTesting())
Canvas->SetHitProxy(NULL);
// Draw rendering mode button.
FTexture* IconTxtr = NULL;
switch (SpriteEmitter->EmitterRenderMode)
{
case ERM_Normal:
IconTxtr = GetIconTexture(Icon_RenderNormal);
break;
case ERM_Point:
IconTxtr = GetIconTexture(Icon_RenderPoint);
break;
case ERM_Cross:
IconTxtr = GetIconTexture(Icon_RenderCross);
break;
case ERM_LightsOnly:
IconTxtr = GetIconTexture(Icon_RenderLights);
break;
case ERM_None:
IconTxtr = GetIconTexture(Icon_RenderNone);
break;
}
check(IconTxtr);
if (Canvas->IsHitTesting())
Canvas->SetHitProxy(new HCascadeEdDrawModeButtonProxy(Emitter, SpriteEmitter->EmitterRenderMode));
Canvas->DrawTile(XPos + 32, 26, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, IconTxtr);
if (Canvas->IsHitTesting())
Canvas->SetHitProxy(NULL);
FTexture* SoloIconTxr = NULL;
if (SpriteEmitter->bIsSoloing)
{
SoloIconTxr = GetIconTexture(Icon_SoloEnabled);
}
else
{
SoloIconTxr = GetIconTexture(Icon_SoloDisabled);
}
check(SoloIconTxr);
if (Canvas->IsHitTesting())
Canvas->SetHitProxy(new HCascadeEdSoloButtonProxy(Emitter));
Canvas->DrawTile(XPos + 52, 26, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, SoloIconTxr);
if (Canvas->IsHitTesting())
Canvas->SetHitProxy(NULL);
DrawColorButton(XPos, Emitter, NULL, Canvas->IsHitTesting(), Canvas);
}
void FCascadeEmitterCanvasClient::DrawCollapsedHeaderBlock(int32 Index, int32 XPos, UParticleEmitter* Emitter, FViewport* Viewport,FCanvas* Canvas)
{
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
if (LODLevel == NULL)
{
return;
}
int32 ViewY = Viewport->GetSizeXY().Y;
FColor HeadColor = Emitter->EmitterEditorColor;
if (Canvas->IsHitTesting())
{
Canvas->SetHitProxy(new HCascadeEdEmitterProxy(Emitter));
}
// If the module is shared w/ higher LOD levels, then mark it as such...
if (LODLevel->bEnabled == true)
{
Canvas->DrawTile(XPos, 0, EmitterCollapsedWidth, EmitterHeadHeight, 0.f, 0.f, 1.f, 1.f, HeadColor);
}
else
{
Canvas->DrawTile(XPos, 0, EmitterCollapsedWidth, EmitterHeadHeight, 0.f, 0.f, 1.f, 1.f, HeadColor, TexModuleDisabledBackground->Resource);
}
// Draw column background
Canvas->DrawTile(XPos, EmitterHeadHeight, EmitterCollapsedWidth, ViewY - EmitterHeadHeight - Origin2D.Y, 0.f, 0.f, 1.f, 1.f, EmitterBackgroundColor);
if (Canvas->IsHitTesting())
{
Canvas->SetHitProxy(NULL);
}
}
void FCascadeEmitterCanvasClient::DrawTypeDataBlock(int32 XPos, UParticleEmitter* Emitter, FViewport* Viewport, FCanvas* Canvas)
{
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
if (LODLevel)
{
UParticleModule* Module = LODLevel->TypeDataModule;
if (Module)
{
check(Module->IsA(UParticleModuleTypeDataBase::StaticClass()));
DrawModule(XPos, EmitterHeadHeight, Emitter, Module, Viewport, Canvas, false);
}
}
}
void FCascadeEmitterCanvasClient::DrawRequiredBlock(int32 XPos, UParticleEmitter* Emitter, FViewport* Viewport, FCanvas* Canvas)
{
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
if (LODLevel)
{
check(LODLevel->RequiredModule);
DrawModule(XPos, EmitterHeadHeight + RequiredModuleOffset * ModuleHeight, Emitter, LODLevel->RequiredModule, Viewport, Canvas, false);
}
}
void FCascadeEmitterCanvasClient::DrawSpawnBlock(int32 XPos, UParticleEmitter* Emitter, FViewport* Viewport, FCanvas* Canvas)
{
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
if (LODLevel)
{
UParticleModule* Module = LODLevel->SpawnModule;
if (Module)
{
check(Module->IsA(UParticleModuleSpawn::StaticClass()));
DrawModule(XPos, EmitterHeadHeight + SpawnModuleOffset * ModuleHeight, Emitter, Module, Viewport, Canvas);
}
}
}
void FCascadeEmitterCanvasClient::DrawModule(int32 XPos, int32 YPos, UParticleEmitter* Emitter, UParticleModule* Module,
FViewport* Viewport, FCanvas* Canvas, bool bDrawEnableButton)
{
// Hack to ensure no black modules...
if (Module->ModuleEditorColor == FColor(0,0,0,0))
{
Module->ModuleEditorColor = FColor::MakeRandomColor();
}
// Grab the correct color to use
FColor ModuleBkgColor;
if (CascadePtr.Pin()->GetIsSoloing() && (Emitter->bIsSoloing == false))
{
ModuleBkgColor = FColor(0,0,0,0);
}
else if (Module == CascadePtr.Pin()->GetSelectedModule())
{
ModuleBkgColor = ModuleColors[Module->GetModuleType()][Selection_Selected];
}
else
{
ModuleBkgColor = ModuleColors[Module->GetModuleType()][Selection_Unselected];
}
// Offset the 2D draw origin
Canvas->PushRelativeTransform(FTranslationMatrix(FVector(XPos,YPos,0)));
bool bCanvasHitTesting = Canvas->IsHitTesting();
// Draw the module box and it's proxy
DrawModule(Canvas, Module, ModuleBkgColor, Emitter);
if (CascadePtr.Pin()->GetIsModuleShared(Module) || (CascadePtr.Pin()->GetCurveEditor().IsValid() && Module->IsDisplayedInCurveEd(CascadePtr.Pin()->GetCurveEditor()->GetEdSetup())))
{
DrawColorButton(XPos, Emitter, Module, bCanvasHitTesting, Canvas);
}
// Draw little 'send properties to graph' button.
if (Module->ModuleHasCurves())
{
DrawCurveButton(Emitter, Module, bCanvasHitTesting, Canvas);
}
// Draw button for 3DDrawMode.
if (CascadePtr.Pin()->GetEditorOptions()->bUseSlimCascadeDraw == false)
{
if (Module->bSupported3DDrawMode)
{
Draw3DDrawButton(Emitter, Module, bCanvasHitTesting, Canvas);
}
}
if (bDrawEnableButton == true)
{
DrawEnableButton(Emitter, Module, bCanvasHitTesting, Canvas);
}
Canvas->PopTransform();
}
void FCascadeEmitterCanvasClient::DrawModule(FCanvas* Canvas, UParticleModule* Module, FColor ModuleBkgColor, UParticleEmitter* Emitter)
{
if (Canvas->IsHitTesting())
Canvas->SetHitProxy(new HCascadeEdModuleProxy(Emitter, Module));
Canvas->DrawTile(-1, -1, EmitterWidth+1, ModuleHeight+2, 0.f, 0.f, 0.f, 0.f, FLinearColor::Black);
if (Canvas->IsHitTesting())
{
Canvas->SetHitProxy(NULL);
return;
}
int32 CurrLODSetting = CascadePtr.Pin()->GetCurrentlySelectedLODLevelIndex();
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
bool bIsModuleValid = true;
{
if (LODLevel)
{
// Excepting the spawn and required modules, check the type data filters.
if (Module != LODLevel->SpawnModule
&& Module != LODLevel->RequiredModule)
{
UCascadeConfiguration* EditorConfig = CascadePtr.Pin()->GetEditorConfiguration();
FName TypeDataName = NAME_None;
if (LODLevel->TypeDataModule)
{
TypeDataName = LODLevel->TypeDataModule->GetClass()->GetFName();
}
bIsModuleValid = EditorConfig->IsModuleTypeValid(TypeDataName, Module->GetClass()->GetFName());
}
if (bIsModuleValid)
{
FString ErrorString;
bIsModuleValid = Module->IsValidForLODLevel(LODLevel,ErrorString);
if (!ErrorString.IsEmpty())
{
new(ModuleErrorStrings) FString(FString::Printf(TEXT("%s: %s"), *Emitter->EmitterName.GetPlainNameString(), *ErrorString));
}
}
else
{
NumRejectedModulesDrawn++;
}
}
}
// If the module is shared w/ higher LOD levels, then mark it as such...
if (bIsModuleValid && LODLevel && LODLevel->IsModuleEditable(Module))
{
Canvas->DrawTile(0, 0, EmitterWidth-1, ModuleHeight, 0.f, 0.f, 1.f, 1.f, ModuleBkgColor);
}
else
{
FColor BkgColor = ModuleBkgColor;
FTexture* BkgTexture = TexModuleDisabledBackground->Resource;
if (!bIsModuleValid)
{
BkgColor.R = 255;
BkgTexture = GetIconTexture(Icon_ModuleDisabled);
}
Canvas->DrawTile(0, 0, EmitterWidth-1, ModuleHeight, 0.f, 0.f, 1.f, 1.f, BkgColor, BkgTexture);
}
int32 XL, YL;
FString ModuleName = Module->GetClass()->GetDescription();
// Postfix name with '+' if shared.
if (CascadePtr.Pin()->GetIsModuleShared(Module))
ModuleName = ModuleName + FString(TEXT("+"));
StringSize(GEngine->GetSmallFont(), XL, YL, *(ModuleName));
int32 StartY = 3;
if (CascadePtr.Pin()->GetEditorOptions()->bCenterCascadeModuleText == true)
{
StartY = FMath::Max<int32>((ModuleHeight - YL) / 2, 3);
}
Canvas->DrawShadowedString(10, StartY, *(ModuleName), GEngine->GetSmallFont(), FLinearColor::White);
}
void FCascadeEmitterCanvasClient::DrawDraggedModule(UParticleModule* Module, FViewport* Viewport, FCanvas* Canvas)
{
FIntPoint MousePos = FIntPoint(Viewport->GetMouseX(), Viewport->GetMouseY());
// Draw indicator for where we would insert this module.
UParticleEmitter* TargetEmitter = NULL;
int32 TargetIndex = INDEX_NONE;
FindDesiredModulePosition(MousePos, TargetEmitter, TargetIndex);
MousePos += Origin2D;
// When dragging, draw the module under the mouse cursor.
FVector Translate(MousePos.X + MouseHoldOffset.X, MousePos.Y + MouseHoldOffset.Y, 0);
// -0.5 comes from previous FVector(FIntPoint) constructor behaviour, not sure if it was intened here
Translate -= FVector(Origin2D.X - 0.5f, Origin2D.Y - 0.5f, 0.f);
if (!Module->IsA(UParticleModuleTypeDataBase::StaticClass()))
{
if (Module->GetModuleType() == EPMT_Required)
{
Translate.Y += RequiredModuleOffset * ModuleHeight;
}
else if (Module->GetModuleType() == EPMT_Spawn)
{
Translate.Y += SpawnModuleOffset * ModuleHeight;
}
else
{
Translate.Y += ModulesOffset * ModuleHeight;
}
}
Canvas->PushRelativeTransform(FTranslationMatrix(Translate));
DrawModule(Canvas, DraggedModule, EmitterSelectedColor, TargetEmitter);
Canvas->PopTransform();
}
void FCascadeEmitterCanvasClient::DrawCurveButton(UParticleEmitter* Emitter, UParticleModule* Module, bool bHitTesting, FCanvas* Canvas)
{
if (bHitTesting)
Canvas->SetHitProxy(new HCascadeEdGraphButton(Emitter, Module));
int32 YPosition = 2;
if (CascadePtr.Pin()->GetEditorOptions()->bCenterCascadeModuleText == true)
{
YPosition = FMath::Max<int32>((ModuleHeight - 16) / 2, 2);
}
Canvas->DrawTile(EmitterWidth - 20, YPosition, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, GetIconTexture(Icon_CurveEdit));
if (bHitTesting)
Canvas->SetHitProxy(NULL);
}
void FCascadeEmitterCanvasClient::DrawColorButton(int32 XPos, UParticleEmitter* Emitter, UParticleModule* Module, bool bHitTesting, FCanvas* Canvas)
{
if (bHitTesting)
Canvas->SetHitProxy(new HCascadeEdColorButtonProxy(Emitter, Module));
if (Module)
{
Canvas->DrawTile(0, 0, 5, ModuleHeight, 0.f, 0.f, 1.f, 1.f, Module->ModuleEditorColor);
}
else
{
Canvas->DrawTile(XPos, 0, 5, EmitterHeadHeight, 0.f, 0.f, 1.f, 1.f, Emitter->EmitterEditorColor);
}
if (bHitTesting)
Canvas->SetHitProxy(NULL);
}
void FCascadeEmitterCanvasClient::Draw3DDrawButton(UParticleEmitter* Emitter, UParticleModule* Module, bool bHitTesting, FCanvas* Canvas)
{
if (bHitTesting)
Canvas->SetHitProxy(new HCascadeEd3DDrawModeButtonProxy(Emitter, Module));
if (Module->b3DDrawMode)
{
Canvas->DrawTile(EmitterWidth - 40, 21, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, GetIconTexture(Icon_3DDrawEnabled));
}
else
{
Canvas->DrawTile(EmitterWidth - 40, 21, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, GetIconTexture(Icon_3DDrawDisabled));
}
if (bHitTesting)
Canvas->SetHitProxy(NULL);
#if defined(_CASCADE_ALLOW_3DDRAWOPTIONS_)
if (Module->b3DDrawMode)
{
if (bHitTesting)
Canvas->SetHitProxy(new HCascadeEd3DDrawModeOptionsButtonProxy(Emitter, Module));
Canvas->DrawTile(10 + 20, 20 + 10, 8, 8, 0.f, 0.f, 1.f, 1.f, FLinearColor::Black);
Canvas->DrawTile(11 + 20, 21 + 10, 6, 6, 0.f, 0.f, 1.f, 1.f, FColor(100,200,100));
if (bHitTesting)
Canvas->SetHitProxy(NULL);
}
#endif //#if defined(_CASCADE_ALLOW_3DDRAWOPTIONS_)
}
void FCascadeEmitterCanvasClient::DrawEnableButton(UParticleEmitter* Emitter, UParticleModule* Module, bool bHitTesting, FCanvas* Canvas)
{
if (bHitTesting)
Canvas->SetHitProxy(new HCascadeEdEnableButtonProxy(Emitter, Module));
if (CascadePtr.Pin()->GetEditorOptions()->bUseSlimCascadeDraw == false)
{
if (Module->bEnabled)
{
Canvas->DrawTile(EmitterWidth - 20, 21, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, GetIconTexture(Icon_ModuleEnabled));
}
else
{
Canvas->DrawTile(EmitterWidth - 20, 21, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, GetIconTexture(Icon_ModuleDisabled));
}
}
else
{
int32 YPosition = 2;
if (CascadePtr.Pin()->GetEditorOptions()->bCenterCascadeModuleText == true)
{
YPosition = FMath::Max<int32>((ModuleHeight - 16) / 2, 2);
}
if (Module->bEnabled)
{
Canvas->DrawTile(EmitterWidth - 40, YPosition, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, GetIconTexture(Icon_ModuleEnabled));
}
else
{
Canvas->DrawTile(EmitterWidth - 40, YPosition, 16, 16, 0.f, 0.f, 1.f, 1.f, FLinearColor::White, GetIconTexture(Icon_ModuleDisabled));
}
}
if (bHitTesting)
Canvas->SetHitProxy(NULL);
}
void FCascadeEmitterCanvasClient::DrawModuleDump(FViewport* Viewport, FCanvas* Canvas)
{
#if defined(_CASCADE_ENABLE_MODULE_DUMP_)
int32 ViewX = Viewport->GetSizeXY().X;
int32 ViewY = Viewport->GetSizeXY().Y;
bool bHitTesting = RI->IsHitTesting();
int32 XPos = ViewX - EmitterWidth - 1;
FColor HeadColor = EmitterUnselectedColor;
FIntPoint SaveOrigin2D = RI->Origin2D;
RI->SetOrigin2D(0, SaveOrigin2D.Y);
Canvas->DrawTile(XPos - 2, 0, XPos + 2, ViewY - Origin2D.Y, 0.f, 0.f, 1.f, 1.f, FLinearColor::Black);
Canvas->DrawTile(XPos, 0, EmitterWidth, EmitterHeadHeight, 0.f, 0.f, 1.f, 1.f, HeadColor);
Canvas->DrawTile(XPos, 0, 5, EmitterHeadHeight, 0.f, 0.f, 1.f, 1.f, FLinearColor::Black);
FString ModuleDumpTitle = NSLOCTEXT("UnrealEd", "ModuleDump", "Module Dump").ToString();
Canvas->DrawShadowedString(XPos + 10, 5, *ModuleDumpTitle, GEngine->GetSmallFont(), FLinearColor::White);
// Draw column background
Canvas->DrawTile(XPos, EmitterHeadHeight, EmitterWidth, ViewY - EmitterHeadHeight - Origin2D.Y, 0.f, 0.f, 1.f, 1.f, FColor(160, 160, 160));
if (bHitTesting)
Canvas->SetHitProxy(NULL);
// Draw the dump module list...
int32 YPos = EmitterHeadHeight;
FVector2D TempOrigin = Origin2D;
Origin2D.X = 0;
for(int32 i = 0; i < CascadePtr.Pin()->ModuleDumpList.Num(); i++)
{
UParticleModule* Module = CascadePtr.Pin()->ModuleDumpList(i);
check(Module);
DrawModule(XPos, YPos, NULL, Module, Viewport, Canvas);
// Update Y position for next module.
YPos += ModuleHeight;
}
Origin2D.X = TempOrigin.X;
RI->SetOrigin2D(SaveOrigin2D);
#endif //#if defined(_CASCADE_ENABLE_MODULE_DUMP_)
}
void FCascadeEmitterCanvasClient::FindDesiredModulePosition(const FIntPoint& Pos, class UParticleEmitter* &OutEmitter, int32 &OutIndex)
{
// Calculate the position on the canvas, not the window...
int32 PositionCheck = Pos.X - Origin2D.X;
int32 CurrentWidth = 0;
int32 EmitterIndex = -1;
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
for (int32 CheckIndex = 0; CheckIndex < ParticleSystem->Emitters.Num(); CheckIndex++)
{
UParticleEmitter* CheckEmitter = ParticleSystem->Emitters[CheckIndex];
if (CheckEmitter)
{
int32 CheckWidth = CheckEmitter->bCollapsed ? EmitterCollapsedWidth : EmitterWidth;
if ((PositionCheck > CurrentWidth) && (PositionCheck <= CurrentWidth + CheckWidth))
{
EmitterIndex = CheckIndex;
break;
}
CurrentWidth += CheckWidth;
}
}
// If invalid Emitter, return nothing.
if (EmitterIndex < 0 || EmitterIndex > ParticleSystem->Emitters.Num()-1)
{
OutEmitter = NULL;
OutIndex = INDEX_NONE;
return;
}
OutEmitter = ParticleSystem->Emitters[EmitterIndex];
UParticleLODLevel* LODLevel = OutEmitter->LODLevels[0];
OutIndex = FMath::Clamp<int32>(((Pos.Y - Origin2D.Y) - EmitterHeadHeight - ModulesOffset * ModuleHeight) / ModuleHeight, 0, LODLevel->Modules.Num());
}
FIntPoint FCascadeEmitterCanvasClient::FindModuleTopLeft(class UParticleEmitter* Emitter, class UParticleModule* Module, FViewport* Viewport)
{
int32 i;
UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem();
int32 EmitterIndex = -1;
for(i = 0; i < ParticleSystem->Emitters.Num(); i++)
{
if (ParticleSystem->Emitters[i] == Emitter)
EmitterIndex = i;
}
int32 ModuleIndex = 0;
if (EmitterIndex != -1)
{
if (Module && Module->IsA(UParticleModuleTypeDataBase::StaticClass()))
{
return FIntPoint(EmitterIndex*EmitterWidth, EmitterHeadHeight);
}
else
{
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel(Emitter);
if (LODLevel)
{
for(i = 0; i < LODLevel->Modules.Num(); i++)
{
if (LODLevel->Modules[i] == Module)
{
ModuleIndex = i;
}
}
}
}
int32 Width = 0;
for (int32 InnerIndex = 0; InnerIndex < EmitterIndex; InnerIndex++)
{
UParticleEmitter* InnerEmitter = ParticleSystem->Emitters[InnerIndex];
if (InnerEmitter)
{
Width += InnerEmitter->bCollapsed ? EmitterCollapsedWidth : EmitterWidth;
}
}
return FIntPoint(Width, EmitterHeadHeight + ModuleIndex*ModuleHeight);
}
TArray<UParticleModule*>& ModuleDumpList = CascadePtr.Pin()->GetDraggedModuleList();
// Must be in the module dump...
check(ModuleDumpList.Num());
for (i = 0; i < ModuleDumpList.Num(); i++)
{
if (ModuleDumpList[i] == Module)
{
int32 OffsetHeight = 0;
if (!Module->IsA(UParticleModuleTypeDataBase::StaticClass()))
{
// When we grab from the dump, we need to account for no 'TypeData'
OffsetHeight = ModuleHeight;
}
return FIntPoint(Viewport->GetSizeXY().X - EmitterWidth - Origin2D.X, EmitterHeadHeight - OffsetHeight + i * EmitterHeadHeight - Origin2D.Y);
}
}
return FIntPoint(0.f, 0.f);
}
void FCascadeEmitterCanvasClient::RemoveFromDraggedList(UParticleModule* Module)
{
TArray<UParticleModule*>& ModuleDumpList = CascadePtr.Pin()->GetDraggedModuleList();
for (int32 i = 0; i < ModuleDumpList.Num(); i++)
{
if (ModuleDumpList[i] == Module)
{
ModuleDumpList.RemoveAt(i);
break;
}
}
}
FTexture* FCascadeEmitterCanvasClient::GetIconTexture(ECascadeIcons eIcon)
{
if ((eIcon >= 0) && (eIcon < Icon_COUNT))
{
UTexture2D* IconTexture = IconTex[eIcon];
if (IconTexture)
{
return IconTexture->Resource;
}
}
check(!TEXT("Cascade: Invalid Icon Request!"));
return NULL;
}
void FCascadeEmitterCanvasClient::OpenModuleMenu()
{
const FVector2D MouseCursorLocation = FSlateApplication::Get().GetCursorPos();
FSlateApplication::Get().PushMenu(
CascadeViewportPtr.Pin().ToSharedRef(),
FWidgetPath(),
BuildMenuWidgetModule(),
MouseCursorLocation,
FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)
);
}
void FCascadeEmitterCanvasClient::OpenEmitterMenu()
{
const FVector2D MouseCursorLocation = FSlateApplication::Get().GetCursorPos();
FSlateApplication::Get().PushMenu(
CascadeViewportPtr.Pin().ToSharedRef(),
FWidgetPath(),
BuildMenuWidgetEmitter(),
MouseCursorLocation,
FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)
);
}
void FCascadeEmitterCanvasClient::OpenBackgroundMenu()
{
const FVector2D MouseCursorLocation = FSlateApplication::Get().GetCursorPos();
FSlateApplication::Get().PushMenu(
CascadeViewportPtr.Pin().ToSharedRef(),
FWidgetPath(),
BuildMenuWidgetBackround(),
MouseCursorLocation,
FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)
);
}
TSharedRef<SWidget> FCascadeEmitterCanvasClient::BuildMenuWidgetModule()
{
UParticleModule* SelectedModule = CascadePtr.Pin()->GetSelectedModule();
const bool bShouldCloseWindowAfterMenuSelection = true; // Set the menu to automatically close when the user commits to a choice
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, CascadePtr.Pin()->GetToolkitCommands());
if (SelectedModule)
{
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().DeleteModule);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().RefreshModule);
if (SelectedModule != NULL)
{
if (SelectedModule->IsA(UParticleModuleRequired::StaticClass()))
{
MenuBuilder.BeginSection("CascadeSyncUseMaterial");
{
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().SyncMaterial);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().UseMaterial);
}
MenuBuilder.EndSection();
}
int32 CurrLODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevelIndex();
if (CurrLODLevel > 0)
{
bool bAddDuplicateOptions = true;
if (SelectedModule)
{
if (CascadePtr.Pin()->GetIsModuleShared(SelectedModule) == true)
{
bAddDuplicateOptions = false;
}
}
if (bAddDuplicateOptions == true)
{
MenuBuilder.BeginSection("CascadeDupe");
{
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().DupeFromHigher);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().ShareFromHigher);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().DupeFromHighest);
}
MenuBuilder.EndSection();
}
else
{
// It's shared... add an unshare option
}
}
if (SelectedModule->SupportsRandomSeed())
{
MenuBuilder.BeginSection("CascadeRandomSeed");
{
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().SetRandomSeed);
}
MenuBuilder.EndSection();
}
else
{
if (CurrLODLevel == 0)
{
// See if there is a seeded version of this module...
FString ClassName = SelectedModule->GetClass()->GetName();
UE_LOG(LogCascade, Log, TEXT("Non-seeded module %s"), *ClassName);
// This only works if the seeded version is names <ClassName>_Seeded!!!!
FString SeededClassName = ClassName + TEXT("_Seeded");
if (FindObject<UClass>(ANY_PACKAGE, *SeededClassName) != NULL)
{
MenuBuilder.BeginSection("CascadeConvertToSeeded");
{
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().ConvertToSeeded);
}
MenuBuilder.EndSection();
}
}
}
int32 CustomEntryCount = SelectedModule->GetNumberOfCustomMenuOptions();
if (CustomEntryCount > 0)
{
MenuBuilder.BeginSection("CascadeCustomMenuOptions");
{
for (int32 EntryIdx = 0; EntryIdx < CustomEntryCount; EntryIdx++)
{
FString DisplayString;
if (SelectedModule->GetCustomMenuEntryDisplayString(EntryIdx, DisplayString) == true)
{
MenuBuilder.AddMenuEntry(
FText::FromString( DisplayString ),
FText(),
FSlateIcon(),
FUIAction(FExecuteAction::CreateSP(CascadePtr.Pin().ToSharedRef(), &FCascade::OnCustomModuleOption, EntryIdx)));
}
}
}
MenuBuilder.EndSection();
}
}
}
return MenuBuilder.MakeWidget();
}
TSharedRef<SWidget> FCascadeEmitterCanvasClient::BuildMenuWidgetEmitter()
{
UParticleEmitter* SelectedEmitter = CascadePtr.Pin()->GetSelectedEmitter();
UCascadeOptions* EditorOptions = CascadePtr.Pin()->GetEditorOptions();
const bool bShouldCloseWindowAfterMenuSelection = true; // Set the menu to automatically close when the user commits to a choice
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, CascadePtr.Pin()->GetToolkitCommands());
if (SelectedEmitter)
{
InitializeModuleEntries();
// Emitter options
{
if (EditorOptions->bUseSubMenus == false)
{
MenuBuilder.BeginSection("CascadeEmitterOptionsNoSubMenus");
{
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().RenameEmitter);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().DuplicateEmitter);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().DuplicateShareEmitter);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().DeleteEmitter);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().ExportEmitter);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().ExportAllEmitters);
}
MenuBuilder.EndSection();
}
else
{
struct Local
{
static void BuildEmitterMenu(FMenuBuilder& Menu)
{
const FCascadeCommands& Actions = FCascadeCommands::Get();
Menu.BeginSection("CascadeEmitter", NSLOCTEXT("Cascade", "EmitterHeader", "Emitter"));
{
Menu.AddMenuEntry(FCascadeCommands::Get().RenameEmitter);
Menu.AddMenuEntry(FCascadeCommands::Get().DuplicateEmitter);
Menu.AddMenuEntry(FCascadeCommands::Get().DuplicateShareEmitter);
Menu.AddMenuEntry(FCascadeCommands::Get().DeleteEmitter);
Menu.AddMenuEntry(FCascadeCommands::Get().ExportEmitter);
Menu.AddMenuEntry(FCascadeCommands::Get().ExportAllEmitters);
}
Menu.EndSection();
}
};
MenuBuilder.BeginSection("CascadeEmitterOptionsNoHeader");
{
MenuBuilder.AddSubMenu(NSLOCTEXT("Cascade", "EmitterSubMenu", "Emitter"), FText::GetEmpty(), FNewMenuDelegate::CreateStatic(&Local::BuildEmitterMenu));
}
MenuBuilder.EndSection();
}
}
// Particle system
{
if (EditorOptions->bUseSubMenus == false)
{
MenuBuilder.BeginSection("CascadeParticleSystemNoSubMenus");
{
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().SelectParticleSystem);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().NewEmitterBefore);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().NewEmitterAfter);
MenuBuilder.AddMenuEntry(FCascadeCommands::Get().RemoveDuplicateModules);
}
MenuBuilder.EndSection();
}
else
{
struct Local
{
static void BuildParticleSystemMenu(FMenuBuilder& Menu)
{
const FCascadeCommands& Actions = FCascadeCommands::Get();
Menu.BeginSection("CascadeParticleSystem", NSLOCTEXT("Cascade", "ParticleSystemHeader", "Particle System"));
{
Menu.AddMenuEntry(FCascadeCommands::Get().SelectParticleSystem);
Menu.AddMenuEntry(FCascadeCommands::Get().NewEmitterBefore);
Menu.AddMenuEntry(FCascadeCommands::Get().NewEmitterAfter);
Menu.AddMenuEntry(FCascadeCommands::Get().RemoveDuplicateModules);
}
Menu.EndSection();
}
};
MenuBuilder.BeginSection("CascadeParticleSystemNoHeader");
{
MenuBuilder.AddSubMenu(NSLOCTEXT("Cascade", "ParticleSystemSubMenu", "Particle System"), FText::GetEmpty(), FNewMenuDelegate::CreateStatic(&Local::BuildParticleSystemMenu));
}
MenuBuilder.EndSection();
}
}
// New module data types
{
if (TypeDataModuleEntries.Num())
{
MenuBuilder.BeginSection("CascadeModuleDatTypes");
{
if (EditorOptions->bUseSubMenus == false)
{
// add the data type modules to the menu
for (int32 i = 0; i < TypeDataModuleEntries.Num(); i++)
{
MenuBuilder.AddMenuEntry(
FText::FromString( TypeDataModuleEntries[i] ),
FText(),
FSlateIcon(),
FUIAction(FExecuteAction::CreateSP(CascadePtr.Pin().ToSharedRef(), &FCascade::OnNewModule, TypeDataModuleIndices[i])));
}
}
else
{
MenuBuilder.AddSubMenu(NSLOCTEXT("Cascade", "NewDataTypeSubMenu", "TypeData"), FText::GetEmpty(), FNewMenuDelegate::CreateRaw(this, &FCascadeEmitterCanvasClient::BuildNewModuleDataTypeMenu));
}
}
MenuBuilder.EndSection();
}
}
// New Modules
{
if (ModuleEntries.Num())
{
if (EditorOptions->bUseSubMenus == false)
{
// Add each module type to menu.
for (int32 i = 0; i < ModuleEntries.Num(); i++)
{
MenuBuilder.AddMenuEntry(
FText::FromString( ModuleEntries[i] ),
FText(),
FSlateIcon(),
FUIAction(FExecuteAction::CreateSP(CascadePtr.Pin().ToSharedRef(), &FCascade::OnNewModule, ModuleIndices[i])));
}
}
else
{
UCascadeConfiguration* EditorConfig = CascadePtr.Pin()->GetEditorConfiguration();
TArray<UClass*> ParticleModuleBaseClasses = CascadePtr.Pin()->GetParticleModuleBaseClasses();
TArray<UClass*> ParticleModuleClasses = CascadePtr.Pin()->GetParticleModuleClasses();
bool bFoundTypeData = false;
FString ModuleName;
// Now, for each module base type, add another branch
for (int32 i = 0; i < ParticleModuleBaseClasses.Num(); i++)
{
ModuleName = ParticleModuleBaseClasses[i]->GetName();
if (IsModuleSuitableForModuleMenu(ModuleName) &&
IsBaseModuleTypeDataPairSuitableForModuleMenu(ModuleName) &&
HasValidChildModules(i))
{
MenuBuilder.AddSubMenu( FText::FromString( ParticleModuleBaseClasses[i]->GetDescription() ), FText::GetEmpty(), FNewMenuDelegate::CreateRaw(this, &FCascadeEmitterCanvasClient::BuildNewModuleSubMenu, i));
}
}
}
}
}
}
return MenuBuilder.MakeWidget();
}
TSharedRef<SWidget> FCascadeEmitterCanvasClient::BuildMenuWidgetBackround()
{
const bool bShouldCloseWindowAfterMenuSelection = true; // Set the menu to automatically close when the user commits to a choice
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, CascadePtr.Pin()->GetToolkitCommands());
{
FFormatNamedArguments Args;
Args.Add( TEXT("ClassName"), FText::FromString( UParticleSpriteEmitter::StaticClass()->GetDescription() ) );
MenuBuilder.AddMenuEntry( FText::Format( NSLOCTEXT("Cascade", "NewSoundEmitter", "New {ClassName}"), Args ),
FText::GetEmpty(),
FSlateIcon(),
FUIAction(FExecuteAction::CreateSP(CascadePtr.Pin().ToSharedRef(), &FCascade::OnNewEmitter)));
}
return MenuBuilder.MakeWidget();
}
void FCascadeEmitterCanvasClient::BuildNewModuleDataTypeMenu(FMenuBuilder& Menu)
{
Menu.BeginSection("CascadeTypeData", NSLOCTEXT("Cascade", "NewDataTypeHeader", "TypeData"));
{
// add the data type modules to the menu
for (int32 i = 0; i < TypeDataModuleEntries.Num(); i++)
{
Menu.AddMenuEntry(
FText::FromString( TypeDataModuleEntries[i] ),
FText(),
FSlateIcon(),
FUIAction(FExecuteAction::CreateSP(CascadePtr.Pin().ToSharedRef(), &FCascade::OnNewModule, TypeDataModuleIndices[i])));
}
}
Menu.EndSection();
}
bool FCascadeEmitterCanvasClient::HasValidChildModules(int32 i) const
{
TArray<UClass*> ParticleModuleBaseClasses = CascadePtr.Pin()->GetParticleModuleBaseClasses();
TArray<UClass*> ParticleModuleClasses = CascadePtr.Pin()->GetParticleModuleClasses();
FString ModuleName;
// Search for all modules of this type
for (int32 j = 0; j < ParticleModuleClasses.Num(); j++)
{
if (ParticleModuleClasses[j]->IsChildOf(ParticleModuleBaseClasses[i]))
{
ModuleName = ParticleModuleClasses[j]->GetName();
if (IsModuleSuitableForModuleMenu(ModuleName) &&
IsModuleTypeDataPairSuitableForModuleMenu(ModuleName))
{
return true;
}
}
}
return false;
}
void FCascadeEmitterCanvasClient::BuildNewModuleSubMenu(FMenuBuilder& Menu, int32 i)
{
TArray<UClass*> ParticleModuleBaseClasses = CascadePtr.Pin()->GetParticleModuleBaseClasses();
TArray<UClass*> ParticleModuleClasses = CascadePtr.Pin()->GetParticleModuleClasses();
FString ModuleName;
// Search for all modules of this type
for (int32 j = 0; j < ParticleModuleClasses.Num(); j++)
{
if (ParticleModuleClasses[j]->IsChildOf(ParticleModuleBaseClasses[i]))
{
ModuleName = ParticleModuleClasses[j]->GetName();
if (IsModuleSuitableForModuleMenu(ModuleName) &&
IsModuleTypeDataPairSuitableForModuleMenu(ModuleName))
{
Menu.AddMenuEntry(
FText::FromString( ParticleModuleClasses[j]->GetDescription() ),
FText(),
FSlateIcon(),
FUIAction(FExecuteAction::CreateSP(CascadePtr.Pin().ToSharedRef(), &FCascade::OnNewModule, j)));
}
}
}
}
void FCascadeEmitterCanvasClient::InitializeModuleEntries()
{
int32 i;
bool bFoundTypeData = false;
UParticleModule* DefModule;
if (!bInitializedModuleEntries)
{
TArray<UClass*> ParticleModuleClasses = CascadePtr.Pin()->GetParticleModuleClasses();
TypeDataModuleEntries.Empty();
TypeDataModuleIndices.Empty();
ModuleEntries.Empty();
ModuleIndices.Empty();
// add the data type modules to the menu
for(i = 0; i < ParticleModuleClasses.Num(); i++)
{
DefModule = ParticleModuleClasses[i]->GetDefaultObject<UParticleModule>();
FString ClassName = ParticleModuleClasses[i]->GetName();
if (ParticleModuleClasses[i]->IsChildOf(UParticleModuleTypeDataBase::StaticClass()))
{
if (IsModuleSuitableForModuleMenu(ClassName))
{
bFoundTypeData = true;
FString NewModuleString = FText::Format(NSLOCTEXT("UnrealEd", "New_F", "New {0}"), FText::FromString(ParticleModuleClasses[i]->GetDescription())).ToString();
TypeDataModuleEntries.Add(NewModuleString);
TypeDataModuleIndices.Add(i);
}
}
}
// Add each module type to menu.
for(i = 0; i < ParticleModuleClasses.Num(); i++)
{
DefModule = ParticleModuleClasses[i]->GetDefaultObject<UParticleModule>();
FString ClassName = ParticleModuleClasses[i]->GetName();
if (ParticleModuleClasses[i]->IsChildOf(UParticleModuleTypeDataBase::StaticClass()) == false)
{
if (IsModuleSuitableForModuleMenu(ClassName))
{
FString NewModuleString = FText::Format(NSLOCTEXT("UnrealEd", "New_F", "New {0}"), FText::FromString(ParticleModuleClasses[i]->GetDescription())).ToString();
ModuleEntries.Add(NewModuleString);
ModuleIndices.Add(i);
}
}
}
bInitializedModuleEntries = true;
}
}
bool FCascadeEmitterCanvasClient::IsModuleSuitableForModuleMenu(FString& InModuleName) const
{
int32 RejectIndex;
UCascadeConfiguration* EditorConfig = CascadePtr.Pin()->GetEditorConfiguration();
return (EditorConfig->ModuleMenu_ModuleRejections.Find(InModuleName, RejectIndex) == false);
}
bool FCascadeEmitterCanvasClient::IsBaseModuleTypeDataPairSuitableForModuleMenu(FString& InModuleName) const
{
int32 RejectIndex;
UCascadeConfiguration* EditorConfig = CascadePtr.Pin()->GetEditorConfiguration();
UParticleEmitter* SelectedEmitter = CascadePtr.Pin()->GetSelectedEmitter();
FString TDName(TEXT("None"));
if (SelectedEmitter)
{
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel();
if (LODLevel && LODLevel->TypeDataModule)
{
TDName = LODLevel->TypeDataModule->GetClass()->GetName();
}
}
FModuleMenuMapper* Mapper = NULL;
for (int32 MapIndex = 0; MapIndex < EditorConfig->ModuleMenu_TypeDataToBaseModuleRejections.Num(); MapIndex++)
{
if (EditorConfig->ModuleMenu_TypeDataToBaseModuleRejections[MapIndex].ObjName == TDName)
{
Mapper = &(EditorConfig->ModuleMenu_TypeDataToBaseModuleRejections[MapIndex]);
break;
}
}
if (Mapper)
{
if (Mapper->InvalidObjNames.Find(InModuleName, RejectIndex) == true)
{
return false;
}
}
return true;
}
bool FCascadeEmitterCanvasClient::IsModuleTypeDataPairSuitableForModuleMenu(FString& InModuleName) const
{
int32 RejectIndex;
UCascadeConfiguration* EditorConfig = CascadePtr.Pin()->GetEditorConfiguration();
UParticleEmitter* SelectedEmitter = CascadePtr.Pin()->GetSelectedEmitter();
FString TDName(TEXT("None"));
if (SelectedEmitter)
{
UParticleLODLevel* LODLevel = CascadePtr.Pin()->GetCurrentlySelectedLODLevel();
if (LODLevel && LODLevel->TypeDataModule)
{
TDName = LODLevel->TypeDataModule->GetClass()->GetName();
}
}
FModuleMenuMapper* Mapper = NULL;
for (int32 MapIndex = 0; MapIndex < EditorConfig->ModuleMenu_TypeDataToSpecificModuleRejections.Num(); MapIndex++)
{
if (EditorConfig->ModuleMenu_TypeDataToSpecificModuleRejections[MapIndex].ObjName == TDName)
{
Mapper = &(EditorConfig->ModuleMenu_TypeDataToSpecificModuleRejections[MapIndex]);
break;
}
}
if (Mapper)
{
if (Mapper->InvalidObjNames.Find(InModuleName, RejectIndex) == true)
{
return false;
}
}
return true;
}