From 359806f9b0d0b66a41edfc886fdc5df1f602bd84 Mon Sep 17 00:00:00 2001 From: Axel Date: Thu, 30 Mar 2023 12:25:11 +0200 Subject: [PATCH] Added importer UI for usd vehicles --- .../Python/add_vehicle_to_vehicle_factory.py | 41 +++ ... => UW_USDPropImporterEditorWidget.uasset} | Bin 151757 -> 147737 bytes .../UW_USDVehicleImporterEditorWidget.uasset | Bin 0 -> 154409 bytes .../CarlaTools/Private/USDImporterWidget.cpp | 299 ++++++++++++++++++ .../CarlaTools/Public/USDImporterWidget.h | 111 ++++++- 5 files changed, 450 insertions(+), 1 deletion(-) create mode 100644 Unreal/CarlaUE4/Plugins/CarlaTools/Content/Python/add_vehicle_to_vehicle_factory.py rename Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/{UW_USDImporterEditorWidget.uasset => UW_USDPropImporterEditorWidget.uasset} (66%) create mode 100644 Unreal/CarlaUE4/Plugins/CarlaTools/Content/USDImporter/UW_USDVehicleImporterEditorWidget.uasset 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 baed9686ffc908732d6f7a195c0f3f48439fe7a4..1c7f1c4531764f58ab569061026916e6c230ec40 100644 GIT binary patch delta 3629 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 delta 7422 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 Ifp~}i8*N6al>h($ 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 0000000000000000000000000000000000000000..29fcb80e48a8e6e56efb594b6db982693dd05d22 GIT binary patch literal 154409 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 +#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); + };