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