Files
BallisticsDocs/Source/EasyBallisticsEditor/Private/EBPhysicalMaterialCustomization.cpp
T
2025-07-02 22:40:58 -07:00

212 lines
7.1 KiB
C++

// Copyright 2016 Mookie. All Rights Reserved.
#include "EBPhysicalMaterialCustomization.h"
#include "PropertyEditorModule.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Images/SImage.h"
#include "EditorStyleSet.h"
#include "Engine/Engine.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "ContentBrowserModule.h"
#include "IContentBrowserSingleton.h"
#include "Framework/Notifications/NotificationManager.h"
#include "Widgets/Notifications/SNotificationList.h"
#define LOCTEXT_NAMESPACE "EBPhysicalMaterialCustomization"
TSharedRef<IDetailCustomization> FEBPhysicalMaterialCustomization::MakeInstance()
{
return MakeShareable(new FEBPhysicalMaterialCustomization);
}
void FEBPhysicalMaterialCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
DetailBuilderPtr = &DetailBuilder;
TArray<TWeakObjectPtr<UObject>> Objects;
DetailBuilder.GetObjectsBeingCustomized(Objects);
if (Objects.Num() == 1)
{
PhysicalMaterialPtr = Cast<UPhysicalMaterial>(Objects[0].Get());
if (PhysicalMaterialPtr.IsValid())
{
// Add Ballistics category
IDetailCategoryBuilder& BallisticsCategory = DetailBuilder.EditCategory("Ballistics", LOCTEXT("BallisticsCategory", "Ballistics"), ECategoryPriority::Important);
UEBMaterialPropertiesAsset* CurrentProperties = GetBallisticProperties(PhysicalMaterialPtr.Get());
// Show current ballistic properties if assigned
if (CurrentProperties)
{
BallisticsCategory.AddCustomRow(LOCTEXT("CurrentPropertiesLabel", "Ballistic Properties"))
.NameContent()
[
SNew(STextBlock)
.Text(LOCTEXT("CurrentPropertiesName", "Ballistic Properties"))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(STextBlock)
.Text(FText::FromString(CurrentProperties->GetName()))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(2.0f, 0.0f)
[
SNew(SButton)
.Text(LOCTEXT("EditProperties", "Edit"))
.OnClicked(this, &FEBPhysicalMaterialCustomization::OnEditBallisticProperties)
.ToolTipText(LOCTEXT("EditPropertiesTooltip", "Edit the ballistic properties asset"))
]
];
}
else
{
// Show assignment options
BallisticsCategory.AddCustomRow(LOCTEXT("AssignPropertiesLabel", "Assign Ballistic Properties"))
.WholeRowContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(2.0f, 0.0f)
[
SNew(SButton)
.Text(LOCTEXT("CreateNew", "Create New"))
.OnClicked(this, &FEBPhysicalMaterialCustomization::OnCreateBallisticProperties)
.ToolTipText(LOCTEXT("CreateNewTooltip", "Create a new ballistic properties asset for this material"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(2.0f, 0.0f)
[
SNew(SButton)
.Text(LOCTEXT("AssignExisting", "Assign Existing"))
.OnClicked(this, &FEBPhysicalMaterialCustomization::OnAssignBallisticProperties)
.ToolTipText(LOCTEXT("AssignExistingTooltip", "Assign an existing ballistic properties asset to this material"))
]
];
}
}
}
}
FReply FEBPhysicalMaterialCustomization::OnCreateBallisticProperties()
{
if (PhysicalMaterialPtr.IsValid())
{
// Create new Material Properties Asset
FString PackageName = PhysicalMaterialPtr->GetPackage()->GetName() + TEXT("_BallisticProps");
FString AssetName = PhysicalMaterialPtr->GetName() + TEXT("_BallisticProps");
UPackage* Package = CreatePackage(*PackageName);
UEBMaterialPropertiesAsset* NewAsset = NewObject<UEBMaterialPropertiesAsset>(Package, *AssetName, RF_Public | RF_Standalone);
// Set some sensible defaults based on material name
FString MaterialName = PhysicalMaterialPtr->GetName().ToLower();
if (MaterialName.Contains("steel") || MaterialName.Contains("metal"))
{
NewAsset->MaterialProperties.DensityGPerCm3 = 7.85f;
NewAsset->MaterialProperties.MaterialHardness = 200.0f;
NewAsset->MaterialName = "Steel";
}
else if (MaterialName.Contains("wood"))
{
NewAsset->MaterialProperties.DensityGPerCm3 = 0.6f;
NewAsset->MaterialProperties.MaterialHardness = 30.0f;
NewAsset->MaterialName = "Wood";
}
else if (MaterialName.Contains("concrete"))
{
NewAsset->MaterialProperties.DensityGPerCm3 = 2.4f;
NewAsset->MaterialProperties.MaterialHardness = 100.0f;
NewAsset->MaterialName = "Concrete";
}
// Mark package dirty and register with asset registry
Package->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(NewAsset);
// Associate with physical material
SetBallisticProperties(PhysicalMaterialPtr.Get(), NewAsset);
// Refresh the details panel
if (DetailBuilderPtr)
{
DetailBuilderPtr->ForceRefreshDetails();
}
// Show success notification
FNotificationInfo Info(LOCTEXT("CreatedBallisticProps", "Created ballistic properties asset"));
Info.ExpireDuration = 3.0f;
FSlateNotificationManager::Get().AddNotification(Info);
}
return FReply::Handled();
}
FReply FEBPhysicalMaterialCustomization::OnAssignBallisticProperties()
{
// Open content browser to select existing Material Properties Asset
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
FAssetPickerConfig AssetPickerConfig;
AssetPickerConfig.Filter.ClassPaths.Add(UEBMaterialPropertiesAsset::StaticClass()->GetClassPathName());
AssetPickerConfig.bAllowNullSelection = false;
AssetPickerConfig.OnAssetSelected = FOnAssetSelected::CreateLambda([this](const FAssetData& AssetData)
{
if (UEBMaterialPropertiesAsset* SelectedAsset = Cast<UEBMaterialPropertiesAsset>(AssetData.GetAsset()))
{
SetBallisticProperties(PhysicalMaterialPtr.Get(), SelectedAsset);
if (DetailBuilderPtr)
{
DetailBuilderPtr->ForceRefreshDetails();
}
}
});
ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig);
return FReply::Handled();
}
FReply FEBPhysicalMaterialCustomization::OnEditBallisticProperties()
{
if (UEBMaterialPropertiesAsset* Properties = GetBallisticProperties(PhysicalMaterialPtr.Get()))
{
// Open the asset for editing
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(Properties);
}
return FReply::Handled();
}
UEBMaterialPropertiesAsset* FEBPhysicalMaterialCustomization::GetBallisticProperties(UPhysicalMaterial* PhysMat) const
{
if (!PhysMat) return nullptr;
// For now, use a simple naming convention or metadata
// In a production system, you might add a UPROPERTY to UPhysicalMaterial
FString AssetName = PhysMat->GetName() + TEXT("_BallisticProps");
FString PackageName = PhysMat->GetPackage()->GetName() + TEXT("_BallisticProps");
return LoadObject<UEBMaterialPropertiesAsset>(nullptr, *PackageName, nullptr, LOAD_NoWarn | LOAD_Quiet);
}
void FEBPhysicalMaterialCustomization::SetBallisticProperties(UPhysicalMaterial* PhysMat, UEBMaterialPropertiesAsset* Properties)
{
// In a production system, you would store this reference properly
// For now, we rely on naming conventions and the Material Response Map system
}
#undef LOCTEXT_NAMESPACE