216 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 相关类
- TsArkitDataReceiver(ArkitDataReceiver)
- TsChingmuMocapReceiverActor(ChingmuMocapReceiverActor)
- TsMotionReceiverActor(MotionReceiverActor) => BP_MotionReceiver定义了MotionNetConfig.ini。
- TsMotionSenderActor(MotionSenderActor)
# TsChingmuMocapReceiverActor
1. Init()在Server才会Spawn TsChingmuMocapReceiverActor。
2. ConnectChingMu()**ChingmuComp.StartConnectServer()**
3. Multicast_AligmMotionTime()寻找场景中的BP_MotionReceiver并且调用Receiver.AlignTimeStamp()。
## ChingmuMocapReceiverActor
```c++
void AChingmuMocapReceiverActor::BeginPlay()
{
Super::BeginPlay();
MaxHumanCount = 10;
MaxRigidBodyCount = 10;
CacheLimit = 240;
SampledHumanData = NewObject<UMocapFrameData>();
ThreadInterval = 0.002;
BackIndexCount = int64(UMotionUtils::BackSampleTime / (1000.0 / CHINGMU_SERVER_FPS));//BackSampleTime = 100ms CHINGMU_SERVER_FPS =120ms
ChingmuComp = Cast<UChingMUComponent>(GetComponentByClass(UChingMUComponent::StaticClass()));
if (ChingmuComp == nullptr)
{
UE_LOG(LogTemp, Error, TEXT("Chingmu Component is missing!!"));
}
Thread = new FChingmuThread("Chingmu Data Thread", this);
Sender = GetMotionSender();
}
```
### FChingmuThread
用途为:
```c++
uint32 FChingmuThread::Run()
{
FTransform Tmp;
while (bRun)
{
if (OwnerActor && OwnerActor->UseThread && OwnerActor->ChingmuComp && OwnerActor->ChingmuComp->IsConnected())
{
CurTime = ULiveDirectorStatics::GetUnixTime();
// Human
for (auto HumanIndex = 0; HumanIndex < OwnerActor->MaxHumanCount; HumanIndex++)
{
const auto bRes = OwnerActor->ChingmuComp->FullBodyMotionCapBaseBonesLocalSpaceRotation(
OwnerActor->ChingmuFullAddress, HumanIndex, TmpTimeCode);
if (bRes)
{
if (!HumanToLastReceiveTime.Contains(HumanIndex))//空数据处理。
{
HumanToLastReceiveTime.Add(HumanIndex, 0);
}
if (HumanToLastReceiveTime[HumanIndex] != TmpTimeCode.Frames)//接收端时间与
{
HumanToLastReceiveTime[HumanIndex] = TmpTimeCode.Frames;
OwnerActor->OnGetHumanData_NotInGameThread(HumanIndex, CurTime, TmpTimeCode.Frames);
}
else
{
// get same frame, skip
break;
}
}
}
// Rigidbody
for (auto RigidBodyIndex = OwnerActor->RigidBodyStartIndex; RigidBodyIndex < OwnerActor->RigidBodyStartIndex
+ OwnerActor->MaxRigidBodyCount; RigidBodyIndex++)
{
OwnerActor->ChingmuComp->GetTrackerPoseTC(OwnerActor->ChingmuFullAddress, RigidBodyIndex, Tmp,
TmpTimeCode);
if (!RigidBodyToLastReceiveTransform.Contains(RigidBodyIndex))
{
RigidBodyToLastReceiveTransform.Add(RigidBodyIndex, FTransform::Identity);
}
// 道具的TmpTimeCode.Frames永远为0所以无法用帧数判断
// 改为transform判断
if (!RigidBodyToLastReceiveTransform[RigidBodyIndex].Equals(Tmp))
{
RigidBodyToLastReceiveTransform[RigidBodyIndex] = Tmp;
OwnerActor->OnGetRigidBodyData_NotInGameThread(RigidBodyIndex, Tmp, CurTime, TmpTimeCode.Frames);
}
}
}
if (bRun)
{
FPlatformProcess::Sleep(OwnerActor ? OwnerActor->ThreadInterval : 0.004);
}
else
{
break;
}
}
UE_LOG(LogTemp, Warning, TEXT("%s finish work."), *ThreadName)
return 0;
}
```
## MocapData
```c++
#define MOCAP_BONE_COUNT 23
enum E_MotionType
{
Human,
RigidBody
};
enum E_SourceType
{
Mocap,
CMR,
Replay
};
struct ST_MocapFrameData
{
int ID;
int64 TimeStamp;
int FrameIndex;
E_MotionType MotionType;
E_SourceType SourceType;
FVector BonesWorldPos[MOCAP_BONE_COUNT];
FQuat BonesLocalRot[MOCAP_BONE_COUNT];
};
class LIVEDIRECTOR_API UMocapFrameData : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere)
int ID;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
TArray<FVector> BonesWorldPos;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
TArray<FQuat> BonesLocalRot;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
int64 TimeStamp;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
int FrameIndex;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
int MotionType; // 0 human; 1 rigidbody
UPROPERTY(BlueprintReadWrite, EditAnywhere)
int SourceType; // 0 mocap, 1 cmr
public:
void CopyFrom(const ST_MocapFrameData* Other)
{
ID = Other->ID;
TimeStamp = Other->TimeStamp;
FrameIndex = Other->FrameIndex;
MotionType = Other->MotionType;
SourceType = Other->SourceType;
for (auto Index = 0; Index < 23; Index++)
{
BonesWorldPos[Index] = Other->BonesWorldPos[Index];
BonesLocalRot[Index] = Other->BonesLocalRot[Index];
}
}
};
class MocapFrames
{
public:
int ID;
std::vector<ST_MocapFrameData*> Frames = {};
void CalculatePackageAverageInterval(float& Res)
{
if(Frames.size() > 0)
{
auto First = Frames[0];
auto Last = Frames[Frames.size() - 1];
if(Last->FrameIndex > First->FrameIndex)
{
Res = 1.0 * (Last->TimeStamp - First->TimeStamp) / (Last->FrameIndex - First->FrameIndex);
}
}
}
};
```
# MotionCapture(青瞳插件)
实现1个组件与3个动画节点
- [[#ChingMUComponent]]
- [[#AnimNode_ChingMUPose]]:接受骨骼动捕数据。
- [[#AnimNode_ChingMURetargetPose]]:接受重定向后的骨骼动捕数据。
- AnimNode_ChingMURetargetPoseForBuild:
## ***ChingMUComponent***
1.
## FAnimNode_ChingMUPose
1. Initialize_AnyThread():取得**ChingMUComponent**。
2. Update_AnyThread():调用**ChingMUComponent->CalculateBoneCSRotation()**
3. Evaluate_AnyThread()对23根骨骼进行遍历取得RefPose后将从Update_AnyThread()获得动捕数据(**Rotation**覆盖到上面ComponentSpace**根骨骼需要额外添加Location数据**。最后将数据从ComponentSpace => LocalSpace。
## AnimNode_ChingMURetargetPose
1. Initialize_AnyThread()创建曲线逻辑TCHour、TCMinute、TCSecond、TCFrame
2. Update_AnyThread()
3. Evaluate_AnyThread():相关逻辑都实现在这里。
### AnimNode_ChingMURetargetPose::Evaluate_AnyThread()
# TsMotionReceiverActor
只在BeginPlay()中调用了this.MarkAsClientSeamlessTravel(); 具体逻辑在`AMotionReceiverActor`
## MotionReceiverActor