#pragma once #include "CoreMinimal.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" #include "Materials/Material.h" #include "Materials/MaterialInterface.h" #include "Materials/MaterialInstanceConstant.h" #include "Material_ReparentInstance.generated.h" // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- UCLASS() class UMCP_Material_ReparentInstance : public UObject, public IMCPHandler { GENERATED_BODY() public: UPROPERTY(meta=(Description="Material Instance name or path to reparent")) FString MaterialInstance; UPROPERTY(meta=(Description="New parent material name or path (Material or Material Instance)")) FString NewParent; UPROPERTY(meta=(Optional, Description="If true, validate without applying changes")) bool DryRun = false; virtual FString GetDescription() const override { return TEXT("Change the parent material of a Material Instance. " "Validates against circular parent chains."); } virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override { // Load the Material Instance MCPAssets Assets; if (!Assets.Exact(MaterialInstance).Errors(Result).ENone().ETwo().Load()) return; UMaterialInstanceConstant* MI = Assets.Object(); // Load new parent (Material or MaterialInstance) MCPAssets ParentAssets; ParentAssets.NoScans(); ParentAssets.Scan(); ParentAssets.Scan(); if (!ParentAssets.Exact(NewParent).Errors(Result).ENone().ETwo().Load()) return; UMaterialInterface* NewParentObj = ParentAssets.Object(); // Prevent circular parenting UMaterialInterface* Check = NewParentObj; while (Check) { if (Check == MI) { Result.Appendf(TEXT("ERROR: Reparenting to '%s' would create a circular parent chain.\n"), *NameOf(NewParentObj)); return; } UMaterialInstanceConstant* CheckMI = Cast(Check); if (!CheckMI) break; Check = CheckMI->Parent; } FString OldParentName = MI->Parent ? NameOf(MI->Parent) : TEXT("None"); if (DryRun) { Result.Appendf(TEXT("[DRY RUN] Would reparent %s: %s -> %s\n"), *MCPUtils::FormatName(MI), *OldParentName, *NameOf(NewParentObj)); return; } MCPUtils::PreEdit({MI}); MI->Parent = NewParentObj; MCPUtils::PostEdit({MI}); MCPUtils::SaveGenericPackage(MI); Result.Appendf(TEXT("Reparented %s: %s -> %s\n"), *MCPUtils::FormatName(MI), *OldParentName, *NameOf(NewParentObj)); } private: FString NameOf(UMaterialInterface* Obj) { if (UMaterial* M = Cast(Obj)) return MCPUtils::FormatName(M); if (UMaterialInstance* MI = Cast(Obj)) return MCPUtils::FormatName(MI); return Obj->GetPathName(); } };