地址: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; }