class FMyDrawVertexBuffer : public FVertexBuffer
{
public:
/**
* 初始化RHI
*/
virtual void InitRHI()
{
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);
}
public:
TArray<FDynamicMeshVertex> Vertices; //頂點列表
};
/**
* 索引緩衝區
*/
class FMyDrawIndexBuffer : public FIndexBuffer
{
public:
/**
* 初始化RHI
*/
virtual void InitRHI()
{
FRHIResourceCreateInfo CreateInfo;
void* Buffer = nullptr;
IndexBufferRHI = RHICreateAndLockIndexBuffer(sizeof(int32), Indices.Num() * sizeof(int32), BUF_Static, CreateInfo, Buffer);
// 索引數據寫入索引緩衝區.
FMemory::Memcpy(Buffer, Indices.GetData(), Indices.Num() * sizeof(int32));
RHIUnlockIndexBuffer(IndexBufferRHI);
}
public:
TArray<int32> Indices; //索引列表
};
/** Vertex Factory */
class FMyDrawVertexFactory : public FLocalVertexFactory
{
public:
FMyDrawVertexFactory()
{}
/** Initialization */
void Init(const FMyDrawVertexBuffer* VertexBuffer)
{
if (IsInRenderingThread())
{
// Initialize the vertex factory's stream 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_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
InitArrowVertexFactory,
FArrowVertexFactory*, VertexFactory, this,
const FArrowVertexBuffer*, VertexBuffer, VertexBuffer,
{
// Initialize the vertex factory's stream 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);
VertexFactory->SetData(NewData);
});
}
}
};
class FMyDrawSceneProxy : public FPrimitiveSceneProxy
{
public:
FMyDrawSceneProxy(UMyDrawComponent* Component)
: FPrimitiveSceneProxy(Component)
, ArrowColor(Component->ArrowColor)
, ArrowSize(Component->ArrowSize)
, bIsScreenSizeScaled(Component->bIsScreenSizeScaled)
, ScreenSize(Component->ScreenSize)
{
bWillEverBeLit = false;
const float HeadAngle = FMath::DegreesToRadians(ARROW_HEAD_ANGLE);
const float TotalLength = ArrowSize * ARROW_SCALE;
const float HeadLength = TotalLength * ARROW_HEAD_FACTOR;
const float ShaftRadius = TotalLength * ARROW_RADIUS_FACTOR;
const float ShaftLength = (TotalLength - HeadLength) * 1.1f; // 10% overlap between shaft and head
const FVector ShaftCenter = FVector(0.5f * ShaftLength, 0, 0);
BuildConeVerts(HeadAngle, HeadAngle, -HeadLength, TotalLength, 32, VertexBuffer.Vertices, IndexBuffer.Indices);
BuildCylinderVerts(ShaftCenter, FVector(0,0,1), FVector(0,1,0), FVector(1,0,0), ShaftRadius, 0.5f * ShaftLength, 16, VertexBuffer.Vertices, IndexBuffer.Indices);
// Init vertex factory
VertexFactory.Init(&VertexBuffer);
// Enqueue initialization of render resource
BeginInitResource(&VertexBuffer);
BeginInitResource(&IndexBuffer);
BeginInitResource(&VertexFactory);
}
virtual ~FMyDrawSceneProxy()
{
VertexBuffer.ReleaseResource();
IndexBuffer.ReleaseResource();
VertexFactory.ReleaseResource();
}
// FPrimitiveSceneProxy interface.
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
QUICK_SCOPE_CYCLE_COUNTER( STAT_ArrowSceneProxy_DrawDynamicElements );
FMatrix EffectiveLocalToWorld;
{
EffectiveLocalToWorld = GetLocalToWorld();
}
auto MyDrawMaterialRenderProxy = new FColoredMaterialRenderProxy(
GEngine->MyDrawMaterial->GetRenderProxy(IsSelected(), IsHovered()),
MyDrawColor,
"GizmoColor"
);
Collector.RegisterOneFrameMaterialProxy(MyDrawMaterialRenderProxy);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (VisibilityMap & (1 << ViewIndex))
{
const FSceneView* View = Views[ViewIndex];
// Calculate the view-dependent scaling factor.
float ViewScale = 1.0f;
if (bIsScreenSizeScaled && (View->ViewMatrices.GetProjectionMatrix().M[3][3] != 1.0f))
{
const float ZoomFactor = FMath::Min<float>(View->ViewMatrices.GetProjectionMatrix().M[0][0], View->ViewMatrices.GetProjectionMatrix().M[1][1]);
if (ZoomFactor != 0.0f)
{
const float Radius = View->WorldToScreen(Origin).W * (ScreenSize / ZoomFactor);
if (Radius < 1.0f && Radius > 0)
{
ViewScale *= Radius;
}
}
}
// Draw the mesh.
FMeshBatch& Mesh = Collector.AllocateMesh();
FMeshBatchElement& BatchElement = Mesh.Elements[0];
BatchElement.IndexBuffer = &IndexBuffer;
Mesh.bWireframe = false;
Mesh.VertexFactory = &VertexFactory;
Mesh.MaterialRenderProxy = MyDrawMaterialRenderProxy;
BatchElement.PrimitiveUniformBuffer = CreatePrimitiveUniformBufferImmediate(FScaleMatrix(ViewScale) * EffectiveLocalToWorld, GetBounds(), GetLocalBounds(), true, UseEditorDepthTest());
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_World;
Mesh.bCanApplyViewModeOverrides = false;
Collector.AddMesh(ViewIndex, Mesh);
}
}
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = IsShown(View) && (View->Family->EngineShowFlags.BillboardSprites);
Result.bDynamicRelevance = true;
Result.bShadowRelevance = IsShadowCast(View);
Result.bEditorPrimitiveRelevance = UseEditorCompositing(View);
return Result;
}
virtual void OnTransformChanged() override
{
Origin = GetLocalToWorld().GetOrigin();
}
virtual uint32 GetMemoryFootprint( void ) const override { return( sizeof( *this ) + GetAllocatedSize() ); }
uint32 GetAllocatedSize( void ) const { return( FPrimitiveSceneProxy::GetAllocatedSize() ); }
private:
FMyDrawVertexBuffer VertexBuffer;
FMyDrawIndexBuffer IndexBuffer;
FMyDrawVertexFactory VertexFactory;
FVector Origin;
FColor MyDrawColor;
float MyDrawSize;
bool bIsScreenSizeScaled;
float ScreenSize;
};
UMyDrawComponent::UMyDrawComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// Structure to hold one-time initialization
struct FConstructorStatics
{
FName ID_Misc;
FText NAME_Misc;
FConstructorStatics()
: ID_Misc(TEXT("Misc"))
, NAME_Misc(NSLOCTEXT( "SpriteCategory", "Misc", "Misc" ))
{
}
};
static FConstructorStatics ConstructorStatics;
SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
ArrowColor = FColor(255, 0, 0, 255);
ArrowSize = 1.0f;
bHiddenInGame = true;
bUseEditorCompositing = true;
bGenerateOverlapEvents = false;
bIsScreenSizeScaled = false;
ScreenSize = DEFAULT_SCREEN_SIZE;
}
FPrimitiveSceneProxy* UMyDrawComponent::CreateSceneProxy()
{
return new FMyDrawSceneProxy(this);
}
FBoxSphereBounds UMyDrawComponent::CalcBounds(const FTransform& LocalToWorld) const
{
return FBoxSphereBounds(LocalToWorld.GetLocation(), FVector(NewScale, NewScale2, NewScale), FMath::Sqrt(3.0f * FMath::Square(NewScale)));
//FBoxSphereBounds可根據實際情況計算
}
void UMyDrawComponent::SetMyDrawColor_DEPRECATED(FColor NewColor)
{
MyDrawColor = NewColor;
MarkRenderStateDirty();
}
void UMyDrawComponent::SetMyDrawColor_New(FLinearColor NewColor)
{
MyDrawColor = NewColor.ToFColor(true);
MarkRenderStateDirty();
}
其中VertexBuffer、 IndexBuffer、VertexFactory緩衝區機制也可以不使用,不是必須的。通過SceneProxy方式你就可以用opengl或D3D的思想來做一些你想渲染的圖形。