技术好文:UEFoliage工具拓展

简介: 技术好文:UEFoliage工具拓展

项目从4.24升级到4.26后 foliage的editor ui布局发生了很大的变化 顺带原项目中加的功能也要简单的移植下。


1.Fix:


UE的植被有个Invalid的标志,即Fix左边的这个小图标,这个是ue的自带功能选出所有的不合法的植被。


合法条件为: FoliageInstance的BaseComponent是否能在规定范围(射线检测)内找到,且误差在5cm内,且附着的BaseComponent与FoliageInstance是否在同一个level中(项目中新加)。


通俗的理解 所谓的不合法是指1.该植被没有附着的地面 2.地面与植被处于不同的level 3.离开地面过高 4.陷入地面过低。


1/2会影响关卡拆分移动重组导致植被的位置出错。3/4在表现上不美观。


不合法的植被是怎么来的。1美术同学的操作,比如为了美观移动某个植被到“空中”。2.拆关卡,静态合批拆合批的操作。


新加的Fix功能的目的即整体处理上面的4个问题。 具体代码与FoliageEdMode.cpp的SelectInvalidInstances相对应。分成2部分。


part1


step1 找到离需要修复的FoliageInstance最近的地面


step2 给FoliageInstance一个新的BaseId


step3 设置一个能够满足5cm误差的ZOffset来完成”对冲“


1 bool FEdModeFoliage::FixInstanceToGround(AInstancedFoliageActor InIFA, float AlignMaxAngle, FFoliageInfo& Mesh, int32 InstanceIdx)


2 {


3 UWorld InWorld = GetWorld();


4


5 FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(FoliageGroundCheck), true);


6 QueryParams.bReturnFaceIndex = false;


7 FCollisionShape SphereShape;


8 SphereShape.SetSphere(0.f);


9 TArray Hits; Hits.Reserve(16);


10 FHitResult HitResult;


11


12 FFoliageInstance& Instance = Mesh.Instances【InstanceIdx】;


13


14 FVector InstanceTraceRange = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, FOLIAGE_INVALID_TEST_TRACE));


15 FVector Start = Instance.Location + InstanceTraceRange;


16 FVector End = Instance.Location - InstanceTraceRange;


17


18 InWorld->SweepMultiByObjectType(Hits, Start, End, FQuat::Identity, FCollisionObjectQueryParams(ECC_WorldStatic), SphereShape, QueryParams);


19


20 // Find nearest hit


21 float TempDistance = InstanceTraceRange.Size();


22 for (const FHitResult& Hit : Hits)


23 {


24 float HitDistance = (Hit.Location - Instance.Location).Size();


25 if (HitDistance [span style="color: rgba(0, 0, 0, 1)"> TempDistance)


26 {


27 TempDistance = HitDistance;


28 HitResult = Hit;


29 }


30 }


31


32 if (HitResult.bBlockingHit)


33 {


34 UPrimitiveComponent HitComponent = HitResult.Component.Get();


35


36 // Find BSP brush


37 UModelComponent ModelComponent = Cast(HitComponent);


38 if (ModelComponent)


39 {


40 ABrush BrushActor = ModelComponent->GetModel()->FindBrush(HitResult.Location);


41 if (BrushActor)


42 {


43 HitComponent = BrushActor->GetBrushComponent();


44 }


45 }


46


47 // Set new base


48 auto NewBaseId = InIFA->InstanceBaseCache.AddInstanceBaseId(Mesh.ShouldAttachToBaseComponent() ? HitComponent : nullptr);


49 Mesh.RemoveFromBaseHash(InstanceIdx);


50 Instance.BaseId = NewBaseId;


51 if (Instance.BaseId == FFoliageInstanceBaseCache::InvalidBaseId)


52 {


53 Instance.BaseComponent = nullptr;


54 }


55 Mesh.AddToBaseHash(InstanceIdx);


56


57 // Set new ZOffset


58 float InstanceZUpOffsetFixLimitThreshold = CVarOffGroundZUpOffsetThreshold.GetValueOnGameThread();


59 float InstanceZDownOffsetFixLimitThreshold = CVarOffGroundZDownOffsetThreshold.GetValueOnGameThread();


60 float InstanceOffGroundLocalThreshold = CVarOffGroundTreshold.GetValueOnGameThread();


61 float InstanceWorldTreshold = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, InstanceOffGroundLocalThreshold)).Size();


62 float InstanceWorldZUpOffsetFixLimitThreshold = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, InstanceZUpOffsetFixLimitThreshold)).Size();


63 float InstanceWorldZDownOffsetFixLimitThreshold = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, InstanceZDownOffsetFixLimitThreshold)).Size();


64


65 FVector InstanceWorldZOffset = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, Instance.ZOffset));


