I am attempting to create APostProcessVolume(PPV) in code so I can highlight different parts of a mesh which is comprised of several smaller meshes in different colors. Like highlighting the individual hand mesh of a character one color, and the individual leg mesh a different color. While leaving the rest of the mesh body unaffected. This is what I want to see in the Brush Settings after my PPV spawns in the editor.
I want to spawn the PPV with a brush volume that is just big enough to encompass the individual smaller pieces of the mesh and highlight them a unique color. I figured out how to spawn the PPV, set the blendable attributes, but got stuck on making a brush for it.
Searching on the forums I found this guy that attempted this in 2014, but got nowhere.
And this guy who apparently figured out how to create a brush, but I tried his method and got linker errors.
Any help would be appreciated, especially if there is a simpler way of doing this.
My full code is below, you’ll just need to swap in your own materials in the Init() and use your own meshes to test with.
Header:
#pragma once
#include "CoreMinimal.h"
#include "Engine/PostProcessVolume.h"
#include "HighlightManager.generated.h"
UENUM(BlueprintType)
enum class EHighlightColor : uint8
{
GLOW_RED = 0,
GLOW_BLUE,
GLOW_GREEN,
GLOW_YELLOW
};
USTRUCT(BlueprintType)
struct MYTEST_API FHighlight
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite)
APostProcessVolume* PPV;
UPROPERTY(BlueprintReadWrite)
TArray<UPrimitiveComponent*> Meshes;
UPROPERTY(BlueprintReadWrite)
bool Active;
FHighlight()
{
PPV = nullptr;
Active = false;
}
FHighlight(UPrimitiveComponent* MeshToHighlight)
{
PPV = nullptr;
Active = false;
Meshes.Add(MeshToHighlight);
}
};
UCLASS()
class MYTEST_API UHighlightManager : public UGameInstanceSubsystem
{
GENERATED_BODY()
//Automatically fills in the weighted blendables into a passed in PPV
void FillWeightedBlendables(APostProcessVolume* PPV);
//Stores the loaded materials on startup for later use.
TArray<UMaterialInstance*> Mats;
//Holds on to all highlights that have been spawned
TArray<FHighlight> Highlights;
public:
UHighlightManager();
~UHighlightManager();
//Loads the materials for use later.
void Init();
UFUNCTION(BlueprintCallable, Category = "Highlighting | Mesh Highlight")
void HighlightMesh(UPrimitiveComponent* MeshToToggle, EHighlightColor Color);
};
CPP:
#include "_General/Managers/Highlight/HighlightManager.h"
#include "Components/PrimitiveComponent.h"
#include "Kismet/GameplayStatics.h"
#include "ActorFactories/ActorFactory.h"
#include "Editor.h"
#include "ActorFactories/ActorFactoryBoxVolume.h"
#include "Materials/MaterialInstance.h"
#include "Engine/Brush.h"
#include "Engine/BrushBuilder.h"
#include "Builders/CubeBuilder.h"
UHighlightManager::UHighlightManager()
{
Init();
}
UHighlightManager::~UHighlightManager()
{
}
void UHighlightManager::Init()
{
Mats.Add(LoadObject<UMaterialInstance>(nullptr, TEXT("/Game/Art/PPMI_HighlightGlowViewer.PPMI_HighlightGlowViewer")));
Mats.Add(LoadObject<UMaterialInstance>(nullptr, TEXT("/Game/Art/PPMI_HighlightGlowVIE.PPMI_HighlightGlowVIE")));
Mats.Add(LoadObject<UMaterialInstance>(nullptr, TEXT("/Game/Art/PPMI_DetectiveVision.PPMI_DetectiveVision")));
Mats.Add(LoadObject<UMaterialInstance>(nullptr, TEXT("/Game/Art/PPMI_DetectiveVision_Blue.PPMI_DetectiveVision_Blue")));
Mats.Add(LoadObject<UMaterialInstance>(nullptr, TEXT("/Game/Art/PPMI_DetectiveVision_Green.PPMI_DetectiveVision_Green")));
Mats.Add(LoadObject<UMaterialInstance>(nullptr, TEXT("/Game/Art/PPMI_DetectiveVision_Yellow.PPMI_DetectiveVision_Yellow")));
}
void UHighlightManager::FillWeightedBlendables(APostProcessVolume* PPV)
{
for (int i = 0; i < Mats.Num(); i++)
{
UMaterialInstanceDynamic* MatInstance = UMaterialInstanceDynamic::Create(Mats[i], this);
FWeightedBlendable Blendable;
Blendable.Weight = 0.0f;
Blendable.Object = MatInstance;
PPV->Settings.WeightedBlendables.Array.Add(Blendable);
}
}
void UHighlightManager::HighlightMesh(UPrimitiveComponent* MeshToToggle, EHighlightColor Color)
{
UWorld* CurrentWorld = GetWorld();
FHighlight NewHighlight(MeshToToggle);
APostProcessVolume* PPV = (APostProcessVolume*)(CurrentWorld->SpawnActor(APostProcessVolume::StaticClass(), &MeshToToggle->GetComponentTransform()));
FillWeightedBlendables(PPV);
//PPV->bUnbound = true; //Works for 1 mesh, but need to have smaller volume on a per mesh basis
PPV->SpawnCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
//PPV->Brush = ???;
//PPV->BrushBuilder = ???;
//PPV->BrushColor = ???;
//PPV->BrushType = ???;
//PPV->BrushShape, not sure how to get access to this one, or if it's named something else than what is shown in the editor.
//Possible solution is the commented out code block below, but results in linker errors.
//https://forums.unrealengine.com/t/spawn-new-box-brush-in-c/374916
//ABrush* NewBrush = CurrentWorld->SpawnBrush();
//PPV->BrushBuilder = NewObject<UBrushBuilder>(NewBrush, UCubeBuilder::StaticClass(), NAME_None, RF_Transactional); //Why linker errors?
//PPV->Brush = NewObject<UModel>(NewBrush, NAME_None, RF_Transactional);
//PPV->Brush->Initialize(NewBrush, false);
//PPV->BrushType = EBrushType::Brush_Add;
//PPV->BrushBuilder->Build(NewBrush->GetWorld(), NewBrush);
//PPV->SetNeedRebuild(NewBrush->GetLevel());
//GEditor->RebuildAlteredBSP();
NewHighlight.PPV = PPV;
Highlights.Add(NewHighlight);
switch (Color)
{
case EHighlightColor::GLOW_RED:
NewHighlight.PPV->Settings.WeightedBlendables.Array[0].Weight = 1.0f;
break;
case EHighlightColor::GLOW_BLUE:
NewHighlight.PPV->Settings.WeightedBlendables.Array[3].Weight = 1.0f;
break;
case EHighlightColor::GLOW_GREEN:
NewHighlight.PPV->Settings.WeightedBlendables.Array[4].Weight = 1.0f;
break;
case EHighlightColor::GLOW_YELLOW:
NewHighlight.PPV->Settings.WeightedBlendables.Array[5].Weight = 1.0f;
break;
}
//If Mesh To Toggle is Valid
if (MeshToToggle->IsValidLowLevel())
{
//Enable Custom Render Depth on MeshToToggle
MeshToToggle->SetRenderCustomDepth(true);
NewHighlight.Active = true;
}
}
Here is how I was testing my code. This is done in the game level so I can drag and drop mesh references into the event graph. You just need to pass a valid mesh component to the HighlighMesh function, be it an individual mesh, or a sub mesh of a larger mesh.
Again, any help on this will be appreciated. Thank you in advance!