当前位置:网站首页>Mesh merging under ue4/ue5 runtime
Mesh merging under ue4/ue5 runtime
2022-07-07 15:43:00 【WhiteTian】
Original article , Reprint please indicate the source .
StaticMesh The merger of
Preface
Engine version :4.27.2
The premise of the merger : stay UE4.26.2 after , Runtime components are allowed UStaticmesh. The previous method only supports importing under the editor ,
After importing, it will be converted to UStaticMesh Of RenderData Rendering ;
Why merge :
The source of demand is software. We want to use UE4.27.2 Of runtime Under the udatasmith Import function ,
But because datasmith The original intention of our design is to split the model as small as possible , The particle size is very small .
So there are udatasmith After importing, there will be tens of thousands of... In the level , Very low frame rate .
That's why there is this article Merge at run time StaticMesh.
The optimization scheme of the combined batch is as follows
In fact, there are many that can be approved together .
therefore , Here I will focus on udatasmith Import this function , Studied the scheme of joint approval ;
programme | advantage | shortcoming |
---|---|---|
1> modify datasmith Import the code of some plug-ins | Highest efficiency | Difficult to maintain |
2> Make your own set | The efficiency is lower than the scheme 1, Easy maintenance | Easy maintenance |
3> modify datasmith Export plug-ins | Not sure | Difficult to maintain |
Which scheme to use ?
programme 1 In a word , Change DataSmith Source code , I think efficiency is the best .
Why? :
Let's start with the plan 1 How to do it : One by one actor Not yet spawn,mesh Not yet build,collsion, There is no such information as material build Before , Let's filter in advance Mesh Can be merged , After that spawn MeshActor,build StaticMesh Of vertex,collision,material.
Let's talk about the plan 2 How to do it : be-all actor Already in the world spawn Come out ,StaticMesh Of vertex,collision,material This information has been build Okay . Then filter what is good Mesh Can be merged , After that spawn MeshActor,build StaticMesh Of vertex,collision,material.
The scheme is implemented temporarily 2
In contrast , programme 1 It's better than the plan 2 Efficient . But the plan 1 It's more troublesome to change , And I don't think it's easy to maintain . After watching it all day , I realized the scheme first 2.
Video effects :Merge Then the frame rate and DC Significantly improve Jump Watch
UE4/UE5 Runtime Lower merger Mesh
Class diagram
Editor With the implementation of the
Reference resources MergeActor Tool
Use the... Under the editor MergeActorTool The function of the tool , The merging logic will soon be implemented in the editor .Standlone You can also merge .
But it should be noted that this can only be used under the editor , Take a break when you pack .
The specific code of merging under the editor is as follows , As a reference :
// Merge method under editor
void UMyBlueprintFunctionLibrary::MergeMy(const TArray<UPrimitiveComponent*>& ComponentsToMerge, UWorld* World,
const FMeshMergingSettings& InSettings, UMaterialInterface* InBaseMaterial,
UPackage* InOuter, const FString& InBasePackageName,
TArray<UObject*>& OutAssetsToSync, FVector& OutMergedActorLocation,
const float ScreenSize, bool bSilent /*= false*/, FString AppendName)
{
const IMeshMergeUtilities& MeshUtilities = FModuleManager::Get().
LoadModuleChecked<IMeshMergeModule>("MeshMergeUtilities").GetUtilities();
MeshUtilities.MergeComponentsToStaticMeshWithName(ComponentsToMerge, GWorld, InSettings, InBaseMaterial, InOuter, InBasePackageName,
OutAssetsToSync, OutMergedActorLocation, ScreenSize, bSilent, AppendName);
}
// Merge specific logic , Put the same material Mesh Pass it in to complete the merger .
TArray<UObject*> OutAssetsToSync;
FVector OutMergedActorLocation;
const float ScreenAreaSize = TNumericLimits<float>::Max();
FMeshMergingSettings setting;
setting.bMergePhysicsData = 1;
MergeMy(mergedata.Value, GWorld,
setting, nullptr, GetTransientPackage(), FString(),
OutAssetsToSync, OutMergedActorLocation,
ScreenAreaSize, true, mergedata.Key);
UStaticMesh* UtilitiesMergedMesh = nullptr;
if (!OutAssetsToSync.FindItemByClass(&UtilitiesMergedMesh))
{
// Error, TEXT("MergeStaticMeshActors failed. No mesh was created.
continue;
}
for (auto obj : OutAssetsToSync)
{
auto umesh = Cast<UStaticMesh>(obj);
if (!umesh)
continue;
/*auto mat0 = umesh->GetMaterial(0); if (!UKismetSystemLibrary::IsValid(mat0)) continue;*/
OutMergedActorLocation+=FVector(0,0,500);
auto MergedActor = GWorld->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass(), OutMergedActorLocation, FRotator(0, 0, 0));
if (MergedActor)
{
MergedActor->SetMobility(EComponentMobility::Movable);
if (!MergedActor->GetStaticMeshComponent())
continue;
MergedActor->GetStaticMeshComponent()->SetStaticMesh(umesh);
if (mergedata.Value.Num() > 0)
{
UStaticMeshComponent* pSTM = Cast<UStaticMeshComponent>(mergedata.Value[0]);
if (pSTM)
{
//umesh->SetStaticMaterials(pSTM->GetStaticMesh()->GetStaticMaterials());
}
}
GWorld->UpdateCullDistanceVolumes(MergedActor, MergedActor->GetStaticMeshComponent());
MergedActor->AttachToActor(RootActor, FAttachmentTransformRules::KeepWorldTransform);
#if WITH_EDITOR
MergedActor->SetActorLabel(UKismetSystemLibrary::GetDisplayName(umesh));
#endif // endif
}
// Delete the replaced RootActor
for (auto willremovecomp : mergedata.Value)
{
if(!IsValid(DeleteActorArray[willremovecomp]))
continue;
if(!DeleteActorArray[willremovecomp]->IsValidLowLevel())
continue;
TArray<UActorComponent*> OutComponent;
OutComponent = DeleteActorArray[willremovecomp]->K2_GetComponentsByClass(UStaticMeshComponent::StaticClass());
if (OutComponent.Num() < 2)
{
GWorld->DestroyActor(DeleteActorArray[willremovecomp]);
}
else
{
willremovecomp->DestroyComponent();
}
}
}
Runtime With the implementation of the
difficulty 1,StaticMesh Of RenderData turn FMeshDescription
In fact, if you've seen this StaticMesh You should know , The code merged under the editor is used under the editor StaticMesh Unique data to merge , This is the picture below . The variables used are SourceModels
also , Under the editor StaticMesh The build will eventually call Build Method , But these are not available at runtime .
We need to use... In the new version of the engine BuildFromStaticMeshDescriptions To generate UStaticMesh.
BuildFromStaticMeshDescriptions What this method needs is FMeshDescription,FMeshDescription After importing in the editor, there will be , But at runtime UStaticMesh Of SourceModels Does not exist. , What do I do ?
We need to push back , The final rendered data exists UStaticMesh Of RenderData in , So we start from RenderData It turns the data into FMeshDescription Just array .
In turn, merge each one that can be merged Mesh The data from RenderData convert to FMeshDescription, Then put these
FMeshDescription Add once , Give it to UStaticMesh Of BuildFromStaticMeshDescriptions Pass it in and it's done ( Note the size of the data here ,UE Serialization of cannot exceed 2G, But fortunately, we wrote this by ourselves , And then splicing FMeshDescription Let's just control the memory when we need it , This is also related to the speed of merger )
To sum up, the specific steps are :
1>RenderData turn FMeshDescription
2> Splice all FMeshDescription
3> call BuildFromStaticMeshDescriptions
difficulty 2,StaticMesh Build complex collisions
To build complex collisions , So call
UBodySetup->CreatePhysicsMeshes(), If you follow carefully , When you go in, you'll find , stay Runtime Next ,build Collision will call ProcessFormatData_PhysX perhaps ProcessFormatData_Chaos, But the preconditions must be met IsRuntime The judgment of the .
The reason I found this is , After the merger , I'm creating UStaticMesh Objects are written in a common way ,NewObect(xxxxxxx), It turns out that IsRuntime My judgment there has always been false.
if (IsRuntime(this))
{
#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX
bClearMeshes = !RuntimeCookPhysics_PhysX();
#elif WITH_CHAOS
bClearMeshes = !RuntimeCookPhysics_Chaos();
#endif
}
void UBodySetup::CreatePhysicsMeshes()
{
TRACE_CPUPROFILER_EVENT_SCOPE(UBodySetup::CreatePhysicsMeshes);
SCOPE_CYCLE_COUNTER(STAT_CreatePhysicsMeshes);
// Create meshes from cooked data if not already done
if(bCreatedPhysicsMeshes)
{
return;
}
// If we don't have any convex/trimesh data we can skip this whole function
if (bNeverNeedsCookedCollisionData)
{
return;
}
bool bClearMeshes = true;
// Find or create cooked physics data
static FName PhysicsFormatName(FPlatformProperties::GetPhysicsFormat());
FByteBulkData* FormatData = GetCookedData(PhysicsFormatName);
// On dedicated servers we may be cooking generic data and sharing it
if (FormatData == nullptr && IsRunningDedicatedServer())
{
FormatData = GetCookedData(FGenericPlatformProperties::GetPhysicsFormat());
}
if (FormatData)
{
#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX
bClearMeshes = !ProcessFormatData_PhysX(FormatData);
#elif WITH_CHAOS
bClearMeshes = !ProcessFormatData_Chaos(FormatData);
#endif
}
else
{
if (IsRuntime(this))// This place is in Runtime Next, if you use UStaticMesh Words , It's impossible to pass .
{
#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX
bClearMeshes = !RuntimeCookPhysics_PhysX();
#elif WITH_CHAOS
bClearMeshes = !RuntimeCookPhysics_Chaos();
#endif
}
}
// fix up invalid transform to use identity
// this can be here because BodySetup isn't blueprintable
if ( GetLinkerUE4Version() < VER_UE4_FIXUP_BODYSETUP_INVALID_CONVEX_TRANSFORM )
{
for (int32 i=0; i<AggGeom.ConvexElems.Num(); ++i)
{
if ( AggGeom.ConvexElems[i].GetTransform().IsValid() == false )
{
AggGeom.ConvexElems[i].SetTransform(FTransform::Identity);
}
}
}
#if WITH_CHAOS
// For drawing of convex elements we require an index buffer, previously we could
// get this from a PxConvexMesh but Chaos doesn't maintain that data. Instead now
// it is a part of the element rather than the physics geometry, if we load in an
// element without that data present, generate a convex hull from the convex vert
// data and extract the index data from there.
for(FKConvexElem& Convex : AggGeom.ConvexElems)
{
Convex.ComputeChaosConvexIndices();
}
#endif
if(bClearMeshes)
{
ClearPhysicsMeshes();
}
bCreatedPhysicsMeshes = true;
}
After looking at the code , Follow code , I found a solution .
1> First of all, you need to UStaticMesh Derive a class ;
2> And this kind of bAllowCPUAccess It has to be for true;
3> And reload GetWorld();
Then I'm adding one SetWorld Method ;
The specific code of this class is as follows :
/* * from UStaticMesh Derived classes , Allow collision meshes to be cooked at runtime * Do that ,bAllowCPUAccess It has to be for true, And the way GetWorld() A valid... Must be returned world * Otherwise, in the Cook There was a IsRuntime() My judgment is always false */
UCLASS()
class EASYKITRUNTIMEMERGEMESH_API UEKRMM_RuntimeMesh : public UStaticMesh
{
GENERATED_BODY()
public:
UEKRMM_RuntimeMesh()
: World(nullptr)
{
// Set up bAllowCPUAccess by true, Allows you to copy rendered data triangles into the collision mesh
bAllowCPUAccess = true;
}
// UObject Cover
// Overrides allow cooking to collide with the mesh , Simple and complex , From the static grid at run time
virtual UWorld* GetWorld() const override {
return World ? World : UStaticMesh::GetWorld(); }
// end UObject Cover
// Use an effective world , Allow collision meshes , Simple and complex , From the static grid at run time
void SetWorld(UWorld* InWorld) {
World = InWorld; }
private:
UWorld* World;
};
It's easy to use , as follows , And then I'll call UBodySetup->CreatePhysicsMeshes() Just OK 了 :
UEKRMM_RuntimeMesh* StaticMesh = NewObject< UEKRMM_RuntimeMesh >(GetTransientPackage(), MeshName, RF_Public | RF_Standalone);
if(!StaticMesh)
continue;
StaticMesh->InitResources();
// The world must be set
StaticMesh->SetWorld(RootActor->GetWorld());
difficulty 3,StaticMesh texture of material
Plug in encapsulation , Introduction to existing functions and subsequent plans
Features currently supported :
1> All of the same material mesh Merge together : Pass in a AActor Object as RootActor, To be able to RootActor All materials under the are the same UStaticmeshComponent Merge into a single UStaticMesh;
2> The material is correct
3> Ensure there are complex collisions
4> The coordinates are correct
5> To be added : Size calculation before merging , Block : The main purpose is to meet serialization and merge efficiency
6> To be added : Facet reduction plug-in , When merging, you can dynamically reduce faces , I'm going to get a plug-in out as well , Runtime subtraction algorithm
7> To be added :USkeletalMesh Of Merge
8> To be added : serialize
Reference article
Datasmith Runtime Official Blog
Unreal Engine 4.27 Datasmith Runtime Import
UE – StaticMesh analysis
边栏推荐
- 【數字IC驗證快速入門】20、SystemVerilog學習之基本語法7(覆蓋率驅動...內含實踐練習)
- Detailed explanation of Cocos creator 2.4.0 rendering process
- HW初级流量监控,到底该怎么做
- OpenGL's distinction and understanding of VAO, VBO and EBO
- [original] all management without assessment is nonsense!
- 【跟着江科大学Stm32】STM32F103C8T6_PWM控制直流电机_代码
- The "go to definition" in VS2010 does not respond or prompts the solution of "symbol not found"
- Cocos creator collision and collision callback do not take effect
- Async and await
- 【兰州大学】考研初试复试资料分享
猜你喜欢
Unity之ASE实现全屏风沙效果
#HPDC智能基座人才发展峰会随笔
[make a boat diary] [shapr3d STL format to gcode]
【数字IC验证快速入门】23、SystemVerilog项目实践之AHB-SRAMC(3)(AHB协议基本要点)
【数字IC验证快速入门】19、SystemVerilog学习之基本语法6(线程内部通信...内含实践练习)
MySQL bit type resolution
Getting started with webgl (4)
Use cpolar to build a business website (2)
银行需要搭建智能客服模块的中台能力,驱动全场景智能客服务升级
[quick start of Digital IC Verification] 18. Basic grammar of SystemVerilog learning 5 (concurrent threads... Including practical exercises)
随机推荐
2. Basic knowledge of golang
[markdown grammar advanced] make your blog more exciting (IV: set font style and color comparison table)
Vertex shader to slice shader procedure, varying variable
Monthly observation of internet medical field in May 2022
leetcode 241. Different ways to add parentheses design priority for operational expressions (medium)
Zhongang Mining: Fluorite continues to lead the growth of new energy market
Database exception resolution caused by large table delete data deletion
Use of SVN
MySQL bit类型解析
【Markdown语法高级】让你的博客更精彩(四:设置字体样式以及颜色对照表)
Android -- jetpack: the difference between livedata setValue and postvalue
Webcodecs parameter settings -avc1.42e01e meaning
The difference between full-time graduate students and part-time graduate students!
【微信小程序】Chapter(5):微信小程序基础API接口
Bye, Dachang! I'm going to the factory today
Window环境下配置Mongodb数据库
简述keepalived工作原理
Oracle control file loss recovery archive mode method
[understanding of opportunity -40]: direction, rules, choice, effort, fairness, cognition, ability, action, read the five layers of perception of 3GPP 6G white paper
Syntax of generator function (state machine)