virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override { QUICK_SCOPE_CYCLE_COUNTER( STAT_LineBatcherSceneProxy_GetDynamicMeshElements ); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (VisibilityMap & (1 << ViewIndex)) { const FSceneView* View = Views[ViewIndex]; FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex); for (int32 i = 0; i < Lines.Num(); i++) { PDI->DrawLine(Lines[i].Start, Lines[i].End, Lines[i].Color, Lines[i].DepthPriority, Lines[i].Thickness); } for (int32 i = 0; i < Points.Num(); i++) { PDI->DrawPoint(Points[i].Position, Points[i].Color, Points[i].PointSize, Points[i].DepthPriority); } for (int32 i = 0; i < Meshes.Num(); i++) { static FVector const PosX(1.f,0,0); static FVector const PosY(0,1.f,0); static FVector const PosZ(0,0,1.f); FBatchedMesh const& M = Meshes[i]; // this seems far from optimal in terms of perf, but it's for debugging FDynamicMeshBuilder MeshBuilder; // set up geometry for (int32 VertIdx=0; VertIdx < M.MeshVerts.Num(); ++VertIdx) { MeshBuilder.AddVertex( M.MeshVerts[VertIdx], FVector2D::ZeroVector, PosX, PosY, PosZ, FColor::White ); } //MeshBuilder.AddTriangles(M.MeshIndices); for (int32 Idx=0; Idx < M.MeshIndices.Num(); Idx+=3) { MeshBuilder.AddTriangle( M.MeshIndices[Idx], M.MeshIndices[Idx+1], M.MeshIndices[Idx+2] ); } FMaterialRenderProxy* const MaterialRenderProxy = new(FMemStack::Get()) FColoredMaterialRenderProxy(GEngine->DebugMeshMaterial->GetRenderProxy(false), M.Color); MeshBuilder.GetMesh(FMatrix::Identity, MaterialRenderProxy, M.DepthPriority, false, false, ViewIndex, Collector); } } } }
void UGameplayTagReponseTable::TagResponseEvent(const FGameplayTag Tag, int32 NewCount, UAbilitySystemComponent* ASC, int32 idx) { if (!ensure(Entries.IsValidIndex(idx))) { return; } const FGameplayTagResponseTableEntry& Entry = Entries[idx]; int32 TotalCount = 0; { QUICK_SCOPE_CYCLE_COUNTER(ABILITY_TRT_CALC_COUNT); TotalCount += ASC->GetAggregatedStackCount(MakeQuery(Entry.Positive.Tag)); TotalCount -= ASC->GetAggregatedStackCount(MakeQuery(Entry.Negative.Tag)); } FGameplayTagResponseAppliedInfo& Info = RegisteredASCs.FindChecked(ASC); if (TotalCount < 0) { Remove(ASC, Info.PositiveHandle); AddOrUpdate(ASC, Entry.Negative.ResponseGameplayEffect, TotalCount, Info.NegativeHandle); } else if (TotalCount > 0) { Remove(ASC, Info.NegativeHandle); AddOrUpdate(ASC, Entry.Positive.ResponseGameplayEffect, TotalCount, Info.PositiveHandle); } else if (TotalCount == 0) { Remove(ASC, Info.PositiveHandle); Remove(ASC, Info.NegativeHandle); } }
void FPhysScene::WaitPhysScenes() { FGraphEventArray ThingsToComplete; if (PhysicsSceneCompletion.GetReference()) { ThingsToComplete.Add(PhysicsSceneCompletion); } // Loop through scene types to get all scenes // we just wait on everything, though some of these are redundant for (uint32 SceneType = 0; SceneType < NumPhysScenes; ++SceneType) { if (PhysicsSubsceneCompletion[SceneType].GetReference()) { ThingsToComplete.Add(PhysicsSubsceneCompletion[SceneType]); } if (FrameLaggedPhysicsSubsceneCompletion[SceneType].GetReference()) { ThingsToComplete.Add(FrameLaggedPhysicsSubsceneCompletion[SceneType]); } } if (ThingsToComplete.Num()) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FPhysScene_WaitPhysScenes); FTaskGraphInterface::Get().WaitUntilTasksComplete(ThingsToComplete, ENamedThreads::GameThread); } }
void UPaperGroupedSpriteComponent::CreateAllInstanceBodies() { QUICK_SCOPE_CYCLE_COUNTER(STAT_UPaperGroupedSpriteComponent_CreateAllInstanceBodies); FPhysScene* PhysScene = GetWorld()->GetPhysicsScene(); const int32 NumBodies = PerInstanceSpriteData.Num(); check(InstanceBodies.Num() == 0); InstanceBodies.SetNumUninitialized(NumBodies); TArray<FTransform> Transforms; Transforms.Reserve(NumBodies); TArray<TWeakObjectPtr<UBodySetup>> BodySetups; BodySetups.Reserve(NumBodies); for (int32 InstanceIndex = 0; InstanceIndex < NumBodies; ++InstanceIndex) { const FSpriteInstanceData& InstanceData = PerInstanceSpriteData[InstanceIndex]; FBodyInstance* InstanceBody = InitInstanceBody(InstanceIndex, InstanceData, PhysScene); InstanceBodies[InstanceIndex] = InstanceBody; BodySetups.Add((InstanceBody != nullptr) ? InstanceBody->BodySetup : TWeakObjectPtr<UBodySetup>()); } if (SceneProxy != nullptr) { ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER( FSendPaperGroupBodySetups, FGroupedSpriteSceneProxy*, InSceneProxy, (FGroupedSpriteSceneProxy*)SceneProxy, TArray<TWeakObjectPtr<UBodySetup>>, InBodySetups, BodySetups, { InSceneProxy->SetAllBodySetups_RenderThread(InBodySetups); });
PxCollection* MakePhysXCollection(const TArray<UPhysicalMaterial*>& PhysicalMaterials, const TArray<UBodySetup*>& BodySetups, uint64 BaseId) { QUICK_SCOPE_CYCLE_COUNTER(STAT_CreateSharedData); PxCollection* PCollection = PxCreateCollection(); for (UPhysicalMaterial* PhysicalMaterial : PhysicalMaterials) { if (PhysicalMaterial) { PCollection->add(*PhysicalMaterial->GetPhysXMaterial()); } } for (UBodySetup* BodySetup : BodySetups) { for(PxTriangleMesh* TriMesh : BodySetup->TriMeshes) { AddToCollection(PCollection, TriMesh); } for (const FKConvexElem& ConvexElem : BodySetup->AggGeom.ConvexElems) { AddToCollection(PCollection, ConvexElem.ConvexMesh); AddToCollection(PCollection, ConvexElem.ConvexMeshNegX); } } PxSerialization::createSerialObjectIds(*PCollection, PxSerialObjectId(BaseId)); return PCollection; }
void FStartAsyncSimulationFunction::ExecuteTick(float DeltaTime, enum ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { QUICK_SCOPE_CYCLE_COUNTER(FStartAsyncSimulationFunction_ExecuteTick); check(Target); Target->StartAsyncSim(); }
void UPhysicsSerializer::Serialize( FArchive& Ar ) { QUICK_SCOPE_CYCLE_COUNTER(STAT_Serialize); Super::Serialize(Ar); if (Ar.UE4Ver() >= VER_UE4_BODYINSTANCE_BINARY_SERIALIZATION) { bool bCooked = Ar.IsCooking(); Ar << bCooked; if (bCooked) { if (Ar.IsCooking()) { TArray<FName> ActualFormatsToSave; ActualFormatsToSave.Add(FPlatformProperties::GetPhysicsFormat()); BinaryFormatData.Serialize(Ar, this, &ActualFormatsToSave); } else { #if WITH_PHYSX const uint32 Alignment = PHYSX_SERIALIZATION_ALIGNMENT; #else const uint32 Alignment = DEFAULT_ALIGNMENT; #endif BinaryFormatData.Serialize(Ar, this, nullptr, false, Alignment); } } } }
/** * Retrieves the dynamic data for the emitter * * @param bSelected Whether the emitter is selected in the editor * * @return FDynamicEmitterDataBase* The dynamic data, or NULL if it shouldn't be rendered */ FDynamicEmitterDataBase* FParticleBeam2EmitterInstance::GetDynamicData(bool bSelected) { QUICK_SCOPE_CYCLE_COUNTER(STAT_ParticleBeam2EmitterInstance_GetDynamicData); UParticleLODLevel* LODLevel = SpriteTemplate->GetCurrentLODLevel(this); if (IsDynamicDataRequired(LODLevel) == false) { return NULL; } //@todo.SAS. Have this call the UpdateDynamicData function to reduce duplicate code!!! //@SAS. This removes the need for the assertion in the actual render call... if ((ActiveParticles > FDynamicBeam2EmitterData::MaxBeams) || // TTP #33330 - Max of 2048 beams from a single emitter (ParticleStride > ((FDynamicBeam2EmitterData::MaxInterpolationPoints + 2) * (sizeof(FVector) + sizeof(float))) + (FDynamicBeam2EmitterData::MaxNoiseFrequency * (sizeof(FVector) + sizeof(FVector) + sizeof(float) + sizeof(float))) ) // TTP #33330 - Max of 10k per beam (includes interpolation points, noise, etc.) ) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (Component && Component->GetWorld()) { FString ErrorMessage = FString::Printf(TEXT("BeamEmitter with too much data: %s"), Component ? Component->Template ? *(Component->Template->GetName()) : TEXT("No template") : TEXT("No component")); FColor ErrorColor(255,0,0); GEngine->AddOnScreenDebugMessage((uint64)((PTRINT)this), 5.0f, ErrorColor,ErrorMessage); UE_LOG(LogParticles, Log, TEXT("%s"), *ErrorMessage); } #endif //#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) return NULL; } // Allocate the dynamic data FDynamicBeam2EmitterData* NewEmitterData = ::new FDynamicBeam2EmitterData(LODLevel->RequiredModule); { SCOPE_CYCLE_COUNTER(STAT_ParticleMemTime); INC_DWORD_STAT(STAT_DynamicEmitterCount); INC_DWORD_STAT(STAT_DynamicBeamCount); INC_DWORD_STAT_BY(STAT_DynamicEmitterMem, sizeof(FDynamicBeam2EmitterData)); } // Now fill in the source data if( !FillReplayData( NewEmitterData->Source ) ) { delete NewEmitterData; return NULL; } // Setup dynamic render data. Only call this AFTER filling in source data for the emitter. NewEmitterData->Init( bSelected ); return NewEmitterData; }
FMeshBatchAndRelevance::FMeshBatchAndRelevance(const FMeshBatch& InMesh, const FPrimitiveSceneProxy* InPrimitiveSceneProxy, ERHIFeatureLevel::Type FeatureLevel) : Mesh(&InMesh), PrimitiveSceneProxy(InPrimitiveSceneProxy) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FMeshBatchAndRelevance); EBlendMode BlendMode = InMesh.MaterialRenderProxy->GetMaterial(FeatureLevel)->GetBlendMode(); bHasOpaqueOrMaskedMaterial = !IsTranslucentBlendMode(BlendMode); bRenderInMainPass = PrimitiveSceneProxy->ShouldRenderInMainPass(); }
void FNavigationOctree::DemandLazyDataGathering(FNavigationRelevantData& ElementData) { UObject* ElementOb = ElementData.GetOwner(); if (ElementOb == nullptr) { return; } bool bShrink = false; const int32 OrgElementMemory = ElementData.GetGeometryAllocatedSize(); if (ElementData.IsPendingLazyGeometryGathering() == true && ElementData.SupportsGatheringGeometrySlices() == false) { QUICK_SCOPE_CYCLE_COUNTER(STAT_RecastNavMeshGenerator_LazyGeometryExport); UActorComponent& ActorComp = *CastChecked<UActorComponent>(ElementOb); ComponentExportDelegate.ExecuteIfBound(&ActorComp, ElementData); bShrink = true; // mark this element as no longer needing geometry gathering ElementData.bPendingLazyGeometryGathering = false; } if (ElementData.IsPendingLazyModifiersGathering()) { QUICK_SCOPE_CYCLE_COUNTER(STAT_RecastNavMeshGenerator_LazyModifiersExport); INavRelevantInterface* NavElement = Cast<INavRelevantInterface>(ElementOb); check(NavElement); NavElement->GetNavigationData(ElementData); ElementData.bPendingLazyModifiersGathering = false; bShrink = true; } if (bShrink) { // shrink arrays before counting memory // it will be reallocated when adding to octree and RemoveNode will have different value returned by GetAllocatedSize() ElementData.Shrink(); } const int32 ElementMemoryChange = ElementData.GetGeometryAllocatedSize() - OrgElementMemory; const_cast<FNavigationOctree*>(this)->NodesMemory += ElementMemoryChange; INC_MEMORY_STAT_BY(STAT_Navigation_CollisionTreeMemory, ElementMemoryChange); }
void FSingleThreadManager::Tick() { QUICK_SCOPE_CYCLE_COUNTER(STAT_FSingleThreadManager_Tick); // Tick all registered threads. for (int32 RunnableIndex = 0; RunnableIndex < ThreadList.Num(); ++RunnableIndex) { ThreadList[RunnableIndex]->Tick(); } }
/** * Updates the dynamic data for the instance * * @param DynamicData The dynamic data to fill in * @param bSelected true if the particle system component is selected */ bool FParticleBeam2EmitterInstance::UpdateDynamicData(FDynamicEmitterDataBase* DynamicData, bool bSelected) { QUICK_SCOPE_CYCLE_COUNTER(STAT_ParticleBeam2EmitterInstance_UpdateDynamicData); if (ActiveParticles <= 0) { return false; } UParticleLODLevel* LODLevel = SpriteTemplate->GetCurrentLODLevel(this); if ((LODLevel == NULL) || (LODLevel->bEnabled == false)) { return false; } //@SAS. This removes the need for the assertion in the actual render call... if ((ActiveParticles > FDynamicBeam2EmitterData::MaxBeams) || // TTP #33330 - Max of 2048 beams from a single emitter (ParticleStride > ((FDynamicBeam2EmitterData::MaxInterpolationPoints + 2) * (sizeof(FVector) + sizeof(float))) + (FDynamicBeam2EmitterData::MaxNoiseFrequency * (sizeof(FVector) + sizeof(FVector) + sizeof(float) + sizeof(float))) ) // TTP #33330 - Max of 10k per beam (includes interpolation points, noise, etc.) ) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (Component && Component->GetWorld()) { FString ErrorMessage = FString::Printf(TEXT("BeamEmitter with too much data: %s"), Component ? Component->Template ? *(Component->Template->GetName()) : TEXT("No template") : TEXT("No component")); FColor ErrorColor(255,0,0); GEngine->AddOnScreenDebugMessage((uint64)((PTRINT)this), 5.0f, ErrorColor,ErrorMessage); UE_LOG(LogParticles, Log, TEXT("%s"), *ErrorMessage); } #endif //#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) return false; } checkf((DynamicData->GetSource().eEmitterType == DET_Beam2), TEXT("Beam2::UpdateDynamicData> Invalid DynamicData type!")); // Now fill in the source data FDynamicBeam2EmitterData* BeamDynamicData = (FDynamicBeam2EmitterData*)DynamicData; if( !FillReplayData( BeamDynamicData->Source ) ) { return false; } // Setup dynamic render data. Only call this AFTER filling in source data for the emitter. BeamDynamicData->Init( bSelected ); return true; }
void UWorld::WaitForAllAsyncTraceTasks() { #if RUN_ASYNC_TRACE // if running thread, wait until all threads finishes, if we don't do this, there might be more thread running AsyncTraceData& DataBufferExecuted = AsyncTraceState.GetBufferForPreviousFrame(); if (DataBufferExecuted.AsyncTraceCompletionEvent.Num() > 0) { QUICK_SCOPE_CYCLE_COUNTER(STAT_WaitForAllAsyncTraceTasks); FTaskGraphInterface::Get().WaitUntilTasksComplete(DataBufferExecuted.AsyncTraceCompletionEvent,ENamedThreads::GameThread); DataBufferExecuted.AsyncTraceCompletionEvent.Reset(); } #endif // RUN_ASYNC_TRACE }
void FPrimitiveSceneInfo::UpdateStaticMeshes(FRHICommandListImmediate& RHICmdList) { checkSlow(bNeedsStaticMeshUpdate); bNeedsStaticMeshUpdate = false; QUICK_SCOPE_CYCLE_COUNTER(STAT_FPrimitiveSceneInfo_UpdateStaticMeshes); // Remove the primitive's static meshes from the draw lists they're currently in, and re-add them to the appropriate draw lists. for (int32 MeshIndex = 0; MeshIndex < StaticMeshes.Num(); MeshIndex++) { StaticMeshes[MeshIndex].RemoveFromDrawLists(); StaticMeshes[MeshIndex].AddToDrawLists(RHICmdList, Scene); } }
void FPhysScene::WaitClothScene() { FGraphEventArray ThingsToComplete; if (PhysicsSubsceneCompletion[PST_Cloth].GetReference()) { ThingsToComplete.Add(PhysicsSubsceneCompletion[PST_Cloth]); } if (ThingsToComplete.Num()) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FPhysScene_WaitClothScene); FTaskGraphInterface::Get().WaitUntilTasksComplete(ThingsToComplete, ENamedThreads::GameThread); } }
void FThreadManager::Tick() { if (!FPlatformProcess::SupportsMultithreading()) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FSingleThreadManager_Tick); FScopeLock ThreadsLock(&ThreadsCritical); // Tick all registered threads. for (TPair<uint32, FRunnableThread*>& ThreadPair : Threads) { ThreadPair.Value->Tick(); } } }
/** * Draw the scene proxy as a dynamic element * * @param PDI - draw interface to render to * @param View - current view */ virtual void DrawDynamicElements(FPrimitiveDrawInterface* PDI,const FSceneView* View) { QUICK_SCOPE_CYCLE_COUNTER( STAT_ArrowSceneProxy_DrawDynamicElements ); FMatrix EffectiveLocalToWorld; #if WITH_EDITOR if (bLightAttachment) { EffectiveLocalToWorld = GetLocalToWorld().GetMatrixWithoutScale(); } else #endif //WITH_EDITOR { EffectiveLocalToWorld = GetLocalToWorld(); } DrawDirectionalArrow(PDI,EffectiveLocalToWorld,ArrowColor,ArrowSize * 3.0f * ARROW_SCALE,ARROW_SCALE,GetDepthPriorityGroup(View)); }
bool FAnalyticsProviderET::Tick(float DeltaSeconds) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FAnalyticsProviderET_Tick); if (CachedEvents.Num() > 0) { // Countdown to flush FlushEventsCountdown -= DeltaSeconds; // If reached countdown or already at max cached events then flush if (FlushEventsCountdown <= 0 || CachedEvents.Num() >= MaxCachedNumEvents) { FlushEvents(); } } return true; }
FVector2D SGameLayerManager::GetAspectRatioInset(ULocalPlayer* LocalPlayer) const { QUICK_SCOPE_CYCLE_COUNTER(STAT_SGameLayerManager_GetAspectRatioInset); FVector2D Offset(0.f, 0.f); if (LocalPlayer) { FSceneViewInitOptions ViewInitOptions; if (LocalPlayer->CalcSceneViewInitOptions(ViewInitOptions, LocalPlayer->ViewportClient->Viewport)) { FIntRect UnscaledViewRect = ViewInitOptions.GetConstrainedViewRect(); Offset.X = -UnscaledViewRect.Min.X; Offset.Y = -UnscaledViewRect.Min.Y; } } return Offset; }
FByteBulkData* UPhysicsSerializer::GetBinaryData(FName Format, const TArray<FBodyInstance*>& Bodies, const TArray<class UBodySetup*>& BodySetups, const TArray<class UPhysicalMaterial*>& PhysicalMaterials) { if (!FParse::Param(FCommandLine::Get(), TEXT("PhysxSerialization"))) { return nullptr; } #if PLATFORM_MAC return nullptr; //This is not supported right now #endif QUICK_SCOPE_CYCLE_COUNTER(STAT_GetBinaryData); const bool bContainedData = BinaryFormatData.Contains(Format); FByteBulkData* Result = &BinaryFormatData.GetFormat(Format); if (!FParse::Param(FCommandLine::Get(), TEXT("NoPhysxAlignment"))) { Result->SetBulkDataAlignment(PHYSX_SERIALIZATION_ALIGNMENT); } if (!bContainedData) { #if WITH_EDITOR #if WITH_PHYSX TArray<uint8> OutData; FDerivedDataPhysXBinarySerializer* DerivedPhysXSerializer = new FDerivedDataPhysXBinarySerializer(Format, Bodies, BodySetups, PhysicalMaterials, FGuid::NewGuid()); //TODO: Maybe it's worth adding this to the DDC. For now there's a lot of complexity with the guid invalidation so I've left it out. if (DerivedPhysXSerializer->CanBuild()) { DerivedPhysXSerializer->Build(OutData); #endif if (OutData.Num()) { Result->Lock(LOCK_READ_WRITE); FMemory::Memcpy(Result->Realloc(OutData.Num()), OutData.GetData(), OutData.Num()); Result->Unlock(); } } else #endif { UE_LOG(LogPhysics, Warning, TEXT("Attempt to use binary physics data but we are unable to.")); } } return Result->GetBulkDataSize() > 0 ? Result : nullptr; }
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override { QUICK_SCOPE_CYCLE_COUNTER( STAT_BoxSceneProxy_GetDynamicMeshElements ); const FMatrix& LocalToWorld = GetLocalToWorld(); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (VisibilityMap & (1 << ViewIndex)) { const FSceneView* View = Views[ViewIndex]; const FLinearColor DrawColor = GetViewSelectionColor(BoxColor, *View, IsSelected(), IsHovered(), false, IsIndividuallySelected() ); FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex); DrawOrientedWireBox(PDI, LocalToWorld.GetOrigin(), LocalToWorld.GetScaledAxis( EAxis::X ), LocalToWorld.GetScaledAxis( EAxis::Y ), LocalToWorld.GetScaledAxis( EAxis::Z ), BoxExtents, DrawColor, SDPG_World); } } }
void FMeshElementCollector::ProcessTasks() { check(IsInRenderingThread()); check(!ParallelTasks.Num() || bUseAsyncTasks); if (ParallelTasks.Num()) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FMeshElementCollector_ProcessTasks); TArray<TFunction<void()>*, SceneRenderingAllocator>& LocalParallelTasks(ParallelTasks); ParallelFor(ParallelTasks.Num(), [&LocalParallelTasks](int32 Index) { TFunction<void()>* Func = LocalParallelTasks[Index]; (*Func)(); Func->~TFunction<void()>(); } ); ParallelTasks.Empty(); } }
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override { QUICK_SCOPE_CYCLE_COUNTER( STAT_GetDynamicMeshElements_DrawDynamicElements ); const FMatrix& LocalToWorld = GetLocalToWorld(); const int32 CapsuleSides = FMath::Clamp<int32>(CapsuleRadius/4.f, 16, 64); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (VisibilityMap & (1 << ViewIndex)) { const FSceneView* View = Views[ViewIndex]; const FLinearColor DrawCapsuleColor = GetViewSelectionColor(ShapeColor, *View, IsSelected(), IsHovered(), false, IsIndividuallySelected() ); FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex); DrawWireCapsule( PDI, LocalToWorld.GetOrigin(), LocalToWorld.GetScaledAxis( EAxis::X ), LocalToWorld.GetScaledAxis( EAxis::Y ), LocalToWorld.GetScaledAxis( EAxis::Z ), DrawCapsuleColor, CapsuleRadius, CapsuleHalfHeight, CapsuleSides, SDPG_World ); } } }
void FEndPhysicsTickFunction::ExecuteTick(float DeltaTime, enum ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { QUICK_SCOPE_CYCLE_COUNTER(FEndPhysicsTickFunction_ExecuteTick); check(Target); FPhysScene* PhysScene = Target->GetPhysicsScene(); if (PhysScene == NULL) { return; } FGraphEventRef PhysicsComplete = PhysScene->GetCompletionEvent(); if (PhysicsComplete.GetReference() && !PhysicsComplete->IsComplete()) { // don't release the next tick group until the physics has completed and we have run FinishPhysicsSim DECLARE_CYCLE_STAT(TEXT("FSimpleDelegateGraphTask.FinishPhysicsSim"), STAT_FSimpleDelegateGraphTask_FinishPhysicsSim, STATGROUP_TaskGraphTasks); MyCompletionGraphEvent->DontCompleteUntil( FSimpleDelegateGraphTask::CreateAndDispatchWhenReady( FSimpleDelegateGraphTask::FDelegate::CreateUObject(Target, &UWorld::FinishPhysicsSim), GET_STATID(STAT_FSimpleDelegateGraphTask_FinishPhysicsSim), PhysicsComplete, ENamedThreads::GameThread ) ); } else { // it was already done, so let just do it. Target->FinishPhysicsSim(); } #if PHYSX_MEMORY_VALIDATION static int32 Frequency = 0; if (Frequency++ > 10) { Frequency = 0; GPhysXAllocator->ValidateHeaders(); } #endif }
void FTextRenderSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const { QUICK_SCOPE_CYCLE_COUNTER( STAT_TextRenderSceneProxy_GetDynamicMeshElements ); // Vertex factory will not been initialized when the text string is empty or font is invalid. if(VertexFactory.IsInitialized()) { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (VisibilityMap & (1 << ViewIndex)) { const FSceneView* View = Views[ViewIndex]; // Draw the mesh. FMeshBatch& Mesh = Collector.AllocateMesh(); FMeshBatchElement& BatchElement = Mesh.Elements[0]; BatchElement.IndexBuffer = &IndexBuffer; Mesh.VertexFactory = &VertexFactory; BatchElement.PrimitiveUniformBufferResource = &GetUniformBuffer(); BatchElement.FirstIndex = 0; BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3; BatchElement.MinVertexIndex = 0; BatchElement.MaxVertexIndex = VertexBuffer.Vertices.Num() - 1; Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative(); Mesh.bDisableBackfaceCulling = false; Mesh.Type = PT_TriangleList; Mesh.DepthPriorityGroup = SDPG_World; const bool bUseSelectedMaterial = GIsEditor && (View->Family->EngineShowFlags.Selection) ? IsSelected() : false; Mesh.MaterialRenderProxy = TextMaterial->GetRenderProxy(bUseSelectedMaterial); Mesh.bCanApplyViewModeOverrides = !bAlwaysRenderAsText; Collector.AddMesh(ViewIndex, Mesh); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) RenderBounds(Collector.GetPDI(ViewIndex), View->Family->EngineShowFlags, GetBounds(), IsSelected()); #endif } } } }
void FPoseLinkBase::Update(const FAnimationUpdateContext& Context) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FPoseLinkBase_Update); #if DO_CHECK checkf( !bProcessed, TEXT( "Update already in progress, circular link for AnimInstance [%s] Blueprint [%s]" ), \ *Context.AnimInstanceProxy->GetAnimInstanceName(), *GetFullNameSafe(IAnimClassInterface::GetActualAnimClass(Context.AnimInstanceProxy->GetAnimClassInterface()))); TGuardValue<bool> CircularGuard(bProcessed, true); #endif #if WITH_EDITOR if (GIsEditor) { if (LinkedNode == NULL) { //@TODO: Should only do this when playing back AttemptRelink(Context); } // Record the node line activation if (LinkedNode != NULL) { if (Context.AnimInstanceProxy->IsBeingDebugged()) { Context.AnimInstanceProxy->RecordNodeVisit(LinkID, SourceLinkID, Context.GetFinalBlendWeight()); } } } #endif #if ENABLE_ANIMGRAPH_TRAVERSAL_DEBUG checkf(InitializationCounter.IsSynchronizedWith(Context.AnimInstanceProxy->GetInitializationCounter()), TEXT("Calling Update without initialization!")); UpdateCounter.SynchronizeWith(Context.AnimInstanceProxy->GetUpdateCounter()); #endif if (LinkedNode != NULL) { LinkedNode->Update(Context); } }
void FIndirectLightingCache::UpdateCachePrimitivesInternal(FScene* Scene, FSceneRenderer& Renderer, bool bAllowUnbuiltPreview, TMap<FIntVector, FBlockUpdateInfo>& OutBlocksToUpdate, TArray<FIndirectLightingCacheAllocation*>& OutTransitionsOverTimeToUpdate) { SCOPE_CYCLE_COUNTER(STAT_UpdateIndirectLightingCachePrims); const TMap<FPrimitiveComponentId, FAttachmentGroupSceneInfo>& AttachmentGroups = Scene->AttachmentGroups; if (IndirectLightingAllowed(Scene, Renderer)) { if (bUpdateAllCacheEntries) { const uint32 PrimitiveCount = Scene->Primitives.Num(); for (uint32 PrimitiveIndex = 0; PrimitiveIndex < PrimitiveCount; ++PrimitiveIndex) { FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex]; const bool bPrecomputedLightingBufferWasDirty = PrimitiveSceneInfo->NeedsPrecomputedLightingBufferUpdate(); UpdateCachePrimitive(AttachmentGroups, PrimitiveSceneInfo, false, true, OutBlocksToUpdate, OutTransitionsOverTimeToUpdate); // If it was already dirty, then the primitive is already in one of the view dirty primitive list at this point. // This also ensures that a primitive does not get added twice to the list, which could create an array reallocation. if (!bPrecomputedLightingBufferWasDirty) { PrimitiveSceneInfo->MarkPrecomputedLightingBufferDirty(); // Check if it is visible otherwise, it will be updated next time it is visible. for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++) { FViewInfo& View = Renderer.Views[ViewIndex]; if (View.PrimitiveVisibilityMap[PrimitiveIndex]) { // Since the update can be executed on a threaded job (see GILCUpdatePrimTaskEnabled), no reallocation must happen here. checkSlow(View.DirtyPrecomputedLightingBufferPrimitives.Num() < View.DirtyPrecomputedLightingBufferPrimitives.Max()); View.DirtyPrecomputedLightingBufferPrimitives.Push(PrimitiveSceneInfo); break; // We only need to add it in one of the view list. } } } } } else { TArray<uint32> SetBitIndices[4]; { QUICK_SCOPE_CYCLE_COUNTER(STAT_UpdateCachePreWalk); for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++) { FViewInfo& View = Renderer.Views[ViewIndex]; SetBitIndices[ViewIndex].Reserve(View.PrimitiveVisibilityMap.Num()); for (FSceneSetBitIterator BitIt(View.PrimitiveVisibilityMap); BitIt; ++BitIt) { uint32 PrimitiveIndex = BitIt.GetIndex(); SetBitIndices[ViewIndex].Add(PrimitiveIndex); } // Any visible primitives with an indirect shadow need their ILC updated, since that determines the indirect shadow direction for (int32 IndirectPrimitiveIndex = 0; IndirectPrimitiveIndex < View.IndirectShadowPrimitives.Num(); IndirectPrimitiveIndex++) { int32 PrimitiveIndex = View.IndirectShadowPrimitives[IndirectPrimitiveIndex]->GetIndex(); SetBitIndices[ViewIndex].AddUnique(PrimitiveIndex); } } } // Go over the views and operate on any relevant visible primitives for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++) { FViewInfo& View = Renderer.Views[ViewIndex]; const TArray<uint32>& SetBits = SetBitIndices[ViewIndex]; for (int32 i = 0; i < SetBits.Num(); ++i) { uint32 PrimitiveIndex = SetBits[i]; FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex]; const bool bPrecomputedLightingBufferWasDirty = PrimitiveSceneInfo->NeedsPrecomputedLightingBufferUpdate(); const FPrimitiveViewRelevance& PrimitiveRelevance = View.PrimitiveViewRelevanceMap[PrimitiveIndex]; UpdateCachePrimitive(AttachmentGroups, PrimitiveSceneInfo, bAllowUnbuiltPreview, PrimitiveRelevance.bOpaqueRelevance, OutBlocksToUpdate, OutTransitionsOverTimeToUpdate); // If it was already dirty, then the primitive is already in one of the view dirty primitive list at this point. // This also ensures that a primitive does not get added twice to the list, which could create an array reallocation. if (!bPrecomputedLightingBufferWasDirty && PrimitiveSceneInfo->NeedsPrecomputedLightingBufferUpdate()) { // Since the update can be executed on a threaded job (see GILCUpdatePrimTaskEnabled), no reallocation must happen here. checkSlow(View.DirtyPrecomputedLightingBufferPrimitives.Num() < View.DirtyPrecomputedLightingBufferPrimitives.Max()); View.DirtyPrecomputedLightingBufferPrimitives.Push(PrimitiveSceneInfo); } } } } bUpdateAllCacheEntries = false; } }
/** * Captures dynamic replay data for this particle system. * * @param OutData [Out] Data will be copied here * * @return Returns true if successful */ bool FParticleBeam2EmitterInstance::FillReplayData( FDynamicEmitterReplayDataBase& OutData ) { QUICK_SCOPE_CYCLE_COUNTER(STAT_ParticleBeam2EmitterInstance_FillReplayData); if (ActiveParticles <= 0) { return false; } // Call parent implementation first to fill in common particle source data if( !FParticleEmitterInstance::FillReplayData( OutData ) ) { return false; } // If the template is disabled, don't return data. UParticleLODLevel* LODLevel = SpriteTemplate->GetCurrentLODLevel(this); if ((LODLevel == NULL) || (LODLevel->bEnabled == false)) { return false; } OutData.eEmitterType = DET_Beam2; FDynamicBeam2EmitterReplayData* NewReplayData = static_cast< FDynamicBeam2EmitterReplayData* >( &OutData ); NewReplayData->MaterialInterface = GetCurrentMaterial(); // We never want local space for beams NewReplayData->bUseLocalSpace = false; // Never use axis lock for beams NewReplayData->bLockAxis = false; DetermineVertexAndTriangleCount(); NewReplayData->UpVectorStepSize = BeamTypeData->UpVectorStepSize; NewReplayData->TrianglesPerSheet.Empty(BeamTrianglesPerSheet.Num()); NewReplayData->TrianglesPerSheet.AddZeroed(BeamTrianglesPerSheet.Num()); for (int32 BeamIndex = 0; BeamIndex < BeamTrianglesPerSheet.Num(); BeamIndex++) { NewReplayData->TrianglesPerSheet[BeamIndex] = BeamTrianglesPerSheet[BeamIndex]; } int32 IgnoredTaperCount = 0; BeamTypeData->GetDataPointerOffsets(this, NULL, TypeDataOffset, NewReplayData->BeamDataOffset, NewReplayData->InterpolatedPointsOffset, NewReplayData->NoiseRateOffset, NewReplayData->NoiseDeltaTimeOffset, NewReplayData->TargetNoisePointsOffset, NewReplayData->NextNoisePointsOffset, IgnoredTaperCount, NewReplayData->TaperValuesOffset, NewReplayData->NoiseDistanceScaleOffset); NewReplayData->VertexCount = VertexCount; if (BeamModule_Source) { NewReplayData->bUseSource = true; } else { NewReplayData->bUseSource = false; } if (BeamModule_Target) { NewReplayData->bUseTarget = true; } else { NewReplayData->bUseTarget = false; } if (BeamModule_Noise) { NewReplayData->bLowFreqNoise_Enabled = BeamModule_Noise->bLowFreq_Enabled; NewReplayData->bHighFreqNoise_Enabled = false; NewReplayData->bSmoothNoise_Enabled = BeamModule_Noise->bSmooth; } else { NewReplayData->bLowFreqNoise_Enabled = false; NewReplayData->bHighFreqNoise_Enabled = false; NewReplayData->bSmoothNoise_Enabled = false; } NewReplayData->Sheets = (BeamTypeData->Sheets > 0) ? BeamTypeData->Sheets : 1; NewReplayData->Sheets = FMath::Max(NewReplayData->Sheets, 1); NewReplayData->TextureTile = BeamTypeData->TextureTile; NewReplayData->TextureTileDistance = BeamTypeData->TextureTileDistance; NewReplayData->TaperMethod = BeamTypeData->TaperMethod; NewReplayData->InterpolationPoints = BeamTypeData->InterpolationPoints; NewReplayData->NoiseTessellation = 0; NewReplayData->Frequency = 1; NewReplayData->NoiseRangeScale = 1.0f; NewReplayData->NoiseTangentStrength= 1.0f; int32 TessFactor = 1; if ((BeamModule_Noise == NULL) || (BeamModule_Noise->bLowFreq_Enabled == false)) { TessFactor = BeamTypeData->InterpolationPoints ? BeamTypeData->InterpolationPoints : 1; } else { NewReplayData->Frequency = (BeamModule_Noise->Frequency > 0) ? BeamModule_Noise->Frequency : 1; NewReplayData->Frequency = FMath::Max(NewReplayData->Frequency, 1); NewReplayData->NoiseTessellation = (BeamModule_Noise->NoiseTessellation > 0) ? BeamModule_Noise->NoiseTessellation : 1; NewReplayData->NoiseTangentStrength= BeamModule_Noise->NoiseTangentStrength.GetValue(EmitterTime); if (BeamModule_Noise->bNRScaleEmitterTime) { NewReplayData->NoiseRangeScale = BeamModule_Noise->NoiseRangeScale.GetValue(EmitterTime, Component); } else { //-V523 Remove when todo will be implemented //@todo.SAS. Need to address this!!!! // check(0 && TEXT("NoiseRangeScale - No way to get per-particle setting at this time.")); // NewReplayData->NoiseRangeScale = BeamModule_Noise->NoiseRangeScale.GetValue(Particle->RelativeTime, Component); NewReplayData->NoiseRangeScale = BeamModule_Noise->NoiseRangeScale.GetValue(EmitterTime, Component); } NewReplayData->NoiseSpeed = BeamModule_Noise->NoiseSpeed.GetValue(EmitterTime); NewReplayData->NoiseLockTime = BeamModule_Noise->NoiseLockTime; NewReplayData->NoiseLockRadius = BeamModule_Noise->NoiseLockRadius; NewReplayData->bTargetNoise = BeamModule_Noise->bTargetNoise; NewReplayData->NoiseTension = BeamModule_Noise->NoiseTension; } int32 MaxSegments = ((TessFactor * NewReplayData->Frequency) + 1 + 1); // Tessellation * Frequency + FinalSegment + FirstEdge; // Determine the index count NewReplayData->IndexCount = 0; for (int32 Beam = 0; Beam < ActiveParticles; Beam++) { DECLARE_PARTICLE_PTR(Particle, ParticleData + ParticleStride * ParticleIndices[Beam]); int32 CurrentOffset = TypeDataOffset; FBeam2TypeDataPayload* BeamData = NULL; FVector* InterpolatedPoints = NULL; float* NoiseRate = NULL; float* NoiseDelta = NULL; FVector* TargetNoisePoints = NULL; FVector* NextNoisePoints = NULL; float* TaperValues = NULL; float* NoiseDistanceScale = NULL; FBeamParticleModifierPayloadData* SourceModifier = NULL; FBeamParticleModifierPayloadData* TargetModifier = NULL; BeamTypeData->GetDataPointers(this, (const uint8*)Particle, CurrentOffset, BeamData, InterpolatedPoints, NoiseRate, NoiseDelta, TargetNoisePoints, NextNoisePoints, TaperValues, NoiseDistanceScale, SourceModifier, TargetModifier); if (BeamData->TriangleCount > 0) { if (NewReplayData->IndexCount == 0) { NewReplayData->IndexCount = 2; } NewReplayData->IndexCount += BeamData->TriangleCount * NewReplayData->Sheets; // 1 index per triangle in the strip PER SHEET NewReplayData->IndexCount += ((NewReplayData->Sheets - 1) * 4); // 4 extra indices per stitch (degenerates) if (Beam > 0) { NewReplayData->IndexCount += 4; // 4 extra indices per beam (degenerates) } } } if (NewReplayData->IndexCount > 15000) { NewReplayData->IndexStride = sizeof(uint32); } else { NewReplayData->IndexStride = sizeof(uint16); } //@todo. SORTING IS A DIFFERENT ISSUE NOW! // GParticleView isn't going to be valid anymore? uint8* PData = NewReplayData->ParticleData.GetData(); for (int32 i = 0; i < NewReplayData->ActiveParticleCount; i++) { DECLARE_PARTICLE(Particle, ParticleData + ParticleStride * ParticleIndices[i]); FMemory::Memcpy(PData, &Particle, ParticleStride); PData += ParticleStride; } // Set the debug rendering flags... NewReplayData->bRenderGeometry = BeamTypeData->RenderGeometry; NewReplayData->bRenderDirectLine = BeamTypeData->RenderDirectLine; NewReplayData->bRenderLines = BeamTypeData->RenderLines; NewReplayData->bRenderTessellation = BeamTypeData->RenderTessellation; return true; }
void FDecalRendering::BuildVisibleDecalList(const FScene& Scene, const FViewInfo& View, EDecalRenderStage DecalRenderStage, FTransientDecalRenderDataList& OutVisibleDecals) { QUICK_SCOPE_CYCLE_COUNTER(BuildVisibleDecalList); OutVisibleDecals.Empty(Scene.Decals.Num()); const float FadeMultiplier = CVarDecalFadeScreenSizeMultiplier.GetValueOnRenderThread(); const EShaderPlatform ShaderPlatform = View.GetShaderPlatform(); const bool bIsPerspectiveProjection = View.IsPerspectiveProjection(); // Build a list of decals that need to be rendered for this view in SortedDecals for (const FDeferredDecalProxy* DecalProxy : Scene.Decals) { bool bIsShown = true; if (!DecalProxy->IsShown(&View)) { bIsShown = false; } const FMatrix ComponentToWorldMatrix = DecalProxy->ComponentTrans.ToMatrixWithScale(); // can be optimized as we test against a sphere around the box instead of the box itself const float ConservativeRadius = FMath::Sqrt( ComponentToWorldMatrix.GetScaledAxis(EAxis::X).SizeSquared() + ComponentToWorldMatrix.GetScaledAxis(EAxis::Y).SizeSquared() + ComponentToWorldMatrix.GetScaledAxis(EAxis::Z).SizeSquared()); // can be optimized as the test is too conservative (sphere instead of OBB) if(ConservativeRadius < SMALL_NUMBER || !View.ViewFrustum.IntersectSphere(ComponentToWorldMatrix.GetOrigin(), ConservativeRadius)) { bIsShown = false; } if (bIsShown) { FTransientDecalRenderData Data(Scene, DecalProxy, ConservativeRadius); // filter out decals with blend modes that are not supported on current platform if (IsBlendModeSupported(ShaderPlatform, Data.DecalBlendMode)) { if (bIsPerspectiveProjection && Data.DecalProxy->Component->FadeScreenSize != 0.0f) { float Distance = (View.ViewMatrices.ViewOrigin - ComponentToWorldMatrix.GetOrigin()).Size(); float Radius = ComponentToWorldMatrix.GetMaximumAxisScale(); float CurrentScreenSize = ((Radius / Distance) * FadeMultiplier); // fading coefficient needs to increase with increasing field of view and decrease with increasing resolution // FadeCoeffScale is an empirically determined constant to bring us back roughly to fraction of screen size for FadeScreenSize const float FadeCoeffScale = 600.0f; float FOVFactor = ((2.0f/View.ViewMatrices.ProjMatrix.M[0][0]) / View.ViewRect.Width()) * FadeCoeffScale; float FadeCoeff = Data.DecalProxy->Component->FadeScreenSize * FOVFactor; float FadeRange = FadeCoeff * 0.5f; float Alpha = (CurrentScreenSize - FadeCoeff) / FadeRange; Data.FadeAlpha = FMath::Min(Alpha, 1.0f); } EDecalRenderStage LocalDecalRenderStage = FDecalRenderingCommon::ComputeRenderStage(ShaderPlatform, Data.DecalBlendMode); // we could do this test earlier to avoid the decal intersection but getting DecalBlendMode also costs if (View.Family->EngineShowFlags.ShaderComplexity || (DecalRenderStage == LocalDecalRenderStage && Data.FadeAlpha>0.0f) ) { OutVisibleDecals.Add(Data); } } } } if (OutVisibleDecals.Num() > 0) { // Sort by sort order to allow control over composited result // Then sort decals by state to reduce render target switches // Also sort by component since Sort() is not stable struct FCompareFTransientDecalRenderData { FORCEINLINE bool operator()(const FTransientDecalRenderData& A, const FTransientDecalRenderData& B) const { if (B.DecalProxy->SortOrder != A.DecalProxy->SortOrder) { return A.DecalProxy->SortOrder < B.DecalProxy->SortOrder; } // bHasNormal here is more important then blend mode because we want to render every decals that output normals before those that read normal. if (B.bHasNormal != A.bHasNormal) { return B.bHasNormal < A.bHasNormal; // < so that those outputting normal are first. } if (B.DecalBlendMode != A.DecalBlendMode) { return (int32)B.DecalBlendMode < (int32)A.DecalBlendMode; } // Batch decals with the same material together if (B.MaterialProxy != A.MaterialProxy) { return B.MaterialProxy < A.MaterialProxy; } return (PTRINT)B.DecalProxy->Component < (PTRINT)A.DecalProxy->Component; } }; // Sort decals by blend mode to reduce render target switches OutVisibleDecals.Sort(FCompareFTransientDecalRenderData()); } }
void UNavCollision::GetNavigationModifier(FCompositeNavModifier& Modifier, const FTransform& LocalToWorld) { QUICK_SCOPE_CYCLE_COUNTER(STAT_NavCollision_GetNavigationModifier); const TSubclassOf<UNavArea> UseAreaClass = AreaClass ? AreaClass : UNavigationSystem::GetDefaultObstacleArea(); Modifier.ReserveForAdditionalAreas(CylinderCollision.Num() + BoxCollision.Num() + (ConvexCollision.VertexBuffer.Num() > 0 ? ConvexShapeIndices.Num() : 0)); for (int32 i = 0; i < CylinderCollision.Num(); i++) { FTransform CylinderToWorld = LocalToWorld; const FVector Origin = CylinderToWorld.TransformPosition(CylinderCollision[i].Offset); CylinderToWorld.SetTranslation(Origin); FAreaNavModifier AreaMod(CylinderCollision[i].Radius, CylinderCollision[i].Height, CylinderToWorld, UseAreaClass); AreaMod.SetIncludeAgentHeight(true); Modifier.Add(AreaMod); } for (int32 i = 0; i < BoxCollision.Num(); i++) { FTransform BoxToWorld = LocalToWorld; const FVector Origin = BoxToWorld.TransformPosition(BoxCollision[i].Offset); BoxToWorld.SetTranslation(Origin); FAreaNavModifier AreaMod(BoxCollision[i].Extent, BoxToWorld, UseAreaClass); AreaMod.SetIncludeAgentHeight(true); Modifier.Add(AreaMod); } if (ShouldUseConvexCollision()) { // rebuild collision data if needed if (!bHasConvexGeometry) { GatherCollision(); } if (ConvexCollision.VertexBuffer.Num() > 0) { int32 LastVertIndex = 0; TArray<FVector> Verts(ConvexCollision.VertexBuffer); for (int32 i = 0; i < ConvexShapeIndices.Num(); i++) { int32 FirstVertIndex = LastVertIndex; LastVertIndex = ConvexShapeIndices.IsValidIndex(i + 1) ? ConvexShapeIndices[i + 1] : ConvexCollision.VertexBuffer.Num(); FAreaNavModifier AreaMod(Verts, FirstVertIndex, LastVertIndex, ENavigationCoordSystem::Unreal, LocalToWorld, UseAreaClass); AreaMod.SetIncludeAgentHeight(true); Modifier.Add(AreaMod); } } } }