diff --git a/Source/Cut5/Utils/Utils.cpp b/Source/Cut5/Utils/Utils.cpp index 159eb71..13e3696 100644 --- a/Source/Cut5/Utils/Utils.cpp +++ b/Source/Cut5/Utils/Utils.cpp @@ -166,9 +166,9 @@ TArray FUtils::TrackEncodeVideo(const FTrackData& TrackData, c FString StartTime = FString::Printf(TEXT("%02d:%02d:%02d"), StartTimespan.GetHours(), StartTimespan.GetMinutes(), StartTimespan.GetSeconds()); FString EndTime = FString::Printf(TEXT("%02d:%02d:%02d"), EndTimespan.GetHours(), EndTimespan.GetMinutes(), EndTimespan.GetSeconds()); - FString InputFile = TempClipData.ResourcePropertyDataPtr->MoviePath; + FString InputFile = "\"" + TempClipData.ResourcePropertyDataPtr->MoviePath + "\""; - FString OutputFile = FPaths::ConvertRelativePathToFull(ExportPath + FString::FromInt(i) + TEXT(".mp4")); + FString OutputFile = "\"" + FPaths::ConvertRelativePathToFull(ExportPath + FString::FromInt(i) + TEXT(".mp4")) + "\""; int32 StartFrame = (TempClipData.VideoStartFrame) % static_cast(FGlobalData::GlobalFPS);; int32 EndFrame = (TempClipData.VideoEndFrame) % static_cast(FGlobalData::GlobalFPS); @@ -183,6 +183,10 @@ TArray FUtils::TrackEncodeVideo(const FTrackData& TrackData, c EncodeVideoInfo.EncodedVideoName = ExportPath + FString::FromInt(i) + TEXT(".mp4"); EncodeVideoInfo.ClipStartFrame = TempClipData.ClipStartFrame; EncodeVideoInfo.ClipEndFrame = TempClipData.ClipEndFrame; + + EncodeVideoInfo.TrackData = TrackData; + EncodeVideoInfo.ClipData = TempClipData; + EncodeVideoInfos.Add(EncodeVideoInfo); } i++; @@ -241,6 +245,9 @@ TArray FUtils::TrackEncodeAudio(const FTrackData& TrackData, c EncodeVideoInfo.EncodedVideoName = ExportPath + FString::FromInt(i) + TEXT(".mp3"); EncodeVideoInfo.ClipStartFrame = TempClipData.ClipStartFrame; EncodeVideoInfo.ClipEndFrame = TempClipData.ClipEndFrame; + + EncodeVideoInfo.TrackData = TrackData; + EncodeVideoInfo.ClipData = TempClipData; EncodeVideoInfos.Add(EncodeVideoInfo); } i++; @@ -496,6 +503,9 @@ TArray FUtils::ExportPsaf(FTrackData TrackData, const FString& FEncodeVideoInfo EncodeVideoInfo; EncodeVideoInfo.EncodedVideoName = ExportName + FString::FromInt(i) + ".psaf"; EncodeVideoInfo.EncodedVideoTimeCode = FGlobalData::GetTimeData(TempClipData.ClipStartFrame); + + EncodeVideoInfo.TrackData = TrackData; + EncodeVideoInfo.ClipData = TempClipData; EncodeVideoInfos.Add(EncodeVideoInfo); i++; } diff --git a/Source/Cut5/Widgets/Commands/CustomResourceCommands.cpp b/Source/Cut5/Widgets/Commands/CustomResourceCommands.cpp new file mode 100644 index 0000000..890434e --- /dev/null +++ b/Source/Cut5/Widgets/Commands/CustomResourceCommands.cpp @@ -0,0 +1,8 @@ +#include "CustomResourceCommands.h" + +#define LOCTEXT_NAMESPACE "FCurtainCommands" +void FCustomResourceCommands::RegisterCommands() +{ + UI_COMMAND(Delete, "删除", "Executes My FCurtainCommands", EUserInterfaceActionType::Button, FInputChord()); +} +#undef LOCTEXT_NAMESPACE diff --git a/Source/Cut5/Widgets/Commands/CustomResourceCommands.h b/Source/Cut5/Widgets/Commands/CustomResourceCommands.h new file mode 100644 index 0000000..80de613 --- /dev/null +++ b/Source/Cut5/Widgets/Commands/CustomResourceCommands.h @@ -0,0 +1,12 @@ +#pragma once + +class FCustomResourceCommands : public TCommands +{ +public: + FCustomResourceCommands() : TCommands(TEXT("FCustomResourceCommands"), NSLOCTEXT("Contexts", "FCurtainCommands", "FCurtainCommands"), NAME_None, FAppStyle::GetAppStyleSetName()) + { + } + virtual void RegisterCommands() override; + + TSharedPtr Delete; +}; diff --git a/Source/Cut5/Widgets/Commands/ShortcunCommands.h b/Source/Cut5/Widgets/Commands/ShortcunCommands.h index 4ee0464..105aaef 100644 --- a/Source/Cut5/Widgets/Commands/ShortcunCommands.h +++ b/Source/Cut5/Widgets/Commands/ShortcunCommands.h @@ -29,4 +29,7 @@ public: TSharedPtr Copy; TSharedPtr Paste; + TSharedPtr MicroLeft; + TSharedPtr MicroRight; + }; diff --git a/Source/Cut5/Widgets/Commands/ShortcutCommands.cpp b/Source/Cut5/Widgets/Commands/ShortcutCommands.cpp index 98da5f8..06fc85f 100644 --- a/Source/Cut5/Widgets/Commands/ShortcutCommands.cpp +++ b/Source/Cut5/Widgets/Commands/ShortcutCommands.cpp @@ -22,5 +22,8 @@ void FShortCutCommands::RegisterCommands() UI_COMMAND(Copy, "复制", "Executes Start Garbage Collect", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::C, EModifierKey::Control)); UI_COMMAND(Paste, "粘贴", "Executes End Garbage Collect", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::V, EModifierKey::Control)); + + UI_COMMAND(MicroLeft, "左微调", "Executes My FCurtainCommands", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::Left)); + UI_COMMAND(MicroRight, "右微调", "Executes My FCurtainCommands", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::Right)); } #undef LOCTEXT_NAMESPACE diff --git a/Source/Cut5/Widgets/DefineGlobal.h b/Source/Cut5/Widgets/DefineGlobal.h index 9a021a4..138001f 100644 --- a/Source/Cut5/Widgets/DefineGlobal.h +++ b/Source/Cut5/Widgets/DefineGlobal.h @@ -31,7 +31,7 @@ public: inline static float GlobalFPS = 30.0f; inline static FString CurrentProjectName = "DefaultProject"; inline static FString BasePath = FPaths::ProjectDir(); - inline static FString Version = "1.0.0"; + inline static FString Version = "1.0.1"; inline static FString ExportPath = ""; inline static TArray Colors = @@ -97,6 +97,11 @@ struct FStringWithGUID { return this->Guid == Other.Guid; } + + bool operator==(const FGuid& OtherGuid) const + { + return this->Guid == OtherGuid; + } }; enum class ETrackType @@ -168,6 +173,7 @@ struct CUT5_API FTrackData Ar << TrackData.TrackNum; Ar << TrackData.ClipData; Ar << TrackData.DeviceTrack; + Ar << TrackData.IsMute; return Ar; }; @@ -178,6 +184,8 @@ struct CUT5_API FTrackData int32 TrackNum = 1; FDeviceTrack DeviceTrack = FDeviceTrack("None", ETrackType::VideoTrack); TArray ClipData; + + bool IsMute = false; }; struct CUT5_API FCursorData { @@ -250,6 +258,7 @@ struct CUT5_API FPresetsCustomData None, Breathe, Flash, + Gradient, }; TArray Colors = { FLinearColor(1, 1 , 1) }; @@ -920,6 +929,9 @@ struct FEncodeVideoInfo FString EncodedVideoTimeCode = "00:00:00:00"; int32 ClipStartFrame = 0; int32 ClipEndFrame = 0; + + FTrackData TrackData; + FClipData ClipData; }; diff --git a/Source/Cut5/Widgets/SCustomInputPanel.cpp b/Source/Cut5/Widgets/SCustomInputPanel.cpp index 3cc6887..8fe2956 100644 --- a/Source/Cut5/Widgets/SCustomInputPanel.cpp +++ b/Source/Cut5/Widgets/SCustomInputPanel.cpp @@ -14,6 +14,7 @@ #include #include "AudioDevice.h" +#include "Commands/CustomResourceCommands.h" #include "Cut5/Interface/SoundInterface.h" #include "Cut5/Utils/FFMPEGUtils.h" @@ -259,7 +260,7 @@ void SCustomInputPanel::Construct(const FArguments& InArgs) continue; }; - TSharedPtr Resource = SNew(SCustomInputResource).MainInterface(MainWidgetInterface) + TSharedPtr Resource = SNew(SCustomInputResource).MainInterface(MainWidgetInterface).CustomInputPanel(this) .PropertyData(Data).OnCheckBoxChecked_Lambda([this](FTimelinePropertyData& ClickedData, bool bIsChecked) { if (bIsChecked == true) @@ -346,6 +347,30 @@ void SCustomInputPanel::Construct(const FArguments& InArgs) AddPreset(TEXT("启动投影"), TEXT(""), EPresetType::EnableProjector); AddPreset(TEXT("屏蔽投影"), TEXT(""), EPresetType::DisableProjector); + FCustomResourceCommands::Register(); + CommandList = MakeShareable(new FUICommandList); + CommandList->MapAction(FCustomResourceCommands::Get().Delete, FExecuteAction::CreateLambda([this]() + { + int32 i = 0; + for (FTimelinePropertyData& TimelinePropertyData : PropertyData) + { + if (TimelinePropertyData.Guid == SelectMenuProperty) + { + PropertyData.RemoveAt(i); + break; + } + i++; + } + for (TSharedPtr Resource : ResourceInst) + { + if (Resource->PropertyData.Guid == SelectMenuProperty) + { + GridPanel->RemoveSlot(Resource.ToSharedRef()); + break; + } + } + SelectMenuProperty.Invalidate(); + })); } @@ -361,6 +386,7 @@ FReply SCustomInputPanel::OnDrop(const FGeometry& MyGeometry, const FDragDropEve EffectGridPanel->AddSlot(EffectGridPanel->GetChildren()->Num() % 4, EffectGridPanel->GetChildren()->Num() / 4) [ SNew(SCustomInputResource) + .CustomInputPanel(this) .PropertyData(Data) .MainInterface(MainWidgetInterface) ]; @@ -537,6 +563,7 @@ void SCustomInputPanel::LoadPanel(const FString& LoadPlace) SNew(SCustomInputResource) .PropertyData(ReloadPropertyData) .MainInterface(MainWidgetInterface) + .CustomInputPanel(this) ]; PropertyData.Add(ReloadPropertyData); } @@ -569,5 +596,13 @@ void SCustomInputPanel::AddCustomPreset() } +void SCustomInputPanel::ShowMenu() +{ + FMenuBuilder MenuBuilder(true, CommandList); + MenuBuilder.AddMenuEntry(FCustomResourceCommands::Get().Delete); + TSharedPtr MenuContent = MenuBuilder.MakeWidget(); + FSlateApplication::Get().PushMenu(AsShared(), FWidgetPath(), MenuContent.ToSharedRef(), FSlateApplication::Get().GetCursorPos(), FPopupTransitionEffect::ContextMenu); +} + END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/Cut5/Widgets/SCustomInputPanel.h b/Source/Cut5/Widgets/SCustomInputPanel.h index 43874da..0197688 100644 --- a/Source/Cut5/Widgets/SCustomInputPanel.h +++ b/Source/Cut5/Widgets/SCustomInputPanel.h @@ -59,9 +59,15 @@ public: bool bIsAssetPanel = false; bool bIsEditMode = false; + TSharedPtr CommandList; + TSharedPtr EditButton; TSharedPtr ImportImage; TSharedPtr ImportText; TArray SelectedProperties; + FGuid SelectMenuProperty; + + void ShowMenu(); + }; diff --git a/Source/Cut5/Widgets/SCustomInputResource.cpp b/Source/Cut5/Widgets/SCustomInputResource.cpp index 2c550fa..c145e59 100644 --- a/Source/Cut5/Widgets/SCustomInputResource.cpp +++ b/Source/Cut5/Widgets/SCustomInputResource.cpp @@ -4,7 +4,9 @@ #include "SCustomInputResource.h" #include "DefineGlobal.h" +#include "SCustomInputPanel.h" #include "SlateOptMacros.h" +#include "Commands/CustomResourceCommands.h" #include "Cut5/Utils/Utils.h" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION @@ -15,6 +17,7 @@ void SCustomInputResource::Construct(const FArguments& InArgs) VideoCapture = InArgs._VideoCapture; OnCheckBoxChecked = InArgs._OnCheckBoxChecked; MainInterface = InArgs._MainInterface; + CustomInputPanel = InArgs._CustomInputPanel; ChildSlot [ SNew(SBox) @@ -59,7 +62,16 @@ void SCustomInputResource::Construct(const FArguments& InArgs) FReply SCustomInputResource::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - return FReply::Handled().DetectDrag(SharedThis(this), EKeys::LeftMouseButton); + if (MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton)) + { + return FReply::Handled().DetectDrag(SharedThis(this), EKeys::LeftMouseButton); + } + else + { + CustomInputPanel->SelectMenuProperty = PropertyData.Guid; + CustomInputPanel->ShowMenu(); + } + return FReply::Handled(); } FReply SCustomInputResource::OnDragDetected(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) diff --git a/Source/Cut5/Widgets/SCustomInputResource.h b/Source/Cut5/Widgets/SCustomInputResource.h index 854d18e..35929ce 100644 --- a/Source/Cut5/Widgets/SCustomInputResource.h +++ b/Source/Cut5/Widgets/SCustomInputResource.h @@ -20,10 +20,12 @@ public: SLATE_ARGUMENT(cv::VideoCapture, VideoCapture) SLATE_ARGUMENT(ICutMainWidgetInterface*, MainInterface) SLATE_EVENT(FOnCheckBoxChecked, OnCheckBoxChecked) + SLATE_ARGUMENT(class SCustomInputPanel*, CustomInputPanel) SLATE_END_ARGS() /** Constructs this widget with InArgs */ FTimelinePropertyData PropertyData; + SCustomInputPanel* CustomInputPanel; void Construct(const FArguments& InArgs); virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual FReply OnDragDetected(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; diff --git a/Source/Cut5/Widgets/SCutMainWindow.cpp b/Source/Cut5/Widgets/SCutMainWindow.cpp index c751188..67a1165 100644 --- a/Source/Cut5/Widgets/SCutMainWindow.cpp +++ b/Source/Cut5/Widgets/SCutMainWindow.cpp @@ -372,7 +372,14 @@ void SCutMainWindow::Construct(const FArguments& InArgs) FShortCutCommands::Register(); const FName ContextName = FShortCutCommands::Get().GetContextName(); CommandList = MakeShared(); - + CommandList->MapAction(FShortCutCommands::Get().MicroLeft, FExecuteAction::CreateLambda([this]() + { + CutTimeline->AddSelectedClipsOffset(-1); + }), EUIActionRepeatMode::RepeatEnabled); + CommandList->MapAction(FShortCutCommands::Get().MicroRight, FExecuteAction::CreateLambda([this]() + { + CutTimeline->AddSelectedClipsOffset(1); + }), EUIActionRepeatMode::RepeatEnabled); CommandList->MapAction(FShortCutCommands::Get().LeftPerFrame, FExecuteAction::CreateLambda([this]() { @@ -859,6 +866,7 @@ void SCutMainWindow::OpenProject(const FString& Project) CustomInputPanel->GridPanel->AddSlot(i % 2, i / 2) [ SNew(SCustomInputResource) + .CustomInputPanel(CustomInputPanel.Get()) .PropertyData(ReloadPropertyData) ]; CustomInputPanel->PropertyData.Add(ReloadPropertyData); @@ -926,16 +934,22 @@ void SCutMainWindow::ExportProject(const FString& ExportPath) if (ExportPath.IsEmpty()) return; FGlobalData::ExportPath = ExportPath / FGlobalData::CurrentProjectName; - + IDList.Empty(); FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*FGlobalData::ExportPath); - FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*(FGlobalData::ExportPath / "video")); - FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*(FGlobalData::ExportPath / "sound")); - FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*(FGlobalData::ExportPath / "psaf")); + FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*(FGlobalData::ExportPath / "Video")); + FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*(FGlobalData::ExportPath / "Sound")); + FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*(FGlobalData::ExportPath / "PSAF")); tinyxml2::XMLDocument Document; tinyxml2::XMLElement* RootElement = Document.NewElement("Project"); + RootElement->SetAttribute("Name", TCHAR_TO_UTF8(*FGlobalData::CurrentProjectName)); - Document.InsertFirstChild(RootElement); + + tinyxml2::XMLDeclaration* decl = Document.NewDeclaration("xml version=\"1.0\""); + tinyxml2::XMLNode* XMLVersion = Document.InsertFirstChild(decl); + Document.InsertAfterChild(XMLVersion, RootElement); + + tinyxml2::XMLElement* Standard = RootElement->InsertNewChildElement("Standard"); tinyxml2::XMLElement* File = Standard->InsertNewChildElement("File"); File->InsertNewChildElement("Author")->InsertNewText("Sch"); @@ -966,6 +980,7 @@ void SCutMainWindow::ExportProject(const FString& ExportPath) tinyxml2::XMLElement* PlayerLight = PlayerLightList->InsertNewChildElement("PlayerLight"); PlayerLight->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(DeviceID))); PlayerLight->InsertNewChildElement("RoleName")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceTrackGroup->GroupName)); + IDList.Add(TrackData.Guid, DeviceID); DeviceID++; } default: @@ -984,23 +999,27 @@ void SCutMainWindow::ExportProject(const FString& ExportPath) { tinyxml2::XMLElement* RotationSpeaker = RoundSpeakerList->InsertNewChildElement("RotationSpeaker"); RotationSpeaker->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(DeviceID))); - RotationSpeaker->InsertNewChildElement("RoleName")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); + RotationSpeaker->InsertNewChildElement("Name")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); DeviceID++; + IDList.Add(TrackData.Guid, DeviceID); + RotatorSpeakerIndex = DeviceID; } break; case ETrackType::LightArrayTrack: { tinyxml2::XMLElement* LightArray = LightArrayList->InsertNewChildElement("GuangZhen"); LightArray->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(DeviceID))); - LightArray->InsertNewChildElement("RoleName")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); + LightArray->InsertNewChildElement("Name")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); DeviceID++; + IDList.Add(TrackData.Guid, DeviceID); + LightArrayIndex = DeviceID; } break; case ETrackType::ProjectorTrack: { tinyxml2::XMLElement* Projector = ProjectorList->InsertNewChildElement("Projector"); Projector->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(DeviceID))); - Projector->InsertNewChildElement("RoleName")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); + Projector->InsertNewChildElement("Name")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); DeviceID++; } break; @@ -1008,8 +1027,17 @@ void SCutMainWindow::ExportProject(const FString& ExportPath) { tinyxml2::XMLElement* DMLight = DMLightList->InsertNewChildElement("DMLight"); DMLight->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(DeviceID))); - DMLight->InsertNewChildElement("RoleName")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); + DMLight->InsertNewChildElement("Name")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); DeviceID++; + IDList.Add(TrackData.Guid, DeviceID); + } + case ETrackType::LightBarTrack: + { + tinyxml2::XMLElement* LightArray = LightArrayList->InsertNewChildElement("GuangZhen"); + LightArray->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(DeviceID))); + LightArray->InsertNewChildElement("Name")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); + DeviceID++; + IDList.Add(TrackData.Guid, DeviceID); } default: break; @@ -1018,7 +1046,9 @@ void SCutMainWindow::ExportProject(const FString& ExportPath) } - tinyxml2::XMLElement* CardList = RootElement->InsertNewChildElement("CardList"); + int32 CardType = 0; + + tinyxml2::XMLElement* CardList = DeviceList->InsertNewChildElement("CardList"); for (int32 i = 0; i < EffectCardsPanel->EffectCardGroups.Num(); i ++) { if (EffectCardsPanel->EffectCardGroups[i].bIsDedicated) @@ -1027,35 +1057,88 @@ void SCutMainWindow::ExportProject(const FString& ExportPath) { tinyxml2::XMLElement* Card = CardList->InsertNewChildElement("Card"); Card->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].Cards[j].ID))); - Card->InsertNewChildElement("Type")->InsertNewText(EffectCardsPanel->EffectCardGroups[i].bIsDedicated ? "0" : "2"); - Card->InsertNewChildElement("Times"); - Card->InsertNewChildElement("Step"); + int32 TypeID = 0; + if (EffectCardsPanel->EffectCardGroups[i].Cards[j].JumpStepCurtains.Guid.IsValid()) + { + TypeID = 1; + } + Card->InsertNewChildElement("Type")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(TypeID))); + Card->InsertNewChildElement("Times")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(-1))); + + int32 ToStepID = 0; + if (TypeID == 1) + { + for (int32 k = 0; i < CurtainPanel->Groups.Num(); k++) + { + for (FCurtain& Curtain : CurtainPanel->Groups[k].Curtains) + { + if (Curtain.CurtainUUID == EffectCardsPanel->EffectCardGroups[i].Cards[j].JumpStepCurtains.Guid) + { + ToStepID = Curtain.Step + 1; + break; + } + } + } + } + + Card->InsertNewChildElement("Step")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(ToStepID))); Card->InsertNewChildElement("SpecialEffectID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].Cards[j].ID))); - Card->InsertNewChildElement("SerialNumberList"); + Card->InsertNewChildElement("SerialNumberList")->InsertNewChildElement("SerialNumber")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].Cards[j].ID))); } } else { - for (int32 j = 0; j < EffectCardsPanel->EffectCardGroups[i].Cards.Num(); j++) + tinyxml2::XMLElement* Card = CardList->InsertNewChildElement("Card"); + + + Card->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].ID))); + + int32 TypeID = 2; + if (EffectCardsPanel->EffectCardGroups[i].JumpStepCurtains.Guid.IsValid()) { - tinyxml2::XMLElement* Card = CardList->InsertNewChildElement("Card"); - Card->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].ID))); - Card->InsertNewChildElement("Type")->InsertNewText(EffectCardsPanel->EffectCardGroups[0].bIsDedicated ? "0" : "2"); - Card->InsertNewChildElement("Times"); - Card->InsertNewChildElement("Step"); - Card->InsertNewChildElement("SpecialEffectID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].ID))); - tinyxml2::XMLElement* SerialNumberList = Card->InsertNewChildElement("SerialNumberList"); - for (int32 k = 0; k < EffectCardsPanel->EffectCardGroups[i].Cards.Num(); k++) + TypeID = 3; + } + + Card->InsertNewChildElement("Type")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(TypeID))); + Card->InsertNewChildElement("Times")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(-1))); + + + int32 ToStepID = 0; + if (TypeID == 3) + { + for (int32 k = 0; i < CurtainPanel->Groups.Num(); k++) { - SerialNumberList->InsertNewChildElement("SerialNumber")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].Cards[k].ID))); + for (FCurtain& Curtain : CurtainPanel->Groups[k].Curtains) + { + if (Curtain.CurtainUUID == EffectCardsPanel->EffectCardGroups[i].JumpStepCurtains.Guid) + { + ToStepID = Curtain.Step + 1; + break; + } + } } } + Card->InsertNewChildElement("Step")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(ToStepID))); + Card->InsertNewChildElement("SpecialEffectID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].ID))); + tinyxml2::XMLElement* SerialNumberList = Card->InsertNewChildElement("SerialNumberList"); + for (int32 k = 0; k < EffectCardsPanel->EffectCardGroups[i].Cards.Num(); k++) + { + SerialNumberList->InsertNewChildElement("SerialNumber")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].Cards[k].ID))); + } } } // KeyBoard tinyxml2::XMLElement* Keyboard = RootElement->InsertNewChildElement("KeyBoard"); + { + tinyxml2::XMLElement* KeyCode = Keyboard->InsertNewChildElement("KeyCode"); + KeyCode->InsertNewChildElement("CTRL")->InsertNewText(""); + KeyCode->InsertNewChildElement("SHIFT")->InsertNewText(""); + KeyCode->InsertNewChildElement("ALT")->InsertNewText(""); + KeyCode->InsertNewChildElement("KEY")->InsertNewText(""); + KeyCode->InsertNewChildElement("SpecialEffectID")->InsertNewText(""); + } } // ProcessList tinyxml2::XMLElement* ProcessList = RootElement->InsertNewChildElement("ProcessList"); @@ -1291,7 +1374,7 @@ void SCutMainWindow::AddNewCustomPreset(const FString& Name, const FPresetsCusto NewPropertyData.PresetsCustomData = CustomData; NewPropertyData.Name = Name; - TSharedPtr CustomInputResource = SNew(SCustomInputResource).PropertyData(NewPropertyData); + TSharedPtr CustomInputResource = SNew(SCustomInputResource).PropertyData(NewPropertyData).CustomInputPanel(CustomInputPanel.Get()); CustomInputPanel->GridPanel->AddSlot(CustomInputPanel->GridPanel->GetChildren()->Num() % 2, CustomInputPanel->GridPanel->GetChildren()->Num() / 2) [ CustomInputResource.ToSharedRef() @@ -1342,21 +1425,35 @@ tinyxml2::XMLElement* SCutMainWindow::GetDeviceElement(tinyxml2::XMLElement* Par // DMLightList tinyxml2::XMLElement* DMLightList = Light->InsertNewChildElement("DMLightList"); { + int32 j = 0; for (int32 i = 0; i < CutTimeline->TrackGroupInstances.Num(); i++) { const FTrackData& TrackData = StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData; if (TrackData.TrackType == ETrackType::SpotLightTrack) { - tinyxml2::XMLElement* Event_List = DMLightList->InsertNewChildElement(TCHAR_TO_UTF8(*FString::Printf(TEXT("DMLight(%d)"), j)))->InsertNewChildElement("Event_List"); + + tinyxml2::XMLElement* DMLight = DMLightList->InsertNewChildElement(TCHAR_TO_UTF8(*FString::Printf(TEXT("DMLight")))); + DMLight->SetAttribute("ID", TCHAR_TO_UTF8(*FString::FromInt(GetTrackID(TrackData.DeviceTrack.Guid)))); + + tinyxml2::XMLElement* Event_List = DMLight->InsertNewChildElement("Event_List"); + int32 Count = 0; for (int32 k = 0; k < TrackData.ClipData.Num(); k++) { const FClipData& TempClipData = TrackData.ClipData[k]; - Event_List->InsertNewChildElement("Value")->InsertNewText("1"); - Event_List->InsertNewChildElement("Timecode")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::GetMsFromString(FGlobalData::GetTimeData(TempClipData.ClipStartFrame))))); - Event_List->InsertNewChildElement("Value")->InsertNewText("0"); - Event_List->InsertNewChildElement("Timecode")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::GetMsFromString(FGlobalData::GetTimeData(TempClipData.ClipEndFrame))))); + tinyxml2::XMLElement* Event1 = Event_List->InsertNewChildElement("Event"); + Event1->InsertNewChildElement("Value")->InsertNewText("1"); + Event1->InsertNewChildElement("Timecode")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::GetMsFromString(FGlobalData::GetTimeData(TempClipData.ClipStartFrame))))); + + tinyxml2::XMLElement* Event2 = Event_List->InsertNewChildElement("Event"); + Event2->InsertNewChildElement("Value")->InsertNewText("0"); + Event2->InsertNewChildElement("Timecode")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::GetMsFromString(FGlobalData::GetTimeData(TempClipData.ClipEndFrame))))); + Count++; + } + if (Count == 0) + { + Event_List->InsertNewText(""); } } } @@ -1369,8 +1466,14 @@ tinyxml2::XMLElement* SCutMainWindow::GetDeviceElement(tinyxml2::XMLElement* Par const FTrackData& TrackData = StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData; if (TrackData.TrackType == ETrackType::AtomSphereLightTrack) { - tinyxml2::XMLElement* PlayerLight = PlayerLightList->InsertNewChildElement(TCHAR_TO_UTF8(*FString::Printf(TEXT("PlayerLight(%d)"), j))); + tinyxml2::XMLElement* PlayerLight = PlayerLightList->InsertNewChildElement(TCHAR_TO_UTF8(*FString::Printf(TEXT("PlayerLight")))); + PlayerLight->SetAttribute("ID", TCHAR_TO_UTF8(*FString::FromInt(GetTrackID(TrackData.DeviceTrack.Guid)))); + tinyxml2::XMLElement* SpeicalEffect = PlayerLight->InsertNewChildElement("Special_Effects_List"); + if (TrackData.ClipData.Num() == 0) + { + SpeicalEffect->InsertNewText(""); + } for (int32 k = 0; k < TrackData.ClipData.Num(); k++) { @@ -1406,48 +1509,67 @@ tinyxml2::XMLElement* SCutMainWindow::GetDeviceElement(tinyxml2::XMLElement* Par } tinyxml2::XMLElement* GuangZhenList = Light->InsertNewChildElement("GuangZhenList"); { - auto GuangZhenSpecialEffectList = GuangZhenList->InsertNewChildElement("SpecialEffectList"); - for (int32 i = 0; i < CutTimeline->TrackGroupInstances.Num(); i++) + + for (int32 k = 0; k < CutTimeline->TrackGroupInstances.Num(); k++) { - if (StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData.TrackType == ETrackType::LightArrayTrack) + int32 Index = 0; + const FTrackData& TrackData = StaticCastSharedPtr(CutTimeline->TrackGroupInstances[k].Head)->TrackData; + if (TrackData.TrackType == ETrackType::LightArrayTrack || TrackData.TrackType == ETrackType::LightBarTrack) { - TArray EncodeVideoInfos = FUtils::ExportPsaf(StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData, *(FGlobalData::ExportPath / "psaf")); - for (int32 j = 0; j < EncodeVideoInfos.Num(); j++) + auto GuangZhen = GuangZhenList->InsertNewChildElement("GuangZhen"); + GuangZhen->SetAttribute("ID", TCHAR_TO_UTF8(*FString::FromInt(GetTrackID(TrackData.DeviceTrack.Guid)))); + + int32 Count = 0; + auto GuangZhenSpecialEffectList = GuangZhen->InsertNewChildElement("SpecialEffectList"); + for (int32 i = 0; i < CutTimeline->TrackGroupInstances.Num(); i++) { - auto SpeicalEffect = GuangZhenSpecialEffectList->InsertNewChildElement("SpeicalEffect"); + if (StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData.TrackType == ETrackType::LightArrayTrack) { - auto SpeicalEffectTimeCode = SpeicalEffect->InsertNewChildElement("TimeCode"); + TArray EncodeVideoInfos = FUtils::ExportPsaf(StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData, *(FGlobalData::ExportPath / "PSAF")); + for (int32 j = 0; j < EncodeVideoInfos.Num(); j++) { - SpeicalEffectTimeCode->InsertNewText(TCHAR_TO_UTF8(*FUtils::GetMsFromString(EncodeVideoInfos[j].EncodedVideoTimeCode))); - } - auto SpeicalEffectURL = SpeicalEffect->InsertNewChildElement("URL"); - { - SpeicalEffectURL->InsertNewText(TCHAR_TO_UTF8(*EncodeVideoInfos[j].EncodedVideoName)); - } - auto SpeicalEffectLoop = SpeicalEffect->InsertNewChildElement("Loop"); - { - SpeicalEffectLoop->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); - } - auto SpeicalEffectMode = SpeicalEffect->InsertNewChildElement("Mode"); - { - SpeicalEffectMode->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); + auto SpeicalEffect = GuangZhenSpecialEffectList->InsertNewChildElement("SpeicalEffect"); + { + auto SpeicalEffectTimeCode = SpeicalEffect->InsertNewChildElement("TimeCode"); + { + SpeicalEffectTimeCode->InsertNewText(TCHAR_TO_UTF8(*FUtils::GetMsFromString(EncodeVideoInfos[j].EncodedVideoTimeCode))); + } + auto SpeicalEffectURL = SpeicalEffect->InsertNewChildElement("URL"); + { + SpeicalEffectURL->InsertNewText(TCHAR_TO_UTF8(*EncodeVideoInfos[j].EncodedVideoName)); + } + auto SpeicalEffectLoop = SpeicalEffect->InsertNewChildElement("Loop"); + { + SpeicalEffectLoop->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); + } + auto SpeicalEffectMode = SpeicalEffect->InsertNewChildElement("Mode"); + { + SpeicalEffectMode->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); + } + } + Count++; } } } + if (Count == 0) + { + GuangZhenSpecialEffectList->InsertNewText(""); + } + Index++; } } } tinyxml2::XMLElement* RoomLight = Light->InsertNewChildElement("RoomLight"); { - + RoomLight->InsertNewText("0"); } tinyxml2::XMLElement* JLight = Light->InsertNewChildElement("JLight"); { - + JLight->InsertNewText("0"); } tinyxml2::XMLElement* Incense_Machine = Light->InsertNewChildElement("Incense_Machine"); { - + Incense_Machine->InsertNewText("0"); } } } @@ -1479,7 +1601,29 @@ tinyxml2::XMLElement* SCutMainWindow::GetVideoElement(tinyxml2::XMLElement* Pare { ProjectorID->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(TempProjectorID))); } + + tinyxml2::XMLElement* VolumeEventList = Video->InsertNewChildElement("VolumeEventList"); + { + tinyxml2::XMLElement* VolumeEvent = VolumeEventList->InsertNewChildElement("VolumeEvent"); + { + tinyxml2::XMLElement* VolumeEventTimeCode = VolumeEvent->InsertNewChildElement("TimeCode"); + tinyxml2::XMLElement* VolumeEventValue = VolumeEvent->InsertNewChildElement("Value"); + if (EncodeVideoInfo.TrackData.IsMute) + { + VolumeEventTimeCode->InsertNewText("0"); + VolumeEventValue->InsertNewText("0"); + } + else + { + // TODO: Dynamic Volume + VolumeEventTimeCode->InsertNewText("0"); + VolumeEventValue->InsertNewText("100"); + } + + + } + } tinyxml2::XMLElement* ProjectorEventList = Video->InsertNewChildElement("ProjectorEventList"); { @@ -1519,6 +1663,7 @@ tinyxml2::XMLElement* SCutMainWindow::GetVideoElement(tinyxml2::XMLElement* Pare Value->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(ShowProjector))); } } + } } @@ -1534,7 +1679,9 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundElement(tinyxml2::XMLElement* Pare { tinyxml2::XMLElement* Sound = Parent->InsertNewChildElement("Sound"); { + Sound->InsertNewChildElement("RotationSpeakerID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(RotatorSpeakerIndex))); Sound->InsertNewChildElement("URL")->InsertNewText(TCHAR_TO_UTF8(*(FPaths::GetBaseFilename(EncodeVideoInfo.EncodedVideoName) + TEXT(".mp3")))); + Sound->InsertNewChildElement("Loop")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); Sound->InsertNewChildElement("Mode")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); Sound->InsertNewChildElement("Round")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); @@ -1545,6 +1692,30 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundElement(tinyxml2::XMLElement* Pare { tinyxml2::XMLElement* VolumeEventTimeCode = VolumeEvent->InsertNewChildElement("TimeCode"); tinyxml2::XMLElement* VolumeEventValue = VolumeEvent->InsertNewChildElement("Value"); + + if (EncodeVideoInfo.TrackData.IsMute) + { + VolumeEventTimeCode->InsertNewText("0"); + VolumeEventValue->InsertNewText("0"); + } + else + { + // TODO: Dynamic Volume + VolumeEventTimeCode->InsertNewText("0"); + VolumeEventValue->InsertNewText("100"); + } + } + } + tinyxml2::XMLElement* RotationSpeakerEventList = Sound->InsertNewChildElement("RotationSpeakerEventList"); + { + tinyxml2::XMLElement* RotationSpeakerEvent = RotationSpeakerEventList->InsertNewChildElement("RotationSpeakerEvent"); + { + tinyxml2::XMLElement* RotationSpeakerEventTimeCode = RotationSpeakerEvent->InsertNewChildElement("TimeCode"); + tinyxml2::XMLElement* RotationSpeakerEventValue = RotationSpeakerEvent->InsertNewChildElement("Value"); + + + RotationSpeakerEventTimeCode->InsertNewText("0"); + RotationSpeakerEventValue->InsertNewText("0"); } } } @@ -1554,20 +1725,26 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundElement(tinyxml2::XMLElement* Pare tinyxml2::XMLElement* SCutMainWindow::GetVideoListElement(tinyxml2::XMLElement* Parent) { tinyxml2::XMLElement* VideoList = Parent->InsertNewChildElement("VideoList"); + int32 Count = 0; for (int32 i = 0; i < CutTimeline->TrackGroupInstances.Num(); i++) { if (StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData.TrackType == ETrackType::VideoTrack) { FString Filename = FGuid::NewGuid().ToString(); - FString NewExportFilePath = FGlobalData::ExportPath / "video" / Filename; + FString NewExportFilePath = FGlobalData::ExportPath / "Video" / Filename; TArray EncodeVideoInfos = FUtils::TrackEncodeVideo(StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData, NewExportFilePath); for (FEncodeVideoInfo EncodeVideoInfo : EncodeVideoInfos) { GetVideoElement(VideoList, EncodeVideoInfo); + Count++; } } } + if (Count == 0) + { + VideoList->InsertNewText(""); + } return VideoList; } @@ -1575,20 +1752,27 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundListElement(tinyxml2::XMLElement* { tinyxml2::XMLElement* AudioList = Parent->InsertNewChildElement("SoundList"); { + int32 Count = 0; for (int32 i = 0; i < CutTimeline->TrackGroupInstances.Num(); i++) { if (StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData.TrackType == ETrackType::AudioTrack || StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData.TrackType == ETrackType::AudioTrackR) { FString Filename = FGuid::NewGuid().ToString(); - FString NewExportFilePath = FGlobalData::ExportPath / "sound" / Filename; + FString NewExportFilePath = FGlobalData::ExportPath / "Sound" / Filename; TArray EncodeVideoInfos = FUtils::TrackEncodeAudio(StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData, NewExportFilePath); for (FEncodeVideoInfo EncodeVideoInfo : EncodeVideoInfos) { GetSoundElement(AudioList, EncodeVideoInfo); + Count++; } + } } + if (Count == 0) + { + AudioList->InsertNewText(""); + } } return nullptr; } @@ -1609,9 +1793,9 @@ tinyxml2::XMLElement* SCutMainWindow::GetProcessB(tinyxml2::XMLElement* Parent, { tinyxml2::XMLElement* ProcessB = Parent->InsertNewChildElement("ProcessB"); ProcessB->SetAttribute("Name", TCHAR_TO_UTF8(*Curtain->CurtainName)); - tinyxml2::XMLElement* ProcessID = ProcessB->InsertNewChildElement("ID"); - tinyxml2::XMLElement* AutoNext = ProcessB->InsertNewChildElement("AutoNext"); - tinyxml2::XMLElement* TimeLength = ProcessB->InsertNewChildElement("TimeLength"); + ProcessB->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(Curtain->Step))); + ProcessB->InsertNewChildElement("AutoNext")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); + ProcessB->InsertNewChildElement("TimeLength")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(-1))); GetSoundListElement(ProcessB); ProcessB->InsertEndChild(GetDeviceElement(ProcessB)); @@ -1620,15 +1804,49 @@ tinyxml2::XMLElement* SCutMainWindow::GetProcessB(tinyxml2::XMLElement* Parent, // 非必须项 tinyxml2::XMLElement* Identity_SpecialEffects = ProcessB->InsertNewChildElement("Identity_SpecialEffects"); { - + Identity_SpecialEffects->InsertNewText(""); } tinyxml2::XMLElement* IsGlobal = ProcessB->InsertNewChildElement("IsGlobal"); { - + IsGlobal->InsertNewText("1"); } tinyxml2::XMLElement* State = ProcessB->InsertNewChildElement("State"); { - + State->InsertNewText(""); + } + tinyxml2::XMLElement* EnableCard = ProcessB->InsertNewChildElement("EnableCard"); + { + int32 Count = 0; + for (int32 i = 0; i < EffectCardsPanel->EffectCardGroups.Num(); i++) + { + + if (!EffectCardsPanel->EffectCardGroups[i].bIsDedicated) + { + if (EffectCardsPanel->EffectCardGroups[i].UsedCurtains.Contains(Curtain->CurtainUUID)) + { + tinyxml2::XMLElement* SerialNumber = EnableCard->InsertNewChildElement("SerialNumber"); + SerialNumber->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].ID))); + Count++; + } + continue; + } + + for (int32 j = 0 ; j < EffectCardsPanel->EffectCardGroups[i].Cards.Num(); j++) + { + if (EffectCardsPanel->EffectCardGroups[i].Cards[j].UsedCurtains.Contains(Curtain->CurtainUUID)) + { + tinyxml2::XMLElement* SerialNumber = EnableCard->InsertNewChildElement("SerialNumber"); + SerialNumber->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(EffectCardsPanel->EffectCardGroups[i].Cards[j].ID))); + Count++; + } + } + + } + if (Count == 0) + { + EnableCard->InsertNewText(""); + } + } return ProcessB; } @@ -1641,12 +1859,8 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffectList(tinyxml2::XMLElement* { if (!EffectCardsPanel->EffectCardGroups[i].bIsDedicated) { - if (EffectCardsPanel->EffectCardGroups[i].bIsDedicated == false) - { - const FString Name = FPaths::Combine(FGlobalData::BasePath, FGlobalData::CurrentProjectName, TEXT("FX"), EffectCardsPanel->EffectCardGroups[i].GroupName + TEXT(".bin")); - OpenTimeline(Name, true, true); - CurrentSelectedPropertiesInterfaceGuid = EffectCardsPanel->EffectCardGroups[i].Guid; - } + OpenTimeline(FUtils::GroupFullPath(EffectCardsPanel->EffectCardGroups[i].Guid.ToString()), true, true); + CurrentSelectedPropertiesInterfaceGuid = EffectCardsPanel->EffectCardGroups[i].Guid; GetSpecialEffectGroup(SpecialEffectsList, &EffectCardsPanel->EffectCardGroups[i]); SpecialEffectID++; continue; @@ -1661,6 +1875,11 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffectList(tinyxml2::XMLElement* SpecialEffectID++; } + + if (SpecialEffectID == 0) + { + SpecialEffectsList->InsertNewText(""); + } return SpecialEffectsList; } @@ -1675,6 +1894,7 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffectGroup(tinyxml2::XMLElement ID->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(Group->ID))); } tinyxml2::XMLElement* AutoNext = Effect->InsertNewChildElement("AutoNext"); + AutoNext->InsertNewText("0"); GetSoundListElement(Effect); GetDeviceElement(Effect); GetVideoListElement(Effect); @@ -1686,7 +1906,7 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffectGroup(tinyxml2::XMLElement } tinyxml2::XMLElement* State = Effect->InsertNewChildElement("State"); { - + State->InsertNewText("0"); } return Effect; } @@ -1701,25 +1921,9 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffect(tinyxml2::XMLElement* Par { ID->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(Effect->ID))); } - - int32 CardType = 0; - if (Effect->JumpStepCurtains.Guid.IsValid()) - { - CardType = 1; - } - - Effectxml->InsertNewChildElement("Type")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(CardType))); - Effectxml->InsertNewChildElement("Times")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(-1))); - int32 Step = 0; - - for (int32 i = 0; i < CurtainPanel->Groups.Num(); i++) - { - - } - Effectxml->InsertNewChildElement("Step")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(Step))); - tinyxml2::XMLElement* AutoNext = Effectxml->InsertNewChildElement("AutoNext"); + AutoNext->InsertNewText("0"); GetSoundListElement(Effectxml); GetDeviceElement(Effectxml); GetVideoListElement(Effectxml); @@ -1731,10 +1935,17 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffect(tinyxml2::XMLElement* Par } tinyxml2::XMLElement* State = Effectxml->InsertNewChildElement("State"); { - + State->InsertNewText("0"); } return Effectxml; } +int32 SCutMainWindow::GetTrackID(FGuid Guid) const +{ + const int32* Index = IDList.Find(Guid); + const int32 NewIndex = Index != nullptr ? *Index : -1; + return NewIndex; +} + END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/Cut5/Widgets/SCutMainWindow.h b/Source/Cut5/Widgets/SCutMainWindow.h index c72c409..61b8629 100644 --- a/Source/Cut5/Widgets/SCutMainWindow.h +++ b/Source/Cut5/Widgets/SCutMainWindow.h @@ -108,6 +108,12 @@ public: tinyxml2::XMLElement* GetSpecialEffectList(tinyxml2::XMLElement* Parent); tinyxml2::XMLElement* GetSpecialEffectGroup(tinyxml2::XMLElement* Parent, FEffectCardGroup* Group); tinyxml2::XMLElement* GetSpecialEffect(tinyxml2::XMLElement* Parent, FEffectCardProperty* Effect); + + int32 RotatorSpeakerIndex = 0; + int32 LightArrayIndex = 0; + + TMap IDList = {}; + int32 GetTrackID(FGuid Guid) const; }; diff --git a/Source/Cut5/Widgets/SCutTimeline.cpp b/Source/Cut5/Widgets/SCutTimeline.cpp index 3e41720..7873ed0 100644 --- a/Source/Cut5/Widgets/SCutTimeline.cpp +++ b/Source/Cut5/Widgets/SCutTimeline.cpp @@ -188,7 +188,7 @@ void SCutTimeline::Construct(const FArguments& InArgs) .OnValueChanged_Lambda([this](float ChangedValue) { FGlobalData::DefaultTimeTickSpace = FMath::GetMappedRangeValueClamped(FVector2D(0, 1.0), FVector2D(GetCachedGeometry().GetLocalSize().X / FGlobalData::TrackLength, 14), ChangedValue); - + UpdateCursorPosition(GetCursorPosition()); RenderGroup(); }) ] @@ -218,7 +218,7 @@ void SCutTimeline::Construct(const FArguments& InArgs) [ SNew(SBox) .WidthOverride(10000) - .HeightOverride(25) + .HeightOverride(40) // 刻度补差值 ] + SVerticalBox::Slot() @@ -242,7 +242,7 @@ void SCutTimeline::Construct(const FArguments& InArgs) [ SNew(SBox) .WidthOverride(10000) - .HeightOverride(25) + .HeightOverride(40) // 刻度补差值 ] + SVerticalBox::Slot() @@ -266,7 +266,7 @@ void SCutTimeline::Construct(const FArguments& InArgs) [ SAssignNew(TimelineTickBox, SBox) .WidthOverride(FGlobalData::TrackLength * FGlobalData::DefaultTimeTickSpace) - .MaxDesiredHeight(60) + .HeightOverride(40) [ SAssignNew(TickScrollBox, SScrollBox) .ScrollBarVisibility(EVisibility::Hidden) @@ -790,6 +790,69 @@ void SCutTimeline::AddNewDeviceToGroup(FString GroupName, FDeviceTrack DeviceTra { MainWidgetInterface->OnAddNewTrack(ETrackType::PlayerTrack); } +} + +void SCutTimeline::AddSelectedClipsOffset(int32 FrameOffset) +{ + TArray NewSelectedClipData; + for (int32 i = 0; i < TrackGroupInstances.Num(); i++) + { + for (FClipData& TempClipData : StaticCastSharedPtr(TrackGroupInstances[i].Head)->TrackData.ClipData) + { + if (SelectedClips.Contains(TempClipData.ClipGuid)) + { + NewSelectedClipData.Add(&TempClipData); + } + } + } + + int32 MinFrame = FGlobalData::TrackLength; + for (FClipData* ClipData : NewSelectedClipData) + { + if (ClipData->ClipStartFrame < MinFrame) + { + MinFrame = ClipData->ClipStartFrame; + } + } + + int32 MaxFrame = 0; + for (FClipData* ClipData : NewSelectedClipData) + { + if (ClipData->ClipEndFrame > MaxFrame) + { + MaxFrame = ClipData->ClipEndFrame; + } + } + + if (FrameOffset > 0) + { + if (MinFrame >= 0 && MaxFrame < FGlobalData::TrackLength) + { + for (FClipData* ClipData : NewSelectedClipData) + { + ClipData->ClipStartFrame += FrameOffset; + ClipData->ClipEndFrame += FrameOffset; + } + RenderGroup(); + } + } + else if (FrameOffset < 0) + { + if (MinFrame > 0 && MaxFrame < FGlobalData::TrackLength) + { + for (FClipData* ClipData : NewSelectedClipData) + { + ClipData->ClipStartFrame += FrameOffset; + ClipData->ClipEndFrame += FrameOffset; + } + RenderGroup(); + } + } + + + + + } END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/Cut5/Widgets/SCutTimeline.h b/Source/Cut5/Widgets/SCutTimeline.h index 31d185d..41806fb 100644 --- a/Source/Cut5/Widgets/SCutTimeline.h +++ b/Source/Cut5/Widgets/SCutTimeline.h @@ -134,6 +134,9 @@ public: TArray RenderBoxPos {{}, {}}; bool bNeedShowPanel = false; + + + void AddSelectedClipsOffset(int32 FrameOffset); }; diff --git a/Source/Cut5/Widgets/STimelineClip.cpp b/Source/Cut5/Widgets/STimelineClip.cpp index 64c4057..57c01e0 100644 --- a/Source/Cut5/Widgets/STimelineClip.cpp +++ b/Source/Cut5/Widgets/STimelineClip.cpp @@ -19,9 +19,12 @@ #include "DragDropOperator/DragDropOperator.h" #include "Engine/Engine.h" #include "Engine/Texture2D.h" +#include "MicroWidgets/SNewProjectTips.h" #include "Presets/SClipCursor.h" #include "Rendering/DrawElementPayloads.h" #include "Slate/Private/Framework/Application/Menu.h" +#include "Widgets/Input/SSpinBox.h" +#include "Widgets/Layout/SSpacer.h" extern "C" { @@ -49,7 +52,7 @@ FReply STimelineClip::OnBorderMouseButtonDown(const FGeometry& Geometry, const F return FReply::Handled(); } - + if (MainWidgetInterface->GetSelectedMode() == ESelectMode::CutMode) { Body->SelectedClipFrame = (Geometry.AbsoluteToLocal(PointerEvent.GetScreenSpacePosition()).X) / FGlobalData::DefaultTimeTickSpace; @@ -58,11 +61,15 @@ FReply STimelineClip::OnBorderMouseButtonDown(const FGeometry& Geometry, const F } + MainWidgetInterface->GetCutTimeline()->SelectedClips.AddUnique(ClipData->ClipGuid); + MainWidgetInterface->UpdateProperties(nullptr); + MainWidgetInterface->UpdateProperties(this); + LocalPos = Geometry.AbsoluteToLocal(PointerEvent.GetScreenSpacePosition()); const float DragOffset = MainWidgetInterface->GetCutTimeline()->GetCachedGeometry().AbsoluteToLocal(PointerEvent.GetScreenSpacePosition()).X; if (LocalPos.X <= 10) { - if (ClipData->bCanDrag && !MainWidgetInterface->GetCutTimeline()->SelectedClips.Contains(ClipData->ClipGuid)) + if (ClipData->bCanDrag && (!MainWidgetInterface->GetCutTimeline()->SelectedClips.Contains(ClipData->ClipGuid) || MainWidgetInterface->GetCutTimeline()->SelectedClips.Num() == 1)) { const TSharedPtr Clip2ClipDragDropOperation = MakeShared(); Clip2ClipDragDropOperation->TrackBody = StaticCastSharedPtr(Body); @@ -77,7 +84,7 @@ FReply STimelineClip::OnBorderMouseButtonDown(const FGeometry& Geometry, const F } if (LocalPos.X >= Geometry.GetLocalSize().X - 10) { - if (ClipData->bCanDrag && !MainWidgetInterface->GetCutTimeline()->SelectedClips.Contains(ClipData->ClipGuid)) + if (ClipData->bCanDrag && (!MainWidgetInterface->GetCutTimeline()->SelectedClips.Contains(ClipData->ClipGuid) || MainWidgetInterface->GetCutTimeline()->SelectedClips.Num() == 1)) { const TSharedPtr Clip2ClipDragDropOperation = MakeShared(); Clip2ClipDragDropOperation->TrackBody = StaticCastSharedPtr(Body); @@ -679,6 +686,10 @@ void STimelineClip::UpdateMove(int32 X, int32 DragOffset) FReply STimelineClip::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); + + TSharedPtr NewBody = StaticCastSharedPtr(Body); + + bNeedPaintDrag = false; if (LocalPos.X <= 10) { @@ -693,9 +704,14 @@ FReply STimelineClip::OnMouseMove(const FGeometry& MyGeometry, const FPointerEve if (MainWidgetInterface->GetSelectedMode() == ESelectMode::CutMode) { + float Pos = NewBody->GetCachedGeometry().AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()).X / FGlobalData::DefaultTimeTickSpace; + MainWidgetInterface->GetCutTimeline()->CurrentTimeData->SetText(FText::FromString(FGlobalData::GetTimeData(Pos) + " / ")); bNeedPaintDrag = true; PaintDragType = 2; } + + + return FReply::Handled(); } @@ -863,6 +879,9 @@ FReply STimelineClip::OnDragOver(const FGeometry& MyGeometry, const FDragDropEve void STimelineClip::DoSound(ESoundSolveType SolveType, int32 InFrame) { + TSharedPtr NewBody = StaticCastSharedPtr(Body); + if (NewBody->TrackHead->TrackData.IsMute) + return; if (SoundThread == nullptr) { if (ClipData->ResourcePropertyDataPtr == nullptr) @@ -982,5 +1001,256 @@ void STimelineClip::Tick(const FGeometry& AllottedGeometry, const double InCurre } } +TSharedPtr STimelineClip::GetPropertiesWidget() +{ + Selectable.Empty(); + Selectable.Add(MakeShared(TEXT("无"))); + Selectable.Add(MakeShared(TEXT("呼吸"))); + Selectable.Add(MakeShared(TEXT("闪烁"))); + Selectable.Add(MakeShared(TEXT("渐变"))); + + + FTextBlockStyle NormalText = FAppStyle::GetWidgetStyle("NormalText"); + NormalText.SetFontSize(13); + PropertiesWidget = + SNew(SVerticalBox) + + SVerticalBox::Slot() + .Padding(0, 13, 0, 0) + [ + SNew(SBox).HeightOverride(32).WidthOverride(214) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + .VAlign(VAlign_Center) + [ + SNew(SBox) + .WidthOverride(62) + .HeightOverride(32) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("颜色"))) + .Font(NormalText.Font) + .Justification(ETextJustify::Center) + ] + ] + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + [ + SNew(SBox) + .WidthOverride(136) + .HeightOverride(32) + [ + SNew(SImage) + .Image(FUtils::GetBrushFromImage(FUtils::GetResourcesPath("Color.png"), {})) + .ColorAndOpacity_Lambda([this]() + { + if (CustomData.Colors.Num() == 0) + { + return FLinearColor(1, 1, 1, 0.5); + } + return CustomData.Colors[0]; + }) + .OnMouseButtonDown_Lambda([this](const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) + { + MainWidgetInterface->OpenColorPanel(&CustomData.Colors[0]); + return FReply::Handled(); + }) + ] + ] + ] + ] + + SVerticalBox::Slot() + .Padding(0, 13, 0, 0) + [ + SNew(SBox).HeightOverride(32).WidthOverride(214) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + .VAlign(VAlign_Center) + [ + SNew(SBox) + .WidthOverride(62) + .HeightOverride(32) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("动效"))) + .Font(NormalText.Font) + .Justification(ETextJustify::Center) + ] + ] + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + [ + SNew(SBox) + .WidthOverride(136) + .HeightOverride(32) + [ + SNew(SComboBox>) + .OptionsSource(&Selectable) + .OnGenerateWidget_Lambda([this](TSharedPtr InItem) + { + return SNew(STextBlock).Text(FText::FromString(*InItem)); + }) + .OnSelectionChanged_Lambda([this](TSharedPtr InItem, ESelectInfo::Type SelectInfo) + { + switch (Selectable.Find(InItem)) + { + case 0: + CustomData.PresetCustomType = FPresetsCustomData::EPresetCustomType::None; + break; + case 1: + CustomData.PresetCustomType = FPresetsCustomData::EPresetCustomType::Breathe; + break; + case 2: + CustomData.PresetCustomType = FPresetsCustomData::EPresetCustomType::Flash; + break; + case 3: + CustomData.PresetCustomType = FPresetsCustomData::EPresetCustomType::Gradient; + default: + break; + } + }) + [ + SNew(STextBlock) + .Text_Lambda([this]() + { + return FText::FromString( CustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::None ? TEXT("无") : CustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::Breathe ? TEXT("呼吸") : TEXT("闪烁")); + }) + ] + ] + ] + ] + ] + + SVerticalBox::Slot() + .Padding(0, 13, 0, 0) + [ + SNew(SBox).HeightOverride(32).WidthOverride(214) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + .VAlign(VAlign_Center) + [ + SNew(SBox) + .WidthOverride(62) + .HeightOverride(32) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("次数"))) + .Font(NormalText.Font) + .Justification(ETextJustify::Center) + ] + ] + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + [ + SNew(SBox) + .WidthOverride(136) + .HeightOverride(32) + [ + SNew(SSpinBox) + .Value(1) + .MinValue(1) + .OnValueChanged_Lambda([this](const int32& Value) + { + CustomData.Times = Value; + }) + ] + ] + ] + ] + + SVerticalBox::Slot() + .Padding(0, 13, 0, 0) + [ + SNew(SBox).HeightOverride(32).WidthOverride(214) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + .VAlign(VAlign_Center) + [ + SNew(SBox) + .WidthOverride(62) + .HeightOverride(32) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("时间"))) + .Font(NormalText.Font) + .Justification(ETextJustify::Center) + ] + ] + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + [ + SNew(SBox) + .WidthOverride(136) + .HeightOverride(32) + [ + SNew(SSpinBox) + .Value(0.3) + .MinValue(0.3) + .OnValueChanged_Lambda([this](const float& Value) + { + CustomData.Time = Value; + }) + // .TypeInterface(MakeShared>(EUnit::Seconds)) + ] + ] + ] + ] + + SVerticalBox::Slot() + .SizeParam(FStretch(1.0)) + [ + SNew(SSpacer) + ] + + SVerticalBox::Slot() + .HAlign(HAlign_Center) + .VAlign(VAlign_Bottom) + .Padding(0, 0, 0, 24) + [ + SNew(SBox).HeightOverride(40).WidthOverride(144) + [ + SNew(SOverlay) + + SOverlay::Slot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + [ + SNew(SImage) + .Image(FUtils::GetBrushFromImage(FUtils::GetResourcesPath("SaveCustomPreset.png"), {144, 40})) + .OnMouseButtonDown_Lambda([this](const FGeometry&, const FPointerEvent&) + { + TSharedPtr NewProjectTips = SNew(SNewProjectTips).Title(TEXT("保存自定义效果名称")); + NewProjectTips->OnEnsure.BindLambda([this, NewProjectTips](const FString& String) + { + MainWidgetInterface->AddNewCustomPreset(String, CustomData); + GEngine->GameViewport->RemoveViewportWidgetContent(NewProjectTips.ToSharedRef()); + }); + GEngine->GameViewport->AddViewportWidgetContent(NewProjectTips.ToSharedRef() + , 1); + return FReply::Handled(); + }) + ] + + SOverlay::Slot() + [ + SNew(STextBlock) + .Visibility(EVisibility::HitTestInvisible) + .Text(FText::FromString((TEXT("保存自定义效果")))) + .Font(NormalText.Font) + .Justification(ETextJustify::Center) + ] + ] + ]; + // TODO: 配置文件保存自定义效果 + // TODO: 自定义效果拖拽到轨道 + + return PropertiesWidget; +} + END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/Cut5/Widgets/STimelineClip.h b/Source/Cut5/Widgets/STimelineClip.h index 43bd92c..bc70f05 100644 --- a/Source/Cut5/Widgets/STimelineClip.h +++ b/Source/Cut5/Widgets/STimelineClip.h @@ -10,6 +10,7 @@ #include "Cut5/Interface/SoundInterface.h" +#include "Widgets/Input/SComboBox.h" #include "Widgets/Layout/SBox.h" extern "C" { @@ -21,7 +22,7 @@ extern "C" /** * */ -class CUT5_API STimelineClip : public SCompoundWidget +class CUT5_API STimelineClip : public SCompoundWidget, public IPropertiesInterface { public: SLATE_BEGIN_ARGS(STimelineClip) @@ -72,5 +73,11 @@ public: FTimelinePropertyData ThumbnailCodecContext; + + + virtual TSharedPtr GetPropertiesWidget() override; + TSharedPtr>> GroupComboBox; + TArray> Selectable; + FPresetsCustomData CustomData; }; diff --git a/Source/Cut5/Widgets/STrackHead.cpp b/Source/Cut5/Widgets/STrackHead.cpp index 59ef25a..e7d710c 100644 --- a/Source/Cut5/Widgets/STrackHead.cpp +++ b/Source/Cut5/Widgets/STrackHead.cpp @@ -31,7 +31,7 @@ void STrackHead::Construct(const FArguments& InArgs) })); })); - TSharedPtr Image = SNew(SImage); + Image = SNew(SImage); ChildSlot [ SNew(SBox) @@ -103,6 +103,11 @@ void STrackHead::Construct(const FArguments& InArgs) FSlateDynamicImageBrush* Brush = new FSlateDynamicImageBrush(*(ImagePath), FVector2D(48, 48)); Image->SetImage(Brush); + + if (TrackData.TrackType == ETrackType::VideoTrack || TrackData.TrackType == ETrackType::AudioTrackR || TrackData.TrackType == ETrackType::AudioTrack) + { + Image->SetColorAndOpacity(TrackData.IsMute ? FLinearColor(1, 0.5, 0.5, 1) : FLinearColor(1, 1, 1, 1)); + } } FReply STrackHead::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) @@ -113,7 +118,15 @@ FReply STrackHead::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointer MenuBuilder.AddMenuEntry(FTimelineTrackCommands::Get().Remove); FSlateApplication::Get().PushMenu(AsShared(), FWidgetPath(), MenuBuilder.MakeWidget(), FSlateApplication::Get().GetCursorPos(), FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)); } - // + if (MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton)) + { + if (TrackData.TrackType == ETrackType::VideoTrack || TrackData.TrackType == ETrackType::AudioTrackR || TrackData.TrackType == ETrackType::AudioTrack) + { + TrackData.IsMute = !TrackData.IsMute; + Image->SetColorAndOpacity(TrackData.IsMute ? FLinearColor(1, 0.5, 0.5, 1) : FLinearColor(1, 1, 1, 1)); + } + + } return FReply::Unhandled(); } diff --git a/Source/Cut5/Widgets/STrackHead.h b/Source/Cut5/Widgets/STrackHead.h index 165d06f..2922536 100644 --- a/Source/Cut5/Widgets/STrackHead.h +++ b/Source/Cut5/Widgets/STrackHead.h @@ -27,9 +27,10 @@ public: FTrackData TrackData; TSharedPtr CutTimeline; + TSharedPtr Image; ICutMainWidgetInterface* MainWidgetInterface; FString GroupName; virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; - + bool bIsMute = false; TSharedPtr CommandList; };