66 FVector OffsetToGround = Instance.Location - (HitResult.Location + InstanceWorldZOffset);


67 bool bIsUpOffset = OffsetToGround.Z > 0 ? true : false;


68 float DistanceToGround = OffsetToGround.Size();


69


70 if (OffsetToGround.Z >= 0)


71 {


72 if (DistanceToGround > InstanceWorldZUpOffsetFixLimitThreshold)


73 {


74 return false;


75 }


76 if ((DistanceToGround - InstanceWorldTreshold) > KINDA_SMALL_NUMBER)


77 {


78 check(InstanceWorldZUpOffsetFixLimitThreshold > KINDA_SMALL_NUMBER);


79 float ScaleRateLocalToWorld = InstanceZUpOffsetFixLimitThreshold / InstanceWorldZUpOffsetFixLimitThreshold;


80 Instance.ZOffset += DistanceToGround ScaleRateLocalToWorld;


81 }


82 }


83 else


84 {


85 if (DistanceToGround > InstanceWorldZDownOffsetFixLimitThreshold)


86 {


87 return false;


88 }


89 if ((DistanceToGround - InstanceWorldTreshold) > KINDA_SMALL_NUMBER)


90 {


91 check(InstanceWorldZDownOffsetFixLimitThreshold > KINDA_SMALL_NUMBER);


92 float ScaleRateLocalToWorld = InstanceZDownOffsetFixLimitThreshold / InstanceWorldZDownOffsetFixLimitThreshold;


93 Instance.ZOffset -= DistanceToGround ScaleRateLocalToWorld;


94 }


95 }


96 return true;


97 }


98 return false;


99 }


View Code


part2


step1 将植被移到合理的关卡中


1 void FEdModeFoliage::MoveSelectInstancesToCorrectLevel(UWorld InWorld)


2 {


3 GEditor->BeginTransaction(NSLOCTEXT("UnrealEd", "FoliageModeTransaction:MoveSelectInstancesToCorrectLevel", "Move Foliage To Correct Level"));


4 {


5


6 const int32 NumLevels = InWorld->GetNumLevels();


7 for (int32 LevelIdx = 0; LevelIdx < NumLevels; ++LevelIdx)


8 {


9 ULevel Level = InWorld->GetLevel(LevelIdx);


10 AInstancedFoliageActor IFA = AInstancedFoliageActor::GetInstancedFoliageActorForLevel(Level);


11 if (IFA)


12 {


13 bool bFoundSelection = false;


14


15 for (auto& MeshPair : IFA->FoliageInfos)


16 {


17 FFoliageInfo& FoliageInfo = MeshPair.Value;


18


19 if (FoliageInfo.SelectedIndices.Array().Num() > 0)


//代码参考:https://weibo.com/u/7930362378

20 {


21 // Mark actor once we found selection


22 if (!bFoundSelection)


23 {


24 IFA->Modify();


25 bFoundSelection = true;


26 }


27 int InstanceIndex = 0;


28 while (InstanceIndex [span style="color: rgba(0, 0, 0, 1)"> FoliageInfo.Instances.Num())


29 {


30 if (!FixInstanceToCorrectLevel(IFA, MeshPair.Key->AlignMaxAngle, FoliageInfo, InstanceIndex))


31 {


32 InstanceIndex++;


33 }


34 }


35 }


36 }


37 }


38 }


39 }


40 GEditor->EndTransaction();


41 }


View Code


1 bool FEdModeFoliage::FixInstanceToCorrectLevel(AInstancedFoliageActor InIFA, float AlignMaxAngle, FFoliageInfo& Mesh, int32 InstanceIdx)


2 {


3 UWorld InWorld = GetWorld();


4


5 FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(FoliageGroundCheck), true);


6 QueryParams.bReturnFaceIndex = false;


7 FCollisionShape SphereShape;


8 SphereShape.SetSphere(0.f);


9 TArray Hits; Hits.Reserve(16);


10 FHitResult HitResult;


11


12 FFoliageInstance& Instance = Mesh.Instances【InstanceIdx】;


13


14 FVector InstanceTraceRange = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, FOLIAGE_INVALID_TEST_TRACE));


15 FVector Start = Instance.Location + InstanceTraceRange;


16 FVector End = Instance.Location - InstanceTraceRange;


17


18 InWorld->SweepMultiByObjectType(Hits, Start, End, FQuat::Identity, FCollisionObjectQueryParams(ECC_WorldStatic), SphereShape, QueryParams);


19


20 // Find nearest hit


21 float TempDistance = InstanceTraceRange.Size();


22 for (const FHitResult& Hit : Hits)


