From 03c79a2610898bde8d2482365fc9e403c0e28fbc Mon Sep 17 00:00:00 2001 From: "j.foucher" Date: Sat, 11 Apr 2026 10:36:53 +0200 Subject: [PATCH] Add auto-generated selection outline post-process and gizmo polish Selection Outline: - Post-process material auto-generated in C++ (no manual UE editor work) - 8-neighbor edge detection on CustomStencil buffer - Dynamic pixel-accurate offsets via SceneTexture InvSize (any resolution/ratio) - Lerp blending (no overexposure), color AF1500, 1px thickness - BL_SceneColorAfterDOF placement (before TAA, matches stencil resolution) - Deferred creation via OnPostEngineInit (no startup crash) - Material saved as persistent .uasset, loaded at runtime Gizmo Polish: - Arrow shaft thickness halved (0.03), cone tips halved (0.075x0.1) - Gizmo stays at initial position during rotation/scale (no jitter) - Gizmo follows selection during translation Co-Authored-By: Claude Opus 4.6 (1M context) --- .../M_PS_Editor_SelectionOutline.uasset | Bin 61627 -> 28789 bytes .../M_PS_Editor_SelectionOutline2.uasset | Bin 0 -> 69840 bytes .../PS_Editor/GameMode/PS_Editor_Pawn.cpp | 8 +- .../PS_Editor/Gizmo/PS_Editor_Gizmo.cpp | 6 +- .../PS_Editor/Source/PS_Editor/PS_Editor.cpp | 222 ++++++++++++++++++ 5 files changed, 230 insertions(+), 6 deletions(-) create mode 100644 Unreal/Plugins/PS_Editor/Content/M_PS_Editor_SelectionOutline2.uasset diff --git a/Unreal/Plugins/PS_Editor/Content/M_PS_Editor_SelectionOutline.uasset b/Unreal/Plugins/PS_Editor/Content/M_PS_Editor_SelectionOutline.uasset index 39b747f0eeac88551fb3f9858b7745a6406f4c29..176f79ad2d9efc300990944a7313e93032135e12 100644 GIT binary patch literal 28789 zcmd^I34Bb~_rI2^+N<_bk)W+DM8r}h$xK#>L?S_{F*1*gOlD#hgtlUjlv1LlYN^_4 zjjfj2>!sp3bes-i7bs)SnVch7tGOzs<*`OkYX{pIudAD{Q$efQpXzUQ2K?z#8f zcPI8uXtD6l!-o$Cl_R85Swe2nD^NvxEPC@!&HJ&A+$yJDTmIs%E}h!ZysrBOcDxsT zp~CfvWXl`FTQo0E^Cs78G-zUiVa?fZ>#REQ+2yLMXx_NvuNzl~C0zQgSL~`6KHj=} zB+c_b-uwOC3C&kVKG`Jy$oS3rRy41>{r=ZE+5*iFwceg>^J`SM8O;kHIR9j1{H~@e z*VLStUDxnKdzyDOz2L&H;rTZ!f7fnF*e8)~UZ8oa+YkcRz{F%-gu!UHSOepA&a^IB zFbQdPqs5%yu$zo#fz~9%MvuSBh)Ggdm5?Sh**Um_Ca60E(aA>GxyfCM8Uo

