以SceneProxy的方式由邏輯線程向渲染線程傳遞數據來自定義渲染組件

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的思想來做一些你想渲染的圖形。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章