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;
|
if (!Props[0].SetObject(ClassObj, WingOut::Stdout)) return;
|
||||||
|
|
||||||
// Create the asset using the factory.
|
// Create the asset using the factory.
|
||||||
UObject *Blueprint = WingFactories::CreateAsset(Path, Factory, WingOut::Stdout);
|
UObject *Object = WingFactories::CreateAsset(
|
||||||
if (Blueprint == nullptr) return;
|
Path, Factory, nullptr, WingOut::Stdout);
|
||||||
|
if (Object == nullptr) return;
|
||||||
WingOut::Stdout.Printf(TEXT("Created.\n"));
|
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)
|
for (UClass* Class : FactoryClasses)
|
||||||
{
|
{
|
||||||
if (!WingFactories::CanCreate(Class)) continue;
|
if (!WingFactories::CanCreate(Class)) continue;
|
||||||
|
if (WingFactories::IsSpecialCase(Class)) continue;
|
||||||
if (WingFactories::GetParameterNames(Class).Num() > 0) continue;
|
if (WingFactories::GetParameterNames(Class).Num() > 0) continue;
|
||||||
|
|
||||||
FString FactoryName = WingFactories::DeriveFactoryName(Class);
|
FString FactoryName = WingFactories::DeriveFactoryName(Class);
|
||||||
@@ -49,7 +50,8 @@ public:
|
|||||||
{
|
{
|
||||||
UClass* FactoryClass = Configuration->FactoryClass.Get();
|
UClass* FactoryClass = Configuration->FactoryClass.Get();
|
||||||
UFactory* Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass);
|
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"));
|
WingOut::Stdout.Printf(TEXT("Created.\n"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
#include "PackageTools.h"
|
#include "PackageTools.h"
|
||||||
#include "WingProperty.h"
|
#include "WingProperty.h"
|
||||||
#include "AssetRegistry/AssetRegistryModule.h"
|
#include "AssetRegistry/AssetRegistryModule.h"
|
||||||
|
#include "Editor.h"
|
||||||
#include "Kismet2/EnumEditorUtils.h"
|
#include "Kismet2/EnumEditorUtils.h"
|
||||||
|
#include "Subsystems/AssetEditorSubsystem.h"
|
||||||
|
|
||||||
bool WingFactories::CheckNewAssetPath(const FString& Path, WingOut Errors)
|
bool WingFactories::CheckNewAssetPath(const FString& Path, WingOut Errors)
|
||||||
{
|
{
|
||||||
@@ -39,6 +41,14 @@ bool WingFactories::IsBlacklisted(TSubclassOf<UFactory> FactoryClass)
|
|||||||
return false;
|
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)
|
bool WingFactories::CanCreate(TSubclassOf<UFactory> FactoryClass)
|
||||||
{
|
{
|
||||||
if (FactoryClass == nullptr) return false;
|
if (FactoryClass == nullptr) return false;
|
||||||
@@ -53,7 +63,8 @@ TArray<FName> WingFactories::GetParameterNames(TSubclassOf<UFactory> FactoryClas
|
|||||||
return FWingProperty::GetVisibleNames(FactoryClass);
|
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.
|
// Check the blacklist.
|
||||||
if (IsBlacklisted(Factory->GetClass()))
|
if (IsBlacklisted(Factory->GetClass()))
|
||||||
@@ -84,9 +95,28 @@ UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, Wing
|
|||||||
GWarn
|
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
|
// If the asset was created, turn the package into a
|
||||||
// permanent package and encourge the editor to save it
|
// 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)
|
if (NewAsset)
|
||||||
{
|
{
|
||||||
Package->ClearFlags(RF_Transient);
|
Package->ClearFlags(RF_Transient);
|
||||||
@@ -94,10 +124,11 @@ UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, Wing
|
|||||||
Package->MarkPackageDirty();
|
Package->MarkPackageDirty();
|
||||||
FAssetRegistryModule::AssetCreated(NewAsset);
|
FAssetRegistryModule::AssetCreated(NewAsset);
|
||||||
UWingServer::AddTouchedObject(NewAsset);
|
UWingServer::AddTouchedObject(NewAsset);
|
||||||
|
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
|
||||||
|
if (Sub) Sub->OpenEditorForAsset(NewAsset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Errors.Printf(TEXT("ERROR: Factory '%s' failed to create an object\n"), *Factory->GetClass()->GetName());
|
|
||||||
Package->ClearDirtyFlag();
|
Package->ClearDirtyFlag();
|
||||||
Package->MarkAsGarbage();
|
Package->MarkAsGarbage();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -129,4 +160,3 @@ UObject* UEnumFactoryWing::FactoryCreateNew(UClass* Class, UObject* InParent, FN
|
|||||||
}
|
}
|
||||||
return FEnumEditorUtils::CreateUserDefinedEnum(InParent, Name, Flags);
|
return FEnumEditorUtils::CreateUserDefinedEnum(InParent, Name, Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ FWingParameterEditor::InfoMetaMap FWingParameterEditor::GetMaterialParameters(UM
|
|||||||
|
|
||||||
bool FWingParameterEditor::ReportBadType(EMaterialParameterType Type, WingOut Errors)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,32 +276,21 @@ bool FWingParameterEditor::AddOverride(
|
|||||||
bool FWingParameterEditor::RemoveOverride(
|
bool FWingParameterEditor::RemoveOverride(
|
||||||
const Info &ID, UMaterialInstanceConstant *MI, WingOut Errors)
|
const Info &ID, UMaterialInstanceConstant *MI, WingOut Errors)
|
||||||
{
|
{
|
||||||
// Remove the override from all parameter arrays.
|
InfoMetaMap AllParams = GetMaterialParameters(MI);
|
||||||
auto RemoveFrom = [&](auto& Arr) {
|
Metadata *Target = FindParameter(AllParams, ID, Errors);
|
||||||
return Arr.RemoveAll([&](auto& Entry) { return Entry.ParameterInfo == ID; });
|
if (Target == nullptr) return false;
|
||||||
};
|
if (!Target->bOverride)
|
||||||
|
|
||||||
int32 Removed = 0;
|
|
||||||
switch (ID.Type)
|
|
||||||
{
|
{
|
||||||
case EMaterialParameterType::Scalar: Removed += RemoveFrom(MI->ScalarParameterValues); break;
|
Errors.Printf(TEXT("Parameter '%s' is inherited and not overridden"), *StringID(ID));
|
||||||
case EMaterialParameterType::Vector: Removed += RemoveFrom(MI->VectorParameterValues); break;
|
return false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
if (!Meta.bOverride) continue;
|
||||||
return false;
|
if (Info == ID) continue;
|
||||||
|
Update.SetParameterValueEditorOnly(Info, Meta);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,7 +354,6 @@ bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, W
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Common Error Reporting
|
// Common Error Reporting
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -396,6 +395,16 @@ bool WingUtils::CheckCanRename(UEdGraphNode* Node, const FString &Name, WingOut
|
|||||||
return true;
|
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
|
// Reparent validation
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -13,8 +13,12 @@ class WingFactories
|
|||||||
public:
|
public:
|
||||||
// Create an asset on disk, using a factory instance.
|
// Create an asset on disk, using a factory instance.
|
||||||
// Returns the main object. If there are problems,
|
// Returns the main object. If there are problems,
|
||||||
// prints error messages and returns nullptr.
|
// prints error messages and returns nullptr. If
|
||||||
static UObject *CreateAsset(const FString &Path, UFactory *Factory, WingOut Errors);
|
// 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
|
// Some factories are blacklisted, mainly because they
|
||||||
// pop up dialog boxes. In those cases, we deal with it
|
// pop up dialog boxes. In those cases, we deal with it
|
||||||
@@ -22,9 +26,16 @@ public:
|
|||||||
// and then implementing replacement factories.
|
// and then implementing replacement factories.
|
||||||
static bool IsBlacklisted(TSubclassOf<UFactory> FactoryClass);
|
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.
|
// Check if the factory class can be used to create assets.
|
||||||
// makes sure it's not abstract, calls CanCreateNew,
|
// makes sure it's not abstract, checks CanCreateNew,
|
||||||
// calls ShouldShowInNewMenu, and verifies not blacklisted.
|
// checks ShouldShowInNewMenu, and verifies not blacklisted.
|
||||||
static bool CanCreate(TSubclassOf<UFactory> FactoryClass);
|
static bool CanCreate(TSubclassOf<UFactory> FactoryClass);
|
||||||
|
|
||||||
// Get the names of the editable properties for a factory class.
|
// Get the names of the editable properties for a factory class.
|
||||||
|
|||||||
@@ -275,5 +275,6 @@ public:
|
|||||||
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name, WingOut Errors);
|
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 CheckExactlyOneNamed(int Count, const TCHAR *Kind, FName Name, WingOut Errors);
|
||||||
static bool CheckCanRename(UEdGraphNode* Node, const FString &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