x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Create a custom UBrushComponent?

I would like to create a custom UBrushComponent for ProBuilder (I need wireframe rendering).

I created a custom UPrimitiveComponent doing the same thing as UBrushComponent.

The problem is that I can't see the component (even though I add geometry). Why?

Here is the custom UPrimitiveComponent:

PBMC.h

 UCLASS(Blueprintable)
 class UPBMC : public UPrimitiveComponent
 {
     GENERATED_UCLASS_BODY()
     
     UPROPERTY()
     class UModel* Model;
     virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override;
 public:
     virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
     virtual void GetUsedMaterials( TArray<UMaterialInterface*>& OutMaterials ) const override;
     
     UFUNCTION(BlueprintCallable, meta = (DisplayName = "AddFaceToModel"), Category = "Javascript")
     void AddFaceToModel(const TArray<FVector>& Vertices); // Used to add geometry to the component
     
     UFUNCTION(BlueprintCallable, meta = (DisplayName = "UpdateVertices"), Category = "Javascript")
     void UpdateVertices();
 };

PBMC.cpp

 UPBMC::UPBMC(const FObjectInitializer& ObjectInitializer)
 : Super(ObjectInitializer)
 {
     Model = NULL;
 }
 
 FBoxSphereBounds UPBMC::CalcBounds(const FTransform& LocalToWorld) const { /* computes bounds */ }
 void UPBMC::GetUsedMaterials( TArray<UMaterialInterface*>& OutMaterials ) const { /* get materials from polys */ }
 
 FPrimitiveSceneProxy* UPBMC::CreateSceneProxy()
 {
     FPrimitiveSceneProxy* Proxy = NULL;

     // FMeshSceneProxy is just a copy of `FBrushSceneProxy`

     if (Model != NULL)
     {
         // Check to make sure that we want to draw this brushed based on editor settings.
         AActor*    Owner = Cast<AActor>(GetOwner());
         if(Owner)
         {
             // If the editor is in a state where drawing the brush wireframe isn't desired, bail out.
             if( GEngine->ShouldDrawBrushWireframe( Owner ) )
             {
                 Proxy = new FMeshSceneProxy(this, Model, Owner);
             }
         }
         else
         {
             Proxy = new FMeshSceneProxy(this, Model, Owner);
         }
     }
     
     return Proxy;
 }

  // Used to add geometry to the component
 void UPBMC::AddFaceToModel(const TArray<FVector>& Vertices) {
     
     if (Model == NULL)
     {
         Model = NewObject<UModel>(GetWorld(), TEXT("PBModel"));
         Model->Initialize();
         Model->Polys = NewObject<UPolys>(GetOuter(), NAME_None, RF_Transactional);
     }
     
     FPoly NewPoly;
     NewPoly.Init();
     NewPoly.Base = GetOwner()->GetActorLocation();
     
     for(const FVector& V: Vertices) {
         UE_LOG(LogTemp, Warning, TEXT("V: %s"), *V.ToString());
         NewPoly.Vertices.Add(V);
     }
     
     if( NewPoly.Finalize( NULL, 1 ) == 0 )
     {
         Model->Polys->Element.Add( NewPoly );
     }
 }
 
 void UPBMC::UpdateVertices()
 {
     if (Model != NULL)
     {
         Model->BuildVertexBuffers();
         Model->UpdateVertices();
     }
 }

FMeshSceneProxy is just a copy of FBrushSceneProxy.

