Agrarsense
PhysicsUtilities.cpp
Go to the documentation of this file.
1// Copyright (c) 2023 FrostBit Software Lab at the Lapland University of Applied Sciences
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5
6#include "PhysicsUtilities.h"
7#include "Engine/GameViewportClient.h"
8#include "Engine/OverlapResult.h"
9#include "Engine/EngineTypes.h"
10#include "Engine/Engine.h"
11#include "Engine/World.h"
12
13#if WITH_EDITOR
14#include "DrawDebugHelpers.h"
15#endif
16
17bool UPhysicsUtilities::SnapActorToGround(AActor* Actor, float StartZOffset, float EndZOffset)
18{
19 bool ActorPositionWasChanged = false;
20
21 if (!Actor)
22 {
23 return ActorPositionWasChanged;
24 }
25
26 UWorld* World = Actor->GetWorld();
27 if (World && Actor)
28 {
29 // Perform a line trace from a point above the actor's location to a point slightly below it
30 const FVector ActorLocation = Actor->GetActorLocation();
31 const FVector StartLocation = ActorLocation + FVector(0.0f, 0.0f, StartZOffset);
32 const FVector EndLocation = ActorLocation - FVector(0.0f, 0.0f, EndZOffset);
33 FHitResult HitResult;
34 FCollisionQueryParams QueryParams;
35 QueryParams.AddIgnoredActor(Actor);
36 bool Hits = World->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECC_WorldStatic, QueryParams);
37
38 if (Hits)
39 {
40 //Actor->SetActorLocation(HitResult.ImpactPoint);
41 Actor->TeleportTo(HitResult.ImpactPoint, Actor->GetActorRotation());
42 ActorPositionWasChanged = true;
43 }
44 }
45
46 return ActorPositionWasChanged;
47}
48
49bool UPhysicsUtilities::SnapActorAboveGround(AActor* Actor, float AboveOffset)
50{
51 if (!Actor)
52 {
53 return false;
54 }
55
56 // First snap actor to the ground
57 SnapActorToGround(Actor);
58
59 // Then add Z offset
60 const FVector CurrentActorLocation = Actor->GetActorLocation();
61 const FVector EndActorLocation = CurrentActorLocation + FVector(0.0f, 0.0f, AboveOffset);
62 Actor->TeleportTo(EndActorLocation, Actor->GetActorRotation());
63 //Actor->SetActorLocation(EndActorLocation);
64
65 return true;
66}
67
68bool UPhysicsUtilities::DoesTopPercentageMeshOverlap(AActor* Actor, const UStaticMesh* Mesh, float TopPercentage, ECollisionChannel CollisionChannel)
69{
70 if (!Actor)
71 {
72 return false;
73 }
74
75 UWorld* World = Actor->GetWorld();
76
77 if (World && Mesh)
78 {
79 FTransform Transform = Actor->GetActorTransform();
80 FVector ActorLocation = Transform.GetLocation();
81 FVector ActorScale = Transform.GetScale3D();
82
83 // Calculate the top extent of the mesh bounds
84 FVector MeshExtent = Mesh->GetBounds().BoxExtent;
85 FVector TopExtent = FVector(MeshExtent.X, MeshExtent.Y, MeshExtent.Z * TopPercentage);
86
87 FVector StartLocation = ActorLocation + FVector(0.0f, 0.0f, MeshExtent.Z);
88 FVector EndLocation = ActorLocation + FVector(0.0f, 0.0f, MeshExtent.Z * TopPercentage);
89
90 FHitResult HitResult;
91 FCollisionQueryParams QueryParams;
92 QueryParams.AddIgnoredActor(Actor);
93
94 // Perform a line trace to check for collisions in the top percentage of the mesh
95 return World->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, CollisionChannel, QueryParams);
96 }
97
98 return false;
99}
100
101bool UPhysicsUtilities::HasOverlappingActors(const UStaticMesh* StaticMesh, const FTransform& Transform)
102{
103 if (!StaticMesh)
104 {
105#if WITH_EDITOR
106 UE_LOG(LogTemp, Warning, TEXT("UPhysicsUtilities.cpp: StaticMesh is null!"));
107#endif
108 return false;
109 }
110
111 if (!GEngine || !GEngine->GameViewport)
112 {
113 return false;
114 }
115
116 TArray<FOverlapResult> OverlapResults;
117
118 UWorld* World = GEngine->GameViewport->GetWorld();
119 if (World)
120 {
121 FVector Location = Transform.GetLocation();
122 FQuat Rotation = Transform.GetRotation();
123 FVector Scale = Transform.GetScale3D();
124 FBox ActorBounds = StaticMesh->GetBoundingBox().TransformBy(FTransform(Rotation, Location, Scale));
125
126 World->OverlapMultiByObjectType(
127 OverlapResults,
128 Location,
129 Rotation,
130 FCollisionObjectQueryParams(ECC_WorldStatic),
131 FCollisionShape::MakeBox(ActorBounds.GetSize() * 0.5f)
132 );
133 }
134
135 return OverlapResults.Num() > 0;
136}
137
138TArray<AActor*> UPhysicsUtilities::FindOverlappingActorsInSphere(const FTransform& ActorTransform, float Radius, bool DebugVisualizeRadius)
139{
140 return FindOverlappingActorsOfClass<AActor>(ActorTransform, Radius, DebugVisualizeRadius);
141}
142
143template<typename T>
144TArray<T*> UPhysicsUtilities::FindOverlappingActorsOfClass(const FTransform& ActorTransform, float Radius, bool DebugVisualizeRadius)
145{
146 TArray<T*> OverlappingActors;
147
148 if (!GEngine || !GEngine->GameViewport)
149 {
150 return OverlappingActors;
151 }
152
153 UWorld* World = GEngine->GameViewport->GetWorld();
154
155 if (World)
156 {
157 TArray<FOverlapResult> OverlapResults;
158 FCollisionQueryParams Params;
159 Params.AddIgnoredActor(nullptr);
160 Params.bTraceComplex = false;
161 Params.bReturnPhysicalMaterial = false;
162
163 FCollisionObjectQueryParams ObjectQueryParams;
164 ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldStatic);
165
166 bool bOverlap = World->OverlapMultiByObjectType(
167 OverlapResults,
168 ActorTransform.GetLocation(),
169 ActorTransform.GetRotation(),
170 ObjectQueryParams,
171 FCollisionShape::MakeSphere(Radius),
172 Params
173 );
174
175 if (bOverlap)
176 {
177 for (const FOverlapResult& OverlapResult : OverlapResults)
178 {
179 T* OverlappingActor = Cast<T>(OverlapResult.GetActor());
180 if (OverlappingActor)
181 {
182 OverlappingActors.Add(OverlappingActor);
183 }
184 }
185 }
186 }
187
188#if WITH_EDITOR
189 if (DebugVisualizeRadius)
190 {
191 DrawDebugSphere(World, ActorTransform.GetLocation(), Radius, 12, FColor::Green, false, 2.0f, 0, 1.0f);
192 }
193#endif
194
195 return OverlappingActors;
196}
static bool SnapActorAboveGround(AActor *Actor, float AboveOffset=50.0f)
static bool HasOverlappingActors(const UStaticMesh *StaticMesh, const FTransform &Transform)
static bool DoesTopPercentageMeshOverlap(AActor *Actor, const UStaticMesh *Mesh, float TopPercentage, ECollisionChannel CollisionChannel)
static TArray< T * > FindOverlappingActorsOfClass(const FTransform &ActorTransform, float Radius, bool DebugVisualizeRadius=false)
static TArray< AActor * > FindOverlappingActorsInSphere(const FTransform &ActorTransform, float Radius, bool DebugVisualizeRadius=false)
static bool SnapActorToGround(AActor *Actor, float StartZOffset=600.0f, float EndZOffset=600.0f)