void SPropertyEditorCombo::SendToObjects( const FString& NewValue )
{
	const TSharedRef< FPropertyNode > PropertyNode = PropertyEditor->GetPropertyNode();
	UProperty* Property = PropertyNode->GetProperty();

	FString Value;
	FString ToolTipValue;
	if ( bUsesAlternateDisplayValues && !Property->IsA(UStrProperty::StaticClass()))
	{
		// currently only enum properties can use alternate display values; this might change, so assert here so that if support is expanded to other property types
		// without updating this block of code, we'll catch it quickly
		UEnum* Enum = CastChecked<UByteProperty>(Property)->Enum;
		check(Enum);
		int32 Index = INDEX_NONE;
		for( int32 ItemIndex = 0; ItemIndex <  Enum->NumEnums(); ++ItemIndex )
		{
			const FString EnumName = Enum->GetEnumName(ItemIndex);
			const FString DisplayName = Enum->GetDisplayNameText(ItemIndex ).ToString();
			if( DisplayName.Len() > 0 )
			{
				if ( DisplayName == NewValue )
				{
					Index = ItemIndex;
					break;
				}
			}
			else if (EnumName == NewValue)
			{
				Index = ItemIndex;
				break;
			}
		}

		check( Index != INDEX_NONE );

		Value = Enum->GetEnumName(Index);

		ToolTipValue = Enum->GetMetaData( TEXT("ToolTip"), Index );
		FString ToolTipText = Property->GetToolTipText().ToString();
		if (ToolTipValue.Len() > 0)
		{
			ToolTipText = FString::Printf(TEXT("%s\n\n%s"), *ToolTipText, *ToolTipValue);
		}
		SetToolTipText(ToolTipText);
	}
	else
	{
		Value = NewValue;
	}

	const TSharedRef< IPropertyHandle > PropertyHandle = PropertyEditor->GetPropertyHandle();
	PropertyHandle->SetValueFromFormattedString( Value );
}
void SPropertyEditorCombo::SendToObjects( const FString& NewValue )
{
	const TSharedRef< FPropertyNode > PropertyNode = PropertyEditor->GetPropertyNode();
	UProperty* Property = PropertyNode->GetProperty();

	FString Value;
	if ( bUsesAlternateDisplayValues && !Property->IsA(UStrProperty::StaticClass()))
	{
		// currently only enum properties can use alternate display values; this 
		// might change, so assert here so that if support is expanded to other 
		// property types without updating this block of code, we'll catch it quickly
		UEnum* Enum = CastChecked<UByteProperty>(Property)->Enum;
		check(Enum != nullptr);

		const int32 Index = FindEnumValueIndex(Enum, NewValue);
		check( Index != INDEX_NONE );

		Value = Enum->GetEnumName(Index);

		FText ToolTipValue = Enum->GetToolTipText(Index);
		FText ToolTipText = Property->GetToolTipText();
		if (!ToolTipValue.IsEmpty())
		{
			ToolTipText = FText::Format(FText::FromString(TEXT("{0}\n\n{1}")), ToolTipText, ToolTipValue);
		}
		SetToolTipText(ToolTipText);
	}
	else
	{
		Value = NewValue;
	}

	const TSharedRef< IPropertyHandle > PropertyHandle = PropertyEditor->GetPropertyHandle();
	PropertyHandle->SetValueFromFormattedString( Value );
}
void UGatherTextFromMetaDataCommandlet::GatherTextFromUObject(UField* const Field)
{
	const int32 MetaDataCount = 3;
	const FString MetaDataKeys[MetaDataCount] =
	{	TEXT("ToolTip"),			TEXT("DisplayName"),			TEXT("Category")			};
	const FString AssociatedNamespaces[MetaDataCount] =
	{	TEXT("UObjectToolTips"),	TEXT("UObjectDisplayNames"),	TEXT("UObjectCategories")	};

	// Gather for object.
	{
		for(int32 i = 0; i < MetaDataCount; ++i)
		{
			const FString& MetaDataValue = Field->GetMetaData(*MetaDataKeys[i]);
			if(!(MetaDataValue.IsEmpty()))
			{
				const FString Namespace = AssociatedNamespaces[i];
				FLocItem LocItem(MetaDataValue);
				FContext Context;
				Context.Key =	MetaDataKeys[i] == TEXT("Category")
							?	MetaDataValue
							:	Field->GetFullGroupName(true) + TEXT(".") + Field->GetName();
				Context.SourceLocation = TEXT("Run-time MetaData");
				ManifestInfo->AddEntry(TEXT("EntryDescription"), Namespace, LocItem, Context);
			}
		}
	}

	// For enums, also gather for enum values.
	{
		UEnum* Enum = Cast<UEnum>(Field);
		if(Enum)
		{
			const int32 ValueCount = Enum->NumEnums();
			for(int32 i = 0; i < ValueCount; ++i)
			{
				for(int32 j = 0; j < MetaDataCount; ++j)
				{
					const FString& MetaDataValue = Enum->GetMetaData(*MetaDataKeys[j], i);
					if(!(MetaDataValue.IsEmpty()))
					{
						const FString Namespace = AssociatedNamespaces[j];
						FLocItem LocItem(MetaDataValue);
						FContext Context;
						Context.Key =	MetaDataKeys[j] == TEXT("Category")
									?	MetaDataValue
									:	Enum->GetFullGroupName(true) + TEXT(".") + Enum->GetName() + TEXT(".") + Enum->GetEnumName(i);
						Context.SourceLocation = TEXT("Run-time MetaData");
						ManifestInfo->AddEntry(TEXT("EntryDescription"), Namespace, LocItem, Context);
					}
				}
			}
		}
	}
}
예제 #4
0
void UK2Node::PostReconstructNode()
{
	if (!IsTemplate())
	{
		// Make sure we're not dealing with a menu node
		UEdGraph* OuterGraph = GetGraph();
		if( OuterGraph && OuterGraph->Schema )
		{
			const UEdGraphSchema_K2* Schema = Cast<const UEdGraphSchema_K2>(GetSchema());
			// fix up any pin data if it needs to 
			for( auto PinIt = Pins.CreateIterator(); PinIt; ++PinIt )
			{
				UEdGraphPin* CurrentPin = *PinIt;
				const FString& PinCategory = CurrentPin->PinType.PinCategory;

				// fix up enum names if it exists in enum redirects
				if (PinCategory == Schema->PC_Byte)
				{
					UEnum* EnumPtr = Cast<UEnum>(CurrentPin->PinType.PinSubCategoryObject.Get());
					if (EnumPtr)
					{
						const FString& PinValue = CurrentPin->DefaultValue;
						// see if this enum is in EnumRedirects
						int32 EnumIndex = UEnum::FindEnumRedirects(EnumPtr, *PinValue);
						if (EnumIndex != INDEX_NONE)
						{
							FString EnumName = EnumPtr->GetEnumName(EnumIndex);

							// if the name does not match with pin value, override pin value
							if (EnumName != PinValue)
							{
								// I'm not marking package as dirty 
								// as I know that's not going to work during serialize or post load
								CurrentPin->DefaultValue = EnumName;
								continue;
							}
						}
					}
				}
				else if (PinCategory == Schema->PC_Object)
				{
					UClass const* PinClass = Cast<UClass const>(CurrentPin->PinType.PinSubCategoryObject.Get());
					if ((PinClass != nullptr) && PinClass->IsChildOf(UInterface::StaticClass()))
					{
						CurrentPin->PinType.PinCategory = Schema->PC_Interface;
					}
				}
			}
		}
	}
}
void FRCPassPostProcessVisualizeShadingModels::Process(FRenderingCompositePassContext& Context)
{
	SCOPED_DRAW_EVENT(Context.RHICmdList, PostProcessVisualizeShadingModels);
	const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);

	const FSceneView& View = Context.View;
	const FViewInfo& ViewInfo = Context.View;
	const FSceneViewFamily& ViewFamily = *(View.Family);
	
	FIntRect SrcRect = View.ViewRect;
	FIntRect DestRect = View.ViewRect;
	FIntPoint SrcSize = InputDesc->Extent;

	const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);

	// Set the view family's render target/viewport.
	SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef());
	Context.SetViewportAndCallRHI(DestRect);

	// set the state
	Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
	Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
	Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());

	TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
	TShaderMapRef<FPostProcessVisualizeShadingModelsPS> PixelShader(Context.GetShaderMap());

	static FGlobalBoundShaderState BoundShaderState;	

	SetGlobalBoundShaderState(Context.RHICmdList, Context.GetFeatureLevel(), BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader);

	PixelShader->SetPS(Context, ((FViewInfo&)View).ShadingModelMaskInView);

	// Draw a quad mapping scene color to the view's render target
	DrawRectangle(
		Context.RHICmdList,
		DestRect.Min.X, DestRect.Min.Y,
		DestRect.Width(), DestRect.Height(),
		SrcRect.Min.X, SrcRect.Min.Y,
		SrcRect.Width(), SrcRect.Height(),
		DestRect.Size(),
		SrcSize,
		*VertexShader,
		EDRF_UseTriangleOptimization);

	FRenderTargetTemp TempRenderTarget(View, (const FTexture2DRHIRef&)DestRenderTarget.TargetableTexture);
	FCanvas Canvas(&TempRenderTarget, NULL, 0, 0, 0, Context.GetFeatureLevel());

	float X = 30;
	float Y = 28;
	const float YStep = 14;
	const float ColumnWidth = 250;

	FString Line;

	Canvas.DrawShadowedString( X, Y += YStep, TEXT("Visualize ShadingModels (mostly to track down bugs)"), GetStatsFont(), FLinearColor(1, 1, 1));

	Y = 160 - YStep - 4;
	
	uint32 Value = ((FViewInfo&)View).ShadingModelMaskInView;

	Line = FString::Printf(TEXT("View.ShadingModelMaskInView = 0x%x"), Value);
	Canvas.DrawShadowedString( X, Y, *Line, GetStatsFont(), FLinearColor(0.5f, 0.5f, 0.5f));
	Y += YStep;

	UEnum* Enum = FindObject<UEnum>(NULL, TEXT("Engine.EMaterialShadingModel"));
	check(Enum);

	Y += 5;

	for(uint32 i = 0; i < MSM_MAX; ++i)
	{
		FString Name = Enum->GetEnumName(i);
		Line = FString::Printf(TEXT("%d.  %s"), i, *Name);

		bool bThere = (Value & (1 << i)) != 0;

		Canvas.DrawShadowedString(X + 30, Y, *Line, GetStatsFont(), bThere ? FLinearColor(1, 1, 1) : FLinearColor(0, 0, 0) );
		Y += 20;
	}

	Line = FString::Printf(TEXT("(On CPU, based on what gets rendered)"));
	Canvas.DrawShadowedString( X, Y, *Line, GetStatsFont(), FLinearColor(0.5f, 0.5f, 0.5f)); Y += YStep;

	Canvas.Flush_RenderThread(Context.RHICmdList);

	Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());

	// AdjustGBufferRefCount(1) call is done in constructor
	FSceneRenderTargets::Get(Context.RHICmdList).AdjustGBufferRefCount(Context.RHICmdList, -1);
}
예제 #6
0
void UCollisionProfile::LoadProfileConfig(bool bForceInit)
{
	/** 
	 * This function loads all config data to memory
	 * 
	 * 1. First it fixes the meta data for each custom channel name since those meta data is used for #2
	 * 2. Load Default Profile so that it can be used later
	 * 3. Second it sets up Correct ResponseToChannel for all profiles
	 * 4. It loads profile redirect data 
	 **/
	// read "EngineTraceChanne" and "GameTraceChanne" and set meta data
	FConfigSection* Configs = GConfig->GetSectionPrivate( TEXT("/Script/Engine.CollisionProfile"), false, true, GEngineIni );

	// before any op, verify if profiles contains invalid name - such as Custom profile name - remove all of them
	for (auto Iter=Profiles.CreateConstIterator(); Iter; ++Iter)
	{
		// make sure it doens't have any 
		if (Iter->Name == CustomCollisionProfileName)
		{
			UE_LOG(LogCollisionProfile, Error, TEXT("Profiles contain invalid name : %s is reserved for internal use"), *CustomCollisionProfileName.ToString());
			Profiles.RemoveAt(Iter.GetIndex());
		}
	}
	// 1. First loads all meta data for custom channels
	// this can be used in #2, so had to fix up first

	// this is to replace ECollisionChannel's DisplayName to be defined by users
	FString EngineTraceChannel	= TEXT("EngineTraceChannel");
	FString GameTraceChannel	= TEXT("GameTraceChannel");

	// find the enum
	UEnum* Enum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ECollisionChannel"), true);
	// we need this Enum
	check (Enum);
	UStruct* Struct = FCollisionResponseContainer::StaticStruct(); 
	check (Struct);
	const FString KeyName = TEXT("DisplayName");
	const FString TraceType = TEXT("TraceQuery");
	const FString TraceValue = TEXT("1");
	const FString HiddenMeta = TEXT("Hidden");

	// need to initialize displaynames separate
	int32 NumEnum = Enum->NumEnums();
	ChannelDisplayNames.Empty(NumEnum);
	ChannelDisplayNames.AddZeroed(NumEnum);
	TraceTypeMapping.Empty();
	ObjectTypeMapping.Empty();

	// first go through enum entry, and add suffix to displaynames
	int32 PrefixLen = FString(TEXT("ECC_")).Len();

	// need to have mapping table between ECollisionChannel and EObjectTypeQuery/ETraceTypeQuery
	UEnum* ObjectTypeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EObjectTypeQuery"), true);
	UEnum* TraceTypeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ETraceTypeQuery"), true);
	check (ObjectTypeEnum && TraceTypeEnum);

	for ( int32 EnumIndex=0; EnumIndex<NumEnum; ++EnumIndex )
	{
		FString EnumName = Enum->GetEnumName(EnumIndex);
		EnumName = EnumName.RightChop(PrefixLen);
		FName DisplayName = FName(*EnumName);

		if ( IS_VALID_COLLISIONCHANNEL(EnumIndex) )
		{
			// verify if the Struct name matches
			// this is to avoid situations where they mismatch and causes random bugs
			UField* Field = FindField<UField>(Struct, DisplayName);

			if (!Field)
			{
				// error - this is bad. This means somebody changed variable name without changing channel enum
				UE_LOG(LogCollisionProfile, Error, TEXT("Variable (%s) isn't found for Channel (%s). \nPlease make sure you name matches between ECollisionChannel and FCollisionResponseContainer."), *DisplayName.ToString(), *EnumName);
			}

#if WITH_EDITOR
			// clear displayname since we're setting it in below
			Enum->RemoveMetaData(*KeyName, EnumIndex);
			if (Enum->HasMetaData(*HiddenMeta, EnumIndex) == false)
			{
				Enum->SetMetaData(*HiddenMeta, NULL, EnumIndex);
			}
#endif			
		}
		else
		{
			// fix up FCollisionQueryFlag AllObjects flag, if trace type=1
			ECollisionChannel CollisionChannel = (ECollisionChannel)EnumIndex;
			// for any engine level collision profile, we hard coded here
			// meta data doesn't work in cooked build, so we'll have to manually handle them
			if ((CollisionChannel == ECC_Visibility) || (CollisionChannel == ECC_Camera))
			{
				// remove from object query flags
				FCollisionQueryFlag::Get().RemoveFromAllObjectsQueryFlag(CollisionChannel);
				TraceTypeMapping.Add(CollisionChannel);
			}
			else if ( CollisionChannel < ECC_OverlapAll_Deprecated )
			{
				ObjectTypeMapping.Add(CollisionChannel);
			}
		}

		ChannelDisplayNames[EnumIndex] = DisplayName;
	}

	// Now Load Channel setups, and set display names if customized
	// also initialize DefaultResposneContainer with default response for each channel
	FCollisionResponseContainer::DefaultResponseContainer.SetAllChannels(ECR_Block);

	for (auto Iter=DefaultChannelResponses.CreateConstIterator(); Iter; ++Iter)
	{
		const FCustomChannelSetup& CustomChannel = *Iter;
		int32 EnumIndex = CustomChannel.Channel;
		// make sure it is the range of channels we allow to change
		if ( IS_VALID_COLLISIONCHANNEL(EnumIndex) )
		{
			if ( CustomChannel.Name != NAME_None )
			{
				// before you set meta data, you'll have to save this value to modify 
				// ECollisionResponseContainer variables meta data
				FString VariableName = ChannelDisplayNames[EnumIndex].ToString();
				FString DisplayValue = CustomChannel.Name.ToString();

				// also has to set this for internal use
				ChannelDisplayNames[EnumIndex] = FName(*DisplayValue);

#if WITH_EDITOR
				// set displayvalue for this enum entry
				Enum->SetMetaData(*KeyName, *DisplayValue, EnumIndex);
				// also need to remove "Hidden"
				Enum->RemoveMetaData(*HiddenMeta, EnumIndex);
#endif 
				// now add MetaData type for trace type if it does
				if (CustomChannel.bTraceType)
				{
#if WITH_EDITOR
					// add to enum
					Enum->SetMetaData(*TraceType, *TraceValue, EnumIndex);
#endif
					// remove from all object queries
					FCollisionQueryFlag::Get().RemoveFromAllObjectsQueryFlag(CustomChannel.Channel);
					TraceTypeMapping.Add(CustomChannel.Channel);
				}
				// if it has display value
				else 
				{
#if WITH_EDITOR
					// add to enum
					Enum->RemoveMetaData(*TraceType, EnumIndex);
#endif
					ObjectTypeMapping.Add(CustomChannel.Channel);

					if (CustomChannel.bStaticObject)
					{
						// add to static object 
						FCollisionQueryFlag::Get().AddToAllStaticObjectsQueryFlag(CustomChannel.Channel);
					}
				}
#if WITH_EDITOR
				// now enum is fixed, so find member variable for the field
				UField* Field = FindField<UField>(Struct, FName(*VariableName));
				// I verified up in the class, this can't happen
				check (Field);
				Field->SetMetaData(*KeyName, *DisplayValue);
#endif
			}
			else
			{
				// missing name
				UE_LOG(LogCollisionProfile, Warning, TEXT("Name can't be empty for Channel (%d) "), EnumIndex );
			}

			// allow it to set default response
			FCollisionResponseContainer::DefaultResponseContainer.SetResponse((ECollisionChannel) EnumIndex, CustomChannel.DefaultResponse);
		}
		else
		{
			// you can't customize those channels
			UE_LOG(LogCollisionProfile, Warning, TEXT("Default Setup doesn't allow for predefined engine channels (%d) "), EnumIndex);
		}
	}

#if WITH_EDITOR
	// now propagate all changes to the channels to EObjectTypeQuery and ETraceTypeQuery for blueprint accessibility
	// this code is to show in editor more friendly, so it doesn't have to be set if it's not for editor
	int32 ObjectTypeEnumIndex = 0;
	int32 TraceTypeEnumIndex = 0;

	// first go through fill up ObjectType Enum
	for ( int32 EnumIndex=0; EnumIndex<NumEnum; ++EnumIndex )
	{
		// if not hidden
		const FString& Hidden = Enum->GetMetaData(*HiddenMeta, EnumIndex);
		if ( Hidden.IsEmpty() )
		{
			const FString& DisplayName = Enum->GetMetaData(*KeyName, EnumIndex);
			if ( !DisplayName.IsEmpty() )
			{
				// find out trace type or object type
				if (Enum->GetMetaData(*TraceType, EnumIndex) == TraceValue)
				{
					TraceTypeEnum->RemoveMetaData(*HiddenMeta, TraceTypeEnumIndex);
					TraceTypeEnum->SetMetaData(*KeyName, *DisplayName, TraceTypeEnumIndex);
					++TraceTypeEnumIndex;
				}
				else
				{
					ObjectTypeEnum->RemoveMetaData(*HiddenMeta, ObjectTypeEnumIndex);
					ObjectTypeEnum->SetMetaData(*KeyName, *DisplayName, ObjectTypeEnumIndex);
					++ObjectTypeEnumIndex;
				}
			}
		}
	}

	// make sure TraceTypeEnumIndex matches TraceTypeMapping
	check (TraceTypeMapping.Num() == TraceTypeEnumIndex);
	check (ObjectTypeMapping.Num() == ObjectTypeEnumIndex);
#endif

	// collision redirector has to be loaded before profile
	CollisionChannelRedirectsMap.Empty();

	for(auto Iter = CollisionChannelRedirects.CreateConstIterator(); Iter; ++Iter)
	{
		FName OldName = Iter->OldName;
		FName NewName = Iter->NewName;

		// at least we need to have OldName
		if(OldName!= NAME_None && NewName != NAME_None)
		{
			// add to pair
			CollisionChannelRedirectsMap.Add(OldName, NewName);
		}
		else
		{
			// print error 
			UE_LOG(LogCollisionProfile, Warning, TEXT("CollisionChannel Redirects : Name Can't be none (%s: %s)"), *OldName.ToString(), *NewName.ToString());
		}
	}

	// 2. Second loads all set up back to ResponseToChannels
	// this does a lot of iteration, but this only happens once loaded, so it's better to be convenient than efficient
	// fill up Profiles data
	FillProfileData(Profiles, Enum, KeyName, EditProfiles);

	// 3. It loads redirect data  - now time to load profile redirect
	ProfileRedirectsMap.Empty();

	// handle profile redirect here
	for(auto Iter = ProfileRedirects.CreateConstIterator(); Iter; ++Iter)
	{
		FName OldName = Iter->OldName;
		FName NewName = Iter->NewName;

		// at least we need to have OldName
		if(OldName!= NAME_None && NewName != NAME_None)
		{
			FCollisionResponseTemplate Template;

			// make sure the template exists
			if(FindProfileData(Profiles, NewName, Template))
			{
				// add to pair
				ProfileRedirectsMap.Add(OldName, NewName);
			}
			else
			{
				// print error
				UE_LOG(LogCollisionProfile, Warning, TEXT("ProfileRedirect (%s : %s) - New Name (\'%s\') isn't found "),
					*OldName.ToString(), *NewName.ToString(), *NewName.ToString());
			}
		}
	}

#if WITH_EDITOR
	if (bForceInit)
	{
		// go through objects, and see if we can reinitialize profile
		for(TObjectIterator<UPrimitiveComponent> PrimIt; PrimIt; ++PrimIt)
		{
			PrimIt->UpdateCollisionProfile();
		}
	}
#endif
}
void UGatherTextFromMetaDataCommandlet::GatherTextFromUObject(UField* const Field, const FGatherParameters& Arguments)
{
	// Gather for object.
	{
		if( !Field->HasMetaData( TEXT("DisplayName") ) )
		{
			Field->SetMetaData( TEXT("DisplayName"), *FName::NameToDisplayString( Field->GetName(), Field->IsA( UBoolProperty::StaticClass() ) ) );
		}

		for(int32 i = 0; i < Arguments.InputKeys.Num(); ++i)
		{
			FFormatNamedArguments PatternArguments;
			PatternArguments.Add( TEXT("FieldPath"), FText::FromString( Field->GetFullGroupName(false) ) );

			if( Field->HasMetaData( *Arguments.InputKeys[i] ) )
			{
				const FString& MetaDataValue = Field->GetMetaData(*Arguments.InputKeys[i]);
				if( !MetaDataValue.IsEmpty() )
				{
					PatternArguments.Add( TEXT("MetaDataValue"), FText::FromString(MetaDataValue) );

					const FString Namespace = Arguments.OutputNamespaces[i];
					FLocItem LocItem(MetaDataValue);
					FManifestContext Context;
					Context.Key = FText::Format(Arguments.OutputKeys[i], PatternArguments).ToString();
					Context.SourceLocation = FString::Printf(TEXT("From metadata for key %s of member %s in %s"), *Arguments.InputKeys[i], *Field->GetName(), *Field->GetFullGroupName(true));
					GatherManifestHelper->AddSourceText(Namespace, LocItem, Context);
				}
			}
		}
	}

	// For enums, also gather for enum values.
	{
		UEnum* Enum = Cast<UEnum>(Field);
		if(Enum)
		{
			const int32 ValueCount = Enum->NumEnums();
			for(int32 i = 0; i < ValueCount; ++i)
			{
				if( !Enum->HasMetaData(TEXT("DisplayName"), i) )
				{
					Enum->SetMetaData(TEXT("DisplayName"), *FName::NameToDisplayString(Enum->GetEnumName(i), false), i);
				}

				for(int32 j = 0; j < Arguments.InputKeys.Num(); ++j)
				{
					FFormatNamedArguments PatternArguments;
					PatternArguments.Add( TEXT("FieldPath"), FText::FromString( Enum->GetFullGroupName(false) + TEXT(".") + Enum->GetEnumName(i) ) );

					if( Enum->HasMetaData(*Arguments.InputKeys[j], i) )
					{
						const FString& MetaDataValue = Enum->GetMetaData(*Arguments.InputKeys[j], i);
						if( !MetaDataValue.IsEmpty() )
						{
							PatternArguments.Add( TEXT("MetaDataValue"), FText::FromString(MetaDataValue) );

							const FString Namespace = Arguments.OutputNamespaces[j];
							FLocItem LocItem(MetaDataValue);
							FManifestContext Context;
							Context.Key = FText::Format(Arguments.OutputKeys[j], PatternArguments).ToString();
							Context.SourceLocation = FString::Printf(TEXT("From metadata for key %s of enum value %s of enum %s in %s"), *Arguments.InputKeys[j], *Enum->GetEnumName(i), *Enum->GetName(), *Enum->GetFullGroupName(true));
							GatherManifestHelper->AddSourceText(Namespace, LocItem, Context);
						}
					}
				}
			}
		}
	}
}
예제 #8
0
TSharedPtr<FEnvQueryInstance> UEnvQueryManager::CreateQueryInstance(const UEnvQuery* Template, EEnvQueryRunMode::Type RunMode)
{
	if (Template == nullptr || Template->Options.Num() == 0)
	{
		UE_CLOG(Template != nullptr && Template->Options.Num() == 0, LogEQS, Warning, TEXT("Query [%s] doesn't have any valid options!"), *Template->GetName());
		return nullptr;
	}

	// try to find entry in cache
	FEnvQueryInstance* InstanceTemplate = NULL;
	for (int32 InstanceIndex = 0; InstanceIndex < InstanceCache.Num(); InstanceIndex++)
	{
		if (InstanceCache[InstanceIndex].Template->GetFName() == Template->GetFName() &&
			InstanceCache[InstanceIndex].Instance.Mode == RunMode)
		{
			InstanceTemplate = &InstanceCache[InstanceIndex].Instance;
			break;
		}
	}

	// and create one if can't be found
	if (InstanceTemplate == NULL)
	{
		SCOPE_CYCLE_COUNTER(STAT_AI_EQS_LoadTime);
		
		// duplicate template in manager's world for BP based nodes
		UEnvQuery* LocalTemplate = (UEnvQuery*)StaticDuplicateObject(Template, this, *Template->GetName());

		{
			// memory stat tracking: temporary variable will exist only inside this section
			FEnvQueryInstanceCache NewCacheEntry;
			NewCacheEntry.Template = LocalTemplate;
			NewCacheEntry.Instance.QueryName = LocalTemplate->GetName();
			NewCacheEntry.Instance.Mode = RunMode;

			const int32 Idx = InstanceCache.Add(NewCacheEntry);
			InstanceTemplate = &InstanceCache[Idx].Instance;
		}

		// NOTE: We must iterate over this from 0->Num because we are copying the options from the template into the
		// instance, and order matters!  Since we also may need to remove invalid or null options, we must decrement
		// the iteration pointer when doing so to avoid problems.
		for (int32 OptionIndex = 0; OptionIndex < LocalTemplate->Options.Num(); ++OptionIndex)
		{
			UEnvQueryOption* MyOption = LocalTemplate->Options[OptionIndex];
			if (MyOption == nullptr ||
				MyOption->Generator == nullptr ||
				MyOption->Generator->ItemType == nullptr)
			{
				UE_LOG(LogEQS, Error, TEXT("Trying to spawn a query with broken Template (generator:%s itemType:%s): %s, option %d"),
					MyOption ? (MyOption->Generator ? TEXT("ok") : TEXT("MISSING")) : TEXT("N/A"),
					(MyOption && MyOption->Generator) ? (MyOption->Generator->ItemType ? TEXT("ok") : TEXT("MISSING")) : TEXT("N/A"),
					*GetNameSafe(LocalTemplate), OptionIndex);

				LocalTemplate->Options.RemoveAt(OptionIndex, 1, false);
				--OptionIndex; // See note at top of for loop.  We cannot iterate backwards here.
				continue;
			}

			UEnvQueryOption* LocalOption = (UEnvQueryOption*)StaticDuplicateObject(MyOption, this, TEXT("None"));
			UEnvQueryGenerator* LocalGenerator = (UEnvQueryGenerator*)StaticDuplicateObject(MyOption->Generator, this, TEXT("None"));
			LocalTemplate->Options[OptionIndex] = LocalOption;
			LocalOption->Generator = LocalGenerator;

			EEnvTestCost::Type HighestCost(EEnvTestCost::Low);
			TArray<UEnvQueryTest*> SortedTests = MyOption->Tests;
			TSubclassOf<UEnvQueryItemType> GeneratedType = MyOption->Generator->ItemType;
			for (int32 TestIndex = SortedTests.Num() - 1; TestIndex >= 0; TestIndex--)
			{
				UEnvQueryTest* TestOb = SortedTests[TestIndex];
				if (TestOb == NULL || !TestOb->IsSupportedItem(GeneratedType))
				{
					UE_LOG(LogEQS, Warning, TEXT("Query [%s] can't use test [%s] in option %d [%s], removing it"),
						*GetNameSafe(LocalTemplate), *GetNameSafe(TestOb), OptionIndex, *MyOption->Generator->OptionName);

					SortedTests.RemoveAt(TestIndex, 1, false);
				}
				else if (HighestCost < TestOb->Cost)
				{
					HighestCost = TestOb->Cost;
				}
			}

			if (SortedTests.Num() == 0)
			{
				UE_LOG(LogEQS, Warning, TEXT("Query [%s] doesn't have any tests in option %d [%s]"),
					*GetNameSafe(LocalTemplate), OptionIndex, *MyOption->Generator->OptionName);

				LocalTemplate->Options.RemoveAt(OptionIndex, 1, false);
				--OptionIndex; // See note at top of for loop.  We cannot iterate backwards here.
				continue;
			}

			LocalOption->Tests.Reset(SortedTests.Num());
			for (int32 TestIdx = 0; TestIdx < SortedTests.Num(); TestIdx++)
			{
				UEnvQueryTest* LocalTest = (UEnvQueryTest*)StaticDuplicateObject(SortedTests[TestIdx], this, TEXT("None"));
				LocalOption->Tests.Add(LocalTest);
			}

			// use locally referenced duplicates
			SortedTests = LocalOption->Tests;

			if (SortedTests.Num() && LocalGenerator->bAutoSortTests)
			{
				switch (RunMode)
				{
				case EEnvQueryRunMode::SingleResult:
					SortedTests.Sort(EnvQueryTestSort::FSingleResult(HighestCost));
					break;

				case EEnvQueryRunMode::RandomBest5Pct:
				case EEnvQueryRunMode::RandomBest25Pct:
				case EEnvQueryRunMode::AllMatching:
					SortedTests.Sort(EnvQueryTestSort::FAllMatching());
					break;

				default:
					{
						UEnum* RunModeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EEnvQueryRunMode"));
						UE_LOG(LogEQS, Warning, TEXT("Query [%s] can't be sorted for RunMode: %d [%s]"),
							*GetNameSafe(LocalTemplate), (int32)RunMode, RunModeEnum ? *RunModeEnum->GetEnumName(RunMode) : TEXT("??"));
					}
				}
			}

			CreateOptionInstance(LocalOption, SortedTests, *InstanceTemplate);
		}
	}

	if (InstanceTemplate->Options.Num() == 0)
	{
		return nullptr;
	}

	// create new instance
	TSharedPtr<FEnvQueryInstance> NewInstance(new FEnvQueryInstance(*InstanceTemplate));
	return NewInstance;
}