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();
}
Example #2
0
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();
}