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 39b747f..176f79a 100644 Binary files a/Unreal/Plugins/PS_Editor/Content/M_PS_Editor_SelectionOutline.uasset and b/Unreal/Plugins/PS_Editor/Content/M_PS_Editor_SelectionOutline.uasset differ 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 0000000..97ce585 Binary files /dev/null and b/Unreal/Plugins/PS_Editor/Content/M_PS_Editor_SelectionOutline2.uasset differ 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()