uni=AzTQv*&LaIAt6gY+#!$_`xom${lU?Co5o?Y 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 literal 61627 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 diff --git a/Unreal/Plugins/PS_Editor/Content/M_PS_Editor_SelectionOutline2.uasset b/Unreal/Plugins/PS_Editor/Content/M_PS_Editor_SelectionOutline2.uasset new file mode 100644 index 0000000000000000000000000000000000000000..97ce5857667d390fae3e620070f2a31d96a21dc2 GIT binary patch literal 69840 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 B)29Fc literal 0 HcmV?d00001 diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp index f3b8ae5..1a2b16e 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp @@ -343,11 +343,9 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value) } else if (Mode == EPS_Editor_TransformMode::Rotate) { - // Use combined mouse X+Y screen movement to drive rotation angle const float RotationSpeed = 1.0f; const float AngleDelta = (LookInput.X + LookInput.Y) * RotationSpeed; - // Accumulate total angle since drag start GizmoDragAccumulator += AngleDelta; const FQuat RotationQuat(AxisDir, FMath::DegreesToRadians(GizmoDragAccumulator)); @@ -386,7 +384,11 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value) } } - Gizmo->SetActorLocation(SM->GetSelectionPivot()); + // In translate mode, gizmo follows the selection. In rotate/scale, stay at initial position to avoid jitter. + if (Mode == EPS_Editor_TransformMode::Translate) + { + Gizmo->SetActorLocation(SM->GetSelectionPivot()); + } } } diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Gizmo/PS_Editor_Gizmo.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Gizmo/PS_Editor_Gizmo.cpp index fbce330..072d98c 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Gizmo/PS_Editor_Gizmo.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Gizmo/PS_Editor_Gizmo.cpp @@ -114,8 +114,8 @@ UStaticMeshComponent* APS_Editor_Gizmo::CreateArrow(const FName& Name, USceneCom Comp->SetStaticMesh(CylinderMesh.Object); } - // Shaft: radius ~3 units, length ~60 units - Comp->SetRelativeScale3D(FVector(0.06f, 0.06f, 0.6f)); + // Shaft: thin cylinder, length ~60 units + Comp->SetRelativeScale3D(FVector(0.03f, 0.03f, 0.6f)); Comp->SetRelativeRotation(Rotation); const FVector AxisDir = Rotation.RotateVector(FVector::UpVector); @@ -136,7 +136,7 @@ UStaticMeshComponent* APS_Editor_Gizmo::CreateCone(const FName& Name, USceneComp Comp->SetStaticMesh(ConeMesh.Object); } - Comp->SetRelativeScale3D(FVector(0.15f, 0.15f, 0.2f)); + Comp->SetRelativeScale3D(FVector(0.075f, 0.075f, 0.1f)); Comp->SetRelativeRotation(Rotation); const FVector AxisDir = Rotation.RotateVector(FVector::UpVector); diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/PS_Editor.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/PS_Editor.cpp index b3fd8ab..80c694e 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/PS_Editor.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/PS_Editor.cpp @@ -1,10 +1,232 @@ #include "PS_Editor.h" +#if WITH_EDITOR +#include "Materials/Material.h" +#include "MaterialDomain.h" +#include "Materials/MaterialExpressionSceneTexture.h" +#include "Materials/MaterialExpressionAdd.h" +#include "Materials/MaterialExpressionSubtract.h" +#include "Materials/MaterialExpressionMultiply.h" +#include "Materials/MaterialExpressionAbs.h" +#include "Materials/MaterialExpressionSaturate.h" +#include "Materials/MaterialExpressionConstant.h" +#include "Materials/MaterialExpressionConstant4Vector.h" +#include "Materials/MaterialExpressionTextureCoordinate.h" +#include "Materials/MaterialExpressionLinearInterpolate.h" +#include "Materials/MaterialExpressionComponentMask.h" +#include "Materials/MaterialExpressionAppendVector.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "UObject/SavePackage.h" +#include "Misc/PackageName.h" +#endif + #define LOCTEXT_NAMESPACE "FPS_EditorModule" +#if WITH_EDITOR +static void CreateOutlineMaterialAsset() +{ + const FString MaterialPath = TEXT("/PS_Editor/M_PS_Editor_SelectionOutline"); + const FString AssetName = TEXT("M_PS_Editor_SelectionOutline"); + + if (LoadObject(nullptr, *(MaterialPath + TEXT(".") + AssetName))) + { + return; + } + + UPackage* Package = CreatePackage(*MaterialPath); + UMaterial* Mat = NewObject(Package, *AssetName, RF_Public | RF_Standalone); + Mat->MaterialDomain = EMaterialDomain::MD_PostProcess; + Mat->BlendableLocation = BL_SceneColorAfterDOF; // Before TAA, matches stencil buffer resolution + + auto AddExpr = [&](UMaterialExpression* Expr) { Mat->GetExpressionCollection().AddExpression(Expr); }; + + const float ThicknessPixels = 1.0f; + + // ---- TexCoord ---- + UMaterialExpressionTextureCoordinate* TexCoord = NewObject(Mat); + AddExpr(TexCoord); + TexCoord->MaterialExpressionEditorX = -1600; + TexCoord->MaterialExpressionEditorY = 400; + + // ---- Center stencil sample ---- + UMaterialExpressionSceneTexture* StencilCenter = NewObject(Mat); + StencilCenter->SceneTextureId = PPI_CustomStencil; + AddExpr(StencilCenter); + StencilCenter->Coordinates.Connect(0, TexCoord); + StencilCenter->MaterialExpressionEditorX = -1200; + StencilCenter->MaterialExpressionEditorY = 0; + + // ---- Get InvSize from SceneTexture (output index 2) = 1/resolution ---- + // This gives dynamic pixel size regardless of resolution/aspect ratio + UMaterialExpressionMultiply* PixelOffset = NewObject(Mat); + AddExpr(PixelOffset); + PixelOffset->A.Connect(2, StencilCenter); // Output 2 = InvSize (float2) + PixelOffset->ConstB = ThicknessPixels; + PixelOffset->MaterialExpressionEditorX = -1000; + PixelOffset->MaterialExpressionEditorY = 200; + + // Split into X and Y components + UMaterialExpressionComponentMask* OffsetX = NewObject(Mat); + AddExpr(OffsetX); + OffsetX->Input.Connect(0, PixelOffset); + OffsetX->R = true; OffsetX->G = false; OffsetX->B = false; OffsetX->A = false; + OffsetX->MaterialExpressionEditorX = -800; + OffsetX->MaterialExpressionEditorY = 100; + + UMaterialExpressionComponentMask* OffsetY = NewObject(Mat); + AddExpr(OffsetY); + OffsetY->Input.Connect(0, PixelOffset); + OffsetY->R = false; OffsetY->G = true; OffsetY->B = false; OffsetY->A = false; + OffsetY->MaterialExpressionEditorX = -800; + OffsetY->MaterialExpressionEditorY = 300; + + UMaterialExpressionConstant* ZeroConst = NewObject(Mat); + ZeroConst->R = 0.0f; + AddExpr(ZeroConst); + ZeroConst->MaterialExpressionEditorX = -800; + ZeroConst->MaterialExpressionEditorY = 500; + + // Negative offsets + UMaterialExpressionMultiply* NegX = NewObject(Mat); + AddExpr(NegX); + NegX->A.Connect(0, OffsetX); + NegX->ConstB = -1.0f; + NegX->MaterialExpressionEditorX = -800; + NegX->MaterialExpressionEditorY = 0; + + UMaterialExpressionMultiply* NegY = NewObject(Mat); + AddExpr(NegY); + NegY->A.Connect(0, OffsetY); + NegY->ConstB = -1.0f; + NegY->MaterialExpressionEditorX = -800; + NegY->MaterialExpressionEditorY = 400; + + // ---- 8 direction samples ---- + struct FDirDef { UMaterialExpression* XE; UMaterialExpression* YE; }; + FDirDef Dirs[8] = { + { OffsetX, ZeroConst }, // right + { NegX, ZeroConst }, // left + { ZeroConst, OffsetY }, // down + { ZeroConst, NegY }, // up + { OffsetX, OffsetY }, // bottom-right + { NegX, OffsetY }, // bottom-left + { OffsetX, NegY }, // top-right + { NegX, NegY }, // top-left + }; + + UMaterialExpression* EdgeAccum = nullptr; + + for (int32 i = 0; i < 8; ++i) + { + int32 PosY = 100 + i * 160; + + // Append(X, Y) -> float2 offset + UMaterialExpressionAppendVector* OffsetVec = NewObject(Mat); + AddExpr(OffsetVec); + OffsetVec->A.Connect(0, Dirs[i].XE); + OffsetVec->B.Connect(0, Dirs[i].YE); + OffsetVec->MaterialExpressionEditorX = -600; + OffsetVec->MaterialExpressionEditorY = PosY; + + // TexCoord + offset + UMaterialExpressionAdd* UVAdd = NewObject(Mat); + AddExpr(UVAdd); + UVAdd->A.Connect(0, TexCoord); + UVAdd->B.Connect(0, OffsetVec); + UVAdd->MaterialExpressionEditorX = -400; + UVAdd->MaterialExpressionEditorY = PosY; + + // Sample stencil at offset + UMaterialExpressionSceneTexture* StencilN = NewObject(Mat); + StencilN->SceneTextureId = PPI_CustomStencil; + AddExpr(StencilN); + StencilN->Coordinates.Connect(0, UVAdd); + StencilN->MaterialExpressionEditorX = -200; + StencilN->MaterialExpressionEditorY = PosY; + + // abs(center - neighbor) + UMaterialExpressionSubtract* Sub = NewObject(Mat); + AddExpr(Sub); + Sub->A.Connect(0, StencilCenter); + Sub->B.Connect(0, StencilN); + Sub->MaterialExpressionEditorX = 0; + Sub->MaterialExpressionEditorY = PosY; + + UMaterialExpressionAbs* AbsN = NewObject(Mat); + AddExpr(AbsN); + AbsN->Input.Connect(0, Sub); + AbsN->MaterialExpressionEditorX = 200; + AbsN->MaterialExpressionEditorY = PosY; + + if (!EdgeAccum) + { + EdgeAccum = AbsN; + } + else + { + UMaterialExpressionAdd* AccumAdd = NewObject(Mat); + AddExpr(AccumAdd); + AccumAdd->A.Connect(0, EdgeAccum); + AccumAdd->B.Connect(0, AbsN); + AccumAdd->MaterialExpressionEditorX = 400; + AccumAdd->MaterialExpressionEditorY = PosY; + EdgeAccum = AccumAdd; + } + } + + // ---- Saturate edge mask ---- + UMaterialExpressionSaturate* Saturate = NewObject(Mat); + AddExpr(Saturate); + Saturate->Input.Connect(0, EdgeAccum); + Saturate->MaterialExpressionEditorX = 600; + Saturate->MaterialExpressionEditorY = 500; + + // ---- Outline color (orange, float4) ---- + UMaterialExpressionConstant4Vector* OutlineColor = NewObject(Mat); + OutlineColor->Constant = FColor(0xAF, 0x15, 0x00, 0xFF).ReinterpretAsLinear(); + AddExpr(OutlineColor); + OutlineColor->MaterialExpressionEditorX = 600; + OutlineColor->MaterialExpressionEditorY = 700; + + // ---- Scene input ---- + UMaterialExpressionSceneTexture* SceneInput = NewObject(Mat); + SceneInput->SceneTextureId = PPI_PostProcessInput0; + AddExpr(SceneInput); + SceneInput->MaterialExpressionEditorX = 600; + SceneInput->MaterialExpressionEditorY = 300; + + // ---- Lerp(Scene, OutlineColor, EdgeMask) ---- + UMaterialExpressionLinearInterpolate* Lerp = NewObject(Mat); + AddExpr(Lerp); + Lerp->A.Connect(0, SceneInput); + Lerp->B.Connect(0, OutlineColor); + Lerp->Alpha.Connect(0, Saturate); + Lerp->MaterialExpressionEditorX = 800; + Lerp->MaterialExpressionEditorY = 500; + + // ---- Output ---- + 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: Selection outline material auto-generated (8-sample, dynamic resolution)")); +} +#endif + void FPS_EditorModule::StartupModule() { UE_LOG(LogTemp, Log, TEXT("PS_Editor module started.")); + +#if WITH_EDITOR + FCoreDelegates::OnPostEngineInit.AddStatic(&CreateOutlineMaterialAsset); +#endif } void FPS_EditorModule::ShutdownModule()