├── src └── Plugins │ ├── FoliageShadowImposters │ ├── Resources │ │ ├── Icon128.png │ │ └── FSIButtonIcon.svg │ ├── Source │ │ └── FoliageShadowImposters │ │ │ ├── Private │ │ │ ├── FoliageShadowImpostersCommands.cpp │ │ │ ├── FoliageShadowImpostersStyle.cpp │ │ │ └── FoliageShadowImposters.cpp │ │ │ ├── Public │ │ │ ├── FoliageShadowImpostersStyle.h │ │ │ ├── FoliageShadowImpostersCommands.h │ │ │ └── FoliageShadowImposters.h │ │ │ └── FoliageShadowImposters.Build.cs │ └── FoliageShadowImposters.uplugin │ └── FoliageShadowImpostersRuntime │ ├── Resources │ ├── Icon128.png │ └── FSIButtonIcon.svg │ ├── Source │ └── FoliageShadowImpostersRuntime │ │ ├── Private │ │ ├── FoliageShadowImposterComponent.cpp │ │ └── FoliageShadowImpostersRuntime.cpp │ │ ├── Public │ │ ├── FoliageShadowImpostersRuntime.h │ │ └── FoliageShadowImposterComponent.h │ │ └── FoliageShadowImpostersRuntime.Build.cs │ └── FoliageShadowImpostersRuntime.uplugin ├── LICENSE ├── .gitignore └── README.md /src/Plugins/FoliageShadowImposters/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SabreDartStudios/FoliageShadowImposters/HEAD/src/Plugins/FoliageShadowImposters/Resources/Icon128.png -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImpostersRuntime/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SabreDartStudios/FoliageShadowImposters/HEAD/src/Plugins/FoliageShadowImpostersRuntime/Resources/Icon128.png -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImpostersRuntime/Source/FoliageShadowImpostersRuntime/Private/FoliageShadowImposterComponent.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "FoliageShadowImposterComponent.h" 5 | 6 | UFoliageShadowImposterComponent::UFoliageShadowImposterComponent() 7 | { 8 | CastShadow = true; 9 | bHiddenInGame = true; 10 | bCastHiddenShadow = true; 11 | } -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImpostersRuntime/Source/FoliageShadowImpostersRuntime/Public/FoliageShadowImpostersRuntime.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FFoliageShadowImpostersRuntimeModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | 16 | }; 17 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImposters/Source/FoliageShadowImposters/Private/FoliageShadowImpostersCommands.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "FoliageShadowImpostersCommands.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FFoliageShadowImpostersModule" 6 | 7 | void FFoliageShadowImpostersCommands::RegisterCommands() 8 | { 9 | UI_COMMAND(PluginAction, "FoliageShadowImposters", "Add Foliage Shadow Imposters", EUserInterfaceActionType::Button, FInputChord()); 10 | } 11 | 12 | #undef LOCTEXT_NAMESPACE 13 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImpostersRuntime/Source/FoliageShadowImpostersRuntime/Public/FoliageShadowImposterComponent.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "FoliageInstancedStaticMeshComponent.h" 7 | #include "FoliageShadowImposterComponent.generated.h" 8 | 9 | 10 | /** 11 | * 12 | */ 13 | UCLASS() 14 | class FOLIAGESHADOWIMPOSTERSRUNTIME_API UFoliageShadowImposterComponent : public UFoliageInstancedStaticMeshComponent 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | UFoliageShadowImposterComponent(); 20 | 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImposters/Source/FoliageShadowImposters/Public/FoliageShadowImpostersStyle.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Styling/SlateStyle.h" 7 | 8 | class FFoliageShadowImpostersStyle 9 | { 10 | public: 11 | 12 | static void Initialize(); 13 | 14 | static void Shutdown(); 15 | 16 | /** reloads textures used by slate renderer */ 17 | static void ReloadTextures(); 18 | 19 | /** @return The Slate style set for the Shooter game */ 20 | static const ISlateStyle& Get(); 21 | 22 | static FName GetStyleSetName(); 23 | 24 | private: 25 | 26 | static TSharedRef< class FSlateStyleSet > Create(); 27 | 28 | private: 29 | 30 | static TSharedPtr< class FSlateStyleSet > StyleInstance; 31 | }; -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImposters/Source/FoliageShadowImposters/Public/FoliageShadowImpostersCommands.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Framework/Commands/Commands.h" 7 | #include "FoliageShadowImpostersStyle.h" 8 | 9 | class FFoliageShadowImpostersCommands : public TCommands 10 | { 11 | public: 12 | 13 | FFoliageShadowImpostersCommands() 14 | : TCommands(TEXT("FoliageShadowImposters"), NSLOCTEXT("Contexts", "FoliageShadowImposters", "FoliageShadowImposters Plugin"), NAME_None, FFoliageShadowImpostersStyle::GetStyleSetName()) 15 | { 16 | } 17 | 18 | // TCommands<> interface 19 | virtual void RegisterCommands() override; 20 | 21 | public: 22 | TSharedPtr< FUICommandInfo > PluginAction; 23 | }; 24 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImposters/FoliageShadowImposters.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Foliage Shadow Imposters", 6 | "Description": "A UE5 plugin to add Foliage Shadow Imposters to Nanite Foliage for increased performance.", 7 | "Category": "Other", 8 | "CreatedBy": "Dartanlla", 9 | "CreatedByURL": "https://github.com/SabreDartStudios/FoliageShadowImposters", 10 | "DocsURL": "https://github.com/SabreDartStudios/FoliageShadowImposters", 11 | "MarketplaceURL": "", 12 | "SupportURL": "https://discord.gg/qZ76Cmxcgp", 13 | "CanContainContent": false, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "FoliageShadowImposters", 20 | "Type": "Editor", 21 | "LoadingPhase": "Default" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImpostersRuntime/FoliageShadowImpostersRuntime.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Foliage Shadow Imposters Runtime", 6 | "Description": "A UE5 plugin to add Foliage Shadow Imposters to Nanite Foliage for increased performance. This is the runtime component.", 7 | "Category": "Other", 8 | "CreatedBy": "Dartanlla", 9 | "CreatedByURL": "https://github.com/SabreDartStudios/FoliageShadowImposters", 10 | "DocsURL": "https://github.com/SabreDartStudios/FoliageShadowImposters", 11 | "MarketplaceURL": "", 12 | "SupportURL": "https://discord.gg/qZ76Cmxcgp", 13 | "CanContainContent": false, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "FoliageShadowImpostersRuntime", 20 | "Type": "Runtime", 21 | "LoadingPhase": "Default" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 SabreDartStudios 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImpostersRuntime/Source/FoliageShadowImpostersRuntime/Private/FoliageShadowImpostersRuntime.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "FoliageShadowImpostersRuntime.h" 4 | #include "EngineUtils.h" 5 | #include "InstancedFoliageActor.h" 6 | #include "FoliageShadowImposterComponent.h" 7 | #include "FoliageInstancedStaticMeshComponent.h" 8 | #include "Components/InstancedStaticMeshComponent.h" 9 | 10 | static const FName FoliageShadowImpostersTabName("FoliageShadowImposters"); 11 | 12 | #define LOCTEXT_NAMESPACE "FFoliageShadowImpostersModule" 13 | 14 | void FFoliageShadowImpostersRuntimeModule::StartupModule() 15 | { 16 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 17 | } 18 | 19 | void FFoliageShadowImpostersRuntimeModule::ShutdownModule() 20 | { 21 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 22 | // we call this function before unloading the module. 23 | } 24 | 25 | #undef LOCTEXT_NAMESPACE 26 | 27 | IMPLEMENT_MODULE(FFoliageShadowImpostersRuntimeModule, FoliageShadowImpostersRuntime) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* 75 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImpostersRuntime/Source/FoliageShadowImpostersRuntime/FoliageShadowImpostersRuntime.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class FoliageShadowImpostersRuntime : ModuleRules 6 | { 7 | public FoliageShadowImpostersRuntime(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | 18 | PrivateIncludePaths.AddRange( 19 | new string[] { 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", 29 | // ... add other public dependencies that you statically link with here ... 30 | } 31 | ); 32 | 33 | 34 | PrivateDependencyModuleNames.AddRange( 35 | new string[] 36 | { 37 | "CoreUObject", 38 | "Engine", 39 | "Foliage" 40 | // ... add private dependencies that you statically link with here ... 41 | } 42 | ); 43 | 44 | 45 | DynamicallyLoadedModuleNames.AddRange( 46 | new string[] 47 | { 48 | // ... add any modules that your module loads dynamically here ... 49 | } 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImposters/Source/FoliageShadowImposters/FoliageShadowImposters.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class FoliageShadowImposters : ModuleRules 6 | { 7 | public FoliageShadowImposters(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | 18 | PrivateIncludePaths.AddRange( 19 | new string[] { 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", 29 | // ... add other public dependencies that you statically link with here ... 30 | } 31 | ); 32 | 33 | 34 | PrivateDependencyModuleNames.AddRange( 35 | new string[] 36 | { 37 | "Projects", 38 | "InputCore", 39 | "EditorFramework", 40 | "UnrealEd", 41 | "ToolMenus", 42 | "CoreUObject", 43 | "Engine", 44 | "Slate", 45 | "SlateCore", 46 | "Foliage", 47 | "FoliageShadowImpostersRuntime" 48 | // ... add private dependencies that you statically link with here ... 49 | } 50 | ); 51 | 52 | 53 | DynamicallyLoadedModuleNames.AddRange( 54 | new string[] 55 | { 56 | // ... add any modules that your module loads dynamically here ... 57 | } 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImposters/Source/FoliageShadowImposters/Public/FoliageShadowImposters.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | #include "FoliageShadowImposters.generated.h" 8 | 9 | class FToolBarBuilder; 10 | class FMenuBuilder; 11 | 12 | USTRUCT(BlueprintType) 13 | struct FFoliageShadowImposterReplacementRow : public FTableRowBase 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | /** Should we add the Foliage Shadow Imposter Mesh when we run the plugin process? **/ 19 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 20 | bool bShouldReplace; 21 | 22 | /** This is the foliage mesh we are searching for to add Foliage Shadow Imposters for. **/ 23 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 24 | UStaticMesh* OriginalFoliageMesh; 25 | 26 | /** This is the Foliage Shadow Imposter mesh. This should NOT have WPO materials and should be a low poly mesh for shadow casting. **/ 27 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 28 | UStaticMesh* FoliageSahdowImposterMesh; 29 | 30 | }; 31 | 32 | class FFoliageShadowImpostersModule : public IModuleInterface 33 | { 34 | public: 35 | 36 | /** IModuleInterface implementation */ 37 | virtual void StartupModule() override; 38 | virtual void ShutdownModule() override; 39 | 40 | /** This function will be bound to Command. */ 41 | void PluginButtonClicked(); 42 | 43 | private: 44 | 45 | void RemoveAllFoliageShadowImpostersForThisMesh(FString FoliageShadowImposterMeshPath); 46 | void AddFoliageShadowImpostersForThisMesh(FString MeshPathToSearchFor, FString FoliageShadowImposterMeshPath); 47 | void RegisterMenus(); 48 | 49 | 50 | private: 51 | TSharedPtr PluginCommands; 52 | }; 53 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImposters/Source/FoliageShadowImposters/Private/FoliageShadowImpostersStyle.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "FoliageShadowImpostersStyle.h" 4 | #include "FoliageShadowImposters.h" 5 | #include "Framework/Application/SlateApplication.h" 6 | #include "Styling/SlateStyleRegistry.h" 7 | #include "Slate/SlateGameResources.h" 8 | #include "Interfaces/IPluginManager.h" 9 | #include "Styling/SlateStyleMacros.h" 10 | 11 | #define RootToContentDir Style->RootToContentDir 12 | 13 | TSharedPtr FFoliageShadowImpostersStyle::StyleInstance = nullptr; 14 | 15 | void FFoliageShadowImpostersStyle::Initialize() 16 | { 17 | if (!StyleInstance.IsValid()) 18 | { 19 | StyleInstance = Create(); 20 | FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); 21 | } 22 | } 23 | 24 | void FFoliageShadowImpostersStyle::Shutdown() 25 | { 26 | FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); 27 | ensure(StyleInstance.IsUnique()); 28 | StyleInstance.Reset(); 29 | } 30 | 31 | FName FFoliageShadowImpostersStyle::GetStyleSetName() 32 | { 33 | static FName StyleSetName(TEXT("FoliageShadowImpostersStyle")); 34 | return StyleSetName; 35 | } 36 | 37 | 38 | const FVector2D Icon16x16(16.0f, 16.0f); 39 | const FVector2D Icon20x20(20.0f, 20.0f); 40 | 41 | TSharedRef< FSlateStyleSet > FFoliageShadowImpostersStyle::Create() 42 | { 43 | TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("FoliageShadowImpostersStyle")); 44 | Style->SetContentRoot(IPluginManager::Get().FindPlugin("FoliageShadowImposters")->GetBaseDir() / TEXT("Resources")); 45 | 46 | Style->Set("FoliageShadowImposters.PluginAction", new IMAGE_BRUSH_SVG(TEXT("FSIButtonIcon"), Icon20x20)); 47 | return Style; 48 | } 49 | 50 | void FFoliageShadowImpostersStyle::ReloadTextures() 51 | { 52 | if (FSlateApplication::IsInitialized()) 53 | { 54 | FSlateApplication::Get().GetRenderer()->ReloadTextureResources(); 55 | } 56 | } 57 | 58 | const ISlateStyle& FFoliageShadowImpostersStyle::Get() 59 | { 60 | return *StyleInstance; 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Foliage Shadow Imposters 2 | *This plugin now supports packaged games. There are two plugins: one for the editor and one for runtime.* 3 | 4 | A UE5 plugin to add Foliage Shadow Imposters to Nanite Foliage for increased performance when using World Position Offset (WPO) and Virtual Shadow Maps. We gain performance by disabling the expensive WPO shadow on the Nanite Foliage and replacing it with a cheap non-WPO shadow casting Foliage Shadow Imposter. The compromise here is that the foliage will animate in the wind, but the shadows cast on the ground will not change. This compromise leads to **massive** performance gains. 5 | 6 | This plugin searches all InstancedFoliageActors for an OriginalFoliageMesh and adds a FoliageShadowImposterMesh with the same location, rotation, and scale. The OriginalFoliageMesh should have Nanite Foliage turned on and be set to not cast a shadow in the FoliageType in the Foliage editor. The OriginalFoliageMesh should have World Position Offset enabled. The FoliageShadowImposterMesh should be a low poly LOD for the purpose of casting a cheaper shadow. **The FoliageShadowImposterMesh must have FoliageShadowImposterMesh disabled.** The plugin will automatically enable the shadow for FoliageShadowImposterMesh, set HiddenInGame, and set cast shadow when hidden. 7 | 8 | *This plugin requires Unreal Engine 5.1 or higher.* 9 | 10 | Installation Instructions 11 | 12 | 1. Copy the /src/Plugins/FoliageImposters folder to the /Plugins folder in your UE5 project. 13 | 2. Recompile your project in Visual Studio to compile the plugin source code. It isn't precompiled. 14 | 3. Open the UE5 editor and enable the FoliageShadowImposters plugin if it isn't already enabled. 15 | 4. Create a DataTable with row type FoliageShadowImposterReplacementRow. 16 | 5. Add at least one row to the DataTable. Specify an OriginalFoliageMesh to search for in the world. Then pick a FoliageShadowImposterMesh to add to it. Finally, check the ShouldReplace checkbox. 17 | 6. Right click on the new DataTable and click on Copy Reference. 18 | 7. Close the editor. 19 | 8. Open your DefaultGame.ini file and find the [/Script/EngineSettings.GeneralProjectSettings] section. 20 | 9. Add a new line under this section like this. Paste your refernce inside the double quotes. 21 | ```PathToFoliageShadowImposterReplacementDataTable=""``` 22 | 10. Reopen the editor. 23 | 11. Use the Foliage Editor to place the foliage in the world that you specified as OriginalFoliageMesh in step 5. 24 | 12. Click on the two trees icon in the toolbar at the top-center of the editor window. This will add FoliageShadowImposterMesh for each OriginalFoliageMesh it finds. It will match the location, rotation, and scale of the original. Each time you run the plugin it will remove all of the FoliageShadowImposterMesh's before it adds them again. The plugin will only process rows in the DataTable where the ShouldReplace checkbox is checked. 25 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImposters/Resources/FSIButtonIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImpostersRuntime/Resources/FSIButtonIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Plugins/FoliageShadowImposters/Source/FoliageShadowImposters/Private/FoliageShadowImposters.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "FoliageShadowImposters.h" 4 | #include "FoliageShadowImpostersStyle.h" 5 | #include "FoliageShadowImpostersCommands.h" 6 | #include "EngineUtils.h" 7 | #include "InstancedFoliageActor.h" 8 | #include "FoliageShadowImposterComponent.h" 9 | #include "FoliageInstancedStaticMeshComponent.h" 10 | #include "Components/InstancedStaticMeshComponent.h" 11 | #include "Misc/MessageDialog.h" 12 | #include "ToolMenus.h" 13 | 14 | static const FName FoliageShadowImpostersTabName("FoliageShadowImposters"); 15 | 16 | #define LOCTEXT_NAMESPACE "FFoliageShadowImpostersModule" 17 | 18 | void FFoliageShadowImpostersModule::StartupModule() 19 | { 20 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 21 | 22 | FFoliageShadowImpostersStyle::Initialize(); 23 | FFoliageShadowImpostersStyle::ReloadTextures(); 24 | 25 | FFoliageShadowImpostersCommands::Register(); 26 | 27 | PluginCommands = MakeShareable(new FUICommandList); 28 | 29 | PluginCommands->MapAction( 30 | FFoliageShadowImpostersCommands::Get().PluginAction, 31 | FExecuteAction::CreateRaw(this, &FFoliageShadowImpostersModule::PluginButtonClicked), 32 | FCanExecuteAction()); 33 | 34 | UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FFoliageShadowImpostersModule::RegisterMenus)); 35 | } 36 | 37 | void FFoliageShadowImpostersModule::ShutdownModule() 38 | { 39 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 40 | // we call this function before unloading the module. 41 | 42 | UToolMenus::UnRegisterStartupCallback(this); 43 | 44 | UToolMenus::UnregisterOwner(this); 45 | 46 | FFoliageShadowImpostersStyle::Shutdown(); 47 | 48 | FFoliageShadowImpostersCommands::Unregister(); 49 | } 50 | 51 | void FFoliageShadowImpostersModule::RemoveAllFoliageShadowImpostersForThisMesh(FString FoliageShadowImposterMeshPath) 52 | { 53 | for (TActorIterator FoliageActorIterator(GEditor->GetEditorWorldContext().World()); FoliageActorIterator; ++FoliageActorIterator) 54 | { 55 | AInstancedFoliageActor* CurrentFoliageActor = *FoliageActorIterator; 56 | if (CurrentFoliageActor) 57 | { 58 | if (CurrentFoliageActor) 59 | { 60 | TArray InstancedStaticMeshComponents; 61 | CurrentFoliageActor->GetComponents(InstancedStaticMeshComponents, true); 62 | 63 | for (UInstancedStaticMeshComponent* ActorComp : InstancedStaticMeshComponents) 64 | { 65 | if (UFoliageShadowImposterComponent* InstancedFoliageComp = Cast(ActorComp)) 66 | { 67 | if (InstancedFoliageComp->GetStaticMesh().GetPathName() == FoliageShadowImposterMeshPath) 68 | { 69 | //UE_LOG(LogTemp, Error, TEXT("Found a UFoliageShadowImposteComponent: %s"), *InstancedFoliageComp->GetName()); 70 | InstancedFoliageComp->UnregisterComponent(); 71 | InstancedFoliageComp->DestroyComponent(); 72 | CurrentFoliageActor->Modify(); 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | void FFoliageShadowImpostersModule::AddFoliageShadowImpostersForThisMesh(FString MeshPathToSearchFor, FString FoliageShadowImposterMeshPath) 82 | { 83 | for (TActorIterator FoliageActorIterator(GEditor->GetEditorWorldContext().World()); FoliageActorIterator; ++FoliageActorIterator) 84 | { 85 | AInstancedFoliageActor* CurrentFoliageActor = *FoliageActorIterator; 86 | if (CurrentFoliageActor) 87 | { 88 | if (CurrentFoliageActor) 89 | { 90 | TArray InstancedStaticMeshComponents; 91 | CurrentFoliageActor->GetComponents(InstancedStaticMeshComponents, true); 92 | 93 | for (UInstancedStaticMeshComponent* ActorComp : InstancedStaticMeshComponents) 94 | { 95 | if (UFoliageInstancedStaticMeshComponent* InstancedFoliageComp = Cast(ActorComp)) 96 | { 97 | if (InstancedFoliageComp->GetStaticMesh().GetPathName() == MeshPathToSearchFor) 98 | { 99 | //UE_LOG(LogTemp, Error, TEXT("Found a UFoliageInstancedStaticMeshComponent: %s"), *InstancedFoliageComp->GetName()); 100 | UFoliageShadowImposterComponent* NewImposterMeshComponent = NewObject(CurrentFoliageActor, 101 | UFoliageShadowImposterComponent::StaticClass(), 102 | NAME_None, RF_Transactional); 103 | 104 | CurrentFoliageActor->AddInstanceComponent(NewImposterMeshComponent); 105 | NewImposterMeshComponent->RegisterComponent(); 106 | NewImposterMeshComponent->AttachToComponent(CurrentFoliageActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform); 107 | UStaticMesh* ImposterMeshToUse = Cast(StaticLoadObject(UStaticMesh::StaticClass(), NULL, *FoliageShadowImposterMeshPath)); 108 | NewImposterMeshComponent->SetStaticMesh(ImposterMeshToUse); 109 | CurrentFoliageActor->Modify(); 110 | 111 | FTransform InstanceTransform; 112 | for (int32 FoliageInstanceIndex = InstancedFoliageComp->GetInstanceCount() - 1; FoliageInstanceIndex >= 0; FoliageInstanceIndex--) 113 | { 114 | InstancedFoliageComp->GetInstanceTransform(FoliageInstanceIndex, InstanceTransform, true); 115 | //UE_LOG(LogTemp, Warning, TEXT("Found a foliage instance at: %s with rotation: %s and scale: %s"), *InstanceTransform.GetLocation().ToString(), *InstanceTransform.GetRotation().ToString(), *InstanceTransform.GetScale3D().ToString()); 116 | NewImposterMeshComponent->AddInstance(InstanceTransform, true); 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | void FFoliageShadowImpostersModule::PluginButtonClicked() 127 | { 128 | FString PathToFoliageShadowImposterReplacementDataTable; 129 | GConfig->GetString( 130 | TEXT("/Script/EngineSettings.GeneralProjectSettings"), 131 | TEXT("PathToFoliageShadowImposterReplacementDataTable"), 132 | PathToFoliageShadowImposterReplacementDataTable, 133 | GGameIni 134 | ); 135 | //UE_LOG(LogTemp, Error, TEXT("Loaded parth from Config File: %s"), *PathToFoliageShadowImposterReplacementDataTable); 136 | 137 | UDataTable* FoliageShadowImposterReplacementDataTable = Cast(StaticLoadObject(UDataTable::StaticClass(), NULL, *PathToFoliageShadowImposterReplacementDataTable)); 138 | for (auto RowMapRow : FoliageShadowImposterReplacementDataTable->GetRowMap()) 139 | { 140 | FFoliageShadowImposterReplacementRow* FoliageShadowImposterReplacementRow = reinterpret_cast(RowMapRow.Value); 141 | 142 | if (FoliageShadowImposterReplacementRow->bShouldReplace) 143 | { 144 | RemoveAllFoliageShadowImpostersForThisMesh(FoliageShadowImposterReplacementRow->FoliageSahdowImposterMesh->GetPathName()); 145 | 146 | AddFoliageShadowImpostersForThisMesh(FoliageShadowImposterReplacementRow->OriginalFoliageMesh->GetPathName(), 147 | FoliageShadowImposterReplacementRow->FoliageSahdowImposterMesh->GetPathName()); 148 | } 149 | } 150 | 151 | FMessageDialog::Open(EAppMsgType::Ok, FText(LOCTEXT("PluginButtonDialogText", "Foliage Shadow Imposters have been added!"))); 152 | } 153 | 154 | void FFoliageShadowImpostersModule::RegisterMenus() 155 | { 156 | // Owner will be used for cleanup in call to UToolMenus::UnregisterOwner 157 | FToolMenuOwnerScoped OwnerScoped(this); 158 | 159 | { 160 | UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window"); 161 | { 162 | FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout"); 163 | Section.AddMenuEntryWithCommandList(FFoliageShadowImpostersCommands::Get().PluginAction, PluginCommands); 164 | } 165 | } 166 | 167 | { 168 | UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar"); 169 | { 170 | FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("PluginTools"); 171 | { 172 | FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFoliageShadowImpostersCommands::Get().PluginAction)); 173 | Entry.SetCommandList(PluginCommands); 174 | } 175 | } 176 | } 177 | } 178 | 179 | #undef LOCTEXT_NAMESPACE 180 | 181 | IMPLEMENT_MODULE(FFoliageShadowImpostersModule, FoliageShadowImposters) --------------------------------------------------------------------------------