Add Games File
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
|
||||
#include "AnimNode_VrmVMC.h"
|
||||
#include "AnimationRuntime.h"
|
||||
#include "Animation/AnimInstanceProxy.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
|
||||
#include "VRM4U_VMCSubsystem.h"
|
||||
#include "VrmAssetListObject.h"
|
||||
#include "VrmMetaObject.h"
|
||||
#include "VrmUtil.h"
|
||||
|
||||
#include <algorithm>
|
||||
/////////////////////////////////////////////////////
|
||||
// FAnimNode_ModifyBone
|
||||
|
||||
|
||||
FAnimNode_VrmVMC::FAnimNode_VrmVMC()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
FAnimNode_VrmVMC::~FAnimNode_VrmVMC()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//void FAnimNode_VrmVMC::Update_AnyThread(const FAnimationUpdateContext& Context) {
|
||||
// Super::Update_AnyThread(Context);
|
||||
// //Context.GetDeltaTime();
|
||||
//}
|
||||
|
||||
void FAnimNode_VrmVMC::Initialize_AnyThread(const FAnimationInitializeContext& Context) {
|
||||
Super::Initialize_AnyThread(Context);
|
||||
|
||||
VrmMetaObject_Internal = const_cast<UVrmMetaObject*>(VrmMetaObject);
|
||||
if (VrmMetaObject_Internal == nullptr && EnableAutoSearchMetaData) {
|
||||
VrmAssetListObject_Internal = VRMUtil::GetAssetListObject(VRMGetSkinnedAsset(Context.AnimInstanceProxy->GetSkelMeshComponent()));
|
||||
if (VrmAssetListObject_Internal) {
|
||||
VrmMetaObject_Internal = VrmAssetListObject_Internal->VrmMetaObject;
|
||||
}
|
||||
}
|
||||
|
||||
UVRM4U_VMCSubsystem* subsystem = GEngine->GetEngineSubsystem<UVRM4U_VMCSubsystem>();
|
||||
if (subsystem == nullptr) return;
|
||||
{
|
||||
auto *s = subsystem->FindOrAddServer(ServerAddress, Port);
|
||||
if (s) {
|
||||
s->bForceUpdate = bForceUpdate;
|
||||
}
|
||||
}
|
||||
bCreateServer = true;
|
||||
|
||||
// init global reftransform
|
||||
{
|
||||
const auto Skeleton = Context.AnimInstanceProxy->GetSkeleton();
|
||||
const auto RefSkeleton = Skeleton->GetReferenceSkeleton();
|
||||
const auto& RefSkeletonTransform = RefSkeleton.GetRefBonePose();
|
||||
|
||||
RefSkeletonTransform_global = RefSkeletonTransform;
|
||||
{
|
||||
auto& g = RefSkeletonTransform_global;
|
||||
for (int i = 0; i < RefSkeletonTransform.Num(); ++i) {
|
||||
int parent = RefSkeleton.GetParentIndex(i);
|
||||
if (parent < 0) continue;
|
||||
g[i] = g[i] * g[parent];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
void FAnimNode_VrmVMC::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) {
|
||||
Super::CacheBones_AnyThread(Context);
|
||||
}
|
||||
|
||||
|
||||
void FAnimNode_VrmVMC::UpdateInternal(const FAnimationUpdateContext& Context){
|
||||
Super::UpdateInternal(Context);
|
||||
}
|
||||
|
||||
|
||||
void FAnimNode_VrmVMC::GatherDebugData(FNodeDebugData& DebugData)
|
||||
{
|
||||
FString DebugLine = DebugData.GetNodeName(this);
|
||||
|
||||
DebugLine += "(";
|
||||
AddDebugNodeData(DebugLine);
|
||||
//DebugLine += FString::Printf(TEXT(" Target: %s)"), *BoneToModify.BoneName.ToString());
|
||||
//DebugLine += FString::Printf(TEXT(" Target: %s)"), *BoneNameToModify.ToString());
|
||||
DebugData.AddDebugItem(DebugLine);
|
||||
|
||||
ComponentPose.GatherDebugData(DebugData);
|
||||
}
|
||||
|
||||
void FAnimNode_VrmVMC::EvaluateComponentPose_AnyThread(FComponentSpacePoseContext& Output) {
|
||||
Super::EvaluateComponentPose_AnyThread(Output);
|
||||
}
|
||||
|
||||
void FAnimNode_VrmVMC::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms)
|
||||
{
|
||||
check(OutBoneTransforms.Num() == 0);
|
||||
|
||||
if (VrmMetaObject_Internal == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
UVRM4U_VMCSubsystem* subsystem = GEngine->GetEngineSubsystem<UVRM4U_VMCSubsystem>();
|
||||
if (subsystem == nullptr) return;
|
||||
|
||||
const auto Skeleton = Output.AnimInstanceProxy->GetSkeleton();
|
||||
const auto RefSkeleton = Output.AnimInstanceProxy->GetSkeleton()->GetReferenceSkeleton();
|
||||
const FTransform ComponentTransform = Output.AnimInstanceProxy->GetComponentTransform();
|
||||
const auto& RefSkeletonTransform = RefSkeleton.GetRefBonePose();
|
||||
|
||||
if (RefSkeletonTransform_global.Num() != RefSkeletonTransform.Num()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<int> boneIndexTable;
|
||||
TArray<FBoneTransform> tmpOutTransform;
|
||||
|
||||
FVMCData VMCData;
|
||||
if (subsystem->CopyVMCData(VMCData, ServerAddress, Port) == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (VMCData.BoneData.Num() == 0 && VMCData.CurveData.Num() == 0) {
|
||||
return;
|
||||
}
|
||||
if (bApplyPerfectSync) {
|
||||
for (auto& c : VMCData.CurveData) {
|
||||
if (c.Key.Contains(TEXT("BlendShape.")) == false) continue;
|
||||
|
||||
c.Key.RightChopInline(11); // [blendahape.]
|
||||
}
|
||||
}
|
||||
|
||||
TMap<FString, FTransform>& BoneTrans = VMCData.BoneData;
|
||||
|
||||
const auto &MorphList = Output.AnimInstanceProxy->GetSkelMeshComponent()->GetSkinnedAsset()->GetMorphTargets();
|
||||
for (auto& c : VMCData.CurveData) {
|
||||
#if UE_VERSION_OLDER_THAN(5,3,0)
|
||||
{
|
||||
SmartName::UID_Type NewUID;
|
||||
FName NewName = *c.Key;
|
||||
|
||||
NewUID = Skeleton->GetUIDByName(USkeleton::AnimCurveMappingName, NewName);
|
||||
|
||||
Output.Curve.Set(NewUID, c.Value);
|
||||
}
|
||||
#else
|
||||
auto m = MorphList.FindByPredicate([&c](const TObjectPtr<UMorphTarget> &m) {
|
||||
FString s = c.Key;
|
||||
|
||||
if (m->GetName().Compare(s, ESearchCase::IgnoreCase)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (m) {
|
||||
Output.Curve.Set(*m->GetName(), c.Value);
|
||||
} else {
|
||||
Output.Curve.Set(*c.Key, c.Value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
{
|
||||
bool bFirstBone = true;
|
||||
|
||||
for (const auto &t : VrmMetaObject_Internal->humanoidBoneTable) {
|
||||
#if UE_VERSION_OLDER_THAN(4,27,0)
|
||||
auto *tmpVal = BoneTrans.Find(t.Key.ToLower());
|
||||
if (tmpVal == nullptr) continue;
|
||||
|
||||
auto modelBone = *tmpVal;
|
||||
#else
|
||||
auto filterList= BoneTrans.FilterByPredicate([&t](TPair<FString, FTransform> a) {
|
||||
return a.Key.Compare(t.Key, ESearchCase::IgnoreCase) == 0;
|
||||
}
|
||||
);
|
||||
if (filterList.Num() != 1) continue;
|
||||
auto modelBone = filterList.begin()->Value;
|
||||
#endif
|
||||
|
||||
|
||||
int index = RefSkeleton.FindBoneIndex(*t.Value);
|
||||
if (index < 0) continue;
|
||||
|
||||
FBoneTransform f(FCompactPoseBoneIndex(index), modelBone);
|
||||
//f.Transform.SetRotation(FQuat::Identity);
|
||||
|
||||
if (bFirstBone) {
|
||||
bFirstBone = false;
|
||||
|
||||
if (bUseRemoteCenterPos) {
|
||||
auto v = f.Transform.GetLocation() * ModelRelativeScale;
|
||||
f.Transform.SetTranslation(v);
|
||||
} else {
|
||||
auto v = RefSkeletonTransform[index].GetLocation();
|
||||
f.Transform.SetTranslation(v);
|
||||
}
|
||||
|
||||
|
||||
// root bone
|
||||
FTransform RootTrans;
|
||||
for (auto& a : BoneTrans) {
|
||||
if (a.Key.Compare(TEXT("root"), ESearchCase::IgnoreCase)) {
|
||||
continue;
|
||||
}
|
||||
RootTrans = a.Value;
|
||||
break;
|
||||
}
|
||||
if (index == 0) {
|
||||
// hip == root
|
||||
f.Transform.SetTranslation(f.Transform.GetLocation() + RootTrans.GetLocation());
|
||||
} else {
|
||||
// orig root
|
||||
FBoneTransform bt(FCompactPoseBoneIndex(0), RootTrans);
|
||||
tmpOutTransform.Add(bt);
|
||||
boneIndexTable.Add(0);
|
||||
}
|
||||
|
||||
} else {
|
||||
FVector v = RefSkeletonTransform[index].GetLocation();
|
||||
f.Transform.SetTranslation(v);
|
||||
}
|
||||
|
||||
if (bIgnoreLocalRotation){
|
||||
auto r_refg = RefSkeletonTransform_global[index].GetRotation();
|
||||
auto r_ref = RefSkeletonTransform[index].GetRotation();
|
||||
auto r_vmc = f.Transform.GetRotation();
|
||||
|
||||
auto r_dif = r_refg.Inverse() * r_vmc * r_refg;
|
||||
|
||||
f.Transform.SetRotation(r_ref * r_dif);
|
||||
}
|
||||
//f.Transform.SetTranslation(RefSkeletonTransform[index].GetLocation());
|
||||
tmpOutTransform.Add(f);
|
||||
boneIndexTable.Add(index);
|
||||
}
|
||||
|
||||
// bone hierarchy
|
||||
for (int i = 1; i < tmpOutTransform.Num(); ++i) {
|
||||
int parentBoneIndex = RefSkeleton.GetParentIndex(boneIndexTable[i]);
|
||||
int parentInTable = boneIndexTable.Find(parentBoneIndex);
|
||||
|
||||
for (int j = 0; j < 1000; ++j) {
|
||||
if (parentInTable >= 0) {
|
||||
break;
|
||||
}
|
||||
if (parentBoneIndex < 0) break;
|
||||
|
||||
// add outtransform with ref bone
|
||||
FBoneTransform f(FCompactPoseBoneIndex(parentBoneIndex), RefSkeletonTransform[parentBoneIndex]);
|
||||
tmpOutTransform.Add(f);
|
||||
boneIndexTable.Add(parentBoneIndex);
|
||||
|
||||
parentBoneIndex = RefSkeleton.GetParentIndex(parentBoneIndex);
|
||||
parentInTable = boneIndexTable.Find(parentBoneIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// sort
|
||||
tmpOutTransform.Sort(FCompareBoneTransformIndex());
|
||||
boneIndexTable.Sort();
|
||||
}
|
||||
|
||||
for (int i = 0; i < tmpOutTransform.Num(); ++i) {
|
||||
auto& a = tmpOutTransform[i];
|
||||
|
||||
int parentBoneIndex = RefSkeleton.GetParentIndex(boneIndexTable[i]);
|
||||
|
||||
int parentInHandTable = boneIndexTable.Find(parentBoneIndex);
|
||||
if (parentInHandTable >= 0) {
|
||||
a.Transform *= tmpOutTransform[parentInHandTable].Transform;
|
||||
} else {
|
||||
// root
|
||||
auto BoneSpace = EBoneControlSpace::BCS_ParentBoneSpace;
|
||||
FAnimationRuntime::ConvertBoneSpaceTransformToCS(ComponentTransform, Output.Pose, a.Transform, a.BoneIndex, BoneSpace);
|
||||
}
|
||||
OutBoneTransforms.Add(a);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool FAnimNode_VrmVMC::IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones)
|
||||
{
|
||||
// if both bones are valid
|
||||
//return (BoneToModify.IsValidToEvaluate(RequiredBones));
|
||||
return true;
|
||||
}
|
||||
|
||||
void FAnimNode_VrmVMC::InitializeBoneReferences(const FBoneContainer& RequiredBones)
|
||||
{
|
||||
//BoneToModify.Initialize(RequiredBones);
|
||||
}
|
||||
|
||||
void FAnimNode_VrmVMC::ConditionalDebugDraw(FPrimitiveDrawInterface* PDI, USkeletalMeshComponent* PreviewSkelMeshComp, bool bPreviewForeground) const
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
|
||||
if (VrmMetaObject_Internal == nullptr || PreviewSkelMeshComp == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (PreviewSkelMeshComp->GetWorld() == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESceneDepthPriorityGroup Priority = SDPG_World;
|
||||
if (bPreviewForeground) Priority = SDPG_Foreground;
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
#include "VRM4UCapture.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "VRM4UCaptureLog.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "Internationalization/Internationalization.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "VRM4UMisc"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogVRM4UCapture);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// FSpriterImporterModule
|
||||
|
||||
class FVRM4UCaptureModule : public FDefaultModuleImpl
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ShutdownModule() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IMPLEMENT_MODULE(FVRM4UCaptureModule, VRM4UCapture);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,8 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
//#include "VRMImporterModule.h"
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
//#include "VRMImporterModule.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogVRM4UCapture, Verbose, All);
|
||||
|
||||
//#define SPRITER_IMPORT_ERROR(FormatString, ...) \
|
||||
// if (!bSilent) { UE_LOG(LogVRM4UMIsc, Warning, FormatString, __VA_ARGS__); }
|
||||
//#define SPRITER_IMPORT_WARNING(FormatString, ...) \
|
||||
// if (!bSilent) { UE_LOG(LogVRM4UMIsc, Warning, FormatString, __VA_ARGS__); }
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "VRM4U_VMCSubsystem.h"
|
||||
#include "VrmVMCObject.h"
|
||||
|
||||
#include "Engine/Engine.h"
|
||||
#include "UObject/StrongObjectPtr.h"
|
||||
#include "Misc/ScopeLock.h"
|
||||
#include "OSCManager.h"
|
||||
#include "OSCServer.h"
|
||||
|
||||
bool UVRM4U_VMCSubsystem::CopyVMCData(FVMCData &data, FString ServerAddress, int port) {
|
||||
for (int i = 0; i < VMCObjectList.Num(); ++i) {
|
||||
auto a = VMCObjectList[i].Get();
|
||||
if (a == nullptr) continue;
|
||||
|
||||
if (a->ServerName == ServerAddress && a->port == port) {
|
||||
a->CopyVMCData(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UVRM4U_VMCSubsystem::GetVMCData(TMap<FString, FTransform>& BoneData, TMap<FString, float>& CurveData, FString ServerAddress, int port) {
|
||||
FVMCData d;
|
||||
if (CopyVMCData(d, ServerAddress, port) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BoneData = d.BoneData;
|
||||
CurveData = d.CurveData;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
UVrmVMCObject* UVRM4U_VMCSubsystem::FindOrAddServer(const FString ServerAddress, int port) {
|
||||
|
||||
bool bFound = false;
|
||||
for (int i = 0; i < VMCObjectList.Num(); ++i) {
|
||||
auto a = VMCObjectList[i].Get();
|
||||
if (a == nullptr) continue;
|
||||
|
||||
if (a->ServerName == ServerAddress && a->port == port) {
|
||||
bFound = true;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
if (bFound == false) {
|
||||
int ind = VMCObjectList.AddDefaulted();
|
||||
VMCObjectList[ind].Reset(NewObject<UVrmVMCObject>());
|
||||
VMCObjectList[ind]->CreateServer(ServerAddress, port);
|
||||
return VMCObjectList[ind].Get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UVRM4U_VMCSubsystem::DestroyVMCServer(const FString ServerAddress, int port) {
|
||||
for (int i = 0; i < VMCObjectList.Num(); ++i) {
|
||||
auto a = VMCObjectList[i].Get();
|
||||
if (a == nullptr) continue;
|
||||
|
||||
if (a->ServerName == ServerAddress && a->port == port) {
|
||||
a->DestroyServer();
|
||||
VMCObjectList[i].Reset(nullptr);
|
||||
VMCObjectList.RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void UVRM4U_VMCSubsystem::DestroyVMCServerAll() {
|
||||
while (VMCObjectList.Num()) {
|
||||
auto a = VMCObjectList[0].Get();
|
||||
if (a) {
|
||||
a->DestroyServer();
|
||||
}
|
||||
VMCObjectList[0].Reset(nullptr);
|
||||
VMCObjectList.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool UVRM4U_VMCSubsystem::CreateVMCServer(const FString ServerAddress, int port) {
|
||||
return (FindOrAddServer(ServerAddress, port) != nullptr);
|
||||
}
|
||||
|
||||
void UVRM4U_VMCSubsystem::ClearData(const FString ServerAddress, int port) {
|
||||
for (int i = 0; i < VMCObjectList.Num(); ++i) {
|
||||
auto a = VMCObjectList[i].Get();
|
||||
if (a == nullptr) continue;
|
||||
|
||||
if (a->ServerName == ServerAddress && a->port == port) {
|
||||
VMCObjectList[i]->ClearVMCData();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UVRM4U_VMCSubsystem::Initialize(FSubsystemCollectionBase& Collection) {
|
||||
Super::Initialize(Collection);
|
||||
|
||||
#if WITH_EDITOR
|
||||
FEditorDelegates::BeginStandaloneLocalPlay.AddLambda([&](const uint32 processID) {
|
||||
this->DestroyVMCServerAll();
|
||||
});
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
#include "VrmCaptureManager.h"
|
||||
#include "VrmMocopiReceiver.h"
|
||||
#include "VRM4UCaptureLog.h"
|
||||
|
||||
UVrmMocopiReceiver* UVrmCaptureManager::CreateVrmMocopiReceiver(FString InReceiveIPAddress, int32 InPort, bool bInMulticastLoopback, bool bInStartListening, FString ServerName, UObject* Outer)
|
||||
{
|
||||
{
|
||||
FString InAddress = InReceiveIPAddress;
|
||||
if (!InAddress.IsEmpty() && InAddress != TEXT("0"))
|
||||
{
|
||||
}
|
||||
|
||||
bool bCanBind = false;
|
||||
bool bAppendPort = false;
|
||||
if (ISocketSubsystem* SocketSys = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM))
|
||||
{
|
||||
const TSharedPtr<FInternetAddr> Addr = SocketSys->GetLocalHostAddr(*GLog, bCanBind);
|
||||
if (Addr.IsValid())
|
||||
{
|
||||
InAddress = Addr->ToString(bAppendPort);
|
||||
}
|
||||
}
|
||||
UE_LOG(LogVRM4UCapture, Display, TEXT("MocopiReceiver ReceiveAddress not specified. Using LocalHost IP: '%s'"), *InAddress);
|
||||
}
|
||||
|
||||
if (ServerName.IsEmpty())
|
||||
{
|
||||
ServerName = FString::Printf(TEXT("MocopiReceiver_%s"), *FGuid::NewGuid().ToString(EGuidFormats::Digits));
|
||||
}
|
||||
|
||||
UVrmMocopiReceiver* NewMocopiServer = nullptr;
|
||||
if (Outer)
|
||||
{
|
||||
NewMocopiServer = NewObject<UVrmMocopiReceiver>(Outer, *ServerName, RF_StrongRefOnFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogVRM4UCapture, Warning, TEXT("Outer object not set. MocopiReceiver may be garbage collected if not referenced."));
|
||||
NewMocopiServer = NewObject<UVrmMocopiReceiver>(GetTransientPackage(), *ServerName);
|
||||
}
|
||||
|
||||
if (NewMocopiServer)
|
||||
{
|
||||
//NewMocopiServer->SetMulticastLoopback(bInMulticastLoopback);
|
||||
if (NewMocopiServer->SetAddress(InReceiveIPAddress, InPort))
|
||||
{
|
||||
if (bInStartListening)
|
||||
{
|
||||
NewMocopiServer->Listen();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogVRM4UCapture, Warning, TEXT("Failed to parse ReceiveAddress '%s' for MocopiReceiver."), *InReceiveIPAddress);
|
||||
}
|
||||
|
||||
return NewMocopiServer;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,563 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
|
||||
#include "VrmMocopiReceiver.h"
|
||||
|
||||
#include "VRM4UCaptureLog.h"
|
||||
|
||||
#include "CoreGlobals.h"
|
||||
#include "Stats/Stats.h"
|
||||
#include "Tickable.h"
|
||||
#include "Common/UdpSocketReceiver.h"
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
FMocopiReceiverProxy::FMocopiReceiverProxy(UVrmMocopiReceiver * InReceiver):
|
||||
Receiver(InReceiver){
|
||||
FIPv4Address::Parse("0.0.0.0", ReceiveIPAddress);
|
||||
}
|
||||
|
||||
FMocopiReceiverProxy::~FMocopiReceiverProxy() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void FMocopiReceiverProxy::Stop() {
|
||||
if (SocketReceiver) {
|
||||
delete SocketReceiver;
|
||||
SocketReceiver = nullptr;
|
||||
}
|
||||
if (Socket) {
|
||||
Socket->Close();
|
||||
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(Socket);
|
||||
Socket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FMocopiReceiverProxy::Listen() {
|
||||
FString InServerName = "udp_serv";
|
||||
//int32 Port = 12351;
|
||||
bool bMulticastLoopback = true;
|
||||
|
||||
FUdpSocketBuilder Builder(*InServerName);
|
||||
Builder.BoundToPort(Port);
|
||||
if (ReceiveIPAddress.IsMulticastAddress())
|
||||
{
|
||||
Builder.JoinedToGroup(ReceiveIPAddress);
|
||||
if (bMulticastLoopback)
|
||||
{
|
||||
Builder.WithMulticastLoopback();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bMulticastLoopback)
|
||||
{
|
||||
//UE_LOG(LogOSC, Warning, TEXT("OSCServer '%s' ReceiveIPAddress provided is not a multicast address. Not respecting MulticastLoopback boolean."),
|
||||
// *InServerName);
|
||||
}
|
||||
Builder.BoundToAddress(ReceiveIPAddress);
|
||||
}
|
||||
|
||||
Socket = Builder.Build();
|
||||
if (Socket)
|
||||
{
|
||||
SocketReceiver = new FUdpSocketReceiver(Socket, FTimespan::FromMilliseconds(100), *(InServerName + TEXT("_ListenerThread")));
|
||||
SocketReceiver->OnDataReceived().BindRaw(this, &FMocopiReceiverProxy::OnPacketReceived);
|
||||
SocketReceiver->Start();
|
||||
|
||||
//UE_LOG(LogOSC, Display, TEXT("OSCServer '%s' Listening: %s:%d."), *InServerName, *ReceiveIPAddress.ToString(), Port);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is expected when the server isn't available, so it's not a Warning
|
||||
//UE_LOG(LogOSC, Display, TEXT("OSCServer '%s' failed to bind to socket on %s:%d. Check that the server is available on the specified address."), *InServerName, *ReceiveIPAddress.ToString(), Port);
|
||||
}
|
||||
}
|
||||
|
||||
bool FMocopiReceiverProxy::SetAddress(const FString& InReceiveIPAddress, int32 InPort) {
|
||||
if (!FIPv4Address::Parse(InReceiveIPAddress, ReceiveIPAddress))
|
||||
{
|
||||
//UE_LOG(LogOSC, Error, TEXT("Invalid ReceiveIPAddress '%s'. OSCServer ReceiveIP Address not updated."), *InReceiveIPAddress);
|
||||
return false;
|
||||
}
|
||||
|
||||
Port = InPort;
|
||||
return true;
|
||||
}
|
||||
void FMocopiReceiverProxy::Tick(float InDeltaTime) {
|
||||
|
||||
}
|
||||
|
||||
TStatId FMocopiReceiverProxy::GetStatId() const
|
||||
{
|
||||
RETURN_QUICK_DECLARE_CYCLE_STAT(FMocopiReceiverProxy, STATGROUP_Tickables);
|
||||
}
|
||||
|
||||
void FMocopiReceiverProxy::OnPacketReceived(const FArrayReaderPtr& InData, const FIPv4Endpoint& InEndpoint) {
|
||||
|
||||
RecvDataSet.Append(InData->GetData(), InData->Num());
|
||||
|
||||
// data size limit
|
||||
if (RecvDataSet.Num() >= 1024 * 64) {
|
||||
decltype(RecvDataSet) a;
|
||||
a.Append(RecvDataSet.GetData() + 1024*48, RecvDataSet.Num() - 1024*48);
|
||||
RecvDataSet = a;
|
||||
}
|
||||
|
||||
|
||||
//UE_LOG(LogVRM4UCapture, Verbose, TEXT("recv '%d' "), InData->Num());
|
||||
|
||||
struct LocalStructRawData {
|
||||
TArray<float> f;
|
||||
FQuat rot;
|
||||
FVector pos;
|
||||
|
||||
LocalStructRawData() {
|
||||
f.SetNum(7);
|
||||
}
|
||||
};
|
||||
|
||||
struct LocalStructTransData {
|
||||
//TArray<LocalStructRawData> data;
|
||||
|
||||
TArray<FTransform> transformLocal;
|
||||
TArray <FTransform> transformWorld;
|
||||
TArray <FTransform> transformVrmLocal;
|
||||
TArray <FTransform> transformBvhLocal;
|
||||
|
||||
|
||||
LocalStructTransData() {
|
||||
transformWorld.SetNum(MocopiData::BoneNum);
|
||||
transformLocal.SetNum(MocopiData::BoneNum);
|
||||
transformVrmLocal.SetNum(MocopiData::BoneNum);
|
||||
transformBvhLocal.SetNum(MocopiData::BoneNum);
|
||||
//data.SetNum(MocopiData::BoneNum);
|
||||
}
|
||||
};
|
||||
|
||||
LocalStructTransData d;
|
||||
|
||||
const char FIELD_HEAD[] = {
|
||||
'h', 'e', 'a', 'd',
|
||||
};
|
||||
|
||||
const char FIELD_SKDF[] = {
|
||||
's', 'k', 'd', 'f',
|
||||
};
|
||||
|
||||
const char FIELD_BTRS[] = {
|
||||
'b', 't', 'r', 's',
|
||||
};
|
||||
|
||||
|
||||
const char FIELD_TRAN[] = {
|
||||
't', 'r', 'a', 'n',
|
||||
};
|
||||
|
||||
const char FIELD_BNID[] = {
|
||||
'b', 'n', 'i', 'd',
|
||||
};
|
||||
|
||||
const char FIELD_FRAM[] = {
|
||||
'f', 'r', 'a', 'm',
|
||||
};
|
||||
|
||||
const char FIELD_FNUM[] = {
|
||||
'f', 'n', 'u', 'm',
|
||||
};
|
||||
|
||||
const char FIELD_TIME[] = {
|
||||
't', 'i', 'm', 'e',
|
||||
};
|
||||
|
||||
const int BoneChain[] = {
|
||||
0, // 0
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
7, // 11
|
||||
11, 12, 13,
|
||||
7, // 15
|
||||
15, 16, 17,
|
||||
0, //19
|
||||
19, 20, 21,
|
||||
0, //23
|
||||
23, 24, 25,
|
||||
};
|
||||
|
||||
auto GetValue = [&](uint8*& pData, uint32_t &length, int cur, const char field[4]) {
|
||||
|
||||
if (cur < 4) {
|
||||
return false;
|
||||
}
|
||||
{
|
||||
int t = 0;
|
||||
for (t = 0; t < 4; ++t) {
|
||||
if (field[t] != RecvDataSet.GetData()[cur + t]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (t == 4) {
|
||||
pData = (RecvDataSet.GetData() + cur + 4);
|
||||
length = *reinterpret_cast<uint32*>((RecvDataSet.GetData() + cur - 4));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
pData = nullptr;
|
||||
length = 0;
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
for (int i = RecvDataSet.Num() - 4; i >= 8; --i) {
|
||||
if (isalpha(RecvDataSet.GetData()[i]) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 length = 0;
|
||||
uint8* p = nullptr;
|
||||
if (GetValue(p, length, i, FIELD_FRAM)){
|
||||
|
||||
// p == value
|
||||
// p-4 == field
|
||||
// p-8 == packetsize
|
||||
//
|
||||
// i == field
|
||||
// i-4 == packetsize
|
||||
|
||||
uint16* packetSize = reinterpret_cast<uint16*>(p - 8);
|
||||
|
||||
//UE_LOG(LogVRM4UCapture, Verbose, TEXT("packetSize '%d'"), (*packetSize));
|
||||
|
||||
if ((*packetSize) + 8 <= RecvDataSet.Num()) {
|
||||
//UE_LOG(LogVRM4UCapture, Verbose, TEXT("shrink '%d' -> %d "), RecvDataSet.Num(), (*packetSize) + 8);
|
||||
|
||||
decltype(RecvDataSet) a;
|
||||
a.Append(RecvDataSet.GetData() + i - 4, (*packetSize) + 8);
|
||||
RecvDataSet = a;
|
||||
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
bool bUpdateData = false;
|
||||
for (int packetNo = 0; packetNo < 10; ++packetNo) {
|
||||
|
||||
int32 frame = -1;
|
||||
int32 time = -1;
|
||||
{
|
||||
bool bFindFieldSkdf = false;
|
||||
bool bFindFieldBtrs = false;
|
||||
bool bFindFieldFram = false;
|
||||
|
||||
float* TransformData = nullptr;
|
||||
uint16* boneid = nullptr;
|
||||
|
||||
int i = 0;
|
||||
for (i = 0; i < RecvDataSet.Num() - 4; ++i) {
|
||||
if (isalpha(RecvDataSet.GetData()[i]) == false) {
|
||||
continue;
|
||||
}
|
||||
uint32 length = 0;
|
||||
uint8* p = nullptr;
|
||||
|
||||
if (GetValue(p, length, i, FIELD_SKDF)) {
|
||||
//UE_LOG(LogVRM4UCapture, Verbose, TEXT("skdf %d"), bFindFieldFram);
|
||||
bFindFieldSkdf = true;
|
||||
|
||||
// remove read data
|
||||
FMemory::Memmove(RecvDataSet.GetData(), RecvDataSet.GetData() + (i+4), RecvDataSet.Num() - (i+4));
|
||||
RecvDataSet.SetNum(RecvDataSet.Num() - (i+4));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (bFindFieldFram == false) {
|
||||
if (GetValue(p, length, i, FIELD_FRAM)) {
|
||||
bFindFieldFram = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
int t = 0;
|
||||
for (t = 0; t < 4; ++t) {
|
||||
if (isalpha((RecvDataSet.GetData()[i + t])) == false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (t == 4) {
|
||||
UE_LOG(LogVRM4UCapture, Verbose, TEXT("recv '%c%c%c%c' "),
|
||||
*((char*)(RecvDataSet.GetData()) + i),
|
||||
*((char*)(RecvDataSet.GetData()) + i+1),
|
||||
*((char*)(RecvDataSet.GetData()) + i+2),
|
||||
*((char*)(RecvDataSet.GetData()) + i+3)
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (GetValue(p, length, i, FIELD_FNUM)) {
|
||||
frame = *reinterpret_cast<int32*>(p);
|
||||
}
|
||||
|
||||
if (GetValue(p, length, i, FIELD_TIME)) {
|
||||
time = *reinterpret_cast<int32*>(p);
|
||||
}
|
||||
|
||||
if (frame < 0) continue;
|
||||
|
||||
if (bFindFieldBtrs == false) {
|
||||
if (GetValue(p, length, i, FIELD_BTRS)) {
|
||||
bFindFieldBtrs = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (GetValue(p, length, i, FIELD_BNID)) {
|
||||
boneid = reinterpret_cast<uint16*>(p);
|
||||
}
|
||||
|
||||
if (GetValue(p, length, i, FIELD_TRAN)) {
|
||||
TransformData = reinterpret_cast<float*>(p);
|
||||
}
|
||||
|
||||
if (boneid && TransformData) {
|
||||
// mocopi
|
||||
{
|
||||
LocalStructRawData st;
|
||||
|
||||
st.rot = FQuat(-TransformData[0], -TransformData[1], TransformData[2], TransformData[3]);
|
||||
if (*boneid == 0) {
|
||||
st.rot = FRotator(0, 0, -90).Quaternion() * st.rot;
|
||||
}
|
||||
|
||||
st.pos.Set(TransformData[4], TransformData[5], -TransformData[6]);
|
||||
st.pos *= 100.f;
|
||||
|
||||
d.transformLocal[*boneid] = FTransform(st.rot, st.pos);
|
||||
}
|
||||
|
||||
// vrm
|
||||
{
|
||||
LocalStructRawData st;
|
||||
|
||||
d.transformVrmLocal[*boneid] = FTransform(st.rot, st.pos);
|
||||
st.rot = FQuat(-TransformData[0], -TransformData[1], TransformData[2], TransformData[3]);
|
||||
st.rot = FRotator(0, 0, -90).Quaternion() * st.rot * FRotator(0, 0, -90).Quaternion().Inverse();
|
||||
|
||||
st.pos.Set(TransformData[4], TransformData[6], TransformData[5]);
|
||||
st.pos *= 100.f;
|
||||
|
||||
d.transformVrmLocal[*boneid] = FTransform(st.rot, st.pos);
|
||||
}
|
||||
|
||||
if (*boneid == MocopiData::BoneNum-1) {
|
||||
|
||||
// remove read data
|
||||
FMemory::Memmove(RecvDataSet.GetData(), RecvDataSet.GetData() + i, RecvDataSet.Num() - i);
|
||||
RecvDataSet.SetNum(RecvDataSet.Num() - i);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
boneid = nullptr;
|
||||
TransformData = nullptr;
|
||||
}
|
||||
}// data loop
|
||||
if (bFindFieldSkdf) {
|
||||
continue;
|
||||
}
|
||||
if (bFindFieldFram == false) {
|
||||
break;
|
||||
}
|
||||
} // packet loop
|
||||
|
||||
|
||||
// no packet
|
||||
if (d.transformLocal.Num() != MocopiData::BoneNum) break;
|
||||
|
||||
d.transformWorld = d.transformLocal;
|
||||
|
||||
{
|
||||
auto v = d.transformWorld[0].GetLocation();
|
||||
d.transformWorld[0].SetLocation(FVector(v.X, v.Z, v.Y));
|
||||
}
|
||||
|
||||
for (int i = 1; i < MocopiData::BoneNum; ++i) {
|
||||
int parent = BoneChain[i];
|
||||
|
||||
auto r1 = d.transformWorld[parent].GetRotation();
|
||||
auto p1 = d.transformWorld[parent].GetLocation();
|
||||
|
||||
FTransform::Multiply(&d.transformWorld[i], &d.transformLocal[i], &d.transformWorld[parent]);
|
||||
}
|
||||
|
||||
FStructMocopiData md;
|
||||
if (frame>=0 && time>=0) {
|
||||
md.FrameNo = frame;
|
||||
md.Time = time;
|
||||
}
|
||||
md.MocopiTransformWorld = d.transformWorld;
|
||||
md.MocopiTransformLocal = d.transformLocal;
|
||||
md.VrmTransformLocal = d.transformVrmLocal;
|
||||
|
||||
{
|
||||
TMap<FString, int> BoneIdList = {
|
||||
{"hips", 0},
|
||||
{"leftUpperLeg", 19},
|
||||
{"rightUpperLeg", 23},
|
||||
{"leftLowerLeg", 20},
|
||||
{"rightLowerLeg", 24},
|
||||
{"leftFoot", 21},
|
||||
{"rightFoot", 25},
|
||||
{"spine", 3}, //
|
||||
{"chest", 6}, //
|
||||
{"upperChest", 7}, //
|
||||
{"neck", 8}, //
|
||||
{"head", 9},
|
||||
{"leftShoulder", 11},
|
||||
{"rightShoulder", 15},
|
||||
{"leftUpperArm", 12},
|
||||
{"rightUpperArm", 16},
|
||||
{"leftLowerArm", 13},
|
||||
{"rightLowerArm", 17},
|
||||
{"leftHand", 14},
|
||||
{"rightHand", 18},
|
||||
{"leftToes", 22},
|
||||
{"rightToes", 26},
|
||||
};
|
||||
|
||||
for (auto& b : BoneIdList) {
|
||||
md.VrmTransformBoneList.Add(b.Key, d.transformVrmLocal[b.Value]);
|
||||
}
|
||||
}
|
||||
|
||||
if (Receiver) {
|
||||
Receiver->OnPacketReceived(md);
|
||||
bUpdateData = true;
|
||||
}
|
||||
}// packet loop
|
||||
|
||||
if (bUpdateData && Receiver) {
|
||||
Receiver->PacketBroadcast();
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_LITTLE_ENDIAN
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
UVrmMocopiReceiver::UVrmMocopiReceiver(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer), ReceiverProxy(MakeUnique<FMocopiReceiverProxy>(this))
|
||||
{
|
||||
MocopiReceiveBuffer.Reserve(BufferNum);
|
||||
}
|
||||
|
||||
void UVrmMocopiReceiver::BeginDestroy() {
|
||||
Stop();
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
|
||||
bool UVrmMocopiReceiver::SetAddress(const FString& InReceiveIPAddress, int32 InPort) {
|
||||
return ReceiverProxy->SetAddress(InReceiveIPAddress, InPort);
|
||||
}
|
||||
|
||||
|
||||
void UVrmMocopiReceiver::Stop() {
|
||||
if (ReceiverProxy) {
|
||||
ReceiverProxy->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UVrmMocopiReceiver::Listen() {
|
||||
ReceiverProxy->Listen();
|
||||
}
|
||||
|
||||
|
||||
void UVrmMocopiReceiver::SetBufferNum(int32 Num) {
|
||||
FScopeLock ScopeLock(&BufferCS);
|
||||
Num = FMath::Max(Num, 1);
|
||||
BufferNum = Num;
|
||||
MocopiReceiveBuffer.Reserve(BufferNum);
|
||||
}
|
||||
|
||||
|
||||
void UVrmMocopiReceiver::GetNextFrameData(FStructMocopiData &data, bool &bEnable, bool &bUpdate){
|
||||
bEnable = false;
|
||||
bUpdate = false;
|
||||
|
||||
FScopeLock ScopeLock(&BufferCS);
|
||||
|
||||
if (MocopiReceiveBuffer.Num() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bEnable == false) {
|
||||
for (int i = MocopiReceiveBuffer.Num() - 2; i >= 0; --i) {
|
||||
if (MocopiReceiveBuffer[i].FrameNo == currentFrameNo) {
|
||||
data = MocopiReceiveBuffer[i + 1];
|
||||
bEnable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bEnable == false) {
|
||||
data = MocopiReceiveBuffer.Last();
|
||||
}
|
||||
}
|
||||
|
||||
if (data.FrameNo != currentFrameNo) {
|
||||
bUpdate = true;
|
||||
}
|
||||
currentFrameNo = data.FrameNo;
|
||||
currentTime = data.Time;
|
||||
bEnable = true;
|
||||
}
|
||||
|
||||
void UVrmMocopiReceiver::GetLatestFrameData(FStructMocopiData &data, bool &bEnable, bool &bUpdate){
|
||||
bEnable = false;
|
||||
bUpdate = false;
|
||||
|
||||
FScopeLock ScopeLock(&BufferCS);
|
||||
|
||||
if (MocopiReceiveBuffer.Num() <= 0) {
|
||||
return;
|
||||
}
|
||||
data = MocopiReceiveBuffer.Last();
|
||||
|
||||
if (data.FrameNo != currentFrameNo) {
|
||||
bUpdate = true;
|
||||
}
|
||||
currentFrameNo = data.FrameNo;
|
||||
currentTime = data.Time;
|
||||
bEnable = true;
|
||||
|
||||
}
|
||||
|
||||
void UVrmMocopiReceiver::PacketBroadcast() {
|
||||
FScopeLock ScopeLock(&BufferCS);
|
||||
OnReceived.Broadcast(MocopiReceiveBuffer.Last());
|
||||
}
|
||||
|
||||
void UVrmMocopiReceiver::OnPacketReceived(FStructMocopiData data) {
|
||||
FScopeLock ScopeLock(&BufferCS);
|
||||
|
||||
while (MocopiReceiveBuffer.Num() >= BufferNum - 1) {
|
||||
if (MocopiReceiveBuffer.Num() == 0) {
|
||||
break;
|
||||
}
|
||||
#if UE_VERSION_OLDER_THAN(4,26,0)
|
||||
MocopiReceiveBuffer.RemoveAt(0);
|
||||
#else
|
||||
MocopiReceiveBuffer.PopFront();
|
||||
#endif
|
||||
}
|
||||
MocopiReceiveBuffer.Add(data);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "VrmVMCObject.h"
|
||||
#include "VRM4U_VMCSubsystem.h"
|
||||
|
||||
#include "Engine/Engine.h"
|
||||
#include "UObject/StrongObjectPtr.h"
|
||||
#include "Misc/ScopeLock.h"
|
||||
#include "OSCManager.h"
|
||||
#include "OSCServer.h"
|
||||
|
||||
void UVrmVMCObject::DestroyServer() {
|
||||
ServerName = "";
|
||||
port = 0;
|
||||
|
||||
if (OSCServer.Get()) {
|
||||
OSCServer->Stop();
|
||||
}
|
||||
OSCServer.Reset(nullptr);
|
||||
}
|
||||
void UVrmVMCObject::CreateServer(FString inName, uint16 inPort) {
|
||||
ServerName = inName;
|
||||
port = inPort;
|
||||
#if UE_VERSION_OLDER_THAN(4,25,0)
|
||||
#else
|
||||
OSCServer.Reset(UOSCManager::CreateOSCServer(ServerName, port, true, true, FString(), this));
|
||||
|
||||
OSCServer->OnOscMessageReceivedNative.RemoveAll(nullptr);
|
||||
OSCServer->OnOscMessageReceivedNative.AddUObject(this, &UVrmVMCObject::OSCReceivedMessageEvent);
|
||||
|
||||
#if WITH_EDITOR
|
||||
OSCServer->SetTickInEditor(true);
|
||||
#endif // WITH_EDITOR
|
||||
#endif
|
||||
}
|
||||
|
||||
void UVrmVMCObject::OSCReceivedMessageEvent(const FOSCMessage& Message, const FString& IPAddress, uint16 Port) {
|
||||
|
||||
FOSCAddress a = UOSCManager::GetOSCMessageAddress(Message);
|
||||
FString addressPath = UOSCManager::GetOSCAddressFullPath(a);
|
||||
|
||||
TArray<FString> str;
|
||||
UOSCManager::GetAllStrings(Message, str);
|
||||
|
||||
TArray<float> curve;
|
||||
UOSCManager::GetAllFloats(Message, curve);
|
||||
|
||||
FTransform t;
|
||||
float f = 0;
|
||||
if (curve.Num() == 1) {
|
||||
f = curve[0];
|
||||
}
|
||||
if (curve.Num() >= 7) {
|
||||
t.SetLocation(FVector(-curve[0], curve[2], curve[1]) * 100.f);
|
||||
t.SetRotation(FQuat(-curve[3], curve[5], curve[4], curve[6]));
|
||||
}
|
||||
if (curve.Num() >= 10) {
|
||||
t.SetScale3D(FVector(curve[7], curve[9], curve[8]));
|
||||
}
|
||||
|
||||
if (addressPath == TEXT("/VMC/Ext/Root/Pos")) {
|
||||
VMCData.BoneData.FindOrAdd(str[0]) = t;
|
||||
bDataUpdated = true;
|
||||
}
|
||||
if (addressPath == TEXT("/VMC/Ext/Bone/Pos")) {
|
||||
VMCData.BoneData.FindOrAdd(str[0]) = t;
|
||||
bDataUpdated = true;
|
||||
}
|
||||
if (addressPath == TEXT("/VMC/Ext/Blend/Val")) {
|
||||
VMCData.CurveData.FindOrAdd(str[0]) = f;
|
||||
bDataUpdated = true;
|
||||
}
|
||||
|
||||
if (bDataUpdated == true){
|
||||
bool b = bForceUpdate;
|
||||
if (addressPath == TEXT("/VMC/Ext/OK")) {
|
||||
b = true;
|
||||
}
|
||||
if (addressPath == TEXT("/VMC/Ext/T")) {
|
||||
b = true;
|
||||
}
|
||||
if (addressPath == TEXT("/VMC/Ext/Blend/Apply")) {
|
||||
b = true;
|
||||
}
|
||||
|
||||
if (b) {
|
||||
FScopeLock lock(&cs);
|
||||
VMCData_Cache = VMCData;
|
||||
bDataUpdated = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
bool UVrmVMCObject::CopyVMCData(FVMCData& dst) {
|
||||
FScopeLock lock(&cs);
|
||||
dst = VMCData_Cache;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UVrmVMCObject::ClearVMCData() {
|
||||
FScopeLock lock(&cs);
|
||||
VMCData.ClearData();
|
||||
VMCData_Cache.ClearData();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "BoneContainer.h"
|
||||
#include "BonePose.h"
|
||||
#include "BoneControllers/AnimNode_ModifyBone.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
|
||||
#include "AnimNode_VrmVMC.generated.h"
|
||||
|
||||
class USkeletalMeshComponent;
|
||||
class UVrmMetaObject;
|
||||
class UVrmAssetListObject;
|
||||
|
||||
/**
|
||||
* Simple controller that replaces or adds to the translation/rotation of a single bone.
|
||||
*/
|
||||
USTRUCT(BlueprintInternalUseOnly)
|
||||
struct VRM4UCAPTURE_API FAnimNode_VrmVMC : public FAnimNode_SkeletalControlBase
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta = (PinHiddenByDefault))
|
||||
bool EnableAutoSearchMetaData = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta=(PinHiddenByDefault))
|
||||
const UVrmMetaObject *VrmMetaObject = nullptr;
|
||||
|
||||
#if UE_VERSION_OLDER_THAN(5,0,0)
|
||||
TAssetPtr<UVrmMetaObject> VrmMetaObject_Internal = nullptr;
|
||||
TAssetPtr<UVrmAssetListObject> VrmAssetListObject_Internal = nullptr;
|
||||
#else
|
||||
TSoftObjectPtr<UVrmMetaObject> VrmMetaObject_Internal = nullptr;
|
||||
TSoftObjectPtr<UVrmAssetListObject> VrmAssetListObject_Internal = nullptr;
|
||||
#endif
|
||||
|
||||
//UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta = (PinShownByDefault))
|
||||
//TMap<FString, FTransform> BoneTrans;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta = (PinShownByDefault))
|
||||
bool bUseRemoteCenterPos = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta = (PinShownByDefault))
|
||||
float ModelRelativeScale = 1.f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta = (PinShownByDefault))
|
||||
bool bIgnoreLocalRotation = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta = (PinShownByDefault))
|
||||
FString ServerAddress = "0.0.0.0";
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta = (PinShownByDefault))
|
||||
int Port = 39539;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta = (PinShownByDefault))
|
||||
bool bApplyPerfectSync = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Skeleton, meta = (PinHiddenByDefault))
|
||||
bool bForceUpdate = false;
|
||||
|
||||
bool bCreateServer = false;
|
||||
|
||||
TArray<FTransform> RefSkeletonTransform_global;
|
||||
|
||||
FAnimNode_VrmVMC();
|
||||
virtual ~FAnimNode_VrmVMC();
|
||||
|
||||
// FAnimNode_Base interface
|
||||
virtual void GatherDebugData(FNodeDebugData& DebugData) override;
|
||||
// End of FAnimNode_Base interface
|
||||
|
||||
// FAnimNode_SkeletalControlBase interface
|
||||
virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
|
||||
virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
|
||||
// virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override;
|
||||
virtual void EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) override;
|
||||
virtual void EvaluateComponentPose_AnyThread(FComponentSpacePoseContext& Output) override;
|
||||
virtual bool IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones) override;
|
||||
|
||||
virtual void UpdateInternal(const FAnimationUpdateContext& Context)override;
|
||||
// End of FAnimNode_SkeletalControlBase interface
|
||||
|
||||
virtual void ConditionalDebugDraw(FPrimitiveDrawInterface* PDI, USkeletalMeshComponent* PreviewSkelMeshComp, bool bPreviewForeground = false) const;
|
||||
|
||||
private:
|
||||
// FAnimNode_SkeletalControlBase interface
|
||||
virtual void InitializeBoneReferences(const FBoneContainer& RequiredBones) override;
|
||||
// End of FAnimNode_SkeletalControlBase interface
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Subsystems/EngineSubsystem.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
#include "UObject/StrongObjectPtr.h"
|
||||
|
||||
#include "OSCServer.h"
|
||||
#include "VrmVMCObject.h"
|
||||
|
||||
#include "VRM4U_VMCSubsystem.generated.h"
|
||||
|
||||
|
||||
#if UE_VERSION_OLDER_THAN(4,22,0)
|
||||
|
||||
//Couldn't find parent type for 'VRM4U_AnimSubsystem' named 'UEngineSubsystem'
|
||||
#error "please remove VRM4U_AnimSubsystem.h/cpp for <=UE4.21"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
UCLASS()
|
||||
class VRM4UCAPTURE_API UVRM4U_VMCSubsystem : public UEngineSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = VRM4U)
|
||||
bool CreateVMCServer(const FString ServerAddress, int port);
|
||||
|
||||
void ClearData(const FString ServerAddress, int port);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = VRM4U)
|
||||
void DestroyVMCServer(const FString ServerAddress, int port);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = VRM4U)
|
||||
void DestroyVMCServerAll();
|
||||
|
||||
UVrmVMCObject* FindOrAddServer(const FString ServerAddress, int port);
|
||||
|
||||
TArray< TStrongObjectPtr<UVrmVMCObject> > VMCObjectList;
|
||||
|
||||
bool CopyVMCData(FVMCData& DstData, FString ServerAddress, int port);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = VRM4U)
|
||||
bool GetVMCData(TMap<FString, FTransform> &BoneData, TMap<FString, float> &CurveData, FString ServerAddress, int port);
|
||||
|
||||
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
|
||||
private:
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "Engine/LatentActionManager.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "VrmCaptureManager.generated.h"
|
||||
|
||||
class UVrmMocopiReceiver;
|
||||
|
||||
UCLASS()
|
||||
class VRM4UCAPTURE_API UVrmCaptureManager : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "VRM4U")
|
||||
static UVrmMocopiReceiver* CreateVrmMocopiReceiver(FString ReceiveIPAddress, int32 Port, bool bMulticastLoopback, bool bStartListening, FString ServerName, UObject* Outer = nullptr);
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,157 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Misc/CoreDelegates.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "Interfaces/IPv4/IPv4Address.h"
|
||||
#include "Interfaces/IPv4/IPv4Endpoint.h"
|
||||
#include "Common/UdpSocketReceiver.h"
|
||||
#include "Sockets.h"
|
||||
#include "Common/UdpSocketBuilder.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
#include "Tickable.h"
|
||||
|
||||
|
||||
#if UE_VERSION_OLDER_THAN(4,26,0)
|
||||
#else
|
||||
#include "Containers/RingBuffer.h"
|
||||
#endif
|
||||
|
||||
#include "VrmMocopiReceiver.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
enum MocopiData{
|
||||
BoneNum = 27,
|
||||
};
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FStructMocopiData{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRM4U")
|
||||
int FrameNo = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRM4U")
|
||||
int Time = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, EditFixedSize, Category = "VRM4U")
|
||||
TArray<FTransform> MocopiTransformWorld;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, EditFixedSize, Category = "VRM4U")
|
||||
TArray<FTransform> MocopiTransformLocal;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, EditFixedSize, Category = "VRM4U")
|
||||
TArray<FTransform> VrmTransformLocal;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRM4U")
|
||||
TMap<FString, FTransform> VrmTransformBoneList;
|
||||
|
||||
FStructMocopiData() {
|
||||
MocopiTransformWorld.SetNum(MocopiData::BoneNum);
|
||||
MocopiTransformLocal.SetNum(MocopiData::BoneNum);
|
||||
VrmTransformLocal.SetNum(MocopiData::BoneNum);
|
||||
}
|
||||
};
|
||||
|
||||
class VRM4UCAPTURE_API FMocopiReceiverProxy : public FTickableGameObject
|
||||
{
|
||||
UVrmMocopiReceiver* Receiver;
|
||||
FSocket* Socket = nullptr;
|
||||
FUdpSocketReceiver* SocketReceiver = nullptr;
|
||||
|
||||
FIPv4Address ReceiveIPAddress;
|
||||
int32 Port = 8888;
|
||||
|
||||
TArray<uint8> RecvDataSet;
|
||||
|
||||
public:
|
||||
FMocopiReceiverProxy(UVrmMocopiReceiver *InReceiver);
|
||||
//FMocopiReceiverProxy(UVrmMocopiReceiver& InServer);
|
||||
virtual ~FMocopiReceiverProxy();
|
||||
|
||||
#if WITH_EDITOR
|
||||
virtual bool IsTickableInEditor() const override {
|
||||
return true;
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
void Listen();
|
||||
|
||||
void Stop();
|
||||
|
||||
bool SetAddress(const FString& ReceiveIPAddress, int32 Port);
|
||||
|
||||
virtual void Tick(float InDeltaTime) override;
|
||||
virtual TStatId GetStatId() const override;
|
||||
|
||||
void OnPacketReceived(const FArrayReaderPtr& InData, const FIPv4Endpoint& InEndpoint);
|
||||
};
|
||||
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class VRM4UCAPTURE_API UVrmMocopiReceiver : public UObject
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
int BufferNum = 10;
|
||||
int currentFrameNo = 0;
|
||||
int currentTime = 0;
|
||||
|
||||
mutable FCriticalSection BufferCS;
|
||||
|
||||
TUniquePtr<FMocopiReceiverProxy> ReceiverProxy;
|
||||
|
||||
#if UE_VERSION_OLDER_THAN(4,26,0)
|
||||
TArray<FStructMocopiData> MocopiReceiveBuffer;
|
||||
#else
|
||||
TRingBuffer<FStructMocopiData> MocopiReceiveBuffer;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "VRM4U")
|
||||
void Listen();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "VRM4U")
|
||||
void Stop();
|
||||
|
||||
void OnPacketReceived(const FArrayReaderPtr& InData, const FIPv4Endpoint& InEndpoint);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "VRM4U")
|
||||
bool SetAddress(const FString& ReceiveIPAddress, int32 Port);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "VRM4U")
|
||||
void SetBufferNum(int32 Num = 10);
|
||||
|
||||
void OnPacketReceived(FStructMocopiData data);
|
||||
|
||||
void PacketBroadcast();
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FVrmMocopiReceiverDelegate, FStructMocopiData, data);
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "VRM4U")
|
||||
FVrmMocopiReceiverDelegate OnReceived;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "VRM4U")
|
||||
void GetNextFrameData(FStructMocopiData &data, bool &bEnable, bool &bUpdate);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "VRM4U")
|
||||
void GetLatestFrameData(FStructMocopiData &data, bool &bEnable, bool &bUpdate);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "VRM4U")
|
||||
void GetCurrentTime(int& FrameNo, int& Time) {
|
||||
FrameNo = currentFrameNo;
|
||||
Time = currentTime;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void BeginDestroy() override;
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
#include "UObject/StrongObjectPtr.h"
|
||||
#include "OSCServer.h" // for game build link error
|
||||
|
||||
#include "VrmVMCObject.generated.h"
|
||||
|
||||
struct FOSCMessage;
|
||||
|
||||
struct FVMCData {
|
||||
FString ServerAddress = "";
|
||||
int Port = 0;
|
||||
|
||||
TMap<FString, FTransform> BoneData;
|
||||
TMap<FString, float> CurveData;
|
||||
|
||||
void ClearData() {
|
||||
BoneData.Empty();
|
||||
CurveData.Empty();
|
||||
}
|
||||
|
||||
bool operator==(const FVMCData& Other) const {
|
||||
if (Port != Other.Port) return false;
|
||||
if (ServerAddress != Other.ServerAddress) return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
UCLASS()
|
||||
class VRM4UCAPTURE_API UVrmVMCObject : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FCriticalSection cs;
|
||||
|
||||
TStrongObjectPtr<UOSCServer> OSCServer;
|
||||
|
||||
FVMCData VMCData;
|
||||
FVMCData VMCData_Cache;
|
||||
|
||||
bool bDataUpdated = false;
|
||||
public:
|
||||
|
||||
|
||||
FString ServerName;
|
||||
uint16 port;
|
||||
|
||||
bool bForceUpdate = false;
|
||||
|
||||
void CreateServer(FString name, uint16 port);
|
||||
void DestroyServer();
|
||||
void OSCReceivedMessageEvent(const FOSCMessage& Message, const FString& IPAddress, uint16 Port);
|
||||
|
||||
bool CopyVMCData(FVMCData& dst);
|
||||
void ClearVMCData();
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class VRM4UCapture : ModuleRules
|
||||
{
|
||||
public VRM4UCapture(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[] {
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Networking",
|
||||
"Sockets",
|
||||
"OSC",
|
||||
"AnimGraphRuntime",
|
||||
|
||||
"VRM4U",
|
||||
});
|
||||
|
||||
if (Target.bBuildEditor)
|
||||
{
|
||||
PrivateDependencyModuleNames.Add("UnrealEd");
|
||||
}
|
||||
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[] {
|
||||
});
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[] {
|
||||
});
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// Relative to Engine\Plugins\Runtime\Oculus\OculusVR\Source
|
||||
//"../Runtime/Renderer/Private",
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user