# 相关类 - 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(); ThreadInterval = 0.002; BackIndexCount = int64(UMotionUtils::BackSampleTime / (1000.0 / CHINGMU_SERVER_FPS));//BackSampleTime = 100ms CHINGMU_SERVER_FPS =120ms ChingmuComp = Cast(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 BonesWorldPos; UPROPERTY(BlueprintReadWrite, EditAnywhere) TArray 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 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