Agrarsense
SimulatorJsonParser.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
7
12
20
23
25
39
41
42#include "Kismet/GameplayStatics.h"
43#include "Camera/PlayerCameraManager.h"
44#include "GenericPlatform/GenericPlatformFile.h"
45#include "Serialization/JsonSerializer.h"
46#include "Serialization/JsonWriter.h"
47#include "Misc/DefaultValueHelper.h"
48#include "HAL/PlatformFileManager.h"
49#include "GameFramework/Pawn.h"
50#include "Misc/FileHelper.h"
51#include "Dom/JsonObject.h"
52#include "GameFramework/PlayerController.h"
53#include "CollisionQueryParams.h"
54#include "Engine/GameViewportClient.h"
55#include "Engine/EngineTypes.h"
56#include "Engine/Engine.h"
57#include "Engine/World.h"
58
61
63{
65 {
66 SimulatorLog::Log("SimulatorJsonParser: Spawning objects in main menu is disabled.");
67 return;
68 }
69
70 if (!FPaths::FileExists(Path))
71 {
72 SimulatorLog::Log(FString::Printf(TEXT("SimulatorJsonParser: Path %s doesn't exist."), *Path));
73 return;
74 }
75
76 if (!FPaths::GetExtension(Path).Equals(TEXT("json"), ESearchCase::IgnoreCase))
77 {
78 SimulatorLog::Log("SimulatorJsonParser: Invalid file extension. Only .json files are supported.");
79 return;
80 }
81
82 FString JsonString;
83 if (!FFileHelper::LoadFileToString(JsonString, *Path))
84 {
85 SimulatorLog::Log("SimulatorJsonParser: Failed to read the json file.");
86 return;
87 }
88
89 TSharedPtr<FJsonObject> RootJsonObject = MakeShareable(new FJsonObject);
90 TSharedRef<TJsonReader<TCHAR>> Reader = TJsonReaderFactory<TCHAR>::Create(JsonString);
91 if (!FJsonSerializer::Deserialize(Reader, RootJsonObject))
92 {
93 SimulatorLog::Log("SimulatorJsonParser: Failed to deserialize json file.");
94 return;
95 }
96
97 const TArray<TSharedPtr<FJsonValue>>* JsonObjects;
98 if (!RootJsonObject->TryGetArrayField("objects", JsonObjects))
99 {
100 SimulatorLog::Log("json content doesn't contain 'objects' field. Make sure your json structure is correct.");
101 return;
102 }
103
104 // The key in the JSON object that we are searching for
105 FString FieldTypeString = "type";
106
107 // Pre-process incoming json objects
108 bool JsonContainsSensorType = false;
109 bool JsonContainsDataCaptureType = false;
110 TArray<TSharedPtr<FJsonObject>> FilteredJsonObjects;
111
112 for (const TSharedPtr<FJsonValue>& JsonObjectValue : *JsonObjects)
113 {
114 if (JsonObjectValue->Type != EJson::Object)
115 {
116 continue;
117 }
118
119 TSharedPtr<FJsonObject> JsonObject = JsonObjectValue->AsObject();
120 FString ObjectType;
121 if (!JsonObject->TryGetStringField(FieldTypeString, ObjectType))
122 {
123 continue;
124 }
125
126 // Convert ObjectType to lowercase to avoid case-sensitive checks below.
127 ObjectType.ToLowerInline();
128
129 FilteredJsonObjects.Add(JsonObject);
130
131 if (ObjectType == "sensor")
132 {
133 JsonContainsSensorType = true;
134 }
135 else if (ObjectType == "datacapture")
136 {
137 JsonContainsDataCaptureType = true;
138 }
139 }
140
141 // Check if both "sensor" and "datacapture" types are present in the JSON.
142 // If so, set IsDataCapture to true, indicating that spawned sensors should be tracked
143 // and added to the ADataCapture actor.
144 IsDataCapture = JsonContainsSensorType && JsonContainsDataCaptureType;
145
146 // Process json objects
147 for (const TSharedPtr<FJsonObject>& JsonObject : FilteredJsonObjects)
148 {
149 FString ObjectType;
150 if (!JsonObject->TryGetStringField(FieldTypeString, ObjectType))
151 {
152 continue;
153 }
154
155 ObjectType.ToLowerInline();
156
157 if (ObjectType == "vehicle")
158 {
159 ParseVehicle(JsonObject);
160 }
161 else if (ObjectType == "weather")
162 {
163 ChangeWeather(JsonObject);
164 }
165 else if (ObjectType == "walker")
166 {
167 SpawnWalker(JsonObject);
168 }
169 else if (ObjectType == "sensor")
170 {
171 SpawnSensorWorld(JsonObject);
172 }
173 else if (ObjectType == "prop")
174 {
175 ParsePropOrFoliage(JsonObject, "prop");
176 }
177 else if (ObjectType == "foliage")
178 {
179 ParsePropOrFoliage(JsonObject, "foliage");
180 }
181 else if (ObjectType == "semanticcolors")
182 {
183 ParseAndSetSemanticColors(JsonObject);
184 }
185 else if (ObjectType == "commands")
186 {
187 ParseCommands(JsonObject);
188 }
189 else if (ObjectType == "volumedeletion")
190 {
191 ParseVolumeDeletion(JsonObject);
192 }
193 else if (ObjectType == "datacapture")
194 {
195 ParseDataCapture(JsonObject);
196 }
197 }
198
199 // If IsDataCapture was true, clear array.
200 if (IsDataCapture)
201 {
203 IsDataCapture = false;
204 }
205}
206
207void USimulatorJsonParser::ParseVehicle(const TSharedPtr<FJsonObject>& JsonObject)
208{
209 FString Model;
210 if (!JsonObject->TryGetStringField("model", Model))
211 {
212 // If json doesn't specifiy model, return
213 return;
214 }
215
216 FString Name, ID;
217 JsonObject->TryGetStringField("name", Name);
218 JsonObject->TryGetStringField("id", ID);
219 if (ID.IsEmpty())
220 {
221 ID = Model;
222 }
223
225 bool IsDrone = VehicleType == EVehicleTypes::Drone;
226
227 FTransform SpawnTransform = ParseTransform("spawnPoint", JsonObject);
228
229 // Should use Z value as height from the ground instead of given Z value
230 bool ZIsHeightAboveGround = false;
231 JsonObject->TryGetBoolField("zIsHeightAboveGround", ZIsHeightAboveGround);
232 if (ZIsHeightAboveGround)
233 {
234 ComputeAndSetZFromGround(SpawnTransform);
235 }
236
237 bool DestroyCollidedObjectsOnSpawn = false;
238 JsonObject->TryGetBoolField("destroyOverlappingObjects", DestroyCollidedObjectsOnSpawn);
239
240 AVehicle* VehiclePtr = UAssetLibrary::SpawnVehicle(VehicleType, SpawnTransform, Name, ID, false, 150.0f, DestroyCollidedObjectsOnSpawn);
241
242 if (VehiclePtr)
243 {
244 if (IsDrone)
245 {
246 ParseAndSetDroneParameters(Cast<APIDDrone>(VehiclePtr), JsonObject);
247 }
248 else
249 {
250 ParseAndSetWheeledVehicleParameters(VehiclePtr, JsonObject);
251 }
252
253 HandleSpectatorMovement(JsonObject, Cast<AActor>(VehiclePtr), SpawnTransform);
254 }
255 else
256 {
257 // If Vehicle is nullptr, return
258 return;
259 }
260
261 // Parse possible sensors which will be attached to spawned vehicle
262 const TArray<TSharedPtr<FJsonValue>>* SensorJsonObjects;
263 if (JsonObject->TryGetArrayField("sensors", SensorJsonObjects))
264 {
265 for (const TSharedPtr<FJsonValue>& SensorValue : *SensorJsonObjects)
266 {
267 if (SensorValue->Type == EJson::Object)
268 {
269 TSharedPtr<FJsonObject> SensorJsonObject = SensorValue->AsObject();
270 SpawnSensorToVehicle(VehiclePtr, SensorJsonObject);
271 }
272 }
273 }
274}
275
276void USimulatorJsonParser::ParsePropOrFoliage(const TSharedPtr<FJsonObject>& JsonObject, const FString& ObjectType)
277{
278 FString Model;
279 if (!JsonObject->TryGetStringField("model", Model))
280 {
281 // If json doesn't specifiy model, return
282 return;
283 }
284
285 bool RandomZRotation = false;
286 bool SnapToGround = false;
287 JsonObject->TryGetBoolField("snap_to_ground", SnapToGround);
288 JsonObject->TryGetBoolField("random_z_rotation", RandomZRotation);
289
290 FString Name, ID;
291 JsonObject->TryGetStringField("name", Name);
292 JsonObject->TryGetStringField("id", ID);
293
294 FTransform SpawnTransform = ParseTransform("spawnPoint", JsonObject);
295
296 if (ObjectType.Equals("prop", ESearchCase::IgnoreCase))
297 {
299 UAssetLibrary::SpawnProp(Type, SpawnTransform, Name, ID, RandomZRotation, SnapToGround);
300 }
301 else if (ObjectType.Equals("foliage", ESearchCase::IgnoreCase))
302 {
304 UAssetLibrary::SpawnFoliage(Type, SpawnTransform, Name, ID, RandomZRotation, SnapToGround);
305 }
306}
307
308void USimulatorJsonParser::SpawnSensorToVehicle(AVehicle* Vehicle, const TSharedPtr<FJsonObject>& SensorObject)
309{
310 if (!Vehicle || !SensorObject.IsValid())
311 {
312 return;
313 }
314
315 USensorsManagerComponent* VehicleSensorManager = Vehicle->GetSensorsManager();
316 if (!VehicleSensorManager)
317 {
318 return;
319 }
320
321 TSharedPtr<FJsonObject> ParametersObject = SensorObject->GetObjectField("parameters");
322 if (!ParametersObject.IsValid())
323 {
324 return;
325 }
326
327 // Sensor spawn point (relative)
328 FTransform RelativeTransform = ParseTransform("spawnPoint", SensorObject);
329
330 // Parse ESensorTypes from string
331 FString SensorTypeString;
332 SensorObject->TryGetStringField("model", SensorTypeString);
333 ESensorTypes SensorType = UEnumUtilities::ConvertStringToSensorType(SensorTypeString);
334
335 // It's fine if these are empty,
336 // USensorsManagerComponent will just generate random name and identifier
337 FString SensorIdentifier, Name;
338 SensorObject->TryGetStringField("name", Name);
339 SensorObject->TryGetStringField("id", SensorIdentifier);
340
341 FString AttachedToComponent;
342 SensorObject->TryGetStringField("attachedToComponent", AttachedToComponent);
343
344 FString AttachedToBoneString;
345 FName AttachedToBone;
346 if (SensorObject->TryGetStringField("attachedToBone", AttachedToBoneString))
347 {
348 AttachedToBone = FName(*AttachedToBoneString);
349 }
350
351 ASensorModel* TempActor = nullptr;
352 ASensor* SensorPtr = nullptr;
353 USceneComponent* AttachToComponent = VehicleSensorManager->GetComponentByHierarchyName(AttachedToComponent);
354
355 // Here sensors are spawned through VehicleSensorManager Actor so it can keep track of the spawned sensors.
356 switch (SensorType)
357 {
359 SensorPtr = Cast<ASensor>(VehicleSensorManager->SpawnLidar(RelativeTransform, true, SensorIdentifier, Name, ParseParameters<FLidarParameters>(ParametersObject), true, TempActor, AttachToComponent, AttachedToBone));
360 break;
361
363 SensorPtr = Cast<ASensor>(VehicleSensorManager->SpawnCamera(RelativeTransform, true, SensorIdentifier, Name, ParseParameters<FCameraBaseParameters>(ParametersObject), true, TempActor, AttachToComponent, AttachedToBone));
364 break;
365
367 SensorPtr = Cast<ASensor>(VehicleSensorManager->SpawnThermalCamera(RelativeTransform, true, SensorIdentifier, Name, ParseParameters<FThermalCameraParameters>(ParametersObject), true, TempActor, AttachToComponent, AttachedToBone));
368 break;
369
371 SensorPtr = Cast<ASensor>(VehicleSensorManager->SpawnDepthCamera(RelativeTransform, true, SensorIdentifier, Name, ParseParameters<FDepthCameraParameters>(ParametersObject), true, TempActor, AttachToComponent, AttachedToBone));
372 break;
373
375 SensorPtr = Cast<ASensor>(VehicleSensorManager->SpawnRadar(RelativeTransform, true, SensorIdentifier, Name, ParseParameters<FRadarParameters>(ParametersObject), true, TempActor, AttachToComponent, AttachedToBone));
376 break;
377
379 SensorPtr = Cast<ASensor>(VehicleSensorManager->SpawnSegmentationCamera(RelativeTransform, true, SensorIdentifier, Name, ParseParameters<FCameraBaseParameters>(ParametersObject), true, TempActor, AttachToComponent, AttachedToBone));
380 break;
381
383 SensorPtr = Cast<ASensor>(VehicleSensorManager->SpawnDVSCamera(RelativeTransform, true, SensorIdentifier, Name, ParseParameters<FDVSCameraParameters>(ParametersObject), true, TempActor, AttachToComponent, AttachedToBone));
384 break;
385
386 default:
387 break;
388 }
389
390 // If the vehicle we are attaching is drone,
391 // and the spawned sensor is camera
392 // check if user has given "useGimbal" parameter.
393 if(APIDDrone* DronePtr = Cast<APIDDrone>(Vehicle))
394 {
395 if (ACamera* CameraPtr = Cast<ACamera>(SensorPtr))
396 {
397 bool UseGimbal = false;
398 SensorObject->TryGetBoolField("useGimbal", UseGimbal);
399 CameraPtr->SetUseGimbal(UseGimbal);
400 }
401 }
402}
403
404void USimulatorJsonParser::SpawnSensorWorld(const TSharedPtr<FJsonObject>& SensorObject)
405{
406 if (!SensorObject.IsValid())
407 {
408 return;
409 }
410
411 if (!SensorObject->HasField("parameters") || !SensorObject->HasField("model"))
412 {
413 return;
414 }
415
416 TSharedPtr<FJsonObject> ParametersObject = SensorObject->GetObjectField("parameters");
417 if (!ParametersObject.IsValid())
418 {
419 return;
420 }
421
422 FString SensorTypeString, SensorName, SensorIdentifier;
423 SensorObject->TryGetStringField("model", SensorTypeString);
424 SensorObject->TryGetStringField("name", SensorName);
425 SensorObject->TryGetStringField("id", SensorIdentifier);
426
427 FTransform SpawnTransform = ParseTransform("spawnPoint", SensorObject);
428
429 // Should use Z value as height from the ground instead of given Z value
430 bool ZIsHeightAboveGround = false;
431 SensorObject->TryGetBoolField("zIsHeightAboveGround", ZIsHeightAboveGround);
432 if (ZIsHeightAboveGround)
433 {;
434 ComputeAndSetZFromGround(SpawnTransform);
435 }
436
437 AActor* ActorPtr = nullptr;
438 ESensorTypes SensorType = UEnumUtilities::ConvertStringToSensorType(SensorTypeString);
439
440 UWorld* World = nullptr;
441
442 if (GEngine && GEngine->GameViewport)
443 {
444 World = GEngine->GameViewport->GetWorld();
445 }
446
448 if (!SimulationLevelManager)
449 {
450 SimulatorLog::Log("Failed to spawn Sensor. Reason: Coulnd't to find SimulationLevelManager Actor!");
451 return;
452 }
453
454 USensorsManagerComponent* WorldSensorManager = SimulationLevelManager->GetSensorsManager();
455 if (!WorldSensorManager)
456 {
457 SimulatorLog::Log("Failed to spawn Sensor. Reason: Coulnd't to find USensorsManagerComponent Component!");
458 return;
459 }
460
461 ASensorModel* TempActor = nullptr;
462
463 // Here sensors are spawned through WorldSensorManager Actor so it can keep track of the spawned sensors.
464 switch (SensorType)
465 {
467 ActorPtr = Cast<AActor>(WorldSensorManager->SpawnLidar(SpawnTransform, false, SensorIdentifier, SensorName, ParseParameters<FLidarParameters>(ParametersObject), true, TempActor, nullptr, NAME_None));
468 break;
469
471 ActorPtr = Cast<AActor>(WorldSensorManager->SpawnRadar(SpawnTransform, false, SensorIdentifier, SensorName, ParseParameters<FRadarParameters>(ParametersObject), true, TempActor, nullptr, NAME_None));
472 break;
473
475 ActorPtr = Cast<AActor>(WorldSensorManager->SpawnCamera(SpawnTransform, false, SensorIdentifier, SensorName, ParseParameters<FCameraBaseParameters>(ParametersObject), true, TempActor, nullptr, NAME_None));
476 break;
477
479 ActorPtr = Cast<AActor>(WorldSensorManager->SpawnThermalCamera(SpawnTransform, false, SensorIdentifier, SensorName, ParseThermalCameraParameters(ParametersObject), true, TempActor, nullptr, NAME_None));
480 break;
481
483 ActorPtr = Cast<AActor>(WorldSensorManager->SpawnDepthCamera(SpawnTransform, false, SensorIdentifier, SensorName, ParseParameters<FDepthCameraParameters>(ParametersObject), true, TempActor, nullptr, NAME_None));
484 break;
485
487 ActorPtr = Cast<AActor>(WorldSensorManager->SpawnSegmentationCamera(SpawnTransform, false, SensorIdentifier, SensorName, ParseParameters<FCameraBaseParameters>(ParametersObject), true, TempActor, nullptr, NAME_None));
488 break;
489
491 ActorPtr = Cast<AActor>(WorldSensorManager->SpawnDVSCamera(SpawnTransform, false, SensorIdentifier, SensorName, ParseParameters<FDVSCameraParameters>(ParametersObject), true, TempActor, nullptr, NAME_None));
492 break;
493
494 default:
495 break;
496 }
497
498 if (IsDataCapture)
499 {
500 ACamera* CameraPtr = Cast<ACamera>(ActorPtr);
501 if (CameraPtr)
502 {
503 SpawnedSensorsForDataCapture.Add(Cast<ASensor>(ActorPtr));
504 }
505
506 ALidar* LidarPtr = Cast<ALidar>(ActorPtr);
507 if (LidarPtr)
508 {
509 SpawnedSensorsForDataCapture.Add(Cast<ASensor>(ActorPtr));
510 }
511 }
512
513 bool AttachToSpectator = false;
514 SensorObject->TryGetBoolField("attachToSpectator", AttachToSpectator);
515
516 if (AttachToSpectator)
517 {
519 }
520 else
521 {
522 HandleSpectatorMovement(SensorObject, ActorPtr, SpawnTransform);
523 }
524}
525
526void USimulatorJsonParser::SpawnWalker(const TSharedPtr<FJsonObject>& JsonObject)
527{
528 if (!JsonObject.IsValid())
529 {
530 SimulatorLog::Log("SimulatorJsonParser failed to parse Walker json: Invalid JsonObject");
531 return;
532 }
533
534 FString WalkerTypeString;
535 if (!JsonObject->TryGetStringField("model", WalkerTypeString))
536 {
537 SimulatorLog::Log("SimulatorJsonParser failed to parse Walker json: Model field is missing");
538 return;
539 }
540
541 EWalkerType WalkerType = UEnumUtilities::ConvertStringToWalkerType(WalkerTypeString);
542 if (WalkerType == EWalkerType::NONE)
543 {
544 SimulatorLog::Log("SimulatorJsonParser failed to parse Walker json: Walker is None");
545 return;
546 }
547
548 TSharedPtr<FJsonObject> ParametersObject = JsonObject->GetObjectField("parameters");
549 if (!ParametersObject.IsValid())
550 {
551 SimulatorLog::Log("SimulatorJsonParser failed to parse Walker json: ParametersObject is invalid or missing");
552 return;
553 }
554
555 FString WalkerActionString;
556 if (!ParametersObject->TryGetStringField("walkerAction", WalkerActionString))
557 {
558 SimulatorLog::Log("SimulatorJsonParser failed to parse Walker json: walkerAction field is missing");
559 return;
560 }
561
562 EWalkerAction WalkerAction = UEnumUtilities::ConvertStringToWalkerActionType(WalkerActionString);
563 if (WalkerAction == EWalkerAction::Roaming && !ParametersObject->HasField("spawnPoint"))
564 {
565 SimulatorLog::Log("SimulatorJsonParser failed to parse Walker json: Roaming Walker requires spawnPoint");
566 return;
567 }
568
569 // Retrieve the WalkerEndAction
570 FString WalkerEndActionString;
571 ParametersObject->TryGetStringField("walkerEndAction", WalkerEndActionString);
572 EWalkerEndAction WalkerEndAction = UEnumUtilities::ConvertStringToWalkerEndActionType(WalkerEndActionString);
573
574 // Create and populate WalkerParameters
575 FWalkerParameters WalkerParams;
576 WalkerParams.WalkerType = WalkerType;
577 WalkerParams.WalkerAction = WalkerAction;
578 WalkerParams.WalkerEndAction = WalkerEndAction;
579 WalkerParams.WalkerSpeed = GetIntValueOrDefault(ParametersObject, "walkerSpeed", 50);
580
581 // Parse points or spawnPoint into WalkerParams.Points
582 ParseWalkerSpawnPoints(ParametersObject, WalkerParams);
583
584 // Retrieve optional name and id fields
585 FString Name, ID;
586 ParametersObject->TryGetStringField("name", Name);
587 ParametersObject->TryGetStringField("id", ID);
588
589 // Retrieve the startAutomatically field
590 bool StartAutomatically = true;
591 if (ParametersObject->HasField("startAutomatically"))
592 {
593 ParametersObject->TryGetBoolField("startAutomatically", StartAutomatically);
594 }
595
596 // Finally spawn the Walker
597 AWalker* Walker = UAssetLibrary::SpawnWalker(WalkerParams, Name, ID, StartAutomatically);
598 if (Walker)
599 {
600 HandleSpectatorMovement(JsonObject, Cast<AActor>(Walker), WalkerParams.Points[0]);
601 }
602}
603
604void USimulatorJsonParser::ParseWalkerSpawnPoints(const TSharedPtr<FJsonObject>& ParametersObject, FWalkerParameters& WalkerParams)
605{
606 if (ParametersObject->HasField("points"))
607 {
608 // If the JSON has "points" field, parse those points into WalkerParams.Points
609 TSharedPtr<FJsonObject> PointsObject = ParametersObject->GetObjectField("points");
610 if (PointsObject.IsValid())
611 {
612 int PointIndex = 0;
613 while (true)
614 {
615 FString PointName = FString::Printf(TEXT("point%d"), PointIndex);
616 if (!PointsObject->HasField(PointName))
617 {
618 break;
619 }
620
621 TSharedPtr<FJsonObject> PointObject = PointsObject->GetObjectField(PointName);
622 if (PointObject.IsValid())
623 {
624 FTransform PointTransform = ParseTransform(PointName, PointsObject);
625 WalkerParams.Points.Add(PointTransform);
626 }
627
628 PointIndex++;
629 }
630 }
631 }
632 else if (ParametersObject->HasField("spawnPoint"))
633 {
634 // If the JSON doesn't have "points" field but has "spawnPoint", parse that into WalkerParams.Points
635 FTransform PointTransform = ParseTransform("spawnPoint", ParametersObject);
636 WalkerParams.Points.Add(PointTransform);
637 }
638}
639
640void USimulatorJsonParser::ChangeWeather(const TSharedPtr<FJsonObject>& JsonObject)
641{
642 if (!JsonObject.IsValid())
643 {
644 return;
645 }
646
647 TSharedPtr<FJsonObject> Parameters = JsonObject->GetObjectField("parameters");
648 if (Parameters.IsValid() && GEngine && GEngine->GameViewport)
649 {
650 UWorld* World = GEngine->GameViewport->GetWorld();
651 AWeather* WeatherActor = UAgrarsenseStatics::GetWeatherActor(World);
652 if (WeatherActor)
653 {
654 FWeatherParameters Params = WeatherActor->GetCurrentWeather();
655 FJsonObjectConverter::JsonObjectToUStruct(Parameters.ToSharedRef(), FWeatherParameters::StaticStruct(), &Params);
656 WeatherActor->UpdateWeather(Params, true);
657 }
658 }
659}
660
661static EDroneAction GetDroneAction(FString actionstring)
662{
663 if (actionstring == "stationary") { return EDroneAction::Stationary; }
664 if (actionstring == "manual") { return EDroneAction::Manual; }
665 if (actionstring == "roaming") { return EDroneAction::Roaming; }
666 if (actionstring == "followpath") { return EDroneAction::FollowPath; }
667 else { return EDroneAction::Stationary; }
668}
669
670static EDroneEndAction GetDroneEndAction(FString endActionstring)
671{
672 if (endActionstring == "destroy") { return EDroneEndAction::Destroy; }
673 if (endActionstring == "repeatfrombeginning") { return EDroneEndAction::RepeatFromBeginning; }
674 if (endActionstring == "gobackwards") { return EDroneEndAction::GoBackwards; }
675 if (endActionstring == "generaterandomnew") { return EDroneEndAction::GenerateRandomNew; }
676 if (endActionstring == "stop") { return EDroneEndAction::Stop; }
677 else { return EDroneEndAction::Stop; }
678}
679
680void USimulatorJsonParser::ParseAndSetDroneParameters(APIDDrone* DronePtr, const TSharedPtr<FJsonObject>& JsonObject)
681{
682 if (!DronePtr || !JsonObject.IsValid())
683 {
684 return;
685 }
686
687 TSharedPtr<FJsonObject> ParametersObject = JsonObject->GetObjectField("parameters");
688
689
690 // Should use Z value as height from the ground instead of given Z value
691 bool ZIsHeightAboveGround = false;
692 ParametersObject->TryGetBoolField("zIsHeightAboveGround", ZIsHeightAboveGround);
693
694 if (ParametersObject.IsValid())
695 {
696 FDroneParameters DroneParameters;
697
698 // Attempt to parse FDroneParameters from the json
699 FString DroneActionObject = ParametersObject->GetStringField("droneAction");
700 if (!DroneActionObject.IsEmpty())
701 {
702 EDroneAction droneAction = GetDroneAction(DroneActionObject.ToLower());
703 DroneParameters.DroneAction = droneAction;
704 }
705
706 FString DroneEndActionObject = ParametersObject->GetStringField("droneEndAction");
707 if (!DroneEndActionObject.IsEmpty())
708 {
709 EDroneEndAction droneEndAction = GetDroneEndAction(DroneEndActionObject);
710 DroneParameters.DroneEndAction = droneEndAction;
711 }
712
713 TSharedPtr<FJsonObject> PointsObject = ParametersObject->GetObjectField("points");
714 if (PointsObject.IsValid())
715 {
716 int PointIndex = 0;
717 while (true)
718 {
719 FString PointName = FString::Printf(TEXT("point%d"), PointIndex);
720 if (!PointsObject->HasField(PointName))
721 {
722 break;
723 }
724
725 TSharedPtr<FJsonObject> PointObject = PointsObject->GetObjectField(PointName);
726 if (PointObject.IsValid())
727 {
728 FTransform PointTransform = ParseTransform(PointName, PointsObject);
729
730 if (ZIsHeightAboveGround)
731 {
732 ComputeAndSetZFromGround(PointTransform);
733 }
734
735 DroneParameters.Points.Add(PointTransform);
736 }
737
738 PointIndex++;
739 }
740 }
741
742 bool ShowForwardArrow = false;
743 ParametersObject->TryGetBoolField("showForwardArrow", ShowForwardArrow);
744
745 DronePtr->ChangeDroneParameters(DroneParameters);
746 DronePtr->SetShowForwardArrow(ShowForwardArrow);
747 }
748}
749
750void USimulatorJsonParser::ParseAndSetWheeledVehicleParameters(AVehicle* VehiclePtr, const TSharedPtr<FJsonObject>& JsonObject)
751{
752 if (!VehiclePtr || !JsonObject.IsValid() || !JsonObject->HasField("parameters"))
753 {
754 return;
755 }
756
757 // Get current vehicle parameters
758 FWheeledVehicleParameters Parameters = VehiclePtr->GetVehicleParameters();
759
760 // parse and set new parameters from json
761 TSharedPtr<FJsonObject> ParametersObject = JsonObject->GetObjectField("parameters");
762 Parameters = ParseParameters<FWheeledVehicleParameters>(ParametersObject);
763 VehiclePtr->SetVehicleParameters(Parameters);
764}
765
766void USimulatorJsonParser::HandleSpectatorMovement(const TSharedPtr<FJsonObject>& JsonObject, AActor* Actor, const FTransform& Transform)
767{
768 if (!JsonObject.IsValid() || !Actor)
769 {
770 return;
771 }
772
773 bool follow = false;
774 bool teleport = false;
775 JsonObject->TryGetBoolField("followObject", follow);
776 JsonObject->TryGetBoolField("teleportSpectator", teleport);
777
778 if (!follow && !teleport)
779 {
780 // If neither is set, return
781 return;
782 }
783
784 if (GEngine && GEngine->GameViewport)
785 {
786 UWorld* World = GEngine->GameViewport->GetWorld();
788 if (Spectator)
789 {
790 if (follow)
791 {
792 AVehicle* VehiclePtr = Cast<AVehicle>(Actor);
793 if (VehiclePtr)
794 {
796 if (SimulationLevelManager)
797 {
798 SimulationLevelManager->TakeManualControlOfVehicle(VehiclePtr);
799 }
800 }
801 /*else
802 {
803 Spectator->FollowActor(Actor, false, 150.0f, 50.0f);
804 }*/
805 }
806 else if (teleport)
807 {
808 Spectator->TeleportSpectator(Transform);
809 }
810 }
811 }
812}
813
815{
816 if (!ActorPtr || !GEngine || !GEngine->GameViewport)
817 {
818 return;
819 }
820
821 UWorld* World = GEngine->GameViewport->GetWorld();
822 if (!World)
823 {
824 return;
825 }
826
828 APlayerController* PlayerController = UGameplayStatics::GetPlayerController(World, 0);
829 if (PlayerController && Spectator)
830 {
831 APlayerCameraManager* CameraManager = PlayerController->PlayerCameraManager;
832 if (CameraManager)
833 {
834 USceneComponent* CameraRootComponent = CameraManager->GetRootComponent();
835 if (CameraRootComponent)
836 {
837 // Set initial location and rotation to Spectator location
838 ActorPtr->SetActorLocation(Spectator->GetActorLocation());
839 ActorPtr->SetActorRotation(Spectator->GetActorRotation());
840
841 // Now attach ActorPtr to PlayerCameraManager CameraRootComponent so it follows the camera movement
842 ActorPtr->AttachToComponent(CameraRootComponent, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
843
844 // Delete sensor model from the sensor after attaching to Spectator.
845 ASensor* SensorPtr = Cast<ASensor>(ActorPtr);
846 if (SensorPtr)
847 {
848 auto* SensorModelPtr = SensorPtr->GetSensorModel();
849 if (SensorModelPtr)
850 {
851 SensorModelPtr->Destroy();
852 }
853 }
854 }
855 }
856 }
857}
858
859void USimulatorJsonParser::ParseAndSetSemanticColors(const TSharedPtr<FJsonObject>& JsonObject)
860{
861 UWorld* World = nullptr;
862 if (GEngine && GEngine->GameViewport)
863 {
864 World = GEngine->GameViewport->GetWorld();
865 }
866
867 if (!World)
868 {
869 return;
870 }
871
872 UMaterialParameterCollectionInstance* CollectionInstance = UMaterialParameterCollectionUtilities::LoadMaterialParameterCollectionInstance(World, "/Game/Agrarsense/Data/Semantic/MPC_SemanticCameraColors");
873 if (!CollectionInstance)
874 {
875 SimulatorLog::Log("ROSJsonParser.cpp: Failed to load MaterialParameterCollectionInstance!");
876 return;
877 }
878
879 // Access the "colors" object
880 const TSharedPtr<FJsonObject>* ColorsObjectPtr = nullptr;
881 if (JsonObject->TryGetObjectField(TEXT("colors"), ColorsObjectPtr) && ColorsObjectPtr != nullptr)
882 {
883 const TSharedPtr<FJsonObject>& ColorsObject = *ColorsObjectPtr;
884 for (const auto& Elem : ColorsObject->Values)
885 {
886 FName Type = FName(*Elem.Key);
887 FString VectorString = Elem.Value->AsString();
888
889 // Parse the FVector4 value from the string
890 FVector4 ParsedVector;
891 TArray<FString> ParsedValues;
892 VectorString.ParseIntoArray(ParsedValues, TEXT(","), true);
893 if (ParsedValues.Num() == 4)
894 {
895 ParsedVector.X = FCString::Atof(*ParsedValues[0]);
896 ParsedVector.Y = FCString::Atof(*ParsedValues[1]);
897 ParsedVector.Z = FCString::Atof(*ParsedValues[2]);
898 ParsedVector.W = FCString::Atof(*ParsedValues[3]);
899
900 // try to update the material collection vector value
901 bool success = UMaterialParameterCollectionUtilities::UpdateMaterialCollectionVectorValue(CollectionInstance, Type, ParsedVector);
902 }
903 }
904 }
905}
906
907void USimulatorJsonParser::ParseCommands(const TSharedPtr<FJsonObject>& JsonObject)
908{
909 // Parse json that contains "commands" array of Unreal console commands to execute
910
911 if (!GEngine || !GEngine->GameViewport)
912 {
913 return;
914 }
915
916 UWorld* World = GEngine->GameViewport->GetWorld();
917 if (World)
918 {
919 const TArray<TSharedPtr<FJsonValue>>* CommandsArray;
920 if (JsonObject->TryGetArrayField(TEXT("commands"), CommandsArray))
921 {
922 for (const TSharedPtr<FJsonValue>& CommandValue : *CommandsArray)
923 {
924 FString Command;
925 if (CommandValue->TryGetString(Command))
926 {
927 GEngine->Exec(World, *Command);
928 }
929 }
930 }
931 }
932}
933
934void USimulatorJsonParser::ParseVolumeDeletion(const TSharedPtr<FJsonObject>& JsonObject)
935{
936 if (!GEngine || !GEngine->GameViewport || !JsonObject.IsValid())
937 {
938 return;
939 }
940
941 // Parse spawn location for this volume
942 FTransform SpawnTransform = ParseTransform("spawnPoint", JsonObject);
943
944 // We can use ParseTransform function to parse bounds as the json structure is similar.
945 FTransform TransformBounds = ParseTransform("bounds", JsonObject);
946 FVector Bounds = TransformBounds.GetLocation();
947
948 bool OnlyDestroyTrees = true; // default to only destroy trees
949 JsonObject->TryGetBoolField(TEXT("onlyDestroyTrees"), OnlyDestroyTrees);
950
951 UWorld* World = GEngine->GameViewport->GetWorld();
952
953 if (World)
954 {
955 ADeletionVolume* Volume = World->SpawnActor<ADeletionVolume>(ADeletionVolume::StaticClass(), SpawnTransform);
956 if (Volume)
957 {
958 Volume->ChangeOverlapBounds(Bounds);
959 Volume->DestroyOverlappingActors(OnlyDestroyTrees);
960 }
961 }
962}
963
964void USimulatorJsonParser::ParseDataCapture(const TSharedPtr<FJsonObject>& JsonObject)
965{
966 if (!JsonObject.IsValid() || !GEngine || !GEngine->GameViewport)
967 {
968 return;
969 }
970
971 FCaptureData CaptureData;
972 TSharedPtr<FJsonObject> ParametersObject = JsonObject->GetObjectField("parameters");
973
974 if (ParametersObject.IsValid())
975 {
976 // Should use Z value as height from the ground instead of given Z value
977 bool ZIsHeightAboveGround = false;
978 ParametersObject->TryGetBoolField("zIsHeightAboveGround", ZIsHeightAboveGround);
979
980 bool UseGPS = false;
981 UseGPS = ParametersObject->GetBoolField("useGPSLocation");
982 CaptureData.UseGPSLocation = UseGPS;
983 CaptureData.UseHeightAboveGround = ZIsHeightAboveGround;
984
985 CaptureData.CaptureRotatedViews = ParametersObject->GetBoolField("captureRotatedViews");
986
987 TSharedPtr<FJsonObject> PointsObject = ParametersObject->GetObjectField("points");
988 if (PointsObject.IsValid())
989 {
990 int PointIndex = 0;
991 while (true)
992 {
993 FString PointName = FString::Printf(TEXT("point%d"), PointIndex);
994 if (!PointsObject->HasField(PointName))
995 {
996 break;
997 }
998
999 TSharedPtr<FJsonObject> PointObject = PointsObject->GetObjectField(PointName);
1000 if (PointObject.IsValid())
1001 {
1002 FTransform PointTransform = ParseTransform(PointName, PointsObject);
1003
1004 if (ZIsHeightAboveGround && !UseGPS)
1005 {
1006 ComputeAndSetZFromGround(PointTransform);
1007 }
1008
1009 CaptureData.CapturePositions.Add(PointTransform);
1010 }
1011
1012 PointIndex++;
1013 }
1014 }
1015 }
1016
1017 UWorld* World = GEngine->GameViewport->GetWorld();
1018 if (CaptureData.CapturePositions.Num() != 0 && SpawnedSensorsForDataCapture.Num() != 0 && World)
1019 {
1020
1021 CaptureData.Sensors.Append(SpawnedSensorsForDataCapture);
1022 ADataCapture* DataCaptureActor = World->SpawnActor<ADataCapture>(ADataCapture::StaticClass(), CaptureData.CapturePositions[0]);
1023 DataCaptureActor->SetupDataCapture(CaptureData);
1024 }
1025}
1026
1027FTransform USimulatorJsonParser::ParseTransform(const FString& FieldName, const TSharedPtr<FJsonObject>& Object)
1028{
1029 FTransform SpawnTransform;
1030
1031 const TSharedPtr<FJsonObject>* SpawnPointObject = nullptr;
1032 if (Object->TryGetObjectField(FieldName, SpawnPointObject))
1033 {
1034 double LocationX = 0.0, LocationY = 0.0, LocationZ = 0.0;
1035 double Yaw = 0.0, Pitch = 0.0, Roll = 0.0;
1036
1037 auto& SPO = *SpawnPointObject->Get();
1038
1039 // Attempt to extract location and rotation directly
1040 bool HasLocation = SPO.TryGetNumberField(TEXT("x"), LocationX) && SPO.TryGetNumberField(TEXT("y"), LocationY) && SPO.TryGetNumberField(TEXT("z"), LocationZ);
1041
1042 // Attempt to get rotation information
1043 SPO.TryGetNumberField(TEXT("yaw"), Yaw);
1044 SPO.TryGetNumberField(TEXT("pitch"), Pitch);
1045 SPO.TryGetNumberField(TEXT("roll"), Roll);
1046
1047 // Attempt to get scale information
1048 FVector Scale;
1049 bool ChangeScale = false;
1050 if (SPO.HasField("scaleX") && SPO.HasField("scaleY") && SPO.HasField("scaleZ"))
1051 {
1052 ChangeScale = true;
1053 SPO.TryGetNumberField(TEXT("scaleX"), Scale.X);
1054 SPO.TryGetNumberField(TEXT("scaleY"), Scale.Y);
1055 SPO.TryGetNumberField(TEXT("scaleZ"), Scale.Z);
1056 }
1057
1058 if (HasLocation)
1059 {
1060 //UE_LOG(LogTemp, Warning, TEXT("Has Location!"))
1061 FVector Location(LocationX, LocationY, LocationZ);
1062 FRotator Rotation(Pitch, Yaw, Roll);
1063
1064 SpawnTransform.SetLocation(Location);
1065 SpawnTransform.SetRotation(Rotation.Quaternion());
1066
1067 if (ChangeScale)
1068 {
1069 SpawnTransform.SetScale3D(Scale);
1070 }
1071 }
1072 }
1073
1074 return SpawnTransform;
1075}
1076
1077FVector4 USimulatorJsonParser::ParseVector4(const FString& FieldName, const TSharedPtr<FJsonObject>& Object)
1078{
1079 FVector4 Vector;
1080 const TSharedPtr<FJsonObject>* VectorObject;
1081
1082 if (Object->TryGetObjectField(FieldName, VectorObject))
1083 {
1084 double X, Y, Z, W;
1085 (*VectorObject)->TryGetNumberField(TEXT("x"), X);
1086 (*VectorObject)->TryGetNumberField(TEXT("y"), Y);
1087 (*VectorObject)->TryGetNumberField(TEXT("z"), Z);
1088 (*VectorObject)->TryGetNumberField(TEXT("w"), W);
1089 Vector = FVector4(X, Y, Z, W);
1090 }
1091
1092 return Vector;
1093}
1094
1096{
1097 FThermalCameraParameters ThermalCameraParams;
1098
1099 if (ParametersObject.IsValid())
1100 {
1101 // Parse fields manually because FJsonObjectConverter::JsonObjectToUStruct couldn't parse these properly.
1102 ThermalCameraParams.WarmColor = ParseVector4(TEXT("warmColor"), ParametersObject);
1103 ThermalCameraParams.ColdColor = ParseVector4(TEXT("coldColor"), ParametersObject);
1104 ParametersObject->TryGetBoolField(TEXT("allowCustomNoiseResolution"), ThermalCameraParams.AllowCustomNoiseResolution);
1105 ParametersObject->TryGetNumberField(TEXT("widthResolutionNoise"), ThermalCameraParams.WidthResolutionNoise);
1106 ParametersObject->TryGetNumberField(TEXT("heightResolutionNoise"), ThermalCameraParams.HeightResolutionNoise);
1107
1108 // Parse FCameraBaseParameters
1109 TSharedPtr<FJsonObject> CameraParametersObject = ParametersObject->GetObjectField("cameraParameters");
1110 if (CameraParametersObject.IsValid())
1111 {
1112 ThermalCameraParams.CameraParameters = ParseParameters<FCameraBaseParameters>(CameraParametersObject);
1113 }
1114 }
1115
1116 return ThermalCameraParams;
1117}
1118
1119int32 USimulatorJsonParser::GetIntValueOrDefault(const TSharedPtr<FJsonObject>& JsonObject, const FString& FieldName, int32 DefaultValue)
1120{
1121 int32 Value = DefaultValue;
1122
1123 if (JsonObject.IsValid() && JsonObject->HasField(FieldName))
1124 {
1125 JsonObject->TryGetNumberField(FieldName, Value);
1126 }
1127
1128 return Value;
1129}
1130
1132{
1133 if (!GEngine || !GEngine->GameViewport)
1134 {
1135 return;
1136 }
1137
1138 UWorld* World = GEngine->GameViewport->GetWorld();
1139 if (!World)
1140 {
1141 return;
1142 }
1143
1144 FVector Position = Transform.GetLocation();
1145 FVector Start = Position;
1146 FVector End = Position - FVector(0, 0, 10000); // Trace downwards
1147
1148 FHitResult HitResult;
1149 FCollisionQueryParams QueryParams;
1150
1151 bool Hit = World->LineTraceSingleByChannel(HitResult, Start, End, ECC_Visibility, QueryParams);
1152
1153 if (Hit)
1154 {
1155 float HeightAboveGround = Position.Z;
1156 Position.Z = HitResult.ImpactPoint.Z + (HeightAboveGround * 100.0f);
1157 Transform.SetLocation(Position);
1158 }
1159}
EDroneAction
Definition: DroneAction.h:15
EDroneEndAction
EFoliageTypes
Definition: FoliageTypes.h:15
EPropTypes
Definition: PropTypes.h:15
ESensorTypes
Definition: SensorTypes.h:15
@ SemanticSegmentationCamera
static EDroneEndAction GetDroneEndAction(FString endActionstring)
static EDroneAction GetDroneAction(FString actionstring)
EVehicleTypes
Definition: VehicleTypes.h:15
EWalkerAction
Definition: WalkerAction.h:15
EWalkerEndAction
EWalkerType
Definition: WalkerType.h:16
Definition: Camera.h:53
void SetupDataCapture(FCaptureData NewCaptureData)
Definition: DataCapture.cpp:26
void DestroyOverlappingActors(bool OnlyTrees)
void ChangeOverlapBounds(FVector Bounds)
Definition: Lidar.h:35
void ChangeDroneParameters(const FDroneParameters &newParameters)
Definition: PIDDrone.h:74
void SetShowForwardArrow(bool Show)
Definition: PIDDrone.h:144
Definition: Sensor.h:45
ASensorModel * GetSensorModel() const
Definition: Sensor.h:181
bool TakeManualControlOfVehicle(AVehicle *Vehicle)
USensorsManagerComponent * GetSensorsManager() const
void TeleportSpectator(const FTransform &Transform)
Definition: Spectator.cpp:48
FWheeledVehicleParameters GetVehicleParameters() const
Definition: Vehicle.h:244
void SetVehicleParameters(FWheeledVehicleParameters NewParameters)
Definition: Vehicle.h:234
Definition: Walker.h:28
void UpdateWeather(const FWeatherParameters &WeatherParameters, bool updateToROS)
Definition: Weather.cpp:103
const FWeatherParameters & GetCurrentWeather() const
Definition: Weather.h:76
static void Log(const FString &Message, bool LogToTextFile=true, bool LogToROS=true)
static bool IsPlayingInMainMenu()
static ASpectator * GetSpectator(const UObject *WorldContextObject)
static AWeather * GetWeatherActor(const UObject *WorldContextObject)
static ASimulationLevelManager * GetSimulationLevelManager(const UObject *WorldContextObject)
static AVehicle * SpawnVehicle(EVehicleTypes VehicleType, FTransform SpawnTransform, const FString &ActorName, const FString &ActorID, bool SnapAboveGround=false, float AboveOffset=150.0f, bool DestroyOverlappingActors=false)
static AActor * SpawnProp(EPropTypes PropType, FTransform Transform, FString ActorName="", FString ActorID="", bool RandomZRotation=false, bool SnapToGround=false)
static AWalker * SpawnWalker(FWalkerParameters Parameters, const FString &ActorName="", const FString &ActorID="", bool StartAutomatically=true)
static AActor * SpawnFoliage(EFoliageTypes FoliageType, FTransform Transform, FString ActorName="", FString ActorID="", bool RandomZRotation=true, bool SnapToGround=true)
static EWalkerEndAction ConvertStringToWalkerEndActionType(const FString &String)
static ESensorTypes ConvertStringToSensorType(const FString &String)
static EWalkerAction ConvertStringToWalkerActionType(const FString &String)
static EVehicleTypes ConvertStringToVehicleType(const FString &String)
static EWalkerType ConvertStringToWalkerType(const FString &String)
static EPropTypes ConvertStringToPropType(const FString &String)
static EFoliageTypes ConvertStringToFoliageType(const FString &String)
static bool UpdateMaterialCollectionVectorValue(UMaterialParameterCollectionInstance *MPCInstance, const FName ParameterName, const FVector4 Value)
static UMaterialParameterCollectionInstance * LoadMaterialParameterCollectionInstance(const UObject *WorldContextObject, const FString &Path)
static FTransform ParseTransform(const FString &FieldName, const TSharedPtr< FJsonObject > &Object)
static FVector4 ParseVector4(const FString &FieldName, const TSharedPtr< FJsonObject > &Object)
static void ParseAndSetDroneParameters(APIDDrone *DronePtr, const TSharedPtr< FJsonObject > &JsonObject)
static void ParseAndOperateJSONFile(const FString &Path)
static FThermalCameraParameters ParseThermalCameraParameters(const TSharedPtr< FJsonObject > &ParametersObject)
static void AttachActorToSpectatorCamera(AActor *ActorPtr)
static void ComputeAndSetZFromGround(FTransform &Position)
static TArray< ASensor * > SpawnedSensorsForDataCapture
static void ParsePropOrFoliage(const TSharedPtr< FJsonObject > &JsonObject, const FString &ObjectType)
static void ParseVehicle(const TSharedPtr< FJsonObject > &JsonObject)
static void ParseAndSetWheeledVehicleParameters(AVehicle *VehiclePtr, const TSharedPtr< FJsonObject > &JsonObject)
static void ParseVolumeDeletion(const TSharedPtr< FJsonObject > &JsonObject)
static void ParseDataCapture(const TSharedPtr< FJsonObject > &JsonObject)
static void HandleSpectatorMovement(const TSharedPtr< FJsonObject > &JsonObject, AActor *Actor, const FTransform &Transform)
static void ParseWalkerSpawnPoints(const TSharedPtr< FJsonObject > &ParametersObject, FWalkerParameters &WalkerParams)
static void ParseCommands(const TSharedPtr< FJsonObject > &JsonObject)
static void ParseAndSetSemanticColors(const TSharedPtr< FJsonObject > &JsonObject)
static void ChangeWeather(const TSharedPtr< FJsonObject > &JsonObject)
static void SpawnWalker(const TSharedPtr< FJsonObject > &JsonObject)
static int32 GetIntValueOrDefault(const TSharedPtr< FJsonObject > &JsonObject, const FString &FieldName, int32 DefaultValue)
static void SpawnSensorWorld(const TSharedPtr< FJsonObject > &SensorObject)
static void SpawnSensorToVehicle(AVehicle *Vehicle, const TSharedPtr< FJsonObject > &SensorObject)
bool UseGPSLocation
Definition: DataCapture.h:44
bool UseHeightAboveGround
Definition: DataCapture.h:57
TArray< FTransform > CapturePositions
Definition: DataCapture.h:32
bool CaptureRotatedViews
Definition: DataCapture.h:38
TArray< ASensor * > Sensors
Definition: DataCapture.h:23
EDroneEndAction DroneEndAction
EDroneAction DroneAction
TArray< FTransform > Points
FCameraBaseParameters CameraParameters
EWalkerAction WalkerAction
TArray< FTransform > Points
EWalkerType WalkerType
EWalkerEndAction WalkerEndAction