You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
2212 lines
70 KiB
C++
2212 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(),
|
|
BuildMenuWidgetModule(),
|
|
MouseCursorLocation,
|
|
FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)
|
|
);
|
|
}
|
|
|
|
void FCascadeEmitterCanvasClient::OpenEmitterMenu()
|
|
{
|
|
const FVector2D MouseCursorLocation = FSlateApplication::Get().GetCursorPos();
|
|
|
|
FSlateApplication::Get().PushMenu(
|
|
CascadeViewportPtr.Pin().ToSharedRef(),
|
|
BuildMenuWidgetEmitter(),
|
|
MouseCursorLocation,
|
|
FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)
|
|
);
|
|
}
|
|
|
|
void FCascadeEmitterCanvasClient::OpenBackgroundMenu()
|
|
{
|
|
const FVector2D MouseCursorLocation = FSlateApplication::Get().GetCursorPos();
|
|
|
|
FSlateApplication::Get().PushMenu(
|
|
CascadeViewportPtr.Pin().ToSharedRef(),
|
|
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;
|
|
}
|