void USkeletalMeshComponent::PostPhysicsTick(FPrimitiveComponentPostPhysicsTickFunction &ThisTickFunction) { Super::PostPhysicsTick(ThisTickFunction); // if physics is disabled on dedicated server, no reason to be here. if ( !bEnablePhysicsOnDedicatedServer && IsRunningDedicatedServer() ) { return; } if (IsRegistered() && IsSimulatingPhysics()) { SyncComponentToRBPhysics(); } // this used to not run if not rendered, but that causes issues such as bounds not updated // causing it to not rendered, at the end, I think we should blend body positions // for example if you're only simulating, this has to happen all the time // whether looking at it or not, otherwise // @todo better solution is to check if it has moved by changing SyncComponentToRBPhysics to return true if anything modified // and run this if that is true or rendered // that will at least reduce the chance of mismatch // generally if you move your actor position, this has to happen to approximately match their bounds if( Bodies.Num() > 0 && IsRegistered() ) { BlendInPhysics(); } }
FVector UPrimitiveComponent::GetComponentVelocity() const { if (IsSimulatingPhysics()) { FBodyInstance* BodyInst = GetBodyInstance(); if(BodyInst != NULL) { return BodyInst->GetUnrealWorldVelocity(); } } return Super::GetComponentVelocity(); }
bool USkeletalMeshComponent::IsAnySimulatingPhysics() const { if( bUseSingleBodyPhysics ) { return IsSimulatingPhysics(); } for ( int32 BodyIndex=0; BodyIndex<Bodies.Num(); ++BodyIndex ) { if (Bodies[BodyIndex]->IsInstanceSimulatingPhysics()) { return true; } } return false; }
void UPrimitiveComponent::SyncComponentToRBPhysics() { if(!IsRegistered()) { UE_LOG(LogPhysics, Log, TEXT("SyncComponentToRBPhysics : Component not registered (%s)"), *GetPathName()); return; } // BodyInstance we are going to sync the component to FBodyInstance* UseBI = GetBodyInstance(); if(UseBI == NULL || !UseBI->IsValidBodyInstance()) { UE_LOG(LogPhysics, Log, TEXT("SyncComponentToRBPhysics : Missing or invalid BodyInstance (%s)"), *GetPathName()); return; } AActor* Owner = GetOwner(); if(Owner != NULL) { if (Owner->IsPendingKill() || !Owner->CheckStillInWorld()) { return; } } if (IsPendingKill() || !IsSimulatingPhysics()) { return; } // See if the transform is actually different, and if so, move the component to match physics const FTransform NewTransform = GetComponentTransformFromBodyInstance(UseBI); if(!NewTransform.EqualsNoScale(ComponentToWorld)) { const FVector MoveBy = NewTransform.GetLocation() - ComponentToWorld.GetLocation(); const FQuat NewRotation = NewTransform.GetRotation(); //@warning: do not reference BodyInstance again after calling MoveComponent() - events from the move could have made it unusable (destroying the actor, SetPhysics(), etc) MoveComponent(MoveBy, NewRotation, false, NULL, MOVECOMP_SkipPhysicsMove); } }
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(); }