// 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 FEBPhysicalMaterialCustomization::MakeInstance() { return MakeShareable(new FEBPhysicalMaterialCustomization); } void FEBPhysicalMaterialCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { DetailBuilderPtr = &DetailBuilder; TArray> Objects; DetailBuilder.GetObjectsBeingCustomized(Objects); if (Objects.Num() == 1) { PhysicalMaterialPtr = Cast(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(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("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(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()->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(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