1051 lines
36 KiB
C++
1051 lines
36 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;
|
|
} |