Here is the full code of the custom scene proxy (copied from BrushComponent.cpp):

 struct FPBModelWireVertex
 {
     FVector Position;
     FPackedNormal TangentX;
     FPackedNormal TangentZ;
     FVector2D UV;
 };
 
 
 class FPBModelWireVertexBuffer : public FVertexBuffer
 {
 public:
     
     /** Initialization constructor. */
     FPBModelWireVertexBuffer(UModel* InModel):
     NumVertices(0)
     {
         
         Polys.Append(InModel->Polys->Element);
         for(int32 PolyIndex = 0;PolyIndex < InModel->Polys->Element.Num();PolyIndex++)
         {
             NumVertices += InModel->Polys->Element[PolyIndex].Vertices.Num();
         }
 
     }
     
     // FRenderResource interface.
     virtual void InitRHI() override
     {
         if(NumVertices)
         {
             FRHIResourceCreateInfo CreateInfo;
             VertexBufferRHI = RHICreateVertexBuffer(NumVertices * sizeof(FPBModelWireVertex),BUF_Static, CreateInfo);
             
             FPBModelWireVertex* DestVertex = (FPBModelWireVertex*)RHILockVertexBuffer(VertexBufferRHI,0,NumVertices * sizeof(FPBModelWireVertex),RLM_WriteOnly);
             for(int32 PolyIndex = 0;PolyIndex < Polys.Num();PolyIndex++)
             {
                 FPoly& Poly = Polys[PolyIndex];
                 for(int32 VertexIndex = 0;VertexIndex < Poly.Vertices.Num();VertexIndex++)
                 {
                     DestVertex->Position = Poly.Vertices[VertexIndex];
                     DestVertex->TangentX = FVector(1,0,0);
                     DestVertex->TangentZ = FVector(0,0,1);
                     // TangentZ.w contains the sign of the tangent basis determinant. Assume +1
                     DestVertex->TangentZ.Vector.W = 255;
                     DestVertex->UV.X     = 0.0f;
                     DestVertex->UV.Y     = 0.0f;
                     DestVertex++;
                 }
             }
             RHIUnlockVertexBuffer(VertexBufferRHI);
         }
     }
     
     // Accessors.
     uint32 GetNumVertices() const { return NumVertices; }
     
 private:
     TArray<FPoly> Polys;
     uint32 NumVertices;
 };
 
 class FPBModelWireIndexBuffer : public FIndexBuffer
 {
 public:
     
     /** Initialization constructor. */
     FPBModelWireIndexBuffer(UModel* InModel):
     NumEdges(0)
     {
         
         Polys.Append(InModel->Polys->Element);
         for(int32 PolyIndex = 0;PolyIndex < InModel->Polys->Element.Num();PolyIndex++)
         {
             NumEdges += InModel->Polys->Element[PolyIndex].Vertices.Num();
         }
 
     }
     
     // FRenderResource interface.
     virtual void InitRHI() override
     {
         if(NumEdges)
         {
             FRHIResourceCreateInfo CreateInfo;
             IndexBufferRHI = RHICreateIndexBuffer(sizeof(uint16),NumEdges * 2 * sizeof(uint16),BUF_Static, CreateInfo);
             
             uint16* DestIndex = (uint16*)RHILockIndexBuffer(IndexBufferRHI,0,NumEdges * 2 * sizeof(uint16),RLM_WriteOnly);
             uint16 BaseIndex = 0;
             for(int32 PolyIndex = 0;PolyIndex < Polys.Num();PolyIndex++)
             {
                 FPoly&    Poly = Polys[PolyIndex];
                 for(int32 VertexIndex = 0;VertexIndex < Poly.Vertices.Num();VertexIndex++)
                 {
                     *DestIndex++ = BaseIndex + VertexIndex;
                     *DestIndex++ = BaseIndex + ((VertexIndex + 1) % Poly.Vertices.Num());
                 }
                 BaseIndex += Poly.Vertices.Num();
             }
             RHIUnlockIndexBuffer(IndexBufferRHI);
         }
     }
     
     // Accessors.
     uint32 GetNumEdges() const { return NumEdges; }
     
 private:
     TArray<FPoly> Polys;
     uint32 NumEdges;
 };
 
 
 class FMeshSceneProxy : public FPrimitiveSceneProxy
 {
     
 public:
     FMeshSceneProxy(UPrimitiveComponent* Component, UModel* InModel, AActor* Owner):
     
     FPrimitiveSceneProxy(Component),
     WireIndexBuffer(InModel),
     WireVertexBuffer(InModel),
     bVolume(false),
     bBuilder(false),
     bSolidWhenSelected(false),
     bInManipulation(false),
     BrushColor(GEngine->C_BrushWire),
     BodySetup(nullptr),
     CollisionResponse(Component->GetCollisionResponseToChannels())
     {
         
 //        if(BodySetup == NULL && Owner)
 //        {
 //            BodySetup = NewObject<UBodySetup>(Owner);
 //            check(BodySetup);
 //            BodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;
 //            BodySetup->CreateFromModel( InModel, true );
 //        }
         
         bWillEverBeLit = false;
         
         if(Owner)
         {
             // If the editor is in a state where drawing the brush wireframe isn't desired, bail out.
 //            if( !GEngine->ShouldDrawBrushWireframe( Owner ) )
 //            {
 //                return;
 //            }
             
             // Determine the type of brush this is.
             bVolume = false; // Owner->IsVolumeBrush();
             bBuilder = true; // FActorEditorUtils::IsABuilderBrush( Owner );
             BrushColor = GEngine->C_BrushWire; // Owner->GetWireColor();
             bSolidWhenSelected = true; // Owner->bSolidWhenSelected;
             bInManipulation = true; // Owner->bInManipulation;
             
             // Builder brushes should be unaffected by level coloration, so if this is a builder brush, use
             // the brush color as the level color.
             if ( bBuilder )
             {
                 LevelColor = BrushColor;
             }
             else
             {
                 // Try to find a color for level coloration.
                 ULevel* Level = Owner->GetLevel();
                 ULevelStreaming* LevelStreaming = FLevelUtils::FindStreamingLevel( Level );
                 if ( LevelStreaming )
                 {
                     LevelColor = LevelStreaming->LevelColor;
                 }
             }
         }
         
         bUseEditorDepthTest = !bInManipulation;
         
         // Get a color for property coloration.
         FColor NewPropertyColor;
         GEngine->GetPropertyColorationColor( (UObject*)Component, NewPropertyColor );
         PropertyColor = NewPropertyColor;
         
 
         ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
                                                    InitBrushVertexFactory,
                                                    FLocalVertexFactory*,VertexFactory,&VertexFactory,
                                                    FVertexBuffer*,WireVertexBuffer,&WireVertexBuffer,
                                                    {
                                                        FLocalVertexFactory::FDataType Data;
                                                        Data.PositionComponent = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(WireVertexBuffer,FPBModelWireVertex,Position,VET_Float3);
                                                        Data.TangentBasisComponents[0] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(WireVertexBuffer,FPBModelWireVertex,TangentX,VET_PackedNormal);
                                                        Data.TangentBasisComponents[1] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(WireVertexBuffer,FPBModelWireVertex,TangentZ,VET_PackedNormal);
                                                        Data.TextureCoordinates.Add( STRUCTMEMBER_VERTEXSTREAMCOMPONENT(WireVertexBuffer,FPBModelWireVertex,UV,VET_Float2) );
                                                        VertexFactory->SetData(Data);
                                                    });
 
     }
     
     virtual ~FMeshSceneProxy()
     {
 
         VertexFactory.ReleaseResource();
         WireIndexBuffer.ReleaseResource();
         WireVertexBuffer.ReleaseResource();
 
         
     }
     
     bool IsCollisionView(const FEngineShowFlags& EngineShowFlags, bool & bDrawCollision) const
     {
         const bool bInCollisionView = EngineShowFlags.CollisionVisibility || EngineShowFlags.CollisionPawn;
         if (bInCollisionView && IsCollisionEnabled())
         {
             bDrawCollision = EngineShowFlags.CollisionPawn && (CollisionResponse.GetResponse(ECC_Pawn) != ECR_Ignore);
             bDrawCollision |= EngineShowFlags.CollisionVisibility && (CollisionResponse.GetResponse(ECC_Visibility) != ECR_Ignore);
         }
         else
         {
             bDrawCollision = false;
         }
         
         return bInCollisionView;
     }
     
     virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
     {
         QUICK_SCOPE_CYCLE_COUNTER( STAT_BrushSceneProxy_GetDynamicMeshElements );
         
         if( AllowDebugViewmodes() )
         {
             for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
             {
                 if (VisibilityMap & (1 << ViewIndex))
                 {
                     const FSceneView* View = Views[ViewIndex];
                     
                     bool bDrawCollision = false;
                     const bool bInCollisionView = IsCollisionView(ViewFamily.EngineShowFlags, bDrawCollision);
                     
                     // Draw solid if 'solid when selected' and selected, or we are in a 'collision view'
                     const bool bDrawSolid = ((bSolidWhenSelected && IsSelected()) || (bInCollisionView && bDrawCollision));
                     // Don't draw wireframe if in a collision view mode and not drawing solid
                     const bool bDrawWireframe = !bInCollisionView;
                     
                     // Choose color to draw it
                     FLinearColor DrawColor = BrushColor;
                     // In a collision view mode
                     if(bInCollisionView)
                     {
                         DrawColor = BrushColor;
                     }
                     else if(View->Family->EngineShowFlags.PropertyColoration)
                     {
                         DrawColor = PropertyColor;
                     }
                     else if(View->Family->EngineShowFlags.LevelColoration)
                     {
                         DrawColor = LevelColor;
                     }
                     
                     
                     // SOLID
                     if(bDrawSolid)
                     {
                         if(BodySetup != NULL)
                         {
                             auto SolidMaterialInstance = new FColoredMaterialRenderProxy(
                                                                                          GEngine->ShadedLevelColorationUnlitMaterial->GetRenderProxy(IsSelected(), IsHovered()),
                                                                                          DrawColor
                                                                                          );
                             
                             Collector.RegisterOneFrameMaterialProxy(SolidMaterialInstance);
                             
                             FTransform GeomTransform(GetLocalToWorld());
                             BodySetup->AggGeom.GetAggGeom(GeomTransform, DrawColor.ToFColor(true), /*Material=*/SolidMaterialInstance, false, /*bSolid=*/ true, UseEditorDepthTest(), ViewIndex, Collector);
                         }
                     }
                     // WIREFRAME
                     else if(bDrawWireframe)
                     {
                         // If we have the editor data (Wire Buffers) use those for wireframe
 
                         if(WireIndexBuffer.GetNumEdges() && WireVertexBuffer.GetNumVertices())
                         {
                             auto WireframeMaterial = new FColoredMaterialRenderProxy(
                                                                                      GEngine->LevelColorationUnlitMaterial->GetRenderProxy(IsSelected(), IsHovered()),
                                                                                      GetViewSelectionColor(DrawColor, *View, !(GIsEditor && (View->Family->EngineShowFlags.Selection)) || IsSelected(), IsHovered(), false, IsIndividuallySelected() )
                                                                                      );
                             
                             Collector.RegisterOneFrameMaterialProxy(WireframeMaterial);
                             
                             FMeshBatch& Mesh = Collector.AllocateMesh();
                             FMeshBatchElement& BatchElement = Mesh.Elements[0];
                             BatchElement.IndexBuffer = &WireIndexBuffer;
                             Mesh.VertexFactory = &VertexFactory;
                             Mesh.MaterialRenderProxy = WireframeMaterial;
                             BatchElement.PrimitiveUniformBufferResource = &GetUniformBuffer();
                             BatchElement.FirstIndex = 0;
                             BatchElement.NumPrimitives = WireIndexBuffer.GetNumEdges();
                             BatchElement.MinVertexIndex = 0;
                             BatchElement.MaxVertexIndex = WireVertexBuffer.GetNumVertices() - 1;
                             Mesh.CastShadow = false;
                             Mesh.Type = PT_LineList;
                             Mesh.DepthPriorityGroup = IsSelected() ? SDPG_Foreground : SDPG_World;
                             Collector.AddMesh(ViewIndex, Mesh);
                         }
                         else
 
                             if(BodySetup != NULL)
                                 // If not, use the body setup for wireframe
                             {
                                 FTransform GeomTransform(GetLocalToWorld());
                                 BodySetup->AggGeom.GetAggGeom(GeomTransform, GetSelectionColor(DrawColor, IsSelected(), IsHovered()).ToFColor(true), /* Material=*/ NULL, false, /* bSolid=*/ false, UseEditorDepthTest(), ViewIndex, Collector);
                             }
                         
                     }
                 }
             }
         }
     }
     
     virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
     {
         bool bVisible = false;
         
         
         // We render volumes in collision view. In game, always, in editor, if the EngineShowFlags.Volumes option is on.
         if(bSolidWhenSelected && IsSelected())
         {
             FPrimitiveViewRelevance Result;
             Result.bDrawRelevance = true;
             Result.bDynamicRelevance = true;
             return Result;
         }
         
         const bool bInCollisionView = (View->Family->EngineShowFlags.Collision || View->Family->EngineShowFlags.CollisionVisibility || View->Family->EngineShowFlags.CollisionPawn);
         
         if(IsShown(View))
         {
             bool bNeverShow = false;
             
             if( GIsEditor )
             {
                 const bool bShowBuilderBrush = View->Family->EngineShowFlags.BuilderBrush != 0;
                 
                 // Only render builder brush and only if the show flags indicate that we should render builder brushes.
                 if( bBuilder && (!bShowBuilderBrush) )
                 {
                     bNeverShow = true;
                 }
             }
             
             if(bNeverShow == false)
             {
                 const bool bBSPVisible = View->Family->EngineShowFlags.BSP;
                 const bool bBrushesVisible = View->Family->EngineShowFlags.Brushes;
                 
                 if ( !bVolume ) // EngineShowFlags.Collision does not apply to volumes
                 {
                     if( (bBSPVisible && bBrushesVisible) )
                     {
                         bVisible = true;
                     }
                 }
                 
                 // See if we should be visible because we are in a 'collision view' and have collision enabled
                 if (bInCollisionView && IsCollisionEnabled())
                 {
                     bVisible = true;
                 }
                 
                 // Always show the build brush and any brushes that are selected in the editor.
                 if( GIsEditor )
                 {
                     if( bBuilder || IsSelected() )
                     {
                         bVisible = true;
                     }
                 }
                 
                 if ( bVolume )
                 {
                     const bool bVolumesVisible = View->Family->EngineShowFlags.Volumes;
                     if(!GIsEditor || View->bIsGameView || bVolumesVisible)
                     {
                         bVisible = true;
                     }
                 }        
             }
         }
         
         FPrimitiveViewRelevance Result;
         Result.bDrawRelevance = bVisible;
         Result.bDynamicRelevance = true;
         Result.bShadowRelevance = IsShadowCast(View);
         if(bInManipulation)
         {
             Result.bEditorNoDepthTestPrimitiveRelevance = true;
         }
         
         // Don't render on top in 'collision view' modes
         if(!bInCollisionView && !View->bIsGameView)
         {
             Result.bEditorPrimitiveRelevance = true;
         }
         
         return Result;
     }
     
     virtual void CreateRenderThreadResources() override
     {
 
         VertexFactory.InitResource();
         WireIndexBuffer.InitResource();
         WireVertexBuffer.InitResource();
 
         
     }
     
     virtual uint32 GetMemoryFootprint( void ) const override { return( sizeof( *this ) + GetAllocatedSize() ); }
     uint32 GetAllocatedSize( void ) const { return( FPrimitiveSceneProxy::GetAllocatedSize() ); }
     
 private:
 
     FLocalVertexFactory VertexFactory;
     FPBModelWireIndexBuffer WireIndexBuffer;
     FPBModelWireVertexBuffer WireVertexBuffer;
 
     
     uint32 bVolume : 1;
     uint32 bBuilder : 1;    
     uint32 bSolidWhenSelected : 1;
     uint32 bInManipulation : 1;
     
     FColor BrushColor;
     FLinearColor LevelColor;
     FColor PropertyColor;
     
     /** Collision Response of this component**/
     UBodySetup* BodySetup;
     FCollisionResponseContainer CollisionResponse;
 };










Product Version: UE 4.13
Tags:
more ▼

asked Oct 31 '16 at 03:38 PM in C++ Programming

avatar image

Arthur Masson
294 36 44 61

(comments are locked)
10|2000 characters needed characters left

0 answers: sort voted first
Be the first one to answer this question
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question