diff --git a/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h b/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h index 9e0c96c4..84426fd7 100644 --- a/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h +++ b/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h @@ -145,7 +145,7 @@ public: FORCEINLINE const FName& GetGrabSocketName() const { - return GetVirtualHandSettings().GrabSettings.PalmAttachPointSocketName; + return GetVirtualHandSettings().GrabSettings.GrabAttachPointSocketName; } FORCEINLINE const FVector& GetDefaultFingertipsGrabColliderSize() const diff --git a/Source/SenseGloveEditor/Private/SGEditor/SGAssetUtils.cpp b/Source/SenseGloveEditor/Private/SGEditor/SGAssetUtils.cpp new file mode 100644 index 00000000..87bd98d2 --- /dev/null +++ b/Source/SenseGloveEditor/Private/SGEditor/SGAssetUtils.cpp @@ -0,0 +1,1051 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2023 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGEditor/SGAssetUtils.h" + +#include "Animation/DebugSkelMeshComponent.h" +#include "Animation/Skeleton.h" +#include "Containers/Array.h" +#include "Containers/UnrealString.h" +#include "Engine/SkeletalMesh.h" +#include "Engine/SkeletalMeshSocket.h" +#include "HeadMountedDisplayTypes.h" +#include "IEditableSkeleton.h" +#include "Internationalization/Text.h" +#include "IPersonaToolkit.h" +#include "ISkeletalMeshEditorModule.h" +#include "ISkeletonEditorModule.h" +#include "Misc/AssertionMacros.h" +#include "Misc/CoreMiscDefines.h" +#include "ReferenceSkeleton.h" +#include "Rendering/SkeletalMeshLODRenderData.h" +#include "Rendering/SkeletalMeshRenderData.h" +#include "Subsystems/AssetEditorSubsystem.h" +#include "UObject/Object.h" +#include "UObject/UnrealNames.h" + +#include "SGLog/SGLog.h" +#include "SGSettings/SGSettings.h" + +struct FSGAssetUtils::FImpl +{ + /************************ + * Static methods + ************************/ + + FORCEINLINE static const FSGVirtualHandSettings& GetVirtualHandSettings() + { + const USGSettings* Settings{USGSettings::GetInstance()}; + ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!")); + const FSGVirtualHandSettings& VirtualHandSettings{Settings->GetVirtualHandSettings()}; + return VirtualHandSettings; + } + + FORCEINLINE static const FSGVirtualHandGrabSettings& GetVirtualHandGrabSettings() + { + const FSGVirtualHandSettings& VirtualHandSettings{GetVirtualHandSettings()}; + return VirtualHandSettings.GrabSettings; + } + + FORCEINLINE static const FSGVirtualHandMeshSettings& GetVirtualHandMeshSettings() + { + const FSGVirtualHandSettings& VirtualHandSettings{GetVirtualHandSettings()}; + return VirtualHandSettings.MeshSettings; + } + + FORCEINLINE static const FSGVirtualHandTouchSettings& GetVirtualHandTouchSettings() + { + const FSGVirtualHandSettings& VirtualHandSettings{GetVirtualHandSettings()}; + return VirtualHandSettings.TouchSettings; + } + + static bool IsHandBoneWeighted( + const ISkeletalMeshEditor* SkeletalMeshEditor, const USkeletalMesh* SkeletalMesh, const FName& BoneName); + + static FName GetHandBoneName(const ISkeletalMeshEditor* SkeletalMeshEditor, const USkeletalMesh* SkeletalMesh); + + static bool IsLeftHandMesh(const ISkeletalMeshEditor* SkeletalMeshEditor, const USkeletalMesh* SkeletalMesh); + static bool IsRightHandMesh(const ISkeletalMeshEditor* SkeletalMeshEditor, const USkeletalMesh* SkeletalMesh); + + static FTransform GetHandBoneBoneRefTransform(bool bRight, const FName& BoneName); + + static bool AddSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, + const FName& BoneName, const FName& SocketName, const FTransform& SocketTransform); + + static bool AddGrabAttachPointSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, bool bRight); + + static bool AddFingertipColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, + bool bRight, EHandKeypoint DistalKeypoint, const FName& SocketName); + + static bool AddGrabThumbColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, bool bRight); + static bool AddGrabIndexColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, bool bRight); + static bool AddGrabMiddleColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, bool bRight); + + static bool AddTouchThumbColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, bool bRight); + static bool AddTouchIndexColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, bool bRight); + static bool AddTouchMiddleColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, bool bRight); + static bool AddTouchRingColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, bool bRight); + static bool AddTouchLittleColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, bool bRight); + + static bool AddSenseGloveSockets(const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh); + + static bool AddSenseGloveSockets(USkeletalMesh* SkeletalMesh, USkeleton* Skeleton); + static bool ClearExistingSockets(USkeletalMesh* SkeletalMesh, USkeleton* Skeleton); +}; + +bool FSGAssetUtils::FImpl::IsHandBoneWeighted( + const ISkeletalMeshEditor* SkeletalMeshEditor, const USkeletalMesh* SkeletalMesh, const FName& BoneName) +{ + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + if (!ensureAlwaysMsgf(BoneName != NAME_None, TEXT("Invalid BoneName!"))) + { + return false; + } + + const FReferenceSkeleton& RefSkeleton{SkeletalMesh->GetRefSkeleton()}; + const int32 BoneIndex = RefSkeleton.FindBoneIndex(BoneName); + + // MeshBoneIndex must be an index into the mesh's skeleton, *not* the source skeleton!!! + if (!ensureAlwaysMsgf(BoneIndex != INDEX_NONE, TEXT("Invalid BoneIndex!"))) + { + // If we get an invalid index, we are done here. + return false; + } + + const FSkeletalMeshRenderData* ResourceForRendering{SkeletalMesh->GetResourceForRendering()}; + if (!ensureAlwaysMsgf(ResourceForRendering, TEXT("Invalid ResourceForRendering!"))) + { + // If there's no mesh, then this bone can't possibly be weighted! + return false; + } + + const TIndirectArray& LODRenderData{ResourceForRendering->LODRenderData}; + const int32 LODRenderDataNum{LODRenderData.Num()}; + if (!ensureAlwaysMsgf(LODRenderDataNum > 0, TEXT("Invalid LODRenderDataNum!"))) + { + // If there's no mesh, then this bone can't possibly be weighted! + return false; + } + + const TSharedRef PersonaToolkit{SkeletalMeshEditor->GetPersonaToolkit()}; + const UDebugSkelMeshComponent* PreviewMeshComponent{PersonaToolkit->GetPreviewMeshComponent()}; + if (!ensureAlwaysMsgf(PreviewMeshComponent, TEXT("Invalid PreviewMeshComponent!"))) + { + return false; + } + + const int32 PredictedLODLevel = PreviewMeshComponent->GetPredictedLODLevel(); + + const USkeletalMesh* PreviewSkeletalMeshAsset{PreviewMeshComponent->GetSkeletalMeshAsset()}; + if (!ensureAlwaysMsgf(IsValid(PreviewSkeletalMeshAsset), TEXT("Invalid PreviewSkeletalMeshAsset!"))) + { + return false; + } + + const FSkeletalMeshRenderData* PreviewResourceForRendering{PreviewSkeletalMeshAsset->GetResourceForRendering()}; + if (!ensureAlwaysMsgf(PreviewResourceForRendering, TEXT("Invalid PreviewResourceForRendering!"))) + { + return false; + } + + const TIndirectArray& PreviewLODRenderData{PreviewResourceForRendering->LODRenderData}; + const int32 PreviewLODRenderDataNum{PreviewLODRenderData.Num()}; + if (!ensureAlwaysMsgf(LODRenderDataNum > 0, TEXT("Invalid LODRenderDataNum!"))) + { + return false; + } + + const int32 MaxPreviewLODRenderDataNum = PreviewLODRenderDataNum - 1; + + // Get current LOD + const int32 LODIndex = FMath::Clamp(PredictedLODLevel, 0, MaxPreviewLODRenderDataNum); + const FSkeletalMeshLODRenderData& LODData{PreviewLODRenderData[LODIndex]}; + + // Check whether the bone is vertex weighted + const int32 Index = LODData.ActiveBoneIndices.Find(IntCastChecked(BoneIndex)); + + return Index != INDEX_NONE; +} + +FName FSGAssetUtils::FImpl::GetHandBoneName( + const ISkeletalMeshEditor* SkeletalMeshEditor, const USkeletalMesh* SkeletalMesh) +{ + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return NAME_None; + } + + static const FString HandBonePrefix{TEXT("hand_")}; + + const FReferenceSkeleton& RefSkeleton{SkeletalMesh->GetRefSkeleton()}; + const int32 BoneNum = RefSkeleton.GetNum(); + + for (int i = 0; i < BoneNum; ++i) + { + const FName BoneName{RefSkeleton.GetBoneName(i)}; + const FString BoneNameString{BoneName.ToString()}; + + if (!BoneNameString.StartsWith(HandBonePrefix)) + { + continue; + } + + if (!IsHandBoneWeighted(SkeletalMeshEditor, SkeletalMesh, BoneName)) + { + continue; + } + + return BoneName; + } + + SGLOG_ERROR("Invalid SenseGlove virtual hand mesh!"); + + return NAME_None; +} + +bool FSGAssetUtils::FImpl::IsLeftHandMesh( + const ISkeletalMeshEditor* SkeletalMeshEditor, const USkeletalMesh* SkeletalMesh) +{ + static constexpr int32 HandBoneNameIndex = static_cast(EHandKeypoint::Wrist); + + const FSGVirtualHandMeshSettings& MeshSettings{GetVirtualHandMeshSettings()}; + + if (!ensureAlwaysMsgf(HandBoneNameIndex < MeshSettings.RightHandBoneNames.Num() + && HandBoneNameIndex < MeshSettings.RightHandBoneNames.Num(), + TEXT("Invalid HandBoneNameIndex"))) + { + return false; + } + + const FName LeftHandBoneName{MeshSettings.LeftHandBoneNames[HandBoneNameIndex]}; + const FName HandBoneName{GetHandBoneName(SkeletalMeshEditor, SkeletalMesh)}; + + if (HandBoneName == NAME_None) + { + return false; + } + + if (HandBoneName != LeftHandBoneName) + { + return false; + } + + return true; +} + +bool FSGAssetUtils::FImpl::IsRightHandMesh( + const ISkeletalMeshEditor* SkeletalMeshEditor, const USkeletalMesh* SkeletalMesh) +{ + static constexpr int32 HandBoneNameIndex = static_cast(EHandKeypoint::Wrist); + + const FSGVirtualHandMeshSettings& MeshSettings{GetVirtualHandSettings().MeshSettings}; + + if (!ensureAlwaysMsgf(HandBoneNameIndex < MeshSettings.RightHandBoneNames.Num() + && HandBoneNameIndex < MeshSettings.RightHandBoneNames.Num(), + TEXT("Invalid HandBoneNameIndex"))) + { + return false; + } + + const FName RightHandBoneName{MeshSettings.RightHandBoneNames[HandBoneNameIndex]}; + const FName HandBoneName{GetHandBoneName(SkeletalMeshEditor, SkeletalMesh)}; + + if (HandBoneName == NAME_None) + { + return false; + } + + if (HandBoneName != RightHandBoneName) + { + return false; + } + + return true; +} + +FTransform FSGAssetUtils::FImpl::GetHandBoneBoneRefTransform(const bool bRight, const FName& BoneName) +{ + const FSGVirtualHandMeshSettings& MeshSettings{GetVirtualHandSettings().MeshSettings}; + + const TSoftObjectPtr& HandMesh{ + bRight + ? MeshSettings.LeftHandReferenceMesh + : MeshSettings.RightHandReferenceMesh + }; + + if (HandMesh.IsValid()) + { + const FReferenceSkeleton& ReferenceSkeleton{HandMesh->GetRefSkeleton()}; + const int32 BoneIndex = ReferenceSkeleton.FindBoneIndex(BoneName); + + if (BoneIndex == INDEX_NONE) + { + SGLOG_WARNING(FString::Printf(TEXT("Could not find a bone index for the '%s' joint!"), + *BoneName.ToString())); + } + + const TArray Transforms{ReferenceSkeleton.GetRefBonePose()}; + ensureAlwaysMsgf(Transforms.Num() > 0 && Transforms.Num() > BoneIndex, + TEXT("Invalid bone index '%d' or no transforms found!"), BoneIndex); + + const FTransform& Transform{Transforms[BoneIndex]}; + return Transform; + } + + ensureAlwaysMsgf( + bRight + ? MeshSettings.RightHandDefaultReferenceBoneTransforms.Contains(BoneName) + : MeshSettings.LeftHandDefaultReferenceBoneTransforms.Contains(BoneName), + TEXT("Invalid bone name '%s'!"), *BoneName.ToString()); + + const FTransform& Transform{ + bRight + ? MeshSettings.RightHandDefaultReferenceBoneTransforms[BoneName] + : MeshSettings.LeftHandDefaultReferenceBoneTransforms[BoneName] + }; + return Transform; + +} + +bool FSGAssetUtils::FImpl::AddSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, + const FName& BoneName, const FName& SocketName, const FTransform& SocketTransform) +{ + if (!ensureAlwaysMsgf(SkeletalMeshEditor, TEXT("Invalid SkeletalMeshEditor!"))) + { + return false; + } + + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + TSharedRef PersonaToolkit{SkeletalMeshEditor->GetPersonaToolkit()}; + TSharedPtr EditableSkeleton{PersonaToolkit->GetEditableSkeleton()}; + if (!ensureAlwaysMsgf(EditableSkeleton.IsValid(), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + USkeletalMeshSocket* Socket{NewObject()}; + Socket->BoneName = BoneName; + Socket->SocketName = SocketName; + if (!ensureAlwaysMsgf(IsValid(Socket), TEXT("Invalid Socket!"))) + { + return false; + } + + const bool bDoesSocketAlreadyExistOnTheMesh = EditableSkeleton->DoesSocketAlreadyExist( + Socket, FText::FromName(SocketName), ESocketParentType::Mesh, SkeletalMesh); + const bool bDoesSocketAlreadyExistOnTheSkeleton = EditableSkeleton->DoesSocketAlreadyExist( + Socket, FText::FromName(SocketName), ESocketParentType::Skeleton, SkeletalMesh); + + if (bDoesSocketAlreadyExistOnTheMesh || bDoesSocketAlreadyExistOnTheSkeleton) + { + SGLOG_ERROR(FString::Printf(TEXT("Socket '%s' already exists on '%s'; refuse to add a duplicate!"), + *SocketName.ToString(), *SkeletalMesh->GetPathName())); + return false; + } + + Socket = EditableSkeleton->AddSocket(BoneName); + if (!ensureAlwaysMsgf(IsValid(Socket), TEXT("Invalid Socket!"))) + { + return false; + } + + EditableSkeleton->RenameSocket(Socket->SocketName, SocketName, SkeletalMesh); + + Socket->RelativeLocation = SocketTransform.GetLocation(); + Socket->RelativeRotation = SocketTransform.GetRotation().Rotator(); + Socket->RelativeScale = SocketTransform.GetScale3D(); + + return true; +} + +bool FSGAssetUtils::FImpl::AddGrabAttachPointSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, const bool bRight) +{ + static constexpr int32 BoneNameIndex = static_cast(EHandKeypoint::Wrist); + + const FSGVirtualHandMeshSettings MeshSettings{GetVirtualHandMeshSettings()}; + + if (!ensureAlwaysMsgf(BoneNameIndex < MeshSettings.RightHandBoneNames.Num() + && BoneNameIndex < MeshSettings.RightHandBoneNames.Num(), + TEXT("Invalid BoneNameIndex"))) + { + return false; + } + + const FName& BoneName{ + bRight + ? MeshSettings.RightHandBoneNames[BoneNameIndex] + : MeshSettings.LeftHandBoneNames[BoneNameIndex] + }; + + const FSGVirtualHandGrabSettings GrabSettings{GetVirtualHandGrabSettings()}; + const FName& SocketName{GrabSettings.GrabAttachPointSocketName}; + const FTransform& SocketTransform{GrabSettings.GrabAttachPointSocketTransform}; + + const bool bAddedSocket = AddSocket( + SkeletalMeshEditor, SkeletalMesh, BoneName, SocketName, SocketTransform); + if (!bAddedSocket) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the socket '%s' to '%s'!"), + *SocketName.ToString(), *SkeletalMesh->GetPathName())); + return false; + } + + return true; +} + +bool FSGAssetUtils::FImpl::AddFingertipColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, + const bool bRight, const EHandKeypoint DistalKeypoint, const FName& SocketName) +{ + const int32 BoneNameIndex = static_cast(DistalKeypoint); + + const FSGVirtualHandMeshSettings MeshSettings{GetVirtualHandMeshSettings()}; + + if (!ensureAlwaysMsgf(BoneNameIndex < MeshSettings.RightHandBoneNames.Num() + && BoneNameIndex < MeshSettings.RightHandBoneNames.Num(), + TEXT("Invalid BoneNameIndex"))) + { + return false; + } + + const FName& BoneName{ + bRight + ? MeshSettings.RightHandBoneNames[BoneNameIndex] + : MeshSettings.LeftHandBoneNames[BoneNameIndex] + }; + + FTransform SocketTransform{ + GetHandBoneBoneRefTransform(bRight, BoneName) + }; + + const bool bAddedSocket = AddSocket( + SkeletalMeshEditor, SkeletalMesh, BoneName, SocketName, MoveTemp(SocketTransform)); + + if (!bAddedSocket) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the socket '%s' to '%s'!"), + *BoneName.ToString(), *SkeletalMesh->GetPathName())); + return false; + } + + return true; +} + +bool FSGAssetUtils::FImpl::AddGrabThumbColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, const bool bRight) +{ + const FSGVirtualHandGrabSettings GrabSettings{GetVirtualHandGrabSettings()}; + const FName& SocketName{GrabSettings.ThumbColliderSocketName}; + return AddFingertipColliderSocket(SkeletalMeshEditor, SkeletalMesh, + bRight, EHandKeypoint::ThumbDistal, SocketName); +} + +bool FSGAssetUtils::FImpl::AddGrabIndexColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, const bool bRight) +{ + const FSGVirtualHandGrabSettings GrabSettings{GetVirtualHandGrabSettings()}; + const FName& SocketName{GrabSettings.IndexColliderSocketName}; + return AddFingertipColliderSocket(SkeletalMeshEditor, SkeletalMesh, + bRight, EHandKeypoint::IndexDistal, SocketName); +} + +bool FSGAssetUtils::FImpl::AddGrabMiddleColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, const bool bRight) +{ + const FSGVirtualHandGrabSettings GrabSettings{GetVirtualHandGrabSettings()}; + const FName& SocketName{GrabSettings.MiddleColliderSocketName}; + return AddFingertipColliderSocket(SkeletalMeshEditor, SkeletalMesh, + bRight, EHandKeypoint::MiddleDistal, SocketName); +} + +bool FSGAssetUtils::FImpl::AddTouchThumbColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, const bool bRight) +{ + const FSGVirtualHandTouchSettings TouchSettings{GetVirtualHandTouchSettings()}; + const FName& SocketName{TouchSettings.ThumbColliderSocketName}; + return AddFingertipColliderSocket(SkeletalMeshEditor, SkeletalMesh, + bRight, EHandKeypoint::ThumbDistal, SocketName); +} + +bool FSGAssetUtils::FImpl::AddTouchIndexColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, const bool bRight) +{ + const FSGVirtualHandTouchSettings TouchSettings{GetVirtualHandTouchSettings()}; + const FName& SocketName{TouchSettings.IndexColliderSocketName}; + return AddFingertipColliderSocket(SkeletalMeshEditor, SkeletalMesh, + bRight, EHandKeypoint::IndexDistal, SocketName); +} + +bool FSGAssetUtils::FImpl::AddTouchMiddleColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, const bool bRight) +{ + const FSGVirtualHandTouchSettings TouchSettings{GetVirtualHandTouchSettings()}; + const FName& SocketName{TouchSettings.MiddleColliderSocketName}; + return AddFingertipColliderSocket(SkeletalMeshEditor, SkeletalMesh, + bRight, EHandKeypoint::MiddleDistal, SocketName); +} + +bool FSGAssetUtils::FImpl::AddTouchRingColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, const bool bRight) +{ + const FSGVirtualHandTouchSettings TouchSettings{GetVirtualHandTouchSettings()}; + const FName& SocketName{TouchSettings.RingColliderSocketName}; + return AddFingertipColliderSocket(SkeletalMeshEditor, SkeletalMesh, + bRight, EHandKeypoint::RingDistal, SocketName); +} + +bool FSGAssetUtils::FImpl::AddTouchLittleColliderSocket( + const ISkeletalMeshEditor* SkeletalMeshEditor, USkeletalMesh* SkeletalMesh, const bool bRight) +{ + const FSGVirtualHandTouchSettings TouchSettings{GetVirtualHandTouchSettings()}; + const FName& SocketName{TouchSettings.PinkyColliderSocketName}; + return AddFingertipColliderSocket(SkeletalMeshEditor, SkeletalMesh, + bRight, EHandKeypoint::LittleDistal, SocketName); +} + +bool FSGAssetUtils::FImpl::AddSenseGloveSockets(const ISkeletalMeshEditor* SkeletalMeshEditor, + USkeletalMesh* SkeletalMesh) +{ + if (!ensureAlwaysMsgf(SkeletalMeshEditor, TEXT("Invalid SkeletalMeshEditor!"))) + { + return false; + } + + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + const bool bIsLeft = IsLeftHandMesh(SkeletalMeshEditor, SkeletalMesh); + const bool bIsRight = IsRightHandMesh(SkeletalMeshEditor, SkeletalMesh); + const bool bIsHandMesh = bIsLeft || bIsRight; + if (!bIsHandMesh) + { + SGLOG_ERROR(FString::Printf(TEXT("Asset '%s' is not a valid virtual hand mesh!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool bAddedGrabAttachPointSocket = AddGrabAttachPointSocket( + SkeletalMeshEditor, SkeletalMesh, bIsRight); + if (!bAddedGrabAttachPointSocket) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the grab attach point socket to asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool GrabThumbCollider = AddGrabThumbColliderSocket( + SkeletalMeshEditor, SkeletalMesh, bIsRight); + if (!GrabThumbCollider) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the grab thumb collider socket to asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool GrabIndexCollider = AddGrabIndexColliderSocket( + SkeletalMeshEditor, SkeletalMesh, bIsRight); + if (!GrabIndexCollider) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the grab index collider socket to asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool GrabMiddleCollider = AddGrabMiddleColliderSocket( + SkeletalMeshEditor, SkeletalMesh, bIsRight); + if (!GrabMiddleCollider) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the grab middle collider socket to asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool TouchThumbCollider = AddTouchThumbColliderSocket( + SkeletalMeshEditor, SkeletalMesh, bIsRight); + if (!TouchThumbCollider) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the touch thumb collider socket to asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool TouchIndexCollider = AddTouchIndexColliderSocket( + SkeletalMeshEditor, SkeletalMesh, bIsRight); + if (!TouchIndexCollider) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the touch index collider socket to asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool TouchMiddleCollider = AddTouchMiddleColliderSocket( + SkeletalMeshEditor, SkeletalMesh, bIsRight); + if (!TouchMiddleCollider) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the touch middle collider socket to asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool TouchRingCollider = AddTouchRingColliderSocket( + SkeletalMeshEditor, SkeletalMesh, bIsRight); + if (!TouchRingCollider) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the touch ring collider socket to asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + + const bool TouchLittleCollider = AddTouchLittleColliderSocket( + SkeletalMeshEditor, SkeletalMesh, bIsRight); + if (!TouchLittleCollider) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the touch little collider socket to asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + + return true; +} + +bool FSGAssetUtils::FImpl::AddSenseGloveSockets(USkeletalMesh* SkeletalMesh, USkeleton* Skeleton) +{ + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + if (!ensureAlwaysMsgf(IsValid(Skeleton), TEXT("Invalid Skeleton!"))) + { + return false; + } + + const bool bAssetEdotirOpenForSkeletalMesh = IsAssetEditorOpenForAsset(SkeletalMesh); + if (bAssetEdotirOpenForSkeletalMesh) + { + const int32 ClosedEditorsNum = CloseAllEditorsForAsset(SkeletalMesh); + (void) ClosedEditorsNum; + } + + const bool bAssetEdotirOpenForSkeleton = IsAssetEditorOpenForAsset(Skeleton); + if (bAssetEdotirOpenForSkeleton) + { + const int32 ClosedEditorsNum = CloseAllEditorsForAsset(Skeleton); + (void) ClosedEditorsNum; + } + + const bool bCanModifySkeletalMesh = SkeletalMesh->CanModify(); + if (!bCanModifySkeletalMesh) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not modify the asset '%s'!"), *SkeletalMesh->GetPathName())); + return false; + } + + const bool bCanModifySkeleton = Skeleton->CanModify(); + if (!bCanModifySkeleton) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not modify the asset '%s'!"), *Skeleton->GetPathName())); + return false; + } + + bool bOpenedSkeletalMeshEditor = OpenEditorForAsset(SkeletalMesh); + if (!bOpenedSkeletalMeshEditor) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not open the editor for the asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + TArray AssetEditors{FindEditorsForAsset(SkeletalMesh)}; + if (AssetEditors.IsEmpty()) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not find any editor for the asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + for (IAssetEditorInstance* AssetEditor: AssetEditors) + { + if (!ensureAlwaysMsgf(AssetEditor, TEXT("Invalid AssetEditor!"))) + { + return false; + } + + const ISkeletalMeshEditor* SkeletalMeshEditor{static_cast(AssetEditor)}; + if (!SkeletalMeshEditor) + { + continue; + } + + const bool bAddedSenseGloveSockets = AddSenseGloveSockets(SkeletalMeshEditor, SkeletalMesh); + if (!bAddedSenseGloveSockets) + { + SGLOG_ERROR(FString::Printf(TEXT("Failed to add the SenseGlove sockets to the asset '%s'!"), + *SkeletalMesh->GetPathName())); + const int32 ClosedEditorsNum = CloseAllEditorsForAsset(SkeletalMesh); + (void) ClosedEditorsNum; + return false; + } + + const int32 ClosedEditorsNum = CloseAllEditorsForAsset(SkeletalMesh); + (void) ClosedEditorsNum; + + break; + } + + SkeletalMesh->PostEditChange(); + Skeleton->PostEditChange(); + + const bool bMarkedSkeletalMeshPackageDirty = SkeletalMesh->MarkPackageDirty(); + if (!bMarkedSkeletalMeshPackageDirty) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not mark the asset '%s' as dirty!"), *SkeletalMesh->GetPathName())); + return false; + } + + const bool bMarkedSkeletonPackageDirty = Skeleton->MarkPackageDirty(); + if (!bMarkedSkeletonPackageDirty) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not mark the asset '%s' as dirty!"), *Skeleton->GetPathName())); + return false; + } + + bOpenedSkeletalMeshEditor = OpenEditorForAsset(SkeletalMesh); + if (!bOpenedSkeletalMeshEditor) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not refresh the editor for the asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool bOpenedSkeletonEditor = OpenEditorForAsset(Skeleton); + if (!bOpenedSkeletonEditor) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not refresh the editor for the asset '%s'!"), + *Skeleton->GetPathName())); + return false; + } + + return true; +} + + +bool FSGAssetUtils::FImpl::ClearExistingSockets(USkeletalMesh* SkeletalMesh, USkeleton* Skeleton) +{ + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + if (!ensureAlwaysMsgf(IsValid(Skeleton), TEXT("Invalid Skeleton!"))) + { + return false; + } + + const bool bAssetEdotirOpenForSkeletalMesh = IsAssetEditorOpenForAsset(SkeletalMesh); + if (bAssetEdotirOpenForSkeletalMesh) + { + const int32 ClosedEditorsNum = CloseAllEditorsForAsset(SkeletalMesh); + (void) ClosedEditorsNum; + } + + const bool bAssetEdotirOpenForSkeleton = IsAssetEditorOpenForAsset(Skeleton); + if (bAssetEdotirOpenForSkeleton) + { + const int32 ClosedEditorsNum = CloseAllEditorsForAsset(Skeleton); + (void) ClosedEditorsNum; + } + + const bool bCanModifySkeletalMesh = SkeletalMesh->CanModify(); + if (!bCanModifySkeletalMesh) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not modify the asset '%s'!"), *SkeletalMesh->GetPathName())); + return false; + } + + const bool bCanModifySkeleton = Skeleton->CanModify(); + if (!bCanModifySkeleton) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not modify the asset '%s'!"), *Skeleton->GetPathName())); + return false; + } + + Skeleton->Sockets.Empty(); + + SkeletalMesh->PostEditChange(); + Skeleton->PostEditChange(); + + const bool bMarkedSkeletalMeshPackageDirty = SkeletalMesh->MarkPackageDirty(); + if (!bMarkedSkeletalMeshPackageDirty) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not mark the asset '%s' as dirty!"), *SkeletalMesh->GetPathName())); + return false; + } + + const bool bMarkedSkeletonPackageDirty = Skeleton->MarkPackageDirty(); + if (!bMarkedSkeletonPackageDirty) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not mark the asset '%s' as dirty!"), *Skeleton->GetPathName())); + return false; + } + + const bool bOpenedSkeletalMeshEditor = OpenEditorForAsset(SkeletalMesh); + if (!bOpenedSkeletalMeshEditor) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not refresh the editor for the asset '%s'!"), + *SkeletalMesh->GetPathName())); + return false; + } + + const bool bOpenedSkeletonEditor = OpenEditorForAsset(Skeleton); + if (!bOpenedSkeletonEditor) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not refresh the editor for the asset '%s'!"), + *Skeleton->GetPathName())); + return false; + } + + return true; +} + +UAssetEditorSubsystem* FSGAssetUtils::GetAssetEditorSubsystem() +{ + if (!ensureAlwaysMsgf(GEditor, TEXT("Invalid GEditor!"))) + { + return nullptr; + } + + UAssetEditorSubsystem* AssetEditorSubsystem{GEditor->GetEditorSubsystem()}; + return AssetEditorSubsystem; +} + +UObject* FSGAssetUtils::GetEditedAsset() +{ + UAssetEditorSubsystem* AssetEditorSubsystem{GetAssetEditorSubsystem()}; + if (!ensureAlwaysMsgf(AssetEditorSubsystem, TEXT("Invalid AssetEditorSubsystem!"))) + { + return nullptr; + } + + TArray EditedAssets{AssetEditorSubsystem->GetAllEditedAssets()}; + for (UObject* EditedAsset: EditedAssets) + { + TArray AssetEditors{FindEditorsForAsset(EditedAsset)}; + for (IAssetEditorInstance* AssetEditor: AssetEditors) + { + if (!ensureAlwaysMsgf(AssetEditor, TEXT("Invalid AssetEditor!"))) + { + return nullptr; + } + + TSharedPtr TabManager = AssetEditor->GetAssociatedTabManager(); + if (!ensureAlwaysMsgf(TabManager.IsValid(), TEXT("Invalid TabManager!"))) + { + return nullptr; + } + + TSharedPtr OwnerTab{TabManager->GetOwnerTab()}; + if (!ensureAlwaysMsgf(OwnerTab.IsValid(), TEXT("Invalid OwnerTab!"))) + { + return nullptr; + } + + const bool bForeground = OwnerTab->IsForeground(); + if (bForeground) + { + return EditedAsset; + } + } + } + + ensureAlwaysMsgf(false, TEXT("The code flow should never reach here!")); + + return nullptr; +} + +bool FSGAssetUtils::IsAssetEditorOpenForAsset(UObject* Asset) +{ + if (!ensureAlwaysMsgf(Asset, TEXT("Invalid Asset!"))) + { + return false; + } + + TArray AssetEditors{FindEditorsForAsset(Asset)}; + return !AssetEditors.IsEmpty(); +} + +TArray FSGAssetUtils::FindEditorsForAsset(UObject* Asset) +{ + if (!ensureAlwaysMsgf(Asset, TEXT("Invalid Asset!"))) + { + return TArray{}; + } + + UAssetEditorSubsystem* AssetEditorSubsystem{GetAssetEditorSubsystem()}; + if (!ensureAlwaysMsgf(AssetEditorSubsystem, TEXT("Invalid AssetEditorSubsystem!"))) + { + return TArray{}; + } + + TArray AssetEditors{AssetEditorSubsystem->FindEditorsForAsset(Asset)}; + return AssetEditors; +} + +bool FSGAssetUtils::OpenEditorForAsset(UObject* Asset) +{ + if (!ensureAlwaysMsgf(Asset, TEXT("Invalid Asset!"))) + { + return false; + } + + UAssetEditorSubsystem* AssetEditorSubsystem{GetAssetEditorSubsystem()}; + if (!ensureAlwaysMsgf(AssetEditorSubsystem, TEXT("Invalid AssetEditorSubsystem!"))) + { + return false; + } + + const bool bOpenedEditor = AssetEditorSubsystem->OpenEditorForAsset(Asset); + return bOpenedEditor; +} + +int32 FSGAssetUtils::CloseAllEditorsForAsset(UObject* Asset) +{ + if (!ensureAlwaysMsgf(Asset, TEXT("Invalid Asset!"))) + { + return 0; + } + + UAssetEditorSubsystem* AssetEditorSubsystem{GetAssetEditorSubsystem()}; + if (!ensureAlwaysMsgf(AssetEditorSubsystem, TEXT("Invalid AssetEditorSubsystem!"))) + { + return false; + } + + const int32 ClosedEditorsNum = AssetEditorSubsystem->CloseAllEditorsForAsset(Asset); + return ClosedEditorsNum; +} + +bool FSGAssetUtils::AddSenseGloveSockets(USkeletalMesh* SkeletalMesh) +{ + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + USkeleton* Skeleton{SkeletalMesh->GetSkeleton()}; + if (!ensureAlwaysMsgf(IsValid(Skeleton), TEXT("Invalid Skeleton!"))) + { + return false; + } + + const bool bAddedSenseGloveSockets = FImpl::AddSenseGloveSockets(SkeletalMesh, Skeleton); + return bAddedSenseGloveSockets; +} + +bool FSGAssetUtils::AddSenseGloveSockets(USkeleton* Skeleton) +{ + if (!ensureAlwaysMsgf(IsValid(Skeleton), TEXT("Invalid Skeleton!"))) + { + return false; + } + + USkeletalMesh* SkeletalMesh{Skeleton->GetPreviewMesh()}; + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + const bool bAddedSenseGloveSockets = FImpl::AddSenseGloveSockets(SkeletalMesh, Skeleton); + return bAddedSenseGloveSockets; +} + +bool FSGAssetUtils::ClearExistingSockets(USkeletalMesh* SkeletalMesh) +{ + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + USkeleton* Skeleton{SkeletalMesh->GetSkeleton()}; + if (!ensureAlwaysMsgf(IsValid(Skeleton), TEXT("Invalid Skeleton!"))) + { + return false; + } + + const bool bClearedExistingSockets = FImpl::ClearExistingSockets(SkeletalMesh, Skeleton); + return bClearedExistingSockets; +} + +bool FSGAssetUtils::ClearExistingSockets(USkeleton* Skeleton) +{ + if (!ensureAlwaysMsgf(IsValid(Skeleton), TEXT("Invalid Skeleton!"))) + { + return false; + } + + USkeletalMesh* SkeletalMesh{Skeleton->GetPreviewMesh()}; + if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) + { + return false; + } + + const bool bClearedExistingSockets = FImpl::ClearExistingSockets(SkeletalMesh, Skeleton); + return bClearedExistingSockets; +} \ No newline at end of file diff --git a/Source/SenseGloveEditor/Private/SGEditor/SGContentBrowserExtension.cpp b/Source/SenseGloveEditor/Private/SGEditor/SGContentBrowserExtension.cpp new file mode 100644 index 00000000..8c751e8a --- /dev/null +++ b/Source/SenseGloveEditor/Private/SGEditor/SGContentBrowserExtension.cpp @@ -0,0 +1,296 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGEditor/SGContentBrowserExtension.h" + +#include "Animation/Skeleton.h" +#include "AssetRegistry/AssetData.h" +#include "Containers/UnrealString.h" +#include "ContentBrowserDelegates.h" +#include "ContentBrowserModule.h" +#include "Editor.h" +#include "Engine/SkeletalMesh.h" +#include "Framework/Commands/UIAction.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/MultiBox/MultiBoxExtender.h" +#include "GenericPlatform/GenericPlatformMisc.h" +#include "Internationalization/Text.h" +#include "Math/Transform.h" +#include "Misc/MessageDialog.h" +#include "ReferenceSkeleton.h" +#include "UObject/Object.h" + +#include "SGEditor/SGAssetUtils.h" +#include "SGEditor/SGPluginStyle.h" + +#define LOCTEXT_NAMESPACE "SGContentBrowserExtension" + +struct FSGContentBrowserExtension::FImpl +{ + /************************ + * Static methods + ************************/ + + static void TryAddSenseGloveSockets(const TArray& SelectedAssets); + static void TryClearExistingSockets(const TArray& SelectedAssets); +}; + +void FSGContentBrowserExtension::FImpl::TryAddSenseGloveSockets(const TArray& SelectedAssets) +{ + const FText ConfirmationDialogText{ + LOCTEXT("SenseGloveAddSocketsConfirmation", + "Do you want to add the SenseGlove sockets?") + }; + const EAppReturnType::Type DialogResult{ + FMessageDialog::Open(EAppMsgType::YesNo, ConfirmationDialogText) + }; + + if (DialogResult != EAppReturnType::Yes) + { + return; + } + + bool bAllAssetsSucceeded = true; + + for (const FAssetData& Asset: SelectedAssets) + { + USkeletalMesh* SkeletalMesh{Cast(Asset.GetAsset())}; + USkeleton* Skeleton{Cast(Asset.GetAsset())}; + + if (!IsValid(SkeletalMesh) && !IsValid(Skeleton)) + { + continue; + } + + bool bAddedSenseGloveSockets = false; + + if (IsValid(SkeletalMesh)) + { + bAddedSenseGloveSockets = FSGAssetUtils::AddSenseGloveSockets(SkeletalMesh); + } + else if (IsValid(Skeleton)) + { + bAddedSenseGloveSockets = FSGAssetUtils::AddSenseGloveSockets(Skeleton); + } + + if (!bAddedSenseGloveSockets) + { + bAllAssetsSucceeded = false; + + const FText ResultDialogText{ + FText::Format( + LOCTEXT("SenseGloveAddSocketsResultFailedOn", + "Failed to add the SenseGlove sockets to '{0}'!"), + FText::FromString(Asset.GetSoftObjectPath().ToString())) + }; + FMessageDialog::Open(EAppMsgType::Ok, ResultDialogText); + } + + if (!bAddedSenseGloveSockets) + { + break; + } + } + + if (bAllAssetsSucceeded) + { + const FText ResultDialogText{ + LOCTEXT("SenseGloveAddSocketsResultSuccess", + "Successfully added the SenseGlove grab and touch sockets. If you wish the changes to persist, save" + " the modified asset(s) now!") + }; + FMessageDialog::Open(EAppMsgType::Ok, ResultDialogText); + } +} + +void FSGContentBrowserExtension::FImpl::TryClearExistingSockets(const TArray& SelectedAssets) +{ + const FText ConfirmationDialogText{ + LOCTEXT("SenseGloveClearExistingSocketsConfirmation", + "Are you sure you want to clear all the existing sockets? This cannot be undone!") + }; + const EAppReturnType::Type DialogResult{ + FMessageDialog::Open(EAppMsgType::YesNo, ConfirmationDialogText) + }; + + if (DialogResult != EAppReturnType::Yes) + { + return; + } + + bool bAllAssetsSucceeded = true; + + for (const FAssetData& Asset: SelectedAssets) + { + USkeletalMesh* SkeletalMesh{Cast(Asset.GetAsset())}; + USkeleton* Skeleton{Cast(Asset.GetAsset())}; + + if (!IsValid(SkeletalMesh) && !IsValid(Skeleton)) + { + continue; + } + + bool bClearedExistingSockets = false; + + if (IsValid(SkeletalMesh)) + { + bClearedExistingSockets = FSGAssetUtils::ClearExistingSockets(SkeletalMesh); + } + else if (IsValid(Skeleton)) + { + bClearedExistingSockets = FSGAssetUtils::ClearExistingSockets(Skeleton); + } + + if (!bClearedExistingSockets) + { + bAllAssetsSucceeded = false; + + const FText ResultDialogText{ + FText::Format( + LOCTEXT("SenseGloveClearExistingSocketsResultFailedOn", + "Failed to clear all the existing sockets on '{0}'!"), + FText::FromString(Asset.GetSoftObjectPath().ToString())) + }; + FMessageDialog::Open(EAppMsgType::Ok, ResultDialogText); + } + + if (!bClearedExistingSockets) + { + break; + } + } + + if (bAllAssetsSucceeded) + { + const FText ResultDialogText{ + LOCTEXT("SenseGloveClearExistingSocketsResultSuccess", + "Successfully cleared all the existing sockets! If you wish the changes to persist, save the" + " modified asset(s) now!") + }; + FMessageDialog::Open(EAppMsgType::Ok, ResultDialogText); + } +} + +void FSGContentBrowserExtension::Register() +{ + FContentBrowserModule& ContentBrowserModule{ + FModuleManager::LoadModuleChecked(TEXT("ContentBrowser")) + }; + + TArray& ContextMenuDelegates{ + ContentBrowserModule.GetAllAssetViewContextMenuExtenders() + }; + + ContextMenuDelegates.Add(FContentBrowserMenuExtender_SelectedAssets::CreateStatic(&OnExtendAssetSelectionMenu)); +} + +TSharedRef FSGContentBrowserExtension::OnExtendAssetSelectionMenu( + const TArray& SelectedAssets) +{ + TSharedRef Extender{MakeShared()}; + Extender->AddMenuExtension( + "CommonAssetActions", + EExtensionHook::After, + nullptr, + FMenuExtensionDelegate::CreateStatic(&AssetExtenderCallback, SelectedAssets) + ); + return MoveTemp(Extender); +} + +void FSGContentBrowserExtension::AssetExtenderCallback( + FMenuBuilder& MenuBuilder, const TArray SelectedAssets) +{ + bool bShouldExtendTheMenu = true; + + for (const FAssetData& Asset: SelectedAssets) + { + if (Asset.IsRedirector() + || FName{*Asset.AssetClassPath.ToString()} == NAME_Class + || (Asset.PackageFlags & PKG_FilterEditorOnly)) + { + bShouldExtendTheMenu = false; + break; + } + + const bool bIsChildOfUSkeletalMesh = Asset.GetClass()->IsChildOf(USkeletalMesh::StaticClass()); + const bool bIsChildOfUSkeleton = Asset.GetClass()->IsChildOf(USkeleton::StaticClass()); + + if (!bIsChildOfUSkeletalMesh && !bIsChildOfUSkeleton) + { + bShouldExtendTheMenu = false; + break; + } + } + + if (!bShouldExtendTheMenu) + { + return; + } + + MenuBuilder.BeginSection(FName{TEXT("SenseGloveSection")}, LOCTEXT("SenseGloveHeading", "SenseGlove")); + + MenuBuilder.AddMenuEntry( + LOCTEXT("SenseGloveAddSocketsLabel", "Add SenseGlove Sockets"), + LOCTEXT("SenseGloveAddSocketsToolTip", "Adds and sets up the SenseGlove grab and touch sockets"), + FSGPluginStyle::GetStyleSetIcon(), + FUIAction{ + FExecuteAction::CreateLambda([SelectedAssets]()-> void + { + FImpl::TryAddSenseGloveSockets(SelectedAssets); + }) + }, + NAME_None, + EUserInterfaceActionType::Button, + NAME_None); + + MenuBuilder.AddMenuEntry( + LOCTEXT("SenseGloveClearExistingSocketsLabel", "Clear Existing Sockets"), + LOCTEXT("SenseGloveClearExistingSocketsToolTip", + "Clears all the existing sockets; SenseGlove or otherwise"), + FSGPluginStyle::GetStyleSetIcon(), + FUIAction{ + FExecuteAction::CreateLambda([SelectedAssets]()-> void + { + FImpl::TryClearExistingSockets(SelectedAssets); + }) + }, + NAME_None, + EUserInterfaceActionType::Button, + NAME_None); + + MenuBuilder.EndSection(); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveEditor/Private/SGEditor/SGContentBrowserExtension_SkeletalMesh.cpp b/Source/SenseGloveEditor/Private/SGEditor/SGContentBrowserExtension_SkeletalMesh.cpp deleted file mode 100644 index f9a7acb0..00000000 --- a/Source/SenseGloveEditor/Private/SGEditor/SGContentBrowserExtension_SkeletalMesh.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/** - * @file - * - * @author Mamadou Babaei - * - * @section LICENSE - * - * (The MIT License) - * - * Copyright (c) 2020 - 2024 SenseGlove - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @section DESCRIPTION - * - * - */ - - -#include "SGEditor/SGContentBrowserExtension_SkeletalMesh.h" - -#include "AssetRegistry/AssetData.h" -#include "AssetRegistry/AssetRegistryModule.h" -#include "ContentBrowserDelegates.h" -#include "ContentBrowserModule.h" -#include "Editor.h" -#include "Engine/SkeletalMeshSocket.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" -#include "Framework/MultiBox/MultiBoxExtender.h" -#include "Math/Transform.h" -#include "ReferenceSkeleton.h" - -#include "SGEditor/SGPluginStyle.h" -#include "SGLog/SGLog.h" - -#define LOCTEXT_NAMESPACE "SGContentBrowserExtension_SkeletalMesh" - -void FSGContentBrowserExtension_SkeletalMesh::Register() -{ - FContentBrowserModule& ContentBrowserModule{ - FModuleManager::LoadModuleChecked(TEXT("ContentBrowser")) - }; - - TArray& ContextMenuDelegates{ - ContentBrowserModule.GetAllAssetViewContextMenuExtenders() - }; - - ContextMenuDelegates.Add(FContentBrowserMenuExtender_SelectedAssets::CreateStatic(&OnExtendAssetSelectionMenu)); -} - -TSharedRef FSGContentBrowserExtension_SkeletalMesh::OnExtendAssetSelectionMenu( - const TArray& SelectedAssets) -{ - TSharedRef Extender{MakeShared()}; - Extender->AddMenuExtension( - "CommonAssetActions", - EExtensionHook::After, - nullptr, - FMenuExtensionDelegate::CreateStatic(&AssetExtenderCallback, SelectedAssets) - ); - return MoveTemp(Extender); -} - -void FSGContentBrowserExtension_SkeletalMesh::AssetExtenderCallback( - FMenuBuilder& MenuBuilder, const TArray SelectedAssets) -{ - MenuBuilder.BeginSection(FName{TEXT("SenseGloveSection")}, LOCTEXT("SenseGloveHeading", "SenseGlove")); - { - for (const FAssetData& Asset: SelectedAssets) - { - if (!Asset.IsRedirector() - && FName{*Asset.AssetClassPath.ToString()} != NAME_Class - && !(Asset.PackageFlags & PKG_FilterEditorOnly)) - { - if (Asset.GetClass()->IsChildOf(USkeletalMesh::StaticClass())) - { - MenuBuilder.AddMenuEntry( - LOCTEXT("AddSenseGloveSocketsLabel", "Add SenseGlove Sockets"), - LOCTEXT( - "AddSenseGloveSocketsToolTip", "Adds and sets up the SenseGlove grab and touch sockets"), - FSGPluginStyle::GetStyleSetIcon(), - FUIAction{ - FExecuteAction::CreateLambda([SelectedAssets]()-> void - { - for (const FAssetData& Asset: SelectedAssets) - { - USkeletalMesh* SkeletalMesh{Cast(Asset.GetAsset())}; - - if (!IsValid(SkeletalMesh)) - { - continue; - } - - AddSenseGloveSockets(SkeletalMesh); - } - }) - }, - NAME_None, - EUserInterfaceActionType::Button, - NAME_None); - } - } - } - } - MenuBuilder.EndSection(); -} - -void FSGContentBrowserExtension_SkeletalMesh::AddSenseGloveSockets(USkeletalMesh* SkeletalMesh) -{ - if (!ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh!"))) - { - return; - } - - const USkeleton* Skeleton{SkeletalMesh->GetSkeleton()}; - if (!ensureAlwaysMsgf(IsValid(Skeleton), TEXT("Invalid Skeleton!"))) - { - return; - } - - USkeletalMeshSocket* GrabSocket{NewObject()}; - if (!IsValid(GrabSocket)) - { - // show proper messages - SGLOG("Failed to create new socket!"); - } - - GrabSocket->SocketName = FName(TEXT("CodeGrabSocket")); - GrabSocket->BoneName = FName(TEXT("hand_l")); - GrabSocket->RelativeLocation = FVector(0.0f, 0.0f, 0.0f); - GrabSocket->RelativeRotation = FRotator(0.0f, 0.0f, 0.0f); - GrabSocket->RelativeScale = FVector(1.0f, 1.0f, 1.0f); - - SkeletalMesh->AddSocket(GrabSocket, true); - - const bool bMarkedSkeletalMeshPackageDirty = SkeletalMesh->MarkPackageDirty(); - const bool bMarkedSkeletonPackageDirty = Skeleton->MarkPackageDirty(); - - SGLOG("Socket added to hand_l?", bMarkedSkeletalMeshPackageDirty, bMarkedSkeletonPackageDirty); - - if (bMarkedSkeletalMeshPackageDirty && bMarkedSkeletonPackageDirty) - { - FAssetRegistryModule::AssetCreated(GrabSocket); - - FEditorDelegates::RefreshEditor.Broadcast(); - - // Log success - SGLOG("Create new socket!"); - } - else - { - // Log errors - SGLOG("Failed to create new socket 2!"); - } - - // Maybe we try to add them to this? - FReferenceSkeleton ReferenceSkeleton{Skeleton->GetReferenceSkeleton()}; - FReferenceSkeletonModifier SkeletonModifier{ReferenceSkeleton, Skeleton}; - -} - -#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveEditor/Private/SGEditor/SGContentBrowserExtension_Skeleton.cpp b/Source/SenseGloveEditor/Private/SGEditor/SGContentBrowserExtension_Skeleton.cpp deleted file mode 100644 index c6d4fff5..00000000 --- a/Source/SenseGloveEditor/Private/SGEditor/SGContentBrowserExtension_Skeleton.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @file - * - * @author Mamadou Babaei - * - * @section LICENSE - * - * (The MIT License) - * - * Copyright (c) 2020 - 2024 SenseGlove - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @section DESCRIPTION - * - * - */ - - -#include "SGEditor/SGContentBrowserExtension_Skeleton.h" - -#include "AssetRegistry/AssetData.h" -#include "ContentBrowserDelegates.h" -#include "ContentBrowserModule.h" -#include "Engine/SkeletalMeshSocket.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" -#include "Framework/MultiBox/MultiBoxExtender.h" -#include "Math/Transform.h" -#include "ReferenceSkeleton.h" - -#include "SGEditor/SGPluginStyle.h" -#include "SGLog/SGLog.h" - -#define LOCTEXT_NAMESPACE "SGContentBrowserExtension_Skeleton" - -void FSGContentBrowserExtension_Skeleton::Register() -{ - FContentBrowserModule& ContentBrowserModule{ - FModuleManager::LoadModuleChecked(TEXT("ContentBrowser")) - }; - - TArray& ContextMenuDelegates{ - ContentBrowserModule.GetAllAssetViewContextMenuExtenders() - }; - - ContextMenuDelegates.Add(FContentBrowserMenuExtender_SelectedAssets::CreateStatic(&OnExtendAssetSelectionMenu)); -} - -TSharedRef FSGContentBrowserExtension_Skeleton::OnExtendAssetSelectionMenu( - const TArray& SelectedAssets) -{ - TSharedRef Extender{MakeShared()}; - Extender->AddMenuExtension( - "CommonAssetActions", - EExtensionHook::After, - nullptr, - FMenuExtensionDelegate::CreateStatic(&AssetExtenderCallback, SelectedAssets) - ); - return MoveTemp(Extender); -} - -void FSGContentBrowserExtension_Skeleton::AssetExtenderCallback( - FMenuBuilder& MenuBuilder, const TArray SelectedAssets) -{ - MenuBuilder.BeginSection(FName{TEXT("SenseGlove")}, LOCTEXT("SenseGloveHeading", "SenseGlove")); - { - for (const FAssetData& Asset: SelectedAssets) - { - if (!Asset.IsRedirector() - && FName{*Asset.AssetClassPath.ToString()} != NAME_Class - && !(Asset.PackageFlags & PKG_FilterEditorOnly)) - { - if (Asset.GetClass()->IsChildOf(USkeleton::StaticClass())) - { - MenuBuilder.AddMenuEntry( - LOCTEXT("AddSenseGloveSocketsLabel", "Add SenseGlove Sockets"), - LOCTEXT( - "AddSenseGloveSocketsToolTip", "Adds and sets up the SenseGlove grab and touch sockets"), - FSGPluginStyle::GetStyleSetIcon(), - FUIAction{ - FExecuteAction::CreateLambda([SelectedAssets]()-> void - { - for (const FAssetData& Asset: SelectedAssets) - { - USkeleton* Skeleton{Cast(Asset.GetAsset())}; - - if (!IsValid(Skeleton)) - { - continue; - } - - AddSenseGloveSockets(Skeleton); - } - }) - }, - NAME_None, - EUserInterfaceActionType::Button, - NAME_None); - } - } - } - } - MenuBuilder.EndSection(); -} - -void FSGContentBrowserExtension_Skeleton::AddSenseGloveSockets(USkeleton* Skeleton) -{ - if (!ensureAlwaysMsgf(IsValid(Skeleton), TEXT("Invalid Skeleton!"))) - { - return; - } - -} - -#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveEditor/Private/SGEditor/SGSkeletalMeshEditor.cpp b/Source/SenseGloveEditor/Private/SGEditor/SGSkeletalMeshEditor.cpp deleted file mode 100644 index 3b436c88..00000000 --- a/Source/SenseGloveEditor/Private/SGEditor/SGSkeletalMeshEditor.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/** - * @file - * - * @author Mamadou Babaei - * - * @section LICENSE - * - * (The MIT License) - * - * Copyright (c) 2020 - 2024 SenseGlove - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @section DESCRIPTION - * - * - */ - - -#include "SGEditor/SGSkeletalMeshEditor.h" - -#include "Framework/MultiBox/MultiBoxExtender.h" -#include "ISkeletalMeshEditor.h" -#include "ISkeletalMeshEditorModule.h" -#include "Modules/ModuleManager.h" - -#include "SGEditor/SGAssetTypeActions_SkeletalMesh.h" -#include "SGEditor/SGPluginStyle.h" -#include "SGEditor/SGSkeletalMeshEditorCommands.h" - -#define LOCTEXT_NAMESPACE "SkeletalMeshEditor" - -struct FSGSkeletalMeshEditor::FImpl -{ - /************************ - * Member variables - ************************/ - - TSharedPtr AssetActions; - TSharedPtr EditorCommands; - TSharedPtr MenuExtender; - TSharedPtr ToolbarExtender; - - /************************ - * Owner object - ************************/ - - FSGSkeletalMeshEditor* Owner; - - /************************ - * Constructor / Destructor - ************************/ - - explicit FImpl(FSGSkeletalMeshEditor* InOwner); - ~FImpl(); - - /************************ - * Default copy constructor & copy assignment operator - ************************/ - - FImpl(const FImpl& Rhs) = default; - FImpl& operator=(const FImpl& Rhs) = default; -}; - -TSharedRef FSGSkeletalMeshEditor::GetInstance() -{ - static TSharedPtr Instance{MakeShared()}; - return Instance.ToSharedRef(); -} - -FSGSkeletalMeshEditor::FSGSkeletalMeshEditor() - : Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) -{ -} - -FSGSkeletalMeshEditor::~FSGSkeletalMeshEditor() = default; - -void FSGSkeletalMeshEditor::Initialize() const -{ - //RegisterAssetTypeActions(); - RegisterCommands(); - ExtendMenu(); - ExtendToolbar(); -} - -void FSGSkeletalMeshEditor::Deinitialize() const -{ - UnregisterCommands(); -} - -void FSGSkeletalMeshEditor::RegisterAssetTypeActions() const -{ - if (Pimpl->AssetActions.IsValid()) - { - Pimpl->AssetActions.Reset(); - } - Pimpl->AssetActions = MakeShared(); - - IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); - - AssetTools.RegisterAssetTypeActions(Pimpl->AssetActions.ToSharedRef()); -} - -void FSGSkeletalMeshEditor::RegisterCommands() const -{ - if (Pimpl->EditorCommands.IsValid()) - { - Pimpl->EditorCommands.Reset(); - } - Pimpl->EditorCommands = MakeShared(); - Pimpl->EditorCommands->RegisterCommands(); -} - -void FSGSkeletalMeshEditor::UnregisterCommands() const -{ - if (!Pimpl->EditorCommands.IsValid()) - { - return; - } - Pimpl->EditorCommands->Unregister(); -} - -void FSGSkeletalMeshEditor::ExtendMenu() const -{ - if (Pimpl->MenuExtender.IsValid()) - { - Pimpl->MenuExtender.Reset(); - } - Pimpl->MenuExtender = MakeShared(); - - ISkeletalMeshEditorModule& SkeletonEditorModule{ - FModuleManager::LoadModuleChecked("SkeletalMeshEditor") - }; - const TSharedPtr ExtensibilityManager{ - SkeletonEditorModule.GetMenuExtensibilityManager() - }; - - if (!ensureAlwaysMsgf(ExtensibilityManager.IsValid(), TEXT("Invalid ExtensibilityManager!"))) - { - return; - } - - Pimpl->MenuExtender->AddMenuExtension( - FName{TEXT("AssetEditorActions")}, - EExtensionHook::After, - Pimpl->EditorCommands->CommandList, - FMenuExtensionDelegate::CreateLambda([&](FMenuBuilder MenuBuilder)-> void - { - MenuBuilder.BeginSection(FName{TEXT("SenseGlove")}, LOCTEXT("SenseGloveHeading", "SenseGlove")); - { - MenuBuilder.AddMenuEntry( - Pimpl->EditorCommands->AddSenseGloveSockets, - NAME_None, - LOCTEXT("AddSenseGloveSocketsLabel", "Add SenseGlove Sockets"), - LOCTEXT( - "AddSenseGloveSocketsToolTip", "Adds and sets up the SenseGlove grab and touch sockets"), - FSlateIcon{FSGPluginStyle::GetStyleSetIcon()}, - NAME_None); - } - MenuBuilder.EndSection(); - })); - - ExtensibilityManager->AddExtender(Pimpl->MenuExtender); -} - -void FSGSkeletalMeshEditor::ExtendToolbar() const -{ - if (Pimpl->ToolbarExtender.IsValid()) - { - Pimpl->ToolbarExtender.Reset(); - } - Pimpl->ToolbarExtender = MakeShared(); - - ISkeletalMeshEditorModule& SkeletalMeshEditorModule{ - FModuleManager::LoadModuleChecked("SkeletalMeshEditor") - }; - const TSharedPtr ExtensibilityManager{ - SkeletalMeshEditorModule.GetToolBarExtensibilityManager() - }; - - if (!ensureAlwaysMsgf(ExtensibilityManager.IsValid(), TEXT("Invalid ExtensibilityManager!"))) - { - return; - } - - Pimpl->ToolbarExtender->AddToolBarExtension( - FName{TEXT("SkeletalMesh")}, - EExtensionHook::After, - Pimpl->EditorCommands->CommandList, - FToolBarExtensionDelegate::CreateLambda([&](FToolBarBuilder& ToolbarBuilder)-> void - { - ToolbarBuilder.BeginSection(FName{TEXT("SenseGlove")}); - { - ToolbarBuilder.AddToolBarButton( - Pimpl->EditorCommands->AddSenseGloveSockets, - NAME_None, - LOCTEXT("AddSenseGloveSocketsLabel", "Add SenseGlove Sockets"), - LOCTEXT( - "AddSenseGloveSocketsToolTip", "Adds and sets up the SenseGlove grab and touch sockets"), - FSGPluginStyle::GetStyleSetIcon(), - NAME_None, - FNewMenuDelegate{} - ); - } - ToolbarBuilder.EndSection(); - })); - - ExtensibilityManager->AddExtender(Pimpl->ToolbarExtender); -} - -FSGSkeletalMeshEditor::FImpl::FImpl(FSGSkeletalMeshEditor* InOwner) - : Owner(InOwner) -{ -} - -FSGSkeletalMeshEditor::FImpl::~FImpl() = default; - -void FSGSkeletalMeshEditor::FImplDeleter::operator()(const FImpl* P) const -{ - delete P; -} - -#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveEditor/Private/SGEditor/SGSkeletalMeshEditorCommands.cpp b/Source/SenseGloveEditor/Private/SGEditor/SGSkeletalMeshEditorCommands.cpp deleted file mode 100644 index 48229d50..00000000 --- a/Source/SenseGloveEditor/Private/SGEditor/SGSkeletalMeshEditorCommands.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @file - * - * @author Mamadou Babaei - * - * @section LICENSE - * - * (The MIT License) - * - * Copyright (c) 2020 - 2024 SenseGlove - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @section DESCRIPTION - * - * - */ - - -#include "SGEditor/SGSkeletalMeshEditorCommands.h" - -#include "Framework/Notifications/NotificationManager.h" -#include "Widgets/Notifications/SNotificationList.h" - -#include "SGEditor/SGPluginStyle.h" - -#define LOCTEXT_NAMESPACE "SkeletalMeshEditorCommands" - -FSGSkeletalMeshEditorCommands::FSGSkeletalMeshEditorCommands() - : TCommands{ - TEXT("SGSkeletalMeshEditor"), - NSLOCTEXT("Contexts", "SGSkeletalMeshEditor", "SenseGlove SkeletalMesh Editor"), - NAME_None, - FSGPluginStyle::GetInstance()->GetStyleSetName() - } -{ -} - -FSGSkeletalMeshEditorCommands::~FSGSkeletalMeshEditorCommands() = default; - -void FSGSkeletalMeshEditorCommands::RegisterCommands() -{ - if (AddSenseGloveSockets.IsValid()) - { - AddSenseGloveSockets.Reset(); - } - UI_COMMAND(AddSenseGloveSockets, - "Add SenseGlove Sockets", - "Adds and sets up the SenseGlove grab and touch sockets", - EUserInterfaceActionType::Button, - FInputChord{}); - - if (CommandList.IsValid()) - { - CommandList.Reset(); - } - CommandList = MakeShared(); - CommandList->MapAction(AddSenseGloveSockets, - FExecuteAction::CreateLambda([&]()-> void - { - FMessageDialog::Open( - EAppMsgType::Ok, FText::FromString( - FString{ - "The SenseGlove grab and touch sockets have been added." - " If you wish the changes to persist, save the asset now!" - })); - }), - FCanExecuteAction::CreateLambda([&]()-> bool - { - return true; - }), - EUIActionRepeatMode::RepeatDisabled); -} - -#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveEditor/Private/SGEditor/SGSkeletonEditorCommands.cpp b/Source/SenseGloveEditor/Private/SGEditor/SGSkeletonEditorCommands.cpp deleted file mode 100644 index 39ecd65d..00000000 --- a/Source/SenseGloveEditor/Private/SGEditor/SGSkeletonEditorCommands.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @file - * - * @author Mamadou Babaei - * - * @section LICENSE - * - * (The MIT License) - * - * Copyright (c) 2020 - 2024 SenseGlove - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @section DESCRIPTION - * - * - */ - - -#include "SGEditor/SGSkeletonEditorCommands.h" - -#include "Framework/Notifications/NotificationManager.h" -#include "Widgets/Notifications/SNotificationList.h" - -#include "SGEditor/SGPluginStyle.h" - -#define LOCTEXT_NAMESPACE "SkeletonEditorCommands" - -FSGSkeletonEditorCommands::FSGSkeletonEditorCommands() - : TCommands{ - TEXT("SGSkeletonEditor"), - NSLOCTEXT("Contexts", "SGSkeletonEditor", "SenseGlove Skeleton Editor"), - NAME_None, - FSGPluginStyle::GetInstance()->GetStyleSetName() - } -{ -} - -FSGSkeletonEditorCommands::~FSGSkeletonEditorCommands() = default; - -void FSGSkeletonEditorCommands::RegisterCommands() -{ - if (AddSenseGloveSockets.IsValid()) - { - AddSenseGloveSockets.Reset(); - } - UI_COMMAND(AddSenseGloveSockets, - "Add SenseGlove Sockets", - "Adds and sets up the SenseGlove grab and touch sockets", - EUserInterfaceActionType::Button, - FInputChord{}); - - if (CommandList.IsValid()) - { - CommandList.Reset(); - } - CommandList = MakeShared(); - CommandList->MapAction(AddSenseGloveSockets, - FExecuteAction::CreateLambda([&]()-> void - { - FMessageDialog::Open( - EAppMsgType::Ok, FText::FromString( - FString{ - "The SenseGlove grab and touch sockets have been added." - " If you wish the changes to persist, save the asset now!" - })); - }), - FCanExecuteAction::CreateLambda([&]()-> bool - { - return true; - }), - EUIActionRepeatMode::RepeatDisabled); -} - -#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveEditor/Private/SGEditor/SGSkeletonEditor.cpp b/Source/SenseGloveEditor/Private/SGEditor/SGSocketsEditor.cpp similarity index 51% rename from Source/SenseGloveEditor/Private/SGEditor/SGSkeletonEditor.cpp rename to Source/SenseGloveEditor/Private/SGEditor/SGSocketsEditor.cpp index 6e8415b9..59e05c6f 100644 --- a/Source/SenseGloveEditor/Private/SGEditor/SGSkeletonEditor.cpp +++ b/Source/SenseGloveEditor/Private/SGEditor/SGSocketsEditor.cpp @@ -33,27 +33,26 @@ */ -#include "SGEditor/SGSkeletonEditor.h" +#include "SGEditor/SGSocketsEditor.h" #include "Framework/MultiBox/MultiBoxExtender.h" -#include "ISkeletonEditor.h" #include "ISkeletonEditorModule.h" #include "Modules/ModuleManager.h" -#include "SGEditor/SGAssetTypeActions_Skeleton.h" +// #include "SGEditor/SGAssetTypeActions_Skeleton.h" #include "SGEditor/SGPluginStyle.h" -#include "SGEditor/SGSkeletonEditorCommands.h" +#include "SGEditor/SGSocketsEditorCommands.h" -#define LOCTEXT_NAMESPACE "SkeletonEditor" +#define LOCTEXT_NAMESPACE "SocketsEditor" -struct FSGSkeletonEditor::FImpl +struct FSGSocketsEditor::FImpl { /************************ * Member variables ************************/ - TSharedPtr AssetActions; - TSharedPtr EditorCommands; + // TSharedPtr AssetActions; + TSharedPtr EditorCommands; TSharedPtr MenuExtender; TSharedPtr ToolbarExtender; @@ -61,13 +60,13 @@ struct FSGSkeletonEditor::FImpl * Owner object ************************/ - FSGSkeletonEditor* Owner; + FSGSocketsEditor* Owner; /************************ * Constructor / Destructor ************************/ - explicit FImpl(FSGSkeletonEditor* InOwner); + explicit FImpl(FSGSocketsEditor* InOwner); ~FImpl(); /************************ @@ -78,20 +77,20 @@ struct FSGSkeletonEditor::FImpl FImpl& operator=(const FImpl& Rhs) = default; }; -TSharedRef FSGSkeletonEditor::GetInstance() +TSharedRef FSGSocketsEditor::GetInstance() { - static TSharedPtr Instance{MakeShared()}; + static TSharedPtr Instance{MakeShared()}; return Instance.ToSharedRef(); } -FSGSkeletonEditor::FSGSkeletonEditor() +FSGSocketsEditor::FSGSocketsEditor() : Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) { } -FSGSkeletonEditor::~FSGSkeletonEditor() = default; +FSGSocketsEditor::~FSGSocketsEditor() = default; -void FSGSkeletonEditor::Initialize() const +void FSGSocketsEditor::Initialize() const { //RegisterAssetTypeActions(); RegisterCommands(); @@ -99,35 +98,35 @@ void FSGSkeletonEditor::Initialize() const ExtendToolbar(); } -void FSGSkeletonEditor::Deinitialize() const +void FSGSocketsEditor::Deinitialize() const { UnregisterCommands(); } -void FSGSkeletonEditor::RegisterAssetTypeActions() const +void FSGSocketsEditor::RegisterAssetTypeActions() const { - if (Pimpl->AssetActions.IsValid()) - { - Pimpl->AssetActions.Reset(); - } - Pimpl->AssetActions = MakeShared(); - - IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); - - AssetTools.RegisterAssetTypeActions(Pimpl->AssetActions.ToSharedRef()); + // if (Pimpl->AssetActions.IsValid()) + // { + // Pimpl->AssetActions.Reset(); + // } + // Pimpl->AssetActions = MakeShared(); + // + // IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); + // + // AssetTools.RegisterAssetTypeActions(Pimpl->AssetActions.ToSharedRef()); } -void FSGSkeletonEditor::RegisterCommands() const +void FSGSocketsEditor::RegisterCommands() const { if (Pimpl->EditorCommands.IsValid()) { Pimpl->EditorCommands.Reset(); } - Pimpl->EditorCommands = MakeShared(); + Pimpl->EditorCommands = MakeShared(); Pimpl->EditorCommands->RegisterCommands(); } -void FSGSkeletonEditor::UnregisterCommands() const +void FSGSocketsEditor::UnregisterCommands() const { if (!Pimpl->EditorCommands.IsValid()) { @@ -136,7 +135,7 @@ void FSGSkeletonEditor::UnregisterCommands() const Pimpl->EditorCommands->Unregister(); } -void FSGSkeletonEditor::ExtendMenu() const +void FSGSocketsEditor::ExtendMenu() const { if (Pimpl->MenuExtender.IsValid()) { @@ -144,14 +143,30 @@ void FSGSkeletonEditor::ExtendMenu() const } Pimpl->MenuExtender = MakeShared(); + ISkeletonEditorModule& SkeletalMeshEditorModule{ + FModuleManager::LoadModuleChecked("SkeletalMeshEditor") + }; + ISkeletonEditorModule& SkeletonEditorModule{ FModuleManager::LoadModuleChecked("SkeletonEditor") }; - const TSharedPtr ExtensibilityManager{ + + const TSharedPtr SkeletalMeshExtensibilityManager{ + SkeletalMeshEditorModule.GetMenuExtensibilityManager() + }; + + const TSharedPtr SkeletonExtensibilityManager{ SkeletonEditorModule.GetMenuExtensibilityManager() }; - if (!ensureAlwaysMsgf(ExtensibilityManager.IsValid(), TEXT("Invalid ExtensibilityManager!"))) + if (!ensureAlwaysMsgf(SkeletonExtensibilityManager.IsValid(), + TEXT("Invalid SkeletonExtensibilityManager!"))) + { + return; + } + + if (!ensureAlwaysMsgf(SkeletalMeshExtensibilityManager.IsValid(), + TEXT("Invalid SkeletalMeshExtensibilityManager!"))) { return; } @@ -165,21 +180,31 @@ void FSGSkeletonEditor::ExtendMenu() const MenuBuilder.BeginSection(FName{TEXT("SenseGlove")}, LOCTEXT("SenseGloveHeading", "SenseGlove")); { MenuBuilder.AddMenuEntry( - Pimpl->EditorCommands->AddSenseGloveSockets, + Pimpl->EditorCommands->SenseGloveAddSockets, NAME_None, - LOCTEXT("AddSenseGloveSocketsLabel", "Add SenseGlove Sockets"), + LOCTEXT("SenseGloveAddSocketsLabel", "Add SenseGlove Sockets"), LOCTEXT( - "AddSenseGloveSocketsToolTip", "Adds and sets up the SenseGlove grab and touch sockets"), + "SenseGloveAddSocketsToolTip", "Adds and sets up the SenseGlove grab and touch sockets"), FSGPluginStyle::GetStyleSetIcon(), NAME_None); + + MenuBuilder.AddMenuEntry( + Pimpl->EditorCommands->SenseGloveClearExistingSockets, + NAME_None, + LOCTEXT("SenseGloveClearExistingSocketsLabel", "Clear Existing Sockets"), + LOCTEXT( + "SenseGloveClearSocketsToolTip", "Clears all sockets; SenseGlove or otherwise"), + FSlateIcon{FSGPluginStyle::GetStyleSetIcon()}, + NAME_None); } MenuBuilder.EndSection(); })); - ExtensibilityManager->AddExtender(Pimpl->MenuExtender); + SkeletalMeshExtensibilityManager->AddExtender(Pimpl->MenuExtender); + SkeletonExtensibilityManager->AddExtender(Pimpl->MenuExtender); } -void FSGSkeletonEditor::ExtendToolbar() const +void FSGSocketsEditor::ExtendToolbar() const { if (Pimpl->ToolbarExtender.IsValid()) { @@ -187,20 +212,36 @@ void FSGSkeletonEditor::ExtendToolbar() const } Pimpl->ToolbarExtender = MakeShared(); + ISkeletonEditorModule& SkeletalMeshEditorModule{ + FModuleManager::LoadModuleChecked("SkeletalMeshEditor") + }; + ISkeletonEditorModule& SkeletonEditorModule{ FModuleManager::LoadModuleChecked("SkeletonEditor") }; - const TSharedPtr ExtensibilityManager{ + + const TSharedPtr SkeletalMeshExtensibilityManager{ + SkeletalMeshEditorModule.GetToolBarExtensibilityManager() + }; + + const TSharedPtr SkeletonExtensibilityManager{ SkeletonEditorModule.GetToolBarExtensibilityManager() }; - if (!ensureAlwaysMsgf(ExtensibilityManager.IsValid(), TEXT("Invalid ExtensibilityManager!"))) + if (!ensureAlwaysMsgf(SkeletonExtensibilityManager.IsValid(), + TEXT("Invalid SkeletonExtensibilityManager!"))) + { + return; + } + + if (!ensureAlwaysMsgf(SkeletalMeshExtensibilityManager.IsValid(), + TEXT("Invalid SkeletalMeshExtensibilityManager!"))) { return; } Pimpl->ToolbarExtender->AddToolBarExtension( - FName{TEXT("Skeleton")}, + FName{TEXT("SkeletalMesh")}, EExtensionHook::After, Pimpl->EditorCommands->CommandList, FToolBarExtensionDelegate::CreateLambda([&](FToolBarBuilder& ToolbarBuilder)-> void @@ -208,11 +249,22 @@ void FSGSkeletonEditor::ExtendToolbar() const ToolbarBuilder.BeginSection(FName{TEXT("SenseGlove")}); { ToolbarBuilder.AddToolBarButton( - Pimpl->EditorCommands->AddSenseGloveSockets, + Pimpl->EditorCommands->SenseGloveAddSockets, NAME_None, - LOCTEXT("AddSenseGloveSocketsLabel", "Add SenseGlove Sockets"), + LOCTEXT("SenseGloveAddSocketsLabel", "Add SenseGlove Sockets"), LOCTEXT( - "AddSenseGloveSocketsToolTip", "Adds and sets up the SenseGlove grab and touch sockets"), + "SenseGloveAddSocketsToolTip", "Adds and sets up the SenseGlove grab and touch sockets"), + FSGPluginStyle::GetStyleSetIcon(), + NAME_None, + FNewMenuDelegate{} + ); + + ToolbarBuilder.AddToolBarButton( + Pimpl->EditorCommands->SenseGloveClearExistingSockets, + NAME_None, + LOCTEXT("SenseGloveClearExistingSocketsLabel", "Clear Existing Sockets"), + LOCTEXT( + "SenseGloveClearSocketsToolTip", "Clears all sockets; SenseGlove or otherwise"), FSGPluginStyle::GetStyleSetIcon(), NAME_None, FNewMenuDelegate{} @@ -221,17 +273,18 @@ void FSGSkeletonEditor::ExtendToolbar() const ToolbarBuilder.EndSection(); })); - ExtensibilityManager->AddExtender(Pimpl->ToolbarExtender); + SkeletalMeshExtensibilityManager->AddExtender(Pimpl->ToolbarExtender); + SkeletonExtensibilityManager->AddExtender(Pimpl->ToolbarExtender); } -FSGSkeletonEditor::FImpl::FImpl(FSGSkeletonEditor* InOwner) +FSGSocketsEditor::FImpl::FImpl(FSGSocketsEditor* InOwner) : Owner(InOwner) { } -FSGSkeletonEditor::FImpl::~FImpl() = default; +FSGSocketsEditor::FImpl::~FImpl() = default; -void FSGSkeletonEditor::FImplDeleter::operator()(const FImpl* P) const +void FSGSocketsEditor::FImplDeleter::operator()(const FImpl* P) const { delete P; } diff --git a/Source/SenseGloveEditor/Private/SGEditor/SGSocketsEditorCommands.cpp b/Source/SenseGloveEditor/Private/SGEditor/SGSocketsEditorCommands.cpp new file mode 100644 index 00000000..68c0574d --- /dev/null +++ b/Source/SenseGloveEditor/Private/SGEditor/SGSocketsEditorCommands.cpp @@ -0,0 +1,224 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGEditor/SGSocketsEditorCommands.h" + +#include "Animation/Skeleton.h" +#include "Containers/UnrealString.h" +#include "Engine/SkeletalMesh.h" +#include "Framework/Commands/UIAction.h" +#include "GenericPlatform/GenericPlatformMisc.h" +#include "Internationalization/Text.h" +#include "Misc/MessageDialog.h" +#include "UObject/Object.h" + +#include "SGEditor/SGAssetUtils.h" +#include "SGEditor/SGPluginStyle.h" + +#define LOCTEXT_NAMESPACE "SocketsEditorCommands" + +struct FSGSocketsEditorCommands::FImpl +{ + /************************ + * Static methods + ************************/ + + static void TryAddSenseGloveSockets(); + static void TryClearExistingSockets(); +}; + +void FSGSocketsEditorCommands::FImpl::TryAddSenseGloveSockets() +{ + const FText ConfirmationDialogText{ + LOCTEXT("SenseGloveAddSocketsConfirmation", + "Do you want to add the SenseGlove sockets?") + }; + const EAppReturnType::Type DialogResult{ + FMessageDialog::Open(EAppMsgType::YesNo, ConfirmationDialogText) + }; + + if (DialogResult != EAppReturnType::Yes) + { + return; + } + + UObject* EditedAsset{FSGAssetUtils::GetEditedAsset()}; + USkeletalMesh* EditedSkeletalMesh{Cast(EditedAsset)}; + USkeleton* EditedSkeleton{Cast(EditedAsset)}; + + bool bAddedSenseGloveSockets = false; + if (IsValid(EditedSkeletalMesh)) + { + bAddedSenseGloveSockets = FSGAssetUtils::AddSenseGloveSockets(EditedSkeletalMesh); + } + else if (IsValid(EditedSkeleton)) + { + bAddedSenseGloveSockets = FSGAssetUtils::AddSenseGloveSockets(EditedSkeleton); + } + + if (bAddedSenseGloveSockets) + { + const FText ResultDialogText{ + LOCTEXT("SenseGloveAddSocketsResultSuccess", + "Successfully added the SenseGlove grab and touch sockets. If you wish the changes to persist, save" + " the modified asset(s) now!") + }; + FMessageDialog::Open(EAppMsgType::Ok, ResultDialogText); + } + else + { + const FText ResultDialogText{ + LOCTEXT("SenseGloveAddSocketsResultSuccessFailed", + "Failed to add the SenseGlove sockets!") + }; + FMessageDialog::Open(EAppMsgType::Ok, ResultDialogText); + } +} + +void FSGSocketsEditorCommands::FImpl::TryClearExistingSockets() +{ + const FText ConfirmationDialogText{ + LOCTEXT("SenseGloveClearExistingSocketsConfirmation", + "Are you sure you want to clear all the existing sockets? This cannot be undone!") + }; + const EAppReturnType::Type DialogResult{ + FMessageDialog::Open(EAppMsgType::YesNo, ConfirmationDialogText) + }; + + if (DialogResult != EAppReturnType::Yes) + { + return; + } + + UObject* EditedAsset{FSGAssetUtils::GetEditedAsset()}; + USkeletalMesh* EditedSkeletalMesh{Cast(EditedAsset)}; + USkeleton* EditedSkeleton{Cast(EditedAsset)}; + + bool bClearedExistingSockets = false; + if (IsValid(EditedSkeletalMesh)) + { + bClearedExistingSockets = FSGAssetUtils::ClearExistingSockets(EditedSkeletalMesh); + } + else if (IsValid(EditedSkeleton)) + { + bClearedExistingSockets = FSGAssetUtils::ClearExistingSockets(EditedSkeleton); + } + + if (bClearedExistingSockets) + { + const FText ResultDialogText{ + LOCTEXT("SenseGloveClearExistingSocketsResultSuccess", + "Successfully cleared all the existing sockets! If you wish the changes to persist, save the" + " modified asset(s) now!") + }; + FMessageDialog::Open(EAppMsgType::Ok, ResultDialogText); + } + else + { + const FText ResultDialogText{ + LOCTEXT("SenseGloveClearExistingSocketsResultSuccessFailed", + "Failed to clear all the existing sockets!") + }; + FMessageDialog::Open(EAppMsgType::Ok, ResultDialogText); + } +} + +FSGSocketsEditorCommands::FSGSocketsEditorCommands() + : TCommands{ + TEXT("SGSocketsEditor"), + NSLOCTEXT("Contexts", "SGSocketsEditor", "SenseGlove Sockets Editor"), + NAME_None, + FSGPluginStyle::GetInstance()->GetStyleSetName() + } +{ +} + +FSGSocketsEditorCommands::~FSGSocketsEditorCommands() = default; + +void FSGSocketsEditorCommands::RegisterCommands() +{ + if (CommandList.IsValid()) + { + CommandList.Reset(); + } + + CommandList = MakeShared(); + + if (SenseGloveAddSockets.IsValid()) + { + SenseGloveAddSockets.Reset(); + } + + if (SenseGloveClearExistingSockets.IsValid()) + { + SenseGloveClearExistingSockets.Reset(); + } + + UI_COMMAND(SenseGloveAddSockets, + "Add SenseGlove Sockets", + "Adds and sets up the SenseGlove grab and touch sockets", + EUserInterfaceActionType::Button, + FInputChord{}); + + UI_COMMAND(SenseGloveClearExistingSockets, + "Clear Existing Sockets", + "Clears all the existing sockets; SenseGlove or otherwise", + EUserInterfaceActionType::Button, + FInputChord{}); + + CommandList->MapAction(SenseGloveAddSockets, + FExecuteAction::CreateLambda([&]()-> void + { + FImpl::TryAddSenseGloveSockets(); + }), + FCanExecuteAction::CreateLambda([&]()-> bool + { + return true; + }), + EUIActionRepeatMode::RepeatDisabled); + + CommandList->MapAction(SenseGloveClearExistingSockets, + FExecuteAction::CreateLambda([&]()-> void + { + FImpl::TryClearExistingSockets(); + }), + FCanExecuteAction::CreateLambda([&]()-> bool + { + return true; + }), + EUIActionRepeatMode::RepeatDisabled); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveEditor/Private/SenseGloveEditorModule.cpp b/Source/SenseGloveEditor/Private/SenseGloveEditorModule.cpp index 7587fff8..8b4f0d5c 100644 --- a/Source/SenseGloveEditor/Private/SenseGloveEditorModule.cpp +++ b/Source/SenseGloveEditor/Private/SenseGloveEditorModule.cpp @@ -35,11 +35,9 @@ #include "SenseGloveEditorModule.h" -#include "SGEditor/SGContentBrowserExtension_SkeletalMesh.h" -#include "SGEditor/SGContentBrowserExtension_Skeleton.h" +#include "SGEditor/SGContentBrowserExtension.h" #include "SGEditor/SGPluginStyle.h" -#include "SGEditor/SGSkeletalMeshEditor.h" -#include "SGEditor/SGSkeletonEditor.h" +#include "SGEditor/SGSocketsEditor.h" #include "SGLog/SGLog.h" #define LOCTEXT_NAMESPACE "FSenseGloveEditorModule" @@ -49,10 +47,8 @@ void FSenseGloveEditorModule::StartupModule() SGLOG("Starting up SenseGloveEditor module..."); FSGPluginStyle::GetInstance()->Initialize(); - FSGContentBrowserExtension_SkeletalMesh::Register(); - FSGContentBrowserExtension_Skeleton::Register(); - FSGSkeletalMeshEditor::GetInstance()->Initialize(); - FSGSkeletonEditor::GetInstance()->Initialize(); + FSGContentBrowserExtension::Register(); + FSGSocketsEditor::GetInstance()->Initialize(); } void FSenseGloveEditorModule::PreUnloadCallback() @@ -69,8 +65,7 @@ void FSenseGloveEditorModule::ShutdownModule() { SGLOG("Shutting down SenseGloveEditor module..."); - FSGSkeletalMeshEditor::GetInstance()->Deinitialize(); - FSGSkeletonEditor::GetInstance()->Deinitialize(); + FSGSocketsEditor::GetInstance()->Deinitialize(); FSGPluginStyle::GetInstance()->Deinitialize(); } diff --git a/Source/SenseGloveEditor/Public/SGEditor/SGAssetTypeActions_SkeletalMesh.h b/Source/SenseGloveEditor/Public/SGEditor/SGAssetTypeActions_SkeletalMesh.h index 3b224d6e..787fce7d 100644 --- a/Source/SenseGloveEditor/Public/SGEditor/SGAssetTypeActions_SkeletalMesh.h +++ b/Source/SenseGloveEditor/Public/SGEditor/SGAssetTypeActions_SkeletalMesh.h @@ -50,7 +50,7 @@ class IToolkitHost; class UClass; class UObject; -class FSGAssetTypeActions_SkeletalMesh : public FAssetTypeActions_Base +class SENSEGLOVEEDITOR_API FSGAssetTypeActions_SkeletalMesh : public FAssetTypeActions_Base { public: virtual FText GetName() const override; diff --git a/Source/SenseGloveEditor/Public/SGEditor/SGAssetTypeActions_Skeleton.h b/Source/SenseGloveEditor/Public/SGEditor/SGAssetTypeActions_Skeleton.h index 7a6279e7..0408d7b6 100644 --- a/Source/SenseGloveEditor/Public/SGEditor/SGAssetTypeActions_Skeleton.h +++ b/Source/SenseGloveEditor/Public/SGEditor/SGAssetTypeActions_Skeleton.h @@ -50,7 +50,7 @@ class IToolkitHost; class UClass; class UObject; -class FSGAssetTypeActions_Skeleton : public FAssetTypeActions_Base +class SENSEGLOVEEDITOR_API FSGAssetTypeActions_Skeleton : public FAssetTypeActions_Base { public: virtual FText GetName() const override; diff --git a/Source/SenseGloveEditor/Public/SGEditor/SGContentBrowserExtension_SkeletalMesh.h b/Source/SenseGloveEditor/Public/SGEditor/SGAssetUtils.h similarity index 62% rename from Source/SenseGloveEditor/Public/SGEditor/SGContentBrowserExtension_SkeletalMesh.h rename to Source/SenseGloveEditor/Public/SGEditor/SGAssetUtils.h index 9c64d3ec..2ff69fb3 100644 --- a/Source/SenseGloveEditor/Public/SGEditor/SGContentBrowserExtension_SkeletalMesh.h +++ b/Source/SenseGloveEditor/Public/SGEditor/SGAssetUtils.h @@ -36,23 +36,37 @@ #pragma once #include "Containers/Array.h" +#include "HAL/Platform.h" #include "Templates/SharedPointer.h" -struct FAssetData; -class FMenuBuilder; +class IAssetEditorInstance; +class UAssetEditorSubsystem; class USkeletalMesh; +class USkeleton; +class UObject; -class FSGContentBrowserExtension_SkeletalMesh +class SENSEGLOVEEDITOR_API FSGAssetUtils final { public: - static void Register(); + static UAssetEditorSubsystem* GetAssetEditorSubsystem(); - static TSharedRef OnExtendAssetSelectionMenu(const TArray& SelectedAssets); - static void AssetExtenderCallback(FMenuBuilder& MenuBuilder, const TArray SelectedAssets); + static UObject* GetEditedAsset(); - static void AddSenseGloveSockets(USkeletalMesh* SkeletalMesh); + static bool IsAssetEditorOpenForAsset(UObject* Asset); + static TArray FindEditorsForAsset(UObject* Asset); + static bool OpenEditorForAsset(UObject* Asset); + static int32 CloseAllEditorsForAsset(UObject* Asset); + + static bool AddSenseGloveSockets(USkeletalMesh* SkeletalMesh); + static bool AddSenseGloveSockets(USkeleton* Skeleton); + + static bool ClearExistingSockets(USkeletalMesh* SkeletalMesh); + static bool ClearExistingSockets(USkeleton* Skeleton); private: - FSGContentBrowserExtension_SkeletalMesh() = delete; - virtual ~FSGContentBrowserExtension_SkeletalMesh() = delete; + struct FImpl; + +private: + FSGAssetUtils() = delete; + virtual ~FSGAssetUtils() = delete; }; \ No newline at end of file diff --git a/Source/SenseGloveEditor/Public/SGEditor/SGContentBrowserExtension_Skeleton.h b/Source/SenseGloveEditor/Public/SGEditor/SGContentBrowserExtension.h similarity index 87% rename from Source/SenseGloveEditor/Public/SGEditor/SGContentBrowserExtension_Skeleton.h rename to Source/SenseGloveEditor/Public/SGEditor/SGContentBrowserExtension.h index f4fa0c98..fc09a85b 100644 --- a/Source/SenseGloveEditor/Public/SGEditor/SGContentBrowserExtension_Skeleton.h +++ b/Source/SenseGloveEditor/Public/SGEditor/SGContentBrowserExtension.h @@ -40,19 +40,21 @@ struct FAssetData; class FMenuBuilder; -class USkeleton; +class USkeletalMesh; -class FSGContentBrowserExtension_Skeleton +class SENSEGLOVEEDITOR_API FSGContentBrowserExtension { public: static void Register(); +protected: static TSharedRef OnExtendAssetSelectionMenu(const TArray& SelectedAssets); static void AssetExtenderCallback(FMenuBuilder& MenuBuilder, const TArray SelectedAssets); - static void AddSenseGloveSockets(USkeleton* Skeleton); +private: + struct FImpl; private: - FSGContentBrowserExtension_Skeleton() = delete; - virtual ~FSGContentBrowserExtension_Skeleton() = delete; + FSGContentBrowserExtension() = delete; + virtual ~FSGContentBrowserExtension() = delete; }; \ No newline at end of file diff --git a/Source/SenseGloveEditor/Public/SGEditor/SGPluginStyle.h b/Source/SenseGloveEditor/Public/SGEditor/SGPluginStyle.h index e6b0ee6e..99b4aed4 100644 --- a/Source/SenseGloveEditor/Public/SGEditor/SGPluginStyle.h +++ b/Source/SenseGloveEditor/Public/SGEditor/SGPluginStyle.h @@ -40,7 +40,7 @@ class ISlateStyle; -class FSGPluginStyle final : public FSlateStyleSet +class SENSEGLOVEEDITOR_API FSGPluginStyle final : public FSlateStyleSet { public: FORCEINLINE static TSharedRef GetInstance() @@ -49,12 +49,6 @@ public: return Instance.ToSharedRef(); } - FORCEINLINE static const FSlateStyleSet& GetStyleSet() - { - const FSlateStyleSet& StyleSet{GetInstance()->GetStyleSet()}; - return StyleSet; - } - FORCEINLINE static const FName& GetStyleSetIconName() { static const FName IconName{TEXT("SenseGlove.Icon")}; diff --git a/Source/SenseGloveEditor/Public/SGEditor/SGSkeletalMeshEditor.h b/Source/SenseGloveEditor/Public/SGEditor/SGSkeletalMeshEditor.h deleted file mode 100644 index 02add114..00000000 --- a/Source/SenseGloveEditor/Public/SGEditor/SGSkeletalMeshEditor.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file - * - * @author Mamadou Babaei - * - * @section LICENSE - * - * (The MIT License) - * - * Copyright (c) 2020 - 2024 SenseGlove - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @section DESCRIPTION - * - * - */ - - -#pragma once - -#include "Framework/MultiBox/MultiBoxExtender.h" -#include "Templates/SharedPointer.h" - -class FSGSkeletalMeshEditor -{ -public: - static TSharedRef GetInstance(); - -private: - struct FImpl; - - struct FImplDeleter - { - void operator()(const FImpl* P) const; - }; - - TUniquePtr Pimpl; - FImplDeleter PimplDeleter; - -public: - FSGSkeletalMeshEditor(); - virtual ~FSGSkeletalMeshEditor(); - -public: - void Initialize() const; - void Deinitialize() const; - - void RegisterAssetTypeActions() const; - - void RegisterCommands() const; - void UnregisterCommands() const; - - void ExtendMenu() const; - - void ExtendToolbar() const; -}; \ No newline at end of file diff --git a/Source/SenseGloveEditor/Public/SGEditor/SGSkeletonEditorCommands.h b/Source/SenseGloveEditor/Public/SGEditor/SGSkeletonEditorCommands.h deleted file mode 100644 index d069c0b6..00000000 --- a/Source/SenseGloveEditor/Public/SGEditor/SGSkeletonEditorCommands.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file - * - * @author Mamadou Babaei - * - * @section LICENSE - * - * (The MIT License) - * - * Copyright (c) 2020 - 2024 SenseGlove - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @section DESCRIPTION - * - * - */ - - -#pragma once - -#include "Framework/Commands/Commands.h" -#include "Framework/Commands/UICommandList.h" -#include "Templates/SharedPointer.h" - -class FSGSkeletonEditorCommands final : public TCommands -{ -public: - TSharedPtr CommandList; - TSharedPtr AddSenseGloveSockets; - -public: - FSGSkeletonEditorCommands(); - virtual ~FSGSkeletonEditorCommands() override; - -public: - virtual void RegisterCommands() override; -}; \ No newline at end of file diff --git a/Source/SenseGloveEditor/Public/SGEditor/SGSkeletonEditor.h b/Source/SenseGloveEditor/Public/SGEditor/SGSocketsEditor.h similarity index 92% rename from Source/SenseGloveEditor/Public/SGEditor/SGSkeletonEditor.h rename to Source/SenseGloveEditor/Public/SGEditor/SGSocketsEditor.h index 6e6e1b71..18be3f1c 100644 --- a/Source/SenseGloveEditor/Public/SGEditor/SGSkeletonEditor.h +++ b/Source/SenseGloveEditor/Public/SGEditor/SGSocketsEditor.h @@ -38,10 +38,10 @@ #include "Framework/MultiBox/MultiBoxExtender.h" #include "Templates/SharedPointer.h" -class FSGSkeletonEditor +class SENSEGLOVEEDITOR_API FSGSocketsEditor { public: - static TSharedRef GetInstance(); + static TSharedRef GetInstance(); private: struct FImpl; @@ -55,8 +55,8 @@ private: FImplDeleter PimplDeleter; public: - FSGSkeletonEditor(); - virtual ~FSGSkeletonEditor(); + FSGSocketsEditor(); + virtual ~FSGSocketsEditor(); public: void Initialize() const; diff --git a/Source/SenseGloveEditor/Public/SGEditor/SGSkeletalMeshEditorCommands.h b/Source/SenseGloveEditor/Public/SGEditor/SGSocketsEditorCommands.h similarity index 82% rename from Source/SenseGloveEditor/Public/SGEditor/SGSkeletalMeshEditorCommands.h rename to Source/SenseGloveEditor/Public/SGEditor/SGSocketsEditorCommands.h index f7cce0ee..9999c26b 100644 --- a/Source/SenseGloveEditor/Public/SGEditor/SGSkeletalMeshEditorCommands.h +++ b/Source/SenseGloveEditor/Public/SGEditor/SGSocketsEditorCommands.h @@ -39,15 +39,19 @@ #include "Framework/Commands/UICommandList.h" #include "Templates/SharedPointer.h" -class FSGSkeletalMeshEditorCommands final : public TCommands +class SENSEGLOVEEDITOR_API FSGSocketsEditorCommands final : public TCommands { public: TSharedPtr CommandList; - TSharedPtr AddSenseGloveSockets; + TSharedPtr SenseGloveAddSockets; + TSharedPtr SenseGloveClearExistingSockets; + +private: + struct FImpl; public: - FSGSkeletalMeshEditorCommands(); - virtual ~FSGSkeletalMeshEditorCommands() override; + FSGSocketsEditorCommands(); + virtual ~FSGSocketsEditorCommands() override; public: virtual void RegisterCommands() override; diff --git a/Source/SenseGloveEditor/SenseGloveEditor.Build.cs b/Source/SenseGloveEditor/SenseGloveEditor.Build.cs index 6df406cb..6785c2cc 100644 --- a/Source/SenseGloveEditor/SenseGloveEditor.Build.cs +++ b/Source/SenseGloveEditor/SenseGloveEditor.Build.cs @@ -56,7 +56,15 @@ public class SenseGloveEditor : ModuleRules "Slate", "SlateCore", "UnrealEd", + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { "SenseGloveLog", + "SenseGloveSettings", + "SenseGloveTypes", } ); } diff --git a/Source/SenseGloveSettings/Private/SGSettings/SGVirtualHandGrabSettings.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGVirtualHandGrabSettings.cpp index 090d99c0..6b60c5f0 100644 --- a/Source/SenseGloveSettings/Private/SGSettings/SGVirtualHandGrabSettings.cpp +++ b/Source/SenseGloveSettings/Private/SGSettings/SGVirtualHandGrabSettings.cpp @@ -37,7 +37,8 @@ FSGVirtualHandGrabSettings::FSGVirtualHandGrabSettings() : FSGVirtualHandGrabSettings{ - FName{TEXT("GrabPalmAttachPoint")}, + FName{TEXT("GrabAttachPoint")}, + FTransform{FQuat::Identity, FVector{0.0f, 5.0f, -6.61557f}}, FVector{0.04f, 0.04f, 0.04f}, FName{TEXT("GrabThumbCollider")}, FName{TEXT("GrabIndexCollider")}, @@ -47,12 +48,14 @@ FSGVirtualHandGrabSettings::FSGVirtualHandGrabSettings() } FSGVirtualHandGrabSettings::FSGVirtualHandGrabSettings( - const FName& InPalmAttachPointSocketName, + const FName& InGrabAttachPointSocketName, + const FTransform& InGrabAttachPointSocketTransform, const FVector& InDefaultColliderSize, const FName& InThumbColliderSocketName, const FName& InIndexColliderSocketName, const FName& InMiddleColliderSocketName) - : PalmAttachPointSocketName{InPalmAttachPointSocketName}, + : GrabAttachPointSocketName{InGrabAttachPointSocketName}, + GrabAttachPointSocketTransform{InGrabAttachPointSocketTransform}, DefaultColliderSize{InDefaultColliderSize}, ThumbColliderSocketName{InThumbColliderSocketName}, IndexColliderSocketName{InIndexColliderSocketName}, diff --git a/Source/SenseGloveSettings/Private/SGSettings/SGVirtualHandMeshSettings.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGVirtualHandMeshSettings.cpp index b0b214dc..dcb0cde1 100644 --- a/Source/SenseGloveSettings/Private/SGSettings/SGVirtualHandMeshSettings.cpp +++ b/Source/SenseGloveSettings/Private/SGSettings/SGVirtualHandMeshSettings.cpp @@ -35,6 +35,8 @@ #include "SGSettings/SGVirtualHandMeshSettings.h" +#include "UObject/UnrealNames.h" + #include "Engine/SkeletalMesh.h" FSGVirtualHandMeshSettings::FSGVirtualHandMeshSettings() diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGDebugGizmoSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGDebugGizmoSettings.h index 85e4b910..be676daf 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGDebugGizmoSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGDebugGizmoSettings.h @@ -35,6 +35,8 @@ #pragma once +#include "HAL/Platform.h" +#include "Math/Color.h" #include "UObject/ObjectMacros.h" #include "SGDebugGizmoSettings.generated.h" diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGHandTrackingSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGHandTrackingSettings.h index 701c3731..a5d12a64 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGHandTrackingSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGHandTrackingSettings.h @@ -35,6 +35,7 @@ #pragma once +#include "HAL/Platform.h" #include "UObject/ObjectMacros.h" #include "SGHandTrackingSettings.generated.h" diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGInitializationSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGInitializationSettings.h index 6257b076..a443e5dc 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGInitializationSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGInitializationSettings.h @@ -35,6 +35,7 @@ #pragma once +#include "HAL/Platform.h" #include "UObject/ObjectMacros.h" #include "SGInitializationSettings.generated.h" diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGTrackingSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGTrackingSettings.h index 4bb1a5a2..d8a6d07c 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGTrackingSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGTrackingSettings.h @@ -35,6 +35,7 @@ #pragma once +#include "HAL/Platform.h" #include "UObject/ObjectMacros.h" #include "SGSettings/SGGloveTrackingSettings.h" diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandAnimationSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandAnimationSettings.h index 87f5ec25..d060ac22 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandAnimationSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandAnimationSettings.h @@ -35,6 +35,8 @@ #pragma once +#include "HAL/Platform.h" +#include "Math/Rotator.h" #include "UObject/ObjectMacros.h" #include "UObject/SoftObjectPtr.h" diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandDebuggingSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandDebuggingSettings.h index b9d55d04..9cd18e8d 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandDebuggingSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandDebuggingSettings.h @@ -35,6 +35,9 @@ #pragma once +#include "HAL/Platform.h" +#include "Math/Color.h" +#include "Math/Vector.h" #include "UObject/ObjectMacros.h" #include "SGTypes/SGDebugTypes.h" diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandGrabSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandGrabSettings.h index eaa981da..dd890481 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandGrabSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandGrabSettings.h @@ -35,6 +35,9 @@ #pragma once +#include "Math/Transform.h" +#include "Math/Vector.h" +#include "UObject/NameTypes.h" #include "UObject/ObjectMacros.h" #include "UObject/SoftObjectPtr.h" @@ -52,7 +55,10 @@ struct SENSEGLOVESETTINGS_API FSGVirtualHandGrabSettings public: UPROPERTY(Config, EditDefaultsOnly, Category="Grab") - FName PalmAttachPointSocketName; + FName GrabAttachPointSocketName; + + UPROPERTY(Config, EditDefaultsOnly, Category="Grab") + FTransform GrabAttachPointSocketTransform; UPROPERTY(Config, EditDefaultsOnly, Category="Grab") FVector DefaultColliderSize; @@ -70,7 +76,8 @@ public: FSGVirtualHandGrabSettings(); FSGVirtualHandGrabSettings( - const FName& InPalmAttachPointSocketName, + const FName& InGrabAttachPointSocketName, + const FTransform& InGrabAttachPointSocketTransform, const FVector& InDefaultColliderSize, const FName& InThumbColliderSocketName, const FName& InIndexColliderSocketName, diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandMeshSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandMeshSettings.h index ee27336d..802b1a03 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandMeshSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandMeshSettings.h @@ -35,6 +35,12 @@ #pragma once +#include "Containers/Array.h" +#include "Containers/Map.h" +#include "Engine/SkeletalMesh.h" +#include "Math/Rotator.h" +#include "Math/Transform.h" +#include "UObject/NameTypes.h" #include "UObject/ObjectMacros.h" #include "UObject/SoftObjectPtr.h" diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandTouchSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandTouchSettings.h index f7e42210..7be100a2 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandTouchSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGVirtualHandTouchSettings.h @@ -35,6 +35,8 @@ #pragma once +#include "Math/Vector.h" +#include "UObject/NameTypes.h" #include "UObject/ObjectMacros.h" #include "UObject/SoftObjectPtr.h" diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGWristTrackingSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGWristTrackingSettings.h index af9aa0d5..30bc7ff7 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGWristTrackingSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGWristTrackingSettings.h @@ -36,6 +36,8 @@ #pragma once #include "InputCoreTypes.h" +#include "Math/Rotator.h" +#include "Math/Vector.h" #include "UObject/ObjectMacros.h" #include "SGSettings/SGDebugGizmoSettings.h" diff --git a/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp b/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp index 73009128..cd6ddc9a 100644 --- a/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp +++ b/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp @@ -899,10 +899,12 @@ USGHandPose* FSGXRTracker::FImpl::GetHandPose(const bool bRight) FTransform FSGXRTracker::FImpl::GetMeshRawBoneRefTransform(const bool bRight, const FName& BoneName) { + const FSGVirtualHandMeshSettings& MeshSettings{GetVirtualHandSettings().MeshSettings}; + const TSoftObjectPtr& HandMesh{ bRight - ? GetVirtualHandSettings().MeshSettings.LeftHandReferenceMesh - : GetVirtualHandSettings().MeshSettings.RightHandReferenceMesh + ? MeshSettings.LeftHandReferenceMesh + : MeshSettings.RightHandReferenceMesh }; if (HandMesh.IsValid()) @@ -924,26 +926,24 @@ FTransform FSGXRTracker::FImpl::GetMeshRawBoneRefTransform(const bool bRight, co return Transform; } - const FSGVirtualHandMeshSettings& Settings{GetVirtualHandSettings().MeshSettings}; ensureAlwaysMsgf( bRight - ? Settings.RightHandDefaultReferenceBoneTransforms.Contains(BoneName) - : Settings.LeftHandDefaultReferenceBoneTransforms.Contains(BoneName), + ? MeshSettings.RightHandDefaultReferenceBoneTransforms.Contains(BoneName) + : MeshSettings.LeftHandDefaultReferenceBoneTransforms.Contains(BoneName), TEXT("Invalid bone name '%s'!"), *BoneName.ToString()); const FTransform& Transform{ bRight - ? Settings.RightHandDefaultReferenceBoneTransforms[BoneName] - : Settings.LeftHandDefaultReferenceBoneTransforms[BoneName] + ? MeshSettings.RightHandDefaultReferenceBoneTransforms[BoneName] + : MeshSettings.LeftHandDefaultReferenceBoneTransforms[BoneName] }; return Transform; } FTransform FSGXRTracker::FImpl::GetMeshWristChildBoneRefTransform(const bool bRight, const FName& BoneName) { - FQuat RootRotationCorrection{ - GetVirtualHandSettings().MeshSettings.RootBoneRotationCorrection - }; + const FSGVirtualHandMeshSettings& MeshSettings{GetVirtualHandSettings().MeshSettings}; + FQuat RootRotationCorrection{MeshSettings.RootBoneRotationCorrection}; const FTransform& RootBoneRefTransform{GetMeshRawBoneRefTransform(bRight, EHandKeypoint::Wrist)}; const FTransform& RawTransform{GetMeshRawBoneRefTransform(bRight, BoneName)}; FQuat CorrectedRootBoneRefRotation{MoveTemp(RootRotationCorrection) * RootBoneRefTransform.GetRotation()}; @@ -956,9 +956,8 @@ FTransform FSGXRTracker::FImpl::GetMeshWristChildBoneRefTransform(const bool bRi FTransform FSGXRTracker::FImpl::GetMeshFingerStartBoneRefTransform( const bool bRight, const EHandKeypoint Parent, const FName& BoneName) { - FQuat RootRotationCorrection{ - GetVirtualHandSettings().MeshSettings.RootBoneRotationCorrection - }; + const FSGVirtualHandMeshSettings& MeshSettings{GetVirtualHandSettings().MeshSettings}; + FQuat RootRotationCorrection{MeshSettings.RootBoneRotationCorrection}; const FTransform& RootBoneRefTransform{GetMeshRawBoneRefTransform(bRight, EHandKeypoint::Wrist)}; FQuat CorrectedRootBoneRefRotation{MoveTemp(RootRotationCorrection) * RootBoneRefTransform.GetRotation()}; FTransform CorrectedRootBoneRefTransform{ diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGCoreTypes.h b/Source/SenseGloveTypes/Public/SGTypes/SGCoreTypes.h index 33005ba2..086993f6 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGCoreTypes.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGCoreTypes.h @@ -35,7 +35,7 @@ #pragma once -#include "CoreTypes.h" +#include "HAL/Platform.h" #include "UObject/ObjectMacros.h" UENUM(BlueprintType) diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGDebugTypes.h b/Source/SenseGloveTypes/Public/SGTypes/SGDebugTypes.h index c7032c56..9260fe7c 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGDebugTypes.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGDebugTypes.h @@ -36,6 +36,7 @@ #pragma once #include "CoreTypes.h" +#include "HAL/Platform.h" #include "UObject/ObjectMacros.h" /** diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGFloatArray.h b/Source/SenseGloveTypes/Public/SGTypes/SGFloatArray.h index 0e4c8fba..c9b27d16 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGFloatArray.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGFloatArray.h @@ -35,6 +35,7 @@ #pragma once +#include "Containers/Array.h" #include "UObject/ObjectMacros.h" #include "SGFloatArray.generated.h" diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGNameArray.h b/Source/SenseGloveTypes/Public/SGTypes/SGNameArray.h index b3dd3ff9..719cf8bb 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGNameArray.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGNameArray.h @@ -35,6 +35,7 @@ #pragma once +#include "Containers/Array.h" #include "UObject/NameTypes.h" #include "SGNameArray.generated.h" diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGQuatArray.h b/Source/SenseGloveTypes/Public/SGTypes/SGQuatArray.h index b726ad81..e0605ab9 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGQuatArray.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGQuatArray.h @@ -35,6 +35,7 @@ #pragma once +#include "Containers/Array.h" #include "Math/Quat.h" #include "SGQuatArray.generated.h" diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGRotatorArray.h b/Source/SenseGloveTypes/Public/SGTypes/SGRotatorArray.h index f1ee31ca..82fc80cb 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGRotatorArray.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGRotatorArray.h @@ -35,6 +35,7 @@ #pragma once +#include "Containers/Array.h" #include "Math/Rotator.h" #include "SGRotatorArray.generated.h" diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGTrackingTypes.h b/Source/SenseGloveTypes/Public/SGTypes/SGTrackingTypes.h index 3b6f889f..bdaf0f2a 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGTrackingTypes.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGTrackingTypes.h @@ -35,7 +35,7 @@ #pragma once -#include "CoreTypes.h" +#include "HAL/Platform.h" #include "UObject/ObjectMacros.h" /** diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGTypes.h b/Source/SenseGloveTypes/Public/SGTypes/SGTypes.h index 451c6b46..e6d7f102 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGTypes.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGTypes.h @@ -35,7 +35,7 @@ #pragma once -#include "CoreTypes.h" +#include "HAL/Platform.h" #include "UObject/ObjectMacros.h" /** diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGVectorArray.h b/Source/SenseGloveTypes/Public/SGTypes/SGVectorArray.h index a30919f3..19e29641 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGVectorArray.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGVectorArray.h @@ -35,6 +35,7 @@ #pragma once +#include "HAL/Platform.h" #include "Math/Vector.h" #include "SGVectorArray.generated.h"