UE4学习笔记:访问StaticMesh的网格顶点并画点

学习来源:UE4官方论坛:

地址:https://forums.unrealengine.com/development-discussion/c-gameplay-programming/7509-accessing-vertex-positions-of-static-mesh#post1824245

这里面第一页Rama大神的代码在现在的引擎中已经不再适用,我也在这个问题底下发了新的代码,32楼就是本人。而且之前的代码中并没有具体写出画点的步骤,对U++(UE4魔改C++,笑)不熟悉的朋友可能这一点也需要了解。

注意事项

需要添加以下头文件:

#include "Components/StaticMeshComponent.h"
#include "Rendering/PositionVertexBuffer.h"
#include "Engine/StaticMesh.h"
#include "StaticMeshResources.h"
#include "DrawDebugHelpers.h"

访问StaticMesh顶点:

是自定义的成员函数,代码如下:
头文件:(在GENERATE BODY( )的下面加入代码)

UFUNCTION(BlueprintPure, Category = "Corridor", meta = (Keywords = "corridor vertex mesh meshdata", NativeBreakFunc))

TArray<FVector> MeshData(const UStaticMeshComponent* StaticMeshComponent);

这里大神还扩展了蓝图类,这样在蓝图中也可以用这个函数。

CPP:

TArray<FVector> AGetStaticMeshVertexes::MeshData(const UStaticMeshComponent* StaticMeshComponent)
{
     
	TArray<FVector> vertices = TArray<FVector>();
	
	//~~~~~~~~~~~~~~~~~~~~
	// Many thanks to Rama for this solution! :)
	//
	// Vertex Buffer
	if (!IsValidLowLevel()) return vertices;
	if (!StaticMeshComponent) return vertices;
	if (!StaticMeshComponent->GetStaticMesh()) return vertices;
	if (!StaticMeshComponent->GetStaticMesh()->RenderData) return vertices;
	if (StaticMeshComponent->GetStaticMesh()->RenderData->LODResources.Num() > 0)
	{
     
		FPositionVertexBuffer* VertexBuffer = &StaticMeshComponent->GetStaticMesh()->RenderData->LODResources[0].VertexBuffers.PositionVertexBuffer;
		if (VertexBuffer)
		{
     
			const int32 VertexCount = VertexBuffer->GetNumVertices();
			for (int32 Index = 0; Index < VertexCount; Index++)
			{
     
				//This is in the Static Mesh Actor Class, so it is location and tranform of the SMActor
				const FVector WorldSpaceVertexLocation = GetActorLocation() + GetTransform().TransformVector(VertexBuffer->VertexPosition(Index));
				//add to output FVector array
				vertices.Add(WorldSpaceVertexLocation);
			}
		}
	}

	return vertices;
}

其实也就是运用FPositionVertexBuffer这个类,没有什么特别巧妙的算法

描点

声明:我创建的C++父类是Actor

首先在头文件中声明UPROPERTY宏和StaticMeshComponent组件

UPROPERTY(VisibleAnywhere)

UStaticMeshComponent *explode_actor;

这个宏是为了让你定义的这个组件在细节面板中可以被看到
UE4学习笔记:访问StaticMesh的网格顶点并画点_第1张图片
没加的话就看不到explode_actor的这些信息了

然后在CPP中,初始化函数中加入:

explode_actor = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));

explode_actor->SetupAttachment(RootComponent);

这是把explode_actor这个StaticMeshComponent附着到RootComponent(根组件)上

Tick函数中的代码就很简单了

TArray<FVector> Points = MeshData(explode_actor);
	for (int i = 0; i < Points.Num() ; i++)
	{
     
		DrawDebugPoint(GetWorld(), Points[i], 20, FColor::Orange);
	}

第一句就是得到explode_actor的顶点数据,数组里存放的是顶点的坐标

for循环里面遍历数组并画点,画点用到了DrawDebugPoint这个函数,参数也是一目了然,第三个20是点的尺寸,第四个是颜色,如果这个函数用不了,那是没加 #include "DrawDebugHelpers.h"这个头文件。

运行结果

我是随便给了一个模型,因为之前声明了UPROPERTY宏,所以可以在这里修改
UE4学习笔记:访问StaticMesh的网格顶点并画点_第2张图片

UE4学习笔记:访问StaticMesh的网格顶点并画点_第3张图片
UE4学习笔记:访问StaticMesh的网格顶点并画点_第4张图片
UE4学习笔记:访问StaticMesh的网格顶点并画点_第5张图片

完整代码

头文件:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "GetStaticMeshVertexes.generated.h"

UCLASS()
class EXPERIMENT_2_24_VER_API AGetStaticMeshVertexes : public AActor
{
     
	GENERATED_BODY()

		UFUNCTION(BlueprintPure, Category = "Corridor", meta = (Keywords = "corridor vertex mesh meshdata", NativeBreakFunc))
		TArray<FVector> MeshData(const UStaticMeshComponent* StaticMeshComponent);
	
public:	
	// Sets default values for this actor's properties
	AGetStaticMeshVertexes();
	
	UPROPERTY(VisibleAnywhere)

	UStaticMeshComponent *explode_actor;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

};

CPP:

// Fill out your copyright notice in the Description page of Project Settings.


#include "GetStaticMeshVertexes.h"
#include "Components/StaticMeshComponent.h"
#include "Rendering/PositionVertexBuffer.h"
#include "Engine/StaticMesh.h"
#include "StaticMeshResources.h"
#include "DrawDebugHelpers.h"




// Sets default values
AGetStaticMeshVertexes::AGetStaticMeshVertexes()
{
     
 	// 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;
	explode_actor = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	explode_actor->SetupAttachment(RootComponent);

}

void AGetStaticMeshVertexes::BeginPlay()
{
     
	Super::BeginPlay();
}

void AGetStaticMeshVertexes::Tick(float DeltaTime)
{
     
	Super::Tick(DeltaTime);
	TArray<FVector> Points = MeshData(explode_actor);
	for (int i = 0; i < Points.Num() ; i++)
	{
     
		DrawDebugPoint(GetWorld(), Points[i], 20, FColor::Orange);
	}
	
}

// Called when the game starts or when spawned
TArray<FVector> AGetStaticMeshVertexes::MeshData(const UStaticMeshComponent* StaticMeshComponent)
{
     
	TArray<FVector> vertices = TArray<FVector>();
	
	//~~~~~~~~~~~~~~~~~~~~
	// Many thanks to Rama for this solution! :)
	//
	// Vertex Buffer
	if (!IsValidLowLevel()) return vertices;
	if (!StaticMeshComponent) return vertices;
	if (!StaticMeshComponent->GetStaticMesh()) return vertices;
	if (!StaticMeshComponent->GetStaticMesh()->RenderData) return vertices;
	if (StaticMeshComponent->GetStaticMesh()->RenderData->LODResources.Num() > 0)
	{
     
		FPositionVertexBuffer* VertexBuffer = &StaticMeshComponent->GetStaticMesh()->RenderData->LODResources[0].VertexBuffers.PositionVertexBuffer;
		if (VertexBuffer)
		{
     
			const int32 VertexCount = VertexBuffer->GetNumVertices();
			for (int32 Index = 0; Index < VertexCount; Index++)
			{
     
				//This is in the Static Mesh Actor Class, so it is location and tranform of the SMActor
				const FVector WorldSpaceVertexLocation = GetActorLocation() + GetTransform().TransformVector(VertexBuffer->VertexPosition(Index));
				//add to output FVector array
				vertices.Add(WorldSpaceVertexLocation);
			}
		}
	}

	return vertices;
}



你可能感兴趣的:(笔记)