I am following an old tutorial about custom PrimitiveComponent and try to test it in UE 4.27.2. After modifying, it passes compilation, but when I drag my custom component to actor, UE crash and say:
null resource entry in uniform buffer parameters FLocalVertexFactoryUniformShaderParameters.Resources[0], ResourceType 0x6
I don’t know what is wrong?
Here is my code:
Source\TestModule\TestModule.Build.cs
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class TestModule : ModuleRules
{
public TestModule(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "Slate","SlateCore","InputCore", "HeadMountedDisplay" ,"RenderCore","RHI"});
PrivateDependencyModuleNames.AddRange(new string[] {});
}
}
Source\TestModule\Public\TestCustomComponent.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/PrimitiveComponent.h"
#include "TestCustomComponent.generated.h"
/**
*
*/
UCLASS(ClassGroup=(CustomGroup), meta=(BlueprintSpawnableComponent))
class TESTMODULE_API UTestCustomComponent : public UPrimitiveComponent
{
GENERATED_BODY()
public:
virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
};
Source\TestModule\Private\TestCustomComponent.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TestCustomComponent.h"
#include "DynamicMeshBuilder.h"
/*Vertex Buffer*/
class FTestCustomComponentVertexBuffer : public FVertexBuffer
{
public:
TArray<FDynamicMeshVertex> Vertices;
virtual void InitRHI() override
{
FRHIResourceCreateInfo CreateInfo;
void* VertexBufferData = nullptr;
VertexBufferRHI = RHICreateAndLockVertexBuffer(Vertices.Num() * sizeof(FDynamicMeshVertex), BUF_Static, CreateInfo, VertexBufferData);
FMemory::Memcpy(VertexBufferData, Vertices.GetData(), Vertices.Num() * sizeof(FDynamicMeshVertex));
RHIUnlockVertexBuffer(VertexBufferRHI);
}
};
/*Index Buffer*/
class FTestCustomComponentIndexBuffer : 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);
}
};
/*Vertex Factory*/
class FTestCustomComponentVertexFactory : public FLocalVertexFactory
{
public:
FTestCustomComponentVertexFactory() : FLocalVertexFactory(ERHIFeatureLevel::SM5, "FTestCustomComponentVertexFactory") { }
/*Initialization*/
void Init(const FTestCustomComponentVertexBuffer* VertexBuffer)
{
if (IsInRenderingThread())
{
//Initialize the vertex factory'sstream components
FDataType NewData;
NewData.PositionComponent = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer, FDynamicMeshVertex, Position, VET_Float3);
NewData.TextureCoordinates.Add(FVertexStreamComponent(VertexBuffer, STRUCT_OFFSET(FDynamicMeshVertex, TextureCoordinate), sizeof(FDynamicMeshVertex), VET_Float2));
NewData.TangentBasisComponents[0] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer,
FDynamicMeshVertex, TangentX, VET_PackedNormal);
NewData.TangentBasisComponents[1] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer,
FDynamicMeshVertex, TangentZ, VET_PackedNormal);
SetData(NewData);
}
else
{
ENQUEUE_RENDER_COMMAND(InitTestCustomComponentVertexFactor)([VertexBuffer, this](FRHICommandListImmediate& RHICmdList) {
//Initialize the vertex factory's stream componnents,
FDataType NewData;
NewData.PositionComponent = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(
VertexBuffer, FDynamicMeshVertex, Position, VET_Float3);
NewData.TextureCoordinates.Add(FVertexStreamComponent(VertexBuffer, STRUCT_OFFSET(FDynamicMeshVertex, TextureCoordinate), sizeof(FDynamicMeshVertex), VET_Float2));
NewData.TangentBasisComponents[0] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer,
FDynamicMeshVertex, TangentX, VET_PackedNormal);
NewData.TangentBasisComponents[1] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer,
FDynamicMeshVertex, TangentZ, VET_PackedNormal);
SetData(NewData);
});
}
}
};
class FTestCustomComponentSceneProxy : public FPrimitiveSceneProxy
{
public:
FTestCustomComponentIndexBuffer IndexBuffer;
FTestCustomComponentVertexBuffer VertexBuffer;
FTestCustomComponentVertexFactory VertexFactory;
public:
FTestCustomComponentSceneProxy(UPrimitiveComponent* Component)
:FPrimitiveSceneProxy(Component)
{
const float BoxSize = 100.0f;
// fill vertex
VertexBuffer.Vertices.Add(FVector(0.0F));
VertexBuffer.Vertices.Add(FVector(BoxSize, 0.0f, 0.0f));
VertexBuffer.Vertices.Add(FVector(0.0f, BoxSize, 0.0f));
VertexBuffer.Vertices.Add(FVector(0.0f, 0.0f, BoxSize));
// fill index
IndexBuffer.Indices.Add(0);
IndexBuffer.Indices.Add(1);
IndexBuffer.Indices.Add(2);
IndexBuffer.Indices.Add(0);
IndexBuffer.Indices.Add(2);
IndexBuffer.Indices.Add(3);
IndexBuffer.Indices.Add(0);
IndexBuffer.Indices.Add(3);
IndexBuffer.Indices.Add(1);
IndexBuffer.Indices.Add(3);
IndexBuffer.Indices.Add(2);
IndexBuffer.Indices.Add(1);
// init
VertexFactory.Init(&VertexBuffer);
BeginInitResource(&IndexBuffer);
BeginInitResource(&VertexBuffer);
BeginInitResource(&VertexFactory);
}
virtual ~FTestCustomComponentSceneProxy()
{
VertexBuffer.ReleaseResource();
IndexBuffer.ReleaseResource();
VertexFactory.ReleaseResource();
}
SIZE_T GetTypeHash() const override
{
static size_t UniquePointer;
return reinterpret_cast<size_t>(&UniquePointer);
}
virtual uint32 GetMemoryFootprint(void) const override
{
return (sizeof(*this) + GetAllocatedSize());
}
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>&
Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, class FMeshElementCollector& Collector) const override
{
FBox TestDynamicBox = FBox(FVector(-100.0f), FVector(100.0f));
DrawWireBox(
Collector.GetPDI(0),
GetLocalToWorld(),
TestDynamicBox,
FLinearColor::Red,
ESceneDepthPriorityGroup::SDPG_Foreground,
10.0f
);
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View)const override
{
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = true;
Result.bStaticRelevance = true;
Result.bDynamicRelevance = true;
return Result;
}
virtual void DrawStaticElements(FStaticPrimitiveDrawInterface* PDI)override
{
FMeshBatch Mesh;
FMeshBatchElement& BatchElement = Mesh.Elements[0];
BatchElement.IndexBuffer = &IndexBuffer;
Mesh.bWireframe = false;
Mesh.VertexFactory = &VertexFactory;
Mesh.MaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)
->GetRenderProxy();
FBoxSphereBounds PreSkinnedLocalBounds;
GetPreSkinnedLocalBounds(PreSkinnedLocalBounds);
BatchElement.PrimitiveUniformBuffer = CreatePrimitiveUniformBufferImmediate(GetLocalToWorld(), GetBounds()
, GetLocalBounds(), PreSkinnedLocalBounds, ReceivesDecals(), DrawsVelocity(), LpvBiasMultiplier);
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
BatchElement.MinVertexIndex = 0;
BatchElement.MaxVertexIndex = VertexBuffer.Vertices.Num() - 1;
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
Mesh.Type = PT_TriangleList;
Mesh.DepthPriorityGroup = SDPG_Foreground;
Mesh.bCanApplyViewModeOverrides = false;
Mesh.bDisableBackfaceCulling = false;
PDI->DrawMesh(Mesh, 1.0f);
}
};
FPrimitiveSceneProxy* UTestCustomComponent::CreateSceneProxy()
{
return new FTestCustomComponentSceneProxy(this);
}