Primitive
components are the most complex type of Actor
Component because they not only have a transform, but are also rendered on screen.
MeshComponent
. When Visual Studio loads, add the following to your class header file:UCLASS(ClassGroup=Experimental, meta = (BlueprintSpawnableComponent)) public: virtual FPrimitiveSceneProxy* CreateSceneProxy() override; TArray<int32> Indices; TArray<FVector> Vertices; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Materials) UMaterial* TheMaterial;
CreateSceneProxy
function in our cpp file:FPrimitiveSceneProxy* UMyMeshComponent::CreateSceneProxy() { FPrimitiveSceneProxy* Proxy = NULL; Proxy = new FMySceneProxy(this); return Proxy; }
FMySceneProxy
, which we need to implement. Do so by adding the following code above the CreateSceneProxy
function:class FMySceneProxy : public FPrimitiveSceneProxy { public: FMySceneProxy(UMyMeshComponent* Component) :FPrimitiveSceneProxy(Component), Indices(Component->Indices), TheMaterial(Component->TheMaterial) { VertexBuffer = FMyVertexBuffer(); IndexBuffer = FMyIndexBuffer(); for (FVector Vertex : Component->Vertices) { Vertices.Add(FDynamicMeshVertex(Vertex)); } }; UPROPERTY() UMaterial* TheMaterial; virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override { FPrimitiveViewRelevance Result; Result.bDynamicRelevance = true; Result.bDrawRelevance = true; Result.bNormalTranslucencyRelevance = true; return Result; } virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FDynamicMeshBuilder MeshBuilder; if (Vertices.Num() == 0) { return; } MeshBuilder.AddVertices(Vertices); MeshBuilder.AddTriangles(Indices); MeshBuilder.GetMesh(FMatrix::Identity, new FColoredMaterialRenderProxy(TheMaterial->GetRenderProxy(false), FLinearColor::Gray), GetDepthPriorityGroup(Views[ViewIndex]), true, true, ViewIndex, Collector); } } uint32 FMySceneProxy::GetMemoryFootprint(void) const override { return sizeof(*this); } virtual ~FMySceneProxy() {}; private: TArray<FDynamicMeshVertex> Vertices; TArray<int32> Indices; FMyVertexBuffer VertexBuffer; FMyIndexBuffer IndexBuffer; };
class FMyVertexBuffer : public FVertexBuffer { public: TArray<FVector> Vertices; virtual void InitRHI() override { FRHIResourceCreateInfo CreateInfo; VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FVector), BUF_Static, CreateInfo); void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FVector), RLM_WriteOnly); FMemory::Memcpy(VertexBufferData, Vertices.GetData(), Vertices.Num() * sizeof(FVector)); RHIUnlockVertexBuffer(VertexBufferRHI); } }; class FMyIndexBuffer : public FIndexBuffer { public: TArray<int32> Indices; virtual void InitRHI() override { FRHIResourceCreateInfo CreateInfo; IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), Indices.Num() * sizeof(int32), BUF_Static, CreateInfo); void* Buffer = RHILockIndexBuffer(IndexBufferRHI, 0, Indices.Num() * sizeof(int32), RLM_WriteOnly); FMemory::Memcpy(Buffer, Indices.GetData(), Indices.Num() * sizeof(int32)); RHIUnlockIndexBuffer(IndexBufferRHI); } };
UMyMeshComponent::UMyMeshComponent() { static ConstructorHelpers::FObjectFinder<UMaterial> Material(TEXT("Material'/Engine/BasicShapes/BasicShapeMaterial'")); if (Material.Object != NULL) { TheMaterial = (UMaterial*)Material.Object; } Vertices.Add(FVector(10, 0, 0)); Vertices.Add(FVector(0, 10, 0)); Vertices.Add(FVector(0, 0, 10)); Indices.Add(0); Indices.Add(1); Indices.Add(2); }
#pragma once #include "Components/MeshComponent.h" #include "MyMeshComponent.generated.h" UCLASS(ClassGroup = Experimental, meta = (BlueprintSpawnableComponent)) class UE4COOKBOOK_API UMyMeshComponent : public UMeshComponent { GENERATED_BODY() public: virtual FPrimitiveSceneProxy* CreateSceneProxy() override; TArray<int32> Indices; TArray<FVector> Vertices; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Materials) UMaterial* TheMaterial; UMyMeshComponent(); }; #include "UE4Cookbook.h" #include "MyMeshComponent.h" #include <VertexFactory.h> #include "DynamicMeshBuilder.h" class FMyVertexBuffer : public FVertexBuffer { public: TArray<FVector> Vertices; virtual void InitRHI() override { FRHIResourceCreateInfo CreateInfo; VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FVector), BUF_Static, CreateInfo); void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FVector), RLM_WriteOnly); FMemory::Memcpy(VertexBufferData, Vertices.GetData(), Vertices.Num() * sizeof(FVector)); RHIUnlockVertexBuffer(VertexBufferRHI); } }; class FMyIndexBuffer : public FIndexBuffer { public: TArray<int32> Indices; virtual void InitRHI() override { FRHIResourceCreateInfo CreateInfo; IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), Indices.Num() * sizeof(int32), BUF_Static, CreateInfo); void* Buffer = RHILockIndexBuffer(IndexBufferRHI, 0, Indices.Num() * sizeof(int32), RLM_WriteOnly); FMemory::Memcpy(Buffer, Indices.GetData(), Indices.Num() * sizeof(int32)); RHIUnlockIndexBuffer(IndexBufferRHI); } }; class FMySceneProxy : public FPrimitiveSceneProxy { public: FMySceneProxy(UMyMeshComponent* Component) :FPrimitiveSceneProxy(Component), Indices(Component->Indices), TheMaterial(Component->TheMaterial) { VertexBuffer = FMyVertexBuffer(); IndexBuffer = FMyIndexBuffer(); for (FVector Vertex : Component->Vertices) { Vertices.Add(FDynamicMeshVertex(Component->GetComponentLocation() + Vertex)); } }; UPROPERTY() UMaterial* TheMaterial; virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override { FPrimitiveViewRelevance Result; Result.bDynamicRelevance = true; Result.bDrawRelevance = true; Result.bNormalTranslucencyRelevance = true; return Result; } virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FDynamicMeshBuilder MeshBuilder; if (Vertices.Num() == 0) { return; } MeshBuilder.AddVertices(Vertices); MeshBuilder.AddTriangles(Indices); MeshBuilder.GetMesh(FMatrix::Identity, new FColoredMaterialRenderProxy(TheMaterial->GetRenderProxy(false), FLinearColor::Gray), GetDepthPriorityGroup(Views[ViewIndex]), true, true, ViewIndex, Collector); } } void FMySceneProxy::OnActorPositionChanged() override { VertexBuffer.ReleaseResource(); IndexBuffer.ReleaseResource(); } uint32 FMySceneProxy::GetMemoryFootprint(void) const override { return sizeof(*this); } virtual ~FMySceneProxy() {}; private: TArray<FDynamicMeshVertex> Vertices; TArray<int32> Indices; FMyVertexBuffer VertexBuffer; FMyIndexBuffer IndexBuffer; }; FPrimitiveSceneProxy* UMyMeshComponent::CreateSceneProxy() { FPrimitiveSceneProxy* Proxy = NULL; Proxy = new FMySceneProxy(this); return Proxy; } UMyMeshComponent::UMyMeshComponent() { static ConstructorHelpers::FObjectFinder<UMaterial> Material(TEXT("Material'/Engine/BasicShapes/BasicShapeMaterial'")); if (Material.Object != NULL) { TheMaterial = (UMaterial*)Material.Object; } Vertices.Add(FVector(10, 0, 0)); Vertices.Add(FVector(0, 10, 0)); Vertices.Add(FVector(0, 0, 10)); Indices.Add(0); Indices.Add(1); Indices.Add(2); }
Actor
in the editor and add the new mesh component to it to see that your triangle is rendered. Experiment by changing the values added with Vertices. Add and see how the geometry changes after a recompile.Actor
to be rendered, the data describing it needs to be made accessible to the rendering thread.PrimitiveComponent
class defines a CreateSceneProxy
function that returns FPrimitiveSceneProxy*
. This function allows custom components like ours to return an object based on FPrimitiveSceneProxy
, leveraging polymorphism.SceneProxy
object to take in an instance of our component so that each SceneProxy
created knows about the component instance it is associated with.GetDynamicMeshElements
.IndexBuffer
and a VertexBuffer
. Each of the buffer classes we create are helpers that assist the Scene Proxy with allocating platform-specific memory for the two buffers. They do so in the InitRHI
(also known as Initialize Render Hardware Interface) function, wherein they use functions from the RHI API to create a vertex buffer, lock it, copy the required data, and then unlock it.ObjectFinder
template so that our mesh will have a material.3.129.21.166