void FPhysScene::UpdateActiveTransforms(uint32 SceneType) { if (SceneType == PST_Cloth) //cloth doesn't bother with updating components to bodies so we don't need to store any transforms { return; } PxScene* PScene = GetPhysXScene(SceneType); check(PScene); SCOPED_SCENE_READ_LOCK(PScene); PxU32 NumTransforms = 0; const PxActiveTransform* PActiveTransforms = PScene->getActiveTransforms(NumTransforms); ActiveBodyInstances[SceneType].Empty(NumTransforms); ActiveDestructibleActors[SceneType].Empty(NumTransforms); for (PxU32 TransformIdx = 0; TransformIdx < NumTransforms; ++TransformIdx) { const PxActiveTransform& PActiveTransform = PActiveTransforms[TransformIdx]; PxRigidActor* RigidActor = PActiveTransform.actor->isRigidActor(); ensure(!RigidActor->userData || !FPhysxUserData::IsGarbage(RigidActor->userData)); if (FBodyInstance* BodyInstance = FPhysxUserData::Get<FBodyInstance>(RigidActor->userData)) { if (BodyInstance->InstanceBodyIndex == INDEX_NONE && BodyInstance->OwnerComponent.IsValid() && BodyInstance->IsInstanceSimulatingPhysics()) { ActiveBodyInstances[SceneType].Add(BodyInstance); } } else if (const FDestructibleChunkInfo* DestructibleChunkInfo = FPhysxUserData::Get<FDestructibleChunkInfo>(RigidActor->userData)) { ActiveDestructibleActors[SceneType].Add(RigidActor); } } }
void FPhysScene::TermPhysScene(uint32 SceneType) { check(SceneType < NumPhysScenes); #if WITH_PHYSX PxScene* PScene = GetPhysXScene(SceneType); if(PScene != NULL) { #if WITH_APEX NxApexScene* ApexScene = GetApexScene(SceneType); if(ApexScene != NULL) { GPhysCommandHandler->DeferredRelease(ApexScene); } #endif // #if WITH_APEX if ( SceneType == PST_Sync && VehicleManager != NULL ) { delete VehicleManager; VehicleManager = NULL; } #if WITH_SUBSTEPPING delete PhysSubSteppers[SceneType]; PhysSubSteppers[SceneType] = NULL; #endif // @todo block on any running scene before calling this GPhysCommandHandler->DeferredRelease(PScene); // Remove from the map GPhysXSceneMap.Remove(PhysXSceneIndex[SceneType]); } #endif }
void FPhysScene::SetUpForFrame(const FVector* NewGrav, float InDeltaSeconds, float InMaxPhysicsDeltaTime) { DeltaSeconds = InDeltaSeconds; MaxPhysicsDeltaTime = InMaxPhysicsDeltaTime; #if WITH_PHYSX if (NewGrav) { // Loop through scene types to get all scenes for (uint32 SceneType = 0; SceneType < NumPhysScenes; ++SceneType) { PxScene* PScene = GetPhysXScene(SceneType); if (PScene != NULL) { //@todo phys_thread don't do this if gravity changes //@todo, to me it looks like we should avoid this if the gravity has not changed, the lock is probably expensive // Lock scene lock, in case it is required SCENE_LOCK_WRITE(PScene); PScene->setGravity(U2PVector(*NewGrav)); // Unlock scene lock, in case it is required SCENE_UNLOCK_WRITE(PScene); } } } #endif }
bool FPhysScene::SubstepSimulation(uint32 SceneType, FGraphEventRef &InOutCompletionEvent) { #if WITH_PHYSX check(SceneType != PST_Cloth); //we don't bother sub-stepping cloth float UseDelta = UseSyncTime(SceneType)? SyncDeltaSeconds : DeltaSeconds; float SubTime = PhysSubSteppers[SceneType]->UpdateTime(UseDelta); PxScene* PScene = GetPhysXScene(SceneType); if(SubTime <= 0.f) { return false; }else { //we have valid scene and subtime so enqueue task PhysXCompletionTask* Task = new PhysXCompletionTask(InOutCompletionEvent, PScene->getTaskManager()); ENamedThreads::Type NamedThread = PhysSingleThreadedMode() ? ENamedThreads::GameThread : ENamedThreads::AnyThread; DECLARE_CYCLE_STAT(TEXT("FSimpleDelegateGraphTask.SubstepSimulationImp"), STAT_FSimpleDelegateGraphTask_SubstepSimulationImp, STATGROUP_TaskGraphTasks); FSimpleDelegateGraphTask::CreateAndDispatchWhenReady( FSimpleDelegateGraphTask::FDelegate::CreateRaw(PhysSubSteppers[SceneType], &FPhysSubstepTask::StepSimulation, Task), GET_STATID(STAT_FSimpleDelegateGraphTask_SubstepSimulationImp), NULL, NamedThread ); return true; } #endif }
void FPhysScene::ProcessPhysScene(uint32 SceneType) { SCOPE_CYCLE_COUNTER(STAT_TotalPhysicsTime); SCOPE_CYCLE_COUNTER(STAT_PhysicsFetchDynamicsTime); check(SceneType < NumPhysScenes); if (bPhysXSceneExecuting[SceneType] == 0) { // Not executing this scene, must call TickPhysScene before calling this function again. UE_LOG(LogPhysics, Log, TEXT("WaitPhysScene`: Not executing this scene (%d) - aborting."), SceneType); return; } if (FrameLagAsync()) { static_assert(PST_MAX == 3, "Physics scene static test failed."); // Here we assume the PST_Sync is the master and never fame lagged if (SceneType == PST_Sync) { // the one frame lagged one should be done by now. check(!FrameLaggedPhysicsSubsceneCompletion[PST_Async].GetReference() || FrameLaggedPhysicsSubsceneCompletion[PST_Async]->IsComplete()); } else if (SceneType == PST_Async) { FrameLaggedPhysicsSubsceneCompletion[PST_Async] = NULL; } } // Reset execution flag //This fetches and gets active transforms. It's important that the function that calls this locks because getting the transforms and using the data must be an atomic operation #if WITH_PHYSX PxScene* PScene = GetPhysXScene(SceneType); check(PScene); PxU32 OutErrorCode = 0; #if !WITH_APEX PScene->lockWrite(); PScene->fetchResults(true, &OutErrorCode); PScene->unlockWrite(); #else // #if !WITH_APEX // The APEX scene calls the fetchResults function for the PhysX scene, so we only call ApexScene->fetchResults(). NxApexScene* ApexScene = GetApexScene(SceneType); check(ApexScene); ApexScene->fetchResults(true, &OutErrorCode); #endif // #if !WITH_APEX UpdateActiveTransforms(SceneType); if (OutErrorCode != 0) { UE_LOG(LogPhysics, Log, TEXT("PHYSX FETCHRESULTS ERROR: %d"), OutErrorCode); } #endif // WITH_PHYSX PhysicsSubsceneCompletion[SceneType] = NULL; bPhysXSceneExecuting[SceneType] = false; }
void AProjectTapGameMode::BeginPlay() { Super::BeginPlay(); auto gameState = GetGameState<AProjectTapGameState>(); OnCameraChangedDelegateHandle = gameState->CameraChanged.AddUFunction( this , TEXT( "OnCameraChanged" ) ); OnGameStateChangedDelegateHandle = gameState->GameStateChanged.AddUFunction( this , TEXT( "OnStateChanged" ) ); auto physicsWorld = GetWorld()->GetPhysicsScene(); auto scene = physicsWorld->GetPhysXScene(0); }
void FPhysScene::ProcessPhysScene(uint32 SceneType) { SCOPE_CYCLE_COUNTER(STAT_TotalPhysicsTime); SCOPE_CYCLE_COUNTER(STAT_PhysicsFetchDynamicsTime); check(SceneType < NumPhysScenes); if( bPhysXSceneExecuting[SceneType] == 0 ) { // Not executing this scene, must call TickPhysScene before calling this function again. UE_LOG(LogPhysics, Log, TEXT("WaitPhysScene`: Not executing this scene (%d) - aborting."), SceneType); return; } PhysicsSubsceneCompletion[SceneType] = NULL; if (FrameLagAsync()) { checkAtCompileTime(PST_MAX == 2, Assumtiopns_about_physics_scenes); // Here we assume the PST_Sync is the master and never fame lagged if (SceneType == PST_Sync) { // the one frame lagged one should be done by now. check(!FrameLaggedPhysicsSubsceneCompletion[PST_Async].GetReference() || FrameLaggedPhysicsSubsceneCompletion[PST_Async]->IsComplete()); } else { FrameLaggedPhysicsSubsceneCompletion[PST_Async] = NULL; } } #if WITH_PHYSX PxScene* PScene = GetPhysXScene(SceneType); check(PScene); PxU32 OutErrorCode = 0; #if !WITH_APEX PScene->lockWrite(); PScene->fetchResults( true, &OutErrorCode ); PScene->unlockWrite(); #else // #if !WITH_APEX // The APEX scene calls the fetchResults function for the PhysX scene, so we only call ApexScene->fetchResults(). NxApexScene* ApexScene = GetApexScene(SceneType); check(ApexScene); ApexScene->fetchResults( true, &OutErrorCode ); #endif // #if !WITH_APEX if(OutErrorCode != 0) { UE_LOG(LogPhysics, Log, TEXT("PHYSX FETCHRESULTS ERROR: %d"), OutErrorCode); } #endif // WITH_PHYSX // Reset execution flag bPhysXSceneExecuting[SceneType] = false; }
void FPhysScene::EndFrame(ULineBatchComponent* InLineBatcher) { check(IsInGameThread()); PhysicsSceneCompletion = NULL; /** * At this point physics simulation has finished. We obtain both scene locks so that the various read/write operations needed can be done quickly. * This means that anyone attempting to write on other threads will be blocked. This is OK because acessing any of these game objects from another thread is probably a bad idea! */ SCOPED_SCENE_WRITE_LOCK(GetPhysXScene(PST_Sync)); SCOPED_SCENE_WRITE_LOCK(bAsyncSceneEnabled ? GetPhysXScene(PST_Async) : nullptr); if (bAsyncSceneEnabled) { SyncComponentsToBodies_AssumesLocked(PST_Async); } SyncComponentsToBodies_AssumesLocked(PST_Sync); // Perform any collision notification events DispatchPhysNotifications_AssumesLocked(); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // Handle debug rendering if (InLineBatcher) { AddDebugLines(PST_Sync, InLineBatcher); if (bAsyncSceneEnabled) { AddDebugLines(PST_Async, InLineBatcher); } } #endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST) }
void FPhysScene::SyncComponentsToBodies(uint32 SceneType) { #if WITH_PHYSX PxScene* PScene = GetPhysXScene(SceneType); check(PScene); SCENE_LOCK_READ(PScene); PxU32 NumTransforms = 0; const PxActiveTransform* PActiveTransforms = PScene->getActiveTransforms(NumTransforms); SCENE_UNLOCK_READ(PScene); for(PxU32 TransformIdx=0; TransformIdx<NumTransforms; TransformIdx++) { const PxActiveTransform& PActiveTransform = PActiveTransforms[TransformIdx]; FBodyInstance* BodyInst = FPhysxUserData::Get<FBodyInstance>(PActiveTransform.userData); if( BodyInst != NULL && BodyInst->InstanceBodyIndex == INDEX_NONE && BodyInst->OwnerComponent != NULL && BodyInst->IsInstanceSimulatingPhysics() ) { check(BodyInst->OwnerComponent->IsRegistered()); // shouldn't have a physics body for a non-registered component! AActor* Owner = BodyInst->OwnerComponent->GetOwner(); // See if the transform is actually different, and if so, move the component to match physics const FTransform NewTransform = BodyInst->GetUnrealWorldTransform(); if(!NewTransform.EqualsNoScale(BodyInst->OwnerComponent->ComponentToWorld)) { const FVector MoveBy = NewTransform.GetLocation() - BodyInst->OwnerComponent->ComponentToWorld.GetLocation(); const FRotator NewRotation = NewTransform.Rotator(); //UE_LOG(LogTemp, Log, TEXT("MOVING: %s"), *BodyInst->OwnerComponent->GetPathName()); //@warning: do not reference BodyInstance again after calling MoveComponent() - events from the move could have made it unusable (destroying the actor, SetPhysics(), etc) BodyInst->OwnerComponent->MoveComponent(MoveBy, NewRotation, false, NULL, MOVECOMP_SkipPhysicsMove); } // Check if we didn't fall out of the world if(Owner != NULL && !Owner->IsPendingKill()) { Owner->CheckStillInWorld(); } } } #endif }
void FPhysScene::TermPhysScene(uint32 SceneType) { check(SceneType < NumPhysScenes); #if WITH_PHYSX PxScene* PScene = GetPhysXScene(SceneType); if (PScene != NULL) { #if WITH_APEX NxApexScene* ApexScene = GetApexScene(SceneType); if (ApexScene != NULL) { GPhysCommandHandler->DeferredRelease(ApexScene); } #endif // #if WITH_APEX #if WITH_VEHICLE if (SceneType == PST_Sync && VehicleManager != NULL) { delete VehicleManager; VehicleManager = NULL; } #endif #if WITH_SUBSTEPPING if (SceneType == PST_Sync && PhysSubSteppers[SceneType]) { PhysSubSteppers[SceneType]->SetVehicleManager(NULL); } delete PhysSubSteppers[SceneType]; PhysSubSteppers[SceneType] = NULL; #endif // @todo block on any running scene before calling this GPhysCommandHandler->DeferredRelease(PScene); GPhysCommandHandler->DeferredDeleteSimEventCallback(SimEventCallback[SceneType]); // Commands may have accumulated as the scene is terminated - flush any commands for this scene. GPhysCommandHandler->Flush(); // Remove from the map GPhysXSceneMap.Remove(PhysXSceneIndex[SceneType]); } #endif }
bool FPhysScene::SubstepSimulation(uint32 SceneType, FGraphEventRef &InOutCompletionEvent) { float UseDelta = UseSyncTime(SceneType)? SyncDeltaSeconds : DeltaSeconds; float SubTime = PhysSubSteppers[SceneType]->UpdateTime(UseDelta); PxScene* PScene = GetPhysXScene(SceneType); #if WITH_APEX NxApexScene* ApexScene = GetApexScene(SceneType); if(!ApexScene || SubTime <= 0.f) { return false; }else { //we have valid scene and subtime so enqueue task PhysXCompletionTask* Task = new PhysXCompletionTask(InOutCompletionEvent, PScene->getTaskManager()); FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(FSimpleDelegateGraphTask::FDelegate::CreateRaw(PhysSubSteppers[SceneType], &FPhysSubstepTask::StepSimulation, ApexScene, Task), TEXT("SubstepSimulationImp")); return true; } #endif }
void FPhysScene::ApplyWorldOffset(FVector InOffset) { #if WITH_PHYSX // Loop through scene types to get all scenes for (uint32 SceneType = 0; SceneType < NumPhysScenes; ++SceneType) { PxScene* PScene = GetPhysXScene(SceneType); if (PScene != NULL) { // Lock scene lock, in case it is required SCENE_LOCK_WRITE(PScene); PScene->shiftOrigin(U2PVector(-InOffset)); // Unlock scene lock, in case it is required SCENE_UNLOCK_WRITE(PScene); } } #endif }
void FPhysScene::SetIsStaticLoading(bool bStaticLoading) { #if WITH_PHYSX // Loop through scene types to get all scenes for (uint32 SceneType = 0; SceneType < NumPhysScenes; ++SceneType) { PxScene* PScene = GetPhysXScene(SceneType); if (PScene != NULL) { // Lock scene lock, in case it is required SCENE_LOCK_WRITE(PScene); // Sets the rebuild rate hint, to 1 frame if static loading PScene->setDynamicTreeRebuildRateHint(bStaticLoading ? 5 : PhysXSlowRebuildRate); // Unlock scene lock, in case it is required SCENE_UNLOCK_WRITE(PScene); } } #endif }
void FPhysScene::SyncComponentsToBodies(uint32 SceneType) { SCOPE_CYCLE_COUNTER(STAT_TotalPhysicsTime); SCOPE_CYCLE_COUNTER(STAT_SyncComponentsToBodies); for (FBodyInstance* BodyInstance : ActiveBodyInstances[SceneType]) { if (BodyInstance == nullptr) { continue; } check(BodyInstance->OwnerComponent->IsRegistered()); // shouldn't have a physics body for a non-registered component! AActor* Owner = BodyInstance->OwnerComponent->GetOwner(); // See if the transform is actually different, and if so, move the component to match physics const FTransform NewTransform = BodyInstance->GetUnrealWorldTransform(); if (!NewTransform.EqualsNoScale(BodyInstance->OwnerComponent->ComponentToWorld)) { const FVector MoveBy = NewTransform.GetLocation() - BodyInstance->OwnerComponent->ComponentToWorld.GetLocation(); const FRotator NewRotation = NewTransform.Rotator(); //@warning: do not reference BodyInstance again after calling MoveComponent() - events from the move could have made it unusable (destroying the actor, SetPhysics(), etc) BodyInstance->OwnerComponent->MoveComponent(MoveBy, NewRotation, false, NULL, MOVECOMP_SkipPhysicsMove); } // Check if we didn't fall out of the world if (Owner != NULL && !Owner->IsPendingKill()) { Owner->CheckStillInWorld(); } } #if WITH_APEX if (ActiveDestructibleActors[SceneType].Num()) { SCOPED_SCENE_READ_LOCK(GetPhysXScene(SceneType)); UDestructibleComponent::UpdateDestructibleChunkTM(ActiveDestructibleActors[SceneType]); } #endif }
/** Add any debug lines from the physics scene to the supplied line batcher. */ void FPhysScene::AddDebugLines(uint32 SceneType, class ULineBatchComponent* LineBatcherToUse) { check(SceneType < NumPhysScenes); if (LineBatcherToUse) { #if WITH_PHYSX // Render PhysX debug data PxScene* PScene = GetPhysXScene(SceneType); const PxRenderBuffer& DebugData = PScene->getRenderBuffer(); BatchPxRenderBufferLines(*LineBatcherToUse, DebugData); #if WITH_APEX // Render APEX debug data NxApexScene* ApexScene = GetApexScene(SceneType); const PxRenderBuffer* RenderBuffer = ApexScene->getRenderBuffer(); if (RenderBuffer != NULL) { BatchPxRenderBufferLines(*LineBatcherToUse, *RenderBuffer); ApexScene->updateRenderResources(); } #endif // WITH_APEX #endif // WITH_PHYSX } }
/** Exposes creation of physics-engine scene outside Engine (for use with PhAT for example). */ FPhysScene::FPhysScene() #if WITH_APEX : PendingApexDamageManager(new FPendingApexDamageManager) #endif { LineBatcher = NULL; OwningWorld = NULL; #if WITH_PHYSX #if WITH_VEHICLE VehicleManager = NULL; #endif PhysxUserData = FPhysxUserData(this); // Create dispatcher for tasks if (PhysSingleThreadedMode()) { CPUDispatcher = new FPhysXCPUDispatcherSingleThread(); } else { CPUDispatcher = new FPhysXCPUDispatcher(); } // Create sim event callback SimEventCallback = new FPhysXSimEventCallback(); #endif //#if WITH_PHYSX // initialize console variable - this console variable change requires it to restart scene. static bool bInitializeConsoleVariable = true; static float InitialAverageFrameRate = 0.016f; UPhysicsSettings * PhysSetting = UPhysicsSettings::Get(); if (bInitializeConsoleVariable) { InitialAverageFrameRate = PhysSetting->InitialAverageFrameRate; FrameTimeSmoothingFactor[PST_Sync] = PhysSetting->SyncSceneSmoothingFactor; FrameTimeSmoothingFactor[PST_Async] = PhysSetting->AsyncSceneSmoothingFactor; bInitializeConsoleVariable = false; } #if WITH_SUBSTEPPING bSubstepping = PhysSetting->bSubstepping; bSubsteppingAsync = PhysSetting->bSubsteppingAsync; #endif bAsyncSceneEnabled = PhysSetting->bEnableAsyncScene; NumPhysScenes = bAsyncSceneEnabled ? PST_Async + 1 : PST_Cloth + 1; // Create scenes of all scene types for (uint32 SceneType = 0; SceneType < NumPhysScenes; ++SceneType) { // Create the physics scene InitPhysScene(SceneType); // Also initialize scene data bPhysXSceneExecuting[SceneType] = false; // Initialize to a value which would be acceptable if FrameTimeSmoothingFactor[i] = 1.0f, i.e. constant simulation substeps AveragedFrameTime[SceneType] = InitialAverageFrameRate; // gets from console variable, and clamp to [0, 1] - 1 should be fixed time as 30 fps FrameTimeSmoothingFactor[SceneType] = FMath::Clamp<float>(FrameTimeSmoothingFactor[SceneType], 0.f, 1.f); } if (!bAsyncSceneEnabled) { PhysXSceneIndex[PST_Async] = 0; } // Make sure we use the sync scene for apex world support of destructibles in the async scene #if WITH_APEX NxApexScene* ApexScene = GetApexScene(bAsyncSceneEnabled ? PST_Async : PST_Sync); check(ApexScene); PxScene* SyncPhysXScene = GetPhysXScene(PST_Sync); check(SyncPhysXScene); check(GApexModuleDestructible); GApexModuleDestructible->setWorldSupportPhysXScene(*ApexScene, SyncPhysXScene); GApexModuleDestructible->setDamageApplicationRaycastFlags(NxDestructibleActorRaycastFlags::AllChunks, *ApexScene); #endif }
/** Exposes ticking of physics-engine scene outside Engine. */ void FPhysScene::TickPhysScene(uint32 SceneType, FGraphEventRef& InOutCompletionEvent) { SCOPE_CYCLE_COUNTER(STAT_TotalPhysicsTime); SCOPE_CYCLE_COUNTER(STAT_PhysicsKickOffDynamicsTime); check(SceneType < NumPhysScenes); if (bPhysXSceneExecuting[SceneType] != 0) { // Already executing this scene, must call WaitPhysScene before calling this function again. UE_LOG(LogPhysics, Log, TEXT("TickPhysScene: Already executing scene (%d) - aborting."), SceneType); return; } #if WITH_SUBSTEPPING if (IsSubstepping(SceneType)) //we don't bother sub-stepping cloth { //We're about to start stepping so swap buffers. Might want to find a better place for this? PhysSubSteppers[SceneType]->SwapBuffers(); } #endif /** * clamp down... if this happens we are simming physics slower than real-time, so be careful with it. * it can improve framerate dramatically (really, it is the same as scaling all velocities down and * enlarging all timesteps) but at the same time, it will screw with networking (client and server will * diverge a lot more.) */ float UseDelta = FMath::Min(UseSyncTime(SceneType) ? SyncDeltaSeconds : DeltaSeconds, MaxPhysicsDeltaTime); // Only simulate a positive time step. if (UseDelta <= 0.f) { if (UseDelta < 0.f) { // only do this if negative. Otherwise, whenever we pause, this will come up UE_LOG(LogPhysics, Warning, TEXT("TickPhysScene: Negative timestep (%f) - aborting."), UseDelta); } return; } #if WITH_PHYSX GatherPhysXStats(GetPhysXScene(SceneType), SceneType); #endif /** * Weight frame time according to PhysScene settings. */ AveragedFrameTime[SceneType] *= FrameTimeSmoothingFactor[SceneType]; AveragedFrameTime[SceneType] += (1.0f - FrameTimeSmoothingFactor[SceneType])*UseDelta; // Set execution flag bPhysXSceneExecuting[SceneType] = true; check(!InOutCompletionEvent.GetReference()); // these should be gone because nothing is outstanding InOutCompletionEvent = FGraphEvent::CreateGraphEvent(); bool bTaskOutstanding = false; #if WITH_PHYSX #if WITH_VEHICLE if (VehicleManager && SceneType == PST_Sync) { float TickTime = AveragedFrameTime[SceneType]; #if WITH_SUBSTEPPING if (IsSubstepping(SceneType)) { TickTime = UseSyncTime(SceneType) ? SyncDeltaSeconds : DeltaSeconds; } #endif VehicleManager->PreTick(TickTime); #if WITH_SUBSTEPPING if (IsSubstepping(SceneType) == false) #endif { VehicleManager->Update(AveragedFrameTime[SceneType]); } } #endif #if !WITH_APEX PxScene* PScene = GetPhysXScene(SceneType); if (PScene && (UseDelta > 0.f)) { PhysXCompletionTask* Task = new PhysXCompletionTask(InOutCompletionEvent, PScene->getTaskManager()); PScene->lockWrite(); PScene->simulate(AveragedFrameTime[SceneType], Task); PScene->unlockWrite(); Task->removeReference(); bTaskOutstanding = true; } #else // #if !WITH_APEX // The APEX scene calls the simulate function for the PhysX scene, so we only call ApexScene->simulate(). NxApexScene* ApexScene = GetApexScene(SceneType); if(ApexScene && UseDelta > 0.f) { #if WITH_SUBSTEPPING if (IsSubstepping(SceneType)) //we don't bother sub-stepping cloth { bTaskOutstanding = SubstepSimulation(SceneType, InOutCompletionEvent); }else #endif { PhysXCompletionTask* Task = new PhysXCompletionTask(InOutCompletionEvent, ApexScene->getTaskManager()); ApexScene->simulate(AveragedFrameTime[SceneType], true, Task); Task->removeReference(); bTaskOutstanding = true; } } #endif // #if !WITH_APEX #endif // WITH_PHYSX if (!bTaskOutstanding) { InOutCompletionEvent->DispatchSubsequents(); // nothing to do, so nothing to wait for } #if WITH_SUBSTEPPING bSubstepping = UPhysicsSettings::Get()->bSubstepping; bSubsteppingAsync = UPhysicsSettings::Get()->bSubsteppingAsync; #endif }