diff --git a/Unreal/CarlaUE4/Plugins/CarlaTools/Content/Python/add_vehicle_to_vehicle_factory.py b/Unreal/CarlaUE4/Plugins/CarlaTools/Content/Python/add_vehicle_to_vehicle_factory.py new file mode 100644 index 000000000..0faddced6 --- /dev/null +++ b/Unreal/CarlaUE4/Plugins/CarlaTools/Content/Python/add_vehicle_to_vehicle_factory.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +""" +Script to add new vehicles to the vehicle factory and json file +""" +import unreal +import argparse +import json + +argparser = argparse.ArgumentParser() +argparser.add_argument( + '-v', '--vehicle_blueprint_path', + metavar='V', + default='', + type=str, + help='Path to add to the vehicle blueprint') +argparser.add_argument( + '-n', '--name', + metavar='N', + default='', + type=str, + help='vehicle name') +args = argparser.parse_args() + +# load vehicle and factory +vehicle_factory_path = '/Game/Carla/Blueprints/Vehicles/VehicleFactory.VehicleFactory_C' +vehicle_factory_class = unreal.load_object(None, vehicle_factory_path) +vehicle_factory_default_object = unreal.get_default_object(vehicle_factory_class) +vehicle_blueprint_path = args.vehicle_blueprint_path + '_C' +vehicle_blueprint = unreal.load_object(None, vehicle_blueprint_path) +vehicle_list = vehicle_factory_default_object.get_editor_property("Vehicles") + +# generate the new field +new_vehicle_parameters = unreal.VehicleParameters() +new_vehicle_parameters.make = 'usd' +new_vehicle_parameters.model = args.name +new_vehicle_parameters.class_ = vehicle_blueprint +new_vehicle_parameters.generation = 2 +new_vehicle_parameters.base_type = 'car' + +# add to the vehicle list +vehicle_list.append(new_vehicle_parameters) diff --git a/Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDImporterEditorWidget.uasset b/Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDPropImporterEditorWidget.uasset similarity index 66% rename from Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDImporterEditorWidget.uasset rename to Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDPropImporterEditorWidget.uasset index baed9686f..1c7f1c453 100644 Binary files a/Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDImporterEditorWidget.uasset and b/Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDPropImporterEditorWidget.uasset differ diff --git a/Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDVehicleImporterEditorWidget.uasset b/Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDVehicleImporterEditorWidget.uasset new file mode 100644 index 000000000..29fcb80e4 Binary files /dev/null and b/Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDVehicleImporterEditorWidget.uasset differ diff --git a/Unreal/CarlaUE4/Plugins/CarlaTools/Source/CarlaTools/Private/USDImporterWidget.cpp b/Unreal/CarlaUE4/Plugins/CarlaTools/Source/CarlaTools/Private/USDImporterWidget.cpp index 868f383f8..cbb0b6591 100644 --- a/Unreal/CarlaUE4/Plugins/CarlaTools/Source/CarlaTools/Private/USDImporterWidget.cpp +++ b/Unreal/CarlaUE4/Plugins/CarlaTools/Source/CarlaTools/Private/USDImporterWidget.cpp @@ -9,6 +9,16 @@ #include "IMeshMergeUtilities.h" #include "MeshMergeModule.h" #include "Components/PrimitiveComponent.h" +#include "Components/StaticMeshComponent.h" +#include "Kismet/KismetSystemLibrary.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Engine/Blueprint.h" +#include "ReferenceSkeleton.h" +#include "Components/SkeletalMeshComponent.h" +#include "PackageHelperFunctions.h" +#include "EditorAssetLibrary.h" +#include +#include void UUSDImporterWidget::ImportUSDProp( @@ -68,3 +78,292 @@ bool UUSDImporterWidget::MergeStaticMeshComponents( MeshUtilities.MergeComponentsToStaticMesh(ComponentsToMerge, World, MeshMergeSettings, nullptr, nullptr, DestMesh, AssetsToSync, NewLocation, ScreenAreaSize, true); return true; } + +TArray UUSDImporterWidget::MergeMeshComponents( + TArray ComponentsToMerge, + const FString& DestMesh) +{ + if(!ComponentsToMerge.Num()) + { + return {}; + } + UWorld* World = ComponentsToMerge[0]->GetWorld(); + const IMeshMergeUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked("MeshMergeUtilities").GetUtilities(); + FMeshMergingSettings MeshMergeSettings; + TArray AssetsToSync; + const float ScreenAreaSize = TNumericLimits::Max(); + FVector NewLocation; + MeshUtilities.MergeComponentsToStaticMesh(ComponentsToMerge, World, MeshMergeSettings, nullptr, nullptr, DestMesh, AssetsToSync, NewLocation, ScreenAreaSize, true); + return AssetsToSync; +} + +bool IsChildrenOf(USceneComponent* Component, FString StringInParent) +{ + USceneComponent* CurrentComponent = Component; + while(CurrentComponent) + { + FString ComponentName = UKismetSystemLibrary::GetDisplayName(CurrentComponent); + if(ComponentName.Contains(StringInParent)) + { + return true; + } + CurrentComponent = CurrentComponent->GetAttachParent(); + } + return false; +} + +FVehicleMeshParts UUSDImporterWidget::SplitVehicleParts(AActor* BlueprintActor) +{ + FVehicleMeshParts Result; + TArray MeshComponents; + BlueprintActor->GetComponents(MeshComponents, false); + FVector BodyLocation = FVector(0,0,0); + for (UStaticMeshComponent* Component : MeshComponents) + { + if (!Component->GetStaticMesh()) + { + continue; + } + FString ComponentName = UKismetSystemLibrary::GetDisplayName(Component); + if (ComponentName.Contains("door_0")) + { + Result.DoorFL.Add(Component); + Result.Anchors.DoorFL = Component->GetComponentTransform().GetLocation(); + } + else if (ComponentName.Contains("door_1")) + { + Result.DoorFR.Add(Component); + Result.Anchors.DoorFR = Component->GetComponentTransform().GetLocation(); + } + else if (ComponentName.Contains("door_2")) + { + Result.DoorRL.Add(Component); + Result.Anchors.DoorRL = Component->GetComponentTransform().GetLocation(); + } + else if (ComponentName.Contains("door_3")) + { + Result.DoorRR.Add(Component); + Result.Anchors.DoorRR = Component->GetComponentTransform().GetLocation(); + } + else if (ComponentName.Contains("trunk")) + { + Result.Trunk.Add(Component); + Result.Anchors.Trunk = Component->GetComponentTransform().GetLocation(); + } + else if (ComponentName.Contains("hood")) + { + Result.Hood.Add(Component); + Result.Anchors.Hood = Component->GetComponentTransform().GetLocation(); + } + else if (IsChildrenOf(Component, "suspension_0")) + { + Result.WheelFL.Add(Component); + if (ComponentName.Contains("wheel")) + { + Result.Anchors.WheelFL = Component->GetComponentTransform().GetLocation(); + } + } + else if (IsChildrenOf(Component, "suspension_1")) + { + Result.WheelFR.Add(Component); + if (ComponentName.Contains("wheel")) + { + Result.Anchors.WheelFR = Component->GetComponentTransform().GetLocation(); + } + } + else if (IsChildrenOf(Component, "suspension_2")) + { + Result.WheelRL.Add(Component); + if (ComponentName.Contains("wheel")) + { + Result.Anchors.WheelRL = Component->GetComponentTransform().GetLocation(); + } + } + else if (IsChildrenOf(Component, "suspension_3")) + { + Result.WheelRR.Add(Component); + if (ComponentName.Contains("wheel")) + { + Result.Anchors.WheelRR = Component->GetComponentTransform().GetLocation(); + } + } + else if (ComponentName.Contains("Collision")) + { + + } + else + { + Result.Body.Add(Component); + if (ComponentName.Contains("body")) + { + BodyLocation = Component->GetComponentTransform().GetLocation(); + } + } + } + Result.Anchors.DoorFR -= BodyLocation; + Result.Anchors.DoorFL -= BodyLocation; + Result.Anchors.DoorRR -= BodyLocation; + Result.Anchors.DoorRL -= BodyLocation; + Result.Anchors.WheelFR -= BodyLocation; + Result.Anchors.WheelFL -= BodyLocation; + Result.Anchors.WheelRR -= BodyLocation; + Result.Anchors.WheelRL -= BodyLocation; + Result.Anchors.Hood -= BodyLocation; + Result.Anchors.Trunk -= BodyLocation; + return Result; +} + +FMergedVehicleMeshParts UUSDImporterWidget::GenerateVehicleMeshes( + const FVehicleMeshParts& VehicleMeshParts, const FString& DestPath) +{ + FMergedVehicleMeshParts Result; + auto MergePart = + [](TArray Components, const FString& DestMeshPath) + -> UStaticMesh* + { + if (!Components.Num()) + { + return nullptr; + } + TArray Output = MergeMeshComponents(Components, DestMeshPath); + if (Output.Num()) + { + return Cast(Output[0]); + } + else + { + return nullptr; + } + }; + Result.DoorFR = MergePart(VehicleMeshParts.DoorFR, DestPath + "_door_fr"); + Result.DoorFL = MergePart(VehicleMeshParts.DoorFL, DestPath + "_door_fl"); + Result.DoorRR = MergePart(VehicleMeshParts.DoorRR, DestPath + "_door_rr"); + Result.DoorRL = MergePart(VehicleMeshParts.DoorRL, DestPath + "_door_rl"); + Result.Trunk = MergePart(VehicleMeshParts.Trunk, DestPath + "_trunk"); + Result.Hood = MergePart(VehicleMeshParts.Hood, DestPath + "_hood"); + Result.WheelFR = MergePart(VehicleMeshParts.WheelFR, DestPath + "_wheel_fr"); + Result.WheelFL = MergePart(VehicleMeshParts.WheelFL, DestPath + "_wheel_fl"); + Result.WheelRR = MergePart(VehicleMeshParts.WheelRR, DestPath + "_wheel_rr"); + Result.WheelRL = MergePart(VehicleMeshParts.WheelRL, DestPath + "_wheel_rl"); + Result.Body = MergePart(VehicleMeshParts.Body, DestPath + "_body"); + Result.Anchors = VehicleMeshParts.Anchors; + return Result; +} + +AActor* UUSDImporterWidget::GenerateNewVehicleBlueprint( + UWorld* World, + UClass* BaseClass, + USkeletalMesh* NewSkeletalMesh, + const FString &DestPath, + const FMergedVehicleMeshParts& VehicleMeshes) +{ + std::unordered_map> MeshMap = { + {"SM_DoorFR", {VehicleMeshes.DoorFR, VehicleMeshes.Anchors.DoorFR}}, + {"SM_DoorFL", {VehicleMeshes.DoorFL, VehicleMeshes.Anchors.DoorFL}}, + {"SM_DoorRR", {VehicleMeshes.DoorRR, VehicleMeshes.Anchors.DoorRR}}, + {"SM_DoorRL", {VehicleMeshes.DoorRL, VehicleMeshes.Anchors.DoorRL}}, + {"Trunk", {VehicleMeshes.Trunk, VehicleMeshes.Anchors.Trunk}}, + {"Hood", {VehicleMeshes.Hood, VehicleMeshes.Anchors.Hood}}, + {"Wheel_FR", {VehicleMeshes.WheelFR, FVector(0,0,0)}}, + {"Wheel_FL", {VehicleMeshes.WheelFL, FVector(0,0,0)}}, + {"Wheel_RR", {VehicleMeshes.WheelRR, FVector(0,0,0)}}, + {"Wheel_RL", {VehicleMeshes.WheelRL, FVector(0,0,0)}}, + {"Body", {VehicleMeshes.Body, FVector(0,0,0)}} + }; + + AActor* TemplateActor = World->SpawnActor(BaseClass); + // Get an replace all static meshes with the appropiate mesh + TArray MeshComponents; + TemplateActor->GetComponents(MeshComponents); + for (UStaticMeshComponent* Component : MeshComponents) + { + std::string ComponentName = TCHAR_TO_UTF8(*Component->GetName()); + auto &MapElement = MeshMap[ComponentName]; + UStaticMesh* ComponentMesh = MapElement.first; + FVector MeshLocation = MapElement.second; + if(ComponentMesh) + { + Component->SetStaticMesh(ComponentMesh); + Component->SetRelativeLocation(MeshLocation); + } + UE_LOG(LogCarlaTools, Log, TEXT("Component name %s, name %s"), + *UKismetSystemLibrary::GetDisplayName(Component), *Component->GetName()); + } + + // Get the skeletal mesh and modify it to match the vehicle parameters + USkeletalMeshComponent* SkeletalMeshComponent = Cast( + TemplateActor->GetComponentByClass(USkeletalMeshComponent::StaticClass())); + if(!SkeletalMeshComponent) + { + UE_LOG(LogCarlaTools, Log, TEXT("Skeletal mesh component not found")); + return nullptr; + } + USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->SkeletalMesh; + TMap NewBoneTransform = { + {"Wheel_Front_Left", FTransform(VehicleMeshes.Anchors.WheelFL)}, + {"Wheel_Front_Right", FTransform(VehicleMeshes.Anchors.WheelFR)}, + {"Wheel_Rear_Right", FTransform(VehicleMeshes.Anchors.WheelRR)}, + {"Wheel_Rear_Left", FTransform(VehicleMeshes.Anchors.WheelRL)} + }; + if(!SkeletalMesh) + { + UE_LOG(LogCarlaTools, Log, TEXT("Mesh not generated, skeletal mesh missing")); + return nullptr; + } + bool bSuccess = EditSkeletalMeshBones(NewSkeletalMesh, NewBoneTransform); + if (!NewSkeletalMesh || !bSuccess) + { + UE_LOG(LogCarlaTools, Log, TEXT("Blueprint generation error")); + return nullptr; + } + SkeletalMeshComponent->SetSkeletalMesh(NewSkeletalMesh); + + // Create the new blueprint vehicle + FKismetEditorUtilities::FCreateBlueprintFromActorParams Params; + Params.bReplaceActor = true; + Params.bKeepMobility = true; + Params.bDeferCompilation = false; + Params.bOpenBlueprint = false; + Params.ParentClassOverride = BaseClass; + FKismetEditorUtilities::CreateBlueprintFromActor( + DestPath, + TemplateActor, + Params); + return nullptr; +} + +bool UUSDImporterWidget::EditSkeletalMeshBones( + USkeletalMesh* NewSkeletalMesh, + const TMap &NewBoneTransforms) +{ + if(!NewSkeletalMesh) + { + UE_LOG(LogCarlaTools, Log, TEXT("Skeletal mesh invalid")); + return false; + } + FReferenceSkeleton& ReferenceSkeleton = NewSkeletalMesh->RefSkeleton; + FReferenceSkeletonModifier SkeletonModifier(ReferenceSkeleton, NewSkeletalMesh->Skeleton); + for (auto& Element : NewBoneTransforms) + { + const FString& BoneName = Element.Key; + const FTransform& BoneTransform = Element.Value; + int32 BoneIdx = SkeletonModifier.FindBoneIndex(FName(*BoneName)); + if (BoneIdx == INDEX_NONE) + { + UE_LOG(LogCarlaTools, Log, TEXT("Bone %s not found"), *BoneName); + } + UE_LOG(LogCarlaTools, Log, TEXT("Bone %s corresponds to index %d"), *BoneName, BoneIdx); + SkeletonModifier.UpdateRefPoseTransform(BoneIdx, BoneTransform); + } + + // UE_LOG(LogCarlaTools, Log, TEXT("Creating new skeletal mesh in path %s"), *PackagePath); + // UPackage* NewPackage = CreatePackage(nullptr, *PackagePath); + // UObject* NewObject = DuplicateObject(Skeleton, NewPackage); + // SavePackageHelper(NewPackage, *PackagePath); + NewSkeletalMesh->MarkPackageDirty(); + UPackage* Package = NewSkeletalMesh->GetOutermost(); + return UPackage::SavePackage( + Package, NewSkeletalMesh, + EObjectFlags::RF_Public | EObjectFlags::RF_Standalone, + *(Package->GetName()), GError, nullptr, true, true, SAVE_NoError); +} diff --git a/Unreal/CarlaUE4/Plugins/CarlaTools/Source/CarlaTools/Public/USDImporterWidget.h b/Unreal/CarlaUE4/Plugins/CarlaTools/Source/CarlaTools/Public/USDImporterWidget.h index fedc8be17..be4eccf9a 100644 --- a/Unreal/CarlaUE4/Plugins/CarlaTools/Source/CarlaTools/Public/USDImporterWidget.h +++ b/Unreal/CarlaUE4/Plugins/CarlaTools/Source/CarlaTools/Public/USDImporterWidget.h @@ -4,15 +4,106 @@ #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" +#include "Animation/Skeleton.h" #include "USDImporterWidget.generated.h" +USTRUCT(BlueprintType) +struct CARLATOOLS_API FVehicleMeshAnchorPoints +{ + GENERATED_BODY(); + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector DoorFR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector DoorFL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector DoorRR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector DoorRL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector WheelFR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector WheelFL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector WheelRR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector WheelRL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector Hood; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVector Trunk; + +}; + +USTRUCT(BlueprintType) +struct CARLATOOLS_API FVehicleMeshParts +{ + GENERATED_BODY(); + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray DoorFR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray DoorFL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray DoorRR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray DoorRL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray Trunk; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray Hood; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray WheelFR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray WheelFL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray WheelRR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray WheelRL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + TArray Body; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVehicleMeshAnchorPoints Anchors; +}; + +USTRUCT(BlueprintType) +struct CARLATOOLS_API FMergedVehicleMeshParts +{ + GENERATED_BODY(); + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* DoorFR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* DoorFL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* DoorRR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* DoorRL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* Trunk; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* Hood; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* WheelFR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* WheelFL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* WheelRR; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* WheelRL; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + UStaticMesh* Body; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="USD Importer") + FVehicleMeshAnchorPoints Anchors; +}; + UCLASS() class CARLATOOLS_API UUSDImporterWidget : public UUserWidget { GENERATED_BODY() - public: +public: UFUNCTION(BlueprintCallable, Category="USD Importer") void ImportUSDProp(const FString& USDPath, const FString& DestinationAssetPath, bool bAsBlueprint = true); @@ -22,4 +113,22 @@ class CARLATOOLS_API UUSDImporterWidget : public UUserWidget static AActor* GetGeneratedBlueprint(UWorld* World, const FString& USDPath); UFUNCTION(BlueprintCallable, Category="USD Importer") static bool MergeStaticMeshComponents(TArray Actors, const FString& DestMesh); + UFUNCTION(BlueprintCallable, Category="USD Importer") + static TArray MergeMeshComponents(TArray Components, const FString& DestMesh); + UFUNCTION(BlueprintCallable, Category="USD Importer") + static FVehicleMeshParts SplitVehicleParts(AActor* BlueprintActor); + UFUNCTION(BlueprintCallable, Category="USD Importer") + static FMergedVehicleMeshParts GenerateVehicleMeshes(const FVehicleMeshParts& VehicleMeshParts, const FString& DestPath); + UFUNCTION(BlueprintCallable, Category="USD Importer") + static AActor* GenerateNewVehicleBlueprint( + UWorld* World, + UClass* BaseClass, + USkeletalMesh* NewSkeletalMesh, + const FString &DestPath, + const FMergedVehicleMeshParts& VehicleMeshes); + UFUNCTION(BlueprintCallable, Category="USD Importer") + static bool EditSkeletalMeshBones( + USkeletalMesh* Skeleton, + const TMap &NewBoneTransforms); + };