diff --git a/content/blog/2024-10-02-vrm4u.md b/content/blog/2024-10-02-vrm4u.md new file mode 100644 index 0000000..0719bd1 --- /dev/null +++ b/content/blog/2024-10-02-vrm4u.md @@ -0,0 +1,147 @@ ++++ +date = "2024-10-02" +tags = ["vrm","ue"] +title = "ue5.5と最新のvmc事情" ++++ + +unreal engine 5.5.0 previewがインストールできるようになっています。今回は最新環境のvmc事情を解説します。 + +## vmcとは + +webカメラから表情や動きをキャラクターに反映させるためのものです。vmcはprotocolとclientがあります。大抵はprotocolを指します。webカメラからの読み取りをcaptureといいます。つまり、captureとprotocolとclientを組みわせて動作します。ueで使うには更にvmcを受信してキャラクターに反映させるpluginが必要です。わけがわからないと思いますが、そんな感じです。 + +- https://github.com/sh-akira/VirtualMotionCapture +- https://github.com/ruyo/VRM4U +- https://github.com/HAL9HARUKU/VMC4UE +- https://github.com/HAL9HARUKU/ueOSC +- https://github.com/HAL9HARUKU/VRMMapExporter +- https://github.com/vrm-c/UniVRM + +個人的な話ですが、現在、ue5.4, ue5.5の環境下ではbuild後に表情が動かない問題に悩まされています。 + +## vmc4ueをue5.5でbuildしてみよう + +> この手順は興味がある人以外は読み飛ばすことを推奨します。表情を動かしたい人はこの方法で問題を解決することができません。 + +`vmc4ue`はue5.1までしかbuildされていません。そこでue5.5でbuildして使えるようにしてみます。 + +まずc++でprojectを作成し、patchをsrcに当てて`$project/Plugins/`に入れます。projectをueで開くとbuildされます。正常に終了するとeditorが開きます。 + +なお、patchはbuildが通るよう適当に作ったものです。vmcは動きますが、表情は動きませんでした。 + +```cpp:VMC4UEBlueprintFunctionLibrary.cpp.patch +--- ./VMC4UE/VMC4UE/Source/VMC4UE/Source/VMC4UEBlueprintFunctionLibrary.cpp ++++ ./VMC4UEBlueprintFunctionLibrary.cpp +@@ -119,27 +119,29 @@ UVMC4UEStreamingSkeletalMeshTransform* UVMC4UEBlueprin + { + return nullptr; + } +- ++ ++ UVMC4UEStreamingSkeletalMeshTransform* StreamingSkeletalMeshTransform = nullptr; ++ ++ // Try to get existing transform + { +- // Get + FRWScopeLock RWScopeLock(OSCManager->RWLock, FRWScopeLockType::SLT_ReadOnly); +- auto StreamingSkeletalMeshTransform = OSCManager->StreamingSkeletalMeshTransformMap.Find(Port); +- if (StreamingSkeletalMeshTransform != nullptr) ++ auto FoundTransform = OSCManager->StreamingSkeletalMeshTransformMap.Find(Port); ++ if (FoundTransform != nullptr) + { +- return *StreamingSkeletalMeshTransform; ++ return *FoundTransform; + } + } ++ ++ // Create new transform if not found + { +- // Create + FRWScopeLock RWScopeLock(OSCManager->RWLock, FRWScopeLockType::SLT_Write); +- auto StreamingSkeletalMeshTransform = OSCManager->StreamingSkeletalMeshTransformMap.Find(Port); +- if (StreamingSkeletalMeshTransform != nullptr) ++ auto FoundTransform = OSCManager->StreamingSkeletalMeshTransformMap.Find(Port); ++ if (FoundTransform != nullptr) + { +- return *StreamingSkeletalMeshTransform; ++ return *FoundTransform; + } +- UVMC4UEStreamingSkeletalMeshTransform* NewStreamingSkeletalMeshTransform = NewObject(); + +- //FRWScopeLock RWScopeLock2(NewStreamingSkeletalMeshTransform->RWLock, FRWScopeLockType::SLT_Write); ++ UVMC4UEStreamingSkeletalMeshTransform* NewStreamingSkeletalMeshTransform = NewObject(); + OSCManager->StreamingSkeletalMeshTransformMap.Emplace(Port, NewStreamingSkeletalMeshTransform); + + // Bind Port +@@ -149,9 +151,10 @@ UVMC4UEStreamingSkeletalMeshTransform* UVMC4UEBlueprin + + OSCManager->OscReceivers.Emplace(OscReceiver); + +- return NewStreamingSkeletalMeshTransform; ++ StreamingSkeletalMeshTransform = NewStreamingSkeletalMeshTransform; + } +- return nullptr; ++ ++ return StreamingSkeletalMeshTransform; + } + + void UVMC4UEBlueprintFunctionLibrary::RefreshConnection(float Seconds) +``` + +```cpp:VMC4UEBoneMappingAssetFactory.cpp.patch +--- ./VMC4UE/Source/VMC4UEEd/Source/VMC4UEBoneMappingAssetFactory.cpp ++++ ./VMC4UEBoneMappingAssetFactory.cpp +@@ -5,6 +5,8 @@ + #include "../../VMC4UE/Include/VMC4UEStreamingData.h" + #include "Dom/JsonObject.h" + #include "JsonObjectConverter.h" ++#include "UObject/ConstructorHelpers.h" ++#include "UObject/UObjectGlobals.h" + + UVMC4UEBoneMappingAssetFactory::UVMC4UEBoneMappingAssetFactory(const FObjectInitializer &ObjectInitializer) + : Super(ObjectInitializer) +@@ -26,11 +28,12 @@ + return UVMC4UEVRMMapping::StaticClass(); + } + ++ + UObject *UVMC4UEBoneMappingAssetFactory::FactoryCreateText(UClass *InClass, UObject *InParent, FName InName, EObjectFlags Flags, UObject *Context, const TCHAR *Type, const TCHAR *&Buffer, const TCHAR *BuferEnd, FFeedbackContext *Warn) + { + FString TextData = FString(Buffer); + +- UVMC4UEVRMMapping *NewAsset = CastChecked(StaticConstructObject_Internal(InClass, InParent, InName, Flags)); ++ UVMC4UEVRMMapping* NewAsset = NewObject(InParent, InClass, InName, Flags); + if (!IsValid(NewAsset)) + { + return nullptr; +``` + +```sh +$ git clone https://github.com/HAL9HARUKU/VMC4UE + +$ patch -u ./VMC4UE/VMC4UE/Source/VMC4UE/Source/VMC4UEBlueprintFunctionLibrary.cpp < VMC4UEBlueprintFunctionLibrary.cpp.patch +$ patch -u ./VMC4UE/VMC4UE/Source/VMC4UEEd/Source/VMC4UEBoneMappingAssetFactory.cpp < VMC4UEBoneMappingAssetFactory.cpp.patch +``` + +## vrm4u(vmc)はbuild後にも表情を動かせるのだろうか + +わかりません。私の環境下では動きませんでした。他の人も動かない可能性がありますが、issueを読む限り動くようにも思えます。 + +それではどうするのか。livelink(face)を使います。ここからは少しめんどくさいことになりますが、かなり多くのアプリが必要です。 + +- 動き : webcam motion capture(vmc送信) + vseeface(vmc受信/送信) + vrm4u(vmc受信) +- 表情 : iphone + livelink face + +まず、`vrm4u`は`webcam motion capture`のvmcを直接受信できません。なので、一旦、vseefaceなどのclientで受信する必要があります。それをvrm4uで受信するportに送信します。なお、webcam(vmc)は`vmc4ue`では直接受信できて動きます。また、xr-animator, vseeface, vmc(client)も受信できて動いています。不思議な現象です。 + +vrm4u(vmc)はbuild後は表情が動かないので、表情はlivelinkを使用します。これはiphoneに`livelink face`というappがあります。ueでいくつかのpluginを有効にします。 + +- live link +- apple arkit +- apple arkit face support + +`/VRM4U/Util/Actor/latest/BP_LiveLinkFace`をmapにおいて、`live link subject -> iphone`, `target actor sk -> SK_$name`を設定します。 + +![](/img/ue-2024-10-01-7.36.38.png) + +これでbuildすると表情を動かすことができます。