diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e33c2f1
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,91 @@
+[*.{cpp,h}]
+
+# Naming convention rules (note: currently need to be ordered from more to less specific)
+
+cpp_naming_rule.aactor_prefixed.symbols = aactor_class
+cpp_naming_rule.aactor_prefixed.style = aactor_style
+
+cpp_naming_rule.swidget_prefixed.symbols = swidget_class
+cpp_naming_rule.swidget_prefixed.style = swidget_style
+
+cpp_naming_rule.uobject_prefixed.symbols = uobject_class
+cpp_naming_rule.uobject_prefixed.style = uobject_style
+
+cpp_naming_rule.booleans_prefixed.symbols = boolean_vars
+cpp_naming_rule.booleans_prefixed.style = boolean_style
+
+cpp_naming_rule.structs_prefixed.symbols = structs
+cpp_naming_rule.structs_prefixed.style = unreal_engine_structs
+
+cpp_naming_rule.enums_prefixed.symbols = enums
+cpp_naming_rule.enums_prefixed.style = unreal_engine_enums
+
+cpp_naming_rule.templates_prefixed.symbols = templates
+cpp_naming_rule.templates_prefixed.style = unreal_engine_templates
+
+cpp_naming_rule.general_names.symbols = all_symbols
+cpp_naming_rule.general_names.style = unreal_engine_default
+
+# Naming convention symbols
+
+cpp_naming_symbols.aactor_class.applicable_kinds = class
+cpp_naming_symbols.aactor_class.applicable_type = AActor
+
+cpp_naming_symbols.swidget_class.applicable_kinds = class
+cpp_naming_symbols.swidget_class.applicable_type = SWidget
+
+cpp_naming_symbols.uobject_class.applicable_kinds = class
+cpp_naming_symbols.uobject_class.applicable_type = UObject
+
+cpp_naming_symbols.boolean_vars.applicable_kinds = local,parameter,field
+cpp_naming_symbols.boolean_vars.applicable_type = bool
+
+cpp_naming_symbols.enums.applicable_kinds = enum
+
+cpp_naming_symbols.templates.applicable_kinds = template_class
+
+cpp_naming_symbols.structs.applicable_kinds = struct
+
+cpp_naming_symbols.all_symbols.applicable_kinds = *
+
+# Naming convention styles
+
+cpp_naming_style.unreal_engine_default.capitalization = pascal_case
+cpp_naming_style.unreal_engine_default.required_prefix =
+cpp_naming_style.unreal_engine_default.required_suffix =
+cpp_naming_style.unreal_engine_default.word_separator =
+
+cpp_naming_style.unreal_engine_enums.capitalization = pascal_case
+cpp_naming_style.unreal_engine_enums.required_prefix = E
+cpp_naming_style.unreal_engine_enums.required_suffix =
+cpp_naming_style.unreal_engine_enums.word_separator =
+
+cpp_naming_style.unreal_engine_templates.capitalization = pascal_case
+cpp_naming_style.unreal_engine_templates.required_prefix = T
+cpp_naming_style.unreal_engine_templates.required_suffix =
+cpp_naming_style.unreal_engine_templates.word_separator =
+
+cpp_naming_style.unreal_engine_structs.capitalization = pascal_case
+cpp_naming_style.unreal_engine_structs.required_prefix = F
+cpp_naming_style.unreal_engine_structs.required_suffix =
+cpp_naming_style.unreal_engine_structs.word_separator =
+
+cpp_naming_style.uobject_style.capitalization = pascal_case
+cpp_naming_style.uobject_style.required_prefix = U
+cpp_naming_style.uobject_style.required_suffix =
+cpp_naming_style.uobject_style.word_separator =
+
+cpp_naming_style.aactor_style.capitalization = pascal_case
+cpp_naming_style.aactor_style.required_prefix = A
+cpp_naming_style.aactor_style.required_suffix =
+cpp_naming_style.aactor_style.word_separator =
+
+cpp_naming_style.swidget_style.capitalization = pascal_case
+cpp_naming_style.swidget_style.required_prefix = S
+cpp_naming_style.swidget_style.required_suffix =
+cpp_naming_style.swidget_style.word_separator =
+
+cpp_naming_style.boolean_style.capitalization = pascal_case
+cpp_naming_style.boolean_style.required_prefix = b
+cpp_naming_style.boolean_style.required_suffix =
+cpp_naming_style.boolean_style.word_separator =
\ No newline at end of file
diff --git a/.idea/.idea.lost_planet.dir/.idea/.gitignore b/.idea/.idea.lost_planet.dir/.idea/.gitignore
new file mode 100644
index 0000000..11c3289
--- /dev/null
+++ b/.idea/.idea.lost_planet.dir/.idea/.gitignore
@@ -0,0 +1,15 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/projectSettingsUpdater.xml
+/modules.xml
+/.idea.lost_planet.iml
+/contentModel.xml
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/.idea.lost_planet.dir/.idea/.name b/.idea/.idea.lost_planet.dir/.idea/.name
new file mode 100644
index 0000000..f11fcf6
--- /dev/null
+++ b/.idea/.idea.lost_planet.dir/.idea/.name
@@ -0,0 +1 @@
+lost_planet
\ No newline at end of file
diff --git a/.idea/.idea.lost_planet.dir/.idea/encodings.xml b/.idea/.idea.lost_planet.dir/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.lost_planet.dir/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.lost_planet.dir/.idea/indexLayout.xml b/.idea/.idea.lost_planet.dir/.idea/indexLayout.xml
new file mode 100644
index 0000000..f5a863a
--- /dev/null
+++ b/.idea/.idea.lost_planet.dir/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.lost_planet.dir/.idea/vcs.xml b/.idea/.idea.lost_planet.dir/.idea/vcs.xml
new file mode 100644
index 0000000..c8397c9
--- /dev/null
+++ b/.idea/.idea.lost_planet.dir/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.lost_planet/.idea/.gitignore b/.idea/.idea.lost_planet/.idea/.gitignore
new file mode 100644
index 0000000..44f5ed1
--- /dev/null
+++ b/.idea/.idea.lost_planet/.idea/.gitignore
@@ -0,0 +1,15 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/.idea.lost_planet.iml
+/projectSettingsUpdater.xml
+/modules.xml
+/contentModel.xml
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/.idea.lost_planet/.idea/.name b/.idea/.idea.lost_planet/.idea/.name
new file mode 100644
index 0000000..f11fcf6
--- /dev/null
+++ b/.idea/.idea.lost_planet/.idea/.name
@@ -0,0 +1 @@
+lost_planet
\ No newline at end of file
diff --git a/.idea/.idea.lost_planet/.idea/encodings.xml b/.idea/.idea.lost_planet/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.lost_planet/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.lost_planet/.idea/indexLayout.xml b/.idea/.idea.lost_planet/.idea/indexLayout.xml
new file mode 100644
index 0000000..f5a863a
--- /dev/null
+++ b/.idea/.idea.lost_planet/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.lost_planet/.idea/vcs.xml b/.idea/.idea.lost_planet/.idea/vcs.xml
new file mode 100644
index 0000000..c8397c9
--- /dev/null
+++ b/.idea/.idea.lost_planet/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vsconfig b/.vsconfig
new file mode 100644
index 0000000..55fa82e
--- /dev/null
+++ b/.vsconfig
@@ -0,0 +1,19 @@
+{
+ "version": "1.0",
+ "components": [
+ "Component.Unreal.Debugger",
+ "Component.Unreal.Ide",
+ "Microsoft.Net.Component.4.6.2.TargetingPack",
+ "Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL",
+ "Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64",
+ "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL",
+ "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64",
+ "Microsoft.VisualStudio.Component.VC.Llvm.Clang",
+ "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
+ "Microsoft.VisualStudio.Component.Windows11SDK.22621",
+ "Microsoft.VisualStudio.Workload.CoreEditor",
+ "Microsoft.VisualStudio.Workload.ManagedDesktop",
+ "Microsoft.VisualStudio.Workload.NativeDesktop",
+ "Microsoft.VisualStudio.Workload.NativeGame"
+ ]
+}
diff --git a/Plugins/Gaea2Unreal-main/GaeaUnrealTools.uplugin b/Plugins/Gaea2Unreal-main/GaeaUnrealTools.uplugin
new file mode 100644
index 0000000..13ee5ac
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/GaeaUnrealTools.uplugin
@@ -0,0 +1,35 @@
+{
+ "FileVersion": 3,
+ "Version": 1,
+ "VersionName": "2.2.0.7",
+ "FriendlyName": "Gaea2Unreal",
+ "Description": "Enhance your landscape creation workflow.",
+ "Category": "Gaea",
+ "CreatedBy": "QuadSpinner",
+ "CreatedByURL": "https://quadspinner.com/",
+ "DocsURL": "https://gaea.app/uedocs",
+ "MarketplaceURL": "",
+ "SupportURL": "",
+ "EngineVersion": "5.7.0",
+ "CanContainContent": true,
+ "IsBetaVersion": true,
+ "Installed": true,
+ "Modules": [
+ {
+ "Name": "GaeaUnrealTools",
+ "Type": "Editor",
+ "LoadingPhase": "Default"
+ },
+ {
+ "Name": "GaeaUEToolsEditor",
+ "Type": "Editor",
+ "LoadingPhase": "Default"
+ }
+ ],
+ "Plugins": [
+ {
+ "Name": "EditorScriptingUtilities",
+ "Enabled": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Plugins/Gaea2Unreal-main/Resources/Icon128.png b/Plugins/Gaea2Unreal-main/Resources/Icon128.png
new file mode 100644
index 0000000..372b725
Binary files /dev/null and b/Plugins/Gaea2Unreal-main/Resources/Icon128.png differ
diff --git a/Plugins/Gaea2Unreal-main/Resources/ImporterIcon.png b/Plugins/Gaea2Unreal-main/Resources/ImporterIcon.png
new file mode 100644
index 0000000..4e570dc
Binary files /dev/null and b/Plugins/Gaea2Unreal-main/Resources/ImporterIcon.png differ
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/GaeaUEToolsEditor.Build.cs b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/GaeaUEToolsEditor.Build.cs
new file mode 100644
index 0000000..70a33fc
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/GaeaUEToolsEditor.Build.cs
@@ -0,0 +1,49 @@
+using System.IO;
+using UnrealBuildTool;
+
+public class GaeaUEToolsEditor : ModuleRules
+{
+ public GaeaUEToolsEditor(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ var EngineDir = Path.GetFullPath(base.Target.RelativeEnginePath);
+ PublicSystemIncludePaths.AddRange(new string[]
+
+ {
+ Path.Combine(EngineDir, "Source/Editor/LandscapeEditor/Private")
+ }
+ );
+
+ PublicDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "Core", "Landscape", "EditorStyle",
+ }
+ );
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "CoreUObject",
+ "Engine",
+ "Slate",
+ "SlateCore",
+ "InputCore",
+ "LevelEditor",
+ "Projects",
+ "MaterialEditor",
+ "Landscape",
+ "UnrealEd",
+ "ToolMenus",
+ "EditorFramework",
+ "EditorSubsystem",
+ "LandscapeEditor",
+ "Foliage",
+ "Json",
+ "JsonUtilities",
+ "AssetRegistry", "EditorScriptingUtilities"
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GMCSettings.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GMCSettings.cpp
new file mode 100644
index 0000000..965e46f
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GMCSettings.cpp
@@ -0,0 +1,12 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "GMCSettings.h"
+
+UGMCSettings::UGMCSettings()
+{
+}
+
+UGMCSettings::~UGMCSettings()
+{
+}
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GWindow.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GWindow.cpp
new file mode 100644
index 0000000..243a6d3
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GWindow.cpp
@@ -0,0 +1,135 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "GWindow.h"
+#include "GaeaSubsystem.h"
+#include "WorldPartition/WorldPartitionSubsystem.h"
+#include "PropertyEditorModule.h"
+#include "Widgets/Input/SButton.h"
+#include "Widgets/Layout/SScrollBox.h"
+
+DEFINE_LOG_CATEGORY(GaeaWindow)
+
+SMCWindow::SMCWindow()
+{
+}
+
+SMCWindow::~SMCWindow()
+{
+}
+
+SGaeaImportWindow::SGaeaImportWindow()
+{
+}
+
+SGaeaImportWindow::~SGaeaImportWindow()
+{
+ if (ImporterSettings)
+ {
+ ImporterSettings->RemoveFromRoot();
+ ImporterSettings = nullptr;
+ }
+}
+
+void SGaeaImportWindow::Construct(const FArguments& InArgs)
+{
+
+ CreateDetailsView();
+
+ SWindow::Construct(SWindow::FArguments()
+ .Title(InArgs._Title)
+ .ClientSize(InArgs._ClientSize)
+ .SizingRule(InArgs._SizingRule)
+ );
+
+
+ this->SetContent(SNew(SVerticalBox)
+ + SVerticalBox::Slot()
+ .FillHeight(1)
+ [
+ SNew(SScrollBox)
+ + SScrollBox::Slot()
+ [
+ PropertyWidget.ToSharedRef()
+ ]
+ ]
+ + SVerticalBox::Slot()
+ .AutoHeight()
+ .HAlign(HAlign_Fill)
+ .VAlign(VAlign_Bottom)
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .FillWidth(0.5)
+ [
+ SNew(SButton)
+ .Text(FText::FromString(("Import Heightmap")))
+ .HAlign(HAlign_Center)
+ .VAlign(VAlign_Center)
+ .ButtonColorAndOpacity(FLinearColor::Gray)
+ .OnClicked(this, &SGaeaImportWindow::OnImportClicked)
+ ]
+ + SHorizontalBox::Slot()
+ .FillWidth(0.5)
+ [
+ SNew(SButton)
+ .Text(FText::FromString(("Create Landscape")))
+ .HAlign(HAlign_Center)
+ .VAlign(VAlign_Center)
+ .IsEnabled_Lambda([this]() {return ImporterSettings != nullptr
+ && !ImporterSettings->HeightMapFileName.IsEmpty()
+ && (ImporterSettings->WeightmapFileNames.Num() == 0
+ || ImporterSettings->LandscapeMaterialLayerNames.Num() == 0
+ || ImporterSettings->WeightmapFileNames.Num() < ImporterSettings->LandscapeMaterialLayerNames.Num());})
+ .ButtonColorAndOpacity(FColor::Emerald)
+ .OnClicked(this, &SGaeaImportWindow::OnCreateLandscapeClicked)
+ ]
+ ]
+ );
+
+}
+
+
+void SGaeaImportWindow::CreateDetailsView()
+{
+ FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor");
+
+ FDetailsViewArgs Args;
+ Args.bAllowSearch = false;
+ Args.bHideSelectionTip = true;
+ Args.bShowScrollBar = true;
+
+ ImporterSettings = NewObject();
+ ImporterSettings->AddToRoot();
+ PropertyWidget = PropertyModule.CreateDetailView(Args);
+ PropertyWidget->SetObject(ImporterSettings);
+
+ ImporterSettings->Components = FIntPoint(8,8);
+ ImporterSettings->Resolution = FIntPoint(505,505);
+ ImporterSettings->TotalComponents = ImporterSettings->Components.X * ImporterSettings->Components.Y;
+}
+
+FReply SGaeaImportWindow::OnImportClicked() const
+{
+ if (UGaeaSubsystem* Manager = UGaeaSubsystem::GetGaeaSubsystem())
+ {
+ check(ImporterSettings != nullptr); // check if Importer Settings is valid
+ Manager->ImportHeightmap(ImporterSettings->HeightMapFileName, ImporterSettings->jsonFileName, ImporterSettings->Scale, ImporterSettings->Location, ImporterSettings->WeightmapFileNames, ImporterSettings->StoredPath); // Set heightmap path, json path and scale
+ UE_LOG(GaeaWindow, Display, TEXT("Heightmap file path is: %s"), *ImporterSettings->HeightMapFileName); // Log the heightmap file path
+ UE_LOG(GaeaWindow, Display, TEXT("Json file path is: %s"), *ImporterSettings->jsonFileName); // Log the heightmap file path
+ UE_LOG(GaeaWindow, Display, TEXT("Scale value is: %s"), *ImporterSettings->Scale.ToString()); // Log the scale
+ return FReply::Handled();
+ }
+ return FReply::Handled();
+}
+
+FReply SGaeaImportWindow::OnCreateLandscapeClicked()
+{
+ if (UGaeaSubsystem* Manager = UGaeaSubsystem::GetGaeaSubsystem())
+ {
+ Manager->CreateLandscapeActor(ImporterSettings);
+ return FReply::Handled();
+ }
+
+ return FReply::Handled();
+}
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaCommands.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaCommands.cpp
new file mode 100644
index 0000000..3e22b5c
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaCommands.cpp
@@ -0,0 +1,16 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "GaeaCommands.h"
+#include "InputCoreTypes.h"
+
+#define LOCTEXT_NAMESPACE "FGaea"
+
+void FGaeaCommands::RegisterCommands()
+{
+ UI_COMMAND(OpenImporter, "Open Importer", "Opens the Landscape Importer Window", EUserInterfaceActionType::Button, FInputChord(EKeys::P, EModifierKey::Control | EModifierKey::Alt));
+ UI_COMMAND(DeleteSelectedWPLandscape, "Delete Selected WP Landscape", "Deletes a World Partitioned Landscape and its proxies", EUserInterfaceActionType::None, FInputChord(EKeys::V, EModifierKey::Control));
+}
+
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaEditorStyle.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaEditorStyle.cpp
new file mode 100644
index 0000000..8849a91
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaEditorStyle.cpp
@@ -0,0 +1,76 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "GaeaEditorStyle.h"
+
+#include "Interfaces/IPluginManager.h"
+#include "Styling/CoreStyle.h"
+#include "Styling/SlateStyleRegistry.h"
+
+DEFINE_LOG_CATEGORY(GaeaEditorStyle)
+
+TUniquePtr FGaeaEditorStyle::StyleSet;
+
+void FGaeaEditorStyle::Initialize()
+{
+ if (StyleSet.IsValid())
+ {
+ // Only set up once
+ return;
+ }
+
+ // Create the style sheet
+ StyleSet = MakeUnique(GetStyleSetName());
+
+ //const FString ContentDir = "All/Plugins/GaeaUnrealTools/Icons/";
+
+ /*static const FString ContentDir = IPluginManager::Get().FindPlugin(TEXT("GaeaUnrealTools"))->GetContentDir();
+ UE_LOG(LogTemp, Warning, TEXT("Content Directory is: %s"), *ContentDir);
+
+ // Construct the relative path to the image file
+ const FString IconPath = ContentDir + "/Icons/ImporterIcon.png";*/
+
+ FString PluginPath = FPaths::Combine(FPaths::ProjectPluginsDir(), TEXT("GaeaUnrealTools"));
+ if (!FPaths::DirectoryExists(PluginPath))
+ PluginPath = FPaths::Combine(FPaths::EnginePluginsDir(), TEXT("GaeaUnrealTools"));
+
+ if (FPaths::DirectoryExists(PluginPath))
+ {
+ // Plugin found, construct the resources path
+ const FString ResourcesPath = FPaths::Combine(PluginPath, TEXT("Resources"));
+ UE_LOG(GaeaEditorStyle, Display, TEXT("Resources Directory is: %s"), *ResourcesPath);
+
+ // Construct the relative path to the image file
+ const FString IconPath = ResourcesPath + "/ImporterIcon.png";
+ StyleSet->Set("ImporterIcon", new FSlateImageBrush(IconPath, FVector2D(40.0f, 40.0f)));
+ }
+ else
+ {
+ // Plugin not found
+ UE_LOG(GaeaEditorStyle, Warning, TEXT("GaeaUnrealTools plugin not found"));
+ }
+
+
+ // Register the style set
+ FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get());
+}
+
+void FGaeaEditorStyle::Shutdown()
+{
+ // Unregister the style set
+ if (StyleSet.IsValid())
+ {
+ FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get());
+ StyleSet.Reset();
+ }
+}
+
+FName FGaeaEditorStyle::GetStyleSetName()
+{
+ static const FName StyleSetName(TEXT("GaeaEditorStyle"));
+ return StyleSetName;
+}
+
+FString FGaeaEditorStyle::RootToPluginContentDir(const FString& RelativePath, const TCHAR* Extension)
+{
+ static const FString ContentDir = IPluginManager::Get().FindPlugin(TEXT("GaeaUnrealTools"))->GetContentDir();
+ return (ContentDir / RelativePath) + Extension;
+}
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaLandscapeComponent.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaLandscapeComponent.cpp
new file mode 100644
index 0000000..2fc6f11
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaLandscapeComponent.cpp
@@ -0,0 +1,47 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "GaeaLandscapeComponent.h"
+#include "Engine.h"
+
+// Sets default values for this component's properties
+UGaeaLandscapeComponent::UGaeaLandscapeComponent()
+{
+ // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
+ // off to improve performance if you don't need them.
+ PrimaryComponentTick.bCanEverTick = false;
+ bIsEditorOnly = true;
+
+ // ...
+
+
+}
+
+
+// Called when the game starts
+void UGaeaLandscapeComponent::BeginPlay()
+{
+ Super::BeginPlay();
+
+ // ...
+
+}
+
+
+// Called every frame
+void UGaeaLandscapeComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
+{
+ Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+
+ // ...
+}
+
+void UGaeaLandscapeComponent::DestroyComponent(bool bPromoteChildren)
+{
+ if (GEngine)
+ {
+ // Display a message on the screen
+ GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Attempted to delete the Gaea Landscape Component, but this operation is not allowed on landscapes created with the Gaea plugin."));
+ }
+}
+
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaSubsystem.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaSubsystem.cpp
new file mode 100644
index 0000000..43a8e85
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaSubsystem.cpp
@@ -0,0 +1,1082 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "GaeaSubsystem.h"
+
+#include "DesktopPlatformModule.h"
+#include "FileHelpers.h"
+#include "Widgets/SWindow.h"
+#include "GWindow.h"
+#include "LandscapeEditorObject.h"
+#include "SlateBasics.h"
+#include "Widgets/Layout/SBox.h"
+#include "Widgets/Text/STextBlock.h"
+#include "Widgets/Input/SButton.h"
+#include "Landscape.h"
+#include "LandscapeProxy.h"
+#include "LandscapeRegionUtils.h"
+#include "LandscapeStreamingProxy.h"
+#include "Serialization/JsonSerializer.h"
+#include "JsonObjectConverter.h"
+#include "LandscapeEditorUtils.h"
+#include "LandscapeSubsystem.h"
+#include "LocationVolume.h"
+#include "ActorFactories/ActorFactory.h"
+#include "ToolMenus.h"
+#include "Builders/CubeBuilder.h"
+#include "Subsystems/EditorActorSubsystem.h"
+#include "VT/VirtualTexture.h"
+#include "GaeaLandscapeComponent.h"
+#include "AssetRegistry/AssetRegistryModule.h"
+#include "AssetToolsModule.h"
+#include "AsyncTreeDifferences.h"
+#include "EditorAssetLibrary.h"
+#include "Modules/ModuleManager.h"
+#include "Misc/PackageName.h"
+#include "LandscapeInfo.h"
+#include "Editor.h"
+#include "LandscapeEditLayer.h"
+#include "LandscapeEdit.h"
+
+
+#include "Materials/MaterialExpressionLandscapeLayerBlend.h"
+#include "WorldPartition/WorldPartition.h"
+
+
+DEFINE_LOG_CATEGORY(GaeaSubsystem)
+
+#define LOCTEXT_NAMESPACE "GaeaSubsystem"
+
+
+UGaeaSubsystem* UGaeaSubsystem::GetGaeaSubsystem()
+{
+ return GEditor->GetEditorSubsystem();
+}
+
+/*void UGaeaSubsystem::SpawnGMCWindow()
+{
+ //Load Property Module
+ FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor");
+
+ //Create Details View Arguments
+ FDetailsViewArgs Args;
+ Args.bAllowSearch = false;
+ Args.bHideSelectionTip = true;
+ Args.bShowScrollBar = true;
+
+ //Create Panel Settings Object
+ PanelSettings = NewObject();
+ PropertyWidget = PropertyModule.CreateDetailView(Args);
+ PropertyWidget->SetObject(PanelSettings);
+
+ // Create Gaea Window Instance
+ const TSharedRef WindowRef = SNew(SGaeaWindow)
+ .Title(FText::FromString("Gaea Landscape Material Creator"))
+ .ClientSize(FVector2D(475, 200))
+ .SizingRule(ESizingRule::UserSized);
+
+
+ // Create Window with Details View and Button
+ WindowRef->SetContent(
+ SNew(SVerticalBox)
+ + SVerticalBox::Slot()
+ .FillHeight(1)
+ [
+ SNew(SScrollBox)
+ + SScrollBox::Slot()
+ [
+ PropertyWidget.ToSharedRef()
+ ]
+ ]
+ + SVerticalBox::Slot()
+ //.Padding(0)
+ .AutoHeight()
+ .HAlign(HAlign_Fill)
+ .VAlign(VAlign_Bottom)
+ [
+ SNew(SButton)
+ .Text(FText::FromString(("Generate Landscape Material")))
+ .HAlign(HAlign_Center)
+ .VAlign(VAlign_Center)
+ .ButtonColorAndOpacity(FColor::Emerald)
+ ]
+ );
+
+ // Check if TWeakPtr is valid before spawning a window
+ if(WindowValidator.IsValid()==false)
+ {
+ FSlateApplication::Get().AddWindow(WindowRef);
+ /*if(GEngine)
+ GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Cyan, TEXT("Spawned the Window."));#1#
+ WindowValidator = WindowRef;
+ }
+ else
+ {
+ WindowValidator.Pin()->BringToFront(true);
+ /*if(GEngine)
+ GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, TEXT("Window Exists Already."));#1#
+
+ }
+
+
+}*/
+
+
+void UGaeaSubsystem::SpawnGImporterWindow()
+{
+ if(ImporterWindowValidator.IsValid()==false)
+ {
+ const TSharedRef WindowRef = SNew(SGaeaImportWindow)
+ .Title(FText::FromString("Gaea Landscape Importer"))
+ .ClientSize(FVector2D(500, 385))
+ .SizingRule(ESizingRule::UserSized);
+
+ FSlateApplication::Get().AddWindow(WindowRef);
+ ImporterWindowValidator = WindowRef;
+
+ }
+ else
+ {
+ ImporterWindowValidator.Pin()->BringToFront(true);
+
+ }
+}
+
+void UGaeaSubsystem::ReimportGaeaTerrain()
+{
+ TArray InfoObjects;
+ TArray WeightOutImportDescriptors;
+ TArray WeightImportResults;
+ TArray MaterialImportLayers;
+ TArray WeightOutMessage;
+ bool ReimportWeightmaps = false;
+ TArray WeightOutData;
+ TArray FinalWeightOutData;
+
+ if (UEditorActorSubsystem* ActorSubsystem = GEditor->GetEditorSubsystem())
+ {
+ const TArray& SelectedActors = ActorSubsystem->GetSelectedLevelActors(); // Use the subsystem instance to get references to selected actors.
+ UGaeaLandscapeComponent* GaeaComponent = SelectedActors[0]->FindComponentByClass(); // Check for a Gaea Landscape Component.
+ if (SelectedActors.Num() > 0 && GaeaComponent) // Check if component is valid and we actually have a selected actor.
+ {
+ AActor* Actor = SelectedActors[0];
+ ALandscape* Landscape = Cast(Actor);
+
+ ULandscapeInfo* LandscapeActorInfo = Landscape->GetLandscapeInfo();
+
+ FString JsonPath = GaeaComponent->DefinitionFilepath.FilePath;
+ FString HeightPath = GaeaComponent->HeightmapFilepath.FilePath;
+
+ bool bStatus = false;
+ FString JsonMessage = "";
+ const FGaeaJson GaeaDefinition = CreateStructFromJson(JsonPath, bStatus, JsonMessage);
+
+ if(bStatus)
+ {
+ UE_LOG(GaeaSubsystem, Display, TEXT("ScaleX: %f, ScaleY: %f, Height: %f, Resolution: %d"),
+ GaeaDefinition.ScaleX, GaeaDefinition.ScaleY, GaeaDefinition.Height, GaeaDefinition.Resolution);
+ FVector LandscapeLocation = FVector(0,0,GaeaDefinition.Height*100/2);
+ FVector LandscapeScale = FVector(GaeaDefinition.ScaleX * 100 / GaeaDefinition.Resolution,GaeaDefinition.ScaleY * 100 / GaeaDefinition.Resolution,GaeaDefinition.Height * 100 / 512); // Apply scaling formula to our passed in scale variable from the ImporterPanelSettings
+
+ constexpr bool bSingleFile = true;
+ FLandscapeImportDescriptor OutImportDescriptor;
+ OutImportDescriptor.Scale = LandscapeScale;
+ FText OutMessage;
+
+
+ ULandscapeEditorObject* DefaultValueObject = ULandscapeEditorObject::StaticClass()->GetDefaultObject(); // Create Landscape Editor Object instance
+ check(DefaultValueObject);
+
+ int32 OutQuadsPerSection = DefaultValueObject->NewLandscape_QuadsPerSection;
+ int32 OutSectionsPerComponent = DefaultValueObject->NewLandscape_SectionsPerComponent;
+ FIntPoint OutComponentCount = DefaultValueObject->NewLandscape_ComponentCount;
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Heightmap Data Setup
+
+ ELandscapeImportResult ImportResult = FLandscapeImportHelper::GetHeightmapImportDescriptor(HeightPath, bSingleFile, false, OutImportDescriptor, OutMessage);
+
+ int32 DescriptorIndex = OutImportDescriptor.FileResolutions.Num() / 2;
+
+ FLandscapeImportHelper::ChooseBestComponentSizeForImport(OutImportDescriptor.ImportResolutions[DescriptorIndex].Width, OutImportDescriptor.ImportResolutions[DescriptorIndex].Height, OutQuadsPerSection, OutSectionsPerComponent, OutComponentCount);
+
+ TArray ImportData;
+ ImportResult = FLandscapeImportHelper::GetHeightmapImportData(OutImportDescriptor, DescriptorIndex, ImportData, OutMessage);
+
+ const int32 QuadsPerComponent = OutSectionsPerComponent * OutQuadsPerSection;
+ const int32 SizeX = OutComponentCount.X * QuadsPerComponent + 1;
+ const int32 SizeY = OutComponentCount.Y * QuadsPerComponent + 1;
+
+ TArray FinalHeightData;
+ FLandscapeImportHelper::TransformHeightmapImportData(ImportData, FinalHeightData, OutImportDescriptor.ImportResolutions[DescriptorIndex], FLandscapeImportResolution(SizeX, SizeY), ELandscapeImportTransformType::ExpandCentered);
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Setup prerequisites for SetHeightData and SetAlphaData
+
+ FIntRect ComponentsRect = Landscape->GetBoundingRect() + Landscape->GetSectionBase();
+ const int32 CompSizeX = ComponentsRect.Width() + 1;
+ const int32 CompSizeY = ComponentsRect.Height() + 1;
+
+ FLandscapeEditDataInterface LandscapeEdit(Landscape->GetLandscapeInfo());
+
+ // New setup for 5.6 to get the Guid of the base edit layer. Required for SetEditLayer and therefore SetHeight/Weight Data to function properly.
+ ULandscapeEditLayerBase* Layer = Landscape->GetEditLayer(0);
+ LandscapeEdit.SetEditLayer(Layer->GetGuid());
+
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Weightmap Data Setup and SetAlphaData Execution
+
+ if (LandscapeActorInfo && !GaeaComponent->WeightmapFilepaths.IsEmpty())
+ {
+
+ // Loop through all layers associated with the landscape and get the relevant layer objects.
+ for (const FLandscapeInfoLayerSettings& LayerSettings : LandscapeActorInfo->Layers)
+ {
+ if (ULandscapeLayerInfoObject* LayerInfoObject = LayerSettings.LayerInfoObj)
+ {
+ InfoObjects.Add(LayerInfoObject);
+
+ }
+ }
+
+ for (int32 i = 0; i < InfoObjects.Num(); i++)
+ {
+ if (InfoObjects[i])
+ {
+ Landscape->ClearEditLayer(i, nullptr, ELandscapeToolTargetTypeFlags::Heightmap);
+ Landscape->ClearEditLayer(i, nullptr, ELandscapeToolTargetTypeFlags::Weightmap);
+
+ }
+ }
+
+ WeightOutMessage.AddDefaulted(InfoObjects.Num());
+ WeightOutImportDescriptors.AddDefaulted(InfoObjects.Num());
+ WeightImportResults.AddDefaulted(InfoObjects.Num());
+
+ TArray ClearWeightData;
+ ClearWeightData.SetNum(CompSizeX * CompSizeY); // Ensure the size matches the landscape component
+ FMemory::Memset(ClearWeightData.GetData(), 255, CompSizeX * CompSizeY);
+
+
+ if (GaeaComponent->WeightmapFilepaths.Num() >= InfoObjects.Num() - 1)
+ {
+ LandscapeEdit.SetAlphaData(InfoObjects[0], ComponentsRect.Min.X, ComponentsRect.Min.Y, ComponentsRect.Max.X, ComponentsRect.Max.Y, ClearWeightData.GetData(), 0, ELandscapeLayerPaintingRestriction::None);
+
+ for(int32 i = 1; i < InfoObjects.Num(); i++)
+ {
+ int32 WeightmapIndex = i - 1;
+ if (WeightmapIndex < GaeaComponent->WeightmapFilepaths.Num())
+ {
+ FLandscapeImportHelper::GetWeightmapImportDescriptor(GaeaComponent->WeightmapFilepaths[WeightmapIndex].FilePath, true, false, InfoObjects[i]->GetLayerName(), WeightOutImportDescriptors[i], WeightOutMessage[i]);
+ FLandscapeImportHelper::GetWeightmapImportData(WeightOutImportDescriptors[i], DescriptorIndex, InfoObjects[i]->GetLayerName(), WeightOutData, WeightOutMessage[i]);
+ FLandscapeImportHelper::TransformWeightmapImportData(WeightOutData, FinalWeightOutData, OutImportDescriptor.ImportResolutions[DescriptorIndex], FLandscapeImportResolution(SizeX, SizeY), ELandscapeImportTransformType::ExpandCentered);
+
+ if(FinalWeightOutData.Num() == CompSizeX * CompSizeY)
+ {
+ LandscapeEdit.SetAlphaData(InfoObjects[i], ComponentsRect.Min.X, ComponentsRect.Min.Y, ComponentsRect.Max.X, ComponentsRect.Max.Y, FinalWeightOutData.GetData(), 0, ELandscapeLayerPaintingRestriction::None);
+ }
+
+ }
+ Landscape->RequestLayersContentUpdateForceAll(Update_All);
+ }
+
+ }
+
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Execute SetHeightData
+
+ if (FinalHeightData.Num() == CompSizeX * CompSizeY)
+ {
+
+ LandscapeEdit.SetHeightData( ComponentsRect.Min.X, ComponentsRect.Min.Y, ComponentsRect.Max.X, ComponentsRect.Max.Y,
+ FinalHeightData.GetData(),
+ 0,
+ true,
+ nullptr, // Normals data
+ nullptr, // Alpha blend data
+ nullptr, // Raise/lower data
+ false, // Do not create new components
+ nullptr, // No custom heightmap texture
+ nullptr, // No XY offsetmap texture
+ true, // Update bounds
+ true, // Update collision
+ true // Generate mipmaps
+ );
+
+ Landscape->RequestLayersContentUpdateForceAll(ELandscapeLayerUpdateMode::Update_Heightmap_All);
+
+ Landscape->SetActorScale3D(LandscapeScale);
+
+ }
+ else
+ {
+ if (GEngine)
+ {
+ GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Original landscape resolution does not match the reimport texture resolution"));
+ }
+ }
+
+ }
+
+
+ }
+ }
+}
+
+void UGaeaSubsystem::ReimportGaeaWPTerrain()
+{
+ TArray InfoObjects;
+ TArray WeightOutImportDescriptors;
+ TArray WeightImportResults;
+ TArray WeightOutMessage;
+ bool ReimportWeightmaps = false;
+ TArray WeightOutData;
+ TArray FinalWeightOutData;
+
+ UEditorActorSubsystem* ActorSubsystem = GEditor->GetEditorSubsystem(); // Get an instance of the Editor Actor Subsystem.
+ if (ActorSubsystem)
+ {
+ const TArray& SelectedActors = ActorSubsystem->GetSelectedLevelActors();
+ if (SelectedActors.Num() > 0)
+ {
+ AActor* Actor = SelectedActors[0];
+ ALandscape* Landscape = Cast(Actor);
+ ULandscapeInfo* LandscapeActorInfo = Landscape->GetLandscapeInfo();
+
+ if (Landscape)
+ {
+ if (UGaeaLandscapeComponent* GaeaComponent = Actor->FindComponentByClass())
+ {
+ FString JsonPath = GaeaComponent->DefinitionFilepath.FilePath;
+ FString HeightPath = GaeaComponent->HeightmapFilepath.FilePath;
+
+ bool bStatus = false;
+ FString JsonMessage = "";
+ const FGaeaJson GaeaDefinition = CreateStructFromJson(JsonPath, bStatus, JsonMessage);
+
+ if(bStatus)
+ {
+ FVector LandscapeLocation = FVector(0, 0, GaeaDefinition.Height * 100 / 2);
+ FVector LandscapeScale = FVector(
+ GaeaDefinition.ScaleX * 100 / GaeaDefinition.Resolution,
+ GaeaDefinition.ScaleY * 100 / GaeaDefinition.Resolution,
+ GaeaDefinition.Height * 100 / 512
+ );
+
+ constexpr bool bSingleFile = true;
+ FLandscapeImportDescriptor OutImportDescriptor;
+ OutImportDescriptor.Scale = LandscapeScale;
+ FText OutMessage;
+
+ ELandscapeImportResult ImportResult = FLandscapeImportHelper::GetHeightmapImportDescriptor(HeightPath, bSingleFile, false, OutImportDescriptor, OutMessage);
+
+ if (ImportResult == ELandscapeImportResult::Success)
+ {
+ int32 DescriptorIndex = OutImportDescriptor.FileResolutions.Num() / 2;
+
+ ULandscapeEditorObject* DefaultValueObject = ULandscapeEditorObject::StaticClass()->GetDefaultObject();
+ check(DefaultValueObject);
+
+ int32 OutQuadsPerSection = DefaultValueObject->NewLandscape_QuadsPerSection;
+ int32 OutSectionsPerComponent = DefaultValueObject->NewLandscape_SectionsPerComponent;
+ FIntPoint OutComponentCount = DefaultValueObject->NewLandscape_ComponentCount;
+
+ FLandscapeImportHelper::ChooseBestComponentSizeForImport(
+ OutImportDescriptor.ImportResolutions[DescriptorIndex].Width,
+ OutImportDescriptor.ImportResolutions[DescriptorIndex].Height,
+ OutQuadsPerSection,
+ OutSectionsPerComponent,
+ OutComponentCount
+ );
+
+ TArray ImportData;
+ ImportResult = FLandscapeImportHelper::GetHeightmapImportData(OutImportDescriptor, DescriptorIndex, ImportData, OutMessage);
+
+ if (ImportResult == ELandscapeImportResult::Success)
+ {
+ TArray FinalHeightData;
+
+ // Calculate SizeX and SizeY based on the component count and the number of quads per component
+ const int32 QuadsPerComponent = OutSectionsPerComponent * OutQuadsPerSection;
+ const int32 SizeX = OutComponentCount.X * QuadsPerComponent + 1;
+ const int32 SizeY = OutComponentCount.Y * QuadsPerComponent + 1;
+
+ FLandscapeImportHelper::TransformHeightmapImportData(
+ ImportData,
+ FinalHeightData,
+ OutImportDescriptor.ImportResolutions[DescriptorIndex],
+ FLandscapeImportResolution(SizeX, SizeY),
+ ELandscapeImportTransformType::ExpandCentered
+ );
+
+ if (LandscapeActorInfo && !GaeaComponent->WeightmapFilepaths.IsEmpty())
+ {
+ // Loop through all layers associated with the landscape and get the relevant layer objects.
+ for (const FLandscapeInfoLayerSettings& LayerSettings : LandscapeActorInfo->Layers)
+ {
+ if (ULandscapeLayerInfoObject* LayerInfoObject = LayerSettings.LayerInfoObj)
+ {
+ InfoObjects.Add(LayerInfoObject);
+ }
+ }
+
+ for (int32 i = 0; i < InfoObjects.Num(); i++)
+ {
+ if (InfoObjects[i])
+ {
+ Landscape->ClearEditLayer(i,nullptr,ELandscapeToolTargetTypeFlags::Weightmap); // Have to clear all layers, or SetAlphaData will accumulate or present with visual artifacting.
+ }
+ }
+
+ WeightOutMessage.AddDefaulted(InfoObjects.Num());
+ WeightOutImportDescriptors.AddDefaulted(InfoObjects.Num());
+ WeightImportResults.AddDefaulted(InfoObjects.Num());
+ }
+
+ // Collect and process each landscape proxy
+ TArray AllProxies;
+ Landscape->GetLandscapeInfo()->ForEachLandscapeProxy([&AllProxies](ALandscapeProxy* Proxy)
+ {
+ AllProxies.Add(Proxy);
+ return true; // Continue iteration
+ });
+
+ for (ALandscapeProxy* Proxy : AllProxies)
+ {
+ FLandscapeEditDataInterface LandscapeEdit(Proxy->GetLandscapeInfo());
+
+ FIntRect ComponentsRect = Proxy->GetBoundingRect() + Proxy->GetSectionBase();
+ const int32 CompSizeX = ComponentsRect.Width() + 1;
+ const int32 CompSizeY = ComponentsRect.Height() + 1;
+
+ // Define the region in FinalHeightData corresponding to this proxy
+ TArray ProxyHeightData;
+ ProxyHeightData.SetNum(CompSizeX * CompSizeY);
+
+ for (int32 Y = 0; Y < CompSizeY; ++Y)
+ {
+ for (int32 X = 0; X < CompSizeX; ++X)
+ {
+ int32 SrcX = ComponentsRect.Min.X + X;
+ int32 SrcY = ComponentsRect.Min.Y + Y;
+ int32 SrcIndex = SrcY * SizeX + SrcX; // SizeX is the width of the full landscape
+
+ int32 DestIndex = Y * CompSizeX + X;
+ if (FinalHeightData.IsValidIndex(SrcIndex) && ProxyHeightData.IsValidIndex(DestIndex))
+ {
+ ProxyHeightData[DestIndex] = FinalHeightData[SrcIndex];
+ }
+ }
+ }
+
+ if (ProxyHeightData.Num() == CompSizeX * CompSizeY)
+ {
+
+ ULandscapeEditLayerBase* Layer = Landscape->GetEditLayer(0);
+ LandscapeEdit.SetEditLayer(Layer->GetGuid());
+
+ LandscapeEdit.SetHeightData(
+ ComponentsRect.Min.X, ComponentsRect.Min.Y,
+ ComponentsRect.Max.X, ComponentsRect.Max.Y,
+ ProxyHeightData.GetData(),
+ 0, true, nullptr, nullptr, nullptr,
+ false, nullptr, nullptr,
+ true, true, true
+ );
+
+ Landscape->RequestLayersContentUpdateForceAll(ELandscapeLayerUpdateMode::Update_All);
+
+
+ Proxy->SetActorScale3D(LandscapeScale);
+ }
+ else
+ {
+ if (GEngine)
+ {
+ GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Resolution mismatch for proxy."));
+ }
+ }
+
+
+ if(InfoObjects.Num() > 0)
+ {
+ TArray FirstLayerData;
+ FirstLayerData.SetNum(CompSizeX * CompSizeY);
+ FMemory::Memset(FirstLayerData.GetData(), 255, CompSizeX * CompSizeY);
+ LandscapeEdit.SetAlphaData(InfoObjects[0],ComponentsRect.Min.X, ComponentsRect.Min.Y,ComponentsRect.Max.X, ComponentsRect.Max.Y,FirstLayerData.GetData(),0);
+
+ for (int32 i = 1; i < InfoObjects.Num(); i++) // Start from 1 to skip first layer
+ {
+ int32 WeightmapIndex = i - 1;
+
+ if (WeightmapIndex < GaeaComponent->WeightmapFilepaths.Num())
+ {
+ FLandscapeImportHelper::GetWeightmapImportDescriptor(GaeaComponent->WeightmapFilepaths[WeightmapIndex].FilePath, true, false, InfoObjects[i]->GetLayerName(), WeightOutImportDescriptors[i], WeightOutMessage[i]);
+ FLandscapeImportHelper::GetWeightmapImportData(WeightOutImportDescriptors[i], DescriptorIndex, InfoObjects[i]->GetLayerName(), WeightOutData, WeightOutMessage[i]);
+ FLandscapeImportHelper::TransformWeightmapImportData(WeightOutData, FinalWeightOutData, OutImportDescriptor.ImportResolutions[DescriptorIndex], FLandscapeImportResolution(SizeX, SizeY), ELandscapeImportTransformType::ExpandCentered);
+
+ // Apply weightmap data for the current proxy
+ TArray ProxyWeightData;
+ ProxyWeightData.SetNum(CompSizeX * CompSizeY);
+
+ for (int32 Y = 0; Y < CompSizeY; ++Y)
+ {
+ for (int32 X = 0; X < CompSizeX; ++X)
+ {
+ int32 SrcX = ComponentsRect.Min.X + X;
+ int32 SrcY = ComponentsRect.Min.Y + Y;
+ int32 SrcIndex = SrcY * SizeX + SrcX; // SizeX is the width of the full landscape
+
+ int32 DestIndex = Y * CompSizeX + X;
+ if (FinalWeightOutData.IsValidIndex(SrcIndex) && ProxyWeightData.IsValidIndex(DestIndex))
+ {
+ ProxyWeightData[DestIndex] = FinalWeightOutData[SrcIndex];
+ }
+ }
+ }
+
+ if (ProxyWeightData.Num() == CompSizeX * CompSizeY)
+ {
+
+ ULandscapeEditLayerBase* Layer = Landscape->GetEditLayer(0);
+ LandscapeEdit.SetEditLayer(Layer->GetGuid());
+
+ LandscapeEdit.SetAlphaData(
+ InfoObjects[i],
+ ComponentsRect.Min.X, ComponentsRect.Min.Y,
+ ComponentsRect.Max.X, ComponentsRect.Max.Y,
+ ProxyWeightData.GetData(),
+ 0, ELandscapeLayerPaintingRestriction::None);
+
+ }
+ else
+ {
+ if (GEngine)
+ {
+ GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Original landscape resolution does not match the reimport texture resolution"));
+ }
+ }
+ }
+
+ }
+ }
+ else
+ {
+ UE_LOG(GaeaSubsystem, Error, TEXT("InfoObject count is 0."));
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+void UGaeaSubsystem::ImportHeightmap(FString& Heightmap, FString& JSON, FVector& Scale, FVector& Location, TArray& Weightmaps, FString& CachedPath)
+{
+ check(ImporterWindowValidator!= nullptr);
+ JSON.Empty(); // Empty out json path from ImporterPanelSettings
+ Heightmap.Empty(); // Empty out heightmap path from ImporterPanelSettings
+
+ const TSharedPtr LocalNativeWindow = ImporterWindowValidator.Pin()->GetNativeWindow();
+ check(LocalNativeWindow!= nullptr);
+ const void* ParentWindow = LocalNativeWindow->GetOSWindowHandle();
+
+ const FString DialogTitle = TEXT("Import Heightmap"); // Create dialog window title.
+ const FString DefaultPath = DefaultDialogPath.IsEmpty() ? FPaths::ProjectDir() : DefaultDialogPath; // Set DefaultPath to DefaultDialogPath if it isn't empty, otherwise use ProjectDir().
+ const FString DefaultFile = TEXT("");
+ FString OutPath = TEXT("");
+ const FString FileFilter = TEXT("Heightmap files (*.r16, *.raw, *.png)|*.r16;*.raw;*.png|"); // Heightmap type file filter.
+
+ TArray SelectedFilePath;
+
+ if (FDesktopPlatformModule::Get()->OpenFileDialog(ParentWindow, DialogTitle, DefaultPath, DefaultFile, FileFilter, EFileDialogFlags::None, SelectedFilePath)) {
+
+ Heightmap = SelectedFilePath[0]; // Set path to heightmap.
+
+ OutPath = FPaths::GetPath(Heightmap); // Remove asset name & extension to create our folder directory.
+
+ CachedPath = OutPath; // Store path onto details panel uobject for secondary array generation when the frontend array is reordered.
+
+ DefaultDialogPath = OutPath; // Set DefaultDialogPath to OutPath so that next time the user clicks the import button, we open to a previously known directory.
+
+ IFileManager& FileManager = IFileManager::Get(); // Get instance of File Manager.
+
+
+
+ TArray WeightFilesInDirectory;
+
+ FileManager.FindFiles(WeightFilesInDirectory, *(OutPath / TEXT("*.png*")), true, false); // Get all pngs from directory.
+
+ for(int i = 0; i < WeightFilesInDirectory.Num(); i++)
+ {
+ if (WeightFilesInDirectory[i].Contains(TEXT("W_"), ESearchCase::IgnoreCase))
+ {
+ Weightmaps.Add(WeightFilesInDirectory[i]);
+ }
+ }
+
+ TArray FilesInDirectory; // Create array of strings.
+
+ FileManager.FindFiles(FilesInDirectory, *(OutPath / TEXT("*.json*")), true, false); // Get all instances of json files from directory.
+
+ if (FilesInDirectory.IsEmpty()) // Early exit if json isn't present.
+ {
+ JSON = TEXT("None");
+ UE_LOG(GaeaSubsystem, Log, TEXT("No json files found."));
+ return;
+ }
+
+
+ bool bDefinitionFound = false; // Flag to indicate whether a definition file is found
+
+ for(int i = 0; i < FilesInDirectory.Num(); i++)
+ {
+ if (FilesInDirectory[i].Contains(TEXT("definition"), ESearchCase::IgnoreCase))
+ {
+ JSON = FPaths::Combine(*OutPath, *FilesInDirectory[i]); // Write json filepath
+ bDefinitionFound = true; // Set the flag to true
+ break;
+ }
+ }
+
+ if (!bDefinitionFound)
+ {
+ // If the flag is still false after the loop, no definition file was found
+ UE_LOG(GaeaSubsystem, Warning, TEXT("No Definition.json found."));
+ return;
+ }
+ }
+
+ else
+ {
+ // No file was selected; the file dialog was closed without selecting a file.
+ UE_LOG(GaeaSubsystem, Log, TEXT("Dialog was closed. No files selected"));
+ return;
+
+ }
+ bool bStatus = false;
+ FString JsonMessage = "";
+ const FGaeaJson GaeaDefinition = CreateStructFromJson(JSON, bStatus, JsonMessage);
+
+ if(bStatus)
+ {
+ UE_LOG(GaeaSubsystem, Display, TEXT("ScaleX: %f, ScaleY: %f, Height: %f, Resolution: %d"),
+ GaeaDefinition.ScaleX, GaeaDefinition.ScaleY, GaeaDefinition.Height, GaeaDefinition.Resolution);
+ Location = FVector(0,0,GaeaDefinition.Height*100/2);
+ Scale = FVector(GaeaDefinition.ScaleX * 100 / GaeaDefinition.Resolution,GaeaDefinition.ScaleY * 100 / GaeaDefinition.Resolution,GaeaDefinition.Height * 100 / 512); // Apply scaling formula to our passed in scale variable from the ImporterPanelSettings
+ }
+
+
+
+}
+
+FString UGaeaSubsystem::ReadStringFromFile(FString Path, bool& bOutSuccess, FString& OutMessage)
+{
+ if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*Path))
+ {
+ bOutSuccess = false;
+ OutMessage = FString::Printf(TEXT("Failed to read file - File does not exist - '%s'"), *Path);\
+
+ UE_LOG(GaeaSubsystem, Error, TEXT("Failed to read file - File does not exist."));
+ return "";
+ }
+
+ FString OutString = "";
+
+ if(!FFileHelper::LoadFileToString(OutString, *Path))
+ {
+ bOutSuccess = false;
+ OutMessage = FString::Printf(TEXT("Failed to read file - Is this a text file? - '%s'"), *Path);
+ UE_LOG(GaeaSubsystem, Error, TEXT("Failed to read file - Is this a text file?"));
+ return "";
+ }
+
+ bOutSuccess = true;
+ OutMessage = FString::Printf(TEXT("File read successfully - '%s'"), *Path);
+ //UE_LOG(LogTemp, Log, TEXT("File read successfully."));
+ return OutString;
+
+}
+
+TSharedPtr UGaeaSubsystem::ReadJson(FString Path, bool& bOutSuccess, FString& OutMessage)
+{
+ const FString JSONString = ReadStringFromFile(Path, bOutSuccess, OutMessage);
+ if (!bOutSuccess)
+ {
+ return nullptr;
+ }
+
+ TSharedPtr RetJsonObject;
+
+ if(!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(JSONString), RetJsonObject))
+ {
+ bOutSuccess = false;
+ OutMessage = FString::Printf(TEXT("Json read failed - '%s'"), *Path);
+ UE_LOG(GaeaSubsystem, Error, TEXT("Json read failed."));
+ return nullptr;
+ }
+
+ bOutSuccess = true;
+ OutMessage = FString::Printf(TEXT("Successfully read Json - '%s'"), *Path);
+ UE_LOG(GaeaSubsystem, Log, TEXT("Successfully read Json."));
+ return RetJsonObject;
+}
+
+FGaeaJson UGaeaSubsystem::CreateStructFromJson(FString Path, bool& bOutSuccess, FString& OutMessage)
+{
+ const TSharedPtr JsonObject = ReadJson(Path, bOutSuccess, OutMessage);
+ if(!bOutSuccess)
+ {
+ return FGaeaJson();
+ }
+
+ FGaeaJson RetGaeaJson;
+
+ if(!FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), &RetGaeaJson))
+ {
+ bOutSuccess = false;
+ OutMessage = FString::Printf(TEXT("Json conversion failed - '%s'"), *Path);
+ UE_LOG(GaeaSubsystem, Error, TEXT("Json conversion failed."));
+ return FGaeaJson();
+ }
+
+ bOutSuccess = true;
+ OutMessage = FString::Printf(TEXT("Json conversion succeeded - '%s'"), *Path);
+ //UE_LOG(LogTemp, Log, TEXT("Json conversion succeeded."));
+ return RetGaeaJson;
+
+}
+
+ALandscape* UGaeaSubsystem::GetLandscape(ULandscapeInfo* LandscapeInfo) const
+{
+ ALandscape* LandscapeActor = LandscapeInfo->LandscapeActor.Get();
+ if (LandscapeActor != nullptr)
+ {
+ return LandscapeActor;
+ }
+ return nullptr;
+}
+
+/* FGuid UGaeaSubsystem::GetLayerGuidFromIndex(int32 Index, ULandscapeInfo* LandscapeInfo) const
+{
+ ALandscape* Landscape = GetLandscape(LandscapeInfo);
+ FLandscapeLayer* Layer = Landscape ? Landscape->GetLayer(Index) : nullptr;
+ return Layer ? Layer->Guid : FGuid();
+}*/
+
+void UGaeaSubsystem::CreateLandscapeActor(UImporterPanelSettings* Settings)
+{
+ constexpr bool bSingleFile = true;
+ FLandscapeImportDescriptor OutImportDescriptor;
+ TArray WeightOutImportDescriptors;
+ TArray WeightImportResults;
+ TArray MaterialImportLayers;
+ TArray LayerInfoObjects;
+ OutImportDescriptor.Scale = Settings->Scale;
+ FText OutMessage;
+ TArray WeightOutMessage;
+ bool ImportWeightmaps = false;
+
+ UWorld* World = nullptr;
+ {
+ FWorldContext& EditorWorldContext = GEditor->GetEditorWorldContext();
+ World = EditorWorldContext.World(); // We want to create the landscape in the editor world.
+ }
+
+ ULandscapeSubsystem* LandscapeSubsystem = World->GetSubsystem();
+ bool bIsGridBased = LandscapeSubsystem->IsGridBased();
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Heightmap Import/Data Creation
+
+ ELandscapeImportResult ImportResult = FLandscapeImportHelper::GetHeightmapImportDescriptor(Settings->HeightMapFileName, bSingleFile, Settings->FlipYAxis, OutImportDescriptor, OutMessage);
+ int32 DescriptorIndex = OutImportDescriptor.FileResolutions.Num() / 2;
+
+ ULandscapeEditorObject* DefaultValueObject = ULandscapeEditorObject::StaticClass()->GetDefaultObject(); // Create Landscape Editor Object instance
+ check(DefaultValueObject);
+
+ int32 OutQuadsPerSection = DefaultValueObject->NewLandscape_QuadsPerSection;
+ int32 OutSectionsPerComponent = DefaultValueObject->NewLandscape_SectionsPerComponent;
+ FIntPoint OutComponentCount = DefaultValueObject->NewLandscape_ComponentCount;
+
+ // We automatically adjust/fit the data so users avoid having to guess.
+ FLandscapeImportHelper::ChooseBestComponentSizeForImport(OutImportDescriptor.ImportResolutions[DescriptorIndex].Width, OutImportDescriptor.ImportResolutions[DescriptorIndex].Height, OutQuadsPerSection, OutSectionsPerComponent, OutComponentCount);
+
+ TArray ImportData;
+ ImportResult = FLandscapeImportHelper::GetHeightmapImportData(OutImportDescriptor, DescriptorIndex, ImportData, OutMessage);
+
+
+ const int32 QuadsPerComponent = OutSectionsPerComponent * OutQuadsPerSection;
+ const int32 SizeX = OutComponentCount.X * QuadsPerComponent + 1;
+ const int32 SizeY = OutComponentCount.Y * QuadsPerComponent + 1;
+
+ TArray FinalHeightData;
+ FLandscapeImportHelper::TransformHeightmapImportData(ImportData, FinalHeightData, OutImportDescriptor.ImportResolutions[DescriptorIndex], FLandscapeImportResolution(SizeX, SizeY), ELandscapeImportTransformType::ExpandCentered);
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Weightmap Import/Data Creation
+
+ FString PackagePath = Settings->LayerInfoFolder.Path;
+ FString Name;
+ bool PathExists = false;
+
+ if(!PackagePath.IsEmpty())
+ {
+ //Check if path exists. User may have altered it after picking via the widget.
+ PathExists = UEditorAssetLibrary::DoesDirectoryExist(PackagePath);
+ }
+
+ UE_LOG(GaeaSubsystem, Display, TEXT("LandscapeMaterialLayerNames Count: %d"), Settings->LandscapeMaterialLayerNames.Num());
+ UE_LOG(GaeaSubsystem, Display, TEXT("LayerInfoFolder Path: %s"), *Settings->LayerInfoFolder.Path);
+
+ if (!Settings->LandscapeMaterialLayerNames.IsEmpty() && PathExists && Settings->LandscapeMaterialLayerNames.Num() >= 2 && Settings->WeightmapFileNames.Num() == (Settings->LandscapeMaterialLayerNames.Num() - 1))
+ {
+ //Checks to see if we have weightmaps to import. If there are 2 or less, we skip this.
+ ImportWeightmaps = true;
+ }
+
+ if(ImportWeightmaps)
+ {
+ // Load the AssetToolsModule. This is needed to create layer info objects. However, we may add the option to choose existing info objects instead of always creating new ones as it is not technically needed to have unique info objects.
+ FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools");
+
+ //Create LandscapeLayerInfoObjects for use with weightmaps.
+ for(int i = 0; i < Settings->LandscapeMaterialLayerNames.Num(); i++)
+ {
+ Name = Settings->LandscapeMaterialLayerNames[i].ToString();
+ Name = Name.Replace(TEXT(" "), TEXT("")); // Remove whitespaces from Name
+ FString NewAssetName;
+ FString DummyPackageName;
+ AssetToolsModule.Get().CreateUniqueAssetName(PackagePath / Name, TEXT(""), DummyPackageName, NewAssetName);
+ UObject* CreatedAsset = AssetToolsModule.Get().CreateAsset(NewAssetName, PackagePath, ULandscapeLayerInfoObject::StaticClass(), nullptr);
+
+ ULandscapeLayerInfoObject* LayerInfoObj = Cast(CreatedAsset);
+
+ if(LayerInfoObj)
+ {
+ LayerInfoObj->SetLayerName(Settings->LandscapeMaterialLayerNames[i], true);
+ LayerInfoObj->SetBlendMethod(ELandscapeTargetLayerBlendMethod::PremultipliedAlphaBlending, true);
+ }
+ LayerInfoObjects.Add(LayerInfoObj);
+ }
+
+
+ //Set layer names
+ for(int i = 0; i < Settings->LandscapeMaterialLayerNames.Num(); i++)
+ {
+ FLandscapeImportLayerInfo LayerInfo;
+ LayerInfo.LayerName = Settings->LandscapeMaterialLayerNames[i];
+ MaterialImportLayers.Add(LayerInfo);
+ MaterialImportLayers[i].LayerName = LayerInfo.LayerName;
+ }
+
+ //Fill first Layer with data. Layer 1 should never have areas with no data, as that results in black spots in the final material render.
+ MaterialImportLayers[0].LayerData = TArray();
+ MaterialImportLayers[0].SourceFilePath = ""; // Base layer doesn't contain a file
+ MaterialImportLayers[0].LayerInfo = LayerInfoObjects[0];
+ const int32 DataSize = SizeX * SizeY;
+ MaterialImportLayers[0].LayerData.AddUninitialized(DataSize);
+ uint8* ByteData = MaterialImportLayers[0].LayerData.GetData();
+ FMemory::Memset(ByteData, 255, DataSize);
+
+
+ WeightOutImportDescriptors.AddDefaulted(Settings->LandscapeMaterialLayerNames.Num());
+ WeightOutMessage.AddDefaulted(Settings->LandscapeMaterialLayerNames.Num());
+ WeightImportResults.AddDefaulted(Settings->LandscapeMaterialLayerNames.Num());
+
+ if(Settings->WeightmapFilePaths.IsEmpty()) // If the user hasn't touched the File Names order, we create the full file paths now.
+ {
+ for(int i = 0; i < Settings->WeightmapFileNames.Num(); i++)
+ {
+ FString FullPath = FPaths::Combine(*Settings->StoredPath, *Settings->WeightmapFileNames[i]);
+ UE_LOG(GaeaSubsystem, Display, TEXT("Weightmap Full Path: %s"), *FullPath);
+ Settings->WeightmapFilePaths.Add(FullPath);
+ }
+ }
+
+ // The first landscape layer is already filled, so the user should always be using 1 weightmap file less than the total amount of landscape layers.
+ for(int32 i = 1; i < Settings->LandscapeMaterialLayerNames.Num(); i++)
+ {
+ int32 WeightmapIndex = i - 1;
+ if (WeightmapIndex < Settings->WeightmapFilePaths.Num())
+ {
+ WeightImportResults[i] = FLandscapeImportHelper::GetWeightmapImportDescriptor(Settings->WeightmapFilePaths[WeightmapIndex], bSingleFile, Settings->FlipYAxis, Settings->LandscapeMaterialLayerNames[i], WeightOutImportDescriptors[i],WeightOutMessage[i]);
+
+ TArray WeightOutData;
+ FLandscapeImportHelper::GetWeightmapImportData(WeightOutImportDescriptors[i], DescriptorIndex, Settings->LandscapeMaterialLayerNames[i], WeightOutData, WeightOutMessage[i]);
+
+ TArray FinalWeightOutData;
+ FLandscapeImportHelper::TransformWeightmapImportData(WeightOutData, FinalWeightOutData, OutImportDescriptor.ImportResolutions[DescriptorIndex], FLandscapeImportResolution(SizeX, SizeY), ELandscapeImportTransformType::ExpandCentered);
+
+ MaterialImportLayers[i].LayerName = Settings->LandscapeMaterialLayerNames[i];
+ MaterialImportLayers[i].LayerInfo = LayerInfoObjects[i];
+ MaterialImportLayers[i].LayerData = MoveTemp(FinalWeightOutData);
+
+ }
+ }
+
+ UE_LOG(GaeaSubsystem, Display, TEXT("MaterialImportLayers Length: %d"), MaterialImportLayers.Num());
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Misc Settings for Landscape Actor Creation
+
+ const FVector Offset = FTransform(Settings->Rotation,FVector::ZeroVector, Settings->Scale).TransformVector(FVector(-OutComponentCount.X * QuadsPerComponent / 2., -OutComponentCount.Y * QuadsPerComponent / 2., 0.));
+
+ ALandscape* Landscape = World->SpawnActor(Settings->Location + Offset, Settings->Rotation);
+ Landscape->LandscapeMaterial = Settings->LandscapeMaterial;
+ Landscape->SetActorRelativeScale3D(Settings->Scale);
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Create Gaea Landscape Component
+
+ // This component stores filenames on the actor for any potential reimport. More features will eventually be added to this component.
+ UGaeaLandscapeComponent* GaeaComponent = NewObject(Landscape, UGaeaLandscapeComponent::StaticClass(), TEXT("Gaea Landscape Component"));
+
+ if(GaeaComponent)
+ {
+ GaeaComponent->RegisterComponent();
+ Landscape->AddInstanceComponent(GaeaComponent); // Must use this so that the component appears in the landscape actor details panel.
+ Landscape->AddOwnedComponent(GaeaComponent); // Landscape actor will manage the lifetime of the component.
+
+ // Set component height/json paths from our Settings object. We'll use these stored paths for the reimport feature.
+ GaeaComponent->HeightmapFilepath.FilePath = Settings->HeightMapFileName;
+ GaeaComponent->DefinitionFilepath.FilePath = Settings->jsonFileName;
+
+ // Set the weightmap file paths to the file path array on the component
+ if (!Settings->WeightmapFilePaths.IsEmpty())
+ {
+ GaeaComponent->WeightmapFilepaths.SetNum(Settings->WeightmapFilePaths.Num());
+
+ for (int32 i = 0; i < Settings->WeightmapFilePaths.Num(); i++)
+ {
+ GaeaComponent->WeightmapFilepaths[i].FilePath = Settings->WeightmapFilePaths[i];
+ }
+ }
+
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Call Import Function With Height & Weight Data On Landscape Actor
+
+ TMap> HeightmapDataPerLayers;
+ HeightmapDataPerLayers.Add(FGuid(), MoveTemp(FinalHeightData));
+ TMap> MaterialLayerDataPerLayers;
+
+ if(Settings->LandscapeMaterialLayerNames.IsEmpty() || !PathExists || Settings->LandscapeMaterialLayerNames.Num() < 2)
+ {
+ MaterialLayerDataPerLayers.Add(FGuid(), TArray());
+ }
+ else
+ {
+ MaterialLayerDataPerLayers.Add(FGuid(), MaterialImportLayers);
+ }
+
+ // This is the black box function the engine uses to inject all of our height/weight data into our landscape actor.
+ Landscape->Import(FGuid::NewGuid(), 0, 0, SizeX - 1, SizeY - 1, OutSectionsPerComponent, OutQuadsPerSection, HeightmapDataPerLayers, *Settings->HeightMapFileName, MaterialLayerDataPerLayers, ELandscapeImportAlphamapType::Additive, TArrayView());
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Calculate Lighting LOD and Update Final Settings
+
+ // automatically calculate a lighting LOD that won't crash lightmass (hopefully)
+ // < 2048x2048 -> LOD0
+ // >=2048x2048 -> LOD1
+ // >= 4096x4096 -> LOD2
+ // >= 8192x8192 -> LOD3
+ Landscape->StaticLightingLOD = FMath::DivideAndRoundUp(FMath::CeilLogTwo((SizeX * SizeY) / (2048 * 2048) + 1), (uint32)2);
+
+ ULandscapeInfo* LandscapeInfo = Landscape->GetLandscapeInfo();
+ check(LandscapeInfo);
+ LandscapeInfo->UpdateLayerInfoMap(Landscape);
+
+ Landscape->RegisterAllComponents();
+
+ for(int32 i = 0; i < Settings->LandscapeMaterialLayerNames.Num(); i++)
+ {
+ if(MaterialImportLayers[i].LayerInfo != nullptr)
+ {
+ // New method introduced in 5.5 to create Target Layers. Without this, weightmap import will not work.
+ Landscape->AddTargetLayer(MaterialImportLayers[i].LayerName, FLandscapeTargetLayerSettings(MaterialImportLayers[i].LayerInfo, MaterialImportLayers[i].SourceFilePath));
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// World Partition Setup
+
+ if (bIsGridBased && Settings->bIsWorldPartition) // This can be considered a partial WP solution, as a significant portion of the needed code is hidden behind the landscape editor mode and private files. This results in slower creation of landscape proxies and no region volume support.
+ {
+ //Fake scoped task. ChangeGridSize doesn't provide any out data to hook into, so this is really only so the user doesn't think their editor is frozen.
+ FScopedSlowTask SlowTask(100.f, FText::FromString(TEXT("Creating landscape chunks, importing bits and bytes. Might be here for awhile.")));
+ SlowTask.MakeDialog(false);
+ SlowTask.EnterProgressFrame(10.f);
+ LandscapeSubsystem->ChangeGridSize(LandscapeInfo, Settings->WorldPartitionGridSize); // WP function call
+ SlowTask.EnterProgressFrame(90.f);
+ }
+
+ ImporterWindowValidator.Pin()->RequestDestroyWindow();
+}
+
+TArray UGaeaSubsystem::GetLandscapeLayerBlendNodes(UMaterialInterface* MaterialInterface)
+{
+ TArray ConstAllExpressions;
+ TArray OutExpressions;
+
+ if (MaterialInterface && MaterialInterface->GetBaseMaterial())
+ {
+ MaterialInterface->GetBaseMaterial()->GetAllExpressionsOfType(ConstAllExpressions);
+
+ for (const UMaterialExpressionLandscapeLayerBlend* Expression : ConstAllExpressions)
+ {
+ OutExpressions.Add(const_cast(Expression));
+ }
+ }
+ return OutExpressions;
+}
+
+TArray UGaeaSubsystem::GetLandscapeLayerBlendNames(TArray LayerBlends, TArray& Names)
+{
+
+ TArray OutNames;
+
+ for (UMaterialExpressionLandscapeLayerBlend* LayerBlend : LayerBlends)
+ {
+ if (LayerBlend)
+ {
+ for (const FLayerBlendInput& Layer : LayerBlend->Layers)
+ {
+ OutNames.AddUnique(Layer.LayerName);
+ }
+ }
+ }
+ Names = OutNames;
+ return OutNames;
+
+}
+
+void UGaeaSubsystem::GetLandscapeActorProxies(ALandscape* Landscape,TArray& LandscapeStreamingProxies)
+{
+ if (Landscape)
+ {
+ Landscape->GetLandscapeInfo()->ForEachLandscapeProxy([&LandscapeStreamingProxies](ALandscapeProxy* Proxy)
+ {
+ LandscapeStreamingProxies.Add(Proxy);
+ return true;
+ });
+ }
+}
+
+
+
+
+
+#undef LOCTEXT_NAMESPACE
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaUEToolsEditor.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaUEToolsEditor.cpp
new file mode 100644
index 0000000..f6b433c
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaUEToolsEditor.cpp
@@ -0,0 +1,262 @@
+#include "GaeaUEToolsEditor.h"
+#include "GaeaEditorStyle.h"
+#include "EditorStyleSet.h"
+#include "GaeaCommands.h"
+#include "LevelEditor.h"
+#include "Misc/CoreDelegates.h"
+#include "Subsystems/EditorActorSubsystem.h"
+#include "GaeaLandscapeComponent.h"
+#include "Landscape.h"
+#include "ToolMenus.h"
+#include "WorldPartition/WorldPartitionSubsystem.h"
+
+DEFINE_LOG_CATEGORY(GaeaUETools);
+
+#define LOCTEXT_NAMESPACE "FGaeaUEToolsEditorModule"
+
+void FGaeaUEToolsEditorModule::StartupModule()
+{
+ FGaeaEditorStyle::Initialize();
+ FGaeaCommands::Register();
+ /*UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(
+ this, &FGaeaUEToolsEditorModule::RegisterMCWindow));*/ // Register Material Creator Callback
+
+ UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(
+ this, &FGaeaUEToolsEditorModule::RegisterGaeaActorMenu));
+
+ /*UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(
+ this, &FGaeaUEToolsEditorModule::RegisterLandscapeActorMenu));*/
+
+
+ UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(
+ this, &FGaeaUEToolsEditorModule::RegisterImporterWindow)); // Register Importer Callback
+
+ // Bind the commands
+ const FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked("LevelEditor");
+ const TSharedRef Commands = LevelEditor.GetGlobalLevelEditorActions();
+
+ Commands->MapAction(
+ FGaeaCommands::Get().OpenImporter,
+ FExecuteAction::CreateLambda([this]()
+ {
+
+ UE_LOG(LogTemp, Log, TEXT("Opening Gaea Landscape Importer Window"));
+ if (UGaeaSubsystem* GSubsystem = UGaeaSubsystem::GetGaeaSubsystem())
+ {
+ //UE_LOG(LogTemp, Log, TEXT("Opened Gaea Landscape Importer Window."));
+ GSubsystem->SpawnGImporterWindow();
+ }
+
+ })
+ );
+
+}
+
+void FGaeaUEToolsEditorModule::ShutdownModule()
+{
+ FGaeaEditorStyle::Shutdown();
+ FGaeaCommands::Unregister();
+ UToolMenus::UnregisterOwner(this); //Unregister menu entry
+}
+
+/*void FGaeaUEToolsEditorModule::RegisterMCWindow()
+{
+ //Register owner for menu entry
+ FToolMenuOwnerScoped OwnerScoped(this);
+
+ //Set our menu entry name
+ constexpr TCHAR ParentMenuName[] = TEXT("ContentBrowser.Toolbar");
+ UToolMenu* GaeaToolBar = UToolMenus::Get()->ExtendMenu(ParentMenuName);
+ GaeaToolBar->StyleName = TEXT("GaeaToolbar");
+
+ //Find the section of the editor menu we want to add to
+ //FToolMenuSection& Section = GaeaToolBar->FindOrAddSection("Tools");
+ FToolMenuSection& Section = GaeaToolBar->AddSection(TEXT("Gaea Material Creator"), LOCTEXT("GaeaMaterialTools_Label", "GaeaMaterialTools"));
+
+ //Create and define our UI action
+ FToolUIAction OpenGaeaMaterialAction;
+ OpenGaeaMaterialAction.ExecuteAction = FToolMenuExecuteAction::CreateLambda([](const FToolMenuContext& InContext)
+ {
+ if ( UGaeaSubsystem* Manager = UGaeaSubsystem::GetGaeaSubsystem())
+ {
+ Manager->SpawnGMCWindow(); // Spawn Material Creator Window
+ }
+ }
+ );
+
+ //Create our custom toolbar entry
+ const FToolMenuEntry OpenGaeaLandscapeMaterialCreator = FToolMenuEntry::InitToolBarButton(
+ TEXT("OpenGaeaLandscapeMaterialCreator"),
+ OpenGaeaMaterialAction,
+ LOCTEXT("OpenGaeaLandscapeMaterialCreator_Label", "Gaea Landscape Material Creation"),
+ LOCTEXT("OpenGaeaLandscapeMaterialCreator_Label_Description", "Opens Gaea Material Creator"),
+ FSlateIcon(FAppStyle::Get().GetStyleSetName(), TEXT("ClassIcon.Material")))
+ ;
+
+ //Add our custom menu entry to our previously selected menu section
+ Section.AddEntry(OpenGaeaLandscapeMaterialCreator);
+}*/
+
+void FGaeaUEToolsEditorModule::RegisterImporterWindow()
+{
+ //Register owner for menu entry
+ FToolMenuOwnerScoped OwnerScoped(this);
+
+ //Set our menu entry name
+ constexpr TCHAR ParentMenuName[] = TEXT("LevelEditor.LevelEditorToolBar.User");
+ UToolMenu* GaeaToolBar = UToolMenus::Get()->ExtendMenu(ParentMenuName);
+ GaeaToolBar->StyleName = TEXT("GaeaToolbar");
+
+ //Find the section of the editor menu we want to add to
+ //FToolMenuSection& Section = GaeaToolBar->FindOrAddSection("User");
+ FToolMenuSection& Section = GaeaToolBar->AddSection(TEXT("Gaea Landscape Importer"), LOCTEXT("GaeaLandscapeImporter_Label", "GaeaImporterTools"));
+
+ //Create and define our UI action
+ FToolUIAction OpenGaeaImporter;
+ OpenGaeaImporter.ExecuteAction = FToolMenuExecuteAction::CreateLambda([](const FToolMenuContext& InContext)
+ {
+ if ( UGaeaSubsystem* Manager = UGaeaSubsystem::GetGaeaSubsystem())
+ {
+ Manager->SpawnGImporterWindow(); // Spawn Importer Window
+ }
+ }
+ );
+
+ //Create our custom toolbar entry - OLD. Does not work with editor shortcut key.
+ /*const FToolMenuEntry OpenGaeaLandscapeImporter = FToolMenuEntry::InitToolBarButton(
+ TEXT("OpenGaeaLandscapeImporter"),
+ OpenGaeaImporter,
+ LOCTEXT("OpenGaeaLandscapeImporter_Label", "Gaea Landscape Importer"),
+ LOCTEXT("OpenGaeaLandscapeImporter_Label_Description", "Opens Gaea Landscape Importer"),
+ //FSlateIcon(FAppStyle::Get().GetStyleSetName(), TEXT("ClassIcon.LandscapeComponent")));
+ FSlateIcon(FGaeaEditorStyle::GetStyleSetName(), TEXT("ImporterIcon"))
+ );*/
+
+ const FToolMenuEntry OpenGaeaLandscapeImporter = FToolMenuEntry::InitToolBarButton(
+ FGaeaCommands::Get().OpenImporter,
+ LOCTEXT("OpenGaeaLandscapeImporter_Label", "Gaea Landscape Importer"),
+ LOCTEXT("OpenGaeaLandscapeImporter_Label_Description", "Opens Gaea Landscape Importer"),
+ FSlateIcon(FGaeaEditorStyle::GetStyleSetName(), TEXT("ImporterIcon"))
+ );
+
+
+ //Add our custom menu entry to our previously selected menu section
+ Section.AddEntry(OpenGaeaLandscapeImporter);
+}
+
+void FGaeaUEToolsEditorModule::RegisterGaeaActorMenu()
+{
+ // Register owner for menu entry
+ FToolMenuOwnerScoped OwnerScoped(this);
+
+ // Create menu entry button in a new sub-menu
+ UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.ActorContextMenu");
+ Menu->AddDynamicSection("GaeaOptions", FNewToolMenuDelegate::CreateLambda([this](UToolMenu* InMenu)
+{
+ // This delegate is called when it is time to create the menu section
+ UEditorActorSubsystem* ActorSubsystem = GEditor->GetEditorSubsystem();
+ if (ActorSubsystem)
+ {
+ const TArray& SelectedActors = ActorSubsystem->GetSelectedLevelActors();
+ if (SelectedActors.Num() > 0)
+ {
+ AActor* Actor = SelectedActors[0];
+ if (Actor && Actor->GetClass()->IsChildOf(ALandscape::StaticClass()))
+ {
+ if (UGaeaLandscapeComponent* GaeaComponent = Actor->FindComponentByClass())
+ {
+ // If a Landscape is selected, then add an item to the section
+ FToolMenuSection& Section = InMenu->AddSection("GaeaOptionsSection",
+ FText::FromString("Gaea Options"),
+ FToolMenuInsert("ActorGeneral", EToolMenuInsertType::Before));
+
+ Section.AddSubMenu(
+ "GaeaLandscapeSubMenu",
+ FText::FromString("Gaea Landscape Actions"),
+ FText::FromString("Contains various actions for landscapes imported from Gaea"),
+ FNewMenuDelegate::CreateRaw(this, &FGaeaUEToolsEditorModule::GaeaActorActions),
+ false,
+ FSlateIcon(FGaeaEditorStyle::GetStyleSetName(), TEXT("ImporterIcon"))
+ );
+
+ }
+ }
+ }
+ }
+}));
+
+}
+
+void FGaeaUEToolsEditorModule::GaeaActorActions(FMenuBuilder& MenuBuilder)
+{
+ FUIAction ExecuteReimportGaeaLandscape(
+ FExecuteAction::CreateLambda([]() {
+ if (UGaeaSubsystem* Manager = UGaeaSubsystem::GetGaeaSubsystem())
+ {
+ UWorld* World = GEditor->GetEditorWorldContext().World();
+ if (World)
+ {
+ if (World->IsPartitionedWorld())
+ {
+ Manager->ReimportGaeaWPTerrain(); // Reimport for World Partition
+ }
+ else
+ {
+ Manager->ReimportGaeaTerrain(); // Reimport for standard landscape
+ }
+ } // ReimportTerrain
+ }
+ })
+);
+
+ MenuBuilder.AddMenuEntry(
+ FText::FromString("Refresh Landscape in Place"),
+ FText::FromString("Clears current landscape actor internal data and fills with the associated heightmap/weightmaps"),
+ FSlateIcon(),
+ ExecuteReimportGaeaLandscape
+ );
+}
+
+void FGaeaUEToolsEditorModule::RegisterLandscapeActorMenu()
+{
+ FToolMenuOwnerScoped OwnerScoped(this);
+
+ UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorSceneOutliner.ContextMenu");
+
+ FToolMenuSection& Section = Menu->FindOrAddSection("ActorOptions");
+
+ Section.AddDynamicEntry("DeleteWPLandscapeEntry", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& Section)
+ {
+ FToolMenuEntry Entry = FToolMenuEntry::InitMenuEntry(
+ "DeleteWPLandscapeEntry",
+ FText::FromString("Delete World Partitioned Landscape"),
+ FText::FromString("Deletes selected landscape and its proxies."),
+ FSlateIcon(FGaeaEditorStyle::GetStyleSetName(), "ImporterIcon"),
+ FUIAction(
+ FExecuteAction::CreateLambda([]()
+ {
+ if (UGaeaSubsystem* Subsystem = UGaeaSubsystem::GetGaeaSubsystem())
+ {
+ //Subsystem->DeleteWPLandscape();
+ }
+ }),
+ FCanExecuteAction::CreateLambda([]()
+ {
+ UEditorActorSubsystem* ActorSubsystem = GEditor->GetEditorSubsystem();
+ const TArray& Selected = ActorSubsystem->GetSelectedLevelActors();
+ const UWorld* World = GEditor->GetEditorWorldContext().World();
+ const bool bWP = World->IsPartitionedWorld();
+ return Selected.Num() > 0 && Selected[0]->IsA() && bWP;
+ })
+ )
+ );
+
+ Entry.InsertPosition = FToolMenuInsert("EditSubMenu", EToolMenuInsertType::After);
+ Section.AddEntry(Entry);
+ }));
+}
+
+
+#undef LOCTEXT_NAMESPACE
+
+IMPLEMENT_MODULE(FGaeaUEToolsEditorModule, GaeaUEToolsEditor)
\ No newline at end of file
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaWindow.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaWindow.cpp
new file mode 100644
index 0000000..5378406
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/GaeaWindow.cpp
@@ -0,0 +1,14 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "GaeaWindow.h"
+
+SGaeaWindow::SGaeaWindow()
+{
+
+}
+
+SGaeaWindow::~SGaeaWindow()
+{
+
+}
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/ImporterPanelSettings.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/ImporterPanelSettings.cpp
new file mode 100644
index 0000000..15b97b1
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Private/ImporterPanelSettings.cpp
@@ -0,0 +1,39 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "ImporterPanelSettings.h"
+#include "GaeaSubsystem.h"
+
+UImporterPanelSettings::UImporterPanelSettings()
+{
+}
+
+UImporterPanelSettings::~UImporterPanelSettings()
+{
+}
+
+void UImporterPanelSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
+{
+ FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
+
+ if (PropertyName == GET_MEMBER_NAME_CHECKED(UImporterPanelSettings, WeightmapFileNames))
+ {
+ WeightmapFilePaths.Empty();
+ for(int i = 0; i < WeightmapFileNames.Num(); i++)
+ {
+ FString FullPath = FPaths::Combine(*StoredPath, *WeightmapFileNames[i]);
+ WeightmapFilePaths.Add(FullPath); // When WeightmapFileNames is reordered or changed, we mutate WeightmapFilePaths to generate the full paths. This is for UX benefits, and the paths will be used later with the actual layer system.
+
+ }
+ }
+ else if (PropertyName == GET_MEMBER_NAME_CHECKED(UImporterPanelSettings, LandscapeMaterial))
+ {
+ UGaeaSubsystem* Manager = UGaeaSubsystem::GetGaeaSubsystem();
+ TArray BlendNodes = Manager->GetLandscapeLayerBlendNodes(LandscapeMaterial);
+ Manager->GetLandscapeLayerBlendNames(BlendNodes, LandscapeMaterialLayerNames);
+ }
+
+
+ Super::PostEditChangeProperty(PropertyChangedEvent);
+}
+
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GMCSettings.h b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GMCSettings.h
new file mode 100644
index 0000000..3c665a3
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GMCSettings.h
@@ -0,0 +1,64 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Materials/MaterialInstance.h"
+#include "GMCSettings.generated.h"
+
+/**
+ *
+ */
+
+UENUM()
+enum class ELandscapeBlendLayersType : uint8
+{
+ LB_WeightedBlend,
+ LB_AlphaBlends,
+ LB_HeightBlends,
+};
+
+USTRUCT(BlueprintType)
+struct GAEAUETOOLSEDITOR_API FGaeaLandscapeSetting
+{
+ GENERATED_BODY()
+
+ // Material to pull textures from. Used with MS materials, mostly.
+ UPROPERTY(EditAnywhere, Category="Gaea")
+ UMaterialInstance* InstancedMaterial = nullptr;
+
+ // Function to create "instanced" layer functions from to inject into the landscape master material
+ UPROPERTY(EditAnywhere, Category="Gaea")
+ UMaterialFunction* MaterialFunctionBase = nullptr;
+
+ // Sets the landscape layer name and layer function name
+ UPROPERTY(EditAnywhere, Category="Gaea")
+ FName LayerName;
+
+ // Sets the parameter grouping within the function layer
+ UPROPERTY(EditAnywhere, Category="Gaea")
+ FName LayerGrouping;
+
+ // Sets the landscape layer blend type
+ UPROPERTY(EditAnywhere, Category="Gaea")
+ ELandscapeBlendLayersType LandscapeLayerType = ELandscapeBlendLayersType::LB_WeightedBlend;
+
+
+};
+UCLASS()
+class GAEAUETOOLSEDITOR_API UGMCSettings : public UObject
+{
+ GENERATED_BODY()
+public:
+ UGMCSettings();
+ ~UGMCSettings();
+
+ UPROPERTY(EditAnywhere, Category="Landscape Layer Creation settings")
+ TArray LandscapeLayerSettings;
+
+ UPROPERTY(EditAnywhere, DisplayName="Material Save Location",meta = (ContentDir), Category="Landscape Creation settings")
+ FDirectoryPath ContentBrowserPath;
+
+ UPROPERTY(EditAnywhere, Category="Landscape Creation settings")
+ FString LandscapeMaterialName;
+};
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GWindow.h b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GWindow.h
new file mode 100644
index 0000000..32e6b83
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GWindow.h
@@ -0,0 +1,69 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "Widgets/SWindow.h"
+#include "DesktopPlatformModule.h"
+#include "ImporterPanelSettings.h"
+#include "CoreMinimal.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(GaeaWindow, Log, All);
+
+/*-----------------------------------------------------------------------------
+ Material Creator Window Class
+-----------------------------------------------------------------------------*/
+class GAEAUETOOLSEDITOR_API SMCWindow : public SWindow
+{
+public:
+ SMCWindow();
+ ~SMCWindow();
+
+ SLATE_BEGIN_ARGS(SMCWindow)
+ {
+ Title(FText::GetEmpty());
+ ClientSize(FVector2d(800,600));
+ SizingRule(ESizingRule::UserSized);
+ }
+
+ SLATE_ARGUMENT(FText, Title);
+ SLATE_ARGUMENT(FVector2D, ClientSize);
+ SLATE_ARGUMENT(ESizingRule, SizingRule);
+
+ SLATE_END_ARGS()
+};
+
+/*-----------------------------------------------------------------------------
+ Import Window Class
+-----------------------------------------------------------------------------*/
+
+class GAEAUETOOLSEDITOR_API SGaeaImportWindow : public SWindow
+{
+public:
+ SGaeaImportWindow();
+ ~SGaeaImportWindow();
+
+ SLATE_BEGIN_ARGS(SGaeaImportWindow)
+ {
+ Title(FText::GetEmpty());
+ ClientSize(FVector2d(800,600));
+ SizingRule(ESizingRule::UserSized);
+ }
+
+ SLATE_ARGUMENT(FText, Title);
+ SLATE_ARGUMENT(FVector2D, ClientSize);
+ SLATE_ARGUMENT(ESizingRule, SizingRule);
+
+ SLATE_END_ARGS()
+
+ void Construct(const FArguments& InArgs);
+
+ void CreateDetailsView();
+
+private:
+
+ UImporterPanelSettings* ImporterSettings = nullptr;
+ TSharedPtr PropertyWidget;
+ FReply OnImportClicked() const;
+ FReply OnCreateLandscapeClicked();
+
+};
\ No newline at end of file
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaCommands.h b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaCommands.h
new file mode 100644
index 0000000..4053e91
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaCommands.h
@@ -0,0 +1,23 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#pragma once
+
+#include "Framework/Commands/Commands.h"
+#include "EditorStyleSet.h"
+
+
+/**
+ *
+ */
+class GAEAUETOOLSEDITOR_API FGaeaCommands : public TCommands
+{
+public:
+
+ FGaeaCommands() : TCommands(TEXT("Gaea"), NSLOCTEXT("Contexts", "Gaea", "Gaea Commands"), NAME_None, FAppStyle::GetAppStyleSetName())
+ {
+ }
+
+ virtual void RegisterCommands() override;
+
+ TSharedPtr OpenImporter;
+ TSharedPtr DeleteSelectedWPLandscape;
+};
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaEditorStyle.h b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaEditorStyle.h
new file mode 100644
index 0000000..7a70c1b
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaEditorStyle.h
@@ -0,0 +1,27 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "Styling/SlateStyle.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(GaeaEditorStyle, Log, All);
+
+class FGaeaEditorStyle final
+{
+public:
+
+ static void Initialize();
+ static void Shutdown();
+
+ static ISlateStyle& Get()
+ {
+ return *StyleSet.Get();
+ }
+
+ static FName GetStyleSetName();
+
+private:
+
+ static FString RootToPluginContentDir(const FString& RelativePath, const TCHAR* Extension);
+
+ static TUniquePtr StyleSet;
+};
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaLandscapeComponent.h b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaLandscapeComponent.h
new file mode 100644
index 0000000..41cd887
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaLandscapeComponent.h
@@ -0,0 +1,44 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Components/ActorComponent.h"
+#include "GaeaLandscapeComponent.generated.h"
+
+class ULandscapeLayerInfoObject;
+
+UCLASS(ClassGroup=(Custom), meta=(NotBlueprintable, NotBlueprintType))
+class GAEAUETOOLSEDITOR_API UGaeaLandscapeComponent final : public UActorComponent
+{
+ GENERATED_BODY()
+
+public:
+ // Sets default values for this component's properties
+ UGaeaLandscapeComponent();
+
+ UPROPERTY(EditAnywhere, Category="Gaea Landscape")
+ bool LockProperties = true;
+
+ UPROPERTY(EditAnywhere, Category = "Gaea Landscape", meta = (FilePathFilter = "png", EditCondition = "!LockProperties"))
+ FFilePath HeightmapFilepath;
+
+ UPROPERTY(EditAnywhere, Category = "Gaea Landscape", meta = (FilePathFilter = "json", EditCondition = "!LockProperties"))
+ FFilePath DefinitionFilepath;
+
+ UPROPERTY(EditAnywhere, Category = "Gaea Landscape", meta = (FilePathFilter = "png", EditCondition = "!LockProperties"))
+ TArray WeightmapFilepaths;
+
+
+protected:
+ // Called when the game starts
+ virtual void BeginPlay() override;
+
+public:
+ // Called every frame
+ virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
+
+ virtual void DestroyComponent(bool bPromoteChildren = false) override;
+
+
+};
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaSubsystem.h b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaSubsystem.h
new file mode 100644
index 0000000..724301b
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaSubsystem.h
@@ -0,0 +1,124 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "Math/MathFwd.h"
+#include "EditorSubsystem.h"
+#include "Modules/ModuleManager.h"
+#include "IDetailsView.h"
+#include "Input/Reply.h"
+#include "IDesktopPlatform.h"
+//#include "GaeaUEFunctionLibrary.h"
+//#include "GMCSettings.h"
+#include "ImporterPanelSettings.h"
+#include "LandscapeImportHelper.h"
+#include "LandscapeTiledImage.h"
+#include "Landscape.h"
+#include "GaeaSubsystem.generated.h"
+
+/**
+ *
+ */
+
+DECLARE_LOG_CATEGORY_EXTERN(GaeaSubsystem, Log, All);
+
+class FJsonObject;
+
+USTRUCT()
+struct FGaeaJson
+{
+ GENERATED_BODY()
+
+ UPROPERTY()
+ FString ID = TEXT("");
+ UPROPERTY()
+ int32 Resolution = 0;
+ UPROPERTY()
+ float ScaleX = 1.0f;
+ UPROPERTY()
+ float ScaleY = 1.0f;
+ UPROPERTY()
+ float ScaleZ = 1.0f;
+ UPROPERTY()
+ float Height = 0.0f;
+ UPROPERTY()
+ FString Unit = TEXT("");
+
+};
+
+UCLASS(NotBlueprintable)
+class GAEAUETOOLSEDITOR_API UGaeaSubsystem final : public UEditorSubsystem
+{
+ GENERATED_BODY()
+public:
+
+ // Get instance of the Gaea Subsystem
+ UFUNCTION()
+ static UGaeaSubsystem* GetGaeaSubsystem();
+
+ // Spawn the Gaea Material Creator Window - future release
+ /*UFUNCTION(Category="Gaea")
+ void SpawnGMCWindow();*/
+
+ // Spawn the Gaea Landscape Creator Window
+ UFUNCTION()
+ void SpawnGImporterWindow();
+
+ UFUNCTION()
+ void ReimportGaeaTerrain();
+
+ UFUNCTION()
+ void ReimportGaeaWPTerrain();
+
+ // Open import dialog for heightmap file types. Will set a path for heightmap and json files.
+ UFUNCTION()
+ void ImportHeightmap(FString& Heightmap, FString& JSON, FVector& Scale, FVector& Location, TArray& Weightmaps, FString& CachedPath);
+
+ UFUNCTION()
+ FString ReadStringFromFile(FString Path, bool& bOutSuccess, FString& OutMessage);
+
+ TSharedPtr ReadJson(FString Path, bool& bOutSuccess, FString& OutMessage);
+
+ UFUNCTION()
+ FGaeaJson CreateStructFromJson(FString Path, bool& bOutSuccess, FString& OutMessage);
+
+ UFUNCTION()
+ ALandscape* GetLandscape(ULandscapeInfo* LandscapeInfo) const;
+
+ //FGuid GetLayerGuidFromIndex(int32 Index, ULandscapeInfo* LandscapeInfo) const;
+
+ //Creates a landscape actor from our panel settings.
+ UFUNCTION()
+ void CreateLandscapeActor(UImporterPanelSettings* Settings);
+
+ UFUNCTION()
+ TArray GetLandscapeLayerBlendNodes(UMaterialInterface* MaterialInterface);
+
+ UFUNCTION()
+ TArray GetLandscapeLayerBlendNames(TArray LayerBlends, TArray& Names);
+
+ UFUNCTION()
+ void GetLandscapeActorProxies(ALandscape* Landscape, TArray& LandscapeStreamingProxies);
+
+
+private:
+
+ UPROPERTY()
+ UImporterPanelSettings* ImporterSettings = nullptr;
+
+ /*UPROPERTY()
+ UGMCSettings* PanelSettings = nullptr;*/
+
+ UPROPERTY()
+ FString DefaultDialogPath;
+
+ TSharedPtr PropertyWidget;
+ TWeakPtr WindowValidator;
+ TWeakPtr ImporterWindowValidator;
+
+
+};
+
+
+
+
+
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaUEToolsEditor.h b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaUEToolsEditor.h
new file mode 100644
index 0000000..95e68e3
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaUEToolsEditor.h
@@ -0,0 +1,39 @@
+#pragma once
+
+//#include "CoreMinimal.h"
+#include "Modules/ModuleInterface.h"
+#include "Modules/ModuleManager.h"
+#include "GaeaSubsystem.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(GaeaUETools, Log, All);
+
+class FToolBarBuilder;
+class FMenuBuilder;
+
+class FGaeaUEToolsEditorModule : public IModuleInterface
+{
+public:
+
+ virtual void StartupModule() override;
+ virtual void ShutdownModule() override;
+
+
+ // void RegisterMCWindow();
+
+ // Primary importer window
+ void RegisterImporterWindow();
+
+ // Submenu for all landscape actor options
+ void RegisterGaeaActorMenu();
+
+ // Entry for refreshing landscape from Gaea heightmap and json
+ void GaeaActorActions(FMenuBuilder& MenuBuilder);
+
+ void RegisterLandscapeActorMenu();
+
+
+private:
+
+};
+
+
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaWindow.h b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaWindow.h
new file mode 100644
index 0000000..5600b43
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/GaeaWindow.h
@@ -0,0 +1,56 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "Widgets/SWindow.h"
+#include "CoreMinimal.h"
+
+/**
+ *
+ */
+
+class GAEAUETOOLSEDITOR_API SGaeaWindow : public SWindow
+{
+
+public:
+ SGaeaWindow();
+ virtual ~SGaeaWindow() override;
+
+ SLATE_BEGIN_ARGS(SGaeaWindow)
+ {
+ Title(FText::GetEmpty());
+ ClientSize(FVector2d(800,600));
+ SizingRule(ESizingRule::FixedSize);
+ }
+
+ SLATE_ARGUMENT(FText, Title);
+ SLATE_ARGUMENT(FVector2D, ClientSize);
+ SLATE_ARGUMENT(ESizingRule, SizingRule);
+
+
+ SLATE_END_ARGS()
+
+ void Construct(const FArguments& InArgs)
+ {
+ SWindow::Construct(SWindow::FArguments()
+ .Title(InArgs._Title)
+ .ClientSize(InArgs._ClientSize)
+ .SizingRule(InArgs._SizingRule));
+
+ }
+
+
+ bool bClosed = false;
+
+ FOnWindowClosed WindowStatus;
+
+
+protected:
+
+
+
+
+
+};
+
+
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/ImporterPanelSettings.h b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/ImporterPanelSettings.h
new file mode 100644
index 0000000..a635176
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUEToolsEditor/Public/ImporterPanelSettings.h
@@ -0,0 +1,87 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "Materials/MaterialInterface.h"
+#include "ImporterPanelSettings.generated.h"
+
+/**
+ *
+ */
+UCLASS(NotBlueprintable)
+class GAEAUETOOLSEDITOR_API UImporterPanelSettings final : public UObject
+{
+ GENERATED_BODY()
+
+public:
+ UImporterPanelSettings();
+ ~UImporterPanelSettings();
+
+
+ UPROPERTY(VisibleAnywhere, DisplayName="Heightmap Filename", Category="Filepaths")
+ FString HeightMapFileName;
+
+ UPROPERTY(VisibleAnywhere, DisplayName="JSON Filename", Category="Filepaths")
+ FString jsonFileName;
+
+ //Reorder these to match the desired landscape layer. This must always be less than the amount of layers provided by the landscape material.
+ UPROPERTY(EditAnywhere, DisplayName= "Weightmap Filenames", Category="Filepaths")
+ TArray WeightmapFileNames;
+
+ UPROPERTY()
+ TArray WeightmapFilePaths;
+
+ UPROPERTY()
+ FString StoredPath;
+
+ UPROPERTY()
+ bool EnableEditLayers = true;
+
+ UPROPERTY(EditAnywhere, DisplayName="Flip Y Axis", Category="Landscape Actor Settings")
+ bool FlipYAxis;
+
+ UPROPERTY(EditAnywhere, Category="Landscape Actor Settings|World Partition", DisplayName="Enable World Partition")
+ bool bIsWorldPartition = true;
+
+ // Used only if the level has world partition support.
+ UPROPERTY(EditAnywhere, Category="Landscape Actor Settings|World Partition", meta = (EditCondition = "bIsWorldPartition", UIMin="1", UIMax="16", ClampMin="1", ClampMax="16"))
+ int32 WorldPartitionGridSize = 2;
+
+ //Must be set to automatically setup layer weightmaps.
+ UPROPERTY(Category="Rendering", EditAnywhere, DisplayName="Landscape Material")
+ TObjectPtr LandscapeMaterial;
+
+ UPROPERTY(Category="Rendering", VisibleAnywhere, DisplayName="Landscape Layer Names", meta = (EditCondition = "IsLandscapeMaterialLayerNamesNotEmpty()"))
+ TArray LandscapeMaterialLayerNames;
+
+ UFUNCTION()
+ bool IsLandscapeMaterialLayerNamesNotEmpty() const
+ {
+ return LandscapeMaterialLayerNames.Num() > 0;
+ }
+
+ //Where to create layer info objects.
+ UPROPERTY(Category="Rendering", EditAnywhere, meta = (ContentDir), DisplayName="Layer Info Folder")
+ FDirectoryPath LayerInfoFolder;
+
+ UPROPERTY(EditAnywhere, Category="Transform")
+ FVector Location = FVector(0,0,100);
+
+ UPROPERTY(EditAnywhere, Category="Transform")
+ FRotator Rotation;
+
+ UPROPERTY(EditAnywhere, Category="Transform")
+ FVector Scale = FVector(100,100,100);
+
+ UPROPERTY()
+ FIntPoint Components;
+
+ UPROPERTY()
+ FIntPoint Resolution;
+
+ UPROPERTY()
+ int32 TotalComponents;
+
+ virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
+
+};
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUnrealTools/GaeaUnrealTools.Build.cs b/Plugins/Gaea2Unreal-main/Source/GaeaUnrealTools/GaeaUnrealTools.Build.cs
new file mode 100644
index 0000000..58deadb
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUnrealTools/GaeaUnrealTools.Build.cs
@@ -0,0 +1,53 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+using UnrealBuildTool;
+
+public class GaeaUnrealTools : ModuleRules
+{
+ public GaeaUnrealTools(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PublicIncludePaths.AddRange(
+ new string[] {
+ // ... add public include paths required here ...
+ }
+ );
+
+
+ PrivateIncludePaths.AddRange(
+ new string[] {
+ // ... add other private include paths required here ...
+ }
+ );
+
+
+ PublicDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "Core",
+ // ... add other public dependencies that you statically link with here ...
+ }
+ );
+
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "CoreUObject",
+ "Engine",
+ "Slate",
+ "SlateCore",
+ // ... add private dependencies that you statically link with here ...
+ }
+ );
+
+
+ DynamicallyLoadedModuleNames.AddRange(
+ new string[]
+ {
+ // ... add any modules that your module loads dynamically here ...
+ }
+ );
+ }
+}
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUnrealTools/Private/GaeaUnrealTools.cpp b/Plugins/Gaea2Unreal-main/Source/GaeaUnrealTools/Private/GaeaUnrealTools.cpp
new file mode 100644
index 0000000..155bbe5
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUnrealTools/Private/GaeaUnrealTools.cpp
@@ -0,0 +1,20 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "GaeaUnrealTools.h"
+
+#define LOCTEXT_NAMESPACE "FGaeaUnrealToolsModule"
+
+void FGaeaUnrealToolsModule::StartupModule()
+{
+ // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
+}
+
+void FGaeaUnrealToolsModule::ShutdownModule()
+{
+ // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
+ // we call this function before unloading the module.
+}
+
+#undef LOCTEXT_NAMESPACE
+
+IMPLEMENT_MODULE(FGaeaUnrealToolsModule, GaeaUnrealTools)
\ No newline at end of file
diff --git a/Plugins/Gaea2Unreal-main/Source/GaeaUnrealTools/Public/GaeaUnrealTools.h b/Plugins/Gaea2Unreal-main/Source/GaeaUnrealTools/Public/GaeaUnrealTools.h
new file mode 100644
index 0000000..7c29ee8
--- /dev/null
+++ b/Plugins/Gaea2Unreal-main/Source/GaeaUnrealTools/Public/GaeaUnrealTools.h
@@ -0,0 +1,15 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Modules/ModuleManager.h"
+
+class FGaeaUnrealToolsModule : public IModuleInterface
+{
+public:
+
+ /** IModuleInterface implementation */
+ virtual void StartupModule() override;
+ virtual void ShutdownModule() override;
+};
diff --git a/Plugins/VisualStudioTools/.gitignore b/Plugins/VisualStudioTools/.gitignore
new file mode 100644
index 0000000..ee4287e
--- /dev/null
+++ b/Plugins/VisualStudioTools/.gitignore
@@ -0,0 +1,421 @@
+## Based on VisualStudio and UnrealEngine templates
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+## Get latest from https://github.com/github/gitignore/blob/main/UnrealEngine.gitignore
+
+## VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+## UnrealEngine.gitignore
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+*.ipa
+
+# These project files can be generated by the engine
+*.xcodeproj
+*.xcworkspace
+*.opensdf
+*.sdf
+
+# Precompiled Assets
+SourceArt/**/*.png
+SourceArt/**/*.tga
+
+# Binary Files
+Binaries/*
+Plugins/*/Binaries/*
+
+# Builds
+Build/*
+
+# Whitelist PakBlacklist-.txt files
+!Build/*/
+Build/*/**
+!Build/*/PakBlacklist*.txt
+
+# Don't ignore icon files in Build
+!Build/**/*.ico
+
+# Built data for maps
+*_BuiltData.uasset
+
+# Configuration files generated by the Editor
+Saved/*
+
+# Compiled source files for the engine to use
+Intermediate/*
+Plugins/*/Intermediate/*
+
+# Cache files for the editor to use
+DerivedDataCache/*
diff --git a/Plugins/VisualStudioTools/CODE_OF_CONDUCT.md b/Plugins/VisualStudioTools/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..c72a574
--- /dev/null
+++ b/Plugins/VisualStudioTools/CODE_OF_CONDUCT.md
@@ -0,0 +1,9 @@
+# Microsoft Open Source Code of Conduct
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
+
+Resources:
+
+- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
+- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
+- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
diff --git a/Plugins/VisualStudioTools/CONTRIBUTING.md b/Plugins/VisualStudioTools/CONTRIBUTING.md
new file mode 100644
index 0000000..498ebb7
--- /dev/null
+++ b/Plugins/VisualStudioTools/CONTRIBUTING.md
@@ -0,0 +1,49 @@
+# Contributing
+
+This project welcomes contributions and suggestions. Most contributions require you to agree to a
+Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
+the rights to use your contribution. For details, visit .
+
+When you submit a pull request, a CLA bot will automatically determine whether you need to provide
+a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
+provided by the bot. You will only need to do this once across all repos using our CLA.
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
+For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
+contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+
+## Code Style Guide
+
+The code in the repo follows the existing code conventions described in the Unreal Engine's [Code Standard document](https://docs.unrealengine.com/INT/epic-cplusplus-coding-standard-for-unreal-engine/). The `.editorconfig` file at the source root is used for Visual Studio to check the conventions and report violations.
+
+## Pull Requests
+
+When submitting a pull request, make sure that it has a clean build using the instructions below. A core contributor will review your pull request and provide feedback. Once all the feedback is addressed and the PR is approved, we will merge the changes.
+
+## Build workflow
+The plugin source can be built in isolation using the command below (which wrap the RunUAT.bat script) to ensure it's correct for submition to the Unreal Engine Marketplace.
+
+From a Visual Studio Developer Prompt (or PowerShell Dev Prompt), run the following:
+
+```cmd
+> msbuild -p:UnrealEngine=[path_or_version] -p:OutputPath=[absolute_path]
+``````
+
+- `UnrealEngine` can be either a path to a source build (e.g. `c:\src\ue`) or a version identifier for an installed engine (e.g. `4.27`, `5.2`).
+- `OutputPath` cannot be under the Unreal Engine's folder due to a restriction from `RunUAT.bat`.
+
+> Note: The contents of `OutputPath` will be overwritten!
+
+By default the script will disable Unity Builds in the plugin modules to catch errors from cpp files not including all the required headers. It does not affect the build of other targets and modules.
+
+## Unity build errors
+
+If you get errors due to unity build problems, you get the same errors in Visual Studio by generating the solution with the command below. This will allow Visual Studio to suggest the includes as code fixes. Note that this will overwrite any existing solution and projects that are already present.
+
+```powershell
+$env:VSTUE_IsCustomDevBuild=1; & "C:\Program Files\Epic Games\UE_5.2\Engine\Build\BatchFiles\Build.bat" -projectfiles -project="full_path_to_game.uproject" -game
+```
+
+The module rules for the plugin check the enviroment variable above to use the more strict include settings.
+
+
diff --git a/Plugins/VisualStudioTools/Config/FilterPlugin.ini b/Plugins/VisualStudioTools/Config/FilterPlugin.ini
new file mode 100644
index 0000000..d52dd9e
--- /dev/null
+++ b/Plugins/VisualStudioTools/Config/FilterPlugin.ini
@@ -0,0 +1,9 @@
+[FilterPlugin]
+; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
+; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
+;
+; Examples:
+; /README.txt
+; /Extras/...
+; /Binaries/ThirdParty/*.dll
+/Docs/...
diff --git a/Plugins/VisualStudioTools/Docs/Marketplace_Readme.md b/Plugins/VisualStudioTools/Docs/Marketplace_Readme.md
new file mode 100644
index 0000000..8f3f95c
--- /dev/null
+++ b/Plugins/VisualStudioTools/Docs/Marketplace_Readme.md
@@ -0,0 +1,57 @@
+# Visual Studio Integration Tool
+
+Visual Studio Integration Tool is an Unreal Engine plugin that works in conjunction with Visual Studio to display information about Blueprints assets in C++ code (requires Visual Studio 2022 17.4 or later).
+
+## Installing
+
+### Visual Studio
+
+The tool requires the `Visual Studio Tools for Unreal Engine` component from Visual Studio to be installed. You can find it under the "Game development with C++" workload in the Visual Studio Installer (figure 1).
+
+ \
+*Figure 1 - Installing the Visual Studio component*
+
+### Unreal Engine
+ You can install the plugin in a couple of ways:
+
+ - Through the Epic Games Launcher:
+ - Select the "Install to Engine" option within the Launcher. From there, you can select an engine version for installation.
+ - If you're using the Marketplace website, you can add the plugin to your account and you will have an option to open the Launcher in order to install it as detailed above.
+ - If you already added the plugin to your account, go Library -> Vault in the and locate the plugin there.
+
+ - Through source distribution:
+ - If you're unable to use the Marketplace-based distribution (e.g. you're building the Unreal Engine from source), then you can install the plugin manually by following the instructions found at
+
+## Enabling the plugin
+
+- Through the Unreal Editor
+ - Open your project and then use the Plugin Manager to enable "VisualStudioTools".
+ - See [official documentation](https://docs.unrealengine.com/INT/working-with-plugins-in-unreal-engine/) for more information on how to install and enable plugins.
+- (Advanced) Alternatively, you can manually edit the '.uproject' descriptor for your project and add an entry for the "VisualStudioTools" plugin.
+
+## Usage
+
+Test discovery in Visual Studio 2022
+
+1. Begin by installing and enabling the `Visual Studio Tools for Unreal Engine` plugin.
+2. Open your solution in Visual Studio.
+3. Click on the Test Explorer to show a pop-up that will display available tests. (figure 3).
+4. You can find the logs from the plugin execution in the Tests Output Window.
+5. To refresh your filters for test discovery, you can select the "Options > Unreal Engine > Test Adapter" option under the "Tests" menu. (figure 4)
+
+ \
+*Figure 2 - Unreal Engine project Configuration Page
+
+ \
+*Figure 3 - Menu to rescan the blueprint assets in the game project*
+
+ \
+*Figure 3 - Menu to change options for Tests Discovery
+
+## Troubleshooting
+
+If you encounter any issues when setting up Visual Studio in conjunction with the Unreal Editor plugin, please refer to the [Troubleshooting](https://github.com/microsoft/vc-ue-extensions/blob/main/Docs/Troubleshooting.md) guide in the repository. This guide provides solutions for common issues and is periodically updated to ensure that the latest solutions are available.
+
+## Reporting issues
+
+To report new issues, provide feedback, or request new features, please use the following options: [Report a Problem](https://aka.ms/feedback/cpp/unrealengine/report) and [Suggest a Feature](https://aka.ms/feedback/cpp/unrealengine/suggest). These options will allow you to submit your issue or feedback directly to our team and help us improve the plugin moving forward.
\ No newline at end of file
diff --git a/Plugins/VisualStudioTools/Docs/Troubleshooting.md b/Plugins/VisualStudioTools/Docs/Troubleshooting.md
new file mode 100644
index 0000000..1b2b529
--- /dev/null
+++ b/Plugins/VisualStudioTools/Docs/Troubleshooting.md
@@ -0,0 +1,55 @@
+# Troubleshooting guide
+
+This document describes some of the errors that might happen in the integration with the Unreal Engine and potential ways to mitigate them.
+
+The integration works by Visual Studio being able to invoke the `VisualStudioTools` plugin using the Unreal Editor executable in commandlet mode. That means the following must be true:
+
+- The `Visual Studio Tools for Unreal Engine` component from Visual Studio must be installed. You can find it under the "Game Development with C++" workload in the VS Installer.
+- The game project must be built in a Editor target (e.g., `"Development_Editor"`).
+- The `VisualStudioTools` plugin must be enabled for the project, either explicitly in the .uproject descriptor file or be enabled by default to all projects if installed at the as an engine plugin (via the `"EnabledByDefault=true"` entry in the .uplugin file).
+- Starting on version 17.5, Visual Studio will wait to scan the game project until a file with the `UCLASS/UPROPERTY/UFUNCTION` macros is opened and the Code Lens hints are requested.
+- At the moment, the Code Lens hints will only be displayed for game projects. In particular, the files in the _engine_ project of the solution (with name like "UE4" or "UE5") will not display the hints yet.
+
+## Code Lens are not visible
+
+### Verify the required VS component is installed
+
+In recent versions of UE, the generated solution comes with a `.vsconfig` file, which allows right-clicking on the Solution in VS and selecting "Install Missing Feature(s)". The component is part of the "Game Development with C++" workload.
+
+You can also see this [help page](https://learn.microsoft.com/en-us/visualstudio/install/install-visual-studio?view=vs-2022#step-4---choose-workloads) about installing features using the Visual Studio installer.
+
+### Check if the opened documents have any class decorated with the Unreal macros
+
+For real world projects, scanning the blueprints information might take several seconds and be expensive in terms of machine resources. Visual Studio will only start the operation when the Code Lens are rendered. That means it will wait until a file from the game project with the Unreal macros is opened in the editor.
+
+### Check if a `cpp.hint` file is redefining the relevant Unreal macros
+
+Some projects might have a cpp.hint file that includes the `UCLASS`, `UPROPERTY`, `UFUNCTION` macros. That might suppress the new logic in Visual Studio that uses the macros to display the Code Lens hints.
+
+If that is the case, you can remove those macros from the hint file, save it and try reloading the project.
+
+Note that other macros in the hint file can be left as-is and do not affect the Code Lens hints.
+
+### Ensure the C++ Database is enabled
+
+In Tools > Options > Text Editor > C/C++ > Advanced > Browsing/Navigation, the setting "Disable Database" should be set to "False". This is the default value of this setting.
+
+## Errors showing up in the Output Window and/or Task Center notification
+
+### Message "LogInit: Error: VisualStudioToolsCommandlet looked like a commandlet, but we could not find the class."
+
+Possible causes are the plugin not being installed correctly or installed but not yet enabled for the game project (which is required on installation from the Marketplace).
+
+- See [this section](../README.md#building-and-installing) for installation instructions.
+
+- See [this section](../README.md#optional-enabling-the-plugin) for instructions on how to enable the plugin.
+
+### Message "Command finished with exit code 1" without other errors
+
+Either the game project or the plugin DLL is not yet built. Rebuilding the project should ensure they are available. Then manually rescan the game project using the `Project > Rescan UE Blueprints` menu.
+
+### Task Center error: "Your task failed with the message: Could not find a part of the path...'
+
+This was a known issue when trying to locate the path the Unreal Editor executable, fixed in Visual Studio 17.5-Preview3. This usually happens when the selected Configuration in VS is not one with an "Editor" target.
+
+A workaround is to switch to such configuration and manually rescan the game project using the `Project > Rescan UE Blueprints` menu.
diff --git a/Plugins/VisualStudioTools/Docs/Visual Studio Integration Tool Documentation.pdf b/Plugins/VisualStudioTools/Docs/Visual Studio Integration Tool Documentation.pdf
new file mode 100644
index 0000000..edcdfc4
Binary files /dev/null and b/Plugins/VisualStudioTools/Docs/Visual Studio Integration Tool Documentation.pdf differ
diff --git a/Plugins/VisualStudioTools/Docs/images/configuration_page.png b/Plugins/VisualStudioTools/Docs/images/configuration_page.png
new file mode 100644
index 0000000..d08556b
Binary files /dev/null and b/Plugins/VisualStudioTools/Docs/images/configuration_page.png differ
diff --git a/Plugins/VisualStudioTools/Docs/images/ide_support_ue.png b/Plugins/VisualStudioTools/Docs/images/ide_support_ue.png
new file mode 100644
index 0000000..2216c0c
Binary files /dev/null and b/Plugins/VisualStudioTools/Docs/images/ide_support_ue.png differ
diff --git a/Plugins/VisualStudioTools/Docs/images/test_explorer.png b/Plugins/VisualStudioTools/Docs/images/test_explorer.png
new file mode 100644
index 0000000..e72d9b5
Binary files /dev/null and b/Plugins/VisualStudioTools/Docs/images/test_explorer.png differ
diff --git a/Plugins/VisualStudioTools/Docs/images/test_options.png b/Plugins/VisualStudioTools/Docs/images/test_options.png
new file mode 100644
index 0000000..3a035e3
Binary files /dev/null and b/Plugins/VisualStudioTools/Docs/images/test_options.png differ
diff --git a/Plugins/VisualStudioTools/LICENSE b/Plugins/VisualStudioTools/LICENSE
new file mode 100644
index 0000000..113e51d
--- /dev/null
+++ b/Plugins/VisualStudioTools/LICENSE
@@ -0,0 +1,22 @@
+ Visual Studio Tools for Unreal Engine
+ Copyright (c) Microsoft Corporation.
+
+ MIT License
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
diff --git a/Plugins/VisualStudioTools/README.md b/Plugins/VisualStudioTools/README.md
new file mode 100644
index 0000000..6ed4f59
--- /dev/null
+++ b/Plugins/VisualStudioTools/README.md
@@ -0,0 +1,116 @@
+# Unreal Engine plugin for Visual Studio
+
+This project contains an Unreal Editor plugin that works in conjunction with Visual Studio to help discover and run tests in C++ code.
+
+The plugin can be installed in either the Engine or Game project sources, and it is automatically activated when an Unreal Engine project is opened in Visual Studio.
+
+## Requirements
+
+Before you begin, please make sure you have the following software and tools set up:
+
+1. Visual Studio 2022 has the "Visual Studio Tools for Unreal Engine" component installed.
+ 1. The component can be found in the "Game development with C++" workload or as an individual component.
+2. Unreal Engine, either installed or built from source.
+ 1. To learn how to install or build Unreal Engine, please refer to the following guide: [Installing Unreal Engine](https://docs.unrealengine.com/5.0/en-US/installing-unreal-engine).
+ 1. The source code and instructions have been tested on Unreal Engine versions 4.27 and 5.0+.
+
+## Building and Installing the Plugin
+
+> If you have Unreal Engine installed and set up through the Epic Games Launcher, and you only want to use the plugin, you can skip the steps below and install it directly from the [Unreal Engine Marketplace](https://aka.ms/vsueplugin).
+
+The most straightforward way to use the plugin is to clone the repo under the `Plugins` folder of your game project or engine source. If you have multiple projects in the same Visual Studio solution, it is recommended to install the plugin at the engine level and share the binaries across the projects.
+
+1. Clone the repo by using the following commands:
+ ```powershell
+ git clone https://github.com/microsoft/vc-ue-extensions.git
+ ```
+
+2. Build the plugin from source:
+ ```powershell
+ msbuild -p:UnrealEngine=
+ ```
+ Note#1: `` can be path to source code folder of the engine or the one installed by `Epic Games Launcher` (e.g `C:\Program Files\Epic Games\UE_5.4`)
+ Note#2: Alternatevly you can follow [Unreal Engine building plugins](https://dev.epicgames.com/community/learning/tutorials/qz93/unreal-engine-building-plugins) guide.
+
+3. Clone built plugin.
+
+ 3.1. To Project folder:
+ ```powershell
+ move-item -path ./bin -destination \Plugins\VisualStudioTools
+ ```
+ Note: You have to create `Plugins` folder in the root of the game project if it doens't exisist yet.
+
+ 3.2. To Engine folder:
+ ```powershell
+ move-item -path ./bin -destination Note: To ensure proper activation of the plugin, make sure the correct plugin is selected or the desired changes are made in the `.uproject` file.
+
+## Manually invoking the plugin
+
+The plugin is designed to be used with Visual Studio, and as such, it does not provide any user interfaces, commands, or logs within the Unreal Editor. However, it is still possible to test the plugin's execution by running the **sample** command below:
+
+```powershell
+& "\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" "$Env:UserProfile\Unreal Projects\EmptyProject\EmptyProject.uproject" -run=VisualStudioTools -output "$Env:Temp\vs-ue-tools.json" [-unattended -noshadercompile -nosound -nullrhi -nocpuprofilertrace -nocrashreports -nosplash]
+```
+
+This command will run the plugin for the specified project and save Unreal Engine Blueprint information in the output file. Optional parameters are included to run the command faster.
+
+For more information on the specific command line parameters, you can run the following command in the powershell prompt with `-help`:
+
+```powershell
+& "" "" -run=VisualStudioTools -help [-unattended -noshadercompile -nosound -nullrhi -nocpuprofilertrace -nocrashreports -nosplash]
+```
+
+>Note: The executable name is `UE4Editor-cmd.exe` for UE4.x, located under a similar path.
+
+## Troubleshooting
+
+If you encounter any issues when setting up Visual Studio in conjunction with the Unreal Editor plugin, please refer to the [Troubleshooting](https://github.com/microsoft/vc-ue-extensions/blob/main/Docs/Troubleshooting.md) guide in the repository. This guide provides solutions for common issues and is periodically updated to ensure that the latest solutions are available.
+
+To report new issues, provide feedback, or request new features, please use the following options: [Report a Problem](https://aka.ms/feedback/cpp/unrealengine/report) and [Suggest a Feature](https://aka.ms/feedback/cpp/unrealengine/suggest). These options will allow you to submit your issue or feedback directly to our team and help us improve the plugin moving forward.
+
+## Contributing
+This project welcomes contributions and suggestions. Check out our [contributing guide](CONTRIBUTING.md) for instructions on how to contribute to the project.
+
+## Trademarks
+
+This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
+Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
+Any use of third-party trademarks or logos are subject to those third-party's policies.
\ No newline at end of file
diff --git a/Plugins/VisualStudioTools/SECURITY.md b/Plugins/VisualStudioTools/SECURITY.md
new file mode 100644
index 0000000..c2ba681
--- /dev/null
+++ b/Plugins/VisualStudioTools/SECURITY.md
@@ -0,0 +1,41 @@
+
+
+## Security
+
+Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
+
+If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
+
+## Reporting Security Issues
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
+
+If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
+
+You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
+
+Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
+
+ * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
+ * Full paths of source file(s) related to the manifestation of the issue
+ * The location of the affected source code (tag/branch/commit or direct URL)
+ * Any special configuration required to reproduce the issue
+ * Step-by-step instructions to reproduce the issue
+ * Proof-of-concept or exploit code (if possible)
+ * Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
+
+## Preferred Languages
+
+We prefer all communications to be in English.
+
+## Policy
+
+Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
+
+
diff --git a/Plugins/VisualStudioTools/SUPPORT.md b/Plugins/VisualStudioTools/SUPPORT.md
new file mode 100644
index 0000000..54e20f0
--- /dev/null
+++ b/Plugins/VisualStudioTools/SUPPORT.md
@@ -0,0 +1,13 @@
+# Support
+
+## How to file issues and get help
+
+This project uses the Visual Studio Developer Community to track bugs and feature requests. Please search the existing feedback before filing new ones to avoid duplicates.
+
+For common issues, please refer to our [Troubleshooting](https://github.com/microsoft/vc-ue-extensions/blob/main/Docs/troubleshooting.md) guide in the repository. We will periodically update the guide to provide solutions for common issues.
+
+To report issues, provide feedback, and request features, please use one of the following options: [Report a Problem](https://aka.ms/feedback/cpp/unrealengine/report) and [Suggest a Feature](https://aka.ms/feedback/cpp/unrealengine/suggest).
+
+## Microsoft Support Policy
+
+Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
diff --git a/Plugins/VisualStudioTools/Scripts/Package-Plugin.ps1 b/Plugins/VisualStudioTools/Scripts/Package-Plugin.ps1
new file mode 100644
index 0000000..e2b9c30
--- /dev/null
+++ b/Plugins/VisualStudioTools/Scripts/Package-Plugin.ps1
@@ -0,0 +1,43 @@
+param(
+ [Parameter(Mandatory=$true)]
+ [string]
+ $EnginePath,
+ [Parameter(Mandatory=$true)]
+ [string]
+ $EngineVersion
+)
+
+function New-TemporaryDirectory {
+ $parent = [System.IO.Path]::GetTempPath()
+ $name = [System.IO.Path]::GetRandomFileName()
+ New-Item -ItemType Directory -Path (Join-Path $parent $name)
+}
+
+$PackagePath = New-TemporaryDirectory
+& msbuild "-p:UnrealEngine=$EnginePath;OutputPath=$PackagePath;Versioned=true"
+
+# Add EnabledByDefault property in the descriptor file
+Write-Host "Patch plugin descriptor file"
+$descriptor = "$PackagePath/VisualStudioTools.uplugin"
+$a = Get-Content $descriptor | ConvertFrom-Json
+$a | Add-Member -NotePropertyName EnabledByDefault -NotePropertyValue $true -ErrorAction Ignore
+$a | ConvertTo-Json -depth 100 | Out-File $descriptor -Encoding utf8
+
+Write-Host "Copy Config folder"
+Copy-Item -Path Config -Destination $PackagePath/Config -Recurse
+
+$PublishPath = "publish"
+If(!(test-path -PathType Container $PublishPath))
+{
+ New-Item -ItemType Directory -Path $PublishPath | Out-Null
+}
+
+Write-Host "Create ZIP package"
+$tag = $EngineVersion.Replace(".", "")
+$files = Get-ChildItem $PackagePath -Exclude @("Binaries", "Intermediate")
+$zip = "$PublishPath/VisualStudioTools_v$($a.VersionName)_ue$tag.zip"
+Compress-Archive -Path $files -DestinationPath "$PublishPath/VisualStudioTools_v$($a.VersionName)_ue$tag.zip" -CompressionLevel Fastest
+
+Remove-Item $PackagePath -Force -Recurse
+
+Write-Host "Done: $($zip | Resolve-Path)"
\ No newline at end of file
diff --git a/Plugins/VisualStudioTools/Scripts/SignDetached.proj b/Plugins/VisualStudioTools/Scripts/SignDetached.proj
new file mode 100644
index 0000000..285bbd5
--- /dev/null
+++ b/Plugins/VisualStudioTools/Scripts/SignDetached.proj
@@ -0,0 +1,19 @@
+
+
+
+
+
+ $(MSBuildThisFileDirectory)../../out/
+
+ $(BaseOutputDirectory)
+ $(BaseOutputDirectory)
+
+
+
+
+ Microsoft400
+
+
+
+
+
diff --git a/Plugins/VisualStudioTools/Scripts/packages.config b/Plugins/VisualStudioTools/Scripts/packages.config
new file mode 100644
index 0000000..6cda0ac
--- /dev/null
+++ b/Plugins/VisualStudioTools/Scripts/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/Plugins/VisualStudioTools/Source/.editorconfig b/Plugins/VisualStudioTools/Source/.editorconfig
new file mode 100644
index 0000000..e0f1e96
--- /dev/null
+++ b/Plugins/VisualStudioTools/Source/.editorconfig
@@ -0,0 +1,91 @@
+[*.{cpp,h}]
+
+# Naming convention rules (note: currently need to be ordered from more to less specific)
+
+cpp_naming_rule.aactor_prefixed.symbols = aactor_class
+cpp_naming_rule.aactor_prefixed.style = aactor_style
+
+cpp_naming_rule.swidget_prefixed.symbols = swidget_class
+cpp_naming_rule.swidget_prefixed.style = swidget_style
+
+cpp_naming_rule.uobject_prefixed.symbols = uobject_class
+cpp_naming_rule.uobject_prefixed.style = uobject_style
+
+cpp_naming_rule.booleans_prefixed.symbols = boolean_vars
+cpp_naming_rule.booleans_prefixed.style = boolean_style
+
+cpp_naming_rule.structs_prefixed.symbols = structs
+cpp_naming_rule.structs_prefixed.style = unreal_engine_structs
+
+cpp_naming_rule.enums_prefixed.symbols = enums
+cpp_naming_rule.enums_prefixed.style = unreal_engine_enums
+
+cpp_naming_rule.templates_prefixed.symbols = templates
+cpp_naming_rule.templates_prefixed.style = unreal_engine_templates
+
+cpp_naming_rule.general_names.symbols = all_symbols
+cpp_naming_rule.general_names.style = unreal_engine_default
+
+# Naming convention symbols
+
+cpp_naming_symbols.aactor_class.applicable_kinds = class
+cpp_naming_symbols.aactor_class.applicable_type = AActor
+
+cpp_naming_symbols.swidget_class.applicable_kinds = class
+cpp_naming_symbols.swidget_class.applicable_type = SWidget
+
+cpp_naming_symbols.uobject_class.applicable_kinds = class
+cpp_naming_symbols.uobject_class.applicable_type = UObject
+
+cpp_naming_symbols.boolean_vars.applicable_kinds = local,parameter,field
+cpp_naming_symbols.boolean_vars.applicable_type = bool
+
+cpp_naming_symbols.enums.applicable_kinds = enum
+
+cpp_naming_symbols.templates.applicable_kinds = template_class
+
+cpp_naming_symbols.structs.applicable_kinds = struct
+
+cpp_naming_symbols.all_symbols.applicable_kinds = *
+
+# Naming convention styles
+
+cpp_naming_style.unreal_engine_default.capitalization = pascal_case
+cpp_naming_style.unreal_engine_default.required_prefix =
+cpp_naming_style.unreal_engine_default.required_suffix =
+cpp_naming_style.unreal_engine_default.word_separator =
+
+cpp_naming_style.unreal_engine_enums.capitalization = pascal_case
+cpp_naming_style.unreal_engine_enums.required_prefix = E
+cpp_naming_style.unreal_engine_enums.required_suffix =
+cpp_naming_style.unreal_engine_enums.word_separator =
+
+cpp_naming_style.unreal_engine_templates.capitalization = pascal_case
+cpp_naming_style.unreal_engine_templates.required_prefix = T
+cpp_naming_style.unreal_engine_templates.required_suffix =
+cpp_naming_style.unreal_engine_templates.word_separator =
+
+cpp_naming_style.unreal_engine_structs.capitalization = pascal_case
+cpp_naming_style.unreal_engine_structs.required_prefix = F
+cpp_naming_style.unreal_engine_structs.required_suffix =
+cpp_naming_style.unreal_engine_structs.word_separator =
+
+cpp_naming_style.uobject_style.capitalization = pascal_case
+cpp_naming_style.uobject_style.required_prefix = U
+cpp_naming_style.uobject_style.required_suffix =
+cpp_naming_style.uobject_style.word_separator =
+
+cpp_naming_style.aactor_style.capitalization = pascal_case
+cpp_naming_style.aactor_style.required_prefix = A
+cpp_naming_style.aactor_style.required_suffix =
+cpp_naming_style.aactor_style.word_separator =
+
+cpp_naming_style.swidget_style.capitalization = pascal_case
+cpp_naming_style.swidget_style.required_prefix = S
+cpp_naming_style.swidget_style.required_suffix =
+cpp_naming_style.swidget_style.word_separator =
+
+cpp_naming_style.boolean_style.capitalization = pascal_case
+cpp_naming_style.boolean_style.required_prefix = b
+cpp_naming_style.boolean_style.required_suffix =
+cpp_naming_style.boolean_style.word_separator =
diff --git a/Plugins/VisualStudioTools/Source/VisualStudioBlueprintDebuggerHelper/Private/VisualStudioBlueprintDebuggerHelperModule.cpp b/Plugins/VisualStudioTools/Source/VisualStudioBlueprintDebuggerHelper/Private/VisualStudioBlueprintDebuggerHelperModule.cpp
new file mode 100644
index 0000000..8dec79a
--- /dev/null
+++ b/Plugins/VisualStudioTools/Source/VisualStudioBlueprintDebuggerHelper/Private/VisualStudioBlueprintDebuggerHelperModule.cpp
@@ -0,0 +1,246 @@
+// Copyright 2022 (c) Microsoft. All rights reserved.
+
+#include "VisualStudioBlueprintDebuggerHelperModule.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include