implement the SenseGlove grab/touch sockets one-click-setup ability on any Epic-compliant virtual hand mesh from within the Unreal Editor's Content Browser, Skeleton Editor, or Skeletal Mesh Editor

This commit is contained in:
Mamadou Babaei
2024-07-02 20:50:09 +02:00
parent 17c36fab91
commit 74bc0a29c1
43 changed files with 1788 additions and 967 deletions
@@ -145,7 +145,7 @@ public:
FORCEINLINE const FName& GetGrabSocketName() const FORCEINLINE const FName& GetGrabSocketName() const
{ {
return GetVirtualHandSettings().GrabSettings.PalmAttachPointSocketName; return GetVirtualHandSettings().GrabSettings.GrabAttachPointSocketName;
} }
FORCEINLINE const FVector& GetDefaultFingertipsGrabColliderSize() const FORCEINLINE const FVector& GetDefaultFingertipsGrabColliderSize() const
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,296 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FAssetData>& SelectedAssets);
static void TryClearExistingSockets(const TArray<FAssetData>& SelectedAssets);
};
void FSGContentBrowserExtension::FImpl::TryAddSenseGloveSockets(const TArray<FAssetData>& 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<USkeletalMesh>(Asset.GetAsset())};
USkeleton* Skeleton{Cast<USkeleton>(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<FAssetData>& 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<USkeletalMesh>(Asset.GetAsset())};
USkeleton* Skeleton{Cast<USkeleton>(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<FContentBrowserModule>(TEXT("ContentBrowser"))
};
TArray<FContentBrowserMenuExtender_SelectedAssets>& ContextMenuDelegates{
ContentBrowserModule.GetAllAssetViewContextMenuExtenders()
};
ContextMenuDelegates.Add(FContentBrowserMenuExtender_SelectedAssets::CreateStatic(&OnExtendAssetSelectionMenu));
}
TSharedRef<FExtender> FSGContentBrowserExtension::OnExtendAssetSelectionMenu(
const TArray<FAssetData>& SelectedAssets)
{
TSharedRef<FExtender> Extender{MakeShared<FExtender>()};
Extender->AddMenuExtension(
"CommonAssetActions",
EExtensionHook::After,
nullptr,
FMenuExtensionDelegate::CreateStatic(&AssetExtenderCallback, SelectedAssets)
);
return MoveTemp(Extender);
}
void FSGContentBrowserExtension::AssetExtenderCallback(
FMenuBuilder& MenuBuilder, const TArray<FAssetData> 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
@@ -1,178 +0,0 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FContentBrowserModule>(TEXT("ContentBrowser"))
};
TArray<FContentBrowserMenuExtender_SelectedAssets>& ContextMenuDelegates{
ContentBrowserModule.GetAllAssetViewContextMenuExtenders()
};
ContextMenuDelegates.Add(FContentBrowserMenuExtender_SelectedAssets::CreateStatic(&OnExtendAssetSelectionMenu));
}
TSharedRef<FExtender> FSGContentBrowserExtension_SkeletalMesh::OnExtendAssetSelectionMenu(
const TArray<FAssetData>& SelectedAssets)
{
TSharedRef<FExtender> Extender{MakeShared<FExtender>()};
Extender->AddMenuExtension(
"CommonAssetActions",
EExtensionHook::After,
nullptr,
FMenuExtensionDelegate::CreateStatic(&AssetExtenderCallback, SelectedAssets)
);
return MoveTemp(Extender);
}
void FSGContentBrowserExtension_SkeletalMesh::AssetExtenderCallback(
FMenuBuilder& MenuBuilder, const TArray<FAssetData> 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<USkeletalMesh>(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<USkeletalMeshSocket>()};
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
@@ -1,131 +0,0 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FContentBrowserModule>(TEXT("ContentBrowser"))
};
TArray<FContentBrowserMenuExtender_SelectedAssets>& ContextMenuDelegates{
ContentBrowserModule.GetAllAssetViewContextMenuExtenders()
};
ContextMenuDelegates.Add(FContentBrowserMenuExtender_SelectedAssets::CreateStatic(&OnExtendAssetSelectionMenu));
}
TSharedRef<FExtender> FSGContentBrowserExtension_Skeleton::OnExtendAssetSelectionMenu(
const TArray<FAssetData>& SelectedAssets)
{
TSharedRef<FExtender> Extender{MakeShared<FExtender>()};
Extender->AddMenuExtension(
"CommonAssetActions",
EExtensionHook::After,
nullptr,
FMenuExtensionDelegate::CreateStatic(&AssetExtenderCallback, SelectedAssets)
);
return MoveTemp(Extender);
}
void FSGContentBrowserExtension_Skeleton::AssetExtenderCallback(
FMenuBuilder& MenuBuilder, const TArray<FAssetData> 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<USkeleton>(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
@@ -1,239 +0,0 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FSGAssetTypeActions_SkeletalMesh> AssetActions;
TSharedPtr<FSGSkeletalMeshEditorCommands> EditorCommands;
TSharedPtr<FExtender> MenuExtender;
TSharedPtr<FExtender> 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> FSGSkeletalMeshEditor::GetInstance()
{
static TSharedPtr<FSGSkeletalMeshEditor> Instance{MakeShared<FSGSkeletalMeshEditor>()};
return Instance.ToSharedRef();
}
FSGSkeletalMeshEditor::FSGSkeletalMeshEditor()
: Pimpl(TUniquePtr<FImpl, FImplDeleter>(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<FSGAssetTypeActions_SkeletalMesh>();
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
AssetTools.RegisterAssetTypeActions(Pimpl->AssetActions.ToSharedRef());
}
void FSGSkeletalMeshEditor::RegisterCommands() const
{
if (Pimpl->EditorCommands.IsValid())
{
Pimpl->EditorCommands.Reset();
}
Pimpl->EditorCommands = MakeShared<FSGSkeletalMeshEditorCommands>();
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<FExtender>();
ISkeletalMeshEditorModule& SkeletonEditorModule{
FModuleManager::LoadModuleChecked<ISkeletalMeshEditorModule>("SkeletalMeshEditor")
};
const TSharedPtr<FExtensibilityManager> 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<FExtender>();
ISkeletalMeshEditorModule& SkeletalMeshEditorModule{
FModuleManager::LoadModuleChecked<ISkeletalMeshEditorModule>("SkeletalMeshEditor")
};
const TSharedPtr<FExtensibilityManager> 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
@@ -1,91 +0,0 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FSGSkeletalMeshEditorCommands>{
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<FUICommandList>();
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
@@ -1,91 +0,0 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FSGSkeletonEditorCommands>{
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<FUICommandList>();
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
@@ -33,27 +33,26 @@
*/ */
#include "SGEditor/SGSkeletonEditor.h" #include "SGEditor/SGSocketsEditor.h"
#include "Framework/MultiBox/MultiBoxExtender.h" #include "Framework/MultiBox/MultiBoxExtender.h"
#include "ISkeletonEditor.h"
#include "ISkeletonEditorModule.h" #include "ISkeletonEditorModule.h"
#include "Modules/ModuleManager.h" #include "Modules/ModuleManager.h"
#include "SGEditor/SGAssetTypeActions_Skeleton.h" // #include "SGEditor/SGAssetTypeActions_Skeleton.h"
#include "SGEditor/SGPluginStyle.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 * Member variables
************************/ ************************/
TSharedPtr<FSGAssetTypeActions_Skeleton> AssetActions; // TSharedPtr<FSGAssetTypeActions_Skeleton> AssetActions;
TSharedPtr<FSGSkeletonEditorCommands> EditorCommands; TSharedPtr<FSGSocketsEditorCommands> EditorCommands;
TSharedPtr<FExtender> MenuExtender; TSharedPtr<FExtender> MenuExtender;
TSharedPtr<FExtender> ToolbarExtender; TSharedPtr<FExtender> ToolbarExtender;
@@ -61,13 +60,13 @@ struct FSGSkeletonEditor::FImpl
* Owner object * Owner object
************************/ ************************/
FSGSkeletonEditor* Owner; FSGSocketsEditor* Owner;
/************************ /************************
* Constructor / Destructor * Constructor / Destructor
************************/ ************************/
explicit FImpl(FSGSkeletonEditor* InOwner); explicit FImpl(FSGSocketsEditor* InOwner);
~FImpl(); ~FImpl();
/************************ /************************
@@ -78,20 +77,20 @@ struct FSGSkeletonEditor::FImpl
FImpl& operator=(const FImpl& Rhs) = default; FImpl& operator=(const FImpl& Rhs) = default;
}; };
TSharedRef<FSGSkeletonEditor> FSGSkeletonEditor::GetInstance() TSharedRef<FSGSocketsEditor> FSGSocketsEditor::GetInstance()
{ {
static TSharedPtr<FSGSkeletonEditor> Instance{MakeShared<FSGSkeletonEditor>()}; static TSharedPtr<FSGSocketsEditor> Instance{MakeShared<FSGSocketsEditor>()};
return Instance.ToSharedRef(); return Instance.ToSharedRef();
} }
FSGSkeletonEditor::FSGSkeletonEditor() FSGSocketsEditor::FSGSocketsEditor()
: Pimpl(TUniquePtr<FImpl, FImplDeleter>(new FImpl{this}, PimplDeleter)) : Pimpl(TUniquePtr<FImpl, FImplDeleter>(new FImpl{this}, PimplDeleter))
{ {
} }
FSGSkeletonEditor::~FSGSkeletonEditor() = default; FSGSocketsEditor::~FSGSocketsEditor() = default;
void FSGSkeletonEditor::Initialize() const void FSGSocketsEditor::Initialize() const
{ {
//RegisterAssetTypeActions(); //RegisterAssetTypeActions();
RegisterCommands(); RegisterCommands();
@@ -99,35 +98,35 @@ void FSGSkeletonEditor::Initialize() const
ExtendToolbar(); ExtendToolbar();
} }
void FSGSkeletonEditor::Deinitialize() const void FSGSocketsEditor::Deinitialize() const
{ {
UnregisterCommands(); UnregisterCommands();
} }
void FSGSkeletonEditor::RegisterAssetTypeActions() const void FSGSocketsEditor::RegisterAssetTypeActions() const
{ {
if (Pimpl->AssetActions.IsValid()) // if (Pimpl->AssetActions.IsValid())
{ // {
Pimpl->AssetActions.Reset(); // Pimpl->AssetActions.Reset();
} // }
Pimpl->AssetActions = MakeShared<FSGAssetTypeActions_Skeleton>(); // Pimpl->AssetActions = MakeShared<FSGAssetTypeActions_Skeleton>();
//
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get(); // IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
//
AssetTools.RegisterAssetTypeActions(Pimpl->AssetActions.ToSharedRef()); // AssetTools.RegisterAssetTypeActions(Pimpl->AssetActions.ToSharedRef());
} }
void FSGSkeletonEditor::RegisterCommands() const void FSGSocketsEditor::RegisterCommands() const
{ {
if (Pimpl->EditorCommands.IsValid()) if (Pimpl->EditorCommands.IsValid())
{ {
Pimpl->EditorCommands.Reset(); Pimpl->EditorCommands.Reset();
} }
Pimpl->EditorCommands = MakeShared<FSGSkeletonEditorCommands>(); Pimpl->EditorCommands = MakeShared<FSGSocketsEditorCommands>();
Pimpl->EditorCommands->RegisterCommands(); Pimpl->EditorCommands->RegisterCommands();
} }
void FSGSkeletonEditor::UnregisterCommands() const void FSGSocketsEditor::UnregisterCommands() const
{ {
if (!Pimpl->EditorCommands.IsValid()) if (!Pimpl->EditorCommands.IsValid())
{ {
@@ -136,7 +135,7 @@ void FSGSkeletonEditor::UnregisterCommands() const
Pimpl->EditorCommands->Unregister(); Pimpl->EditorCommands->Unregister();
} }
void FSGSkeletonEditor::ExtendMenu() const void FSGSocketsEditor::ExtendMenu() const
{ {
if (Pimpl->MenuExtender.IsValid()) if (Pimpl->MenuExtender.IsValid())
{ {
@@ -144,14 +143,30 @@ void FSGSkeletonEditor::ExtendMenu() const
} }
Pimpl->MenuExtender = MakeShared<FExtender>(); Pimpl->MenuExtender = MakeShared<FExtender>();
ISkeletonEditorModule& SkeletalMeshEditorModule{
FModuleManager::LoadModuleChecked<ISkeletonEditorModule>("SkeletalMeshEditor")
};
ISkeletonEditorModule& SkeletonEditorModule{ ISkeletonEditorModule& SkeletonEditorModule{
FModuleManager::LoadModuleChecked<ISkeletonEditorModule>("SkeletonEditor") FModuleManager::LoadModuleChecked<ISkeletonEditorModule>("SkeletonEditor")
}; };
const TSharedPtr<FExtensibilityManager> ExtensibilityManager{
const TSharedPtr<FExtensibilityManager> SkeletalMeshExtensibilityManager{
SkeletalMeshEditorModule.GetMenuExtensibilityManager()
};
const TSharedPtr<FExtensibilityManager> SkeletonExtensibilityManager{
SkeletonEditorModule.GetMenuExtensibilityManager() 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; return;
} }
@@ -165,21 +180,31 @@ void FSGSkeletonEditor::ExtendMenu() const
MenuBuilder.BeginSection(FName{TEXT("SenseGlove")}, LOCTEXT("SenseGloveHeading", "SenseGlove")); MenuBuilder.BeginSection(FName{TEXT("SenseGlove")}, LOCTEXT("SenseGloveHeading", "SenseGlove"));
{ {
MenuBuilder.AddMenuEntry( MenuBuilder.AddMenuEntry(
Pimpl->EditorCommands->AddSenseGloveSockets, Pimpl->EditorCommands->SenseGloveAddSockets,
NAME_None, NAME_None,
LOCTEXT("AddSenseGloveSocketsLabel", "Add SenseGlove Sockets"), LOCTEXT("SenseGloveAddSocketsLabel", "Add SenseGlove Sockets"),
LOCTEXT( 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(), FSGPluginStyle::GetStyleSetIcon(),
NAME_None); 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(); 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()) if (Pimpl->ToolbarExtender.IsValid())
{ {
@@ -187,20 +212,36 @@ void FSGSkeletonEditor::ExtendToolbar() const
} }
Pimpl->ToolbarExtender = MakeShared<FExtender>(); Pimpl->ToolbarExtender = MakeShared<FExtender>();
ISkeletonEditorModule& SkeletalMeshEditorModule{
FModuleManager::LoadModuleChecked<ISkeletonEditorModule>("SkeletalMeshEditor")
};
ISkeletonEditorModule& SkeletonEditorModule{ ISkeletonEditorModule& SkeletonEditorModule{
FModuleManager::LoadModuleChecked<ISkeletonEditorModule>("SkeletonEditor") FModuleManager::LoadModuleChecked<ISkeletonEditorModule>("SkeletonEditor")
}; };
const TSharedPtr<FExtensibilityManager> ExtensibilityManager{
const TSharedPtr<FExtensibilityManager> SkeletalMeshExtensibilityManager{
SkeletalMeshEditorModule.GetToolBarExtensibilityManager()
};
const TSharedPtr<FExtensibilityManager> SkeletonExtensibilityManager{
SkeletonEditorModule.GetToolBarExtensibilityManager() 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; return;
} }
Pimpl->ToolbarExtender->AddToolBarExtension( Pimpl->ToolbarExtender->AddToolBarExtension(
FName{TEXT("Skeleton")}, FName{TEXT("SkeletalMesh")},
EExtensionHook::After, EExtensionHook::After,
Pimpl->EditorCommands->CommandList, Pimpl->EditorCommands->CommandList,
FToolBarExtensionDelegate::CreateLambda([&](FToolBarBuilder& ToolbarBuilder)-> void FToolBarExtensionDelegate::CreateLambda([&](FToolBarBuilder& ToolbarBuilder)-> void
@@ -208,11 +249,22 @@ void FSGSkeletonEditor::ExtendToolbar() const
ToolbarBuilder.BeginSection(FName{TEXT("SenseGlove")}); ToolbarBuilder.BeginSection(FName{TEXT("SenseGlove")});
{ {
ToolbarBuilder.AddToolBarButton( ToolbarBuilder.AddToolBarButton(
Pimpl->EditorCommands->AddSenseGloveSockets, Pimpl->EditorCommands->SenseGloveAddSockets,
NAME_None, NAME_None,
LOCTEXT("AddSenseGloveSocketsLabel", "Add SenseGlove Sockets"), LOCTEXT("SenseGloveAddSocketsLabel", "Add SenseGlove Sockets"),
LOCTEXT( 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(), FSGPluginStyle::GetStyleSetIcon(),
NAME_None, NAME_None,
FNewMenuDelegate{} FNewMenuDelegate{}
@@ -221,17 +273,18 @@ void FSGSkeletonEditor::ExtendToolbar() const
ToolbarBuilder.EndSection(); 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) : 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; delete P;
} }
@@ -0,0 +1,224 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<USkeletalMesh>(EditedAsset)};
USkeleton* EditedSkeleton{Cast<USkeleton>(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<USkeletalMesh>(EditedAsset)};
USkeleton* EditedSkeleton{Cast<USkeleton>(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<FSGSocketsEditorCommands>{
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<FUICommandList>();
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
@@ -35,11 +35,9 @@
#include "SenseGloveEditorModule.h" #include "SenseGloveEditorModule.h"
#include "SGEditor/SGContentBrowserExtension_SkeletalMesh.h" #include "SGEditor/SGContentBrowserExtension.h"
#include "SGEditor/SGContentBrowserExtension_Skeleton.h"
#include "SGEditor/SGPluginStyle.h" #include "SGEditor/SGPluginStyle.h"
#include "SGEditor/SGSkeletalMeshEditor.h" #include "SGEditor/SGSocketsEditor.h"
#include "SGEditor/SGSkeletonEditor.h"
#include "SGLog/SGLog.h" #include "SGLog/SGLog.h"
#define LOCTEXT_NAMESPACE "FSenseGloveEditorModule" #define LOCTEXT_NAMESPACE "FSenseGloveEditorModule"
@@ -49,10 +47,8 @@ void FSenseGloveEditorModule::StartupModule()
SGLOG("Starting up SenseGloveEditor module..."); SGLOG("Starting up SenseGloveEditor module...");
FSGPluginStyle::GetInstance()->Initialize(); FSGPluginStyle::GetInstance()->Initialize();
FSGContentBrowserExtension_SkeletalMesh::Register(); FSGContentBrowserExtension::Register();
FSGContentBrowserExtension_Skeleton::Register(); FSGSocketsEditor::GetInstance()->Initialize();
FSGSkeletalMeshEditor::GetInstance()->Initialize();
FSGSkeletonEditor::GetInstance()->Initialize();
} }
void FSenseGloveEditorModule::PreUnloadCallback() void FSenseGloveEditorModule::PreUnloadCallback()
@@ -69,8 +65,7 @@ void FSenseGloveEditorModule::ShutdownModule()
{ {
SGLOG("Shutting down SenseGloveEditor module..."); SGLOG("Shutting down SenseGloveEditor module...");
FSGSkeletalMeshEditor::GetInstance()->Deinitialize(); FSGSocketsEditor::GetInstance()->Deinitialize();
FSGSkeletonEditor::GetInstance()->Deinitialize();
FSGPluginStyle::GetInstance()->Deinitialize(); FSGPluginStyle::GetInstance()->Deinitialize();
} }
@@ -50,7 +50,7 @@ class IToolkitHost;
class UClass; class UClass;
class UObject; class UObject;
class FSGAssetTypeActions_SkeletalMesh : public FAssetTypeActions_Base class SENSEGLOVEEDITOR_API FSGAssetTypeActions_SkeletalMesh : public FAssetTypeActions_Base
{ {
public: public:
virtual FText GetName() const override; virtual FText GetName() const override;
@@ -50,7 +50,7 @@ class IToolkitHost;
class UClass; class UClass;
class UObject; class UObject;
class FSGAssetTypeActions_Skeleton : public FAssetTypeActions_Base class SENSEGLOVEEDITOR_API FSGAssetTypeActions_Skeleton : public FAssetTypeActions_Base
{ {
public: public:
virtual FText GetName() const override; virtual FText GetName() const override;
@@ -36,23 +36,37 @@
#pragma once #pragma once
#include "Containers/Array.h" #include "Containers/Array.h"
#include "HAL/Platform.h"
#include "Templates/SharedPointer.h" #include "Templates/SharedPointer.h"
struct FAssetData; class IAssetEditorInstance;
class FMenuBuilder; class UAssetEditorSubsystem;
class USkeletalMesh; class USkeletalMesh;
class USkeleton;
class UObject;
class FSGContentBrowserExtension_SkeletalMesh class SENSEGLOVEEDITOR_API FSGAssetUtils final
{ {
public: public:
static void Register(); static UAssetEditorSubsystem* GetAssetEditorSubsystem();
static TSharedRef<FExtender> OnExtendAssetSelectionMenu(const TArray<FAssetData>& SelectedAssets); static UObject* GetEditedAsset();
static void AssetExtenderCallback(FMenuBuilder& MenuBuilder, const TArray<FAssetData> SelectedAssets);
static void AddSenseGloveSockets(USkeletalMesh* SkeletalMesh); static bool IsAssetEditorOpenForAsset(UObject* Asset);
static TArray<IAssetEditorInstance*> 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: private:
FSGContentBrowserExtension_SkeletalMesh() = delete; struct FImpl;
virtual ~FSGContentBrowserExtension_SkeletalMesh() = delete;
private:
FSGAssetUtils() = delete;
virtual ~FSGAssetUtils() = delete;
}; };
@@ -40,19 +40,21 @@
struct FAssetData; struct FAssetData;
class FMenuBuilder; class FMenuBuilder;
class USkeleton; class USkeletalMesh;
class FSGContentBrowserExtension_Skeleton class SENSEGLOVEEDITOR_API FSGContentBrowserExtension
{ {
public: public:
static void Register(); static void Register();
protected:
static TSharedRef<FExtender> OnExtendAssetSelectionMenu(const TArray<FAssetData>& SelectedAssets); static TSharedRef<FExtender> OnExtendAssetSelectionMenu(const TArray<FAssetData>& SelectedAssets);
static void AssetExtenderCallback(FMenuBuilder& MenuBuilder, const TArray<FAssetData> SelectedAssets); static void AssetExtenderCallback(FMenuBuilder& MenuBuilder, const TArray<FAssetData> SelectedAssets);
static void AddSenseGloveSockets(USkeleton* Skeleton); private:
struct FImpl;
private: private:
FSGContentBrowserExtension_Skeleton() = delete; FSGContentBrowserExtension() = delete;
virtual ~FSGContentBrowserExtension_Skeleton() = delete; virtual ~FSGContentBrowserExtension() = delete;
}; };
@@ -40,7 +40,7 @@
class ISlateStyle; class ISlateStyle;
class FSGPluginStyle final : public FSlateStyleSet class SENSEGLOVEEDITOR_API FSGPluginStyle final : public FSlateStyleSet
{ {
public: public:
FORCEINLINE static TSharedRef<FSGPluginStyle> GetInstance() FORCEINLINE static TSharedRef<FSGPluginStyle> GetInstance()
@@ -49,12 +49,6 @@ public:
return Instance.ToSharedRef(); return Instance.ToSharedRef();
} }
FORCEINLINE static const FSlateStyleSet& GetStyleSet()
{
const FSlateStyleSet& StyleSet{GetInstance()->GetStyleSet()};
return StyleSet;
}
FORCEINLINE static const FName& GetStyleSetIconName() FORCEINLINE static const FName& GetStyleSetIconName()
{ {
static const FName IconName{TEXT("SenseGlove.Icon")}; static const FName IconName{TEXT("SenseGlove.Icon")};
@@ -1,73 +0,0 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FSGSkeletalMeshEditor> GetInstance();
private:
struct FImpl;
struct FImplDeleter
{
void operator()(const FImpl* P) const;
};
TUniquePtr<FImpl, FImplDeleter> 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;
};
@@ -1,54 +0,0 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FSGSkeletonEditorCommands>
{
public:
TSharedPtr<FUICommandList> CommandList;
TSharedPtr<FUICommandInfo> AddSenseGloveSockets;
public:
FSGSkeletonEditorCommands();
virtual ~FSGSkeletonEditorCommands() override;
public:
virtual void RegisterCommands() override;
};
@@ -38,10 +38,10 @@
#include "Framework/MultiBox/MultiBoxExtender.h" #include "Framework/MultiBox/MultiBoxExtender.h"
#include "Templates/SharedPointer.h" #include "Templates/SharedPointer.h"
class FSGSkeletonEditor class SENSEGLOVEEDITOR_API FSGSocketsEditor
{ {
public: public:
static TSharedRef<FSGSkeletonEditor> GetInstance(); static TSharedRef<FSGSocketsEditor> GetInstance();
private: private:
struct FImpl; struct FImpl;
@@ -55,8 +55,8 @@ private:
FImplDeleter PimplDeleter; FImplDeleter PimplDeleter;
public: public:
FSGSkeletonEditor(); FSGSocketsEditor();
virtual ~FSGSkeletonEditor(); virtual ~FSGSocketsEditor();
public: public:
void Initialize() const; void Initialize() const;
@@ -39,15 +39,19 @@
#include "Framework/Commands/UICommandList.h" #include "Framework/Commands/UICommandList.h"
#include "Templates/SharedPointer.h" #include "Templates/SharedPointer.h"
class FSGSkeletalMeshEditorCommands final : public TCommands<FSGSkeletalMeshEditorCommands> class SENSEGLOVEEDITOR_API FSGSocketsEditorCommands final : public TCommands<FSGSocketsEditorCommands>
{ {
public: public:
TSharedPtr<FUICommandList> CommandList; TSharedPtr<FUICommandList> CommandList;
TSharedPtr<FUICommandInfo> AddSenseGloveSockets; TSharedPtr<FUICommandInfo> SenseGloveAddSockets;
TSharedPtr<FUICommandInfo> SenseGloveClearExistingSockets;
private:
struct FImpl;
public: public:
FSGSkeletalMeshEditorCommands(); FSGSocketsEditorCommands();
virtual ~FSGSkeletalMeshEditorCommands() override; virtual ~FSGSocketsEditorCommands() override;
public: public:
virtual void RegisterCommands() override; virtual void RegisterCommands() override;
@@ -56,7 +56,15 @@ public class SenseGloveEditor : ModuleRules
"Slate", "Slate",
"SlateCore", "SlateCore",
"UnrealEd", "UnrealEd",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"SenseGloveLog", "SenseGloveLog",
"SenseGloveSettings",
"SenseGloveTypes",
} }
); );
} }
@@ -37,7 +37,8 @@
FSGVirtualHandGrabSettings::FSGVirtualHandGrabSettings() FSGVirtualHandGrabSettings::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}, FVector{0.04f, 0.04f, 0.04f},
FName{TEXT("GrabThumbCollider")}, FName{TEXT("GrabThumbCollider")},
FName{TEXT("GrabIndexCollider")}, FName{TEXT("GrabIndexCollider")},
@@ -47,12 +48,14 @@ FSGVirtualHandGrabSettings::FSGVirtualHandGrabSettings()
} }
FSGVirtualHandGrabSettings::FSGVirtualHandGrabSettings( FSGVirtualHandGrabSettings::FSGVirtualHandGrabSettings(
const FName& InPalmAttachPointSocketName, const FName& InGrabAttachPointSocketName,
const FTransform& InGrabAttachPointSocketTransform,
const FVector& InDefaultColliderSize, const FVector& InDefaultColliderSize,
const FName& InThumbColliderSocketName, const FName& InThumbColliderSocketName,
const FName& InIndexColliderSocketName, const FName& InIndexColliderSocketName,
const FName& InMiddleColliderSocketName) const FName& InMiddleColliderSocketName)
: PalmAttachPointSocketName{InPalmAttachPointSocketName}, : GrabAttachPointSocketName{InGrabAttachPointSocketName},
GrabAttachPointSocketTransform{InGrabAttachPointSocketTransform},
DefaultColliderSize{InDefaultColliderSize}, DefaultColliderSize{InDefaultColliderSize},
ThumbColliderSocketName{InThumbColliderSocketName}, ThumbColliderSocketName{InThumbColliderSocketName},
IndexColliderSocketName{InIndexColliderSocketName}, IndexColliderSocketName{InIndexColliderSocketName},
@@ -35,6 +35,8 @@
#include "SGSettings/SGVirtualHandMeshSettings.h" #include "SGSettings/SGVirtualHandMeshSettings.h"
#include "UObject/UnrealNames.h"
#include "Engine/SkeletalMesh.h" #include "Engine/SkeletalMesh.h"
FSGVirtualHandMeshSettings::FSGVirtualHandMeshSettings() FSGVirtualHandMeshSettings::FSGVirtualHandMeshSettings()
@@ -35,6 +35,8 @@
#pragma once #pragma once
#include "HAL/Platform.h"
#include "Math/Color.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "SGDebugGizmoSettings.generated.h" #include "SGDebugGizmoSettings.generated.h"
@@ -35,6 +35,7 @@
#pragma once #pragma once
#include "HAL/Platform.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "SGHandTrackingSettings.generated.h" #include "SGHandTrackingSettings.generated.h"
@@ -35,6 +35,7 @@
#pragma once #pragma once
#include "HAL/Platform.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "SGInitializationSettings.generated.h" #include "SGInitializationSettings.generated.h"
@@ -35,6 +35,7 @@
#pragma once #pragma once
#include "HAL/Platform.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "SGSettings/SGGloveTrackingSettings.h" #include "SGSettings/SGGloveTrackingSettings.h"
@@ -35,6 +35,8 @@
#pragma once #pragma once
#include "HAL/Platform.h"
#include "Math/Rotator.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "UObject/SoftObjectPtr.h" #include "UObject/SoftObjectPtr.h"
@@ -35,6 +35,9 @@
#pragma once #pragma once
#include "HAL/Platform.h"
#include "Math/Color.h"
#include "Math/Vector.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "SGTypes/SGDebugTypes.h" #include "SGTypes/SGDebugTypes.h"
@@ -35,6 +35,9 @@
#pragma once #pragma once
#include "Math/Transform.h"
#include "Math/Vector.h"
#include "UObject/NameTypes.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "UObject/SoftObjectPtr.h" #include "UObject/SoftObjectPtr.h"
@@ -52,7 +55,10 @@ struct SENSEGLOVESETTINGS_API FSGVirtualHandGrabSettings
public: public:
UPROPERTY(Config, EditDefaultsOnly, Category="Grab") UPROPERTY(Config, EditDefaultsOnly, Category="Grab")
FName PalmAttachPointSocketName; FName GrabAttachPointSocketName;
UPROPERTY(Config, EditDefaultsOnly, Category="Grab")
FTransform GrabAttachPointSocketTransform;
UPROPERTY(Config, EditDefaultsOnly, Category="Grab") UPROPERTY(Config, EditDefaultsOnly, Category="Grab")
FVector DefaultColliderSize; FVector DefaultColliderSize;
@@ -70,7 +76,8 @@ public:
FSGVirtualHandGrabSettings(); FSGVirtualHandGrabSettings();
FSGVirtualHandGrabSettings( FSGVirtualHandGrabSettings(
const FName& InPalmAttachPointSocketName, const FName& InGrabAttachPointSocketName,
const FTransform& InGrabAttachPointSocketTransform,
const FVector& InDefaultColliderSize, const FVector& InDefaultColliderSize,
const FName& InThumbColliderSocketName, const FName& InThumbColliderSocketName,
const FName& InIndexColliderSocketName, const FName& InIndexColliderSocketName,
@@ -35,6 +35,12 @@
#pragma once #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/ObjectMacros.h"
#include "UObject/SoftObjectPtr.h" #include "UObject/SoftObjectPtr.h"
@@ -35,6 +35,8 @@
#pragma once #pragma once
#include "Math/Vector.h"
#include "UObject/NameTypes.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "UObject/SoftObjectPtr.h" #include "UObject/SoftObjectPtr.h"
@@ -36,6 +36,8 @@
#pragma once #pragma once
#include "InputCoreTypes.h" #include "InputCoreTypes.h"
#include "Math/Rotator.h"
#include "Math/Vector.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "SGSettings/SGDebugGizmoSettings.h" #include "SGSettings/SGDebugGizmoSettings.h"
@@ -899,10 +899,12 @@ USGHandPose* FSGXRTracker::FImpl::GetHandPose(const bool bRight)
FTransform FSGXRTracker::FImpl::GetMeshRawBoneRefTransform(const bool bRight, const FName& BoneName) FTransform FSGXRTracker::FImpl::GetMeshRawBoneRefTransform(const bool bRight, const FName& BoneName)
{ {
const FSGVirtualHandMeshSettings& MeshSettings{GetVirtualHandSettings().MeshSettings};
const TSoftObjectPtr<USkeletalMesh>& HandMesh{ const TSoftObjectPtr<USkeletalMesh>& HandMesh{
bRight bRight
? GetVirtualHandSettings().MeshSettings.LeftHandReferenceMesh ? MeshSettings.LeftHandReferenceMesh
: GetVirtualHandSettings().MeshSettings.RightHandReferenceMesh : MeshSettings.RightHandReferenceMesh
}; };
if (HandMesh.IsValid()) if (HandMesh.IsValid())
@@ -924,26 +926,24 @@ FTransform FSGXRTracker::FImpl::GetMeshRawBoneRefTransform(const bool bRight, co
return Transform; return Transform;
} }
const FSGVirtualHandMeshSettings& Settings{GetVirtualHandSettings().MeshSettings};
ensureAlwaysMsgf( ensureAlwaysMsgf(
bRight bRight
? Settings.RightHandDefaultReferenceBoneTransforms.Contains(BoneName) ? MeshSettings.RightHandDefaultReferenceBoneTransforms.Contains(BoneName)
: Settings.LeftHandDefaultReferenceBoneTransforms.Contains(BoneName), : MeshSettings.LeftHandDefaultReferenceBoneTransforms.Contains(BoneName),
TEXT("Invalid bone name '%s'!"), *BoneName.ToString()); TEXT("Invalid bone name '%s'!"), *BoneName.ToString());
const FTransform& Transform{ const FTransform& Transform{
bRight bRight
? Settings.RightHandDefaultReferenceBoneTransforms[BoneName] ? MeshSettings.RightHandDefaultReferenceBoneTransforms[BoneName]
: Settings.LeftHandDefaultReferenceBoneTransforms[BoneName] : MeshSettings.LeftHandDefaultReferenceBoneTransforms[BoneName]
}; };
return Transform; return Transform;
} }
FTransform FSGXRTracker::FImpl::GetMeshWristChildBoneRefTransform(const bool bRight, const FName& BoneName) FTransform FSGXRTracker::FImpl::GetMeshWristChildBoneRefTransform(const bool bRight, const FName& BoneName)
{ {
FQuat RootRotationCorrection{ const FSGVirtualHandMeshSettings& MeshSettings{GetVirtualHandSettings().MeshSettings};
GetVirtualHandSettings().MeshSettings.RootBoneRotationCorrection FQuat RootRotationCorrection{MeshSettings.RootBoneRotationCorrection};
};
const FTransform& RootBoneRefTransform{GetMeshRawBoneRefTransform(bRight, EHandKeypoint::Wrist)}; const FTransform& RootBoneRefTransform{GetMeshRawBoneRefTransform(bRight, EHandKeypoint::Wrist)};
const FTransform& RawTransform{GetMeshRawBoneRefTransform(bRight, BoneName)}; const FTransform& RawTransform{GetMeshRawBoneRefTransform(bRight, BoneName)};
FQuat CorrectedRootBoneRefRotation{MoveTemp(RootRotationCorrection) * RootBoneRefTransform.GetRotation()}; FQuat CorrectedRootBoneRefRotation{MoveTemp(RootRotationCorrection) * RootBoneRefTransform.GetRotation()};
@@ -956,9 +956,8 @@ FTransform FSGXRTracker::FImpl::GetMeshWristChildBoneRefTransform(const bool bRi
FTransform FSGXRTracker::FImpl::GetMeshFingerStartBoneRefTransform( FTransform FSGXRTracker::FImpl::GetMeshFingerStartBoneRefTransform(
const bool bRight, const EHandKeypoint Parent, const FName& BoneName) const bool bRight, const EHandKeypoint Parent, const FName& BoneName)
{ {
FQuat RootRotationCorrection{ const FSGVirtualHandMeshSettings& MeshSettings{GetVirtualHandSettings().MeshSettings};
GetVirtualHandSettings().MeshSettings.RootBoneRotationCorrection FQuat RootRotationCorrection{MeshSettings.RootBoneRotationCorrection};
};
const FTransform& RootBoneRefTransform{GetMeshRawBoneRefTransform(bRight, EHandKeypoint::Wrist)}; const FTransform& RootBoneRefTransform{GetMeshRawBoneRefTransform(bRight, EHandKeypoint::Wrist)};
FQuat CorrectedRootBoneRefRotation{MoveTemp(RootRotationCorrection) * RootBoneRefTransform.GetRotation()}; FQuat CorrectedRootBoneRefRotation{MoveTemp(RootRotationCorrection) * RootBoneRefTransform.GetRotation()};
FTransform CorrectedRootBoneRefTransform{ FTransform CorrectedRootBoneRefTransform{
@@ -35,7 +35,7 @@
#pragma once #pragma once
#include "CoreTypes.h" #include "HAL/Platform.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
UENUM(BlueprintType) UENUM(BlueprintType)
@@ -36,6 +36,7 @@
#pragma once #pragma once
#include "CoreTypes.h" #include "CoreTypes.h"
#include "HAL/Platform.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
/** /**
@@ -35,6 +35,7 @@
#pragma once #pragma once
#include "Containers/Array.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "SGFloatArray.generated.h" #include "SGFloatArray.generated.h"
@@ -35,6 +35,7 @@
#pragma once #pragma once
#include "Containers/Array.h"
#include "UObject/NameTypes.h" #include "UObject/NameTypes.h"
#include "SGNameArray.generated.h" #include "SGNameArray.generated.h"
@@ -35,6 +35,7 @@
#pragma once #pragma once
#include "Containers/Array.h"
#include "Math/Quat.h" #include "Math/Quat.h"
#include "SGQuatArray.generated.h" #include "SGQuatArray.generated.h"
@@ -35,6 +35,7 @@
#pragma once #pragma once
#include "Containers/Array.h"
#include "Math/Rotator.h" #include "Math/Rotator.h"
#include "SGRotatorArray.generated.h" #include "SGRotatorArray.generated.h"
@@ -35,7 +35,7 @@
#pragma once #pragma once
#include "CoreTypes.h" #include "HAL/Platform.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
/** /**
@@ -35,7 +35,7 @@
#pragma once #pragma once
#include "CoreTypes.h" #include "HAL/Platform.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
/** /**
@@ -35,6 +35,7 @@
#pragma once #pragma once
#include "HAL/Platform.h"
#include "Math/Vector.h" #include "Math/Vector.h"
#include "SGVectorArray.generated.h" #include "SGVectorArray.generated.h"