// Copyright Epic Games, Inc. All Rights Reserved. #include "RenderMaterial_BP.h" #include "2D/Tex.h" #include "Kismet/KismetRenderingLibrary.h" //#include "3D/ProceduralMeshActor.h" #include "Engine/Canvas.h" #include "TextureGraphEngine.h" #include "Model/Mix/MixUpdateCycle.h" #include #include #include #include #include #include #include #include "Async/Async.h" #include "FxMaterial_DrawMaterial.h" #include "Job/Job.h" TSharedPtr FMaterialInstanceStaticSwitchPermutationMap::Create(UMaterialInterface* InMaterial) { TSharedPtr MISSPMap; if (InMaterial) { MISSPMap = MakeShared(); TStrongObjectPtr MIC = TStrongObjectPtr(NewObject(InMaterial)); #if WITH_EDITOR InMaterial->GetStaticParameterValues(MISSPMap->DefaultStaticParameterSet); MISSPMap->DefaultKey = MISSPMap->KeyFromStaticSwitchParameters(MISSPMap->DefaultStaticParameterSet.StaticSwitchParameters); MIC->SetParentEditorOnly(InMaterial); MIC->ClearParameterValuesEditorOnly(); MIC->SetFlags(RF_Standalone); MIC->MarkPackageDirty(); MIC->PreEditChange(NULL); MIC->PostEditChange(); #endif MISSPMap->PermutationsMap.Add(MISSPMap->DefaultKey, MIC); } return MISSPMap; } UMaterialInterface* FMaterialInstanceStaticSwitchPermutationMap::GetRootMaterial() { TStrongObjectPtr* MICP = PermutationsMap.Find(DefaultKey); if (MICP) { // Use the FIRST MIC that initiated the MISWPermutationMap as the "root Material" // in order to preserve the default values return Cast(MICP->Get()); } return nullptr; } int32 FMaterialInstanceStaticSwitchPermutationMap::KeyFromStaticSwitchParameters(const TArray& Parameters) { int32 Key = 0; for (int32 Index = 0; Index < Parameters.Num(); ++Index) { int32 ParamBitfield = ((int32)(Parameters[Index].Value) << Index); Key |= ParamBitfield; } return Key; } UMaterialInstanceConstant* FMaterialInstanceStaticSwitchPermutationMap::GetMaterialInstance(const TArray& Parameters) { check(DefaultStaticParameterSet.StaticSwitchParameters.Num() == Parameters.Num()); int32 Key = KeyFromStaticSwitchParameters(Parameters); TStrongObjectPtr* MICP = PermutationsMap.Find(Key); if (!MICP) { TStrongObjectPtr MIC = TStrongObjectPtr(NewObject(GetRootMaterial())); #if WITH_EDITOR MIC->SetParentEditorOnly(GetRootMaterial()); MIC->ClearParameterValuesEditorOnly(); MIC->SetFlags(RF_Standalone); MIC->MarkPackageDirty(); FStaticParameterSet InstanceStaticParameterSet = DefaultStaticParameterSet; InstanceStaticParameterSet.StaticSwitchParameters = Parameters; MIC->UpdateStaticPermutation(InstanceStaticParameterSet); MIC->PreEditChange(NULL); MIC->PostEditChange(); #endif PermutationsMap.Add(Key, MIC); return MIC.Get(); } else { return MICP->Get(); } } bool RenderMaterial_BP::ValidateMaterialCompatible(UMaterialInterface* InMaterial) { return FxMaterial_QuadDrawMaterial::ValidateMaterial(InMaterial); } RenderMaterial_BP::RenderMaterial_BP(FString Name, UMaterialInterface* InMaterial) : RenderMaterial(!Name.IsEmpty() ? Name : InMaterial->GetName()) , Material(InMaterial) , FXMaterialObj(std::make_shared()) { // Cast to or build a MIC to be used by this RenderMaterial and its duplicates if (Material && Material->IsA< UMaterialInstanceConstant>()) { MaterialInstance = TStrongObjectPtr(Cast(Material)); } else if (Material && !MaterialInstance) { MaterialInstance = TStrongObjectPtr(NewObject(Material)); #if WITH_EDITOR MaterialInstance->SetParentEditorOnly(Material); MaterialInstance->ClearParameterValuesEditorOnly(); MaterialInstance->SetFlags(RF_Standalone); MaterialInstance->MarkPackageDirty(); MaterialInstance->PreEditChange(NULL); MaterialInstance->PostEditChange(); #endif } check(Material && MaterialInstance); UWorld* world = Util::GetGameWorld(); Canvas = world ? world->GetCanvasForDrawMaterialToRenderTarget() : NewObject(GetTransientPackage(), NAME_None); /// The material pointer will be the same for every material of the same type (even /// though instances can be different so we can just use the address as the hash /// for the sake of completeness HashValue = std::make_shared(DataUtil::Hash((uint8*)Material, sizeof(UMaterial*)), true); std::static_pointer_cast(FXMaterialObj)->Material = MaterialInstance.Get(); } RenderMaterial_BP::~RenderMaterial_BP() { } CHashPtr RenderMaterial_BP::Hash() const { return HashValue; } void RenderMaterial_BP::SetTexture(FName InName, const UTexture* Texture) const { check(Material && MaterialInstance); #if WITH_EDITOR MaterialInstance->SetTextureParameterValueEditorOnly(InName, const_cast(Texture)); #endif } void RenderMaterial_BP::SetArrayTexture(FName InName, const std::vector& Textures) const { check(false); } void RenderMaterial_BP::SetInt(FName InName, int32 Value) const { // Same as set scalar for material instance constant SetFloat(InName, Value); } void RenderMaterial_BP::SetFloat(FName InName, float Value) const { check(Material && MaterialInstance); #if WITH_EDITOR MaterialInstance->SetScalarParameterValueEditorOnly(InName, Value); #endif } void RenderMaterial_BP::SetColor(FName InName, const FLinearColor& Value) const { check(Material && MaterialInstance); #if WITH_EDITOR MaterialInstance->SetVectorParameterValueEditorOnly(InName, Value); #endif } void RenderMaterial_BP::SetIntVector4(FName InName, const FIntVector4& Value) const { check(Material && MaterialInstance); #if WITH_EDITOR MaterialInstance->SetVectorParameterValueEditorOnly(InName, FLinearColor(Value.X, Value.Y, Value.Z, Value.W)); #endif } void RenderMaterial_BP::SetMatrix(FName InName, const FMatrix& Value) const { // UMaterialInstanceConstant does not support assigning matrix parameters } std::shared_ptr RenderMaterial_BP::DuplicateInstance(FString InName) { if (InName.IsEmpty()) InName = Name; RenderMaterial_BPPtr MaterialBP = std::make_shared(InName, MaterialInstance.Get()); //Reuse the same material instance for every invocations MaterialBP->MaterialInstanceValidated = MaterialInstanceValidated; std::shared_ptr Result = std::static_pointer_cast(MaterialBP); return Result; } AsyncPrepareResult RenderMaterial_BP::PrepareResources(const TransformArgs& Args) { if (RequestMaterialValidation) // Only if the Validation of the Material is requested { UObject* ErrorOwner = Args.JobObj->GetErrorOwner(); UMixInterface* Mix = Args.Cycle->GetMix(); RenderMaterial_BP* Self = this; return cti::make_continuable([Self, Mix, ErrorOwner](auto&& Promise) mutable { Util::OnRenderingThread([FWD_PROMISE(Promise), Self, Mix, ErrorOwner](FRHICommandListImmediate& RHI) mutable { bool ValidationResult = ValidateMaterialCompatible(Self->MaterialInstance.Get()); if (ValidationResult) { Self->MaterialInstanceValidated = true; } else { Self->MaterialInstanceValidated = false; const FString ErrorMsg = FString::Format(TEXT("Material '{0}' is not supported in TextureGraph."), { Self->GetName() }); TextureGraphEngine::GetErrorReporter(Mix)->ReportError(static_cast(ETextureGraphErrorType::UNSUPPORTED_MATERIAL), ErrorMsg, ErrorOwner); } Util::OnGameThread([FWD_PROMISE(Promise)]() mutable { Promise.set_value(0); }); }); }); } else { return cti::make_ready_continuable(0); } } void RenderMaterial_BP::DrawMaterial(UMaterialInterface* RenderMaterial, FVector2D ScreenPosition, FVector2D ScreenSize, FVector2D CoordinatePosition, FVector2D CoordinateSize, float Rotation, FVector2D PivotPoint) const { if (RenderMaterial && ScreenSize.X > 0.0f && ScreenSize.Y > 0.0f // Canvas can be NULL if the user tried to draw after EndDrawCanvasToRenderTarget && Canvas->Canvas) { FCanvasTileItem TileItem(ScreenPosition, RenderMaterial->GetRenderProxy(), ScreenSize, CoordinatePosition, CoordinatePosition + CoordinateSize); TileItem.Rotation = FRotator(0, Rotation, 0); TileItem.PivotPoint = PivotPoint; TileItem.SetColor(Canvas->DrawColor); Canvas->DrawItem(TileItem); } } void RenderMaterial_BP::BlitTo(FRHICommandListImmediate& RHI, UTextureRenderTarget2D* RT, const RenderMesh* MeshObj, int32 TargetId) const { if (!MaterialInstanceValidated) return; if (!MeshObj) { FTextureRenderTarget2DResource* RTRes = (FTextureRenderTarget2DResource*)RT->GetRenderTargetResource(); check(RTRes); FTextureRHIRef TextureRHI = RTRes->GetTextureRHI(); check(TextureRHI); TextureRHI->SetName(FName(*RT->GetName())); check(RT); std::static_pointer_cast(FXMaterialObj)->MyBlit(RHI, RT, TextureRHI, MeshObj, TargetId); } else { /* throw std::runtime_error(std::string("RenderMesh_BP : Need to update this code.")); // TODO : Need to update this. /// Removed mesh->Actor(int targetId) function /// Each mesh contains multiple actors and all the actors are present in single target. /// This needs to change into a RenderMesh method auto actor = dynamic_cast(mesh->Actor(targetId)); /// Assert but don't fail catastrophically if the dynamic_cast fails check(actor); if (actor) actor->BlitTo(RT, _instance);*/ } } void RenderMaterial_BP::Bind(int32 Value, const ResourceBindInfo& BindInfo) { SetInt(*BindInfo.Target, Value); } void RenderMaterial_BP::Bind(float Value, const ResourceBindInfo& BindInfo) { SetFloat(*BindInfo.Target, Value); } void RenderMaterial_BP::Bind(const FLinearColor& Value, const ResourceBindInfo& BindInfo) { SetColor(*BindInfo.Target, Value); FName BindName(BindInfo.Target); if (BindName == FTextureGraphMaterialShaderPS::PSCONTROL_ARG) std::static_pointer_cast(FXMaterialObj)->SetVectorParameterValue(*BindInfo.Target, Value); } void RenderMaterial_BP::Bind(const FIntVector4& Value, const ResourceBindInfo& BindInfo) { SetIntVector4(*BindInfo.Target, Value); } void RenderMaterial_BP::Bind(const FMatrix& Value, const ResourceBindInfo& BindInfo) { SetMatrix(*BindInfo.Target, Value); } void RenderMaterial_BP::BindStruct(const char* ValueAddress, size_t StructSize, const ResourceBindInfo& BindInfo) { std::static_pointer_cast(FXMaterialObj)->SetStructParameterValue(*BindInfo.Target, ValueAddress, StructSize); }