地址: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"
是自定义的成员函数,代码如下:
头文件:(在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;
这个宏是为了让你定义的这个组件在细节面板中可以被看到
没加的话就看不到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宏,所以可以在这里修改
头文件:
// 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;
}