Files
Plugin/Source/SenseGloveEditor/Private/SGEditor/SGAssetUtils.cpp
T

1048 lines
37 KiB
C++

/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FSkeletalMeshLODRenderData>& 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<class IPersonaToolkit> 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<FSkeletalMeshLODRenderData>& 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<FBoneIndexType>(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<int32>(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<int32>(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<USkeletalMesh>& 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<FTransform> 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<class IPersonaToolkit> PersonaToolkit{SkeletalMeshEditor->GetPersonaToolkit()};
TSharedPtr<IEditableSkeleton> EditableSkeleton{PersonaToolkit->GetEditableSkeleton()};
if (!ensureAlwaysMsgf(EditableSkeleton.IsValid(), TEXT("Invalid SkeletalMesh!")))
{
return false;
}
USkeletalMeshSocket* Socket{NewObject<USkeletalMeshSocket>()};
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<int32>(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<int32>(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<IAssetEditorInstance*> 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<ISkeletalMeshEditor*>(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<UAssetEditorSubsystem>()};
return AssetEditorSubsystem;
}
UObject* FSGAssetUtils::GetEditedAsset()
{
UAssetEditorSubsystem* AssetEditorSubsystem{GetAssetEditorSubsystem()};
if (!ensureAlwaysMsgf(AssetEditorSubsystem, TEXT("Invalid AssetEditorSubsystem!")))
{
return nullptr;
}
TArray<UObject*> EditedAssets{AssetEditorSubsystem->GetAllEditedAssets()};
for (UObject* EditedAsset: EditedAssets)
{
TArray<IAssetEditorInstance*> AssetEditors{FindEditorsForAsset(EditedAsset)};
for (IAssetEditorInstance* AssetEditor: AssetEditors)
{
if (!ensureAlwaysMsgf(AssetEditor, TEXT("Invalid AssetEditor!")))
{
return nullptr;
}
TSharedPtr<FTabManager> TabManager = AssetEditor->GetAssociatedTabManager();
if (!ensureAlwaysMsgf(TabManager.IsValid(), TEXT("Invalid TabManager!")))
{
return nullptr;
}
TSharedPtr<SDockTab> 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<IAssetEditorInstance*> AssetEditors{FindEditorsForAsset(Asset)};
return !AssetEditors.IsEmpty();
}
TArray<IAssetEditorInstance*> FSGAssetUtils::FindEditorsForAsset(UObject* Asset)
{
if (!ensureAlwaysMsgf(Asset, TEXT("Invalid Asset!")))
{
return TArray<IAssetEditorInstance*>{};
}
UAssetEditorSubsystem* AssetEditorSubsystem{GetAssetEditorSubsystem()};
if (!ensureAlwaysMsgf(AssetEditorSubsystem, TEXT("Invalid AssetEditorSubsystem!")))
{
return TArray<IAssetEditorInstance*>{};
}
TArray<IAssetEditorInstance*> 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;
}