This is a Regression. Tested in:
//UE5/Release-5.0 CL 18747223 GitHub
//UE4/Release-4.27 CL 18319896 Binary
The example can't be completed in UE5 as expected presumably because of a change within SkeletalMeshMerge.h.
What happens if you rename the Struct?
Renaming the struct to SkelMeshMergeUVTransformMapping2 for example will allow the code to compile. However:
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "UObject/NoExportTypes.h" #include "MeshMergeFunctionLibrary.generated.h" /** * Blueprint equivalent of FSkeleMeshMergeSectionMapping * Info to map all the sections from a single source skeletal mesh to * a final section entry in the merged skeletal mesh. */ USTRUCT(BlueprintType) struct PROJECTNAME_API FSkelMeshMergeSectionMapping_BP { GENERATED_BODY() /** Indices to final section entries of the merged skeletal mesh */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh Merge Params") TArray < int32 > SectionIDs; }; /** * Used to wrap a set of UV Transforms for one mesh. */ USTRUCT(BlueprintType) struct PROJECTNAME_API FSkelMeshMergeUVTransform { GENERATED_BODY() /** A list of how UVs should be transformed on a given mesh, where index represents a specific UV channel. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh Merge Params") TArray < FTransform > UVTransforms; }; /** * Blueprint equivalent of FSkelMeshMergeUVTransforms * Info to map all the sections about how to transform their UVs */ USTRUCT(BlueprintType) struct PROJECTNAME_API FSkelMeshMergeUVTransformMapping { GENERATED_BODY() /** For each UV channel on each mesh, how the UVS should be transformed. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh Merge Params") TArray < FSkelMeshMergeUVTransform > UVTransformsPerMesh; }; /** * Struct containing all parameters used to perform a Skeletal Mesh merge. */ USTRUCT(BlueprintType) struct PROJECTNAME_API FSkeletalMeshMergeParams { GENERATED_BODY() FSkeletalMeshMergeParams() { StripTopLODS = 0; bNeedsCpuAccess = false; bSkeletonBefore = false; Skeleton = nullptr; } // An optional array to map sections from the source meshes to merged section entries UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray < FSkelMeshMergeSectionMapping_BP > MeshSectionMappings; // An optional array to transform the UVs in each mesh UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray < FSkelMeshMergeUVTransformMapping > UVTransformsPerMesh; // The list of skeletal meshes to merge. UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray < USkeletalMesh* > MeshesToMerge; // The number of high LODs to remove from input meshes UPROPERTY(EditAnywhere, BlueprintReadWrite) int32 StripTopLODS; // Whether or not the resulting mesh needs to be accessed by the CPU for any reason (e.g. for spawning particle effects). UPROPERTY(EditAnywhere, BlueprintReadWrite) uint32 bNeedsCpuAccess : 1; // Update skeleton before merge. Otherwise, update after. // Skeleton must also be provided. UPROPERTY(EditAnywhere, BlueprintReadWrite) uint32 bSkeletonBefore : 1; // Skeleton that will be used for the merged mesh. // Leave empty if the generated skeleton is OK. UPROPERTY(EditAnywhere, BlueprintReadOnly) class USkeleton* Skeleton; }; /** * */ UCLASS() class PROJECTNAME_API UMeshMergeFunctionLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() public: /** * Merges the given meshes into a single mesh. * @return The merged mesh (will be invalid if the merge failed). */ UFUNCTION(BlueprintCallable, Category = "Mesh Merge", meta = (UnsafeDuringActorConstruction = "true")) static class USkeletalMesh* MergeMeshes(const FSkeletalMeshMergeParams& Params); };
4. In MeshMergeFunctionLibrary.cpp delete everything then paste in the the code from the example:
// Fill out your copyright notice in the Description page of Project Settings. #include "MeshMergeFunctionLibrary.h" #include "SkeletalMeshMerge.h" #include "Engine/SkeletalMeshSocket.h" #include "Engine/SkeletalMesh.h" #include "Animation/Skeleton.h" static void ToMergeParams(const TArray<FSkelMeshMergeSectionMapping_BP>& InSectionMappings, TArray<FSkelMeshMergeSectionMapping>& OutSectionMappings) { if (InSectionMappings.Num() > 0) { OutSectionMappings.AddUninitialized(InSectionMappings.Num()); for (int32 i = 0; i < InSectionMappings.Num(); ++i) { OutSectionMappings[i].SectionIDs = InSectionMappings[i].SectionIDs; } } }; static void ToMergeParams(const TArray<FSkelMeshMergeUVTransformMapping>& InUVTransformsPerMesh, TArray<FSkelMeshMergeUVTransforms>& OutUVTransformsPerMesh) { if (InUVTransformsPerMesh.Num() > 0) { OutUVTransformsPerMesh.Empty(); OutUVTransformsPerMesh.AddUninitialized(InUVTransformsPerMesh.Num()); for (int32 i = 0; i < InUVTransformsPerMesh.Num(); ++i) { TArray<TArray<FTransform>>& OutUVTransforms = OutUVTransformsPerMesh[i].UVTransformsPerMesh; const TArray<FSkelMeshMergeUVTransform>& InUVTransforms = InUVTransformsPerMesh[i].UVTransformsPerMesh; if (InUVTransforms.Num() > 0) { OutUVTransforms.Empty(); OutUVTransforms.AddUninitialized(InUVTransforms.Num()); for (int32 j = 0; j < InUVTransforms.Num(); j++) { OutUVTransforms[i] = InUVTransforms[i].UVTransforms; } } } } }; USkeletalMesh* UMeshMergeFunctionLibrary::MergeMeshes(const FSkeletalMeshMergeParams& Params) { TArray<USkeletalMesh*> MeshesToMergeCopy = Params.MeshesToMerge; MeshesToMergeCopy.RemoveAll([](USkeletalMesh* InMesh) { return InMesh == nullptr; }); if (MeshesToMergeCopy.Num() <= 1) { UE_LOG(LogTemp, Warning, TEXT("Must provide multiple valid Skeletal Meshes in order to perform a merge.")); return nullptr; } EMeshBufferAccess BufferAccess = Params.bNeedsCpuAccess ? EMeshBufferAccess::ForceCPUAndGPU : EMeshBufferAccess::Default; TArray<FSkelMeshMergeSectionMapping> SectionMappings; TArray<FSkelMeshMergeUVTransforms> UvTransforms; ToMergeParams(Params.MeshSectionMappings, SectionMappings); ToMergeParams(Params.UVTransformsPerMesh, UvTransforms); bool bRunDuplicateCheck = false; USkeletalMesh* BaseMesh = NewObject<USkeletalMesh>(); if (Params.Skeleton && Params.bSkeletonBefore) { BaseMesh->Skeleton = Params.Skeleton; bRunDuplicateCheck = true; for (USkeletalMeshSocket* Socket : BaseMesh->GetMeshOnlySocketList()) { if (Socket) { UE_LOG(LogTemp, Warning, TEXT("SkelMeshSocket: %s"), *(Socket->SocketName.ToString())); } } for (USkeletalMeshSocket* Socket : BaseMesh->Skeleton->Sockets) { if (Socket) { UE_LOG(LogTemp, Warning, TEXT("SkelSocket: %s"), *(Socket->SocketName.ToString())); } } } FSkeletalMeshMerge Merger(BaseMesh, MeshesToMergeCopy, SectionMappings, Params.StripTopLODS, BufferAccess, UvTransforms.GetData()); if (!Merger.DoMerge()) { UE_LOG(LogTemp, Warning, TEXT("Merge failed!")); return nullptr; } if (Params.Skeleton && !Params.bSkeletonBefore) { BaseMesh->Skeleton = Params.Skeleton; } if (bRunDuplicateCheck) { TArray<FName> SkelMeshSockets; TArray<FName> SkelSockets; for (USkeletalMeshSocket* Socket : BaseMesh->GetMeshOnlySocketList()) { if (Socket) { SkelMeshSockets.Add(Socket->GetFName()); UE_LOG(LogTemp, Warning, TEXT("SkelMeshSocket: %s"), *(Socket->SocketName.ToString())); } } for (USkeletalMeshSocket* Socket : BaseMesh->Skeleton->Sockets) { if (Socket) { SkelSockets.Add(Socket->GetFName()); UE_LOG(LogTemp, Warning, TEXT("SkelSocket: %s"), *(Socket->SocketName.ToString())); } } TSet<FName> UniqueSkelMeshSockets; TSet<FName> UniqueSkelSockets; UniqueSkelMeshSockets.Append(SkelMeshSockets); UniqueSkelSockets.Append(SkelSockets); int32 Total = SkelSockets.Num() + SkelMeshSockets.Num(); int32 UniqueTotal = UniqueSkelMeshSockets.Num() + UniqueSkelSockets.Num(); UE_LOG(LogTemp, Warning, TEXT("SkelMeshSocketCount: %d | SkelSocketCount: %d | Combined: %d"), SkelMeshSockets.Num(), SkelSockets.Num(), Total); UE_LOG(LogTemp, Warning, TEXT("SkelMeshSocketCount: %d | SkelSocketCount: %d | Combined: %d"), UniqueSkelMeshSockets.Num(), UniqueSkelSockets.Num(), UniqueTotal); UE_LOG(LogTemp, Warning, TEXT("Found Duplicates: %s"), *((Total != UniqueTotal) ? FString("True") : FString("False"))); } return BaseMesh; }
5. Save all in Visual Studio
6. In the Editor press Recompile and Reload in the bottom right corner
Expected Results:
The Code compiles and the user can continue to follow along with the example.
Actual Results:
The Code fails to compile stating "Error: Duplicate struct name: SkelMeshMergeUVTransformMapping also exists in file E:\Git\UE5Rel-18747223\Engine\Source\Runtime\Engine\Public\SkeletalMeshMerge.h"
Request a info about UE-127172 bug tracker
How do I set a material as a post-processing material?
How does TextureRenderTarget2D get TArray<uint8> type data?
Why does the REMOVE method of map container remove elements have memory leaks?
I want to call when different components get hit on a multi cylinder target actor.
How to delete some elements correctly when deleting an array loop?
UMG RichText not appear image when packaged
There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-142182 in the post.