CustomDepth stencil 2 on spline meshes, SplineNetwork rebuild, PP occluded (disabled)

- Enable RenderCustomDepth + stencil value 2 on spline tube mesh
- Restore stencil 2 on deselect (selection sets stencil 1 for outline)
- Auto-generated M_PS_Editor_SplineOccluded PP material (stencil + depth compare)
- PP occluded effect disabled for now (depth comparison unreliable)
- AISpline triggers SplineNetwork::RebuildNetwork on BeginPlay (next tick)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-14 19:18:41 +02:00
parent 24e50ab919
commit 6c39b5888f
4 changed files with 174 additions and 5 deletions

View File

@@ -53,6 +53,9 @@ void APS_Editor_Pawn::BeginPlay()
OutlinePostProcess->Settings.WeightedBlendables.Array.Add(FWeightedBlendable(1.0f, OutlineMat));
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Selection outline material loaded"));
}
// TODO: Spline occluded PP material — disabled for now, depth comparison unreliable
// Consider translucent second-pass approach instead of post-process
else
{
UE_LOG(LogTemp, Warning, TEXT("PS_Editor: Outline material /PS_Editor/M_PS_Editor_SelectionOutline not found. "

View File

@@ -16,6 +16,7 @@
#include "Materials/MaterialExpressionComponentMask.h"
#include "Materials/MaterialExpressionAppendVector.h"
#include "Materials/MaterialExpressionVectorParameter.h"
#include "Materials/MaterialExpressionIf.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "UObject/SavePackage.h"
#include "Misc/PackageName.h"
@@ -220,6 +221,168 @@ static void CreateOutlineMaterialAsset()
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Selection outline material auto-generated (8-sample, dynamic resolution)"));
}
static void CreateSplineOccludedMaterialAsset()
{
const FString MaterialPath = TEXT("/PS_Editor/M_PS_Editor_SplineOccluded");
const FString AssetName = TEXT("M_PS_Editor_SplineOccluded");
if (LoadObject<UMaterial>(nullptr, *(MaterialPath + TEXT(".") + AssetName)))
{
return;
}
UPackage* Package = CreatePackage(*MaterialPath);
UMaterial* Mat = NewObject<UMaterial>(Package, *AssetName, RF_Public | RF_Standalone);
Mat->MaterialDomain = EMaterialDomain::MD_PostProcess;
Mat->BlendableLocation = BL_SceneColorAfterDOF;
auto AddExpr = [&](UMaterialExpression* Expr) { Mat->GetExpressionCollection().AddExpression(Expr); };
// ---- Scene inputs ----
UMaterialExpressionSceneTexture* SceneColor = NewObject<UMaterialExpressionSceneTexture>(Mat);
SceneColor->SceneTextureId = PPI_PostProcessInput0;
AddExpr(SceneColor);
SceneColor->MaterialExpressionEditorX = -400;
SceneColor->MaterialExpressionEditorY = 0;
UMaterialExpressionSceneTexture* CustomStencil = NewObject<UMaterialExpressionSceneTexture>(Mat);
CustomStencil->SceneTextureId = PPI_CustomStencil;
AddExpr(CustomStencil);
CustomStencil->MaterialExpressionEditorX = -400;
CustomStencil->MaterialExpressionEditorY = 200;
UMaterialExpressionSceneTexture* CustomDepthTex = NewObject<UMaterialExpressionSceneTexture>(Mat);
CustomDepthTex->SceneTextureId = PPI_CustomDepth;
AddExpr(CustomDepthTex);
CustomDepthTex->MaterialExpressionEditorX = -400;
CustomDepthTex->MaterialExpressionEditorY = 400;
UMaterialExpressionSceneTexture* SceneDepthTex = NewObject<UMaterialExpressionSceneTexture>(Mat);
SceneDepthTex->SceneTextureId = PPI_SceneDepth;
AddExpr(SceneDepthTex);
SceneDepthTex->MaterialExpressionEditorX = -400;
SceneDepthTex->MaterialExpressionEditorY = 600;
// ---- Stencil == 2 check: abs(stencil - 2) < 0.5 ----
UMaterialExpressionConstant* StencilTarget = NewObject<UMaterialExpressionConstant>(Mat);
StencilTarget->R = 2.0f;
AddExpr(StencilTarget);
StencilTarget->MaterialExpressionEditorX = -200;
StencilTarget->MaterialExpressionEditorY = 200;
UMaterialExpressionSubtract* StencilDiff = NewObject<UMaterialExpressionSubtract>(Mat);
AddExpr(StencilDiff);
StencilDiff->A.Connect(0, CustomStencil);
StencilDiff->B.Connect(0, StencilTarget);
StencilDiff->MaterialExpressionEditorX = 0;
StencilDiff->MaterialExpressionEditorY = 200;
UMaterialExpressionAbs* StencilAbs = NewObject<UMaterialExpressionAbs>(Mat);
AddExpr(StencilAbs);
StencilAbs->Input.Connect(0, StencilDiff);
StencilAbs->MaterialExpressionEditorX = 200;
StencilAbs->MaterialExpressionEditorY = 200;
// Constants for IF outputs
UMaterialExpressionConstant* One = NewObject<UMaterialExpressionConstant>(Mat);
One->R = 1.0f;
AddExpr(One);
One->MaterialExpressionEditorX = 200;
One->MaterialExpressionEditorY = 100;
UMaterialExpressionConstant* Zero = NewObject<UMaterialExpressionConstant>(Mat);
Zero->R = 0.0f;
AddExpr(Zero);
Zero->MaterialExpressionEditorX = 200;
Zero->MaterialExpressionEditorY = 350;
UMaterialExpressionConstant* Half = NewObject<UMaterialExpressionConstant>(Mat);
Half->R = 0.5f;
AddExpr(Half);
Half->MaterialExpressionEditorX = 200;
Half->MaterialExpressionEditorY = 250;
// IF(Half > StencilAbs, 1, 0) → IsSpline
UMaterialExpressionIf* IsSpline = NewObject<UMaterialExpressionIf>(Mat);
AddExpr(IsSpline);
IsSpline->A.Connect(0, Half);
IsSpline->B.Connect(0, StencilAbs);
IsSpline->AGreaterThanB.Connect(0, One);
IsSpline->AEqualsB.Connect(0, Zero);
IsSpline->ALessThanB.Connect(0, Zero);
IsSpline->MaterialExpressionEditorX = 400;
IsSpline->MaterialExpressionEditorY = 200;
// ---- Depth comparison: CustomDepth > SceneDepth + 1 ----
UMaterialExpressionConstant* DepthBias = NewObject<UMaterialExpressionConstant>(Mat);
DepthBias->R = 1.0f;
AddExpr(DepthBias);
DepthBias->MaterialExpressionEditorX = -200;
DepthBias->MaterialExpressionEditorY = 600;
UMaterialExpressionAdd* SceneDepthBiased = NewObject<UMaterialExpressionAdd>(Mat);
AddExpr(SceneDepthBiased);
SceneDepthBiased->A.Connect(0, SceneDepthTex);
SceneDepthBiased->B.Connect(0, DepthBias);
SceneDepthBiased->MaterialExpressionEditorX = 0;
SceneDepthBiased->MaterialExpressionEditorY = 600;
// IF(CustomDepth > SceneDepth+1, 1, 0) → IsOccluded
UMaterialExpressionIf* IsOccluded = NewObject<UMaterialExpressionIf>(Mat);
AddExpr(IsOccluded);
IsOccluded->A.Connect(0, CustomDepthTex);
IsOccluded->B.Connect(0, SceneDepthBiased);
IsOccluded->AGreaterThanB.Connect(0, One);
IsOccluded->AEqualsB.Connect(0, Zero);
IsOccluded->ALessThanB.Connect(0, Zero);
IsOccluded->MaterialExpressionEditorX = 400;
IsOccluded->MaterialExpressionEditorY = 500;
// ---- Final mask = IsSpline * IsOccluded * 0.6 ----
UMaterialExpressionMultiply* CombinedMask = NewObject<UMaterialExpressionMultiply>(Mat);
AddExpr(CombinedMask);
CombinedMask->A.Connect(0, IsSpline);
CombinedMask->B.Connect(0, IsOccluded);
CombinedMask->MaterialExpressionEditorX = 600;
CombinedMask->MaterialExpressionEditorY = 350;
UMaterialExpressionMultiply* FinalMask = NewObject<UMaterialExpressionMultiply>(Mat);
AddExpr(FinalMask);
FinalMask->A.Connect(0, CombinedMask);
FinalMask->ConstB = 0.6f;
FinalMask->MaterialExpressionEditorX = 800;
FinalMask->MaterialExpressionEditorY = 350;
// ---- Gray color ----
UMaterialExpressionConstant4Vector* GrayColor = NewObject<UMaterialExpressionConstant4Vector>(Mat);
GrayColor->Constant = FLinearColor(0.35f, 0.35f, 0.35f, 1.0f);
AddExpr(GrayColor);
GrayColor->MaterialExpressionEditorX = 600;
GrayColor->MaterialExpressionEditorY = 600;
// ---- Lerp(SceneColor, GrayColor, FinalMask) ----
UMaterialExpressionLinearInterpolate* Lerp = NewObject<UMaterialExpressionLinearInterpolate>(Mat);
AddExpr(Lerp);
Lerp->A.Connect(0, SceneColor);
Lerp->B.Connect(0, GrayColor);
Lerp->Alpha.Connect(0, FinalMask);
Lerp->MaterialExpressionEditorX = 1000;
Lerp->MaterialExpressionEditorY = 200;
Mat->GetEditorOnlyData()->EmissiveColor.Connect(0, Lerp);
Mat->PreEditChange(nullptr);
Mat->PostEditChange();
FString PackageFilename = FPackageName::LongPackageNameToFilename(MaterialPath, FPackageName::GetAssetPackageExtension());
FSavePackageArgs SaveArgs;
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone;
UPackage::SavePackage(Package, Mat, *PackageFilename, SaveArgs);
FAssetRegistryModule::AssetCreated(Mat);
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Spline occluded PP material auto-generated (stencil 2, depth compare)"));
}
static void CreateSplineLineMaterialAsset()
{
const FString MaterialPath = TEXT("/PS_Editor/M_PS_Editor_SplineLine");
@@ -270,6 +433,7 @@ void FPS_EditorModule::StartupModule()
#if WITH_EDITOR
FCoreDelegates::OnPostEngineInit.AddStatic(&CreateOutlineMaterialAsset);
FCoreDelegates::OnPostEngineInit.AddStatic(&CreateSplineLineMaterialAsset);
FCoreDelegates::OnPostEngineInit.AddStatic(&CreateSplineOccludedMaterialAsset);
#endif
}

View File

@@ -30,6 +30,8 @@ APS_Editor_SplineActor::APS_Editor_SplineActor()
SplineMeshComp->SetCollisionResponseToChannel(ECC_Camera, ECR_Block);
SplineMeshComp->bUseComplexAsSimpleCollision = true;
SplineMeshComp->SetCastShadow(false);
SplineMeshComp->SetRenderCustomDepth(true);
SplineMeshComp->SetCustomDepthStencilValue(2);
// Handle parent
HandleRoot = CreateDefaultSubobject<USceneComponent>(TEXT("HandleRoot"));
@@ -421,15 +423,12 @@ void APS_Editor_SplineActor::SetSelected(bool bSelected)
if (SplineMeshComp)
{
UMaterialInstanceDynamic* Mat = bSelected ? SplineMatSelected : SplineMat;
UE_LOG(LogTemp, Log, TEXT("PS_Editor: SetSelected(%s) - SplineMat=%s, SplineMatSelected=%s, Using=%s"),
bSelected ? TEXT("true") : TEXT("false"),
SplineMat ? TEXT("valid") : TEXT("NULL"),
SplineMatSelected ? TEXT("valid") : TEXT("NULL"),
Mat ? TEXT("valid") : TEXT("NULL"));
if (Mat)
{
SplineMeshComp->SetMaterial(0, Mat);
}
// Restore stencil 2 for occluded PP effect when deselected (selection sets it to 1)
SplineMeshComp->SetCustomDepthStencilValue(bSelected ? 1 : 2);
}
// Center cube: selected color when selected, normal color otherwise