//------------------------------------------------------------------------------ int32 FDeferredScriptTracker::ResolveDeferredScripts(ULinkerLoad* Linker) { FArchive& Ar = *Linker; if (FStructScriptLoader::ShouldDeferScriptSerialization(Ar)) { return 0; } #if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS TGuardValue<ULinkerLoad*> ScopedResolvingLinker(ResolvingLinker, Linker); #endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS TArray<FDeferredScriptLoader> DefferedLinkerScripts; DeferredScriptLoads.MultiFind(Linker, DefferedLinkerScripts); // remove before we resolve, because a failed call to // FDeferredScriptLoader::Resolve() could insert back into this list DeferredScriptLoads.Remove(Linker); int32 const SerializationPosToRestore = Ar.Tell(); int32 ResolveCount = 0; for (FDeferredScriptLoader& DeferredScript : DefferedLinkerScripts) { if (DeferredScript.Resolve(Ar)) { ++ResolveCount; } } Ar.Seek(SerializationPosToRestore); return ResolveCount; }
void USceneCaptureComponentCube::UpdateDeferredCaptures( FSceneInterface* Scene ) { UWorld* World = Scene->GetWorld(); if( World && CubedSceneCapturesToUpdateMap.Num() > 0 ) { World->SendAllEndOfFrameUpdates(); // Only update the scene captures associated with the current scene. // Updating others not associated with the scene would cause invalid data to be rendered into the target TArray< TWeakObjectPtr<USceneCaptureComponentCube> > SceneCapturesToUpdate; CubedSceneCapturesToUpdateMap.MultiFind( World, SceneCapturesToUpdate ); for( TWeakObjectPtr<USceneCaptureComponentCube> Component : SceneCapturesToUpdate ) { if( Component.IsValid() ) { Scene->UpdateSceneCaptureContents( Component.Get() ); } } // All scene captures for this world have been updated CubedSceneCapturesToUpdateMap.Remove( World ); } }
void USceneCaptureComponent2D::UpdateDeferredCaptures( FSceneInterface* Scene ) { UWorld* World = Scene->GetWorld(); if( World && SceneCapturesToUpdateMap.Num() > 0 ) { // Only update the scene captures assoicated with the current scene. // Updating others not associated with the scene would cause invalid data to be rendered into the target TArray<USceneCaptureComponent2D*> SceneCapturesToUpdate; SceneCapturesToUpdateMap.MultiFind( World, SceneCapturesToUpdate ); for( USceneCaptureComponent2D* Component : SceneCapturesToUpdate ) { Scene->UpdateSceneCaptureContents( Component ); } // All scene captures for this world have been updated SceneCapturesToUpdateMap.Remove( World) ; } }
/** * Loads all of the UClass-es used by code so we can search for commandlets */ static void LoadAllClasses(void) { TArray<FString> ScriptPackageNames; TMultiMap<FString,FString>* Sec = GConfig->GetSectionPrivate( TEXT("UnrealEd.EditorEngine"), 0, 1, GEngineIni ); if (Sec != NULL) { // Get the list of all code packages Sec->MultiFind( FString(TEXT("EditPackages")), ScriptPackageNames ); // Iterate through loading each package for (INT Index = 0; Index < ScriptPackageNames.Num(); Index++) { // Loading without LOAD_Verify should load all objects UPackage* Package = UObject::LoadPackage(NULL,*ScriptPackageNames(Index),LOAD_NoWarn | LOAD_Quiet); if (Package == NULL) { warnf(TEXT("Error loading package %s"),*ScriptPackageNames(Index)); } } } else { warnf(TEXT("Error finding the code packages")); } }
void URuntimeMeshLibrary::CalculateTangentsForMesh(TFunction<int32(int32 Index)> IndexAccessor, TFunction<FVector(int32 Index)> VertexAccessor, TFunction<FVector2D(int32 Index)> UVAccessor, TFunction<void(int32 Index, FVector TangentX, FVector TangentY, FVector TangentZ)> TangentSetter, int32 NumVertices, int32 NumUVs, int32 NumIndices, bool bCreateSmoothNormals) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMeshLibrary_CalculateTangentsForMesh); if (NumVertices == 0 || NumIndices == 0) { return; } // Calculate the duplicate vertices map if we're wanting smooth normals. Don't find duplicates if we don't want smooth normals // that will cause it to only smooth across faces sharing a common vertex, not across faces with vertices of common position const TMultiMap<uint32, uint32> DuplicateVertexMap = bCreateSmoothNormals ? FRuntimeMeshInternalUtilities::FindDuplicateVerticesMap(VertexAccessor, NumVertices) : TMultiMap<uint32, uint32>(); // Number of triangles const int32 NumTris = NumIndices / 3; // Map of vertex to triangles in Triangles array TMultiMap<uint32, uint32> VertToTriMap; // Map of vertex to triangles to consider for normal calculation TMultiMap<uint32, uint32> VertToTriSmoothMap; // Normal/tangents for each face TArray<FVector> FaceTangentX, FaceTangentY, FaceTangentZ; FaceTangentX.AddUninitialized(NumTris); FaceTangentY.AddUninitialized(NumTris); FaceTangentZ.AddUninitialized(NumTris); // Iterate over triangles for (int TriIdx = 0; TriIdx < NumTris; TriIdx++) { uint32 CornerIndex[3]; FVector P[3]; for (int32 CornerIdx = 0; CornerIdx < 3; CornerIdx++) { // Find vert index (clamped within range) uint32 VertIndex = FMath::Min(IndexAccessor((TriIdx * 3) + CornerIdx), NumVertices - 1); CornerIndex[CornerIdx] = VertIndex; P[CornerIdx] = VertexAccessor(VertIndex); // Find/add this vert to index buffer TArray<uint32> VertOverlaps; DuplicateVertexMap.MultiFind(VertIndex, VertOverlaps); // Remember which triangles map to this vert VertToTriMap.AddUnique(VertIndex, TriIdx); VertToTriSmoothMap.AddUnique(VertIndex, TriIdx); // Also update map of triangles that 'overlap' this vert (ie don't match UV, but do match smoothing) and should be considered when calculating normal for (int32 OverlapIdx = 0; OverlapIdx < VertOverlaps.Num(); OverlapIdx++) { // For each vert we overlap.. int32 OverlapVertIdx = VertOverlaps[OverlapIdx]; // Add this triangle to that vert VertToTriSmoothMap.AddUnique(OverlapVertIdx, TriIdx); // And add all of its triangles to us TArray<uint32> OverlapTris; VertToTriMap.MultiFind(OverlapVertIdx, OverlapTris); for (int32 OverlapTriIdx = 0; OverlapTriIdx < OverlapTris.Num(); OverlapTriIdx++) { VertToTriSmoothMap.AddUnique(VertIndex, OverlapTris[OverlapTriIdx]); } } } // Calculate triangle edge vectors and normal const FVector Edge21 = P[1] - P[2]; const FVector Edge20 = P[0] - P[2]; const FVector TriNormal = (Edge21 ^ Edge20).GetSafeNormal(); // If we have UVs, use those to calculate if (NumUVs == NumVertices) { const FVector2D T1 = UVAccessor(CornerIndex[0]); const FVector2D T2 = UVAccessor(CornerIndex[1]); const FVector2D T3 = UVAccessor(CornerIndex[2]); // float X1 = P[1].X - P[0].X; // float X2 = P[2].X - P[0].X; // float Y1 = P[1].Y - P[0].Y; // float Y2 = P[2].Y - P[0].Y; // float Z1 = P[1].Z - P[0].Z; // float Z2 = P[2].Z - P[0].Z; // // float S1 = U1.X - U0.X; // float S2 = U2.X - U0.X; // float T1 = U1.Y - U0.Y; // float T2 = U2.Y - U0.Y; // // float R = 1.0f / (S1 * T2 - S2 * T1); // FaceTangentX[TriIdx] = FVector((T2 * X1 - T1 * X2) * R, (T2 * Y1 - T1 * Y2) * R, // (T2 * Z1 - T1 * Z2) * R); // FaceTangentY[TriIdx] = FVector((S1 * X2 - S2 * X1) * R, (S1 * Y2 - S2 * Y1) * R, // (S1 * Z2 - S2 * Z1) * R); FMatrix ParameterToLocal( FPlane(P[1].X - P[0].X, P[1].Y - P[0].Y, P[1].Z - P[0].Z, 0), FPlane(P[2].X - P[0].X, P[2].Y - P[0].Y, P[2].Z - P[0].Z, 0), FPlane(P[0].X, P[0].Y, P[0].Z, 0), FPlane(0, 0, 0, 1) ); FMatrix ParameterToTexture( FPlane(T2.X - T1.X, T2.Y - T1.Y, 0, 0), FPlane(T3.X - T1.X, T3.Y - T1.Y, 0, 0), FPlane(T1.X, T1.Y, 1, 0), FPlane(0, 0, 0, 1) ); // Use InverseSlow to catch singular matrices. Inverse can miss this sometimes. const FMatrix TextureToLocal = ParameterToTexture.Inverse() * ParameterToLocal; FaceTangentX[TriIdx] = TextureToLocal.TransformVector(FVector(1, 0, 0)).GetSafeNormal(); FaceTangentY[TriIdx] = TextureToLocal.TransformVector(FVector(0, 1, 0)).GetSafeNormal(); } else { FaceTangentX[TriIdx] = Edge20.GetSafeNormal(); FaceTangentY[TriIdx] = (FaceTangentX[TriIdx] ^ TriNormal).GetSafeNormal(); } FaceTangentZ[TriIdx] = TriNormal; } // Arrays to accumulate tangents into TArray<FVector> VertexTangentXSum, VertexTangentYSum, VertexTangentZSum; VertexTangentXSum.AddZeroed(NumVertices); VertexTangentYSum.AddZeroed(NumVertices); VertexTangentZSum.AddZeroed(NumVertices); // For each vertex.. for (int VertxIdx = 0; VertxIdx < NumVertices; VertxIdx++) { // Find relevant triangles for normal TArray<uint32> SmoothTris; VertToTriSmoothMap.MultiFind(VertxIdx, SmoothTris); for (int i = 0; i < SmoothTris.Num(); i++) { uint32 TriIdx = SmoothTris[i]; VertexTangentZSum[VertxIdx] += FaceTangentZ[TriIdx]; } // Find relevant triangles for tangents TArray<uint32> TangentTris; VertToTriMap.MultiFind(VertxIdx, TangentTris); for (int i = 0; i < TangentTris.Num(); i++) { uint32 TriIdx = TangentTris[i]; VertexTangentXSum[VertxIdx] += FaceTangentX[TriIdx]; VertexTangentYSum[VertxIdx] += FaceTangentY[TriIdx]; } } // Finally, normalize tangents and build output arrays for (int VertxIdx = 0; VertxIdx < NumVertices; VertxIdx++) { FVector& TangentX = VertexTangentXSum[VertxIdx]; FVector& TangentY = VertexTangentYSum[VertxIdx]; FVector& TangentZ = VertexTangentZSum[VertxIdx]; TangentX.Normalize(); //TangentY.Normalize(); TangentZ.Normalize(); // Use Gram-Schmidt orthogonalization to make sure X is orthonormal with Z TangentX -= TangentZ * (TangentZ | TangentX); TangentX.Normalize(); TangentY.Normalize(); TangentSetter(VertxIdx, TangentX, TangentY, TangentZ); } }
// Internal helper function to find all graph nodes that reference the specified object static int32 FindReferencedGraphNodes(TMultiMap<UObject*, FRefGraphItem*>& InputGraphNodeList, UObject* ReferencedObj, TArray<FRefGraphItem*>& FoundNodes) { InputGraphNodeList.MultiFind(ReferencedObj, FoundNodes); return FoundNodes.Num(); }
void FPropertyTable::UpdateColumns() { if( Orientation == EPropertyTableOrientation::AlignPropertiesInColumns) { TMultiMap< UProperty*, TSharedRef< IPropertyTableColumn > > ColumnsMap; for (int ColumnIdx = 0; ColumnIdx < Columns.Num(); ++ColumnIdx) { TSharedRef< IDataSource > DataSource = Columns[ColumnIdx]->GetDataSource(); TSharedPtr< FPropertyPath > PropertyPath = DataSource->AsPropertyPath(); if( PropertyPath.IsValid() && PropertyPath->GetNumProperties() > 0 ) { ColumnsMap.Add(PropertyPath->GetRootProperty().Property.Get(), Columns[ColumnIdx]); } } Columns.Empty(); if ( ShowRowHeader ) { TSharedRef< IPropertyTableColumn > NewColumn = MakeShareable( new FPropertyTableRowHeaderColumn( SharedThis( this ) ) ); Columns.Add( NewColumn ); } if ( ShowObjectName ) { TSharedRef< IPropertyTableColumn > NewColumn = MakeShareable( new FPropertyTableObjectNameColumn( SharedThis( this ) ) ); NewColumn->SetFrozen( true ); Columns.Add( NewColumn ); } TArray< TWeakObjectPtr< UStruct > > UniqueTypes; TArray< int > TypeCounter; for( auto ObjectIter = SourceObjects.CreateConstIterator(); ObjectIter; ++ObjectIter ) { auto Object = *ObjectIter; if( !Object.IsValid() ) { continue; } const TSharedRef< FObjectPropertyNode > RootObjectNode = GetObjectPropertyNode( Object ); TWeakObjectPtr< UStruct > Type; if ( RootPath->GetNumProperties() == 0 ) { Type = RootObjectNode->GetObjectBaseClass(); } else { const TSharedPtr< FPropertyNode > PropertyNode = FPropertyNode::FindPropertyNodeByPath( RootPath, RootObjectNode ); if ( !PropertyNode.IsValid() ) { continue; } const TWeakObjectPtr< UProperty > Property = PropertyNode->GetProperty(); if ( !Property.IsValid() || !Property->IsA( UStructProperty::StaticClass() ) ) { continue; } UStructProperty* StructProperty = Cast< UStructProperty >( Property.Get() ); Type = StructProperty->Struct; } if ( !Type.IsValid() ) { continue; } int FoundIndex = -1; if ( UniqueTypes.Find( Type, /*OUT*/FoundIndex ) ) { TypeCounter[ FoundIndex ] = TypeCounter[ FoundIndex ] + 1; continue; } UniqueTypes.Add( Type ); TypeCounter.Add( 1 ); } if ( UniqueTypes.Num() > 0 ) { int HighestCountIndex = 0; int HighestCount = 0; for (int Index = 0; Index < TypeCounter.Num(); Index++) { if ( TypeCounter[Index] > HighestCount ) { HighestCountIndex = Index; HighestCount = TypeCounter[Index]; } } TWeakObjectPtr< UStruct > PrimaryType = UniqueTypes[ HighestCountIndex ]; for (TFieldIterator<UProperty> PropertyIter( PrimaryType.Get(), EFieldIteratorFlags::IncludeSuper); PropertyIter; ++PropertyIter) { TWeakObjectPtr< UProperty > Property = *PropertyIter; if ( PropertyIter->HasAnyPropertyFlags(CPF_AssetRegistrySearchable) ) { TArray< TSharedRef< IPropertyTableColumn > > MapResults; ColumnsMap.MultiFind(Property.Get(), MapResults); if(MapResults.Num() > 0) { for (int MapIdx = 0; MapIdx < MapResults.Num(); ++MapIdx) { Columns.Add(MapResults[MapIdx]); } } else { TSharedRef< IPropertyTableColumn > NewColumn = CreateColumn( Property ); Columns.Add( NewColumn ); } } } } } else { Columns.Empty(); if( SourceObjects.Num() > 0 ) { UClass* ObjectClass = SourceObjects[0]->GetClass(); TSharedRef< IPropertyTableColumn > HeadingColumn = MakeShareable( new FPropertyTablePropertyNameColumn( SharedThis( this ) ) ); Columns.Add( HeadingColumn ); for( auto ObjectIter = SourceObjects.CreateConstIterator(); ObjectIter; ++ObjectIter ) { auto Object = *ObjectIter; if( Object.IsValid() ) { const TSharedRef< FObjectPropertyNode > ObjectNode = GetObjectPropertyNode( Object ); const TSharedPtr< FPropertyNode > PropertyNode = FPropertyNode::FindPropertyNodeByPath( RootPath, ObjectNode ); UProperty* Property = PropertyNode->GetProperty(); if ( Property != NULL && Property->IsA( UArrayProperty::StaticClass() ) ) { for (int ChildIdx = 0; ChildIdx < PropertyNode->GetNumChildNodes(); ChildIdx++) { TSharedPtr< FPropertyNode > ChildNode = PropertyNode->GetChildNode( ChildIdx ); FPropertyInfo Extension; Extension.Property = ChildNode->GetProperty(); Extension.ArrayIndex = ChildNode->GetArrayIndex(); TSharedRef< FPropertyPath > Path = FPropertyPath::CreateEmpty()->ExtendPath( Extension ); TSharedRef< IPropertyTableColumn > NewColumn = MakeShareable( new FPropertyTableColumn( SharedThis( this ), Object, Path ) ); Columns.Add( NewColumn ); } } else if ( Property != NULL ) { TSharedRef< IPropertyTableColumn > NewColumn = MakeShareable( new FPropertyTableColumn( SharedThis( this ), Object ) ); Columns.Add( NewColumn ); } } } } } ColumnsChanged.Broadcast(); }
void FPropertyTable::UpdateRows() { if( Orientation == EPropertyTableOrientation::AlignPropertiesInColumns ) { TMultiMap< UObject*, TSharedRef< IPropertyTableRow > > RowsMap; for (int RowIdx = 0; RowIdx < Rows.Num(); ++RowIdx) { RowsMap.Add(Rows[RowIdx]->GetDataSource()->AsUObject().Get(), Rows[RowIdx]); } Rows.Empty(); for( auto ObjectIter = SourceObjects.CreateConstIterator(); ObjectIter; ++ObjectIter ) { const TWeakObjectPtr< UObject >& Object = *ObjectIter; if( Object.IsValid() ) { const TSharedRef< FObjectPropertyNode > ObjectNode = GetObjectPropertyNode( Object ); const TSharedPtr< FPropertyNode > PropertyNode = FPropertyNode::FindPropertyNodeByPath( RootPath, ObjectNode ); //@todo This system will need to change in order to properly support arrays [11/30/2012 Justin.Sargent] if ( PropertyNode.IsValid() ) { UProperty* Property = PropertyNode->GetProperty(); if ( Property != NULL && Property->IsA( UArrayProperty::StaticClass() ) ) { for (int ChildIdx = 0; ChildIdx < PropertyNode->GetNumChildNodes(); ChildIdx++) { TSharedPtr< FPropertyNode > ChildNode = PropertyNode->GetChildNode( ChildIdx ); FPropertyInfo Extension; Extension.Property = ChildNode->GetProperty(); Extension.ArrayIndex = ChildNode->GetArrayIndex(); TSharedRef< FPropertyPath > Path = FPropertyPath::CreateEmpty()->ExtendPath( Extension ); TArray< TSharedRef< IPropertyTableRow > > MapResults; bool Found = false; RowsMap.MultiFind(Object.Get(), MapResults); for (int MapIdx = 0; MapIdx < MapResults.Num(); ++MapIdx) { if (FPropertyPath::AreEqual(Path, MapResults[MapIdx]->GetPartialPath())) { Found = true; Rows.Add(MapResults[MapIdx]); break; } } if (!Found) { Rows.Add( MakeShareable( new FPropertyTableRow( SharedThis( this ), Object, Path ) ) ); } } } else { TArray< TSharedRef< IPropertyTableRow > > MapResults; bool Found = false; RowsMap.MultiFind(Object.Get(), MapResults); for (int MapIdx = 0; MapIdx < MapResults.Num(); ++MapIdx) { if (MapResults[MapIdx]->GetPartialPath()->GetNumProperties() == 0) { Found = true; Rows.Add(MapResults[MapIdx]); break; } } if (!Found) { Rows.Add( MakeShareable( new FPropertyTableRow( SharedThis( this ), Object ) ) ); } } } } } } const TSharedPtr< IPropertyTableColumn > Column = SortedByColumn.Pin(); if ( Column.IsValid() && SortedColumnMode != EColumnSortMode::None ) { Column->Sort( Rows, SortedColumnMode ); } RowsChanged.Broadcast(); }
void FRCPassPostProcessSelectionOutlineColor::Process(FRenderingCompositePassContext& Context) { SCOPED_DRAW_EVENT(Context.RHICmdList, PostProcessSelectionOutlineBuffer, DEC_SCENE_ITEMS); const FPooledRenderTargetDesc* SceneColorInputDesc = GetInputDesc(ePId_Input0); if(!SceneColorInputDesc) { // input is not hooked up correctly return; } const FViewInfo& View = Context.View; FIntRect ViewRect = View.ViewRect; FIntPoint SrcSize = SceneColorInputDesc->Extent; // Get the output render target const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context); // Set the render target/viewport. SetRenderTarget(Context.RHICmdList, FTextureRHIParamRef(), DestRenderTarget.TargetableTexture); // This is a reversed Z depth surface, so 0.0f is the far plane. Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect()); Context.SetViewportAndCallRHI(ViewRect); if (View.Family->EngineShowFlags.Selection) { const bool bUseGetMeshElements = ShouldUseGetDynamicMeshElements(); if (bUseGetMeshElements) { FHitProxyDrawingPolicyFactory::ContextType FactoryContext; //@todo - use memstack TMap<FName, int32> ActorNameToStencilIndex; ActorNameToStencilIndex.Add(NAME_BSP, 1); Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); Context.RHICmdList.SetBlendState(TStaticBlendStateWriteMask<CW_NONE, CW_NONE, CW_NONE, CW_NONE>::GetRHI()); for (int32 MeshBatchIndex = 0; MeshBatchIndex < View.DynamicMeshElements.Num(); MeshBatchIndex++) { const FMeshBatchAndRelevance& MeshBatchAndRelevance = View.DynamicMeshElements[MeshBatchIndex]; const FPrimitiveSceneProxy* PrimitiveSceneProxy = MeshBatchAndRelevance.PrimitiveSceneProxy; if (PrimitiveSceneProxy->IsSelected() && MeshBatchAndRelevance.Mesh->bUseSelectionOutline) { const int32* AssignedStencilIndexPtr = ActorNameToStencilIndex.Find(PrimitiveSceneProxy->GetOwnerName()); if (!AssignedStencilIndexPtr) { AssignedStencilIndexPtr = &ActorNameToStencilIndex.Add(PrimitiveSceneProxy->GetOwnerName(), ActorNameToStencilIndex.Num() + 1); } // This is a reversed Z depth surface, using CF_GreaterEqual. // Note that the stencil value will overflow with enough selected objects Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<true, CF_GreaterEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace>::GetRHI(), *AssignedStencilIndexPtr); const FMeshBatch& MeshBatch = *MeshBatchAndRelevance.Mesh; FHitProxyDrawingPolicyFactory::DrawDynamicMesh(Context.RHICmdList, View, FactoryContext, MeshBatch, false, true, MeshBatchAndRelevance.PrimitiveSceneProxy, MeshBatch.BatchHitProxyId); } } } else if (View.VisibleDynamicPrimitives.Num() > 0) { TDynamicPrimitiveDrawer<FHitProxyDrawingPolicyFactory> Drawer(Context.RHICmdList, &View, FHitProxyDrawingPolicyFactory::ContextType(), true, false, false, true); TMultiMap<FName, const FPrimitiveSceneInfo*> PrimitivesByActor; for (int32 PrimitiveIndex = 0; PrimitiveIndex < View.VisibleDynamicPrimitives.Num();PrimitiveIndex++) { const FPrimitiveSceneInfo* PrimitiveSceneInfo = View.VisibleDynamicPrimitives[PrimitiveIndex]; // Only draw the primitive if relevant if(PrimitiveSceneInfo->Proxy->IsSelected()) { PrimitivesByActor.Add(PrimitiveSceneInfo->Proxy->GetOwnerName(), PrimitiveSceneInfo); } } if (PrimitivesByActor.Num()) { // 0 means no object, 1 means BSP so we start with 2 uint32 StencilValue = 2; Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); Context.RHICmdList.SetBlendState(TStaticBlendStateWriteMask<CW_NONE, CW_NONE, CW_NONE, CW_NONE>::GetRHI()); // Sort by actor TArray<FName> Actors; PrimitivesByActor.GetKeys(Actors); for( TArray<FName>::TConstIterator ActorIt(Actors); ActorIt; ++ActorIt ) { bool bBSP = *ActorIt == NAME_BSP; if (bBSP) { // This is a reversed Z depth surface, using CF_GreaterEqual. Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<true, CF_GreaterEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace>::GetRHI(), 1); } else { // This is a reversed Z depth surface, using CF_GreaterEqual. Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<true, CF_GreaterEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace>::GetRHI(), StencilValue); // we want to use 1..255 for all objects, not correct silhouettes after that is acceptable StencilValue = (StencilValue == 255) ? 2 : (StencilValue + 1); } TArray<const FPrimitiveSceneInfo*> Primitives; PrimitivesByActor.MultiFind(*ActorIt, Primitives); for( TArray<const FPrimitiveSceneInfo*>::TConstIterator PrimIt(Primitives); PrimIt; ++PrimIt ) { const FPrimitiveSceneInfo* PrimitiveSceneInfo = *PrimIt; // Render the object to the stencil/depth buffer Drawer.SetPrimitive(PrimitiveSceneInfo->Proxy); PrimitiveSceneInfo->Proxy->DrawDynamicElements(&Drawer, &View); } } } } // to get an outline around the objects if it's partly outside of the screen { FIntRect InnerRect = ViewRect; // 1 as we have an outline that is that thick InnerRect.InflateRect(-1); // We could use Clear with InnerRect but this is just an optimization - on some hardware it might do a full clear (and we cannot disable yet) // RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, InnerRect); // so we to 4 clears - one for each border. // top Context.RHICmdList.SetScissorRect(true, ViewRect.Min.X, ViewRect.Min.Y, ViewRect.Max.X, InnerRect.Min.Y); Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect()); // bottom Context.RHICmdList.SetScissorRect(true, ViewRect.Min.X, InnerRect.Max.Y, ViewRect.Max.X, ViewRect.Max.Y); Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect()); // left Context.RHICmdList.SetScissorRect(true, ViewRect.Min.X, ViewRect.Min.Y, InnerRect.Min.X, ViewRect.Max.Y); Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect()); // right Context.RHICmdList.SetScissorRect(true, InnerRect.Max.X, ViewRect.Min.Y, ViewRect.Max.X, ViewRect.Max.Y); Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect()); Context.RHICmdList.SetScissorRect(false, 0, 0, 0, 0); } } // Resolve to the output Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams()); }