UE4绘制自定义模型

      UE4中的UProceduralMeshComponent组件可以用于绘制自定义形状的模型,该组件位于ProceduralMeshComponent插件中,如果要引用该插件中的功能就要在XXXX.build.cs文件中加入对ProceduralMeshComponent模块的引用,如下所示

PublicDependencyModuleNames.AddRange(new string[] {......, "ProceduralMeshComponent"});

      UProceduralMeshComponent类中CreateMeshSection_LinearColor函数用于根据顶点数组等数据创建模型,UpdateMeshSection_LinearColor用于更新模型,ClearMeshSection和ClearAllMeshSections用于删除模型。
      本文用CreateMeshSection_LinearColor演示如何创建三角形面片,四边形面片以及如何根据一组折线上的点画出指定宽度的折线面片。
      首先创建一个CustomMeshActor.h和CustomMeshActor.cpp文件,定义一个ACustomMeshActor类,类中有一个UProceduralMeshComponent组件成员变量,代码如下

//CustomMeshActor.h
#pragma once
#include"CoreMinimal.h"
#include"GameFramework/Actor.h"
#include"ProceduralMeshComponent.h"
#include"CustomMeshActor.generated.h"
UCLASS()
class THIRDPERSON_API ACustomMeshActor :public AActor
{
    GENERATED_UCLASS_BODY()
public:
    ACustomMeshActor();
    UPROPERTY()
        UProceduralMeshComponent * DrawMeshComponent;
};

//CustomMeshActor.cpp
#include "CustomMeshActor.h"
ACustomMeshActor::ACustomMeshActor(const FObjectInitializer& ObjectInitializer)
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    RootComponent = CreateDefaultSubobject<USceneComponent>("Root");
    DrawMeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("DrawMeshComponent"));
    DrawMeshComponent->SetupAttachment(RootComponent);
}

1、三角形面片
      定义DrawTriangle函数用于绘制三角形面片

void ACustomMeshActor::DrawTriangle(UMaterialInterface * InMaterial)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    vertices.Add(FVector(0,0,0));
    vertices.Add(FVector(0,100,0));
    vertices.Add(FVector(100,0,0));
    triangles.Add(0);
    triangles.Add(1);
    triangles.Add(2);

    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

上面的代码画一个由(0,0,0),(0,100,0)和(100,0,0)三个顶点围成的三角形,该三角形的材质以函数的参数传入。
       画一个三角形需要三个顶点和三个顶点索引,也就是上面的vertices和triangles,其中triangles是顶点的索引,必须是3的倍数,3个索引表示一个三角形,而且三个索引对应的顶点必须逆时针排列才能看到该三角面(逆时针是根据视角决定的,如从上往下看和从下往上看的逆时针方向不一样。此外如果需要从两个相反的方向都能看到三角面,可以将材质设置为双面材质)。
      在编辑中创建一个基类为ACustomMeshActor的蓝图类BPCustomMeshActor,在ConstructionScript函数中使用DrawTriangle函数就可以看到绘制出来的三角形面片,材质使用了纯红色的自定义材质RedMaterial,如下图所示


2、四边形片面
      定义DrawPlane函数用于绘制四边形面片,四边形可以由两个三角形组成,因此有6个索引,而顶点只有4个

void ACustomMeshActor::DrawPlane(UMaterialInterface * InMaterial)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    vertices.Add(FVector(0, 0, 0));
    vertices.Add(FVector(0, 100, 0));
    vertices.Add(FVector(100, 100, 0));
    vertices.Add(FVector(100, 0, 0));
    triangles.Add(0);
    triangles.Add(1);
    triangles.Add(2);
    triangles.Add(2);
    triangles.Add(3);
    triangles.Add(0);

    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

      效果如下图

3、折线面片
      定义DrawLine用于绘制折线面片,InPoints是折线上的点,InWidth是线的宽度,为了简单起见这里只画XY平面的折线,即要确保InPoints中点的Z轴值为0或全都相等,

