void USkeletalMeshComponent::PostBlendPhysics() { SCOPE_CYCLE_COUNTER(STAT_UpdateLocalToWorldAndOverlaps); // Flip bone buffer and send 'post anim' notification FinalizeBoneTransform(); // Update Child Transform - The above function changes bone transform, so will need to update child transform UpdateChildTransforms(); // animation often change overlap. UpdateOverlaps(); // Cached local bounds are now out of date InvalidateCachedBounds(); // update bounds UpdateBounds(); // Need to send new bounds to MarkRenderTransformDirty(); // New bone positions need to be sent to render thread MarkRenderDynamicDataDirty(); }
void USkeletalMeshComponent::PerformBlendPhysicsBones(const TArray<FBoneIndexType>& InRequiredBones, TArray<FTransform>& InLocalAtoms) { // Get drawscale from Owner (if there is one) FVector TotalScale3D = ComponentToWorld.GetScale3D(); FVector RecipScale3D = TotalScale3D.Reciprocal(); UPhysicsAsset * const PhysicsAsset = GetPhysicsAsset(); check( PhysicsAsset ); if (GetNumSpaceBases() == 0) { return; } // Get the scene, and do nothing if we can't get one. FPhysScene* PhysScene = nullptr; if (GetWorld() != nullptr) { PhysScene = GetWorld()->GetPhysicsScene(); } if (PhysScene == nullptr) { return; } // Make sure scratch space is big enough. TArray<FAssetWorldBoneTM> WorldBoneTMs; WorldBoneTMs.Reset(); WorldBoneTMs.AddZeroed(GetNumSpaceBases()); FTransform LocalToWorldTM = ComponentToWorld; LocalToWorldTM.RemoveScaling(); TArray<FTransform>& EditableSpaceBases = GetEditableSpaceBases(); struct FBodyTMPair { FBodyInstance* BI; FTransform TM; }; TArray<FBodyTMPair> PendingBodyTMs; #if WITH_PHYSX // Lock the scenes we need (flags set in InitArticulated) if (bHasBodiesInSyncScene) { SCENE_LOCK_READ(PhysScene->GetPhysXScene(PST_Sync)) } if (bHasBodiesInAsyncScene) { SCENE_LOCK_READ(PhysScene->GetPhysXScene(PST_Async)) } #endif // For each bone - see if we need to provide some data for it. for(int32 i=0; i<InRequiredBones.Num(); i++) { int32 BoneIndex = InRequiredBones[i]; // See if this is a physics bone.. int32 BodyIndex = PhysicsAsset ? PhysicsAsset->FindBodyIndex(SkeletalMesh->RefSkeleton.GetBoneName(BoneIndex)) : INDEX_NONE; // need to update back to physX so that physX knows where it was after blending bool bUpdatePhysics = false; FBodyInstance* BodyInstance = NULL; // If so - get its world space matrix and its parents world space matrix and calc relative atom. if(BodyIndex != INDEX_NONE ) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // tracking down TTP 280421. Remove this if this doesn't happen. if ( !ensure(Bodies.IsValidIndex(BodyIndex)) ) { UE_LOG(LogPhysics, Warning, TEXT("%s(Mesh %s, PhysicsAsset %s)"), *GetName(), *GetNameSafe(SkeletalMesh), *GetNameSafe(PhysicsAsset)); if ( PhysicsAsset ) { UE_LOG(LogPhysics, Warning, TEXT(" - # of BodySetup (%d), # of Bodies (%d), Invalid BodyIndex(%d)"), PhysicsAsset->BodySetup.Num(), Bodies.Num(), BodyIndex); } continue; } #endif BodyInstance = Bodies[BodyIndex]; //if simulated body copy back and blend with animation if(BodyInstance->IsInstanceSimulatingPhysics()) { FTransform PhysTM = BodyInstance->GetUnrealWorldTransform_AssumesLocked(); // Store this world-space transform in cache. WorldBoneTMs[BoneIndex].TM = PhysTM; WorldBoneTMs[BoneIndex].bUpToDate = true; float UsePhysWeight = (bBlendPhysics)? 1.f : BodyInstance->PhysicsBlendWeight; // Find this bones parent matrix. FTransform ParentWorldTM; // if we wan't 'full weight' we just find if(UsePhysWeight > 0.f) { if(BoneIndex == 0) { ParentWorldTM = LocalToWorldTM; } else { // If not root, get parent TM from cache (making sure its up-to-date). int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex); UpdateWorldBoneTM(WorldBoneTMs, ParentIndex, this, LocalToWorldTM, TotalScale3D); ParentWorldTM = WorldBoneTMs[ParentIndex].TM; } // Then calc rel TM and convert to atom. FTransform RelTM = PhysTM.GetRelativeTransform(ParentWorldTM); RelTM.RemoveScaling(); FQuat RelRot(RelTM.GetRotation()); FVector RelPos = RecipScale3D * RelTM.GetLocation(); FTransform PhysAtom = FTransform(RelRot, RelPos, InLocalAtoms[BoneIndex].GetScale3D()); // Now blend in this atom. See if we are forcing this bone to always be blended in InLocalAtoms[BoneIndex].Blend( InLocalAtoms[BoneIndex], PhysAtom, UsePhysWeight ); if(BoneIndex == 0) { //We must update RecipScale3D based on the atom scale of the root TotalScale3D *= InLocalAtoms[0].GetScale3D(); RecipScale3D = TotalScale3D.Reciprocal(); } if (UsePhysWeight < 1.f) { bUpdatePhysics = true; } } } } // Update SpaceBases entry for this bone now if( BoneIndex == 0 ) { EditableSpaceBases[0] = InLocalAtoms[0]; } else { const int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex); EditableSpaceBases[BoneIndex] = InLocalAtoms[BoneIndex] * EditableSpaceBases[ParentIndex]; /** * Normalize rotations. * We want to remove any loss of precision due to accumulation of error. * i.e. A componentSpace transform is the accumulation of all of its local space parents. The further down the chain, the greater the error. * SpaceBases are used by external systems, we feed this to PhysX, send this to gameplay through bone and socket queries, etc. * So this is a good place to make sure all transforms are normalized. */ EditableSpaceBases[BoneIndex].NormalizeRotation(); } if (bUpdatePhysics && BodyInstance) { //This is extremely inefficient. We need to obtain a write lock which will block other threads from blending //For now I'm juts deferring it to the end of this loop, but in general we need to move it all out of here and do it when the blend task is done FBodyTMPair* BodyTMPair = new (PendingBodyTMs) FBodyTMPair; BodyTMPair->BI = BodyInstance; BodyTMPair->TM = EditableSpaceBases[BoneIndex] * ComponentToWorld; } } #if WITH_PHYSX //See above for read lock instead of write lock // Unlock the scenes if (bHasBodiesInSyncScene) { SCENE_UNLOCK_READ(PhysScene->GetPhysXScene(PST_Sync)) } if (bHasBodiesInAsyncScene) { SCENE_UNLOCK_READ(PhysScene->GetPhysXScene(PST_Async)) } if(PendingBodyTMs.Num()) { //This is extremely inefficient. We need to obtain a write lock which will block other threads from blending //For now I'm juts deferring it to the end of this loop, but in general we need to move it all out of here and do it when the blend task is done if (bHasBodiesInSyncScene) { SCENE_LOCK_WRITE(PhysScene->GetPhysXScene(PST_Sync)) } if (bHasBodiesInAsyncScene) { SCENE_LOCK_WRITE(PhysScene->GetPhysXScene(PST_Async)) } for (const FBodyTMPair& BodyTMPair : PendingBodyTMs) { BodyTMPair.BI->SetBodyTransform(BodyTMPair.TM, ETeleportType::TeleportPhysics); } if (bHasBodiesInSyncScene) { SCENE_UNLOCK_WRITE(PhysScene->GetPhysXScene(PST_Sync)) } if (bHasBodiesInAsyncScene) { SCENE_UNLOCK_WRITE(PhysScene->GetPhysXScene(PST_Async)) } } #endif // Transforms updated, cached local bounds are now out of date. InvalidateCachedBounds(); }
void USkeletalMeshComponent::RefreshBoneTransforms() { SCOPE_CYCLE_COUNTER(STAT_RefreshBoneTransforms); // Can't do anything without a SkeletalMesh // Do nothing more if no bones in skeleton. if( !SkeletalMesh || SpaceBases.Num() == 0 ) { return; } AActor * Owner = GetOwner(); const FAnimUpdateRateParameters & UpdateRateParams = Owner ? Owner->AnimUpdateRateParams : FAnimUpdateRateParameters(); { FScopeLockPhysXWriter LockPhysXForWriting; // Recalculate the RequiredBones array, if necessary if( !bRequiredBonesUpToDate ) { RecalcRequiredBones(PredictedLODLevel); } // Update rate turned off, evaluate every frame. if( !bEnableUpdateRateOptimizations || (UpdateRateParams.GetEvaluationRate() <= 1) ) { // evaluate pure animations, and fill up LocalAtoms EvaluateAnimation(); // We need the mesh space bone transforms now for renderer to get delta from ref pose: FillSpaceBases(); // Invalidate cached bones. CachedLocalAtoms.Empty(); CachedSpaceBases.Empty(); } else { // figure out if our cache is invalid. const bool bInvalidCachedBones = (LocalAtoms.Num() != SkeletalMesh->RefSkeleton.GetNum()) || (LocalAtoms.Num() != CachedLocalAtoms.Num()) || (SpaceBases.Num() != CachedSpaceBases.Num()); // If cache is invalid, we need to rebuild it. And we can't interpolate. // (same path if we're not interpolating and not skipping a frame). if( bInvalidCachedBones || (!UpdateRateParams.ShouldInterpolateSkippedFrames() && !UpdateRateParams.ShouldSkipEvaluation()) ) { // evaluate pure animations, and fill up LocalAtoms EvaluateAnimation(); // Fill SpaceBases from LocalAtoms FillSpaceBases(); // Cache bones CachedLocalAtoms = LocalAtoms; CachedSpaceBases = SpaceBases; } else { // No interpolation, just copy // @todo: if we don't blend any physics, we could even skip the copy. if( !UpdateRateParams.ShouldInterpolateSkippedFrames() ) { LocalAtoms = CachedLocalAtoms; SpaceBases = CachedSpaceBases; } else { // If we are not skipping evaluation this frame, refresh cache. if( !UpdateRateParams.ShouldSkipEvaluation() ) { // Preserve LocalAtoms and SpaceBases, so we can keep interpolation. Exchange(LocalAtoms, CachedLocalAtoms); Exchange(SpaceBases, CachedSpaceBases); // evaluate pure animations, and fill up LocalAtoms EvaluateAnimation(); // Fill SpaceBases from LocalAtoms FillSpaceBases(); Exchange(LocalAtoms, CachedLocalAtoms); Exchange(SpaceBases, CachedSpaceBases); } // Interpolate { SCOPE_CYCLE_COUNTER(STAT_InterpolateSkippedFrames); const float Alpha = 0.25f + (1.f / float(FMath::Max(UpdateRateParams.GetEvaluationRate(), 2) * 2)); FAnimationRuntime::LerpBoneTransforms(LocalAtoms, CachedLocalAtoms, Alpha, RequiredBones); FAnimationRuntime::LerpBoneTransforms(SpaceBases, CachedSpaceBases, Alpha, RequiredBones); } } } } // Transforms updated, cached local bounds are now out of date. InvalidateCachedBounds(); // update physics data from animated data UpdateKinematicBonesToPhysics(false); UpdateRBJointMotors(); } // @todo anim : hack TTP 224385 ANIM: Skeletalmesh double buffer // this is problem because intermediate buffer changes physics position as well // this causes issue where a half of frame, physics position is fixed with anim pose, and the other half is real simulated position // if you enable physics in tick, since that's before physics update, you'll get animation pose dominating physics pose, which isn't what you want. (Or what you'll see) // so do not update transform if physics is on. This problem will be solved by double buffer, when we keep one buffer for intermediate, and the other buffer for result query { FScopeLockPhysXReader LockPhysXForReading; if( !IsSimulatingPhysics() ) { SCOPE_CYCLE_COUNTER(STAT_UpdateLocalToWorldAndOverlaps); // New bone positions need to be sent to render thread UpdateComponentToWorld(); // animation often change overlap. UpdateOverlaps(); } } MarkRenderDynamicDataDirty(); }