项目从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