void ACustomMeshActor::DrawLine(UMaterialInterface * InMaterial,const TArray<FVector>& InPoints, float InWidth)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    int32 num = InPoints.Num();
    if (num < 2)
    {
        return;
    }
    for (int32 i = 0; i < num; i++)
    {
        FVector right(0, -1, 0);
        if (i == num - 1)
        {
            right = FVector::CrossProduct(InPoints[i] - InPoints[i-1], FVector(0, 0, 1));
        }
        else
        {
            right = FVector::CrossProduct(InPoints[i+1] - InPoints[i], FVector(0, 0, 1));
        }
        right.Normalize();
        right = -right;//右手座标系转左手座标系
        FVector pt1 = InPoints[i] - right * InWidth / 2;
        FVector pt2 = InPoints[i] + right * InWidth / 2;
        vertices.Add(pt1);
        vertices.Add(pt2);
        int vNum = vertices.Num();
        if (i > 0)
        {
            triangles.Add(vNum - 4);
            triangles.Add(vNum - 3);
            triangles.Add(vNum - 2);
            triangles.Add(vNum - 2);
            triangles.Add(vNum - 3);
            triangles.Add(vNum - 1);
        }
    }
    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

      right是该线段的右向单位向量,也就是在XY二维平面上与该线段垂直的向量,加减right向量就是将点左右平移,triangles添加索引与vertices加入顶点的顺序有关。绘制出来后如下图所示

      上面几个例子中normals;tangents;vertexColors;uvs;这些变量都没有添加值,具体使用时可根据需要添加,尤其是uvs一般都会有值的。
附上完整的代码:
CustomMeshActor.h

#pragma once
#include"CoreMinimal.h"
#include"GameFramework/Actor.h"
#include"ProceduralMeshComponent.h"
#include"CustomMeshActor.generated.h"
UCLASS()
class THIRDPERSON_API ACustomMeshActor :public AActor
{
    GENERATED_UCLASS_BODY()
public:
    ACustomMeshActor();
    UFUNCTION(BlueprintCallable, Category = "CustomMeshActor")
    void DrawTriangle(UMaterialInterface* InMaterial);
    UFUNCTION(BlueprintCallable, Category = "CustomMeshActor")
    void DrawPlane(UMaterialInterface* InMaterial);
    UFUNCTION(BlueprintCallable, Category = "CustomMeshActor")
    void DrawLine(UMaterialInterface * InMaterial, const TArray<FVector>& InPoints, float InWidth);

    UPROPERTY()
        UProceduralMeshComponent * DrawMeshComponent;
};

CustomMeshActor.cpp

#include "CustomMeshActor.h"
ACustomMeshActor::ACustomMeshActor(const FObjectInitializer& ObjectInitializer)
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    RootComponent = CreateDefaultSubobject<USceneComponent>("Root");
    DrawMeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("DrawMeshComponent"));
    DrawMeshComponent->SetupAttachment(RootComponent);
}

void ACustomMeshActor::DrawTriangle(UMaterialInterface * InMaterial)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    vertices.Add(FVector(0,0,0));
    vertices.Add(FVector(0,100,0));
    vertices.Add(FVector(100,0,0));
    triangles.Add(0);
    triangles.Add(1);
    triangles.Add(2);

    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

void ACustomMeshActor::DrawPlane(UMaterialInterface * InMaterial)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    vertices.Add(FVector(0, 0, 0));
    vertices.Add(FVector(0, 100, 0));
    vertices.Add(FVector(100, 100, 0));
    vertices.Add(FVector(100, 0, 0));
    triangles.Add(0);
    triangles.Add(1);
    triangles.Add(2);
    triangles.Add(2);
    triangles.Add(3);
    triangles.Add(0);

    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

void ACustomMeshActor::DrawLine(UMaterialInterface * InMaterial,const TArray<FVector>& InPoints, float InWidth)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    int32 num = InPoints.Num();
    if (num < 2)
    {
        return;
    }
    for (int32 i = 0; i < num; i++)
    {
        FVector right(0, -1, 0);
        if (i == num - 1)
        {
            right = FVector::CrossProduct(InPoints[i] - InPoints[i-1], FVector(0, 0, 1));
        }
        else
        {
            right = FVector::CrossProduct(InPoints[i+1] - InPoints[i], FVector(0, 0, 1));
        }
        right.Normalize();
        right = -right;//右手座标系转左手座标系
        FVector pt1 = InPoints[i] - right * InWidth / 2;
        FVector pt2 = InPoints[i] + right * InWidth / 2;
        vertices.Add(pt1);
        vertices.Add(pt2);
        int vNum = vertices.Num();
        if (i > 0)
        {
            triangles.Add(vNum - 4);
            triangles.Add(vNum - 3);
            triangles.Add(vNum - 2);
            triangles.Add(vNum - 2);
            triangles.Add(vNum - 3);
            triangles.Add(vNum - 1);
        }
    }
    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章