Agrarsense
AssetLibrary.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 "AssetLibrary.h"
7
17
21
22#include "Kismet/KismetSystemLibrary.h"
23#include "Engine/GameViewportClient.h"
24#include "GameFramework/Actor.h"
25#include "Engine/Engine.h"
26#include "UObject/Class.h"
27#include "Engine/World.h"
28
29// Paths to data assets
30static const FString VEHICLE_ASSET_PATH = "/Game/Agrarsense/Data/Vehicles/VehicleActorAssetMap";
31static const FString FOLIAGE_ASSET_PATH = "/Game/Agrarsense/Data/Foliage/FoliageActorAssetMap";
32static const FString WALKER_ASSET_PATH = "/Game/Agrarsense/Data/Walkers/WalkerActorAssetMap";
33static const FString PROP_ASSET_PATH = "/Game/Agrarsense/Data/Props/PropActorAssetMap";
34
35TWeakObjectPtr<UFoliageActorAssetMapDataAsset> UAssetLibrary::FoliageActorAssetMapDataAsset;
36TArray<FInstancedActorData> UAssetLibrary::AddedFoliageActors;
37TMap<EFoliageTypes, FString> UAssetLibrary::FoliageTypeMap;
38TArray<EFoliageTypes> UAssetLibrary::FoliageKeys;
39
40TWeakObjectPtr <UVehicleActorAssetMapDataAsset> UAssetLibrary::VehicleDataAsset;
41TArray<FVehicleData> UAssetLibrary::SpawnedVehicles;
42
43TWeakObjectPtr<UPropActorAssetMapDataAsset> UAssetLibrary::PropActorAssetMapDataAsset;
44TArray<FInstancedActorData> UAssetLibrary::SpawnedPropActors;
45TMap<EPropTypes, FString> UAssetLibrary::PropTypeMap;
46TArray<EPropTypes> UAssetLibrary::PropKeys;
47
48TWeakObjectPtr<UWalkerActorAssetMapDataAsset> UAssetLibrary::WalkerActorAssetMapDataAsset;
49TArray<TWeakObjectPtr<AWalker>> UAssetLibrary::SpanwedWalkers;
50
51AActor* UAssetLibrary::SpawnFoliage(EFoliageTypes FoliageType, FTransform Transform,
52 FString ActorName, FString ActorID,
53 bool RandomZRotation, bool SnapToGround)
54{
56
57 if (!FoliageActorAssetMapDataAsset.IsValid())
58 {
59 return nullptr;
60 }
61
62 UFoliageActorAssetDataAsset* ActorAssetData = FoliageActorAssetMapDataAsset->Foliages.FindRef(FoliageType);
63 TSubclassOf<AActor> ActorClass = ActorAssetData ? ActorAssetData->ActorAsset.Actor : nullptr;
64
65 AActor* SpawnedActor = TrySpawnActor(ActorClass, Transform, ActorName, ActorID, SnapToGround, RandomZRotation);
66
67 return SpawnedActor;
68}
69
71 FTransform SpawnTransform, const FString& ActorName,
72 const FString& ActorID, bool SnapAboveGround,
73 const float AboveOffset, const bool DestroyOverlappingActors)
74{
76
77 if (!VehicleDataAsset.IsValid())
78 {
79 return nullptr;
80 }
81
82 UVehicleActorAssetDataAsset* ActorAssetData = VehicleDataAsset->Vehicles.FindRef(VehicleType);
83 TSubclassOf<AActor> ActorClass = ActorAssetData ? ActorAssetData->ActorAsset.Actor : nullptr;
84
85 ESpawnActorCollisionHandlingMethod SpawnCollisionHandling = ESpawnActorCollisionHandlingMethod::Undefined;
86 if (VehicleType == EVehicleTypes::Drone)
87 {
88 // Workaround for drone
89 SpawnCollisionHandling = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
90 }
91
92 FString ID = ActorID;
93 if (ID.IsEmpty())
94 {
96 }
97
98 AActor* SpawnedActor = TrySpawnActor(ActorClass, SpawnTransform, ActorName, ID, false, false, SpawnCollisionHandling);
99
100 AVehicle* Vehicle = Cast<AVehicle>(SpawnedActor);
101 if (SpawnedActor && Vehicle)
102 {
103 if (SnapAboveGround)
104 {
106 SpawnTransform = SpawnedActor->GetTransform();
107 }
108
109 FVehicleData VehicleData;
110 VehicleData.Vehicle = Vehicle;
111 VehicleData.VehicleType = VehicleType;
112 SpawnedVehicles.Add(VehicleData);
113
114 Vehicle->TeleportTo(SpawnTransform.GetLocation(), SpawnTransform.GetRotation().Rotator());
115
116 if (DestroyOverlappingActors)
117 {
119 }
120 }
121
122 return Vehicle;
123}
124
125AActor* UAssetLibrary::SpawnProp(EPropTypes PropType, FTransform Transform,
126 FString ActorName, FString ActorID,
127 bool RandomZRotation, bool SnapToGround)
128{
130
131 if (!PropActorAssetMapDataAsset.IsValid())
132 {
133 return nullptr;
134 }
135
136 UPropActorAssetDataAsset* ActorAssetData = PropActorAssetMapDataAsset->Props.FindRef(PropType);
137 TSubclassOf<AActor> ActorClass = ActorAssetData ? ActorAssetData->ActorAsset.Actor : nullptr;
138
139 AActor* SpawnedActor = TrySpawnActor(ActorClass, Transform, ActorName, ActorID, SnapToGround, RandomZRotation);
140
141 AInstancedActor* InstancedActor = Cast<AInstancedActor>(SpawnedActor);
142 if (InstancedActor)
143 {
144 FInstancedActorData InstancedActorData;
145 InstancedActorData.Actor = InstancedActor;
146 SpawnedPropActors.Add(InstancedActorData);
147
148 if (PropTypeMap.Contains(PropType))
149 {
150 FString Model = PropTypeMap[PropType];
151
152 // Set InstactedActor type and model strings, this is used for JSON exporting.
153 InstancedActor->SetTypeAndModel("prop", Model);
154 }
155 }
156
157 return SpawnedActor;
158}
159
160AWalker* UAssetLibrary::SpawnWalker(FWalkerParameters Parameters, const FString& ActorName,
161 const FString& ActorID, bool StartAutomatically)
162{
164
165 if (!WalkerActorAssetMapDataAsset.IsValid())
166 {
167 return nullptr;
168 }
169
170 UWalkerActorAssetDataAsset* ActorAssetData = WalkerActorAssetMapDataAsset->Walkers.FindRef(Parameters.WalkerType);
171 TSubclassOf<AActor> ActorClass = ActorAssetData ? ActorAssetData->ActorAsset.Actor : nullptr;
172
173 FTransform StartTransform;
174 auto& Transforms = Parameters.Points;
175
176 if (!Transforms.IsEmpty())
177 {
178 if (Parameters.WalkerAction == EWalkerAction::FollowPath && Transforms.Num() < 2)
179 {
180 SimulatorLog::Log("Failed to spawn FollowPath Walker due to insufficient path points.");
181 return nullptr;
182 }
183 StartTransform = Transforms[0];
184 }
185
186 FString ID = ActorID;
187 if (ID.IsEmpty())
188 {
190 }
191
192 AActor* SpawnedActor = TrySpawnActor(ActorClass, StartTransform, ActorName, ID, false, false);
193
194 AWalker* Walker = Cast<AWalker>(SpawnedActor);
195 if (Walker)
196 {
198 Walker->SetWalkerParameters(Parameters);
199 Walker->SetIgnoreInput(!StartAutomatically);
200 }
201
202 return Walker;
203}
204
205AActor* UAssetLibrary::TrySpawnActor(TSubclassOf<AActor> ActorClass, FTransform& Transform,
206 FString ActorName, FString ActorID,
207 bool SnapToGround, bool RandomZRotation, ESpawnActorCollisionHandlingMethod CollisionMethod)
208{
209 if (!IsInGameThread())
210 {
211#if WITH_EDITOR
212 UE_LOG(LogTemp, Warning, TEXT("AssetLibrary.cpp: Attempting to spawn actor from a background thread. This is not allowed."));
213#endif
214 return nullptr;
215 }
216
217 if (!ActorClass)
218 {
219#if WITH_EDITOR
220 UE_LOG(LogTemp, Warning, TEXT("AssetLibrary.cpp: ActorClass is nullptr! Cannot spawn actor."));
221#endif
222 return nullptr;
223 }
224
225 UWorld* World = nullptr;
226 if (GEngine && GEngine->GameViewport)
227 {
228 World = GEngine->GameViewport->GetWorld();
229 }
230
231 if (!World)
232 {
233 return nullptr;
234 }
235
236 // Spawn actor deferred
237 AActor* SpawnedActor = World->SpawnActorDeferred<AActor>(ActorClass, Transform, nullptr, nullptr, CollisionMethod);
238
239 if (SpawnedActor)
240 {
241 if (SnapToGround)
242 {
244 Transform = SpawnedActor->GetTransform();
245 }
246
247 if (RandomZRotation)
248 {
249 float RotationAngle = FMath::FRandRange(0.0f, 360.0f);
250 Transform.SetRotation(FQuat::MakeFromEuler(FVector(0.0f, 0.0f, RotationAngle)));
251 }
252
253 // Set ID and name through IActorInformation if the spawned actor implements it
254 if (SpawnedActor && SpawnedActor->GetClass()->ImplementsInterface(UActorInformation::StaticClass()))
255 {
256 IActorInformation::Execute_SetActorIDAndName(SpawnedActor, ActorName, ActorID);
257 }
258
259 // Finish Actor spawning (BeginPlay runs now)
260 SpawnedActor->FinishSpawning(Transform);
261 }
262
263 return SpawnedActor;
264}
265
267{
268 UWorld* World = nullptr;
269 if (GEngine && GEngine->GameViewport)
270 {
271 World = GEngine->GameViewport->GetWorld();
272 }
273
274 if (!World || !SpawnedActor)
275 {
276 return;
277 }
278
279 FVector Origin;
280 FVector BoxExtent;
281 SpawnedActor->GetActorBounds(true, Origin, BoxExtent);
282
283 float SphereRadius = BoxExtent.GetMax();
284
285 TArray<AActor*> OverlappingActors;
286 UKismetSystemLibrary::SphereOverlapActors(World, SpawnedActor->GetTransform().GetLocation(), SphereRadius, {}, nullptr, {}, OverlappingActors);
287
288 for (AActor* OverlappingActor : OverlappingActors)
289 {
290 if (OverlappingActor && OverlappingActor != SpawnedActor)
291 {
292 if (OverlappingActor->IsA(AWalker::StaticClass()) ||
293 OverlappingActor->IsA(AInstancedActor::StaticClass()) ||
294 OverlappingActor->IsA(AVehicle::StaticClass()))
295 {
296 OverlappingActor->Destroy();
297 }
298 }
299 }
300}
301
303{
304 SpawnedVehicles.RemoveAll([](const FVehicleData& VehicleData)
305 {
306 return !VehicleData.Vehicle.IsValid();
307 });
308
309 return SpawnedVehicles;
310}
311
313{
315
317}
318
320{
322
323 EFoliageTypes RandomFoliageType = EFoliageTypes::NONE;
324 if (FoliageKeys.Num())
325 {
326 int32 RandomIndex = FMath::RandRange(0, FoliageKeys.Num() - 1);
327 RandomFoliageType = FoliageKeys[RandomIndex];
328 }
329
330 return RandomFoliageType;
331}
332
334{
335 AddedFoliageActors.RemoveAll([](const FInstancedActorData& ActorData)
336 {
337 return !ActorData.Actor.IsValid();
338 });
339
340 TArray<AInstancedActor*> ValidActors;
341 ValidActors.Reserve(AddedFoliageActors.Num());
342
343 for (const FInstancedActorData& ActorData : AddedFoliageActors)
344 {
345 if (ActorData.Actor.IsValid())
346 {
347 ValidActors.Add(ActorData.Actor.Get());
348 }
349 }
350
351 return ValidActors;
352}
353
355{
357
358 return PropActorAssetMapDataAsset.Get();
359}
360
362{
364
365 EPropTypes RandomPropType = EPropTypes::NONE;
366 if (PropKeys.Num())
367 {
368 int32 RandomIndex = FMath::RandRange(0, PropKeys.Num() - 1);
369 RandomPropType = PropKeys[RandomIndex];
370 }
371
372 return RandomPropType;
373}
374
375TArray<AInstancedActor*> UAssetLibrary::GetAllAddedPropActors()
376{
377 SpawnedPropActors.RemoveAll([](const FInstancedActorData& ActorData)
378 {
379 return !ActorData.Actor.IsValid();
380 });
381
382 TArray<AInstancedActor*> ValidActors;
383 ValidActors.Reserve(SpawnedPropActors.Num());
384
385 for (const FInstancedActorData& ActorData : SpawnedPropActors)
386 {
387 if (ActorData.Actor.IsValid())
388 {
389 ValidActors.Add(ActorData.Actor.Get());
390 }
391 }
392
393 return ValidActors;
394}
395
397{
399
400 return WalkerActorAssetMapDataAsset.Get();
401}
402
404{
405 SpanwedWalkers.RemoveAll([](const TWeakObjectPtr<AWalker>& ActorData)
406 {
407 return !ActorData.IsValid();
408 });
409
410 TArray<AWalker*> ValidWalkers;
411 ValidWalkers.Reserve(SpanwedWalkers.Num());
412
413 for (const TWeakObjectPtr<AWalker>& ActorData : SpanwedWalkers)
414 {
415 AWalker* WalkerPtr = ActorData.Get();
416 if (WalkerPtr)
417 {
418 ValidWalkers.Add(WalkerPtr);
419 }
420 }
421
422 return ValidWalkers;
423}
424
426{
427 TArray<AWalker*> WalkersActors = GetAllWalkers();
428
429 if (WalkersActors.IsEmpty())
430 {
431 return;
432 }
433
434 for (AWalker* Walker : WalkersActors)
435 {
436 if (Walker)
437 {
438 Walker->Destroy();
439 }
440 }
441
442 WalkersActors.Empty();
443
444 FString Message = FString::Printf(TEXT("Destroyed all %d Walkers."), WalkersActors.Num());
445 SimulatorLog::Log(Message);
446}
447
449{
450 if (!VehicleDataAsset.IsValid())
451 {
452 VehicleDataAsset.Reset();
453 FSoftObjectPath VehicleActorAssetMap(VEHICLE_ASSET_PATH);
454 VehicleDataAsset = Cast<UVehicleActorAssetMapDataAsset>(VehicleActorAssetMap.TryLoad());
455 }
456}
457
459{
460 if (FoliageActorAssetMapDataAsset.IsValid())
461 {
462 return;
463 }
464
466 FSoftObjectPath FoliageActorAssetMapDataAssetPath(FOLIAGE_ASSET_PATH);
467 FoliageActorAssetMapDataAsset = Cast<UFoliageActorAssetMapDataAsset>(FoliageActorAssetMapDataAssetPath.TryLoad());
468
469 if (FoliageActorAssetMapDataAsset.IsValid())
470 {
472 FoliageTypeMap.Empty();
473 FoliageTypeMap = UEnumUtilities::CreateEnumStringMap<EFoliageTypes>("/Script/Agrarsense.EFoliageTypes");
474 }
475}
476
478{
479 if (PropActorAssetMapDataAsset.IsValid())
480 {
481 return;
482 }
483
484 FSoftObjectPath PropActorAssetMapDataAssetPath(PROP_ASSET_PATH);
485 PropActorAssetMapDataAsset = Cast<UPropActorAssetMapDataAsset>(PropActorAssetMapDataAssetPath.TryLoad());
486
487 if (PropActorAssetMapDataAsset.IsValid())
488 {
489 PropActorAssetMapDataAsset->Props.GetKeys(PropKeys);
490 SpawnedPropActors.Empty();
491
492 PropTypeMap.Empty();
493 PropTypeMap = UEnumUtilities::CreateEnumStringMap<EPropTypes>("/Script/Agrarsense.EPropTypes");
494 }
495}
496
498{
499 if (!WalkerActorAssetMapDataAsset.IsValid())
500 {
501 FSoftObjectPath WalkerActorAssetMapDataAssetPath(WALKER_ASSET_PATH);
502 WalkerActorAssetMapDataAsset = Cast<UWalkerActorAssetMapDataAsset>(WalkerActorAssetMapDataAssetPath.TryLoad());
503 }
504}
static const FString PROP_ASSET_PATH
static const FString VEHICLE_ASSET_PATH
static const FString FOLIAGE_ASSET_PATH
static const FString WALKER_ASSET_PATH
EFoliageTypes
Definition: FoliageTypes.h:15
EPropTypes
Definition: PropTypes.h:15
EVehicleTypes
Definition: VehicleTypes.h:15
void SetTypeAndModel(const FString &Type, const FString &Model)
Definition: Walker.h:28
static void Log(const FString &Message, bool LogToTextFile=true, bool LogToROS=true)
static TWeakObjectPtr< UFoliageActorAssetMapDataAsset > FoliageActorAssetMapDataAsset
Definition: AssetLibrary.h:174
static UWalkerActorAssetMapDataAsset * GetWalkerActorAssetMapDataAsset()
static TArray< AInstancedActor * > GetAllAddedPropActors()
static void SetupWalkerDataAsset()
static TArray< FInstancedActorData > SpawnedPropActors
Definition: AssetLibrary.h:181
static TMap< EFoliageTypes, FString > FoliageTypeMap
Definition: AssetLibrary.h:175
static AVehicle * SpawnVehicle(EVehicleTypes VehicleType, FTransform SpawnTransform, const FString &ActorName, const FString &ActorID, bool SnapAboveGround=false, float AboveOffset=150.0f, bool DestroyOverlappingActors=false)
static void DestroyAllWalkers()
static TWeakObjectPtr< UVehicleActorAssetMapDataAsset > VehicleDataAsset
Definition: AssetLibrary.h:170
static void SetupVehicleDataAsset()
static EFoliageTypes GetRandomFoliageType()
static TArray< TWeakObjectPtr< AWalker > > SpanwedWalkers
Definition: AssetLibrary.h:187
static void DestroyOverlappingActorsSphere(AActor *SpawnedActor)
static TArray< FVehicleData > GetSpawnedVehicles()
static TArray< EPropTypes > PropKeys
Definition: AssetLibrary.h:183
static TArray< EFoliageTypes > FoliageKeys
Definition: AssetLibrary.h:177
static TMap< EPropTypes, FString > PropTypeMap
Definition: AssetLibrary.h:182
static TArray< AInstancedActor * > GetAllAddedFoliageActors()
static TArray< AWalker * > GetAllWalkers()
static void SetupPropDataAsset()
static TWeakObjectPtr< UPropActorAssetMapDataAsset > PropActorAssetMapDataAsset
Definition: AssetLibrary.h:180
static AActor * SpawnProp(EPropTypes PropType, FTransform Transform, FString ActorName="", FString ActorID="", bool RandomZRotation=false, bool SnapToGround=false)
static void SetupFoliageDataAsset()
static TWeakObjectPtr< UWalkerActorAssetMapDataAsset > WalkerActorAssetMapDataAsset
Definition: AssetLibrary.h:186
static AActor * TrySpawnActor(TSubclassOf< AActor > ActorClass, FTransform &Transform, FString ActorName, FString ActorID, bool SnapToGround, bool RandomZRotation, ESpawnActorCollisionHandlingMethod CollisionMethod=ESpawnActorCollisionHandlingMethod::Undefined)
static AWalker * SpawnWalker(FWalkerParameters Parameters, const FString &ActorName="", const FString &ActorID="", bool StartAutomatically=true)
static EPropTypes GetRandomPropType()
static TArray< FVehicleData > SpawnedVehicles
Definition: AssetLibrary.h:171
static UPropActorAssetMapDataAsset * GetPropActorAssetMapDataAsset()
static UFoliageActorAssetMapDataAsset * GetFoliageActorAssetMapDataAsset()
static AActor * SpawnFoliage(EFoliageTypes FoliageType, FTransform Transform, FString ActorName="", FString ActorID="", bool RandomZRotation=true, bool SnapToGround=true)
static TArray< FInstancedActorData > AddedFoliageActors
Definition: AssetLibrary.h:176
static FString ConvertVehicleTypeToString(EVehicleTypes VehicleType)
static FString ConvertWalkerTypeToString(EWalkerType WalkerType)
static bool SnapActorAboveGround(AActor *Actor, float AboveOffset=50.0f)
static bool SnapActorToGround(AActor *Actor, float StartZOffset=600.0f, float EndZOffset=600.0f)
TSubclassOf< AActor > Actor
Definition: ActorAsset.h:37
TWeakObjectPtr< AInstancedActor > Actor
EVehicleTypes VehicleType
Definition: AssetLibrary.h:46
TWeakObjectPtr< AVehicle > Vehicle
Definition: AssetLibrary.h:40
EWalkerAction WalkerAction
TArray< FTransform > Points
EWalkerType WalkerType