Material instances are now pretty polished. We can create them, and dump and modify all types of parameters. Factory-based creation is now more polished, and it opens new assets in the editor if it can.
This commit is contained in:
@@ -296,8 +296,9 @@ public:
|
||||
if (!Props[0].SetObject(ClassObj, WingOut::Stdout)) return;
|
||||
|
||||
// Create the asset using the factory.
|
||||
UObject *Blueprint = WingFactories::CreateAsset(Path, Factory, WingOut::Stdout);
|
||||
if (Blueprint == nullptr) return;
|
||||
UObject *Object = WingFactories::CreateAsset(
|
||||
Path, Factory, nullptr, WingOut::Stdout);
|
||||
if (Object == nullptr) return;
|
||||
WingOut::Stdout.Printf(TEXT("Created.\n"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "Factories/MaterialInstanceConstantFactoryNew.h"
|
||||
#include "WingFactories.h"
|
||||
#include "Create_MaterialInstance.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Create_MaterialInstance : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, meta=(Description="Full asset path for the new Material Instance (e.g. '/Game/Materials/MI_GoldShiny')"))
|
||||
FString AssetPath;
|
||||
|
||||
UPROPERTY(EditAnywhere, meta=(Description="Parent material package path (Material or Material Instance)"))
|
||||
FString ParentMaterial;
|
||||
|
||||
virtual void Register() override
|
||||
{
|
||||
UWingServer::AddHandler(this,
|
||||
TEXT("Create a new Material Instance Constant asset with a specified parent material."));
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
// Verify that the asset path is valid and available.
|
||||
if (!WingFactories::CheckNewAssetPath(AssetPath, WingOut::Stdout)) return;
|
||||
|
||||
// Load parent material by package path.
|
||||
WingFetcher F(WingOut::Stdout);
|
||||
UMaterialInterface* ParentObj = F.Asset(ParentMaterial).Cast<UMaterialInterface>();
|
||||
if (!ParentObj) return;
|
||||
|
||||
// Create a factory, then use it to create an asset.
|
||||
UMaterialInstanceConstantFactoryNew* Factory =
|
||||
NewObject<UMaterialInstanceConstantFactoryNew>();
|
||||
if (!WingUtils::CheckNewObjectNotNull(
|
||||
Factory, TEXT("factory"), WingOut::Stdout)) return;
|
||||
UObject* MIO = WingFactories::CreateAsset(AssetPath,
|
||||
Factory, UMaterialInstanceConstant::StaticClass(), WingOut::Stdout);
|
||||
if (!MIO) return;
|
||||
|
||||
// Set parent.
|
||||
UMaterialInstanceConstant* MI = Cast<UMaterialInstanceConstant>(MIO);
|
||||
MI->Parent = ParentObj;
|
||||
|
||||
WingOut::Stdout.Printf(TEXT("Created\n"));
|
||||
}
|
||||
};
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
for (UClass* Class : FactoryClasses)
|
||||
{
|
||||
if (!WingFactories::CanCreate(Class)) continue;
|
||||
if (WingFactories::IsSpecialCase(Class)) continue;
|
||||
if (WingFactories::GetParameterNames(Class).Num() > 0) continue;
|
||||
|
||||
FString FactoryName = WingFactories::DeriveFactoryName(Class);
|
||||
@@ -49,7 +50,8 @@ public:
|
||||
{
|
||||
UClass* FactoryClass = Configuration->FactoryClass.Get();
|
||||
UFactory* Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass);
|
||||
WingFactories::CreateAsset(Path, Factory, WingOut::Stdout);
|
||||
UObject *Obj = WingFactories::CreateAsset(Path, Factory, nullptr, WingOut::Stdout);
|
||||
if (!Obj) return;
|
||||
WingOut::Stdout.Printf(TEXT("Created.\n"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
#include "PackageTools.h"
|
||||
#include "WingProperty.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Editor.h"
|
||||
#include "Kismet2/EnumEditorUtils.h"
|
||||
#include "Subsystems/AssetEditorSubsystem.h"
|
||||
|
||||
bool WingFactories::CheckNewAssetPath(const FString& Path, WingOut Errors)
|
||||
{
|
||||
@@ -39,6 +41,14 @@ bool WingFactories::IsBlacklisted(TSubclassOf<UFactory> FactoryClass)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WingFactories::IsSpecialCase(TSubclassOf<UFactory> FactoryClass)
|
||||
{
|
||||
if (FactoryClass == nullptr) return false;
|
||||
FName Name = FactoryClass->GetFName();
|
||||
if (Name == TEXT("MaterialInstanceConstantFactoryNew")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WingFactories::CanCreate(TSubclassOf<UFactory> FactoryClass)
|
||||
{
|
||||
if (FactoryClass == nullptr) return false;
|
||||
@@ -53,7 +63,8 @@ TArray<FName> WingFactories::GetParameterNames(TSubclassOf<UFactory> FactoryClas
|
||||
return FWingProperty::GetVisibleNames(FactoryClass);
|
||||
}
|
||||
|
||||
UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, WingOut Errors)
|
||||
UObject* WingFactories::CreateAsset(
|
||||
const FString& Path, UFactory* Factory, UClass *CheckIs, WingOut Errors)
|
||||
{
|
||||
// Check the blacklist.
|
||||
if (IsBlacklisted(Factory->GetClass()))
|
||||
@@ -84,9 +95,28 @@ UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, Wing
|
||||
GWarn
|
||||
);
|
||||
|
||||
// If the factory created nothing, report the error.
|
||||
if (NewAsset == nullptr)
|
||||
{
|
||||
Errors.Printf(TEXT("ERROR: Factory '%s' failed to create an object\n"),
|
||||
*Factory->GetClass()->GetName());
|
||||
}
|
||||
|
||||
// Verify the class of the created object.
|
||||
if ((NewAsset != nullptr) && (CheckIs != nullptr))
|
||||
{
|
||||
if (!NewAsset->IsA(CheckIs))
|
||||
{
|
||||
Errors.Printf(TEXT("ERROR: factory did not create a '%s'\n"),
|
||||
*CheckIs->GetName());
|
||||
NewAsset = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// If the asset was created, turn the package into a
|
||||
// permanent package and encourge the editor to save it
|
||||
// to disk. Otherwise, mark the package for deletion.
|
||||
// to disk. Also, open the asset in the editor if possible.
|
||||
// Otherwise, mark the package for deletion.
|
||||
if (NewAsset)
|
||||
{
|
||||
Package->ClearFlags(RF_Transient);
|
||||
@@ -94,10 +124,11 @@ UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, Wing
|
||||
Package->MarkPackageDirty();
|
||||
FAssetRegistryModule::AssetCreated(NewAsset);
|
||||
UWingServer::AddTouchedObject(NewAsset);
|
||||
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
|
||||
if (Sub) Sub->OpenEditorForAsset(NewAsset);
|
||||
}
|
||||
else
|
||||
{
|
||||
Errors.Printf(TEXT("ERROR: Factory '%s' failed to create an object\n"), *Factory->GetClass()->GetName());
|
||||
Package->ClearDirtyFlag();
|
||||
Package->MarkAsGarbage();
|
||||
return nullptr;
|
||||
@@ -129,4 +160,3 @@ UObject* UEnumFactoryWing::FactoryCreateNew(UClass* Class, UObject* InParent, FN
|
||||
}
|
||||
return FEnumEditorUtils::CreateUserDefinedEnum(InParent, Name, Flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ FWingParameterEditor::InfoMetaMap FWingParameterEditor::GetMaterialParameters(UM
|
||||
|
||||
bool FWingParameterEditor::ReportBadType(EMaterialParameterType Type, WingOut Errors)
|
||||
{
|
||||
Errors.Printf(TEXT("Material parameter type #%d is not a valid type.\n"));
|
||||
Errors.Printf(TEXT("Material parameter type #%d is not a valid type.\n"), int(Type));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -276,32 +276,21 @@ bool FWingParameterEditor::AddOverride(
|
||||
bool FWingParameterEditor::RemoveOverride(
|
||||
const Info &ID, UMaterialInstanceConstant *MI, WingOut Errors)
|
||||
{
|
||||
// Remove the override from all parameter arrays.
|
||||
auto RemoveFrom = [&](auto& Arr) {
|
||||
return Arr.RemoveAll([&](auto& Entry) { return Entry.ParameterInfo == ID; });
|
||||
};
|
||||
|
||||
int32 Removed = 0;
|
||||
switch (ID.Type)
|
||||
InfoMetaMap AllParams = GetMaterialParameters(MI);
|
||||
Metadata *Target = FindParameter(AllParams, ID, Errors);
|
||||
if (Target == nullptr) return false;
|
||||
if (!Target->bOverride)
|
||||
{
|
||||
case EMaterialParameterType::Scalar: Removed += RemoveFrom(MI->ScalarParameterValues); break;
|
||||
case EMaterialParameterType::Vector: Removed += RemoveFrom(MI->VectorParameterValues); break;
|
||||
case EMaterialParameterType::DoubleVector: Removed += RemoveFrom(MI->DoubleVectorParameterValues); break;
|
||||
case EMaterialParameterType::Texture: Removed += RemoveFrom(MI->TextureParameterValues); break;
|
||||
case EMaterialParameterType::TextureCollection: Removed += RemoveFrom(MI->TextureCollectionParameterValues); break;
|
||||
case EMaterialParameterType::Font: Removed += RemoveFrom(MI->FontParameterValues); break;
|
||||
case EMaterialParameterType::RuntimeVirtualTexture: Removed += RemoveFrom(MI->RuntimeVirtualTextureParameterValues); break;
|
||||
case EMaterialParameterType::SparseVolumeTexture: Removed += RemoveFrom(MI->SparseVolumeTextureParameterValues); break;
|
||||
case EMaterialParameterType::StaticSwitch: Errors.Printf(TEXT("Removal of static switches not implemented")); return false;
|
||||
case EMaterialParameterType::StaticComponentMask: Errors.Printf(TEXT("Removal of static component masks not implemented")); return false;
|
||||
case EMaterialParameterType::Num: Errors.Printf(TEXT("Parameter Type is not valid")); return false;
|
||||
case EMaterialParameterType::None: Errors.Printf(TEXT("Parameter Type is not valid")); return false;
|
||||
Errors.Printf(TEXT("Parameter '%s' is inherited and not overridden"), *StringID(ID));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Removed == 0)
|
||||
FMaterialInstanceParameterUpdateContext Update(MI, EMaterialInstanceClearParameterFlag::All);
|
||||
for (const auto &[Info, Meta] : AllParams)
|
||||
{
|
||||
Errors.Printf(TEXT("No override found for parameter '%s'"), *StringID(ID));
|
||||
return false;
|
||||
if (!Meta.bOverride) continue;
|
||||
if (Info == ID) continue;
|
||||
Update.SetParameterValueEditorOnly(Info, Meta);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -354,7 +354,6 @@ bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, W
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Common Error Reporting
|
||||
// ============================================================
|
||||
@@ -396,6 +395,16 @@ bool WingUtils::CheckCanRename(UEdGraphNode* Node, const FString &Name, WingOut
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WingUtils::CheckNewObjectNotNull(UObject *Obj, const TCHAR *Kind, WingOut Errors)
|
||||
{
|
||||
if (Obj == nullptr)
|
||||
{
|
||||
Errors.Printf(TEXT("NewObject failed to create a %s"), Kind);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Reparent validation
|
||||
// ============================================================
|
||||
|
||||
@@ -13,8 +13,12 @@ class WingFactories
|
||||
public:
|
||||
// Create an asset on disk, using a factory instance.
|
||||
// Returns the main object. If there are problems,
|
||||
// prints error messages and returns nullptr.
|
||||
static UObject *CreateAsset(const FString &Path, UFactory *Factory, WingOut Errors);
|
||||
// prints error messages and returns nullptr. If
|
||||
// CheckIs is nonnull, this does a final check to verify
|
||||
// that the created asset is a child of the specified
|
||||
// class.
|
||||
static UObject *CreateAsset(
|
||||
const FString &Path, UFactory *Factory, UClass *CheckIs, WingOut Errors);
|
||||
|
||||
// Some factories are blacklisted, mainly because they
|
||||
// pop up dialog boxes. In those cases, we deal with it
|
||||
@@ -22,9 +26,16 @@ public:
|
||||
// and then implementing replacement factories.
|
||||
static bool IsBlacklisted(TSubclassOf<UFactory> FactoryClass);
|
||||
|
||||
// Some factories require special-case attention. These
|
||||
// factories are valid factories that can be used by handlers,
|
||||
// and are not blacklisted. However, they should not be
|
||||
// auto-registered by handlers that do bulk registration
|
||||
// of large numbers of factories.
|
||||
static bool IsSpecialCase(TSubclassOf<UFactory> FactoryClass);
|
||||
|
||||
// Check if the factory class can be used to create assets.
|
||||
// makes sure it's not abstract, calls CanCreateNew,
|
||||
// calls ShouldShowInNewMenu, and verifies not blacklisted.
|
||||
// makes sure it's not abstract, checks CanCreateNew,
|
||||
// checks ShouldShowInNewMenu, and verifies not blacklisted.
|
||||
static bool CanCreate(TSubclassOf<UFactory> FactoryClass);
|
||||
|
||||
// Get the names of the editable properties for a factory class.
|
||||
|
||||
@@ -270,10 +270,11 @@ public:
|
||||
|
||||
// ----- Reparent validation -----
|
||||
static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed);
|
||||
|
||||
|
||||
// ----- Common Error Reporting -----
|
||||
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name, WingOut Errors);
|
||||
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, FName Name, WingOut Errors);
|
||||
static bool CheckCanRename(UEdGraphNode* Node, const FString &Name, WingOut Errors);
|
||||
static bool CheckNewObjectNotNull(UObject *Obj, const TCHAR *Kind, WingOut Errors);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user