23 {


24 float HitDistance = (Hit.Location - Instance.Location).Size();


25 if (HitDistance [span style="color: rgba(0, 0, 0, 1)"> TempDistance)


26 {


27 TempDistance = HitDistance;


28 HitResult = Hit;


29 }


30 }


31


32 if (HitResult.bBlockingHit)


33 {


34 UPrimitiveComponent HitComponent = HitResult.Component.Get();


35


36 if (HitComponent->GetComponentLevel() != InIFA->GetLevel())


37 {


38 TSet InInstanceSet;


39 InInstanceSet.Emplace(InstanceIdx);


40 for (auto& Pair : InIFA->FoliageInfos)


41 {


42 if (&Pair.Value.Get() == &Mesh)


43 {


44 UFoliageType* FoliageType = Pair.Key;


45 InIFA->MoveInstancesToLevel(HitComponent->GetComponentLevel(), InInstanceSet, &Mesh, FoliageType);


46 return true;


47 }


48 }


49 }


50 <span style="color: rg

相关文章
|
19天前
|
前端开发 Android开发 开发者
探索移动应用开发之旅:从概念到上线
【7月更文挑战第31天】本文旨在引导读者走进移动应用开发的奇妙世界,从最初的构想到最终的应用上线。我们将探讨移动操作系统的基础知识,介绍跨平台开发工具,并深入理解如何通过代码示例实现一个基本的功能模块。文章不仅涉及技术细节,还关注用户体验和市场趋势,以帮助开发者构建既美观又实用的移动应用。
29 4
|
2月前
|
算法 前端开发 程序员
一个土木工程专业背景的开发者,讲述开源带给他的力量
**谭雪峰在TDengine Open Day分享开源经历,揭示程序员如何通过开源项目成长。自学成才的他,从土木工程转行编程,借助开源社区学习、贡献代码,参与TDengine HiveMQ挑战赛获胜,最终加入涛思数据。开源不仅提升代码质量、提供实战经验,也拓宽技术视野,助力个人品牌建立,促进开发者、项目和社区的共赢。程序员通过参与开源,能从代码编写者转变为问题解决者和工具创造者。**
23 0
|
3月前
|
人工智能 小程序 搜索推荐
【利用AI让知识体系化】从理论层面了解微信小程序(二)
【利用AI让知识体系化】从理论层面了解微信小程序
|
3月前
|
人工智能 小程序 前端开发
【利用AI让知识体系化】从理论层面了解微信小程序(一)
【利用AI让知识体系化】从理论层面了解微信小程序
|
8月前
|
人工智能 并行计算 算法
前言 | AI工程化部署
要达到AI工程化部署的诸多要求,需要除了AI算法以外的诸多技术,因此结合实际的项目经验,希望总结一些在AI工程化部署的有用技术供大家参考
1182 0
|
消息中间件 Cloud Native 中间件
盘点2022:开源热度居高,技术思考与经验分享是开发者的最爱
阿里巴巴中间件陪伴大家又是一年了,春节即将到来,我们不禁回望,这一年我们留下了什么,又收获了什么。
133 0
|
域名解析 开发框架 安全
笔记-搭建安全拓展
搭建安全拓展
133 0
|
Cloud Native 前端开发 Serverless
深入分析Flutter 技术应用与体系化建设 | 开发者社区精选文章合集(十五)
每一个移动开发者都在为 Flutter 带来的“快速开发、富有表现力和灵活的 UI、原生性能”的特色和理念而痴狂,从超级 App 到独立应用,从纯 Flutter 到混合栈,开发者们在不同的场景下乐此不疲的探索和应用着 Flutter 技术,也在面临着各种各样不同的挑战。本篇来看Flutter 技术解析以及体系化建设。
深入分析Flutter 技术应用与体系化建设 | 开发者社区精选文章合集(十五)
|
程序员 vr&ar 开发工具
如何用视频云技术,搞一个爆红的 “反应视频” 项目?
2021 年 2 月,“新内容 新交互” 全球视频云创新挑战赛启幕。本次大赛由英特尔联合阿里云主办,与优酷战略技术合作,天池平台和阿里云视频云团队共同承办。大赛自开赛以来,吸引了全球超过 4600 名选手报名参赛,我们遴选了参赛选手中优秀案例和动人故事,一起走进视频云创业创新者的世界。
如何用视频云技术,搞一个爆红的 “反应视频” 项目?
|
弹性计算 运维 Cloud Native
知识太枯燥?带你趣味学!云开发技术图谱首发上线,开启探索之旅!
开发者社区云开发技术图谱活动首发上线,新任务模式带你解锁图谱学习“新姿势”!赶快邀上你的好友们,一起探索云原生应用开发技术图谱的趣味宇宙吧!参与活动有超高机会赢得小米移动电源一个,礼品限量,先到先得!
知识太枯燥?带你趣味学!云开发技术图谱首发上线,开启探索之旅!